Add an httpreq endpoint for lego and Traefik
jarld can now answer lego's httpreq DNS provider (POST /api/v1/acme/httpreq/present and /cleanup), so lego and Traefik publish DNS-01 challenges with only environment variables, without the jarl CLI. lego's username and API token arrive as HTTP Basic auth, accepted on these routes only. The endpoint is off by default (acme.httpreq.enabled) and HTTPS-only by default: jarld's own TLS, or a trusted proxy that says so with X-Forwarded-Proto or Forwarded (the last value counts). Plain HTTP can be allowed from loopback only or everywhere. GET /api/v1/acme/httpreq shows how jarld sees a request, to check a proxy setup. jarl acme and the endpoint share their code in internal/acme. Documented in docs/acme.md, configuration.md, api.md and permissions.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0221c78c8a
commit
6b44b92975
18 files changed
+1106
-73
No files matched your search
@@ -29,6 +29,27 @@ type Config struct {
|
||||
BIND BIND `yaml:"bind"`
|
||||
Audit Audit `yaml:"audit"`
|
||||
Stats Stats `yaml:"stats"`
|
||||
ACME ACME `yaml:"acme"`
|
||||
}
|
||||
|
||||
// ACME configures the ACME helpers.
|
||||
type ACME struct {
|
||||
HTTPReq HTTPReq `yaml:"httpreq"`
|
||||
}
|
||||
|
||||
// HTTPReq configures the endpoint for lego's httpreq DNS provider
|
||||
// (docs/acme.md). lego sends an API token as an HTTP Basic password, so
|
||||
// by default the endpoint only answers requests that came over HTTPS.
|
||||
type HTTPReq struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
// RequireHTTPS refuses requests that didn't arrive over HTTPS: jarld's
|
||||
// own TLS, or a trusted proxy that says so in X-Forwarded-Proto or
|
||||
// Forwarded.
|
||||
RequireHTTPS bool `yaml:"require_https"`
|
||||
// AllowLoopbackHTTP also accepts plain HTTP from 127.0.0.1 and ::1
|
||||
// (lego on the DNS server itself), unless that address is a trusted
|
||||
// proxy.
|
||||
AllowLoopbackHTTP bool `yaml:"allow_loopback_http"`
|
||||
}
|
||||
|
||||
// TLS configures HTTPS.
|
||||
@@ -172,6 +193,7 @@ func Default() *Config {
|
||||
Retention: StatsRetention{Minute: Duration(7 * 24 * time.Hour), Hour: Duration(90 * 24 * time.Hour), Day: Duration(730 * 24 * time.Hour)},
|
||||
TopN: 100,
|
||||
},
|
||||
ACME: ACME{HTTPReq: HTTPReq{RequireHTTPS: true}},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,4 +396,19 @@ stats:
|
||||
top_n: 100 # names and clients kept per hour and per day
|
||||
anonymize_clients: false # store 10.10.0.0/24 instead of 10.10.0.37
|
||||
exclude_clients: [] # e.g. [127.0.0.1] to ignore local monitoring
|
||||
|
||||
# An endpoint for lego's "httpreq" DNS provider (docs/acme.md), so lego and
|
||||
# Traefik can publish DNS-01 challenges without the jarl CLI. Off by
|
||||
# default. lego sends an API token as an HTTP Basic password, so by default
|
||||
# requests are only accepted over HTTPS: jarld's own tls (above), or a
|
||||
# reverse proxy in trusted_proxies that sends "X-Forwarded-Proto: https"
|
||||
# (or "Forwarded: proto=https"). Nothing else in Jarl depends on this.
|
||||
acme:
|
||||
httpreq:
|
||||
enabled: false
|
||||
require_https: true
|
||||
# Also accept plain HTTP from 127.0.0.1 and ::1, for lego running on
|
||||
# this server. Leave false if a reverse proxy runs on this server but
|
||||
# isn't in trusted_proxies: its requests would look local.
|
||||
allow_loopback_http: false
|
||||
`
|
||||
@@ -18,6 +18,22 @@ func TestExampleParses(t *testing.T) {
|
||||
if c.BIND.ZoneDir != d.BIND.ZoneDir || time.Duration(c.Session.IdleTimeout) != 2*time.Hour || c.Listen[0] != "127.0.0.1:8053" {
|
||||
t.Errorf("example differs from defaults: %+v", c)
|
||||
}
|
||||
if c.ACME != d.ACME || c.ACME.HTTPReq.Enabled || !c.ACME.HTTPReq.RequireHTTPS {
|
||||
t.Errorf("acme = %+v, want the defaults %+v", c.ACME, d.ACME)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPReqDefaults(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "jarl.yaml")
|
||||
// Enabling the endpoint keeps require_https on unless it is turned off.
|
||||
os.WriteFile(path, []byte("acme: {httpreq: {enabled: true}}\n"), 0o644)
|
||||
c, err := Load(path, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if h := c.ACME.HTTPReq; !h.Enabled || !h.RequireHTTPS || h.AllowLoopbackHTTP {
|
||||
t.Errorf("httpreq = %+v", h)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
|
||||
Reference in new issue
Block a user