Skip to content
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
149 changes: 149 additions & 0 deletions otelconf/internal/testtls/testtls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

// Package testtls provides runtime-generated TLS materials for tests.
package testtls // import "go.opentelemetry.io/contrib/otelconf/internal/testtls"

import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"net"
"os"
"path/filepath"
"time"
)

// Material contains generated CA, server, and client cert file paths.
type Material struct {
CACertPath string
CAKeyPath string
ServerCertPath string
ServerKeyPath string
ClientCertPath string
ClientKeyPath string
}

// TB is the minimal testing surface needed by this helper.
type TB interface {
Helper()
TempDir() string
Fatalf(format string, args ...any)
}

// Write generates mTLS assets under t.TempDir so tests do not depend on expiring fixtures.
func Write(t TB) Material {
t.Helper()

dir := t.TempDir()
now := time.Now().UTC()

caKey := mustRSAKey(t)
caTemplate := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
Country: []string{"US"},
Province: []string{"California"},
Locality: []string{"San Francisco"},
Organization: []string{"OpenTelemetry Test CA"},
CommonName: "otelconf test ca",
},
NotBefore: now.Add(-time.Hour),
NotAfter: now.AddDate(10, 0, 0),
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
BasicConstraintsValid: true,
IsCA: true,
}
caDER := mustCertificate(t, caTemplate, caTemplate, &caKey.PublicKey, caKey)

serverKey := mustRSAKey(t)
serverTemplate := &x509.Certificate{
SerialNumber: big.NewInt(now.UnixNano()),
Subject: pkix.Name{
Country: []string{"US"},
Province: []string{"California"},
Locality: []string{"San Francisco"},
Organization: []string{"OpenTelemetry Tests"},
CommonName: "localhost",
},
DNSNames: []string{"localhost"},
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1)},
NotBefore: now.Add(-time.Hour),
NotAfter: now.AddDate(2, 0, 0),
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
serverDER := mustCertificate(t, serverTemplate, caTemplate, &serverKey.PublicKey, caKey)

clientKey := mustRSAKey(t)
clientTemplate := &x509.Certificate{
SerialNumber: big.NewInt(now.UnixNano() + 1),
Subject: pkix.Name{
Country: []string{"US"},
Province: []string{"California"},
Locality: []string{"San Francisco"},
Organization: []string{"OpenTelemetry Tests"},
CommonName: "otelconf test client",
},
NotBefore: now.Add(-time.Hour),
NotAfter: now.AddDate(2, 0, 0),
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
BasicConstraintsValid: true,
}
clientDER := mustCertificate(t, clientTemplate, caTemplate, &clientKey.PublicKey, caKey)

m := Material{
CACertPath: filepath.Join(dir, "ca.crt"),
CAKeyPath: filepath.Join(dir, "ca.key"),
ServerCertPath: filepath.Join(dir, "server.crt"),
ServerKeyPath: filepath.Join(dir, "server.key"),
ClientCertPath: filepath.Join(dir, "client.crt"),
ClientKeyPath: filepath.Join(dir, "client.key"),
}
writeCert(t, m.CACertPath, caDER)
writeKey(t, m.CAKeyPath, caKey)
writeCert(t, m.ServerCertPath, serverDER)
writeKey(t, m.ServerKeyPath, serverKey)
writeCert(t, m.ClientCertPath, clientDER)
writeKey(t, m.ClientKeyPath, clientKey)
return m
}

func mustRSAKey(t TB) *rsa.PrivateKey {
t.Helper()
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatalf("generate rsa key: %v", err)
}
return key
}

func mustCertificate(t TB, template, parent *x509.Certificate, publicKey *rsa.PublicKey, signer *rsa.PrivateKey) []byte {
t.Helper()
der, err := x509.CreateCertificate(rand.Reader, template, parent, publicKey, signer)
if err != nil {
t.Fatalf("create certificate: %v", err)
}
return der
}

func writeCert(t TB, path string, der []byte) {
t.Helper()
block := &pem.Block{Type: "CERTIFICATE", Bytes: der}
if err := os.WriteFile(path, pem.EncodeToMemory(block), 0o600); err != nil {
t.Fatalf("write cert %s: %v", path, err)
}
}

