1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
package logging
import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"net/http"
"time"
"github.com/lxc/incus/v6/internal/server/state"
"github.com/lxc/incus/v6/shared/api"
incustls "github.com/lxc/incus/v6/shared/tls"
)
// WebhookLogger represents a webhook logger.
type WebhookLogger struct {
common
client *http.Client
address string
username string
password string
retry int
}
// NewWebhookLogger instantiates a new webhook logger.
func NewWebhookLogger(s *state.State, name string) (*WebhookLogger, error) {
address, username, password, caCertificate, retry := s.GlobalConfig.LoggingConfigForWebhook(name)
client := &http.Client{}
// Set defaults.
if retry == 0 {
retry = 3
}
// Setup the server for self-signed certirficates.
if caCertificate != "" {
// Prepare the TLS config.
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS13,
}
// Parse the provided certificate.
certBlock, _ := pem.Decode([]byte(caCertificate))
if certBlock == nil {
return nil, errors.New("Invalid remote certificate")
}
serverCert, err := x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("Invalid remote certificate: %w", err)
}
// Add the certificate to the TLS config.
incustls.TLSConfigWithTrustedCert(tlsConfig, serverCert)
// Configure the HTTP client with our TLS config.
client.Transport = &http.Transport{TLSClientConfig: tlsConfig}
}
return &WebhookLogger{
common: newCommonLogger(name, s.GlobalConfig),
client: client,
address: address,
username: username,
password: password,
retry: retry,
}, nil
}
// HandleEvent handles the event received from the internal event listener.
func (c *WebhookLogger) HandleEvent(event api.Event) {
// JSON data.
data, err := json.Marshal(event)
if err != nil {
return
}
// Prepare the request.
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, c.address, bytes.NewReader(data))
if err != nil {
return
}
req.Header.Set("Content-Type", "application/json")
for range c.retry {
resp, err := c.client.Do(req)
if err != nil {
// Wait 10s and try again.
time.Sleep(10 * time.Second)
continue
}
_ = resp.Body.Close()
}
}
// Start starts the webhook logger.
func (c *WebhookLogger) Start() error {
return nil
}
// Stop cleans up the webhook logger.
func (c *WebhookLogger) Stop() {
}
// Validate checks whether the logger configuration is correct.
func (c *WebhookLogger) Validate() error {
if c.address == "" {
return fmt.Errorf("%s: Address cannot be empty", c.name)
}
return nil
}
|