v2.31.0-dev19 35467d7

config.go RawHistoryFolder

1package publictls
2
3import (
4	"context"
5	"fmt"
6
7	"github.com/bnema/gordon/internal/boundaries/out"
8	"github.com/bnema/gordon/internal/domain"
9)
10
11// Config holds the resolved configuration for public ACME TLS.
12type Config struct {
13	Enabled         bool
14	Email           string
15	Challenge       string
16	HTTPPort        int
17	TLSPort         int
18	DataDir         string
19	ObtainBatchSize int
20	DNS             DNSConfig
21}
22
23// EffectiveChallenge represents the resolved ACME challenge configuration.
24type EffectiveChallenge struct {
25	ConfiguredMode domain.ACMEChallengeMode
26	Mode           domain.ACMEChallengeMode
27	Token          string
28	TokenSource    domain.ACMETokenSource
29	Reason         string
30}
31
32// validPort checks that port is in the valid TCP port range 1..65535.
33func validPort(p int) bool {
34	return p >= 1 && p <= 65535
35}
36
37// ResolveEffectiveChallenge resolves the effective ACME challenge configuration
38// from the given Config and optional token resolver.
39func ResolveEffectiveChallenge(ctx context.Context, cfg Config, resolver out.SecretResolver) (EffectiveChallenge, error) {
40	if !cfg.Enabled {
41		configuredMode := domain.ACMEChallengeAuto
42		if parsed, err := domain.ParseACMEChallengeMode(cfg.Challenge); err == nil {
43			configuredMode = parsed
44		}
45		return EffectiveChallenge{
46			ConfiguredMode: configuredMode,
47			Mode:           domain.ACMEChallengeAuto,
48			TokenSource:    domain.ACMETokenSourceNone,
49			Reason:         "ACME disabled",
50		}, nil
51	}
52
53	if cfg.Email == "" {
54		return EffectiveChallenge{}, domain.ErrACMEEmailRequired
55	}
56
57	if !validPort(cfg.TLSPort) {
58		return EffectiveChallenge{}, fmt.Errorf("%w: tls_port must be in range 1..65535", domain.ErrACMEChallengeInvalid)
59	}
60
61	parsedMode, err := domain.ParseACMEChallengeMode(cfg.Challenge)
62	if err != nil {
63		return EffectiveChallenge{}, err
64	}
65
66	switch parsedMode {
67	case domain.ACMEChallengeHTTP01:
68		if !validPort(cfg.HTTPPort) {
69			return EffectiveChallenge{}, fmt.Errorf("%w: http_port must be in range 1..65535 for http-01 challenge", domain.ErrACMEChallengeInvalid)
70		}
71		return EffectiveChallenge{
72			ConfiguredMode: domain.ACMEChallengeHTTP01,
73			Mode:           domain.ACMEChallengeHTTP01,
74			TokenSource:    domain.ACMETokenSourceNone,
75			Reason:         "http-01 challenge selected",
76		}, nil
77
78	case domain.ACMEChallengeCloudflareDNS01:
79		token, source, err := resolveToken(ctx, resolver, true)
80		if err != nil {
81			return EffectiveChallenge{}, fmt.Errorf("resolve Cloudflare token: %w", err)
82		}
83		if token == "" {
84			return EffectiveChallenge{}, domain.ErrCloudflareTokenMissing
85		}
86		return EffectiveChallenge{
87			ConfiguredMode: domain.ACMEChallengeCloudflareDNS01,
88			Mode:           domain.ACMEChallengeCloudflareDNS01,
89			Token:          token,
90			TokenSource:    source,
91			Reason:         "cloudflare dns-01 challenge selected",
92		}, nil
93
94	case domain.ACMEChallengeAuto:
95		token, source, _ := resolveToken(ctx, resolver, false)
96		if token != "" {
97			return EffectiveChallenge{
98				ConfiguredMode: domain.ACMEChallengeAuto,
99				Mode:           domain.ACMEChallengeCloudflareDNS01,
100				Token:          token,
101				TokenSource:    source,
102				Reason:         "auto selected cloudflare dns-01 (token available)",
103			}, nil
104		}
105		if !validPort(cfg.HTTPPort) {
106			return EffectiveChallenge{}, fmt.Errorf("%w: http_port must be in range 1..65535 for http-01 fallback in auto mode", domain.ErrACMEChallengeInvalid)
107		}
108		return EffectiveChallenge{
109			ConfiguredMode: domain.ACMEChallengeAuto,
110			Mode:           domain.ACMEChallengeHTTP01,
111			TokenSource:    domain.ACMETokenSourceNone,
112			Reason:         "auto selected http-01 (no cloudflare token)",
113		}, nil
114
115	default:
116		return EffectiveChallenge{}, fmt.Errorf("%w: %q", domain.ErrACMEChallengeInvalid, parsedMode)
117	}
118}
119
120// resolveToken resolves the Cloudflare token via the resolver. When strict is
121// false, resolver errors are ignored so auto mode can fall back to HTTP-01.
122func resolveToken(ctx context.Context, resolver out.SecretResolver, strict bool) (string, domain.ACMETokenSource, error) {
123	if resolver == nil {
124		return "", domain.ACMETokenSourceNone, nil
125	}
126	sv, err := resolver.ResolveCloudflareToken(ctx)
127	if err != nil {
128		if strict {
129			return "", domain.ACMETokenSourceNone, err
130		}
131		return "", domain.ACMETokenSourceNone, nil
132	}
133	if sv.Value == "" {
134		return "", domain.ACMETokenSourceNone, nil
135	}
136	return sv.Value, sv.Source, nil
137}

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