func writeKey(t TB, path string, key *rsa.PrivateKey) {
t.Helper()
block := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}
if err := os.WriteFile(path, pem.EncodeToMemory(block), 0o600); err != nil {
t.Fatalf("write key %s: %v", path, err)
}
}
69 changes: 69 additions & 0 deletions otelconf/internal/testtls/testtls_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package testtls

import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"net"
"os"
"testing"

"github.com/stretchr/testify/require"
)

func TestWrite(t *testing.T) {
material := Write(t)

caCert := readCertificate(t, material.CACertPath)
serverCert := readCertificate(t, material.ServerCertPath)
clientCert := readCertificate(t, material.ClientCertPath)

_, err := tls.LoadX509KeyPair(material.ServerCertPath, material.ServerKeyPath)
require.NoError(t, err)

_, err = tls.LoadX509KeyPair(material.ClientCertPath, material.ClientKeyPath)
require.NoError(t, err)

require.True(t, caCert.IsCA)
require.Equal(t, "otelconf test ca", caCert.Subject.CommonName)
require.Equal(t, "localhost", serverCert.Subject.CommonName)
require.Equal(t, "otelconf test client", clientCert.Subject.CommonName)
require.Equal(t, []string{"localhost"}, serverCert.DNSNames)
require.Len(t, serverCert.IPAddresses, 1)
require.True(t, serverCert.IPAddresses[0].Equal(net.IPv4(127, 0, 0, 1)))
require.Equal(t, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, serverCert.ExtKeyUsage)
require.Equal(t, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, clientCert.ExtKeyUsage)

roots := x509.NewCertPool()
roots.AddCert(caCert)

_, err = serverCert.Verify(x509.VerifyOptions{
DNSName: "localhost",
Roots: roots,
})
require.NoError(t, err)

_, err = clientCert.Verify(x509.VerifyOptions{
Roots: roots,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
})
require.NoError(t, err)
}

func readCertificate(t *testing.T, path string) *x509.Certificate {
t.Helper()

data, err := os.ReadFile(path)
require.NoError(t, err)

block, _ := pem.Decode(data)
require.NotNil(t, block)
require.Equal(t, "CERTIFICATE", block.Type)

cert, err := x509.ParseCertificate(block.Bytes)
require.NoError(t, err)
return cert
}
17 changes: 10 additions & 7 deletions otelconf/log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import (
collogpb "go.opentelemetry.io/proto/otlp/collector/logs/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"

"go.opentelemetry.io/contrib/otelconf/internal/testtls"
)

