Skip to content

Commit

Permalink
config: add support for certificate configuration (#6376)
Browse files Browse the repository at this point in the history
Part of
#6351

---------

Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com>
  • Loading branch information
codeboten authored Dec 12, 2024
1 parent 0c0b385 commit 741c472
Show file tree
Hide file tree
Showing 12 changed files with 311 additions and 1 deletion.
6 changes: 6 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ issues:
- gosec
- perfsprint
- usestdlibvars
# Ignoring gosec G402: TLS MinVersion too low
# as the https://pkg.go.dev/crypto/tls#Config handles MinVersion default well.
- path: config/*.go
text: "G402: TLS MinVersion too low."
linters:
- gosec
include:
# revive exported should have comment or be unexported.
- EXC0012
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
This module provides an OpenTelemetry logging bridge for `github.com/go-logr/logr`. (#6386)
- Added SNS instrumentation in `go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws`. (#6388)
- Use a `sync.Pool` for metric options in `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp`. (#6394)
- Added support for configuring `Certificate` field when configuring OTLP exporters in `go.opentelemetry.io/contrib/config`. (#6376)

### Changed

Expand Down
2 changes: 1 addition & 1 deletion config/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ require (
go.opentelemetry.io/otel/sdk/log v0.8.0
go.opentelemetry.io/otel/sdk/metric v1.32.0
go.opentelemetry.io/otel/trace v1.32.0
google.golang.org/grpc v1.68.1
gopkg.in/yaml.v3 v3.0.1
)

Expand All @@ -47,6 +48,5 @@ require (
golang.org/x/text v0.21.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
google.golang.org/grpc v1.68.1 // indirect
google.golang.org/protobuf v1.35.2 // indirect
)
1 change: 1 addition & 0 deletions config/testdata/bad_cert.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This is intentionally not a PEM formatted cert file.
20 changes: 20 additions & 0 deletions config/testdata/ca.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDNjCCAh4CCQC0I5IQT7eziDANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJB
VTESMBAGA1UECAwJQXVzdHJhbGlhMQ8wDQYDVQQHDAZTeWRuZXkxEjAQBgNVBAoM
CU15T3JnTmFtZTEVMBMGA1UEAwwMTXlDb21tb25OYW1lMB4XDTIyMDgwMzA0MTky
MVoXDTMyMDczMTA0MTkyMVowXTELMAkGA1UEBhMCQVUxEjAQBgNVBAgMCUF1c3Ry
YWxpYTEPMA0GA1UEBwwGU3lkbmV5MRIwEAYDVQQKDAlNeU9yZ05hbWUxFTATBgNV
BAMMDE15Q29tbW9uTmFtZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AMhGP0dy3zvkdx9zI+/XVjPOWlER0OUp7Sgzidc3nLOk42+bH4ofIVNtOFVqlNKi
O1bImu238VdBhd6R5IZZ1ZdIMcCeDgSJYu2X9wA3m4PKz8IdXo5ly2OHghhmCvqG
WxgqDj5wPXiczQwuf1EcDMtRWbXJ6Z/XH1U68R/kRdNLkiZ2LwtjoQpis5XYckLL
CrdF+AL6GeDIe0Mh9QGs26Vux+2kvaOGNUWRPE6Wt4GkqyKqmzYfR9HbflJ4xHT2
I+jE1lg+jMBeom7z8Z90RE4GGcHjO+Vens/88r5EAjTnFj1Kb5gL2deSHY1m/++R
Z/kRyg+zQJyw4fAzlAA4+VkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAM3gRdTKX
eGwGYVmmKqA2vTxeigQYLHml7OSopcWj2wJfxfp49HXPRuvgpQn9iubxO3Zmhd83
2X1E+T0A8oy5CfxgpAhHb3lY0jm3TjKXm6m+dSODwL3uND8tX+SqR8sRTFxPvPuo
pmvhdTZoRI3EzIiHLTgCuSU25JNP/vrVoKk0JvCkDYTU/WcVfj0v95DTMoWR4JGz
mtBwrgD0EM2XRw5ZMc7sMPli1gqmCbCQUrDZ+rPB78WDCBILBd8Cz75qYTUp98BY
akJyBckdJHAdyEQYDKa9HpmpexOO7IhSXCTEN1DEBgpZgEi/lBDRG/b0OzenUUgt
LUABtWt3pNQ9HA==
-----END CERTIFICATE-----
20 changes: 20 additions & 0 deletions config/v0.3.0/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ package config // import "go.opentelemetry.io/contrib/config/v0.3.0"

import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"os"

"gopkg.in/yaml.v3"

Expand Down Expand Up @@ -155,3 +158,20 @@ func toStringMap(pairs []NameStringValuePair) map[string]string {
}
return output
}

// createTLSConfig creates a tls.Config from a raw certificate bytes
// to verify a server certificate.
func createTLSConfig(certFile string) (*tls.Config, error) {
b, err := os.ReadFile(certFile)
if err != nil {
return nil, err
}
cp := x509.NewCertPool()
if ok := cp.AppendCertsFromPEM(b); !ok {
return nil, errors.New("failed to append certificate to the cert pool")
}

return &tls.Config{
RootCAs: cp,
}, nil
}
18 changes: 18 additions & 0 deletions config/v0.3.0/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"net/url"
"time"

"google.golang.org/grpc/credentials"

"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
"go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
Expand Down Expand Up @@ -154,6 +156,14 @@ func otlpHTTPLogExporter(ctx context.Context, otlpConfig *OTLP) (sdklog.Exporter
opts = append(opts, otlploghttp.WithHeaders(toStringMap(otlpConfig.Headers)))
}

if otlpConfig.Certificate != nil {
creds, err := createTLSConfig(*otlpConfig.Certificate)
if err != nil {
return nil, fmt.Errorf("could not create client tls credentials: %w", err)
}
opts = append(opts, otlploghttp.WithTLSClientConfig(creds))
}

return otlploghttp.New(ctx, opts...)
}

Expand Down Expand Up @@ -196,5 +206,13 @@ func otlpGRPCLogExporter(ctx context.Context, otlpConfig *OTLP) (sdklog.Exporter
opts = append(opts, otlploggrpc.WithHeaders(toStringMap(otlpConfig.Headers)))
}

if otlpConfig.Certificate != nil {
creds, err := credentials.NewClientTLSFromFile(*otlpConfig.Certificate, "")
if err != nil {
return nil, fmt.Errorf("could not create client tls credentials: %w", err)
}
opts = append(opts, otlploggrpc.WithTLSCredentials(creds))
}

return otlploggrpc.New(ctx, opts...)
}
70 changes: 70 additions & 0 deletions config/v0.3.0/log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ package config
import (
"context"
"errors"
"fmt"
"net/url"
"path/filepath"
"reflect"
"testing"

Expand Down Expand Up @@ -221,6 +223,40 @@ func TestLogProcessor(t *testing.T) {
},
wantProcessor: sdklog.NewBatchProcessor(otlpGRPCExporter),
},
{
name: "batch/otlp-grpc-good-ca-certificate",
processor: LogRecordProcessor{
Batch: &BatchLogRecordProcessor{
Exporter: LogRecordExporter{
OTLP: &OTLP{
Protocol: ptr("grpc"),
Endpoint: ptr("localhost:4317"),
Compression: ptr("gzip"),
Timeout: ptr(1000),
Certificate: ptr(filepath.Join("..", "testdata", "ca.crt")),
},
},
},
},
wantProcessor: sdklog.NewBatchProcessor(otlpGRPCExporter),
},
{
name: "batch/otlp-grpc-bad-ca-certificate",
processor: LogRecordProcessor{
Batch: &BatchLogRecordProcessor{
Exporter: LogRecordExporter{
OTLP: &OTLP{
Protocol: ptr("grpc"),
Endpoint: ptr("localhost:4317"),
Compression: ptr("gzip"),
Timeout: ptr(1000),
Certificate: ptr(filepath.Join("..", "testdata", "bad_cert.crt")),
},
},
},
},
wantErr: fmt.Errorf("could not create client tls credentials: %w", errors.New("credentials: failed to append certificates")),
},
{
name: "batch/otlp-grpc-exporter-no-scheme",
processor: LogRecordProcessor{
Expand Down Expand Up @@ -313,6 +349,40 @@ func TestLogProcessor(t *testing.T) {
},
wantProcessor: sdklog.NewBatchProcessor(otlpHTTPExporter),
},
{
name: "batch/otlp-http-good-ca-certificate",
processor: LogRecordProcessor{
Batch: &BatchLogRecordProcessor{
Exporter: LogRecordExporter{
OTLP: &OTLP{
Protocol: ptr("http/protobuf"),
Endpoint: ptr("localhost:4317"),
Compression: ptr("gzip"),
Timeout: ptr(1000),
Certificate: ptr(filepath.Join("..", "testdata", "ca.crt")),
},
},
},
},
wantProcessor: sdklog.NewBatchProcessor(otlpHTTPExporter),
},
{
name: "batch/otlp-http-bad-ca-certificate",
processor: LogRecordProcessor{
Batch: &BatchLogRecordProcessor{
Exporter: LogRecordExporter{
OTLP: &OTLP{
Protocol: ptr("http/protobuf"),
Endpoint: ptr("localhost:4317"),
Compression: ptr("gzip"),
Timeout: ptr(1000),
Certificate: ptr(filepath.Join("..", "testdata", "bad_cert.crt")),
},
},
},
},
wantErr: fmt.Errorf("could not create client tls credentials: %w", errors.New("failed to append certificate to the cert pool")),
},
{
name: "batch/otlp-http-exporter-with-path",
processor: LogRecordProcessor{
Expand Down
17 changes: 17 additions & 0 deletions config/v0.3.0/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"google.golang.org/grpc/credentials"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
Expand Down Expand Up @@ -181,6 +182,14 @@ func otlpHTTPMetricExporter(ctx context.Context, otlpConfig *OTLPMetric) (sdkmet
}
}

if otlpConfig.Certificate != nil {
creds, err := createTLSConfig(*otlpConfig.Certificate)
if err != nil {
return nil, fmt.Errorf("could not create client tls credentials: %w", err)
}
opts = append(opts, otlpmetrichttp.WithTLSClientConfig(creds))
}

return otlpmetrichttp.New(ctx, opts...)
}

Expand Down Expand Up @@ -236,6 +245,14 @@ func otlpGRPCMetricExporter(ctx context.Context, otlpConfig *OTLPMetric) (sdkmet
}
}

if otlpConfig.Certificate != nil {
creds, err := credentials.NewClientTLSFromFile(*otlpConfig.Certificate, "")
if err != nil {
return nil, fmt.Errorf("could not create client tls credentials: %w", err)
}
opts = append(opts, otlpmetricgrpc.WithTLSCredentials(creds))
}

return otlpmetricgrpc.New(ctx, opts...)
}

Expand Down
69 changes: 69 additions & 0 deletions config/v0.3.0/metric_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"net/url"
"path/filepath"
"reflect"
"testing"
"time"
Expand Down Expand Up @@ -215,6 +216,40 @@ func TestReader(t *testing.T) {
},
wantReader: sdkmetric.NewPeriodicReader(otlpGRPCExporter),
},
{
name: "periodic/otlp-grpc-good-ca-certificate",
reader: MetricReader{
Periodic: &PeriodicMetricReader{
Exporter: PushMetricExporter{
OTLP: &OTLPMetric{
Protocol: ptr("grpc"),
Endpoint: ptr("https://localhost:4317"),
Compression: ptr("gzip"),
Timeout: ptr(1000),
Certificate: ptr(filepath.Join("..", "testdata", "ca.crt")),
},
},
},
},
wantReader: sdkmetric.NewPeriodicReader(otlpGRPCExporter),
},
{
name: "periodic/otlp-grpc-bad-ca-certificate",
reader: MetricReader{
Periodic: &PeriodicMetricReader{
Exporter: PushMetricExporter{
OTLP: &OTLPMetric{
Protocol: ptr("grpc"),
Endpoint: ptr("https://localhost:4317"),
Compression: ptr("gzip"),
Timeout: ptr(1000),
Certificate: ptr(filepath.Join("..", "testdata", "bad_cert.crt")),
},
},
},
},
wantErr: fmt.Errorf("could not create client tls credentials: %w", errors.New("credentials: failed to append certificates")),
},
{
name: "periodic/otlp-grpc-exporter-no-endpoint",
reader: MetricReader{
Expand Down Expand Up @@ -408,6 +443,40 @@ func TestReader(t *testing.T) {
},
wantReader: sdkmetric.NewPeriodicReader(otlpHTTPExporter),
},
{
name: "periodic/otlp-http-good-ca-certificate",
reader: MetricReader{
Periodic: &PeriodicMetricReader{
Exporter: PushMetricExporter{
OTLP: &OTLPMetric{
Protocol: ptr("http/protobuf"),
Endpoint: ptr("https://localhost:4317"),
Compression: ptr("gzip"),
Timeout: ptr(1000),
Certificate: ptr(filepath.Join("..", "testdata", "ca.crt")),
},
},
},
},
wantReader: sdkmetric.NewPeriodicReader(otlpHTTPExporter),
},
{
name: "periodic/otlp-http-bad-ca-certificate",
reader: MetricReader{
Periodic: &PeriodicMetricReader{
Exporter: PushMetricExporter{
OTLP: &OTLPMetric{
Protocol: ptr("http/protobuf"),
Endpoint: ptr("https://localhost:4317"),
Compression: ptr("gzip"),
Timeout: ptr(1000),
Certificate: ptr(filepath.Join("..", "testdata", "bad_cert.crt")),
},
},
},
},
wantErr: fmt.Errorf("could not create client tls credentials: %w", errors.New("failed to append certificate to the cert pool")),
},
{
name: "periodic/otlp-http-exporter-with-path",
reader: MetricReader{
Expand Down
18 changes: 18 additions & 0 deletions config/v0.3.0/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"net/url"
"time"

"google.golang.org/grpc/credentials"

"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
Expand Down Expand Up @@ -127,6 +129,14 @@ func otlpGRPCSpanExporter(ctx context.Context, otlpConfig *OTLP) (sdktrace.SpanE
opts = append(opts, otlptracegrpc.WithHeaders(toStringMap(otlpConfig.Headers)))
}

if otlpConfig.Certificate != nil {
creds, err := credentials.NewClientTLSFromFile(*otlpConfig.Certificate, "")
if err != nil {
return nil, fmt.Errorf("could not create client tls credentials: %w", err)
}
opts = append(opts, otlptracegrpc.WithTLSCredentials(creds))
}

return otlptracegrpc.New(ctx, opts...)
}

Expand Down Expand Up @@ -164,6 +174,14 @@ func otlpHTTPSpanExporter(ctx context.Context, otlpConfig *OTLP) (sdktrace.SpanE
opts = append(opts, otlptracehttp.WithHeaders(toStringMap(otlpConfig.Headers)))
}

if otlpConfig.Certificate != nil {
creds, err := createTLSConfig(*otlpConfig.Certificate)
if err != nil {
return nil, fmt.Errorf("could not create client tls credentials: %w", err)
}
opts = append(opts, otlptracehttp.WithTLSClientConfig(creds))
}

return otlptracehttp.New(ctx, opts...)
}

Expand Down
Loading

0 comments on commit 741c472

Please sign in to comment.