Skip to content

Commit b5d6715

Browse files
authored
test(otelconf): replace expiring TLS fixtures with runtime certs (#8835)
1 parent 15d661a commit b5d6715

11 files changed

Lines changed: 308 additions & 63 deletions

File tree

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
// Package testtls provides runtime-generated TLS materials for tests.
5+
package testtls // import "go.opentelemetry.io/contrib/otelconf/internal/testtls"
6+
7+
import (
8+
"crypto/rand"
9+
"crypto/rsa"
10+
"crypto/x509"
11+
"crypto/x509/pkix"
12+
"encoding/pem"
13+
"math/big"
14+
"net"
15+
"os"
16+
"path/filepath"
17+
"time"
18+
)
19+
20+
// Material contains generated CA, server, and client cert file paths.
21+
type Material struct {
22+
CACertPath string
23+
CAKeyPath string
24+
ServerCertPath string
25+
ServerKeyPath string
26+
ClientCertPath string
27+
ClientKeyPath string
28+
}
29+
30+
// TB is the minimal testing surface needed by this helper.
31+
type TB interface {
32+
Helper()
33+
TempDir() string
34+
Fatalf(format string, args ...any)
35+
}
36+
37+
// Write generates mTLS assets under t.TempDir so tests do not depend on expiring fixtures.
38+
func Write(t TB) Material {
39+
t.Helper()
40+
41+
dir := t.TempDir()
42+
now := time.Now().UTC()
43+
44+
caKey := mustRSAKey(t)
45+
caTemplate := &x509.Certificate{
46+
SerialNumber: big.NewInt(1),
47+
Subject: pkix.Name{
48+
Country: []string{"US"},
49+
Province: []string{"California"},
50+
Locality: []string{"San Francisco"},
51+
Organization: []string{"OpenTelemetry Test CA"},
52+
CommonName: "otelconf test ca",
53+
},
54+
NotBefore: now.Add(-time.Hour),
55+
NotAfter: now.AddDate(10, 0, 0),
56+
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
57+
BasicConstraintsValid: true,
58+
IsCA: true,
59+
}
60+
caDER := mustCertificate(t, caTemplate, caTemplate, &caKey.PublicKey, caKey)
61+
62+
serverKey := mustRSAKey(t)
63+
serverTemplate := &x509.Certificate{
64+
SerialNumber: big.NewInt(now.UnixNano()),
65+
Subject: pkix.Name{
66+
Country: []string{"US"},
67+
Province: []string{"California"},
68+
Locality: []string{"San Francisco"},
69+
Organization: []string{"OpenTelemetry Tests"},
70+
CommonName: "localhost",
71+
},
72+
DNSNames: []string{"localhost"},
73+
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1)},
74+
NotBefore: now.Add(-time.Hour),
75+
NotAfter: now.AddDate(2, 0, 0),
76+
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
77+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
78+
BasicConstraintsValid: true,
79+
}
80+
serverDER := mustCertificate(t, serverTemplate, caTemplate, &serverKey.PublicKey, caKey)
81+
82+
clientKey := mustRSAKey(t)
83+
clientTemplate := &x509.Certificate{
84+
SerialNumber: big.NewInt(now.UnixNano() + 1),
85+
Subject: pkix.Name{
86+
Country: []string{"US"},
87+
Province: []string{"California"},
88+
Locality: []string{"San Francisco"},
89+
Organization: []string{"OpenTelemetry Tests"},
90+
CommonName: "otelconf test client",
91+
},
92+
NotBefore: now.Add(-time.Hour),
93+
NotAfter: now.AddDate(2, 0, 0),
94+
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
95+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
96+
BasicConstraintsValid: true,
97+
}
98+
clientDER := mustCertificate(t, clientTemplate, caTemplate, &clientKey.PublicKey, caKey)
99+
100+
m := Material{
101+
CACertPath: filepath.Join(dir, "ca.crt"),
102+
CAKeyPath: filepath.Join(dir, "ca.key"),
103+
ServerCertPath: filepath.Join(dir, "server.crt"),
104+
ServerKeyPath: filepath.Join(dir, "server.key"),
105+
ClientCertPath: filepath.Join(dir, "client.crt"),
106+
ClientKeyPath: filepath.Join(dir, "client.key"),
107+
}
108+
writeCert(t, m.CACertPath, caDER)
109+
writeKey(t, m.CAKeyPath, caKey)
110+
writeCert(t, m.ServerCertPath, serverDER)
111+
writeKey(t, m.ServerKeyPath, serverKey)
112+
writeCert(t, m.ClientCertPath, clientDER)
113+
writeKey(t, m.ClientKeyPath, clientKey)
114+
return m
115+
}
116+
117+
func mustRSAKey(t TB) *rsa.PrivateKey {
118+
t.Helper()
119+
key, err := rsa.GenerateKey(rand.Reader, 2048)
120+
if err != nil {
121+
t.Fatalf("generate rsa key: %v", err)
122+
}
123+
return key
124+
}
125+
126+
func mustCertificate(t TB, template, parent *x509.Certificate, publicKey *rsa.PublicKey, signer *rsa.PrivateKey) []byte {
127+
t.Helper()
128+
der, err := x509.CreateCertificate(rand.Reader, template, parent, publicKey, signer)
129+
if err != nil {
130+
t.Fatalf("create certificate: %v", err)
131+
}
132+
return der
133+
}
134+
135+
func writeCert(t TB, path string, der []byte) {
136+
t.Helper()
137+
block := &pem.Block{Type: "CERTIFICATE", Bytes: der}
138+
if err := os.WriteFile(path, pem.EncodeToMemory(block), 0o600); err != nil {
139+
t.Fatalf("write cert %s: %v", path, err)
140+
}
141+
}
142+
143+
func writeKey(t TB, path string, key *rsa.PrivateKey) {
144+
t.Helper()
145+
block := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}
146+
if err := os.WriteFile(path, pem.EncodeToMemory(block), 0o600); err != nil {
147+
t.Fatalf("write key %s: %v", path, err)
148+
}
149+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package testtls
5+
6+
import (
7+
"crypto/tls"
8+
"crypto/x509"
9+
"encoding/pem"
10+
"net"
11+
"os"
12+
"testing"
13+
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
func TestWrite(t *testing.T) {
18+
material := Write(t)
19+
20+
caCert := readCertificate(t, material.CACertPath)
21+
serverCert := readCertificate(t, material.ServerCertPath)
22+
clientCert := readCertificate(t, material.ClientCertPath)
23+
24+
_, err := tls.LoadX509KeyPair(material.ServerCertPath, material.ServerKeyPath)
25+
require.NoError(t, err)
26+
27+
_, err = tls.LoadX509KeyPair(material.ClientCertPath, material.ClientKeyPath)
28+
require.NoError(t, err)
29+
30+
require.True(t, caCert.IsCA)
31+
require.Equal(t, "otelconf test ca", caCert.Subject.CommonName)
32+
require.Equal(t, "localhost", serverCert.Subject.CommonName)
33+
require.Equal(t, "otelconf test client", clientCert.Subject.CommonName)
34+
require.Equal(t, []string{"localhost"}, serverCert.DNSNames)
35+
require.Len(t, serverCert.IPAddresses, 1)
36+
require.True(t, serverCert.IPAddresses[0].Equal(net.IPv4(127, 0, 0, 1)))
37+
require.Equal(t, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, serverCert.ExtKeyUsage)
38+
require.Equal(t, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, clientCert.ExtKeyUsage)
39+
40+
roots := x509.NewCertPool()
41+
roots.AddCert(caCert)
42+
43+
_, err = serverCert.Verify(x509.VerifyOptions{
44+
DNSName: "localhost",
45+
Roots: roots,
46+
})
47+
require.NoError(t, err)
48+
49+
_, err = clientCert.Verify(x509.VerifyOptions{
50+
Roots: roots,
51+
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
52+
})
53+
require.NoError(t, err)
54+
}
55+
56+
func readCertificate(t *testing.T, path string) *x509.Certificate {
57+
t.Helper()
58+
59+
data, err := os.ReadFile(path)
60+
require.NoError(t, err)
61+
62+
block, _ := pem.Decode(data)
63+
require.NotNil(t, block)
64+
require.Equal(t, "CERTIFICATE", block.Type)
65+
66+
cert, err := x509.ParseCertificate(block.Bytes)
67+
require.NoError(t, err)
68+
return cert
69+
}

otelconf/log_test.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import (
3333
collogpb "go.opentelemetry.io/proto/otlp/collector/logs/v1"
3434
"google.golang.org/grpc"
3535
"google.golang.org/grpc/credentials"
36+
37+
"go.opentelemetry.io/contrib/otelconf/internal/testtls"
3638
)
3739

3840
func TestLoggerProvider(t *testing.T) {
@@ -820,6 +822,7 @@ func Test_otlpGRPCLogExporter(t *testing.T) {
820822
// TODO (#8115): Fix the flakiness on Windows and MacOS.
821823
t.Skip("Test is flaky on Windows and MacOS.")
822824
}
825+
material := testtls.Write(t)
823826
type args struct {
824827
ctx context.Context
825828
otlpConfig *OTLPGrpcExporter
@@ -856,7 +859,7 @@ func Test_otlpGRPCLogExporter(t *testing.T) {
856859
Compression: ptr("gzip"),
857860
Timeout: ptr(5000),
858861
Tls: &GrpcTls{
859-
CaFile: ptr("testdata/server-certs/server.crt"),
862+
CaFile: ptr(material.CACertPath),
860863
},
861864
Headers: []NameStringValuePair{
862865
{Name: "test", Value: ptr("test1")},
@@ -865,7 +868,7 @@ func Test_otlpGRPCLogExporter(t *testing.T) {
865868
},
866869
grpcServerOpts: func() ([]grpc.ServerOption, error) {
867870
opts := []grpc.ServerOption{}
868-
tlsCreds, err := credentials.NewServerTLSFromFile("testdata/server-certs/server.crt", "testdata/server-certs/server.key")
871+
tlsCreds, err := credentials.NewServerTLSFromFile(material.ServerCertPath, material.ServerKeyPath)
869872
if err != nil {
870873
return nil, err
871874
}
@@ -881,9 +884,9 @@ func Test_otlpGRPCLogExporter(t *testing.T) {
881884
Compression: ptr("gzip"),
882885
Timeout: ptr(5000),
883886
Tls: &GrpcTls{
884-
CaFile: ptr("testdata/server-certs/server.crt"),
885-
KeyFile: ptr("testdata/client-certs/client.key"),
886-
CertFile: ptr("testdata/client-certs/client.crt"),
887+
CaFile: ptr(material.CACertPath),
888+
KeyFile: ptr(material.ClientKeyPath),
889+
CertFile: ptr(material.ClientCertPath),
887890
},
888891
Headers: []NameStringValuePair{
889892
{Name: "test", Value: ptr("test1")},
@@ -892,11 +895,11 @@ func Test_otlpGRPCLogExporter(t *testing.T) {
892895
},
893896
grpcServerOpts: func() ([]grpc.ServerOption, error) {
894897
opts := []grpc.ServerOption{}
895-
cert, err := tls.LoadX509KeyPair("testdata/server-certs/server.crt", "testdata/server-certs/server.key")
898+
cert, err := tls.LoadX509KeyPair(material.ServerCertPath, material.ServerKeyPath)
896899
if err != nil {
897900
return nil, err
898901
}
899-
caCert, err := os.ReadFile("testdata/ca.crt")
902+
caCert, err := os.ReadFile(material.CACertPath)
900903
if err != nil {
901904
return nil, err
902905
}

otelconf/metric_test.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ import (
3535
v1 "go.opentelemetry.io/proto/otlp/collector/metrics/v1"
3636
"google.golang.org/grpc"
3737
"google.golang.org/grpc/credentials"
38+
39+
"go.opentelemetry.io/contrib/otelconf/internal/testtls"
3840
)
3941

4042
func TestMeterProvider(t *testing.T) {
@@ -1294,6 +1296,7 @@ func Test_otlpGRPCMetricExporter(t *testing.T) {
12941296
// TODO (#8115): Fix the flakiness on Windows and MacOS.
12951297
t.Skip("Test is flaky on Windows and MacOS.")
12961298
}
1299+
material := testtls.Write(t)
12971300
type args struct {
12981301
ctx context.Context
12991302
otlpConfig *OTLPGrpcMetricExporter
@@ -1330,7 +1333,7 @@ func Test_otlpGRPCMetricExporter(t *testing.T) {
13301333
Compression: ptr("gzip"),
13311334
Timeout: ptr(5000),
13321335
Tls: &GrpcTls{
1333-
CaFile: ptr("testdata/server-certs/server.crt"),
1336+
CaFile: ptr(material.CACertPath),
13341337
},
13351338
Headers: []NameStringValuePair{
13361339
{Name: "test", Value: ptr("test1")},
@@ -1339,7 +1342,7 @@ func Test_otlpGRPCMetricExporter(t *testing.T) {
13391342
},
13401343
grpcServerOpts: func() ([]grpc.ServerOption, error) {
13411344
opts := []grpc.ServerOption{}
1342-
tlsCreds, err := credentials.NewServerTLSFromFile("testdata/server-certs/server.crt", "testdata/server-certs/server.key")
1345+
tlsCreds, err := credentials.NewServerTLSFromFile(material.ServerCertPath, material.ServerKeyPath)
13431346
if err != nil {
13441347
return nil, err
13451348
}
@@ -1355,9 +1358,9 @@ func Test_otlpGRPCMetricExporter(t *testing.T) {
13551358
Compression: ptr("gzip"),
13561359
Timeout: ptr(5000),
13571360
Tls: &GrpcTls{
1358-
CaFile: ptr("testdata/server-certs/server.crt"),
1359-
KeyFile: ptr("testdata/client-certs/client.key"),
1360-
CertFile: ptr("testdata/client-certs/client.crt"),
1361+
CaFile: ptr(material.CACertPath),
1362+
KeyFile: ptr(material.ClientKeyPath),
1363+
CertFile: ptr(material.ClientCertPath),
13611364
},
13621365
Headers: []NameStringValuePair{
13631366
{Name: "test", Value: ptr("test1")},
@@ -1366,11 +1369,11 @@ func Test_otlpGRPCMetricExporter(t *testing.T) {
13661369
},
13671370
grpcServerOpts: func() ([]grpc.ServerOption, error) {
13681371
opts := []grpc.ServerOption{}
1369-
cert, err := tls.LoadX509KeyPair("testdata/server-certs/server.crt", "testdata/server-certs/server.key")
1372+
cert, err := tls.LoadX509KeyPair(material.ServerCertPath, material.ServerKeyPath)
13701373
if err != nil {
13711374
return nil, err
13721375
}
1373-
caCert, err := os.ReadFile("testdata/ca.crt")
1376+
caCert, err := os.ReadFile(material.CACertPath)
13741377
if err != nil {
13751378
return nil, err
13761379
}

0 commit comments

Comments
 (0)