blob: 19baf94b48e295525c617eae294e48cd56153029 [file] [log] [blame]
Serge Bazanski52538842021-08-11 16:22:41 +02001package pki
2
3import (
4 "bytes"
5 "context"
6 "crypto/ed25519"
7 "crypto/rand"
8 "crypto/x509"
9 "testing"
10
Lorenz Brund13c1c62022-03-30 19:58:58 +020011 "go.etcd.io/etcd/client/pkg/v3/testutil"
12 "go.etcd.io/etcd/tests/v3/integration"
Serge Bazanski98e05e12023-04-05 12:44:14 +020013 "go.uber.org/zap"
14
15 "source.monogon.dev/metropolis/pkg/logtree"
Serge Bazanski52538842021-08-11 16:22:41 +020016)
17
18// TestManaged ensures Managed Certificates work, including re-ensuring
19// certificates with the same data and issuing subordinate certificates.
20func TestManaged(t *testing.T) {
Serge Bazanski98e05e12023-04-05 12:44:14 +020021 lt := logtree.New()
22 logtree.PipeAllToTest(t, lt)
Lorenz Brund13c1c62022-03-30 19:58:58 +020023 tb, cancel := testutil.NewTestingTBProthesis("pki-managed")
24 defer cancel()
25 cluster := integration.NewClusterV3(tb, &integration.ClusterConfig{
Serge Bazanski52538842021-08-11 16:22:41 +020026 Size: 1,
Serge Bazanski98e05e12023-04-05 12:44:14 +020027 LoggerBuilder: func(memberName string) *zap.Logger {
28 dn := logtree.DN("etcd." + memberName)
29 return logtree.Zapify(lt.MustLeveledFor(dn), zap.WarnLevel)
30 },
Serge Bazanski52538842021-08-11 16:22:41 +020031 })
32 cl := cluster.Client(0)
Lorenz Brund13c1c62022-03-30 19:58:58 +020033 defer cluster.Terminate(tb)
Serge Bazanski52538842021-08-11 16:22:41 +020034 ctx, ctxC := context.WithCancel(context.Background())
35 defer ctxC()
36 ns := Namespaced("/test-managed/")
37
38 // Test CA certificate issuance.
39 ca := &Certificate{
40 Namespace: &ns,
41 Issuer: SelfSigned,
42 Name: "ca",
43 Template: CA("Test CA"),
44 }
45 caBytes, err := ca.Ensure(ctx, cl)
46 if err != nil {
47 t.Fatalf("Failed to Ensure CA: %v", err)
48 }
49 caCert, err := x509.ParseCertificate(caBytes)
50 if err != nil {
51 t.Fatalf("Failed to parse newly emited CA cert: %v", err)
52 }
53 if !caCert.IsCA {
54 t.Errorf("Newly emitted CA cert is not CA")
55 }
56 if ca.PublicKey == nil {
57 t.Errorf("Newly emitted CA cert has no public key")
58 }
59 if ca.PrivateKey == nil {
60 t.Errorf("Newly emitted CA cert has no public key")
61 }
62
63 // Re-emitting CA certificate with same parameters should return exact same
64 // data.
65 ca2 := &Certificate{
66 Namespace: &ns,
67 Issuer: SelfSigned,
68 Name: "ca",
69 Template: CA("Test CA"),
70 }
71 caBytes2, err := ca2.Ensure(ctx, cl)
72 if err != nil {
73 t.Fatalf("Failed to re-Ensure CA: %v", err)
74 }
75 if !bytes.Equal(caBytes, caBytes2) {
76 t.Errorf("New CA has different x509 certificate")
77 }
78 if !bytes.Equal(ca.PublicKey, ca2.PublicKey) {
79 t.Errorf("New CA has different public key")
80 }
81 if !bytes.Equal(ca.PrivateKey, ca2.PrivateKey) {
82 t.Errorf("New CA has different private key")
83 }
84
85 // Emitting a subordinate certificate should work.
86 client := &Certificate{
87 Namespace: &ns,
88 Issuer: ca2,
89 Name: "client",
90 Template: Client("foo", nil),
91 }
92 clientBytes, err := client.Ensure(ctx, cl)
93 if err != nil {
94 t.Fatalf("Failed to ensure client certificate: %v", err)
95 }
96 clientCert, err := x509.ParseCertificate(clientBytes)
97 if err != nil {
98 t.Fatalf("Failed to parse newly emitted client certificate: %v", err)
99 }
100 if clientCert.IsCA {
101 t.Errorf("New client cert is CA")
102 }
103 if want, got := "foo", clientCert.Subject.CommonName; want != got {
104 t.Errorf("New client CN should be %q, got %q", want, got)
105 }
106 if want, got := caCert.Subject.String(), clientCert.Issuer.String(); want != got {
107 t.Errorf("New client issuer should be %q, got %q", want, got)
108 }
109}
110
111// TestExternal ensures External certificates work correctly, including
112// re-Ensuring certificates with the same public key, and attempting to re-issue
113// the same certificate with a different public key (which should fail).
114func TestExternal(t *testing.T) {
Serge Bazanski98e05e12023-04-05 12:44:14 +0200115 lt := logtree.New()
116 logtree.PipeAllToTest(t, lt)
Lorenz Brund13c1c62022-03-30 19:58:58 +0200117 tb, cancel := testutil.NewTestingTBProthesis("pki-managed")
118 defer cancel()
119 cluster := integration.NewClusterV3(tb, &integration.ClusterConfig{
Serge Bazanski52538842021-08-11 16:22:41 +0200120 Size: 1,
Serge Bazanski98e05e12023-04-05 12:44:14 +0200121 LoggerBuilder: func(memberName string) *zap.Logger {
122 dn := logtree.DN("etcd." + memberName)
123 return logtree.Zapify(lt.MustLeveledFor(dn), zap.WarnLevel)
124 },
Serge Bazanski52538842021-08-11 16:22:41 +0200125 })
126 cl := cluster.Client(0)
Lorenz Brund13c1c62022-03-30 19:58:58 +0200127 defer cluster.Terminate(tb)
Serge Bazanski52538842021-08-11 16:22:41 +0200128 ctx, ctxC := context.WithCancel(context.Background())
129 defer ctxC()
130 ns := Namespaced("/test-external/")
131
132 ca := &Certificate{
133 Namespace: &ns,
134 Issuer: SelfSigned,
135 Name: "ca",
136 Template: CA("Test CA"),
137 }
138
139 // Issuing an external certificate should work.
140 pk, _, err := ed25519.GenerateKey(rand.Reader)
141 if err != nil {
142 t.Fatalf("GenerateKey: %v", err)
143 }
144 server := &Certificate{
145 Namespace: &ns,
146 Issuer: ca,
147 Name: "server",
148 Template: Server([]string{"server"}, nil),
149 Mode: CertificateExternal,
150 PublicKey: pk,
151 }
152 serverBytes, err := server.Ensure(ctx, cl)
153 if err != nil {
154 t.Fatalf("Failed to Ensure server certificate: %v", err)
155 }
156
157 // Issuing an external certificate with the same name but different public key
158 // should fail.
159 pk2, _, err := ed25519.GenerateKey(rand.Reader)
160 if err != nil {
161 t.Fatalf("GenerateKey: %v", err)
162 }
163 server2 := &Certificate{
164 Namespace: &ns,
165 Issuer: ca,
166 Name: "server",
167 Template: Server([]string{"server"}, nil),
168 Mode: CertificateExternal,
169 PublicKey: pk2,
170 }
171 if _, err := server2.Ensure(ctx, cl); err == nil {
172 t.Fatalf("Issuing server certificate with different public key should have failed")
173 }
174
175 // Issuing the external certificate with the same name and same public key
176 // should work and yield the same x509 bytes.
177 server3 := &Certificate{
178 Namespace: &ns,
179 Issuer: ca,
180 Name: "server",
181 Template: Server([]string{"server"}, nil),
182 Mode: CertificateExternal,
183 PublicKey: pk,
184 }
185 serverBytes3, err := server3.Ensure(ctx, cl)
186 if err != nil {
187 t.Fatalf("Failed to re-Ensure server certificate: %v", err)
188 }
189 if !bytes.Equal(serverBytes, serverBytes3) {
190 t.Errorf("New server certificate has different x509 certificate")
191 }
192}