detached 32363a8

refactor(pki): simplify after code review

- Remove redundant rootDER field; derive from rootCert.Raw
- Cache mobileconfig at handler construction instead of per-request
- Use direct map lookup in isDomainAllowed for external routes
- Remove narrating comments in service.go and ca.go
commit
32363a88abdbda80c8e5f8fad2819734fdb3f299
author
brice · · 2026-03-29 17:57 UTC
parent
dddcd1a
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +10 -22
M internal/adapters/in/http/onboarding/handler.go +7 -10
@@ -10,19 +10,17 @@ import (
1010
1111 // Handler serves CA onboarding endpoints on the HTTP port.
1212 type Handler struct {
13- rootPEM []byte
14- rootDER []byte
15- rootCN string
16- cidrs []*net.IPNet // Cloudflare CIDRs — if source IP matches, don't serve onboarding
13+ rootPEM []byte
14+ mobileconfig []byte
15+ cidrs []*net.IPNet
1716 }
1817
1918 // NewHandler creates an onboarding handler.
2019 func NewHandler(rootPEM, rootDER []byte, rootCN string, cloudflareCIDRs []*net.IPNet) *Handler {
2120 return &Handler{
22- rootPEM: rootPEM,
23- rootDER: rootDER,
24- rootCN: rootCN,
25- cidrs: cloudflareCIDRs,
21+ rootPEM: rootPEM,
22+ mobileconfig: pkiadapter.GenerateMobileconfig(rootDER, rootCN),
23+ cidrs: cloudflareCIDRs,
2624 }
2725 }
2826
@@ -35,10 +33,9 @@ func (h *Handler) ServeCACert(w http.ResponseWriter, _ *http.Request) {
3533
3634 // ServeMobileconfig serves an iOS configuration profile with the root CA.
3735 func (h *Handler) ServeMobileconfig(w http.ResponseWriter, _ *http.Request) {
38- mc := pkiadapter.GenerateMobileconfig(h.rootDER, h.rootCN)
3936 w.Header().Set("Content-Type", "application/x-apple-aspen-config")
4037 w.Header().Set("Content-Disposition", "attachment; filename=gordon-ca.mobileconfig")
41- _, _ = w.Write(mc)
38+ _, _ = w.Write(h.mobileconfig)
4239 }
4340
4441 // ServeOnboardingPage serves the CA trust onboarding HTML page.
@@ -10,19 +10,17 @@ import (
10 10
11 // Handler serves CA onboarding endpoints on the HTTP port.11 // Handler serves CA onboarding endpoints on the HTTP port.
12 type Handler struct {12 type Handler struct {
13- rootPEM []byte13+ rootPEM []byte
14- rootDER []byte14+ mobileconfig []byte
15- rootCN string15+ cidrs []*net.IPNet
16- cidrs []*net.IPNet // Cloudflare CIDRs — if source IP matches, don't serve onboarding
17 }16 }
18 17
19 // NewHandler creates an onboarding handler.18 // NewHandler creates an onboarding handler.
20 func NewHandler(rootPEM, rootDER []byte, rootCN string, cloudflareCIDRs []*net.IPNet) *Handler {19 func NewHandler(rootPEM, rootDER []byte, rootCN string, cloudflareCIDRs []*net.IPNet) *Handler {
21 return &Handler{20 return &Handler{
22- rootPEM: rootPEM,21+ rootPEM: rootPEM,
23- rootDER: rootDER,22+ mobileconfig: pkiadapter.GenerateMobileconfig(rootDER, rootCN),
24- rootCN: rootCN,23+ cidrs: cloudflareCIDRs,
25- cidrs: cloudflareCIDRs,
26 }24 }
27 }25 }
28 26
@@ -35,10 +33,9 @@ func (h *Handler) ServeCACert(w http.ResponseWriter, _ *http.Request) {
35 33
36 // ServeMobileconfig serves an iOS configuration profile with the root CA.34 // ServeMobileconfig serves an iOS configuration profile with the root CA.
37 func (h *Handler) ServeMobileconfig(w http.ResponseWriter, _ *http.Request) {35 func (h *Handler) ServeMobileconfig(w http.ResponseWriter, _ *http.Request) {
38- mc := pkiadapter.GenerateMobileconfig(h.rootDER, h.rootCN)
39 w.Header().Set("Content-Type", "application/x-apple-aspen-config")36 w.Header().Set("Content-Type", "application/x-apple-aspen-config")
40 w.Header().Set("Content-Disposition", "attachment; filename=gordon-ca.mobileconfig")37 w.Header().Set("Content-Disposition", "attachment; filename=gordon-ca.mobileconfig")
41- _, _ = w.Write(mc)38+ _, _ = w.Write(h.mobileconfig)
42 }39 }
43 40
44 // ServeOnboardingPage serves the CA trust onboarding HTML page.41 // ServeOnboardingPage serves the CA trust onboarding HTML page.
M internal/adapters/out/pki/ca.go +1 -5
@@ -44,7 +44,6 @@ type CA struct {
4444 rootCert *x509.Certificate
4545 rootKey crypto.Signer
4646 rootPEM []byte
47- rootDER []byte
4847
4948 mu sync.RWMutex
5049 interCert *x509.Certificate
@@ -82,7 +81,7 @@ func NewCA(dataDir string, log zerowrap.Logger) (*CA, error) {
8281 func (ca *CA) RootCertificate() []byte { return ca.rootPEM }
8382
8483 // RootCertificateDER returns the root CA certificate in DER format.
85-func (ca *CA) RootCertificateDER() []byte { return ca.rootDER }
84+func (ca *CA) RootCertificateDER() []byte { return ca.rootCert.Raw }
8685
8786 // RootFingerprint returns the SHA-256 fingerprint of the root CA cert
8887 // formatted as colon-separated hex.
@@ -138,7 +137,6 @@ func (ca *CA) loadOrGenerateRoot() error {
138137 ca.rootCert = cert
139138 ca.rootKey = key
140139 ca.rootPEM = certPEM
141- ca.rootDER = cert.Raw
142140 ca.log.Info().Str("cn", cert.Subject.CommonName).Msg("loaded existing root CA")
143141 return nil
144142 }
@@ -197,7 +195,6 @@ func (ca *CA) loadOrGenerateRoot() error {
197195 ca.rootCert = cert
198196 ca.rootKey = key
199197 ca.rootPEM = certPEM
200- ca.rootDER = certDER
201198 ca.log.Info().Str("cn", cert.Subject.CommonName).Msg("generated new root CA")
202199 return nil
203200 }
@@ -331,7 +328,6 @@ func (ca *CA) IssueCertificate(domain string) (*tls.Certificate, error) {
331328 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
332329 }
333330
334- // AIA extension: URL where clients can fetch the CA cert.
335331 if ca.aiaURL != "" {
336332 template.IssuingCertificateURL = []string{ca.aiaURL}
337333 }
@@ -44,7 +44,6 @@ type CA struct {
44 rootCert *x509.Certificate44 rootCert *x509.Certificate
45 rootKey crypto.Signer45 rootKey crypto.Signer
46 rootPEM []byte46 rootPEM []byte
47- rootDER []byte
48 47
49 mu sync.RWMutex48 mu sync.RWMutex
50 interCert *x509.Certificate49 interCert *x509.Certificate
@@ -82,7 +81,7 @@ func NewCA(dataDir string, log zerowrap.Logger) (*CA, error) {
82 func (ca *CA) RootCertificate() []byte { return ca.rootPEM }81 func (ca *CA) RootCertificate() []byte { return ca.rootPEM }
83 82
84 // RootCertificateDER returns the root CA certificate in DER format.83 // RootCertificateDER returns the root CA certificate in DER format.
85-func (ca *CA) RootCertificateDER() []byte { return ca.rootDER }84+func (ca *CA) RootCertificateDER() []byte { return ca.rootCert.Raw }
86 85
87 // RootFingerprint returns the SHA-256 fingerprint of the root CA cert86 // RootFingerprint returns the SHA-256 fingerprint of the root CA cert
88 // formatted as colon-separated hex.87 // formatted as colon-separated hex.
@@ -138,7 +137,6 @@ func (ca *CA) loadOrGenerateRoot() error {
138 ca.rootCert = cert137 ca.rootCert = cert
139 ca.rootKey = key138 ca.rootKey = key
140 ca.rootPEM = certPEM139 ca.rootPEM = certPEM
141- ca.rootDER = cert.Raw
142 ca.log.Info().Str("cn", cert.Subject.CommonName).Msg("loaded existing root CA")140 ca.log.Info().Str("cn", cert.Subject.CommonName).Msg("loaded existing root CA")
143 return nil141 return nil
144 }142 }
@@ -197,7 +195,6 @@ func (ca *CA) loadOrGenerateRoot() error {
197 ca.rootCert = cert195 ca.rootCert = cert
198 ca.rootKey = key196 ca.rootKey = key
199 ca.rootPEM = certPEM197 ca.rootPEM = certPEM
200- ca.rootDER = certDER
201 ca.log.Info().Str("cn", cert.Subject.CommonName).Msg("generated new root CA")198 ca.log.Info().Str("cn", cert.Subject.CommonName).Msg("generated new root CA")
202 return nil199 return nil
203 }200 }
@@ -331,7 +328,6 @@ func (ca *CA) IssueCertificate(domain string) (*tls.Certificate, error) {
331 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},328 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
332 }329 }
333 330
334- // AIA extension: URL where clients can fetch the CA cert.
335 if ca.aiaURL != "" {331 if ca.aiaURL != "" {
336 template.IssuingCertificateURL = []string{ca.aiaURL}332 template.IssuingCertificateURL = []string{ca.aiaURL}
337 }333 }
M internal/usecase/pki/service.go +2 -7
@@ -78,7 +78,6 @@ func (s *Service) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
7878 return nil, fmt.Errorf("no SNI in ClientHello")
7979 }
8080
81- // Check cache
8281 if entry, ok := s.cache.Load(domain); ok {
8382 cached := entry.(*cachedCert)
8483 if time.Now().Before(cached.expiresAt) {
@@ -87,12 +86,10 @@ func (s *Service) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
8786 s.cache.Delete(domain)
8887 }
8988
90- // Check allowlist
9189 if !s.isDomainAllowed(hello.Context(), domain) {
9290 return nil, fmt.Errorf("domain %q not in route table", domain)
9391 }
9492
95- // Issue new cert
9693 cert, err := s.ca.IssueCertificate(domain)
9794 if err != nil {
9895 s.log.Error().Err(err).Str("domain", domain).Msg("failed to issue leaf cert")
@@ -114,10 +111,8 @@ func (s *Service) isDomainAllowed(ctx context.Context, domain string) bool {
114111 return true
115112 }
116113 }
117- for d := range s.routes.GetExternalRoutes() {
118- if d == domain {
119- return true
120- }
114+ if _, ok := s.routes.GetExternalRoutes()[domain]; ok {
115+ return true
121116 }
122117 return false
123118 }
@@ -78,7 +78,6 @@ func (s *Service) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
78 return nil, fmt.Errorf("no SNI in ClientHello")78 return nil, fmt.Errorf("no SNI in ClientHello")
79 }79 }
80 80
81- // Check cache
82 if entry, ok := s.cache.Load(domain); ok {81 if entry, ok := s.cache.Load(domain); ok {
83 cached := entry.(*cachedCert)82 cached := entry.(*cachedCert)
84 if time.Now().Before(cached.expiresAt) {83 if time.Now().Before(cached.expiresAt) {
@@ -87,12 +86,10 @@ func (s *Service) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
87 s.cache.Delete(domain)86 s.cache.Delete(domain)
88 }87 }
89 88
90- // Check allowlist
91 if !s.isDomainAllowed(hello.Context(), domain) {89 if !s.isDomainAllowed(hello.Context(), domain) {
92 return nil, fmt.Errorf("domain %q not in route table", domain)90 return nil, fmt.Errorf("domain %q not in route table", domain)
93 }91 }
94 92
95- // Issue new cert
96 cert, err := s.ca.IssueCertificate(domain)93 cert, err := s.ca.IssueCertificate(domain)
97 if err != nil {94 if err != nil {
98 s.log.Error().Err(err).Str("domain", domain).Msg("failed to issue leaf cert")95 s.log.Error().Err(err).Str("domain", domain).Msg("failed to issue leaf cert")
@@ -114,10 +111,8 @@ func (s *Service) isDomainAllowed(ctx context.Context, domain string) bool {
114 return true111 return true
115 }112 }
116 }113 }
117- for d := range s.routes.GetExternalRoutes() {114+ if _, ok := s.routes.GetExternalRoutes()[domain]; ok {
118- if d == domain {115+ return true
119- return true
120- }
121 }116 }
122 return false117 return false
123 }118 }

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