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>
90 lines
2.9 KiB
Go
90 lines
2.9 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestExampleParses(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "jarl.yaml")
|
|
os.WriteFile(path, []byte(Example), 0o644)
|
|
c, err := Load(path, false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
d := Default()
|
|
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) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "jarl.yaml")
|
|
os.WriteFile(path, []byte("listen: [\"0.0.0.0:8443\"]\ntls: {cert_file: /a, key_file: /b}\ntrusted_proxies: [10.0.0.1, 192.168.0.0/16]\nsession: {lifetime: 1d}\n"), 0o644)
|
|
c, err := Load(path, false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if time.Duration(c.Session.Lifetime) != 24*time.Hour || len(c.Proxies()) != 2 || c.BIND.FileGroup != "bind" {
|
|
t.Errorf("config = %+v", c)
|
|
}
|
|
os.WriteFile(path, []byte("lisen: []\n"), 0o644)
|
|
if _, err := Load(path, false); err == nil {
|
|
t.Error("unknown field accepted")
|
|
}
|
|
os.WriteFile(path, []byte("tls: {cert_file: /a}\n"), 0o644)
|
|
if _, err := Load(path, false); err == nil {
|
|
t.Error("half TLS config accepted")
|
|
}
|
|
if _, err := Load(filepath.Join(dir, "missing.yaml"), true); err != nil {
|
|
t.Errorf("missing file with allowMissing: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestStatsConfig(t *testing.T) {
|
|
d := Default()
|
|
if d.Stats.Enabled || !d.Stats.QueryLog || d.Stats.TopN != 100 || time.Duration(d.Stats.Retention.Hour) != 90*24*time.Hour {
|
|
t.Errorf("defaults = %+v", d.Stats)
|
|
}
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "jarl.yaml")
|
|
for _, tc := range []struct {
|
|
yaml string
|
|
ok bool
|
|
}{
|
|
{"stats: {enabled: true}", true},
|
|
{"stats: {enabled: true, query_log: false, retention: {minute: 2d}}", true},
|
|
{"stats: {enabled: true, statistics_channel: 0.0.0.0:8058}", false}, // must be loopback
|
|
{"stats: {enabled: true, statistics_channel: localhost}", false},
|
|
{"stats: {enabled: true, log_size: lots}", false},
|
|
{"stats: {enabled: true, retention: {minute: 10m}}", false},
|
|
{"stats: {enabled: true, exclude_clients: [not-an-ip]}", false},
|
|
{"stats: {enabled: false, statistics_channel: nonsense}", true}, // only checked when enabled
|
|
} {
|
|
os.WriteFile(path, []byte(tc.yaml+"\n"), 0o644)
|
|
_, err := Load(path, false)
|
|
if (err == nil) != tc.ok {
|
|
t.Errorf("%s: err = %v, want ok=%v", tc.yaml, err, tc.ok)
|
|
}
|
|
}
|
|
}
|