diff --git a/Makefile.containers b/Makefile.containers index c82b7e9116..15c7019f01 100644 --- a/Makefile.containers +++ b/Makefile.containers @@ -10,8 +10,8 @@ endif ifeq ($(CONTAINER_CLITOOL), docker) CONTAINER_BUILDENV ?= DOCKER_BUILDKIT=1 BUILDKIT_PROGRESS=plain -ifeq ($(shell docker-compose -v >/dev/null 2>&1 || echo FAIL),) -CONTAINER_COMPOSE = docker-compose +ifeq ($(shell docker -v >/dev/null 2>&1 || echo FAIL),) +CONTAINER_COMPOSE = docker compose endif else ifeq ($(CONTAINER_CLITOOL), podman) ifeq ($(shell podman-compose -v >/dev/null 2>&1 || echo FAIL),) diff --git a/internal/collector/otel_collector_plugin.go b/internal/collector/otel_collector_plugin.go index c3a53d09c3..160ec3e113 100644 --- a/internal/collector/otel_collector_plugin.go +++ b/internal/collector/otel_collector_plugin.go @@ -78,6 +78,10 @@ func (oc *Collector) Init(ctx context.Context, mp bus.MessagePipeInterface) erro return fmt.Errorf("write OTel Collector config: %w", err) } + if oc.config.Collector.Receivers.OtlpReceivers != nil { + oc.processReceivers(ctx, oc.config.Collector.Receivers.OtlpReceivers) + } + bootErr := oc.bootup(runCtx) if bootErr != nil { slog.ErrorContext(runCtx, "Unable to start OTel Collector", "error", bootErr) @@ -86,6 +90,31 @@ func (oc *Collector) Init(ctx context.Context, mp bus.MessagePipeInterface) erro return nil } +// Process receivers and log warning for sub-optimal configurations +func (oc *Collector) processReceivers(ctx context.Context, receivers []config.OtlpReceiver) { + for _, receiver := range receivers { + if receiver.OtlpTLSConfig == nil { + slog.WarnContext(ctx, "OTEL receiver is configured without TLS. Connections are unencrypted.") + continue + } + + if receiver.OtlpTLSConfig.GenerateSelfSignedCert { + slog.WarnContext(ctx, + "Self-signed certificate for OTEL receiver requested, "+ + "this is not recommended for production environments.", + ) + + if receiver.OtlpTLSConfig.ExistingCert { + slog.WarnContext(ctx, + "Certificate file already exists, skipping self-signed certificate generation", + ) + } + } else { + slog.WarnContext(ctx, "OTEL receiver is configured without TLS. Connections are unencrypted.") + } + } +} + func (oc *Collector) bootup(ctx context.Context) error { slog.InfoContext(ctx, "Starting OTel collector") errChan := make(chan error) diff --git a/internal/collector/otelcol.tmpl b/internal/collector/otelcol.tmpl index 7430f75adb..bd34cf733b 100644 --- a/internal/collector/otelcol.tmpl +++ b/internal/collector/otelcol.tmpl @@ -27,10 +27,35 @@ receivers: protocols: {{- if eq .Server.Type 0 }} grpc: + endpoint: "{{ .Server.Host }}:{{ .Server.Port }}" + {{- if and .OtlpTLSConfig (or (gt (len .OtlpTLSConfig.Key) 0) (gt (len .OtlpTLSConfig.Cert) 0) (gt (len .OtlpTLSConfig.Key) 0)) }} + tls: + {{ if gt (len .OtlpTLSConfig.Cert) 0 -}} + cert_file: {{ .OtlpTLSConfig.Cert }} + {{- end }} + {{ if gt (len .OtlpTLSConfig.Key) 0 -}} + key_file: {{ .OtlpTLSConfig.Key }} + {{- end }} + {{ if gt (len .OtlpTLSConfig.Ca) 0 -}} + ca_file: {{ .OtlpTLSConfig.Ca }} + {{- end }} + {{- end }} {{- else }} http: - {{- end }} endpoint: "{{- .Server.Host -}}:{{- .Server.Port -}}" + {{- if .OtlpTLSConfig }} + tls: + {{ if gt (len .OtlpTLSConfig.Cert) 0 -}} + cert_file: {{ .OtlpTLSConfig.Cert }} + {{- end }} + {{ if gt (len .OtlpTLSConfig.Key) 0 -}} + key_file: {{ .OtlpTLSConfig.Key }} + {{- end }} + {{ if gt (len .OtlpTLSConfig.Ca) 0 -}} + ca_file: {{ .OtlpTLSConfig.Ca }} + {{- end }} + {{- end }} + {{- end }} {{- end }} {{- range .Receivers.NginxReceivers }} nginx/{{- .InstanceID -}}: diff --git a/internal/collector/settings_test.go b/internal/collector/settings_test.go index caa84f9e1c..1cf282af63 100644 --- a/internal/collector/settings_test.go +++ b/internal/collector/settings_test.go @@ -90,6 +90,20 @@ func TestTemplateWrite(t *testing.T) { }, }, }) + // Clear default config and test collector with TLS enabled + cfg.Collector.Receivers.OtlpReceivers = []config.OtlpReceiver{} + cfg.Collector.Receivers.OtlpReceivers = append(cfg.Collector.Receivers.OtlpReceivers, config.OtlpReceiver{ + Server: &config.ServerConfig{ + Host: "localhost", + Port: 4317, + Type: 0, + }, + OtlpTLSConfig: &config.OtlpTLSConfig{ + Cert: "/tmp/cert.pem", + Key: "/tmp/key.pem", + Ca: "/tmp/ca.pem", + }, + }) require.NotNil(t, cfg) diff --git a/internal/config/config.go b/internal/config/config.go index 9a48722680..faddd11d3a 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -12,9 +12,11 @@ import ( "log/slog" "os" "path/filepath" + "slices" "strings" "time" + selfsignedcerts "github.com/nginx/agent/v3/pkg/tls" uuidLibrary "github.com/nginx/agent/v3/pkg/uuid" "github.com/spf13/cobra" flag "github.com/spf13/pflag" @@ -389,6 +391,11 @@ func resolveCollector(allowedDirs []string) (*Collector, error) { Log: &log, } + // Check for self-signed certificate true in Agent conf + if err = handleSelfSignedCertificates(col); err != nil { + return nil, err + } + err = col.Validate(allowedDirs) if err != nil { return nil, fmt.Errorf("collector config: %w", err) @@ -397,6 +404,57 @@ func resolveCollector(allowedDirs []string) (*Collector, error) { return col, nil } +// generate self-signed certificate for OTEL receiver +// nolint: revive +func handleSelfSignedCertificates(col *Collector) error { + if col.Receivers.OtlpReceivers != nil { + for _, receiver := range col.Receivers.OtlpReceivers { + if receiver.OtlpTLSConfig != nil && receiver.OtlpTLSConfig.GenerateSelfSignedCert { + err := processOtlpReceivers(receiver.OtlpTLSConfig) + if err != nil { + return fmt.Errorf("failed to generate self-signed certificate: %w", err) + } + } + } + } + + return nil +} + +func processOtlpReceivers(tlsConfig *OtlpTLSConfig) error { + sanNames := strings.Split(DefCollectorTLSSANNames, ",") + + if tlsConfig.Ca == "" { + tlsConfig.Ca = DefCollectorTLSCAPath + } + if tlsConfig.Cert == "" { + tlsConfig.Cert = DefCollectorTLSCertPath + } + if tlsConfig.Key == "" { + tlsConfig.Key = DefCollectorTLSKeyPath + } + + if !slices.Contains(sanNames, tlsConfig.ServerName) { + sanNames = append(sanNames, tlsConfig.ServerName) + } + if len(sanNames) > 0 { + existingCert, err := selfsignedcerts.GenerateServerCerts( + sanNames, + tlsConfig.Ca, + tlsConfig.Cert, + tlsConfig.Key, + ) + if err != nil { + return fmt.Errorf("failed to generate self-signed certificate: %w", err) + } + if existingCert { + tlsConfig.ExistingCert = true + } + } + + return nil +} + func resolveCommand() *Command { if !viperInstance.IsSet(CommandRootKey) { return nil diff --git a/internal/config/config_test.go b/internal/config/config_test.go index b5ed25fde5..3daec1c292 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -367,18 +367,19 @@ func getAgentConfig() *Config { { Server: &ServerConfig{ Host: "localhost", - Port: 4321, + Port: 4317, Type: 0, }, Auth: &AuthConfig{ Token: "even-secreter-token", }, - TLS: &TLSConfig{ - Cert: "/path/to/server-cert.pem", - Key: "/path/to/server-cert.pem", - Ca: "/path/to/server-cert.pem", - SkipVerify: true, - ServerName: "local-dataa-plane-server", + OtlpTLSConfig: &OtlpTLSConfig{ + GenerateSelfSignedCert: false, + Cert: "/path/to/server-cert.pem", + Key: "/path/to/server-cert.pem", + Ca: "/path/to/server-cert.pem", + SkipVerify: true, + ServerName: "local-data-plane-server", }, }, }, diff --git a/internal/config/defaults.go b/internal/config/defaults.go index 0b9aedf310..5a2b6b7f4b 100644 --- a/internal/config/defaults.go +++ b/internal/config/defaults.go @@ -14,10 +14,15 @@ const ( DefNginxReloadMonitoringPeriod = 10 * time.Second DefTreatErrorsAsWarnings = true - DefCollectorConfigPath = "/var/run/nginx-agent/opentelemetry-collector-agent.yaml" - DefCollectorLogLevel = "INFO" - DefCollectorLogPath = "/var/log/nginx-agent/opentelemetry-collector-agent.log" - DefConfigDirectories = "/etc/nginx:/usr/local/etc/nginx:/usr/share/nginx/modules" + DefCollectorConfigPath = "/var/run/nginx-agent/opentelemetry-collector-agent.yaml" + DefCollectorTLSGenSelfSignedCert = false + DefCollectorLogLevel = "INFO" + DefCollectorLogPath = "/var/log/nginx-agent/opentelemetry-collector-agent.log" + DefConfigDirectories = "/etc/nginx:/usr/local/etc/nginx:/usr/share/nginx/modules" + DefCollectorTLSCertPath = "/var/lib/nginx-agent/cert.pem" + DefCollectorTLSKeyPath = "/var/lib/nginx-agent/key.pem" + DefCollectorTLSCAPath = "/var/lib/nginx-agent/ca.pem" + DefCollectorTLSSANNames = "127.0.0.1,::1,localhost" DefCommandServerHostKey = "" DefCommandServerPortKey = 0 diff --git a/internal/config/testdata/nginx-agent.conf b/internal/config/testdata/nginx-agent.conf index 1145ddf724..81ed647b37 100644 --- a/internal/config/testdata/nginx-agent.conf +++ b/internal/config/testdata/nginx-agent.conf @@ -29,11 +29,11 @@ collector: auth: Token: "secret-receiver-token" tls: + generate_self_signed_cert: false server_name: "test-local-server" - skip_verify: false - cert: /path/to/server-cert.pem - key: /path/to/server-key.pem - ca: /path/to/server-cert.pem + ca: /tmp/ca.pem + cert: /tmp/cert.pem + key: /tmp/key.pem nginx_receivers: - instance_id: cd7b8911-c2c5-4daf-b311-dbead151d938 stub_status: "http://localhost:4321/status" diff --git a/internal/config/types.go b/internal/config/types.go index 7efa6ef758..7c29e24ca0 100644 --- a/internal/config/types.go +++ b/internal/config/types.go @@ -115,9 +115,9 @@ type ( } OtlpReceiver struct { - Server *ServerConfig `yaml:"-" mapstructure:"server"` - Auth *AuthConfig `yaml:"-" mapstructure:"auth"` - TLS *TLSConfig `yaml:"-" mapstructure:"tls"` + Server *ServerConfig `yaml:"-" mapstructure:"server"` + Auth *AuthConfig `yaml:"-" mapstructure:"auth"` + OtlpTLSConfig *OtlpTLSConfig `yaml:"-" mapstructure:"tls"` } NginxReceiver struct { @@ -186,6 +186,17 @@ type ( SkipVerify bool `yaml:"-" mapstructure:"skip_verify"` } + // Specialized TLS configuration for OtlpReceiver with self-signed cert generation. + OtlpTLSConfig struct { + Cert string `yaml:"-" mapstructure:"cert"` + Key string `yaml:"-" mapstructure:"key"` + Ca string `yaml:"-" mapstructure:"ca"` + ServerName string `yaml:"-" mapstructure:"server_name"` + ExistingCert bool `yaml:"-"` + SkipVerify bool `yaml:"-" mapstructure:"skip_verify"` + GenerateSelfSignedCert bool `yaml:"-" mapstructure:"generate_self_signed_cert"` + } + File struct { Location string `yaml:"-" mapstructure:"location"` } diff --git a/pkg/tls/self_signed_cert.go b/pkg/tls/self_signed_cert.go new file mode 100644 index 0000000000..65e57c4402 --- /dev/null +++ b/pkg/tls/self_signed_cert.go @@ -0,0 +1,207 @@ +// Copyright (c) F5, Inc. +// +// This source code is licensed under the Apache License, Version 2.0 license found in the +// LICENSE file in the root directory of this source tree. + +// Package gencert generates self-signed TLS certificates. +package tls + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "errors" + "fmt" + "math/big" + "os" + "time" +) + +const ( + caOrganization = "F5 Inc. CA" + certOrganization = "F5 Inc." + certFilePermissions = 0o600 + keyFilePermissions = 0o600 +) + +type certReq struct { + template *x509.Certificate + parent *x509.Certificate + publicKey *ecdsa.PublicKey + privateKey *ecdsa.PrivateKey +} + +// Returns x509 Certificate object and bytes in PEM format +func genCert(req *certReq) (*x509.Certificate, []byte, error) { + certBytes, createCertErr := x509.CreateCertificate( + rand.Reader, + req.template, + req.parent, + req.publicKey, + req.privateKey, + ) + + if createCertErr != nil { + return &x509.Certificate{}, []byte{}, fmt.Errorf("error generating certificate: %w", createCertErr) + } + + cert, parseCertErr := x509.ParseCertificate(certBytes) + if parseCertErr != nil { + return &x509.Certificate{}, []byte{}, fmt.Errorf("error parsing certificate: %w", parseCertErr) + } + + b := pem.Block{Type: "CERTIFICATE", Bytes: certBytes} + certPEM := pem.EncodeToMemory(&b) + + return cert, certPEM, nil +} + +// Generates a CA, returns x509 Certificate and private key for signing server certificates +func GenerateCA(now time.Time, caCertPath string) (*x509.Certificate, *ecdsa.PrivateKey, error) { + // Generate key pair for the CA + caKeyPair, caKeyErr := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if caKeyErr != nil { + return &x509.Certificate{}, &ecdsa.PrivateKey{}, fmt.Errorf("failed to generate CA private key: %w", caKeyErr) + } + + // Create CA certificate template + caTemplate := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{Organization: []string{certOrganization}}, + NotBefore: now.Add(-time.Minute), + NotAfter: now.AddDate(1, 0, 0), // 1 year + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + IsCA: true, + MaxPathLen: 1, + } + + // CA is self signed + caRequest := certReq{ + template: &caTemplate, + parent: &caTemplate, + publicKey: &caKeyPair.PublicKey, + privateKey: caKeyPair, + } + + caCert, caCertPEM, caErr := genCert(&caRequest) + if caErr != nil { + return &x509.Certificate{}, &ecdsa.PrivateKey{}, fmt.Errorf( + "error generating certificate authority: %w", + caErr) + } + + // Write the CA certificate to a file + writeCAErr := os.WriteFile(caCertPath, caCertPEM, certFilePermissions) + if writeCAErr != nil { + return &x509.Certificate{}, &ecdsa.PrivateKey{}, fmt.Errorf( + "failed to write ca file: %w", + writeCAErr, + ) + } + + return caCert, caKeyPair, nil +} + +// Writes CA, Cert, Key to specified destinations. If cert files are already present, does nothing, returns true +// nolint: revive +func GenerateServerCerts(hostnames []string, caPath, certPath, keyPath string) (existingCert bool, err error) { + // Check for and return existing cert if it already exists + existingCert, existingCertErr := DoesCertAlreadyExist(certPath) + if existingCertErr != nil { + return false, fmt.Errorf("error reading existing certificate data: %w", existingCertErr) + } + if existingCert { + return true, nil + } + + // Get the local time zone + locationCurrentzone, locErr := time.LoadLocation("Local") + if locErr != nil { + return false, fmt.Errorf("error detecting local timezone: %w", locErr) + } + now := time.Now().In(locationCurrentzone) + + // Create CA first + caCert, caKeyPair, caErr := GenerateCA(now, caPath) + if caErr != nil { + return false, fmt.Errorf("error generating certificate authority: %w", caErr) + } + + // Generate key pair for the server certficate + servKeyPair, servKeyErr := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if servKeyErr != nil { + return false, fmt.Errorf("failed to generate server keypair: %w", servKeyErr) + } + + servTemplate := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + Organization: []string{caOrganization}, + }, + NotBefore: now.Add(-time.Minute), + NotAfter: now.AddDate(1, 0, 0), // 1 year + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + DNSNames: hostnames, + } + + servRequest := certReq{ + template: &servTemplate, + parent: caCert, + publicKey: &servKeyPair.PublicKey, + privateKey: caKeyPair, + } + + // Generate server certficated signed by the CA + _, servCertPEM, servCertErr := genCert(&servRequest) + if servCertErr != nil { + return false, fmt.Errorf("error generating server certificate: %w", servCertErr) + } + + // Write the certificate to a file + writeCertErr := os.WriteFile(certPath, servCertPEM, certFilePermissions) + if writeCertErr != nil { + return false, fmt.Errorf("failed to write certificate file: %w", writeCertErr) + } + + // Write the private key to a file + servKeyBytes, marshalErr := x509.MarshalECPrivateKey(servKeyPair) + if marshalErr != nil { + return false, fmt.Errorf("failed to marshal private key file: %w", marshalErr) + } + b := pem.Block{Type: "EC PRIVATE KEY", Bytes: servKeyBytes} + servKeyPEM := pem.EncodeToMemory(&b) + writeKeyErr := os.WriteFile(keyPath, servKeyPEM, keyFilePermissions) + if writeKeyErr != nil { + return false, fmt.Errorf("failed to write key file: %w", writeKeyErr) + } + + return false, nil +} + +// Returns true if a valid certificate is found at certPath +func DoesCertAlreadyExist(certPath string) (bool, error) { + if _, certErr := os.Stat(certPath); certErr == nil { + certBytes, certReadErr := os.ReadFile(certPath) + if certReadErr != nil { + return false, fmt.Errorf("error reading existing certificate file") + } + certPEM, _ := pem.Decode(certBytes) + if certPEM == nil { + return false, errors.New("error decoding certificate PEM block") + } + _, parseErr := x509.ParseCertificate(certPEM.Bytes) + if parseErr == nil { + return true, nil + } + + return false, fmt.Errorf("error parsing existing certificate: %w", parseErr) + } + + return false, nil +} diff --git a/pkg/tls/self_signed_cert_test.go b/pkg/tls/self_signed_cert_test.go new file mode 100644 index 0000000000..64911d77aa --- /dev/null +++ b/pkg/tls/self_signed_cert_test.go @@ -0,0 +1,241 @@ +// Copyright (c) F5, Inc. +// +// This source code is licensed under the Apache License, Version 2.0 license found in the +// LICENSE file in the root directory of this source tree. + +// Package gencert generates self-signed TLS certificates. +package tls + +import ( + "encoding/pem" + "fmt" + "os" + "testing" + + "github.com/nginx/agent/v3/test/helpers" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// nolint: revive,gocognit +func TestGenerateSelfSignedCert(t *testing.T) { + // Setup temp file paths + caPath := "/tmp/test_ca.pem" + certPath := "/tmp/test_cert.pem" + keyPath := "/tmp/test_key.pem" + hostNames := []string{"localhost", "::1", "127.0.0.1"} + + // Cleanup any pre-existing files from previous tests + defer os.Remove(caPath) + defer os.Remove(certPath) + defer os.Remove(keyPath) + + // Define a struct for test cases + type testCase struct { + name string + caPath string + certPath string + keyPath string + expectedError string + setup func() error + hostNames []string + existingCert bool + } + + tests := []testCase{ + { + name: "Test 1: CA, Cert and key file exist", + setup: func() error { + // Ensure no cert files exist + os.Remove(caPath) + os.Remove(certPath) + os.Remove(keyPath) + + // Create valid PEM files + keyBytes, certBytes := helpers.GenerateSelfSignedCert(t) + caPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes}) + certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes}) + keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: keyBytes}) + if caErr := os.WriteFile(caPath, caPEM, 0o600); caErr != nil { + return caErr + } + if certErr := os.WriteFile(certPath, certPEM, 0o600); certErr != nil { + return certErr + } + + return os.WriteFile(keyPath, keyPEM, 0o600) + }, + caPath: caPath, + certPath: certPath, + keyPath: keyPath, + hostNames: hostNames, + existingCert: true, + expectedError: "", + }, + { + name: "Test 2: Invalid cert data", + setup: func() error { + // Ensure no cert or key files exist + os.Remove(caPath) + os.Remove(certPath) + os.Remove(keyPath) + // Create dummy cert files + if caErr := os.WriteFile(caPath, []byte("dummy ca"), 0o600); caErr != nil { + return caErr + } + if certErr := os.WriteFile(certPath, []byte("dummy cert"), 0o600); certErr != nil { + return certErr + } + + return os.WriteFile(keyPath, []byte("dummy key"), 0o600) + }, + caPath: caPath, + certPath: certPath, + keyPath: keyPath, + hostNames: hostNames, + existingCert: false, + expectedError: "error decoding certificate PEM block", + }, + { + name: "Test 3: Error writing certificate file", + setup: func() error { + // Ensure no cert or key files exist + os.Remove(caPath) + os.Remove(certPath) + os.Remove(keyPath) + + return nil + }, + caPath: caPath, + certPath: "/dev/null/cert.pem", // Path that is guaranteed to fail + keyPath: keyPath, + hostNames: hostNames, + existingCert: false, + expectedError: "failed to write certificate file", + }, + { + name: "Test 4: Error writing key file", + setup: func() error { + return nil + }, + caPath: caPath, + certPath: certPath, + keyPath: "/dev/null/key/pem", // Path that is guaranteed to fail + hostNames: hostNames, + existingCert: false, + expectedError: "failed to write key file", + }, + { + name: "Test 5: Successful certificate generation", + setup: func() error { + // Ensure no cert or key files exist + os.Remove(caPath) + os.Remove(certPath) + os.Remove(keyPath) + + return nil + }, + caPath: caPath, + certPath: certPath, + keyPath: keyPath, + hostNames: hostNames, + existingCert: false, + expectedError: "", + }, + { + name: "Test case 6: Error reading existing certificate file", + setup: func() error { + // Ensure no cert or key files exist + os.Remove(caPath) + os.Remove(certPath) + os.Remove(keyPath) + + // No read/write permissions + if certErr := os.WriteFile(certPath, []byte("dummy cert"), 0o000); certErr != nil { + return certErr + } + + return os.WriteFile(keyPath, []byte("dummy key"), 0o600) + }, + caPath: caPath, + certPath: certPath, + keyPath: keyPath, + hostNames: hostNames, + existingCert: false, + expectedError: "error reading existing certificate data", + }, + { + name: "Test case 7: Error reading existing key file", + setup: func() error { + // Ensure no cert or key files exist + os.Remove(caPath) + os.Remove(certPath) + os.Remove(keyPath) + + if certErr := os.WriteFile(certPath, []byte("dummy cert"), 0o600); certErr != nil { + return certErr + } + + return os.WriteFile(keyPath, []byte("dummy key"), 0o000) + }, + caPath: caPath, + certPath: certPath, + keyPath: keyPath, + hostNames: hostNames, + existingCert: false, + expectedError: "error decoding certificate PEM block", + }, + { + name: "Test case 8: Error parsing TLS key pair", + setup: func() error { + // Ensure no cert or key files exist + os.Remove(caPath) + os.Remove(certPath) + os.Remove(keyPath) + + // Write invalid PEM data to simulate parsing error + err := os.WriteFile(certPath, []byte("invalid cert data"), 0o600) + if err != nil { + return err + } + err = os.WriteFile(keyPath, []byte("invalid key data"), 0o600) + if err != nil { + return err + } + + return nil + }, + caPath: caPath, + certPath: certPath, + keyPath: keyPath, + hostNames: hostNames, + existingCert: false, + expectedError: "error decoding certificate PEM block", + }, + } + // Iterate over the test cases + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.setup() + require.NoError(t, err) + + existingCert, genCertErr := GenerateServerCerts(tc.hostNames, tc.caPath, tc.certPath, tc.keyPath) + + // Check the results + if tc.expectedError != "" { + require.Error(t, genCertErr) + assert.Contains(t, genCertErr.Error(), tc.expectedError) + } else { + require.NoError(t, genCertErr) + _, err = os.Stat(tc.certPath) + require.NoError(t, err) + _, err = os.Stat(tc.keyPath) + require.NoError(t, err) + } + fmt.Printf("tc.ExistingCert is %t\n", tc.existingCert) + if tc.existingCert { + require.True(t, existingCert) + } + }) + } +} diff --git a/test/config/collector/test-opentelemetry-collector-agent.yaml b/test/config/collector/test-opentelemetry-collector-agent.yaml index be6aacfa1a..9deb5be556 100644 --- a/test/config/collector/test-opentelemetry-collector-agent.yaml +++ b/test/config/collector/test-opentelemetry-collector-agent.yaml @@ -11,7 +11,11 @@ receivers: otlp/0: protocols: grpc: - endpoint: "localhost:4321" + endpoint: "localhost:4317" + tls: + cert_file: /tmp/cert.pem + key_file: /tmp/key.pem + ca_file: /tmp/ca.pem nginx/123: endpoint: "http://localhost:80/status" collection_interval: 10s diff --git a/test/mock/collector/nginx-agent.conf b/test/mock/collector/nginx-agent.conf index 4b8c58e696..b2e16388d1 100644 --- a/test/mock/collector/nginx-agent.conf +++ b/test/mock/collector/nginx-agent.conf @@ -37,6 +37,19 @@ collector: disk: {} network: {} filesystem: {} + otlp_receivers: + - server: + host: "127.0.0.1" + port: 4317 + type: 0 + auth: + Token: secret-receiver-token + tls: + server_name: test-local-server + ca: /tmp/ca.pem + cert: /tmp/cert.pem + key: /tmp/key.pem + generate_self_signed_cert: true processors: - type: batch exporters: diff --git a/test/types/config.go b/test/types/config.go index fefaa1964b..68f8830cf4 100644 --- a/test/types/config.go +++ b/test/types/config.go @@ -31,7 +31,7 @@ const ( reloadMonitoringPeriod = 400 * time.Millisecond randomPort1 = 1234 - randomPort2 = 4321 + randomPort2 = 4317 randomPort3 = 1337 )