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

config: add support for certificate configuration #6376

Merged
merged 13 commits into from
Dec 12, 2024
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.
dmathieu marked this conversation as resolved.
Show resolved Hide resolved
- 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 @@

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

"gopkg.in/yaml.v3"

Expand Down Expand Up @@ -155,3 +158,20 @@
}
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
}

Check warning on line 168 in config/v0.3.0/config.go

View check run for this annotation

Codecov / codecov/patch

config/v0.3.0/config.go#L167-L168

Added lines #L167 - L168 were not covered by tests
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
Loading