Merge branch 'totp'

Both branches added database migration 3: DNSSEC (merged first) keeps 3
and TOTP becomes migration 4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
abrown38andClaude Opus 5.5 committed 2026-10-09 15:17:12 +00:00
commit a95dffb471
35 files changed
+1963 -43

No files matched your search

+41
View File
@@ -30,6 +30,7 @@ type Config struct {
StagingDir string `yaml:"staging_dir"`
Log Log `yaml:"log"`
Session Session `yaml:"session"`
TOTP TOTP `yaml:"totp"`
BIND BIND `yaml:"bind"`
Audit Audit `yaml:"audit"`
Backup Backup `yaml:"backup"`
@@ -150,6 +151,28 @@ type Session struct {
}
// BIND says where BIND's files and tools are.
// TOTP configures two-factor login to the web UI (docs/permissions.md).
type TOTP struct {
// Require is who must use it: none (each user decides), admins or
// all (every user who logs in with a password).
Require string `yaml:"require"`
// Issuer is the name authenticator apps show for the account ("" is
// "Jarl <hostname>").
Issuer string `yaml:"issuer"`
}
// IssuerName is Issuer, or "Jarl <short hostname>".
func (t TOTP) IssuerName() string {
if t.Issuer != "" {
return t.Issuer
}
h, _ := os.Hostname()
if h, _, _ = strings.Cut(h, "."); h == "" {
return "Jarl"
}
return "Jarl " + h
}
type BIND struct {
MainConfig string `yaml:"main_config"`
ConfigDir string `yaml:"config_dir"`
@@ -254,6 +277,7 @@ func Default() *Config {
StagingDir: "/var/lib/jarl/staging",
Log: Log{Level: "info", Format: "text"},
Session: Session{Lifetime: Duration(12 * time.Hour), IdleTimeout: Duration(2 * time.Hour), CookieSecure: "auto"},
TOTP: TOTP{Require: "none"},
BIND: BIND{
MainConfig: "/etc/bind/named.conf",
ConfigDir: "/etc/bind/jarl",
@@ -328,6 +352,14 @@ func (c *Config) check() error {
default:
return fmt.Errorf("session.cookie_secure must be auto, always or never")
}
switch c.TOTP.Require {
case "none", "admins", "all":
default:
return fmt.Errorf("totp.require must be none, admins or all")
}
if len(c.TOTP.Issuer) > 64 || strings.ContainsAny(c.TOTP.Issuer, ":") {
return fmt.Errorf("totp.issuer must be at most 64 characters, without ':'")
}
if err := c.Stats.check(); err != nil {
return fmt.Errorf("stats: %w", err)
}
@@ -468,6 +500,15 @@ session:
idle_timeout: 2h # ...or after this long without activity
cookie_secure: auto # auto, always or never
# Two-factor login to the web UI with an authenticator app (TOTP). Each
# user can turn it on under Account. require makes it compulsory: none,
# admins, or all (every user with a password). A user who must use it and
# hasn't set it up can only set it up after logging in. API tokens and
# the local socket are not affected. See docs/permissions.md.
totp:
require: none
issuer: "" # the name in the app; "" is "Jarl <hostname>"
bind:
# The file named loads. 'jarld adopt' makes it include Jarl's config.
main_config: /etc/bind/named.conf