detached 339691e

fix(pki): harden certificate issuance handling

commit
339691e15ff029bd808ed7f8293971dcaa75bdbc
author
brice · · 2026-07-16 10:07 UTC
parent
5e22cc2
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +627 -6
M .mockery.yaml +1 -0
@@ -30,6 +30,7 @@ packages:
3030 CertificateStore:
3131 SecretResolver:
3232 CloudflareZoneResolver:
33+ CertificateAuthority:
3334 github.com/bnema/gordon/internal/boundaries/in:
3435 interfaces:
3536 ContainerService:
@@ -30,6 +30,7 @@ packages:
30 CertificateStore:30 CertificateStore:
31 SecretResolver:31 SecretResolver:
32 CloudflareZoneResolver:32 CloudflareZoneResolver:
33+ CertificateAuthority:
33 github.com/bnema/gordon/internal/boundaries/in:34 github.com/bnema/gordon/internal/boundaries/in:
34 interfaces:35 interfaces:
35 ContainerService:36 ContainerService:
A internal/boundaries/out/mocks/mock_certificate_authority.go +560 -0
@@ -0,0 +1,560 @@
1+// Code generated by mockery; DO NOT EDIT.
2+// github.com/vektra/mockery
3+// template: testify
4+
5+package mocks
6+
7+import (
8+ "crypto/tls"
9+ "crypto/x509"
10+ "time"
11+
12+ mock "github.com/stretchr/testify/mock"
13+)
14+
15+// NewMockCertificateAuthority creates a new instance of MockCertificateAuthority. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
16+// The first argument is typically a *testing.T value.
17+func NewMockCertificateAuthority(t interface {
18+ mock.TestingT
19+ Cleanup(func())
20+}) *MockCertificateAuthority {
21+ mock := &MockCertificateAuthority{}
22+ mock.Mock.Test(t)
23+
24+ t.Cleanup(func() { mock.AssertExpectations(t) })
25+
26+ return mock
27+}
28+
29+// MockCertificateAuthority is an autogenerated mock type for the CertificateAuthority type
30+type MockCertificateAuthority struct {
31+ mock.Mock
32+}
33+
34+type MockCertificateAuthority_Expecter struct {
35+ mock *mock.Mock
36+}
37+
38+func (_m *MockCertificateAuthority) EXPECT() *MockCertificateAuthority_Expecter {
39+ return &MockCertificateAuthority_Expecter{mock: &_m.Mock}
40+}
41+
42+// InstallRoot provides a mock function for the type MockCertificateAuthority
43+func (_mock *MockCertificateAuthority) InstallRoot(cert *x509.Certificate) error {
44+ ret := _mock.Called(cert)
45+
46+ if len(ret) == 0 {
47+ panic("no return value specified for InstallRoot")
48+ }
49+
50+ var r0 error
51+ if returnFunc, ok := ret.Get(0).(func(*x509.Certificate) error); ok {
52+ r0 = returnFunc(cert)
53+ } else {
54+ r0 = ret.Error(0)
55+ }
56+ return r0
57+}
58+
59+// MockCertificateAuthority_InstallRoot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InstallRoot'
60+type MockCertificateAuthority_InstallRoot_Call struct {
61+ *mock.Call
62+}
63+
64+// InstallRoot is a helper method to define mock.On call
65+// - cert *x509.Certificate
66+func (_e *MockCertificateAuthority_Expecter) InstallRoot(cert any) *MockCertificateAuthority_InstallRoot_Call {
67+ return &MockCertificateAuthority_InstallRoot_Call{Call: _e.mock.On("InstallRoot", cert)}
68+}
69+
70+func (_c *MockCertificateAuthority_InstallRoot_Call) Run(run func(cert *x509.Certificate)) *MockCertificateAuthority_InstallRoot_Call {
71+ _c.Call.Run(func(args mock.Arguments) {
72+ var arg0 *x509.Certificate
73+ if args[0] != nil {
74+ arg0 = args[0].(*x509.Certificate)
75+ }
76+ run(
77+ arg0,
78+ )
79+ })
80+ return _c
81+}
82+
83+func (_c *MockCertificateAuthority_InstallRoot_Call) Return(err error) *MockCertificateAuthority_InstallRoot_Call {
84+ _c.Call.Return(err)
85+ return _c
86+}
87+
88+func (_c *MockCertificateAuthority_InstallRoot_Call) RunAndReturn(run func(cert *x509.Certificate) error) *MockCertificateAuthority_InstallRoot_Call {
89+ _c.Call.Return(run)
90+ return _c
91+}
92+
93+// IntermediateExpiresAt provides a mock function for the type MockCertificateAuthority
94+func (_mock *MockCertificateAuthority) IntermediateExpiresAt() time.Time {
95+ ret := _mock.Called()
96+
97+ if len(ret) == 0 {
98+ panic("no return value specified for IntermediateExpiresAt")
99+ }
100+
101+ var r0 time.Time
102+ if returnFunc, ok := ret.Get(0).(func() time.Time); ok {
103+ r0 = returnFunc()
104+ } else {
105+ r0 = ret.Get(0).(time.Time)
106+ }
107+ return r0
108+}
109+
110+// MockCertificateAuthority_IntermediateExpiresAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IntermediateExpiresAt'
111+type MockCertificateAuthority_IntermediateExpiresAt_Call struct {
112+ *mock.Call
113+}
114+
115+// IntermediateExpiresAt is a helper method to define mock.On call
116+func (_e *MockCertificateAuthority_Expecter) IntermediateExpiresAt() *MockCertificateAuthority_IntermediateExpiresAt_Call {
117+ return &MockCertificateAuthority_IntermediateExpiresAt_Call{Call: _e.mock.On("IntermediateExpiresAt")}
118+}
119+
120+func (_c *MockCertificateAuthority_IntermediateExpiresAt_Call) Run(run func()) *MockCertificateAuthority_IntermediateExpiresAt_Call {
121+ _c.Call.Run(func(args mock.Arguments) {
122+ run()
123+ })
124+ return _c
125+}
126+
127+func (_c *MockCertificateAuthority_IntermediateExpiresAt_Call) Return(time1 time.Time) *MockCertificateAuthority_IntermediateExpiresAt_Call {
128+ _c.Call.Return(time1)
129+ return _c
130+}
131+
132+func (_c *MockCertificateAuthority_IntermediateExpiresAt_Call) RunAndReturn(run func() time.Time) *MockCertificateAuthority_IntermediateExpiresAt_Call {
133+ _c.Call.Return(run)
134+ return _c
135+}
136+
137+// IntermediateLifetime provides a mock function for the type MockCertificateAuthority
138+func (_mock *MockCertificateAuthority) IntermediateLifetime() time.Duration {
139+ ret := _mock.Called()
140+
141+ if len(ret) == 0 {
142+ panic("no return value specified for IntermediateLifetime")
143+ }
144+
145+ var r0 time.Duration
146+ if returnFunc, ok := ret.Get(0).(func() time.Duration); ok {
147+ r0 = returnFunc()
148+ } else {
149+ r0 = ret.Get(0).(time.Duration)
150+ }
151+ return r0
152+}
153+
154+// MockCertificateAuthority_IntermediateLifetime_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IntermediateLifetime'
155+type MockCertificateAuthority_IntermediateLifetime_Call struct {
156+ *mock.Call
157+}
158+
159+// IntermediateLifetime is a helper method to define mock.On call
160+func (_e *MockCertificateAuthority_Expecter) IntermediateLifetime() *MockCertificateAuthority_IntermediateLifetime_Call {
161+ return &MockCertificateAuthority_IntermediateLifetime_Call{Call: _e.mock.On("IntermediateLifetime")}
162+}
163+
164+func (_c *MockCertificateAuthority_IntermediateLifetime_Call) Run(run func()) *MockCertificateAuthority_IntermediateLifetime_Call {
165+ _c.Call.Run(func(args mock.Arguments) {
166+ run()
167+ })
168+ return _c
169+}
170+
171+func (_c *MockCertificateAuthority_IntermediateLifetime_Call) Return(duration time.Duration) *MockCertificateAuthority_IntermediateLifetime_Call {
172+ _c.Call.Return(duration)
173+ return _c
174+}
175+
176+func (_c *MockCertificateAuthority_IntermediateLifetime_Call) RunAndReturn(run func() time.Duration) *MockCertificateAuthority_IntermediateLifetime_Call {
177+ _c.Call.Return(run)
178+ return _c
179+}
180+
181+// IssueCertificate provides a mock function for the type MockCertificateAuthority
182+func (_mock *MockCertificateAuthority) IssueCertificate(domain string) (*tls.Certificate, error) {
183+ ret := _mock.Called(domain)
184+
185+ if len(ret) == 0 {
186+ panic("no return value specified for IssueCertificate")
187+ }
188+
189+ var r0 *tls.Certificate
190+ var r1 error
191+ if returnFunc, ok := ret.Get(0).(func(string) (*tls.Certificate, error)); ok {
192+ return returnFunc(domain)
193+ }
194+ if returnFunc, ok := ret.Get(0).(func(string) *tls.Certificate); ok {
195+ r0 = returnFunc(domain)
196+ } else {
197+ if ret.Get(0) != nil {
198+ r0 = ret.Get(0).(*tls.Certificate)
199+ }
200+ }
201+ if returnFunc, ok := ret.Get(1).(func(string) error); ok {
202+ r1 = returnFunc(domain)
203+ } else {
204+ r1 = ret.Error(1)
205+ }
206+ return r0, r1
207+}
208+
209+// MockCertificateAuthority_IssueCertificate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IssueCertificate'
210+type MockCertificateAuthority_IssueCertificate_Call struct {
211+ *mock.Call
212+}
213+
214+// IssueCertificate is a helper method to define mock.On call
215+// - domain string
216+func (_e *MockCertificateAuthority_Expecter) IssueCertificate(domain any) *MockCertificateAuthority_IssueCertificate_Call {
217+ return &MockCertificateAuthority_IssueCertificate_Call{Call: _e.mock.On("IssueCertificate", domain)}
218+}
219+
220+func (_c *MockCertificateAuthority_IssueCertificate_Call) Run(run func(domain string)) *MockCertificateAuthority_IssueCertificate_Call {
221+ _c.Call.Run(func(args mock.Arguments) {
222+ var arg0 string
223+ if args[0] != nil {
224+ arg0 = args[0].(string)
225+ }
226+ run(
227+ arg0,
228+ )
229+ })
230+ return _c
231+}
232+
233+func (_c *MockCertificateAuthority_IssueCertificate_Call) Return(certificate *tls.Certificate, err error) *MockCertificateAuthority_IssueCertificate_Call {
234+ _c.Call.Return(certificate, err)
235+ return _c
236+}
237+
238+func (_c *MockCertificateAuthority_IssueCertificate_Call) RunAndReturn(run func(domain string) (*tls.Certificate, error)) *MockCertificateAuthority_IssueCertificate_Call {
239+ _c.Call.Return(run)
240+ return _c
241+}
242+
243+// LeafLifetime provides a mock function for the type MockCertificateAuthority
244+func (_mock *MockCertificateAuthority) LeafLifetime() time.Duration {
245+ ret := _mock.Called()
246+
247+ if len(ret) == 0 {
248+ panic("no return value specified for LeafLifetime")
249+ }
250+
251+ var r0 time.Duration
252+ if returnFunc, ok := ret.Get(0).(func() time.Duration); ok {
253+ r0 = returnFunc()
254+ } else {
255+ r0 = ret.Get(0).(time.Duration)
256+ }
257+ return r0
258+}
259+
260+// MockCertificateAuthority_LeafLifetime_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LeafLifetime'
261+type MockCertificateAuthority_LeafLifetime_Call struct {
262+ *mock.Call
263+}
264+
265+// LeafLifetime is a helper method to define mock.On call
266+func (_e *MockCertificateAuthority_Expecter) LeafLifetime() *MockCertificateAuthority_LeafLifetime_Call {
267+ return &MockCertificateAuthority_LeafLifetime_Call{Call: _e.mock.On("LeafLifetime")}
268+}
269+
270+func (_c *MockCertificateAuthority_LeafLifetime_Call) Run(run func()) *MockCertificateAuthority_LeafLifetime_Call {
271+ _c.Call.Run(func(args mock.Arguments) {
272+ run()
273+ })
274+ return _c
275+}
276+
277+func (_c *MockCertificateAuthority_LeafLifetime_Call) Return(duration time.Duration) *MockCertificateAuthority_LeafLifetime_Call {
278+ _c.Call.Return(duration)
279+ return _c
280+}
281+
282+func (_c *MockCertificateAuthority_LeafLifetime_Call) RunAndReturn(run func() time.Duration) *MockCertificateAuthority_LeafLifetime_Call {
283+ _c.Call.Return(run)
284+ return _c
285+}
286+
287+// RenewIntermediate provides a mock function for the type MockCertificateAuthority
288+func (_mock *MockCertificateAuthority) RenewIntermediate() error {
289+ ret := _mock.Called()
290+
291+ if len(ret) == 0 {
292+ panic("no return value specified for RenewIntermediate")
293+ }
294+
295+ var r0 error
296+ if returnFunc, ok := ret.Get(0).(func() error); ok {
297+ r0 = returnFunc()
298+ } else {
299+ r0 = ret.Error(0)
300+ }
301+ return r0
302+}
303+
304+// MockCertificateAuthority_RenewIntermediate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RenewIntermediate'
305+type MockCertificateAuthority_RenewIntermediate_Call struct {
306+ *mock.Call
307+}
308+
309+// RenewIntermediate is a helper method to define mock.On call
310+func (_e *MockCertificateAuthority_Expecter) RenewIntermediate() *MockCertificateAuthority_RenewIntermediate_Call {
311+ return &MockCertificateAuthority_RenewIntermediate_Call{Call: _e.mock.On("RenewIntermediate")}
312+}
313+
314+func (_c *MockCertificateAuthority_RenewIntermediate_Call) Run(run func()) *MockCertificateAuthority_RenewIntermediate_Call {
315+ _c.Call.Run(func(args mock.Arguments) {
316+ run()
317+ })
318+ return _c
319+}
320+
321+func (_c *MockCertificateAuthority_RenewIntermediate_Call) Return(err error) *MockCertificateAuthority_RenewIntermediate_Call {
322+ _c.Call.Return(err)
323+ return _c
324+}
325+
326+func (_c *MockCertificateAuthority_RenewIntermediate_Call) RunAndReturn(run func() error) *MockCertificateAuthority_RenewIntermediate_Call {
327+ _c.Call.Return(run)
328+ return _c
329+}
330+
331+// RootCertificate provides a mock function for the type MockCertificateAuthority
332+func (_mock *MockCertificateAuthority) RootCertificate() []byte {
333+ ret := _mock.Called()
334+
335+ if len(ret) == 0 {
336+ panic("no return value specified for RootCertificate")
337+ }
338+
339+ var r0 []byte
340+ if returnFunc, ok := ret.Get(0).(func() []byte); ok {
341+ r0 = returnFunc()
342+ } else {
343+ if ret.Get(0) != nil {
344+ r0 = ret.Get(0).([]byte)
345+ }
346+ }
347+ return r0
348+}
349+
350+// MockCertificateAuthority_RootCertificate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RootCertificate'
351+type MockCertificateAuthority_RootCertificate_Call struct {
352+ *mock.Call
353+}
354+
355+// RootCertificate is a helper method to define mock.On call
356+func (_e *MockCertificateAuthority_Expecter) RootCertificate() *MockCertificateAuthority_RootCertificate_Call {
357+ return &MockCertificateAuthority_RootCertificate_Call{Call: _e.mock.On("RootCertificate")}
358+}
359+
360+func (_c *MockCertificateAuthority_RootCertificate_Call) Run(run func()) *MockCertificateAuthority_RootCertificate_Call {
361+ _c.Call.Run(func(args mock.Arguments) {
362+ run()
363+ })
364+ return _c
365+}
366+
367+func (_c *MockCertificateAuthority_RootCertificate_Call) Return(bytes []byte) *MockCertificateAuthority_RootCertificate_Call {
368+ _c.Call.Return(bytes)
369+ return _c
370+}
371+
372+func (_c *MockCertificateAuthority_RootCertificate_Call) RunAndReturn(run func() []byte) *MockCertificateAuthority_RootCertificate_Call {
373+ _c.Call.Return(run)
374+ return _c
375+}
376+
377+// RootCertificateDER provides a mock function for the type MockCertificateAuthority
378+func (_mock *MockCertificateAuthority) RootCertificateDER() []byte {
379+ ret := _mock.Called()
380+
381+ if len(ret) == 0 {
382+ panic("no return value specified for RootCertificateDER")
383+ }
384+
385+ var r0 []byte
386+ if returnFunc, ok := ret.Get(0).(func() []byte); ok {
387+ r0 = returnFunc()
388+ } else {
389+ if ret.Get(0) != nil {
390+ r0 = ret.Get(0).([]byte)
391+ }
392+ }
393+ return r0
394+}
395+
396+// MockCertificateAuthority_RootCertificateDER_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RootCertificateDER'
397+type MockCertificateAuthority_RootCertificateDER_Call struct {
398+ *mock.Call
399+}
400+
401+// RootCertificateDER is a helper method to define mock.On call
402+func (_e *MockCertificateAuthority_Expecter) RootCertificateDER() *MockCertificateAuthority_RootCertificateDER_Call {
403+ return &MockCertificateAuthority_RootCertificateDER_Call{Call: _e.mock.On("RootCertificateDER")}
404+}
405+
406+func (_c *MockCertificateAuthority_RootCertificateDER_Call) Run(run func()) *MockCertificateAuthority_RootCertificateDER_Call {
407+ _c.Call.Run(func(args mock.Arguments) {
408+ run()
409+ })
410+ return _c
411+}
412+
413+func (_c *MockCertificateAuthority_RootCertificateDER_Call) Return(bytes []byte) *MockCertificateAuthority_RootCertificateDER_Call {
414+ _c.Call.Return(bytes)
415+ return _c
416+}
417+
418+func (_c *MockCertificateAuthority_RootCertificateDER_Call) RunAndReturn(run func() []byte) *MockCertificateAuthority_RootCertificateDER_Call {
419+ _c.Call.Return(run)
420+ return _c
421+}
422+
423+// RootCommonName provides a mock function for the type MockCertificateAuthority
424+func (_mock *MockCertificateAuthority) RootCommonName() string {
425+ ret := _mock.Called()
426+
427+ if len(ret) == 0 {
428+ panic("no return value specified for RootCommonName")
429+ }
430+
431+ var r0 string
432+ if returnFunc, ok := ret.Get(0).(func() string); ok {
433+ r0 = returnFunc()
434+ } else {
435+ r0 = ret.Get(0).(string)
436+ }
437+ return r0
438+}
439+
440+// MockCertificateAuthority_RootCommonName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RootCommonName'
441+type MockCertificateAuthority_RootCommonName_Call struct {
442+ *mock.Call
443+}
444+
445+// RootCommonName is a helper method to define mock.On call
446+func (_e *MockCertificateAuthority_Expecter) RootCommonName() *MockCertificateAuthority_RootCommonName_Call {
447+ return &MockCertificateAuthority_RootCommonName_Call{Call: _e.mock.On("RootCommonName")}
448+}
449+
450+func (_c *MockCertificateAuthority_RootCommonName_Call) Run(run func()) *MockCertificateAuthority_RootCommonName_Call {
451+ _c.Call.Run(func(args mock.Arguments) {
452+ run()
453+ })
454+ return _c
455+}
456+
457+func (_c *MockCertificateAuthority_RootCommonName_Call) Return(s string) *MockCertificateAuthority_RootCommonName_Call {
458+ _c.Call.Return(s)
459+ return _c
460+}
461+
462+func (_c *MockCertificateAuthority_RootCommonName_Call) RunAndReturn(run func() string) *MockCertificateAuthority_RootCommonName_Call {
463+ _c.Call.Return(run)
464+ return _c
465+}
466+
467+// RootFingerprint provides a mock function for the type MockCertificateAuthority
468+func (_mock *MockCertificateAuthority) RootFingerprint() string {
469+ ret := _mock.Called()
470+
471+ if len(ret) == 0 {
472+ panic("no return value specified for RootFingerprint")
473+ }
474+
475+ var r0 string
476+ if returnFunc, ok := ret.Get(0).(func() string); ok {
477+ r0 = returnFunc()
478+ } else {
479+ r0 = ret.Get(0).(string)
480+ }
481+ return r0
482+}
483+
484+// MockCertificateAuthority_RootFingerprint_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RootFingerprint'
485+type MockCertificateAuthority_RootFingerprint_Call struct {
486+ *mock.Call
487+}
488+
489+// RootFingerprint is a helper method to define mock.On call
490+func (_e *MockCertificateAuthority_Expecter) RootFingerprint() *MockCertificateAuthority_RootFingerprint_Call {
491+ return &MockCertificateAuthority_RootFingerprint_Call{Call: _e.mock.On("RootFingerprint")}
492+}
493+
494+func (_c *MockCertificateAuthority_RootFingerprint_Call) Run(run func()) *MockCertificateAuthority_RootFingerprint_Call {
495+ _c.Call.Run(func(args mock.Arguments) {
496+ run()
497+ })
498+ return _c
499+}
500+
501+func (_c *MockCertificateAuthority_RootFingerprint_Call) Return(s string) *MockCertificateAuthority_RootFingerprint_Call {
502+ _c.Call.Return(s)
503+ return _c
504+}
505+
506+func (_c *MockCertificateAuthority_RootFingerprint_Call) RunAndReturn(run func() string) *MockCertificateAuthority_RootFingerprint_Call {
507+ _c.Call.Return(run)
508+ return _c
509+}
510+
511+// UninstallRoot provides a mock function for the type MockCertificateAuthority
512+func (_mock *MockCertificateAuthority) UninstallRoot(cert *x509.Certificate) error {
513+ ret := _mock.Called(cert)
514+
515+ if len(ret) == 0 {
516+ panic("no return value specified for UninstallRoot")
517+ }
518+
519+ var r0 error
520+ if returnFunc, ok := ret.Get(0).(func(*x509.Certificate) error); ok {
521+ r0 = returnFunc(cert)
522+ } else {
523+ r0 = ret.Error(0)
524+ }
525+ return r0
526+}
527+
528+// MockCertificateAuthority_UninstallRoot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UninstallRoot'
529+type MockCertificateAuthority_UninstallRoot_Call struct {
530+ *mock.Call
531+}
532+
533+// UninstallRoot is a helper method to define mock.On call
534+// - cert *x509.Certificate
535+func (_e *MockCertificateAuthority_Expecter) UninstallRoot(cert any) *MockCertificateAuthority_UninstallRoot_Call {
536+ return &MockCertificateAuthority_UninstallRoot_Call{Call: _e.mock.On("UninstallRoot", cert)}
537+}
538+
539+func (_c *MockCertificateAuthority_UninstallRoot_Call) Run(run func(cert *x509.Certificate)) *MockCertificateAuthority_UninstallRoot_Call {
540+ _c.Call.Run(func(args mock.Arguments) {
541+ var arg0 *x509.Certificate
542+ if args[0] != nil {
543+ arg0 = args[0].(*x509.Certificate)
544+ }
545+ run(
546+ arg0,
547+ )
548+ })
549+ return _c
550+}
551+
552+func (_c *MockCertificateAuthority_UninstallRoot_Call) Return(err error) *MockCertificateAuthority_UninstallRoot_Call {
553+ _c.Call.Return(err)
554+ return _c
555+}
556+
557+func (_c *MockCertificateAuthority_UninstallRoot_Call) RunAndReturn(run func(cert *x509.Certificate) error) *MockCertificateAuthority_UninstallRoot_Call {
558+ _c.Call.Return(run)
559+ return _c
560+}
@@ -0,0 +1,560 @@
1+// Code generated by mockery; DO NOT EDIT.
2+// github.com/vektra/mockery
3+// template: testify
4+
5+package mocks
6+
7+import (
8+ "crypto/tls"
9+ "crypto/x509"
10+ "time"
11+
12+ mock "github.com/stretchr/testify/mock"
13+)
14+
15+// NewMockCertificateAuthority creates a new instance of MockCertificateAuthority. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
16+// The first argument is typically a *testing.T value.
17+func NewMockCertificateAuthority(t interface {
18+ mock.TestingT
19+ Cleanup(func())
20+}) *MockCertificateAuthority {
21+ mock := &MockCertificateAuthority{}
22+ mock.Mock.Test(t)
23+
24+ t.Cleanup(func() { mock.AssertExpectations(t) })
25+
26+ return mock
27+}
28+
29+// MockCertificateAuthority is an autogenerated mock type for the CertificateAuthority type
30+type MockCertificateAuthority struct {
31+ mock.Mock
32+}
33+
34+type MockCertificateAuthority_Expecter struct {
35+ mock *mock.Mock
36+}
37+
38+func (_m *MockCertificateAuthority) EXPECT() *MockCertificateAuthority_Expecter {
39+ return &MockCertificateAuthority_Expecter{mock: &_m.Mock}
40+}
41+
42+// InstallRoot provides a mock function for the type MockCertificateAuthority
43+func (_mock *MockCertificateAuthority) InstallRoot(cert *x509.Certificate) error {
44+ ret := _mock.Called(cert)
45+
46+ if len(ret) == 0 {
47+ panic("no return value specified for InstallRoot")
48+ }
49+
50+ var r0 error
51+ if returnFunc, ok := ret.Get(0).(func(*x509.Certificate) error); ok {
52+ r0 = returnFunc(cert)
53+ } else {
54+ r0 = ret.Error(0)
55+ }
56+ return r0
57+}
58+
59+// MockCertificateAuthority_InstallRoot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InstallRoot'
60+type MockCertificateAuthority_InstallRoot_Call struct {
61+ *mock.Call
62+}
63+
64+// InstallRoot is a helper method to define mock.On call
65+// - cert *x509.Certificate
66+func (_e *MockCertificateAuthority_Expecter) InstallRoot(cert any) *MockCertificateAuthority_InstallRoot_Call {
67+ return &MockCertificateAuthority_InstallRoot_Call{Call: _e.mock.On("InstallRoot", cert)}
68+}
69+
70+func (_c *MockCertificateAuthority_InstallRoot_Call) Run(run func(cert *x509.Certificate)) *MockCertificateAuthority_InstallRoot_Call {
71+ _c.Call.Run(func(args mock.Arguments) {
72+ var arg0 *x509.Certificate
73+ if args[0] != nil {
74+ arg0 = args[0].(*x509.Certificate)
75+ }
76+ run(
77+ arg0,
78+ )
79+ })
80+ return _c
81+}
82+
83+func (_c *MockCertificateAuthority_InstallRoot_Call) Return(err error) *MockCertificateAuthority_InstallRoot_Call {
84+ _c.Call.Return(err)
85+ return _c
86+}
87+
88+func (_c *MockCertificateAuthority_InstallRoot_Call) RunAndReturn(run func(cert *x509.Certificate) error) *MockCertificateAuthority_InstallRoot_Call {
89+ _c.Call.Return(run)
90+ return _c
91+}
92+
93+// IntermediateExpiresAt provides a mock function for the type MockCertificateAuthority
94+func (_mock *MockCertificateAuthority) IntermediateExpiresAt() time.Time {
95+ ret := _mock.Called()
96+
97+ if len(ret) == 0 {
98+ panic("no return value specified for IntermediateExpiresAt")
99+ }
100+
101+ var r0 time.Time
102+ if returnFunc, ok := ret.Get(0).(func() time.Time); ok {
103+ r0 = returnFunc()
104+ } else {
105+ r0 = ret.Get(0).(time.Time)
106+ }
107+ return r0
108+}
109+
110+// MockCertificateAuthority_IntermediateExpiresAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IntermediateExpiresAt'
111+type MockCertificateAuthority_IntermediateExpiresAt_Call struct {
112+ *mock.Call
113+}
114+
115+// IntermediateExpiresAt is a helper method to define mock.On call
116+func (_e *MockCertificateAuthority_Expecter) IntermediateExpiresAt() *MockCertificateAuthority_IntermediateExpiresAt_Call {
117+ return &MockCertificateAuthority_IntermediateExpiresAt_Call{Call: _e.mock.On("IntermediateExpiresAt")}
118+}
119+
120+func (_c *MockCertificateAuthority_IntermediateExpiresAt_Call) Run(run func()) *MockCertificateAuthority_IntermediateExpiresAt_Call {
121+ _c.Call.Run(func(args mock.Arguments) {
122+ run()
123+ })
124+ return _c
125+}
126+
127+func (_c *MockCertificateAuthority_IntermediateExpiresAt_Call) Return(time1 time.Time) *MockCertificateAuthority_IntermediateExpiresAt_Call {
128+ _c.Call.Return(time1)
129+ return _c
130+}
131+
132+func (_c *MockCertificateAuthority_IntermediateExpiresAt_Call) RunAndReturn(run func() time.Time) *MockCertificateAuthority_IntermediateExpiresAt_Call {
133+ _c.Call.Return(run)
134+ return _c
135+}
136+
137+// IntermediateLifetime provides a mock function for the type MockCertificateAuthority
138+func (_mock *MockCertificateAuthority) IntermediateLifetime() time.Duration {
139+ ret := _mock.Called()
140+
141+ if len(ret) == 0 {
142+ panic("no return value specified for IntermediateLifetime")
143+ }
144+
145+ var r0 time.Duration
146+ if returnFunc, ok := ret.Get(0).(func() time.Duration); ok {
147+ r0 = returnFunc()
148+ } else {
149+ r0 = ret.Get(0).(time.Duration)
150+ }
151+ return r0
152+}
153+
154+// MockCertificateAuthority_IntermediateLifetime_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IntermediateLifetime'
155+type MockCertificateAuthority_IntermediateLifetime_Call struct {
156+ *mock.Call
157+}
158+
159+// IntermediateLifetime is a helper method to define mock.On call
160+func (_e *MockCertificateAuthority_Expecter) IntermediateLifetime() *MockCertificateAuthority_IntermediateLifetime_Call {
161+ return &MockCertificateAuthority_IntermediateLifetime_Call{Call: _e.mock.On("IntermediateLifetime")}
162+}
163+
164+func (_c *MockCertificateAuthority_IntermediateLifetime_Call) Run(run func()) *MockCertificateAuthority_IntermediateLifetime_Call {
165+ _c.Call.Run(func(args mock.Arguments) {
166+ run()
167+ })
168+ return _c
169+}
170+
171+func (_c *MockCertificateAuthority_IntermediateLifetime_Call) Return(duration time.Duration) *MockCertificateAuthority_IntermediateLifetime_Call {
172+ _c.Call.Return(duration)
173+ return _c
174+}
175+
176+func (_c *MockCertificateAuthority_IntermediateLifetime_Call) RunAndReturn(run func() time.Duration) *MockCertificateAuthority_IntermediateLifetime_Call {
177+ _c.Call.Return(run)
178+ return _c
179+}
180+
181+// IssueCertificate provides a mock function for the type MockCertificateAuthority
182+func (_mock *MockCertificateAuthority) IssueCertificate(domain string) (*tls.Certificate, error) {
183+ ret := _mock.Called(domain)
184+
185+ if len(ret) == 0 {
186+ panic("no return value specified for IssueCertificate")
187+ }
188+
189+ var r0 *tls.Certificate
190+ var r1 error
191+ if returnFunc, ok := ret.Get(0).(func(string) (*tls.Certificate, error)); ok {
192+ return returnFunc(domain)
193+ }
194+ if returnFunc, ok := ret.Get(0).(func(string) *tls.Certificate); ok {
195+ r0 = returnFunc(domain)
196+ } else {
197+ if ret.Get(0) != nil {
198+ r0 = ret.Get(0).(*tls.Certificate)
199+ }
200+ }
201+ if returnFunc, ok := ret.Get(1).(func(string) error); ok {
202+ r1 = returnFunc(domain)
203+ } else {
204+ r1 = ret.Error(1)
205+ }
206+ return r0, r1
207+}
208+
209+// MockCertificateAuthority_IssueCertificate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IssueCertificate'
210+type MockCertificateAuthority_IssueCertificate_Call struct {
211+ *mock.Call
212+}
213+
214+// IssueCertificate is a helper method to define mock.On call
215+// - domain string
216+func (_e *MockCertificateAuthority_Expecter) IssueCertificate(domain any) *MockCertificateAuthority_IssueCertificate_Call {
217+ return &MockCertificateAuthority_IssueCertificate_Call{Call: _e.mock.On("IssueCertificate", domain)}
218+}
219+
220+func (_c *MockCertificateAuthority_IssueCertificate_Call) Run(run func(domain string)) *MockCertificateAuthority_IssueCertificate_Call {
221+ _c.Call.Run(func(args mock.Arguments) {
222+ var arg0 string
223+ if args[0] != nil {
224+ arg0 = args[0].(string)
225+ }
226+ run(
227+ arg0,
228+ )
229+ })
230+ return _c
231+}
232+
233+func (_c *MockCertificateAuthority_IssueCertificate_Call) Return(certificate *tls.Certificate, err error) *MockCertificateAuthority_IssueCertificate_Call {
234+ _c.Call.Return(certificate, err)
235+ return _c
236+}
237+
238+func (_c *MockCertificateAuthority_IssueCertificate_Call) RunAndReturn(run func(domain string) (*tls.Certificate, error)) *MockCertificateAuthority_IssueCertificate_Call {
239+ _c.Call.Return(run)
240+ return _c
241+}
242+
243+// LeafLifetime provides a mock function for the type MockCertificateAuthority
244+func (_mock *MockCertificateAuthority) LeafLifetime() time.Duration {
245+ ret := _mock.Called()
246+
247+ if len(ret) == 0 {
248+ panic("no return value specified for LeafLifetime")
249+ }
250+
251+ var r0 time.Duration
252+ if returnFunc, ok := ret.Get(0).(func() time.Duration); ok {
253+ r0 = returnFunc()
254+ } else {
255+ r0 = ret.Get(0).(time.Duration)
256+ }
257+ return r0
258+}
259+
260+// MockCertificateAuthority_LeafLifetime_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LeafLifetime'
261+type MockCertificateAuthority_LeafLifetime_Call struct {
262+ *mock.Call
263+}
264+
265+// LeafLifetime is a helper method to define mock.On call
266+func (_e *MockCertificateAuthority_Expecter) LeafLifetime() *MockCertificateAuthority_LeafLifetime_Call {
267+ return &MockCertificateAuthority_LeafLifetime_Call{Call: _e.mock.On("LeafLifetime")}
268+}
269+
270+func (_c *MockCertificateAuthority_LeafLifetime_Call) Run(run func()) *MockCertificateAuthority_LeafLifetime_Call {
271+ _c.Call.Run(func(args mock.Arguments) {
272+ run()
273+ })
274+ return _c
275+}
276+
277+func (_c *MockCertificateAuthority_LeafLifetime_Call) Return(duration time.Duration) *MockCertificateAuthority_LeafLifetime_Call {
278+ _c.Call.Return(duration)
279+ return _c
280+}
281+
282+func (_c *MockCertificateAuthority_LeafLifetime_Call) RunAndReturn(run func() time.Duration) *MockCertificateAuthority_LeafLifetime_Call {
283+ _c.Call.Return(run)
284+ return _c
285+}
286+
287+// RenewIntermediate provides a mock function for the type MockCertificateAuthority
288+func (_mock *MockCertificateAuthority) RenewIntermediate() error {
289+ ret := _mock.Called()
290+
291+ if len(ret) == 0 {
292+ panic("no return value specified for RenewIntermediate")
293+ }
294+
295+ var r0 error
296+ if returnFunc, ok := ret.Get(0).(func() error); ok {
297+ r0 = returnFunc()
298+ } else {
299+ r0 = ret.Error(0)
300+ }
301+ return r0
302+}
303+
304+// MockCertificateAuthority_RenewIntermediate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RenewIntermediate'
305+type MockCertificateAuthority_RenewIntermediate_Call struct {
306+ *mock.Call
307+}
308+
309+// RenewIntermediate is a helper method to define mock.On call
310+func (_e *MockCertificateAuthority_Expecter) RenewIntermediate() *MockCertificateAuthority_RenewIntermediate_Call {
311+ return &MockCertificateAuthority_RenewIntermediate_Call{Call: _e.mock.On("RenewIntermediate")}
312+}
313+
314+func (_c *MockCertificateAuthority_RenewIntermediate_Call) Run(run func()) *MockCertificateAuthority_RenewIntermediate_Call {
315+ _c.Call.Run(func(args mock.Arguments) {
316+ run()
317+ })
318+ return _c
319+}
320+
321+func (_c *MockCertificateAuthority_RenewIntermediate_Call) Return(err error) *MockCertificateAuthority_RenewIntermediate_Call {
322+ _c.Call.Return(err)
323+ return _c
324+}
325+
326+func (_c *MockCertificateAuthority_RenewIntermediate_Call) RunAndReturn(run func() error) *MockCertificateAuthority_RenewIntermediate_Call {
327+ _c.Call.Return(run)
328+ return _c
329+}
330+
331+// RootCertificate provides a mock function for the type MockCertificateAuthority
332+func (_mock *MockCertificateAuthority) RootCertificate() []byte {
333+ ret := _mock.Called()
334+
335+ if len(ret) == 0 {
336+ panic("no return value specified for RootCertificate")
337+ }
338+
339+ var r0 []byte
340+ if returnFunc, ok := ret.Get(0).(func() []byte); ok {
341+ r0 = returnFunc()
342+ } else {
343+ if ret.Get(0) != nil {
344+ r0 = ret.Get(0).([]byte)
345+ }
346+ }
347+ return r0
348+}
349+
350+// MockCertificateAuthority_RootCertificate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RootCertificate'
351+type MockCertificateAuthority_RootCertificate_Call struct {
352+ *mock.Call
353+}
354+
355+// RootCertificate is a helper method to define mock.On call
356+func (_e *MockCertificateAuthority_Expecter) RootCertificate() *MockCertificateAuthority_RootCertificate_Call {
357+ return &MockCertificateAuthority_RootCertificate_Call{Call: _e.mock.On("RootCertificate")}
358+}
359+
360+func (_c *MockCertificateAuthority_RootCertificate_Call) Run(run func()) *MockCertificateAuthority_RootCertificate_Call {
361+ _c.Call.Run(func(args mock.Arguments) {
362+ run()
363+ })
364+ return _c
365+}
366+
367+func (_c *MockCertificateAuthority_RootCertificate_Call) Return(bytes []byte) *MockCertificateAuthority_RootCertificate_Call {
368+ _c.Call.Return(bytes)
369+ return _c
370+}
371+
372+func (_c *MockCertificateAuthority_RootCertificate_Call) RunAndReturn(run func() []byte) *MockCertificateAuthority_RootCertificate_Call {
373+ _c.Call.Return(run)
374+ return _c
375+}
376+
377+// RootCertificateDER provides a mock function for the type MockCertificateAuthority
378+func (_mock *MockCertificateAuthority) RootCertificateDER() []byte {
379+ ret := _mock.Called()
380+
381+ if len(ret) == 0 {
382+ panic("no return value specified for RootCertificateDER")
383+ }
384+
385+ var r0 []byte
386+ if returnFunc, ok := ret.Get(0).(func() []byte); ok {
387+ r0 = returnFunc()
388+ } else {
389+ if ret.Get(0) != nil {
390+ r0 = ret.Get(0).([]byte)
391+ }
392+ }
393+ return r0
394+}
395+
396+// MockCertificateAuthority_RootCertificateDER_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RootCertificateDER'
397+type MockCertificateAuthority_RootCertificateDER_Call struct {
398+ *mock.Call
399+}
400+
401+// RootCertificateDER is a helper method to define mock.On call
402+func (_e *MockCertificateAuthority_Expecter) RootCertificateDER() *MockCertificateAuthority_RootCertificateDER_Call {
403+ return &MockCertificateAuthority_RootCertificateDER_Call{Call: _e.mock.On("RootCertificateDER")}
404+}
405+
406+func (_c *MockCertificateAuthority_RootCertificateDER_Call) Run(run func()) *MockCertificateAuthority_RootCertificateDER_Call {
407+ _c.Call.Run(func(args mock.Arguments) {
408+ run()
409+ })
410+ return _c
411+}
412+
413+func (_c *MockCertificateAuthority_RootCertificateDER_Call) Return(bytes []byte) *MockCertificateAuthority_RootCertificateDER_Call {
414+ _c.Call.Return(bytes)
415+ return _c
416+}
417+
418+func (_c *MockCertificateAuthority_RootCertificateDER_Call) RunAndReturn(run func() []byte) *MockCertificateAuthority_RootCertificateDER_Call {
419+ _c.Call.Return(run)
420+ return _c
421+}
422+
423+// RootCommonName provides a mock function for the type MockCertificateAuthority
424+func (_mock *MockCertificateAuthority) RootCommonName() string {
425+ ret := _mock.Called()
426+
427+ if len(ret) == 0 {
428+ panic("no return value specified for RootCommonName")
429+ }
430+
431+ var r0 string
432+ if returnFunc, ok := ret.Get(0).(func() string); ok {
433+ r0 = returnFunc()
434+ } else {
435+ r0 = ret.Get(0).(string)
436+ }
437+ return r0
438+}
439+
440+// MockCertificateAuthority_RootCommonName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RootCommonName'
441+type MockCertificateAuthority_RootCommonName_Call struct {
442+ *mock.Call
443+}
444+
445+// RootCommonName is a helper method to define mock.On call
446+func (_e *MockCertificateAuthority_Expecter) RootCommonName() *MockCertificateAuthority_RootCommonName_Call {
447+ return &MockCertificateAuthority_RootCommonName_Call{Call: _e.mock.On("RootCommonName")}
448+}
449+
450+func (_c *MockCertificateAuthority_RootCommonName_Call) Run(run func()) *MockCertificateAuthority_RootCommonName_Call {
451+ _c.Call.Run(func(args mock.Arguments) {
452+ run()
453+ })
454+ return _c
455+}
456+
457+func (_c *MockCertificateAuthority_RootCommonName_Call) Return(s string) *MockCertificateAuthority_RootCommonName_Call {
458+ _c.Call.Return(s)
459+ return _c
460+}
461+
462+func (_c *MockCertificateAuthority_RootCommonName_Call) RunAndReturn(run func() string) *MockCertificateAuthority_RootCommonName_Call {
463+ _c.Call.Return(run)
464+ return _c
465+}
466+
467+// RootFingerprint provides a mock function for the type MockCertificateAuthority
468+func (_mock *MockCertificateAuthority) RootFingerprint() string {
469+ ret := _mock.Called()
470+
471+ if len(ret) == 0 {
472+ panic("no return value specified for RootFingerprint")
473+ }
474+
475+ var r0 string
476+ if returnFunc, ok := ret.Get(0).(func() string); ok {
477+ r0 = returnFunc()
478+ } else {
479+ r0 = ret.Get(0).(string)
480+ }
481+ return r0
482+}
483+
484+// MockCertificateAuthority_RootFingerprint_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RootFingerprint'
485+type MockCertificateAuthority_RootFingerprint_Call struct {
486+ *mock.Call
487+}
488+
489+// RootFingerprint is a helper method to define mock.On call
490+func (_e *MockCertificateAuthority_Expecter) RootFingerprint() *MockCertificateAuthority_RootFingerprint_Call {
491+ return &MockCertificateAuthority_RootFingerprint_Call{Call: _e.mock.On("RootFingerprint")}
492+}
493+
494+func (_c *MockCertificateAuthority_RootFingerprint_Call) Run(run func()) *MockCertificateAuthority_RootFingerprint_Call {
495+ _c.Call.Run(func(args mock.Arguments) {
496+ run()
497+ })
498+ return _c
499+}
500+
501+func (_c *MockCertificateAuthority_RootFingerprint_Call) Return(s string) *MockCertificateAuthority_RootFingerprint_Call {
502+ _c.Call.Return(s)
503+ return _c
504+}
505+
506+func (_c *MockCertificateAuthority_RootFingerprint_Call) RunAndReturn(run func() string) *MockCertificateAuthority_RootFingerprint_Call {
507+ _c.Call.Return(run)
508+ return _c
509+}
510+
511+// UninstallRoot provides a mock function for the type MockCertificateAuthority
512+func (_mock *MockCertificateAuthority) UninstallRoot(cert *x509.Certificate) error {
513+ ret := _mock.Called(cert)
514+
515+ if len(ret) == 0 {
516+ panic("no return value specified for UninstallRoot")
517+ }
518+
519+ var r0 error
520+ if returnFunc, ok := ret.Get(0).(func(*x509.Certificate) error); ok {
521+ r0 = returnFunc(cert)
522+ } else {
523+ r0 = ret.Error(0)
524+ }
525+ return r0
526+}
527+
528+// MockCertificateAuthority_UninstallRoot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UninstallRoot'
529+type MockCertificateAuthority_UninstallRoot_Call struct {
530+ *mock.Call
531+}
532+
533+// UninstallRoot is a helper method to define mock.On call
534+// - cert *x509.Certificate
535+func (_e *MockCertificateAuthority_Expecter) UninstallRoot(cert any) *MockCertificateAuthority_UninstallRoot_Call {
536+ return &MockCertificateAuthority_UninstallRoot_Call{Call: _e.mock.On("UninstallRoot", cert)}
537+}
538+
539+func (_c *MockCertificateAuthority_UninstallRoot_Call) Run(run func(cert *x509.Certificate)) *MockCertificateAuthority_UninstallRoot_Call {
540+ _c.Call.Run(func(args mock.Arguments) {
541+ var arg0 *x509.Certificate
542+ if args[0] != nil {
543+ arg0 = args[0].(*x509.Certificate)
544+ }
545+ run(
546+ arg0,
547+ )
548+ })
549+ return _c
550+}
551+
552+func (_c *MockCertificateAuthority_UninstallRoot_Call) Return(err error) *MockCertificateAuthority_UninstallRoot_Call {
553+ _c.Call.Return(err)
554+ return _c
555+}
556+
557+func (_c *MockCertificateAuthority_UninstallRoot_Call) RunAndReturn(run func(cert *x509.Certificate) error) *MockCertificateAuthority_UninstallRoot_Call {
558+ _c.Call.Return(run)
559+ return _c
560+}
M internal/usecase/pki/service.go +15 -3
@@ -4,6 +4,7 @@ import (
44 "context"
55 "crypto/tls"
66 "crypto/x509"
7+ "fmt"
78 "strings"
89 "sync"
910 "time"
@@ -129,6 +130,17 @@ func (s *Service) validCachedCertificate(domainName string) *tls.Certificate {
129130 }
130131
131132 func (s *Service) issueCertificate(ctx context.Context, domainName string) (*tls.Certificate, error) {
133+ if ctx == nil {
134+ ctx = context.Background()
135+ }
136+ ctx = zerowrap.WithCtx(ctx, s.log)
137+ ctx = zerowrap.CtxWithFields(ctx, map[string]any{
138+ zerowrap.FieldLayer: "usecase",
139+ zerowrap.FieldUseCase: "IssueCertificate",
140+ "domain": domainName,
141+ })
142+ log := zerowrap.FromCtx(ctx)
143+
132144 if !s.isDomainAllowed(ctx, domainName) {
133145 return nil, nil
134146 }
@@ -138,15 +150,15 @@ func (s *Service) issueCertificate(ctx context.Context, domainName string) (*tls
138150
139151 cert, err := s.ca.IssueCertificate(domainName)
140152 if err != nil {
141- s.log.Error().Err(err).Str("domain", domainName).Msg("failed to issue leaf cert")
142- return nil, err
153+ log.Error().Err(err).Msg("failed to issue leaf certificate")
154+ return nil, fmt.Errorf("issue leaf certificate for %q: %w", domainName, err)
143155 }
144156 if !s.isDomainAllowed(ctx, domainName) {
145157 return nil, nil
146158 }
147159
148160 s.cache.Store(domainName, &cachedCert{cert: cert, expiresAt: certificateExpiry(cert, s.leafLifetime())})
149- s.log.Debug().Str("domain", domainName).Msg("issued new leaf certificate")
161+ log.Debug().Msg("issued new leaf certificate")
150162 return cert, nil
151163 }
152164
@@ -4,6 +4,7 @@ import (
4 "context"4 "context"
5 "crypto/tls"5 "crypto/tls"
6 "crypto/x509"6 "crypto/x509"
7+ "fmt"
7 "strings"8 "strings"
8 "sync"9 "sync"
9 "time"10 "time"
@@ -129,6 +130,17 @@ func (s *Service) validCachedCertificate(domainName string) *tls.Certificate {
129 }130 }
130 131
131 func (s *Service) issueCertificate(ctx context.Context, domainName string) (*tls.Certificate, error) {132 func (s *Service) issueCertificate(ctx context.Context, domainName string) (*tls.Certificate, error) {
133+ if ctx == nil {
134+ ctx = context.Background()
135+ }
136+ ctx = zerowrap.WithCtx(ctx, s.log)
137+ ctx = zerowrap.CtxWithFields(ctx, map[string]any{
138+ zerowrap.FieldLayer: "usecase",
139+ zerowrap.FieldUseCase: "IssueCertificate",
140+ "domain": domainName,
141+ })
142+ log := zerowrap.FromCtx(ctx)
143+
132 if !s.isDomainAllowed(ctx, domainName) {144 if !s.isDomainAllowed(ctx, domainName) {
133 return nil, nil145 return nil, nil
134 }146 }
@@ -138,15 +150,15 @@ func (s *Service) issueCertificate(ctx context.Context, domainName string) (*tls
138 150
139 cert, err := s.ca.IssueCertificate(domainName)151 cert, err := s.ca.IssueCertificate(domainName)
140 if err != nil {152 if err != nil {
141- s.log.Error().Err(err).Str("domain", domainName).Msg("failed to issue leaf cert")153+ log.Error().Err(err).Msg("failed to issue leaf certificate")
142- return nil, err154+ return nil, fmt.Errorf("issue leaf certificate for %q: %w", domainName, err)
143 }155 }
144 if !s.isDomainAllowed(ctx, domainName) {156 if !s.isDomainAllowed(ctx, domainName) {
145 return nil, nil157 return nil, nil
146 }158 }
147 159
148 s.cache.Store(domainName, &cachedCert{cert: cert, expiresAt: certificateExpiry(cert, s.leafLifetime())})160 s.cache.Store(domainName, &cachedCert{cert: cert, expiresAt: certificateExpiry(cert, s.leafLifetime())})
149- s.log.Debug().Str("domain", domainName).Msg("issued new leaf certificate")161+ log.Debug().Msg("issued new leaf certificate")
150 return cert, nil162 return cert, nil
151 }163 }
152 164
M internal/usecase/pki/service_test.go +51 -3
@@ -3,7 +3,9 @@ package pki_test
33 import (
44 "context"
55 "crypto/tls"
6+ "errors"
67 "testing"
8+ "time"
79
810 "github.com/bnema/zerowrap"
911 "github.com/stretchr/testify/assert"
@@ -133,15 +135,61 @@ func TestService_SetAdditionalDomains_RevokesConcurrentIssuance(t *testing.T) {
133135 errs <- getErr
134136 }()
135137
136- <-blockingCA.started
138+ require.Eventually(t, func() bool {
139+ select {
140+ case <-blockingCA.started:
141+ return true
142+ default:
143+ return false
144+ }
145+ }, time.Second, 10*time.Millisecond, "certificate issuance did not start")
137146 svc.SetAdditionalDomains([]string{"new.example.com"})
138147 close(blockingCA.release)
139148
140- require.NoError(t, <-errs)
141- assert.Nil(t, <-result)
149+ var getErr error
150+ require.Eventually(t, func() bool {
151+ select {
152+ case getErr = <-errs:
153+ return true
154+ default:
155+ return false
156+ }
157+ }, time.Second, 10*time.Millisecond, "certificate issuance did not return an error result")
158+ require.NoError(t, getErr)
159+
160+ var cert *tls.Certificate
161+ require.Eventually(t, func() bool {
162+ select {
163+ case cert = <-result:
164+ return true
165+ default:
166+ return false
167+ }
168+ }, time.Second, 10*time.Millisecond, "certificate issuance did not return a certificate result")
169+ assert.Nil(t, cert)
142170 assert.Equal(t, 0, svc.CachedCertCount())
143171 }
144172
173+func TestService_GetCertificate_WrapsIssuanceError(t *testing.T) {
174+ issuerErr := errors.New("issuer unavailable")
175+ ca := mocks.NewMockCertificateAuthority(t)
176+ ca.EXPECT().IntermediateExpiresAt().Return(time.Now().Add(24 * time.Hour)).Maybe()
177+ ca.EXPECT().IntermediateLifetime().Return(24 * time.Hour).Maybe()
178+ ca.EXPECT().IssueCertificate("broken.example.com").Return(nil, issuerErr).Once()
179+ cfg := newRouteCheckerMock(t, "broken.example.com")
180+
181+ ctx, cancel := context.WithCancel(t.Context())
182+ defer cancel()
183+ svc := pkiusecase.NewService(ctx, ca, cfg, nil, testLogger())
184+ defer svc.Stop()
185+
186+ cert, err := svc.GetCertificate(&tls.ClientHelloInfo{ServerName: "broken.example.com"})
187+
188+ assert.Nil(t, cert)
189+ require.ErrorIs(t, err, issuerErr)
190+ assert.Equal(t, `issue leaf certificate for "broken.example.com": issuer unavailable`, err.Error())
191+}
192+
145193 func TestService_GetCertificate_UnknownDomain(t *testing.T) {
146194 dir := t.TempDir()
147195 ca, err := pkiadapter.NewCA(dir, testLogger())
@@ -3,7 +3,9 @@ package pki_test
3 import (3 import (
4 "context"4 "context"
5 "crypto/tls"5 "crypto/tls"
6+ "errors"
6 "testing"7 "testing"
8+ "time"
7 9
8 "github.com/bnema/zerowrap"10 "github.com/bnema/zerowrap"
9 "github.com/stretchr/testify/assert"11 "github.com/stretchr/testify/assert"
@@ -133,15 +135,61 @@ func TestService_SetAdditionalDomains_RevokesConcurrentIssuance(t *testing.T) {
133 errs <- getErr135 errs <- getErr
134 }()136 }()
135 137
136- <-blockingCA.started138+ require.Eventually(t, func() bool {
139+ select {
140+ case <-blockingCA.started:
141+ return true
142+ default:
143+ return false
144+ }
145+ }, time.Second, 10*time.Millisecond, "certificate issuance did not start")
137 svc.SetAdditionalDomains([]string{"new.example.com"})146 svc.SetAdditionalDomains([]string{"new.example.com"})
138 close(blockingCA.release)147 close(blockingCA.release)
139 148
140- require.NoError(t, <-errs)149+ var getErr error
141- assert.Nil(t, <-result)150+ require.Eventually(t, func() bool {
151+ select {
152+ case getErr = <-errs:
153+ return true
154+ default:
155+ return false
156+ }
157+ }, time.Second, 10*time.Millisecond, "certificate issuance did not return an error result")
158+ require.NoError(t, getErr)
159+
160+ var cert *tls.Certificate
161+ require.Eventually(t, func() bool {
162+ select {
163+ case cert = <-result:
164+ return true
165+ default:
166+ return false
167+ }
168+ }, time.Second, 10*time.Millisecond, "certificate issuance did not return a certificate result")
169+ assert.Nil(t, cert)
142 assert.Equal(t, 0, svc.CachedCertCount())170 assert.Equal(t, 0, svc.CachedCertCount())
143 }171 }
144 172
173+func TestService_GetCertificate_WrapsIssuanceError(t *testing.T) {
174+ issuerErr := errors.New("issuer unavailable")
175+ ca := mocks.NewMockCertificateAuthority(t)
176+ ca.EXPECT().IntermediateExpiresAt().Return(time.Now().Add(24 * time.Hour)).Maybe()
177+ ca.EXPECT().IntermediateLifetime().Return(24 * time.Hour).Maybe()
178+ ca.EXPECT().IssueCertificate("broken.example.com").Return(nil, issuerErr).Once()
179+ cfg := newRouteCheckerMock(t, "broken.example.com")
180+
181+ ctx, cancel := context.WithCancel(t.Context())
182+ defer cancel()
183+ svc := pkiusecase.NewService(ctx, ca, cfg, nil, testLogger())
184+ defer svc.Stop()
185+
186+ cert, err := svc.GetCertificate(&tls.ClientHelloInfo{ServerName: "broken.example.com"})
187+
188+ assert.Nil(t, cert)
189+ require.ErrorIs(t, err, issuerErr)
190+ assert.Equal(t, `issue leaf certificate for "broken.example.com": issuer unavailable`, err.Error())
191+}
192+
145 func TestService_GetCertificate_UnknownDomain(t *testing.T) {193 func TestService_GetCertificate_UnknownDomain(t *testing.T) {
146 dir := t.TempDir()194 dir := t.TempDir()
147 ca, err := pkiadapter.NewCA(dir, testLogger())195 ca, err := pkiadapter.NewCA(dir, testLogger())

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close