1 package bannerctl
2
3 import (
4 "context"
5 "crypto/x509"
6 "crypto/x509/pkix"
7 "encoding/base64"
8 "encoding/pem"
9 "fmt"
10 "math/big"
11 "time"
12
13 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14
15 gcpProject "edge-infra.dev/pkg/lib/gcp/project"
16 "edge-infra.dev/pkg/lib/uuid"
17
18 ctrl "sigs.k8s.io/controller-runtime"
19
20 bannerAPI "edge-infra.dev/pkg/edge/apis/banner/v1alpha1"
21 "edge-infra.dev/pkg/edge/controllers/util/edgedb"
22 "edge-infra.dev/pkg/edge/gcpinfra"
23 )
24
25 func (s *Suite) TestReconcileCAManagement() {
26 bannerGUID := uuid.New().UUID
27 generatedProjID := fmt.Sprintf("%s-%s", gcpinfra.ProjectIDPrefix, gcpProject.RandAN(29-(len(gcpinfra.ProjectIDPrefix))))
28 banner := &bannerAPI.Banner{
29 ObjectMeta: metav1.ObjectMeta{
30 Name: bannerGUID,
31 },
32 Spec: bannerAPI.BannerSpec{
33 DisplayName: "banner-for-ca-stuff",
34 GCP: bannerAPI.GCPConfig{
35 ProjectID: generatedProjID,
36 },
37 BSL: bannerAPI.BSLConfig{
38 EnterpriseUnit: bannerAPI.BSLEnterpriseUnit{
39 ID: uuid.New().UUID,
40 },
41 Organization: bannerAPI.BSLOrganization{
42 Name: "test-org-for-ca-stuff",
43 },
44 },
45 },
46 }
47
48 s.createBanner(banner)
49 defer s.deleteBanner(banner)
50
51
52 r := &BannerReconciler{
53 Log: ctrl.Log.WithName("controllers").WithName("Banner"),
54 EdgeDB: &edgedb.EdgeDB{DB: s.db},
55 SecretManager: s.sMgr,
56 }
57
58 err := r.reconcileCerts(s.ctx, banner)
59 s.Require().Nil(err)
60 }
61
62 func (s *Suite) TestCreateCAPool() {
63 bannerGUID := uuid.New().UUID
64 generatedProjID := fmt.Sprintf("%s-%s", gcpinfra.ProjectIDPrefix, gcpProject.RandAN(29-(len(gcpinfra.ProjectIDPrefix))))
65 banner := &bannerAPI.Banner{
66 ObjectMeta: metav1.ObjectMeta{
67 Name: bannerGUID,
68 },
69 Spec: bannerAPI.BannerSpec{
70 DisplayName: "banner-for-ca-stuff-2",
71 GCP: bannerAPI.GCPConfig{
72 ProjectID: generatedProjID,
73 },
74 BSL: bannerAPI.BSLConfig{
75 EnterpriseUnit: bannerAPI.BSLEnterpriseUnit{
76 ID: uuid.New().UUID,
77 },
78 Organization: bannerAPI.BSLOrganization{
79 Name: "test-org-for-ca-stuff-2",
80 },
81 },
82 },
83 }
84
85 s.createBanner(banner)
86 defer s.deleteBanner(banner)
87
88
89 r := &BannerReconciler{
90 Log: ctrl.Log.WithName("controllers").WithName("Banner"),
91 EdgeDB: &edgedb.EdgeDB{DB: s.db},
92 SecretManager: s.sMgr,
93 }
94
95 poolID, err := r.reconcileCAPool(context.TODO(), bannerGUID)
96 s.Require().Nil(err)
97 s.Require().NotNil(poolID)
98
99
100 var bannerIDfromPool string
101 err = s.db.QueryRow("SELECT banner_edge_id FROM ca_pools WHERE ca_pool_edge_id = $1", poolID).Scan(&bannerIDfromPool)
102 s.Require().Nil(err)
103 s.Require().Equal(bannerGUID, bannerIDfromPool)
104
105 err = r.reconcileCACerts(s.ctx, banner, poolID)
106 s.Require().Nil(err)
107
108
109 var caCert string
110 err = s.db.QueryRow("SELECT ca_cert_edge_id FROM ca_certificates WHERE ca_pool_edge_id = $1", poolID).Scan(&caCert)
111 s.Require().Nil(err)
112 s.Require().NotNil(caCert)
113 }
114
115 func (s *Suite) TestCreateCACert() {
116 template := &x509.Certificate{
117 SerialNumber: big.NewInt(1),
118 Subject: pkix.Name{
119 CommonName: "emissary-ca",
120 },
121 NotBefore: time.Now(),
122 NotAfter: time.Now().AddDate(5, 0, 0),
123 KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
124 ExtKeyUsage: []x509.ExtKeyUsage{},
125 IsCA: true,
126 BasicConstraintsValid: true,
127 }
128
129 cert, privateKey, err := generateCert(template)
130 s.Require().Nil(err)
131 s.Require().NotNil(cert)
132 s.Require().NotNil(privateKey)
133
134
135 _, err = base64.StdEncoding.Decode(privateKey, privateKey)
136 s.Require().Nil(err)
137
138 _, err = base64.StdEncoding.Decode(cert, cert)
139 s.Require().Nil(err)
140
141
142 block, _ := pem.Decode(cert)
143 s.Require().NotNil(block)
144 s.Require().Equal("CERTIFICATE", block.Type)
145
146
147 parsedCert, err := x509.ParseCertificate(block.Bytes)
148 s.Require().Nil(err)
149 s.Require().NotNil(parsedCert)
150
151
152 s.Require().True(time.Now().Before(parsedCert.NotAfter))
153 s.Require().True(parsedCert.IsCA)
154
155
156 block, _ = pem.Decode(privateKey)
157 s.Require().NotNil(block)
158 s.Require().Equal("EC PRIVATE KEY", block.Type)
159
160
161 parsedPrivateKey, err := x509.ParseECPrivateKey(block.Bytes)
162 s.Require().Nil(err)
163 s.Require().NotNil(parsedPrivateKey)
164 }
165
166 func (s *Suite) TestReconcileCACert() {
167 bannerGUID := uuid.New().UUID
168 generatedProjID := fmt.Sprintf("%s-%s", gcpinfra.ProjectIDPrefix, gcpProject.RandAN(29-(len(gcpinfra.ProjectIDPrefix))))
169 r := &BannerReconciler{
170 Log: ctrl.Log.WithName("controllers").WithName("Banner"),
171 EdgeDB: &edgedb.EdgeDB{DB: s.db},
172 SecretManager: s.sMgr,
173 }
174
175
176 banner := &bannerAPI.Banner{
177 ObjectMeta: metav1.ObjectMeta{
178 Name: bannerGUID,
179 },
180 Spec: bannerAPI.BannerSpec{
181 DisplayName: "banner-for-ca-stuff-3",
182 GCP: bannerAPI.GCPConfig{
183 ProjectID: generatedProjID,
184 },
185 BSL: bannerAPI.BSLConfig{
186 EnterpriseUnit: bannerAPI.BSLEnterpriseUnit{
187 ID: uuid.New().UUID,
188 },
189 Organization: bannerAPI.BSLOrganization{
190 Name: "test-org-for-ca-stuff-3",
191 },
192 },
193 },
194 }
195
196 s.createBanner(banner)
197 defer s.deleteBanner(banner)
198
199 poolID, err := r.reconcileCAPool(context.TODO(), bannerGUID)
200 s.Require().Nil(err)
201 s.Require().NotNil(poolID)
202
203 err = r.reconcileCACerts(s.ctx, banner, poolID)
204 s.Require().Nil(err)
205 }
206
207 func (s *Suite) TestReconcileCACertsRotation() {
208 bannerGUID := uuid.New().UUID
209 generatedProjID := fmt.Sprintf("%s-%s", gcpinfra.ProjectIDPrefix, gcpProject.RandAN(29-(len(gcpinfra.ProjectIDPrefix))))
210 banner := &bannerAPI.Banner{
211 ObjectMeta: metav1.ObjectMeta{
212 Name: bannerGUID,
213 },
214 Spec: bannerAPI.BannerSpec{
215 DisplayName: "banner-for-ca-stuff-4",
216 GCP: bannerAPI.GCPConfig{
217 ProjectID: generatedProjID,
218 },
219 BSL: bannerAPI.BSLConfig{
220 EnterpriseUnit: bannerAPI.BSLEnterpriseUnit{
221 ID: uuid.New().UUID,
222 },
223 Organization: bannerAPI.BSLOrganization{
224 Name: "test-org-for-ca-stuff-4",
225 },
226 },
227 },
228 }
229
230 s.createBanner(banner)
231 defer s.deleteBanner(banner)
232
233
234 r := &BannerReconciler{
235 Log: ctrl.Log.WithName("controllers").WithName("Banner"),
236 EdgeDB: &edgedb.EdgeDB{DB: s.db},
237 SecretManager: s.sMgr,
238 }
239
240
241 poolID, err := r.reconcileCAPool(context.TODO(), bannerGUID)
242 s.Require().Nil(err)
243 s.Require().NotNil(poolID)
244
245
246 activeCertID := uuid.New().UUID
247 stagedCertID := uuid.New().UUID
248 activeExpiration := time.Now().AddDate(0, 9, 0).Add(time.Second * 3)
249 stagedExpiration := time.Now().AddDate(5, 0, 0)
250
251 _, err = s.db.Exec(`
252 INSERT INTO ca_certificates (ca_cert_edge_id, ca_pool_edge_id, status, cert_ref, private_key_ref, expiration)
253 VALUES ($1, $2, 'active', 'active-cert-ref', 'active-private-key-ref', $3),
254 ($4, $5, 'staged', 'staged-cert-ref', 'staged-private-key-ref', $6)
255 `, activeCertID, poolID, activeExpiration, stagedCertID, poolID, stagedExpiration)
256 s.Require().Nil(err)
257
258
259 err = r.reconcileCACerts(s.ctx, banner, poolID)
260 s.Require().Nil(err)
261
262
263 var status string
264 err = s.db.QueryRow("SELECT status FROM ca_certificates WHERE ca_cert_edge_id = $1", activeCertID).Scan(&status)
265 s.Require().Nil(err)
266 s.Require().Equal("active", status)
267
268 err = s.db.QueryRow("SELECT status FROM ca_certificates WHERE ca_cert_edge_id = $1", stagedCertID).Scan(&status)
269 s.Require().Nil(err)
270 s.Require().Equal("staged", status)
271
272
273
274 time.Sleep(time.Second * 4)
275
276
277 err = r.reconcileCACerts(s.ctx, banner, poolID)
278 s.Require().Nil(err)
279
280
281 err = s.db.QueryRow("SELECT status FROM ca_certificates WHERE ca_cert_edge_id = $1", activeCertID).Scan(&status)
282 s.Require().Nil(err)
283 s.Require().Equal("retired", status)
284
285 err = s.db.QueryRow("SELECT status FROM ca_certificates WHERE ca_cert_edge_id = $1", stagedCertID).Scan(&status)
286 s.Require().Nil(err)
287 s.Require().Equal("active", status)
288 }
289
290 func (s *Suite) TestReconcileCACertsCreateStagedCert() {
291 bannerGUID := uuid.New().UUID
292 generatedProjID := fmt.Sprintf("%s-%s", gcpinfra.ProjectIDPrefix, gcpProject.RandAN(29-(len(gcpinfra.ProjectIDPrefix))))
293 banner := &bannerAPI.Banner{
294 ObjectMeta: metav1.ObjectMeta{
295 Name: bannerGUID,
296 },
297 Spec: bannerAPI.BannerSpec{
298 DisplayName: "banner-for-ca-stuff-5",
299 GCP: bannerAPI.GCPConfig{
300 ProjectID: generatedProjID,
301 },
302 BSL: bannerAPI.BSLConfig{
303 EnterpriseUnit: bannerAPI.BSLEnterpriseUnit{
304 ID: uuid.New().UUID,
305 },
306 Organization: bannerAPI.BSLOrganization{
307 Name: "test-org-for-ca-stuff-5",
308 },
309 },
310 },
311 }
312
313 s.createBanner(banner)
314 defer s.deleteBanner(banner)
315
316
317 r := &BannerReconciler{
318 Log: ctrl.Log.WithName("controllers").WithName("Banner"),
319 EdgeDB: &edgedb.EdgeDB{DB: s.db},
320 SecretManager: s.sMgr,
321 }
322
323
324 poolID, err := r.reconcileCAPool(context.TODO(), bannerGUID)
325 s.Require().Nil(err)
326 s.Require().NotNil(poolID)
327
328
329 activeCertID := uuid.New().UUID
330 activeExpiration := time.Now().AddDate(0, 11, 0)
331
332 _, err = s.db.Exec(`
333 INSERT INTO ca_certificates (ca_cert_edge_id, ca_pool_edge_id, status, cert_ref, private_key_ref, expiration)
334 VALUES ($1, $2, 'active', 'active-cert-ref', 'active-private-key-ref', $3)
335 `, activeCertID, poolID, activeExpiration)
336 s.Require().Nil(err)
337
338
339 err = r.reconcileCACerts(s.ctx, banner, poolID)
340 s.Require().Nil(err)
341
342
343 var stagedCertID string
344 err = s.db.QueryRow("SELECT ca_cert_edge_id FROM ca_certificates WHERE ca_pool_edge_id = $1 AND status = 'staged'", poolID).Scan(&stagedCertID)
345 s.Require().Nil(err)
346 s.Require().NotNil(stagedCertID)
347
348
349 var status string
350 err = s.db.QueryRow("SELECT status FROM ca_certificates WHERE ca_cert_edge_id = $1", activeCertID).Scan(&status)
351 s.Require().Nil(err)
352 s.Require().Equal("active", status)
353 }
354
355 func (s *Suite) TestReconcileCACertsUpdateRetiredToDeleted() {
356 bannerGUID := uuid.New().UUID
357 generatedProjID := fmt.Sprintf("%s-%s", gcpinfra.ProjectIDPrefix, gcpProject.RandAN(29-(len(gcpinfra.ProjectIDPrefix))))
358 banner := &bannerAPI.Banner{
359 ObjectMeta: metav1.ObjectMeta{
360 Name: bannerGUID,
361 },
362 Spec: bannerAPI.BannerSpec{
363 DisplayName: "banner-for-ca-stuff-6",
364 GCP: bannerAPI.GCPConfig{
365 ProjectID: generatedProjID,
366 },
367 BSL: bannerAPI.BSLConfig{
368 EnterpriseUnit: bannerAPI.BSLEnterpriseUnit{
369 ID: uuid.New().UUID,
370 },
371 Organization: bannerAPI.BSLOrganization{
372 Name: "test-org-for-ca-stuff-6",
373 },
374 },
375 },
376 }
377
378 s.createBanner(banner)
379 defer s.deleteBanner(banner)
380
381
382 r := &BannerReconciler{
383 Log: ctrl.Log.WithName("controllers").WithName("Banner"),
384 EdgeDB: &edgedb.EdgeDB{DB: s.db},
385 SecretManager: s.sMgr,
386 }
387
388
389 poolID, err := r.reconcileCAPool(context.TODO(), bannerGUID)
390 s.Require().Nil(err)
391 s.Require().NotNil(poolID)
392
393
394 retiredCertID := uuid.New().UUID
395 expiredDate := time.Now().AddDate(-1, 0, 0)
396 activeCertID := uuid.New().UUID
397 activeExpiration := time.Now().AddDate(5, 0, 0)
398
399 _, err = s.db.Exec(`
400 INSERT INTO ca_certificates (ca_cert_edge_id, ca_pool_edge_id, status, cert_ref, private_key_ref, expiration)
401 VALUES
402 ($1, $2, 'retired', 'retired-cert-ref', 'retired-private-key-ref', $3),
403 ($4, $5, 'active', 'active-cert-ref', 'active-private-key-ref', $6)
404 `, retiredCertID, poolID, expiredDate, activeCertID, poolID, activeExpiration)
405 s.Require().Nil(err)
406
407
408 err = r.reconcileCACerts(s.ctx, banner, poolID)
409 s.Require().Nil(err)
410
411
412 var status string
413 err = s.db.QueryRow("SELECT status FROM ca_certificates WHERE ca_cert_edge_id = $1", retiredCertID).Scan(&status)
414 s.Require().Nil(err)
415 s.Require().Equal("deleted", status)
416 }
417
View as plain text