Skip to content

Commit

Permalink
Merge pull request #802 from CVanF5/gencert
Browse files Browse the repository at this point in the history
Add gencert function
  • Loading branch information
CVanF5 authored Sep 23, 2024
2 parents f8f4443 + 3e13ace commit 13f1b19
Show file tree
Hide file tree
Showing 14 changed files with 631 additions and 23 deletions.
4 changes: 2 additions & 2 deletions Makefile.containers
Original file line number Diff line number Diff line change
Expand Up @@ -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),)
Expand Down
29 changes: 29 additions & 0 deletions internal/collector/otel_collector_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
27 changes: 26 additions & 1 deletion internal/collector/otelcol.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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 -}}:
Expand Down
14 changes: 14 additions & 0 deletions internal/collector/settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
58 changes: 58 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down
15 changes: 8 additions & 7 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
},
},
Expand Down
13 changes: 9 additions & 4 deletions internal/config/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions internal/config/testdata/nginx-agent.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
17 changes: 14 additions & 3 deletions internal/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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"`
}
Expand Down
Loading

0 comments on commit 13f1b19

Please sign in to comment.