func TestLoggerProvider(t *testing.T) {
Expand Down Expand Up @@ -820,6 +822,7 @@ func Test_otlpGRPCLogExporter(t *testing.T) {
// TODO (#8115): Fix the flakiness on Windows and MacOS.
t.Skip("Test is flaky on Windows and MacOS.")
}
material := testtls.Write(t)
type args struct {
ctx context.Context
otlpConfig *OTLPGrpcExporter
Expand Down Expand Up @@ -856,7 +859,7 @@ func Test_otlpGRPCLogExporter(t *testing.T) {
Compression: ptr("gzip"),
Timeout: ptr(5000),
Tls: &GrpcTls{
CaFile: ptr("testdata/server-certs/server.crt"),
CaFile: ptr(material.CACertPath),
},
Headers: []NameStringValuePair{
{Name: "test", Value: ptr("test1")},
Expand All @@ -865,7 +868,7 @@ func Test_otlpGRPCLogExporter(t *testing.T) {
},
grpcServerOpts: func() ([]grpc.ServerOption, error) {
opts := []grpc.ServerOption{}
tlsCreds, err := credentials.NewServerTLSFromFile("testdata/server-certs/server.crt", "testdata/server-certs/server.key")
tlsCreds, err := credentials.NewServerTLSFromFile(material.ServerCertPath, material.ServerKeyPath)
if err != nil {
return nil, err
}
Expand All @@ -881,9 +884,9 @@ func Test_otlpGRPCLogExporter(t *testing.T) {
Compression: ptr("gzip"),
Timeout: ptr(5000),
Tls: &GrpcTls{
CaFile: ptr("testdata/server-certs/server.crt"),
KeyFile: ptr("testdata/client-certs/client.key"),
CertFile: ptr("testdata/client-certs/client.crt"),
CaFile: ptr(material.CACertPath),
KeyFile: ptr(material.ClientKeyPath),
CertFile: ptr(material.ClientCertPath),
},
Headers: []NameStringValuePair{
{Name: "test", Value: ptr("test1")},
Expand All @@ -892,11 +895,11 @@ func Test_otlpGRPCLogExporter(t *testing.T) {
},
grpcServerOpts: func() ([]grpc.ServerOption, error) {
opts := []grpc.ServerOption{}
cert, err := tls.LoadX509KeyPair("testdata/server-certs/server.crt", "testdata/server-certs/server.key")
cert, err := tls.LoadX509KeyPair(material.ServerCertPath, material.ServerKeyPath)
if err != nil {
return nil, err
}
caCert, err := os.ReadFile("testdata/ca.crt")
caCert, err := os.ReadFile(material.CACertPath)
if err != nil {
return nil, err
}
Expand Down
17 changes: 10 additions & 7 deletions otelconf/metric_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import (
v1 "go.opentelemetry.io/proto/otlp/collector/metrics/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"

"go.opentelemetry.io/contrib/otelconf/internal/testtls"
)

func TestMeterProvider(t *testing.T) {
Expand Down Expand Up @@ -1294,6 +1296,7 @@ func Test_otlpGRPCMetricExporter(t *testing.T) {
// TODO (#8115): Fix the flakiness on Windows and MacOS.
t.Skip("Test is flaky on Windows and MacOS.")
}
material := testtls.Write(t)
type args struct {
ctx context.Context
otlpConfig *OTLPGrpcMetricExporter
Expand Down Expand Up @@ -1330,7 +1333,7 @@ func Test_otlpGRPCMetricExporter(t *testing.T) {
Compression: ptr("gzip"),
Timeout: ptr(5000),
Tls: &GrpcTls{
CaFile: ptr("testdata/server-certs/server.crt"),
CaFile: ptr(material.CACertPath),
},
Headers: []NameStringValuePair{
{Name: "test", Value: ptr("test1")},
Expand All @@ -1339,7 +1342,7 @@ func Test_otlpGRPCMetricExporter(t *testing.T) {
},
grpcServerOpts: func() ([]grpc.ServerOption, error) {
opts := []grpc.ServerOption{}
tlsCreds, err := credentials.NewServerTLSFromFile("testdata/server-certs/server.crt", "testdata/server-certs/server.key")
tlsCreds, err := credentials.NewServerTLSFromFile(material.ServerCertPath, material.ServerKeyPath)
if err != nil {
return nil, err
}
Expand All @@ -1355,9 +1358,9 @@ func Test_otlpGRPCMetricExporter(t *testing.T) {
Compression: ptr("gzip"),
Timeout: ptr(5000),
Tls: &GrpcTls{
CaFile: ptr("testdata/server-certs/server.crt"),
KeyFile: ptr("testdata/client-certs/client.key"),
CertFile: ptr("testdata/client-certs/client.crt"),
CaFile: ptr(material.CACertPath),
KeyFile: ptr(material.ClientKeyPath),
CertFile: ptr(material.ClientCertPath),
},
Headers: []NameStringValuePair{
{Name: "test", Value: ptr("test1")},
Expand All @@ -1366,11 +1369,11 @@ func Test_otlpGRPCMetricExporter(t *testing.T) {
},
grpcServerOpts: func() ([]grpc.ServerOption, error) {
opts := []grpc.ServerOption{}
cert, err := tls.LoadX509KeyPair("testdata/server-certs/server.crt", "testdata/server-certs/server.key")
cert, err := tls.LoadX509KeyPair(material.ServerCertPath, material.ServerKeyPath)
if err != nil {
return nil, err
}
caCert, err := os.ReadFile("testdata/ca.crt")
caCert, err := os.ReadFile(material.CACertPath)
if err != nil {
return nil, err
}
Expand Down
Loading
Loading