Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add gencert function #802

Merged
merged 10 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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),)
CVanF5 marked this conversation as resolved.
Show resolved Hide resolved
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 }}"
CVanF5 marked this conversation as resolved.
Show resolved Hide resolved
{{- 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
Loading