diff --git a/otelconf/internal/tls/config.go b/otelconf/internal/tls/config.go new file mode 100644 index 00000000000..8afb018b20e --- /dev/null +++ b/otelconf/internal/tls/config.go @@ -0,0 +1,40 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package tls provides functionality to translate configuration options into tls.Config. +package tls // import "go.opentelemetry.io/contrib/otelconf/internal/tls" + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "os" +) + +// CreateConfig creates a tls.Config from certificate files. +func CreateConfig(caCertFile, clientCertFile, clientKeyFile *string) (*tls.Config, error) { + tlsConfig := &tls.Config{} + if caCertFile != nil { + caText, err := os.ReadFile(*caCertFile) + if err != nil { + return nil, err + } + certPool := x509.NewCertPool() + if !certPool.AppendCertsFromPEM(caText) { + return nil, errors.New("could not create certificate authority chain from certificate") + } + tlsConfig.RootCAs = certPool + } + if clientCertFile != nil { + if clientKeyFile == nil { + return nil, errors.New("client certificate was provided but no client key was provided") + } + clientCert, err := tls.LoadX509KeyPair(*clientCertFile, *clientKeyFile) + if err != nil { + return nil, fmt.Errorf("could not use client certificate: %w", err) + } + tlsConfig.Certificates = []tls.Certificate{clientCert} + } + return tlsConfig, nil +} diff --git a/otelconf/internal/tls/config_test.go b/otelconf/internal/tls/config_test.go new file mode 100644 index 00000000000..f1177f264cf --- /dev/null +++ b/otelconf/internal/tls/config_test.go @@ -0,0 +1,72 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package tls + +import ( + "crypto/tls" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestCreateConfig(t *testing.T) { + tests := []struct { + name string + caCertFile *string + clientCertFile *string + clientKeyFile *string + wantErrContains string + want func(*tls.Config, *testing.T) + }{ + { + name: "no-input", + want: func(result *tls.Config, t *testing.T) { + require.Nil(t, result.Certificates) + require.Nil(t, result.RootCAs) + }, + }, + { + name: "only-cacert-provided", + caCertFile: ptr(filepath.Join("..", "..", "testdata", "ca.crt")), + want: func(result *tls.Config, t *testing.T) { + require.Nil(t, result.Certificates) + require.NotNil(t, result.RootCAs) + }, + }, + { + name: "nonexistent-cacert-file", + caCertFile: ptr("nowhere.crt"), + wantErrContains: "open nowhere.crt:", + }, + { + name: "nonexistent-clientcert-file", + clientCertFile: ptr("nowhere.crt"), + clientKeyFile: ptr("nowhere.crt"), + wantErrContains: "could not use client certificate: open nowhere.crt:", + }, + { + name: "bad-cacert-file", + caCertFile: ptr(filepath.Join("..", "..", "testdata", "bad_cert.crt")), + wantErrContains: "could not create certificate authority chain from certificate", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := CreateConfig(tt.caCertFile, tt.clientCertFile, tt.clientKeyFile) + + if tt.wantErrContains != "" { + require.Contains(t, err.Error(), tt.wantErrContains) + } else { + require.NoError(t, err) + tt.want(got, t) + } + }) + } +} + +func ptr[T any](v T) *T { + return &v +} diff --git a/otelconf/v0.3.0/config.go b/otelconf/v0.3.0/config.go index a468cf3ffcf..f4bbe39f8e3 100644 --- a/otelconf/v0.3.0/config.go +++ b/otelconf/v0.3.0/config.go @@ -6,11 +6,8 @@ package otelconf // import "go.opentelemetry.io/contrib/otelconf/v0.3.0" import ( "context" - "crypto/tls" - "crypto/x509" "errors" "fmt" - "os" "go.opentelemetry.io/otel/baggage" "go.opentelemetry.io/otel/log" @@ -195,33 +192,6 @@ func ParseYAML(file []byte) (*OpenTelemetryConfiguration, error) { return &cfg, nil } -// createTLSConfig creates a tls.Config from certificate files. -func createTLSConfig(caCertFile, clientCertFile, clientKeyFile *string) (*tls.Config, error) { - tlsConfig := &tls.Config{} - if caCertFile != nil { - caText, err := os.ReadFile(*caCertFile) - if err != nil { - return nil, err - } - certPool := x509.NewCertPool() - if !certPool.AppendCertsFromPEM(caText) { - return nil, errors.New("could not create certificate authority chain from certificate") - } - tlsConfig.RootCAs = certPool - } - if clientCertFile != nil { - if clientKeyFile == nil { - return nil, errors.New("client certificate was provided but no client key was provided") - } - clientCert, err := tls.LoadX509KeyPair(*clientCertFile, *clientKeyFile) - if err != nil { - return nil, fmt.Errorf("could not use client certificate: %w", err) - } - tlsConfig.Certificates = []tls.Certificate{clientCert} - } - return tlsConfig, nil -} - // createHeadersConfig combines the two header config fields. Headers take precedence over headersList. func createHeadersConfig(headers []NameStringValuePair, headersList *string) (map[string]string, error) { result := make(map[string]string) diff --git a/otelconf/v0.3.0/config_test.go b/otelconf/v0.3.0/config_test.go index ad00a110724..b0c9890c55d 100644 --- a/otelconf/v0.3.0/config_test.go +++ b/otelconf/v0.3.0/config_test.go @@ -4,7 +4,6 @@ package otelconf import ( - "crypto/tls" "encoding/json" "errors" "os" @@ -610,62 +609,6 @@ func TestSerializeJSON(t *testing.T) { } } -func TestCreateTLSConfig(t *testing.T) { - tests := []struct { - name string - caCertFile *string - clientCertFile *string - clientKeyFile *string - wantErrContains string - want func(*tls.Config, *testing.T) - }{ - { - name: "no-input", - want: func(result *tls.Config, t *testing.T) { - require.Nil(t, result.Certificates) - require.Nil(t, result.RootCAs) - }, - }, - { - name: "only-cacert-provided", - caCertFile: ptr(filepath.Join("..", "testdata", "ca.crt")), - want: func(result *tls.Config, t *testing.T) { - require.Nil(t, result.Certificates) - require.NotNil(t, result.RootCAs) - }, - }, - { - name: "nonexistent-cacert-file", - caCertFile: ptr("nowhere.crt"), - wantErrContains: "open nowhere.crt:", - }, - { - name: "nonexistent-clientcert-file", - clientCertFile: ptr("nowhere.crt"), - clientKeyFile: ptr("nowhere.crt"), - wantErrContains: "could not use client certificate: open nowhere.crt:", - }, - { - name: "bad-cacert-file", - caCertFile: ptr(filepath.Join("..", "testdata", "bad_cert.crt")), - wantErrContains: "could not create certificate authority chain from certificate", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := createTLSConfig(tt.caCertFile, tt.clientCertFile, tt.clientKeyFile) - - if tt.wantErrContains != "" { - require.Contains(t, err.Error(), tt.wantErrContains) - } else { - require.NoError(t, err) - tt.want(got, t) - } - }) - } -} - func TestCreateHeadersConfig(t *testing.T) { tests := []struct { name string diff --git a/otelconf/v0.3.0/log.go b/otelconf/v0.3.0/log.go index 4fefff33e02..7f9c1340e83 100644 --- a/otelconf/v0.3.0/log.go +++ b/otelconf/v0.3.0/log.go @@ -18,6 +18,8 @@ import ( sdklog "go.opentelemetry.io/otel/sdk/log" "go.opentelemetry.io/otel/sdk/resource" "google.golang.org/grpc/credentials" + + "go.opentelemetry.io/contrib/otelconf/internal/tls" ) func loggerProvider(cfg configOptions, res *resource.Resource) (log.LoggerProvider, shutdownFunc, error) { @@ -158,7 +160,7 @@ func otlpHTTPLogExporter(ctx context.Context, otlpConfig *OTLP) (sdklog.Exporter opts = append(opts, otlploghttp.WithHeaders(headersConfig)) } - tlsConfig, err := createTLSConfig(otlpConfig.Certificate, otlpConfig.ClientCertificate, otlpConfig.ClientKey) + tlsConfig, err := tls.CreateConfig(otlpConfig.Certificate, otlpConfig.ClientCertificate, otlpConfig.ClientKey) if err != nil { return nil, err } @@ -211,7 +213,7 @@ func otlpGRPCLogExporter(ctx context.Context, otlpConfig *OTLP) (sdklog.Exporter } if otlpConfig.Certificate != nil || otlpConfig.ClientCertificate != nil || otlpConfig.ClientKey != nil { - tlsConfig, err := createTLSConfig(otlpConfig.Certificate, otlpConfig.ClientCertificate, otlpConfig.ClientKey) + tlsConfig, err := tls.CreateConfig(otlpConfig.Certificate, otlpConfig.ClientCertificate, otlpConfig.ClientKey) if err != nil { return nil, err } diff --git a/otelconf/v0.3.0/metric.go b/otelconf/v0.3.0/metric.go index a355004bba6..03169295137 100644 --- a/otelconf/v0.3.0/metric.go +++ b/otelconf/v0.3.0/metric.go @@ -31,6 +31,8 @@ import ( "go.opentelemetry.io/otel/sdk/metric/metricdata" "go.opentelemetry.io/otel/sdk/resource" "google.golang.org/grpc/credentials" + + "go.opentelemetry.io/contrib/otelconf/internal/tls" ) var zeroScope instrumentation.Scope @@ -184,7 +186,7 @@ func otlpHTTPMetricExporter(ctx context.Context, otlpConfig *OTLPMetric) (sdkmet } } - tlsConfig, err := createTLSConfig(otlpConfig.Certificate, otlpConfig.ClientCertificate, otlpConfig.ClientKey) + tlsConfig, err := tls.CreateConfig(otlpConfig.Certificate, otlpConfig.ClientCertificate, otlpConfig.ClientKey) if err != nil { return nil, err } @@ -250,7 +252,7 @@ func otlpGRPCMetricExporter(ctx context.Context, otlpConfig *OTLPMetric) (sdkmet } if otlpConfig.Certificate != nil || otlpConfig.ClientCertificate != nil || otlpConfig.ClientKey != nil { - tlsConfig, err := createTLSConfig(otlpConfig.Certificate, otlpConfig.ClientCertificate, otlpConfig.ClientKey) + tlsConfig, err := tls.CreateConfig(otlpConfig.Certificate, otlpConfig.ClientCertificate, otlpConfig.ClientKey) if err != nil { return nil, err } diff --git a/otelconf/v0.3.0/trace.go b/otelconf/v0.3.0/trace.go index de7010468d4..56687867838 100644 --- a/otelconf/v0.3.0/trace.go +++ b/otelconf/v0.3.0/trace.go @@ -18,6 +18,8 @@ import ( "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace/noop" "google.golang.org/grpc/credentials" + + "go.opentelemetry.io/contrib/otelconf/internal/tls" ) var errInvalidSamplerConfiguration = errors.New("invalid sampler configuration") @@ -214,7 +216,7 @@ func otlpGRPCSpanExporter(ctx context.Context, otlpConfig *OTLP) (sdktrace.SpanE } if otlpConfig.Certificate != nil || otlpConfig.ClientCertificate != nil || otlpConfig.ClientKey != nil { - tlsConfig, err := createTLSConfig(otlpConfig.Certificate, otlpConfig.ClientCertificate, otlpConfig.ClientKey) + tlsConfig, err := tls.CreateConfig(otlpConfig.Certificate, otlpConfig.ClientCertificate, otlpConfig.ClientKey) if err != nil { return nil, err } @@ -262,7 +264,7 @@ func otlpHTTPSpanExporter(ctx context.Context, otlpConfig *OTLP) (sdktrace.SpanE opts = append(opts, otlptracehttp.WithHeaders(headersConfig)) } - tlsConfig, err := createTLSConfig(otlpConfig.Certificate, otlpConfig.ClientCertificate, otlpConfig.ClientKey) + tlsConfig, err := tls.CreateConfig(otlpConfig.Certificate, otlpConfig.ClientCertificate, otlpConfig.ClientKey) if err != nil { return nil, err }