detached b026f15

Address ACME hardening review feedback

commit
b026f158cbd3028768585456f747241b788d1981
author
brice · · 2026-04-30 07:24 UTC
parent
d015ae1
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +35 -53
M docs/config/server.md +3 -1
@@ -108,6 +108,8 @@ HTTP-01 requires public access to port 80 for each hostname being validated. If
108108
109109 Gordon limits new ACME certificate orders to `obtain_batch_size` per reconcile run (default `1`) so enabling ACME on an existing multi-route server does not burst through every route and hit Let's Encrypt rate limits. Later reloads, restarts, or renewal-loop reconciles continue issuing remaining certificates.
110110
111+If the initial ACME reconcile fails (e.g. due to a transient network error or misconfiguration), Gordon logs the failure and does not start the renewal loop. Operators should fix the underlying error and restart/reload Gordon to trigger a new reconcile attempt.
112+
111113 For Cloudflare Full/Strict, Cloudflare terminates browser TLS at the edge and connects to Gordon over HTTPS. A public ACME certificate served by Gordon is the preferred origin certificate because Cloudflare Strict can validate it without custom origin trust. Cloudflare Flexible mode (HTTPS at the edge, HTTP to Gordon) remains a legacy/compatibility option, but it is not end-to-end HTTPS.
112114
113115 Static certificates have priority, then public ACME certificates, then Gordon's internal CA fallback. Gordon uses the `go-acme/lego` ACME client, including its DNS-provider support for Cloudflare DNS-01.
@@ -123,7 +125,7 @@ When `tls_port` is non-zero, direct HTTP clients (those not arriving through a t
123125
124126 This lets new clients discover and trust the internal CA over plain HTTP without exposing the full application. Trusted proxy traffic (e.g. from Cloudflare) continues through the normal HTTP proxy path unaffected.
125127
126-On HTTPS, onboarding paths are served only on `server.gordon_domain`; app hosts can use `/ca` for their own routes without Gordon intercepting it.
128+On HTTPS, onboarding paths are served only on `server.gordon_domain`; app hosts can use `/ca` for their own routes without Gordon intercepting it. If `gordon_domain` is empty, HTTPS onboarding paths are not served at all to avoid intercepting app hosts.
127129
128130 #### HTTP to HTTPS redirect
129131
@@ -108,6 +108,8 @@ HTTP-01 requires public access to port 80 for each hostname being validated. If
108 108
109 Gordon limits new ACME certificate orders to `obtain_batch_size` per reconcile run (default `1`) so enabling ACME on an existing multi-route server does not burst through every route and hit Let's Encrypt rate limits. Later reloads, restarts, or renewal-loop reconciles continue issuing remaining certificates.109 Gordon limits new ACME certificate orders to `obtain_batch_size` per reconcile run (default `1`) so enabling ACME on an existing multi-route server does not burst through every route and hit Let's Encrypt rate limits. Later reloads, restarts, or renewal-loop reconciles continue issuing remaining certificates.
110 110
111+If the initial ACME reconcile fails (e.g. due to a transient network error or misconfiguration), Gordon logs the failure and does not start the renewal loop. Operators should fix the underlying error and restart/reload Gordon to trigger a new reconcile attempt.
112+
111 For Cloudflare Full/Strict, Cloudflare terminates browser TLS at the edge and connects to Gordon over HTTPS. A public ACME certificate served by Gordon is the preferred origin certificate because Cloudflare Strict can validate it without custom origin trust. Cloudflare Flexible mode (HTTPS at the edge, HTTP to Gordon) remains a legacy/compatibility option, but it is not end-to-end HTTPS.113 For Cloudflare Full/Strict, Cloudflare terminates browser TLS at the edge and connects to Gordon over HTTPS. A public ACME certificate served by Gordon is the preferred origin certificate because Cloudflare Strict can validate it without custom origin trust. Cloudflare Flexible mode (HTTPS at the edge, HTTP to Gordon) remains a legacy/compatibility option, but it is not end-to-end HTTPS.
112 114
113 Static certificates have priority, then public ACME certificates, then Gordon's internal CA fallback. Gordon uses the `go-acme/lego` ACME client, including its DNS-provider support for Cloudflare DNS-01.115 Static certificates have priority, then public ACME certificates, then Gordon's internal CA fallback. Gordon uses the `go-acme/lego` ACME client, including its DNS-provider support for Cloudflare DNS-01.
@@ -123,7 +125,7 @@ When `tls_port` is non-zero, direct HTTP clients (those not arriving through a t
123 125
124 This lets new clients discover and trust the internal CA over plain HTTP without exposing the full application. Trusted proxy traffic (e.g. from Cloudflare) continues through the normal HTTP proxy path unaffected.126 This lets new clients discover and trust the internal CA over plain HTTP without exposing the full application. Trusted proxy traffic (e.g. from Cloudflare) continues through the normal HTTP proxy path unaffected.
125 127
126-On HTTPS, onboarding paths are served only on `server.gordon_domain`; app hosts can use `/ca` for their own routes without Gordon intercepting it.128+On HTTPS, onboarding paths are served only on `server.gordon_domain`; app hosts can use `/ca` for their own routes without Gordon intercepting it. If `gordon_domain` is empty, HTTPS onboarding paths are not served at all to avoid intercepting app hosts.
127 129
128 #### HTTP to HTTPS redirect130 #### HTTP to HTTPS redirect
129 131
M docs/superpowers/plans/2026-04-30-acme-http-onboarding-hardening.md +10 -10
@@ -18,24 +18,24 @@
1818 - Modify: `internal/app/run_http_handlers_test.go`
1919 - Modify/Create: `internal/app/public_tls_startup_test.go` if helper isolation is needed
2020
21-#### Sub-task 1: Well-known HTTP onboarding paths
21+### Sub-task 1: Well-known HTTP onboarding paths
2222
2323 - [ ] Add tests showing direct HTTP serves `/.well-known/gordon/`, `/.well-known/gordon/ca.crt`, and `/.well-known/gordon/ca.mobileconfig`.
2424 - [ ] Keep existing direct HTTP `/`, `/ca`, `/ca.crt`, `/ca.mobileconfig` compatibility tests green.
2525 - [ ] Run `rtk go test ./internal/app/... -run 'TestCreateHTTPHandlers_DirectHTTP.*Gordon|TestCreateHTTPHandlers_DirectHTTP.*CA'` and confirm new tests fail before implementation.
2626
27-#### Sub-task 2: HTTPS host gating
27+### Sub-task 2: HTTPS host gating
2828
2929 - [ ] Add tests showing `httpsHandler` serves `/ca` only when `req.Host == cfg.Server.GordonDomain`.
3030 - [ ] Add tests showing `/ca` on an arbitrary app host reaches the proxy path instead of onboarding.
3131 - [ ] Run `rtk go test ./internal/app/... -run 'TestCreateHTTPHandlers_HTTPSOnboarding'` and confirm the arbitrary-host test fails before implementation.
3232
33-#### Sub-task 3: Delayed ACME runtime startup
33+### Sub-task 3: Delayed ACME runtime startup
3434
3535 - [ ] Add a small app-level test helper/fake proving runtime public TLS `Reconcile` and `StartRenewalLoop` happen after proxy readiness.
3636 - [ ] Run the targeted test and confirm it fails with current service-construction-time reconciliation.
3737
38-#### Sub-task 4: ACME obtain batch limiting
38+### Sub-task 4: ACME obtain batch limiting
3939
4040 - [ ] Add usecase tests proving one reconcile run obtains only a bounded number of missing certificates.
4141 - [ ] Add a test proving a later reconcile run can continue with the remaining missing certificates.
@@ -53,25 +53,25 @@
5353 - Modify: `internal/app/run.go`
5454 - Modify: `internal/app/run_http_handlers_test.go`
5555
56-#### Sub-task 1: Onboarding route changes
56+### Sub-task 1: Onboarding route changes
5757
5858 - [ ] Add `/.well-known/gordon/` direct HTTP onboarding routes.
5959 - [ ] Keep legacy direct HTTP `/`, `/ca`, `/ca.crt`, `/ca.mobileconfig` routes.
6060 - [ ] Replace unconditional HTTPS `/ca*` registrations with a host-gated handler that only serves onboarding on `server.gordon_domain`.
6161
62-#### Sub-task 2: Delayed runtime ACME startup
62+### Sub-task 2: Delayed runtime ACME startup
6363
6464 - [ ] Change runtime service creation to initialize public TLS without immediate reconcile/renewal loop side effects.
6565 - [ ] After registry, HTTP proxy, and TLS listeners are ready, call public TLS `Reconcile` and `StartRenewalLoop`.
6666 - [ ] Preserve local CLI read-only behavior: `NewKernelQuiet` must still avoid reconcile/renewal loop side effects.
6767
68-#### Sub-task 3: ACME obtain batch limiting
68+### Sub-task 3: ACME obtain batch limiting
6969
7070 - [ ] Add a conservative default obtain batch size for runtime reconciliation.
7171 - [ ] Make `Service.Reconcile` process at most the configured number of missing targets per run.
7272 - [ ] Log when targets remain pending so operators understand that issuance is intentionally staged.
7373
74-#### Sub-task 4: Compatibility check
74+### Sub-task 4: Compatibility check
7575
7676 - [ ] Verify ACME challenge handler is still registered before onboarding/proxy handlers.
7777 - [ ] Verify trusted proxy traffic keeps existing behavior.
@@ -91,7 +91,7 @@
9191 - Modify: `docs/cli/tls.md` if status docs need wording
9292 - Modify: `docs/config/server.md` to document Cloudflare Full/Strict origin HTTPS expectations
9393
94-#### Sub-task 1: Documentation
94+### Sub-task 1: Documentation
9595
9696 - [ ] Document that gray-cloud HTTP-01 requires public/NAT/firewall access to port 80.
9797 - [ ] Document that Cloudflare-only IP allowlisting on port 80 is incompatible with gray-cloud HTTP-01; use DNS-01 or open 80 for direct validation.
@@ -99,7 +99,7 @@
9999 - [ ] Explain Cloudflare flexible HTTP origin as legacy/explicit, not preferred for end-to-end HTTPS.
100100 - [ ] Document Cloudflare Full/Strict expected flow: browser -> Cloudflare HTTPS edge cert, Cloudflare -> Gordon HTTPS origin cert. Public ACME certs served by Gordon are the preferred origin certificate because Cloudflare Strict can validate them without a custom origin trust setup.
101101
102-#### Sub-task 2: Verification
102+### Sub-task 2: Verification
103103
104104 - [ ] Run `rtk gofmt -w` on changed Go files.
105105 - [ ] Run targeted app tests.
@@ -18,24 +18,24 @@
18 - Modify: `internal/app/run_http_handlers_test.go`18 - Modify: `internal/app/run_http_handlers_test.go`
19 - Modify/Create: `internal/app/public_tls_startup_test.go` if helper isolation is needed19 - Modify/Create: `internal/app/public_tls_startup_test.go` if helper isolation is needed
20 20
21-#### Sub-task 1: Well-known HTTP onboarding paths21+### Sub-task 1: Well-known HTTP onboarding paths
22 22
23 - [ ] Add tests showing direct HTTP serves `/.well-known/gordon/`, `/.well-known/gordon/ca.crt`, and `/.well-known/gordon/ca.mobileconfig`.23 - [ ] Add tests showing direct HTTP serves `/.well-known/gordon/`, `/.well-known/gordon/ca.crt`, and `/.well-known/gordon/ca.mobileconfig`.
24 - [ ] Keep existing direct HTTP `/`, `/ca`, `/ca.crt`, `/ca.mobileconfig` compatibility tests green.24 - [ ] Keep existing direct HTTP `/`, `/ca`, `/ca.crt`, `/ca.mobileconfig` compatibility tests green.
25 - [ ] Run `rtk go test ./internal/app/... -run 'TestCreateHTTPHandlers_DirectHTTP.*Gordon|TestCreateHTTPHandlers_DirectHTTP.*CA'` and confirm new tests fail before implementation.25 - [ ] Run `rtk go test ./internal/app/... -run 'TestCreateHTTPHandlers_DirectHTTP.*Gordon|TestCreateHTTPHandlers_DirectHTTP.*CA'` and confirm new tests fail before implementation.
26 26
27-#### Sub-task 2: HTTPS host gating27+### Sub-task 2: HTTPS host gating
28 28
29 - [ ] Add tests showing `httpsHandler` serves `/ca` only when `req.Host == cfg.Server.GordonDomain`.29 - [ ] Add tests showing `httpsHandler` serves `/ca` only when `req.Host == cfg.Server.GordonDomain`.
30 - [ ] Add tests showing `/ca` on an arbitrary app host reaches the proxy path instead of onboarding.30 - [ ] Add tests showing `/ca` on an arbitrary app host reaches the proxy path instead of onboarding.
31 - [ ] Run `rtk go test ./internal/app/... -run 'TestCreateHTTPHandlers_HTTPSOnboarding'` and confirm the arbitrary-host test fails before implementation.31 - [ ] Run `rtk go test ./internal/app/... -run 'TestCreateHTTPHandlers_HTTPSOnboarding'` and confirm the arbitrary-host test fails before implementation.
32 32
33-#### Sub-task 3: Delayed ACME runtime startup33+### Sub-task 3: Delayed ACME runtime startup
34 34
35 - [ ] Add a small app-level test helper/fake proving runtime public TLS `Reconcile` and `StartRenewalLoop` happen after proxy readiness.35 - [ ] Add a small app-level test helper/fake proving runtime public TLS `Reconcile` and `StartRenewalLoop` happen after proxy readiness.
36 - [ ] Run the targeted test and confirm it fails with current service-construction-time reconciliation.36 - [ ] Run the targeted test and confirm it fails with current service-construction-time reconciliation.
37 37
38-#### Sub-task 4: ACME obtain batch limiting38+### Sub-task 4: ACME obtain batch limiting
39 39
40 - [ ] Add usecase tests proving one reconcile run obtains only a bounded number of missing certificates.40 - [ ] Add usecase tests proving one reconcile run obtains only a bounded number of missing certificates.
41 - [ ] Add a test proving a later reconcile run can continue with the remaining missing certificates.41 - [ ] Add a test proving a later reconcile run can continue with the remaining missing certificates.
@@ -53,25 +53,25 @@
53 - Modify: `internal/app/run.go`53 - Modify: `internal/app/run.go`
54 - Modify: `internal/app/run_http_handlers_test.go`54 - Modify: `internal/app/run_http_handlers_test.go`
55 55
56-#### Sub-task 1: Onboarding route changes56+### Sub-task 1: Onboarding route changes
57 57
58 - [ ] Add `/.well-known/gordon/` direct HTTP onboarding routes.58 - [ ] Add `/.well-known/gordon/` direct HTTP onboarding routes.
59 - [ ] Keep legacy direct HTTP `/`, `/ca`, `/ca.crt`, `/ca.mobileconfig` routes.59 - [ ] Keep legacy direct HTTP `/`, `/ca`, `/ca.crt`, `/ca.mobileconfig` routes.
60 - [ ] Replace unconditional HTTPS `/ca*` registrations with a host-gated handler that only serves onboarding on `server.gordon_domain`.60 - [ ] Replace unconditional HTTPS `/ca*` registrations with a host-gated handler that only serves onboarding on `server.gordon_domain`.
61 61
62-#### Sub-task 2: Delayed runtime ACME startup62+### Sub-task 2: Delayed runtime ACME startup
63 63
64 - [ ] Change runtime service creation to initialize public TLS without immediate reconcile/renewal loop side effects.64 - [ ] Change runtime service creation to initialize public TLS without immediate reconcile/renewal loop side effects.
65 - [ ] After registry, HTTP proxy, and TLS listeners are ready, call public TLS `Reconcile` and `StartRenewalLoop`.65 - [ ] After registry, HTTP proxy, and TLS listeners are ready, call public TLS `Reconcile` and `StartRenewalLoop`.
66 - [ ] Preserve local CLI read-only behavior: `NewKernelQuiet` must still avoid reconcile/renewal loop side effects.66 - [ ] Preserve local CLI read-only behavior: `NewKernelQuiet` must still avoid reconcile/renewal loop side effects.
67 67
68-#### Sub-task 3: ACME obtain batch limiting68+### Sub-task 3: ACME obtain batch limiting
69 69
70 - [ ] Add a conservative default obtain batch size for runtime reconciliation.70 - [ ] Add a conservative default obtain batch size for runtime reconciliation.
71 - [ ] Make `Service.Reconcile` process at most the configured number of missing targets per run.71 - [ ] Make `Service.Reconcile` process at most the configured number of missing targets per run.
72 - [ ] Log when targets remain pending so operators understand that issuance is intentionally staged.72 - [ ] Log when targets remain pending so operators understand that issuance is intentionally staged.
73 73
74-#### Sub-task 4: Compatibility check74+### Sub-task 4: Compatibility check
75 75
76 - [ ] Verify ACME challenge handler is still registered before onboarding/proxy handlers.76 - [ ] Verify ACME challenge handler is still registered before onboarding/proxy handlers.
77 - [ ] Verify trusted proxy traffic keeps existing behavior.77 - [ ] Verify trusted proxy traffic keeps existing behavior.
@@ -91,7 +91,7 @@
91 - Modify: `docs/cli/tls.md` if status docs need wording91 - Modify: `docs/cli/tls.md` if status docs need wording
92 - Modify: `docs/config/server.md` to document Cloudflare Full/Strict origin HTTPS expectations92 - Modify: `docs/config/server.md` to document Cloudflare Full/Strict origin HTTPS expectations
93 93
94-#### Sub-task 1: Documentation94+### Sub-task 1: Documentation
95 95
96 - [ ] Document that gray-cloud HTTP-01 requires public/NAT/firewall access to port 80.96 - [ ] Document that gray-cloud HTTP-01 requires public/NAT/firewall access to port 80.
97 - [ ] Document that Cloudflare-only IP allowlisting on port 80 is incompatible with gray-cloud HTTP-01; use DNS-01 or open 80 for direct validation.97 - [ ] Document that Cloudflare-only IP allowlisting on port 80 is incompatible with gray-cloud HTTP-01; use DNS-01 or open 80 for direct validation.
@@ -99,7 +99,7 @@
99 - [ ] Explain Cloudflare flexible HTTP origin as legacy/explicit, not preferred for end-to-end HTTPS.99 - [ ] Explain Cloudflare flexible HTTP origin as legacy/explicit, not preferred for end-to-end HTTPS.
100 - [ ] Document Cloudflare Full/Strict expected flow: browser -> Cloudflare HTTPS edge cert, Cloudflare -> Gordon HTTPS origin cert. Public ACME certs served by Gordon are the preferred origin certificate because Cloudflare Strict can validate them without a custom origin trust setup.100 - [ ] Document Cloudflare Full/Strict expected flow: browser -> Cloudflare HTTPS edge cert, Cloudflare -> Gordon HTTPS origin cert. Public ACME certs served by Gordon are the preferred origin certificate because Cloudflare Strict can validate them without a custom origin trust setup.
101 101
102-#### Sub-task 2: Verification102+### Sub-task 2: Verification
103 103
104 - [ ] Run `rtk gofmt -w` on changed Go files.104 - [ ] Run `rtk gofmt -w` on changed Go files.
105 - [ ] Run targeted app tests.105 - [ ] Run targeted app tests.
M internal/app/kernel.go +3 -3
@@ -61,9 +61,9 @@ func newKernel(configPath string, initLog kernelLoggerInit) (*Kernel, error) {
6161
6262 // Prefer full service wiring so local CLI can execute the same operations
6363 // as remote mode without going through HTTP admin endpoints.
64- // Use StartPublicTLS: false to prevent ACME Reconcile and renewal loop
65- // side effects during read-only CLI commands (e.g. gordon tls status).
66- if svc, fullErr := createServicesWithOptions(ctx, v, cfg, log, serviceOptions{StartPublicTLS: false}); fullErr == nil {
64+ // ACME Reconcile and renewal loop are only started from runServers,
65+ // so there are no side effects for read-only CLI commands.
66+ if svc, fullErr := createServicesWithOptions(ctx, v, cfg, log); fullErr == nil {
6767 // Wrap cleanup to stop public TLS service (with its renewal loop)
6868 // before the logger is cleaned up.
6969 wrappedCleanup := func() {
@@ -61,9 +61,9 @@ func newKernel(configPath string, initLog kernelLoggerInit) (*Kernel, error) {
61 61
62 // Prefer full service wiring so local CLI can execute the same operations62 // Prefer full service wiring so local CLI can execute the same operations
63 // as remote mode without going through HTTP admin endpoints.63 // as remote mode without going through HTTP admin endpoints.
64- // Use StartPublicTLS: false to prevent ACME Reconcile and renewal loop64+ // ACME Reconcile and renewal loop are only started from runServers,
65- // side effects during read-only CLI commands (e.g. gordon tls status).65+ // so there are no side effects for read-only CLI commands.
66- if svc, fullErr := createServicesWithOptions(ctx, v, cfg, log, serviceOptions{StartPublicTLS: false}); fullErr == nil {66+ if svc, fullErr := createServicesWithOptions(ctx, v, cfg, log); fullErr == nil {
67 // Wrap cleanup to stop public TLS service (with its renewal loop)67 // Wrap cleanup to stop public TLS service (with its renewal loop)
68 // before the logger is cleaned up.68 // before the logger is cleaned up.
69 wrappedCleanup := func() {69 wrappedCleanup := func() {
M internal/app/run.go +19 -39
@@ -418,40 +418,31 @@ func initAccessLog(cfg Config, log zerowrap.Logger) (*accesslog.Writer, error) {
418418 return writer, nil
419419 }
420420
421-// serviceOptions controls optional behavior for createServicesWithOptions.
422-type serviceOptions struct {
423- // StartPublicTLS controls whether ACME Reconcile and renewal loop are started.
424- // Runtime starts public TLS after listeners bind; local CLI uses false for read-only operations.
425- StartPublicTLS bool
426-}
427-
428421 // serviceInit holds the shared context for service initialization helpers.
429422 type serviceInit struct {
430- ctx context.Context
431- v *viper.Viper
432- cfg Config
433- log zerowrap.Logger
434- svc *services
435- startPublicTLS bool
423+ ctx context.Context
424+ v *viper.Viper
425+ cfg Config
426+ log zerowrap.Logger
427+ svc *services
436428 }
437429
438430 // createServices creates all the application services for server runtime.
439431 // Public ACME reconciliation is started later, after the HTTP listener is bound.
440432 func createServices(ctx context.Context, v *viper.Viper, cfg Config, log zerowrap.Logger) (_ *services, retErr error) {
441- return createServicesWithOptions(ctx, v, cfg, log, serviceOptions{StartPublicTLS: false})
433+ return createServicesWithOptions(ctx, v, cfg, log)
442434 }
443435
444-// createServicesWithOptions creates all the application services with the given options.
445-// When StartPublicTLS is false, ACME Reconcile and renewal loop are skipped so that
446-// read-only CLI commands (e.g. gordon tls status) do not perform ACME side effects.
447-func createServicesWithOptions(ctx context.Context, v *viper.Viper, cfg Config, log zerowrap.Logger, opts serviceOptions) (_ *services, retErr error) {
436+// createServicesWithOptions creates all the application services.
437+// ACME Reconcile and renewal loop are started later from runServers,
438+// after HTTP listeners are bound.
439+func createServicesWithOptions(ctx context.Context, v *viper.Viper, cfg Config, log zerowrap.Logger) (_ *services, retErr error) {
448440 si := &serviceInit{
449- ctx: ctx,
450- v: v,
451- cfg: cfg,
452- log: log,
453- svc: &services{},
454- startPublicTLS: opts.StartPublicTLS,
441+ ctx: ctx,
442+ v: v,
443+ cfg: cfg,
444+ log: log,
445+ svc: &services{},
455446 }
456447 defer func() {
457448 if retErr != nil {
@@ -609,21 +600,10 @@ func (si *serviceInit) initPublicTLS() error {
609600 log.Warn().Err(err).Msg("failed to load ACME certificates, continuing")
610601 }
611602
612- if si.startPublicTLS {
613- if err := startPublicTLSRuntime(ctx, svc, log); err != nil {
614- log.Warn().Err(err).Msg("failed to start public ACME TLS runtime, continuing")
615- }
616- log.Info().
617- Str("email", si.cfg.TLS.ACME.Email).
618- Str("challenge", string(effective.Mode)).
619- Str("reason", effective.Reason).
620- Msg("public ACME TLS initialized")
621- } else {
622- log.Info().
623- Str("email", si.cfg.TLS.ACME.Email).
624- Str("challenge", string(effective.Mode)).
625- Msg("public ACME TLS initialized (runtime start deferred)")
626- }
603+ log.Info().
604+ Str("email", si.cfg.TLS.ACME.Email).
605+ Str("challenge", string(effective.Mode)).
606+ Msg("public ACME TLS initialized (runtime start deferred)")
627607
628608 si.svc.publicTLSSvc = svc
629609 si.svc.publicTLSRuntime = svc
@@ -418,40 +418,31 @@ func initAccessLog(cfg Config, log zerowrap.Logger) (*accesslog.Writer, error) {
418 return writer, nil418 return writer, nil
419 }419 }
420 420
421-// serviceOptions controls optional behavior for createServicesWithOptions.
422-type serviceOptions struct {
423- // StartPublicTLS controls whether ACME Reconcile and renewal loop are started.
424- // Runtime starts public TLS after listeners bind; local CLI uses false for read-only operations.
425- StartPublicTLS bool
426-}
427-
428 // serviceInit holds the shared context for service initialization helpers.421 // serviceInit holds the shared context for service initialization helpers.
429 type serviceInit struct {422 type serviceInit struct {
430- ctx context.Context423+ ctx context.Context
431- v *viper.Viper424+ v *viper.Viper
432- cfg Config425+ cfg Config
433- log zerowrap.Logger426+ log zerowrap.Logger
434- svc *services427+ svc *services
435- startPublicTLS bool
436 }428 }
437 429
438 // createServices creates all the application services for server runtime.430 // createServices creates all the application services for server runtime.
439 // Public ACME reconciliation is started later, after the HTTP listener is bound.431 // Public ACME reconciliation is started later, after the HTTP listener is bound.
440 func createServices(ctx context.Context, v *viper.Viper, cfg Config, log zerowrap.Logger) (_ *services, retErr error) {432 func createServices(ctx context.Context, v *viper.Viper, cfg Config, log zerowrap.Logger) (_ *services, retErr error) {
441- return createServicesWithOptions(ctx, v, cfg, log, serviceOptions{StartPublicTLS: false})433+ return createServicesWithOptions(ctx, v, cfg, log)
442 }434 }
443 435
444-// createServicesWithOptions creates all the application services with the given options.436+// createServicesWithOptions creates all the application services.
445-// When StartPublicTLS is false, ACME Reconcile and renewal loop are skipped so that437+// ACME Reconcile and renewal loop are started later from runServers,
446-// read-only CLI commands (e.g. gordon tls status) do not perform ACME side effects.438+// after HTTP listeners are bound.
447-func createServicesWithOptions(ctx context.Context, v *viper.Viper, cfg Config, log zerowrap.Logger, opts serviceOptions) (_ *services, retErr error) {439+func createServicesWithOptions(ctx context.Context, v *viper.Viper, cfg Config, log zerowrap.Logger) (_ *services, retErr error) {
448 si := &serviceInit{440 si := &serviceInit{
449- ctx: ctx,441+ ctx: ctx,
450- v: v,442+ v: v,
451- cfg: cfg,443+ cfg: cfg,
452- log: log,444+ log: log,
453- svc: &services{},445+ svc: &services{},
454- startPublicTLS: opts.StartPublicTLS,
455 }446 }
456 defer func() {447 defer func() {
457 if retErr != nil {448 if retErr != nil {
@@ -609,21 +600,10 @@ func (si *serviceInit) initPublicTLS() error {
609 log.Warn().Err(err).Msg("failed to load ACME certificates, continuing")600 log.Warn().Err(err).Msg("failed to load ACME certificates, continuing")
610 }601 }
611 602
612- if si.startPublicTLS {603+ log.Info().
613- if err := startPublicTLSRuntime(ctx, svc, log); err != nil {604+ Str("email", si.cfg.TLS.ACME.Email).
614- log.Warn().Err(err).Msg("failed to start public ACME TLS runtime, continuing")605+ Str("challenge", string(effective.Mode)).
615- }606+ Msg("public ACME TLS initialized (runtime start deferred)")
616- log.Info().
617- Str("email", si.cfg.TLS.ACME.Email).
618- Str("challenge", string(effective.Mode)).
619- Str("reason", effective.Reason).
620- Msg("public ACME TLS initialized")
621- } else {
622- log.Info().
623- Str("email", si.cfg.TLS.ACME.Email).
624- Str("challenge", string(effective.Mode)).
625- Msg("public ACME TLS initialized (runtime start deferred)")
626- }
627 607
628 si.svc.publicTLSSvc = svc608 si.svc.publicTLSSvc = svc
629 si.svc.publicTLSRuntime = svc609 si.svc.publicTLSRuntime = svc

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