Certificates for other hosts: jarl cert get
jarld obtains and renews a certificate for another host; the host asks
for it with one command, waits, downloads it when it is newer than its
copy and runs a hook with the paths in JARL_CERT_* variables.
- Certificates have an owner user; another user can't take the name.
An existing one is obtained again only when a name is added or
removed. A new certificates grant (names=) says which names a user
may have; it is never part of "*". Users may own
certificates.max_per_user certificates (10) or their own cert_limit.
- POST /certificates/{name}/request; owners may list, show, export and
delete theirs; keys only over HTTPS, the socket or loopback; every
download is recorded and shown as "Pulled by".
- CLI: jarl cert get (saved settings, all saved when no name), cert
saved, cert forget, cert schedule (daily systemd timer, Linux, root).
- Migration 14. Tests: server, auth, and e2e against Pebble.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b4bb99b482
commit
9db76a113c
29 files changed
+1568
-40
No files matched your search
@@ -896,6 +896,58 @@ if [ -n "$PEBBLE" ]; then
|
||||
run "a second certificate for the web UI is refused (exit 6)" 6 $J cert create other --domain nas.adbraun.test --use web
|
||||
run "certificate metrics" 0 curl -sf http://127.0.0.1:$HTTP/metrics
|
||||
contains '^jarl_certificate_failing{certificate="ns1",source="acme"} 0$'
|
||||
# Certificates for other hosts: jarl cert get (docs/certificates.md#certificates-for-other-hosts).
|
||||
run "user for other hosts' certificates" 0 $J user create hosts --no-password --grant "certificates:write names=*.adbraun.test"
|
||||
run "its token" 0 sh -c "$J token create mx --user hosts -q > $E/hosts.token"
|
||||
run "a second user with the same grant" 0 $J user create hosts2 --no-password --grant "certificates:write names=*.adbraun.test"
|
||||
run "and its token" 0 sh -c "$J token create web --user hosts2 -q > $E/hosts2.token"
|
||||
run "a user without a certificates grant" 0 $J user create nocert --no-password --role acme
|
||||
run "and its token too" 0 sh -c "$J token create nocert --user nocert -q > $E/nocert.token"
|
||||
CG="--token @$E/hosts.token"; CGD=$E/certget
|
||||
mkdir -p $CGD
|
||||
HOOK='echo "$JARL_CERT_NAME $JARL_CERT_RENEWED $(basename $JARL_CERT_KEY)" >> '$CGD/hook.log
|
||||
run "cert get: obtained by jarld, downloaded here" 0 $J $CG cert get mail -d mail.adbraun.test --dir $CGD/mail --hook "$HOOK" --config-dir $CGD/conf
|
||||
contains "mail .*new files"
|
||||
run "the files" 0 ls $CGD/mail
|
||||
contains "^chain.pem$"
|
||||
contains "^fullchain.pem$"
|
||||
contains "^jarl-cert.json$"
|
||||
run "the key is mode 600" 0 stat -c %a $CGD/mail/privkey.pem
|
||||
contains "^600$"
|
||||
run "the key matches" 0 sh -c "openssl x509 -in $CGD/mail/cert.pem -noout -pubkey | cmp - \$(openssl pkey -in $CGD/mail/privkey.pem -pubout > $CGD/pub.pem; echo $CGD/pub.pem)"
|
||||
run "the hook ran" 0 cat $CGD/hook.log
|
||||
contains "^mail 0 privkey.pem$"
|
||||
run "the settings are saved, readable only by their owner" 0 stat -c %a $CGD/conf/mail.json
|
||||
contains "^600$"
|
||||
run "cert get again: up to date, no hook" 0 $J cert get --config-dir $CGD/conf
|
||||
contains "mail .*up to date"
|
||||
if [ "$(wc -l < $CGD/hook.log)" = 1 ]; then ok "the hook didn't run again"; else bad "hook ran again: $(cat $CGD/hook.log)"; fi
|
||||
run "cert saved" 0 $J cert saved --config-dir $CGD/conf
|
||||
contains "mail *mail.adbraun.test .*$CGD/mail"
|
||||
run "another user can't take the name (exit 5)" 5 $J --token @$E/hosts2.token cert get mail -d mail.adbraun.test --dir $CGD/x --no-save --config-dir $CGD/conf2
|
||||
contains "taken"
|
||||
run "an ACME token can't get certificates (exit 3)" 3 $J --token @$E/nocert.token cert get x -d x.adbraun.test --dir $CGD/x --no-save --config-dir $CGD/conf2
|
||||
run "a name outside the grant (exit 3)" 3 $J $CG cert get other -d mail.example.org --dir $CGD/other --no-save --config-dir $CGD/conf
|
||||
run "the admin sees the owner and who downloaded it" 0 $J cert show mail
|
||||
contains "Owner: *hosts"
|
||||
contains "Pulled by: *token mx (hosts)"
|
||||
run "jarld renews it" 0 $J cert issue mail
|
||||
run "cert get: the renewed one, hook told" 0 $J cert get --config-dir $CGD/conf
|
||||
contains "mail .*new files"
|
||||
run "the hook ran for the renewal" 0 tail -1 $CGD/hook.log
|
||||
contains "^mail 1 privkey.pem$"
|
||||
run "a name added: obtained again" 0 $J cert get mail -d mail.adbraun.test -d smtp.adbraun.test --config-dir $CGD/conf
|
||||
run "the new certificate has both names" 0 openssl x509 -in $CGD/mail/cert.pem -noout -ext subjectAltName
|
||||
contains "smtp.adbraun.test"
|
||||
run "cert schedule writes the units" 0 $J cert schedule --unit-dir $CGD --no-systemctl
|
||||
run "the service runs cert get" 0 cat $CGD/jarl-cert-get.service
|
||||
contains "^ExecStart=.* cert get --config-dir"
|
||||
run "the timer runs daily" 0 cat $CGD/jarl-cert-get.timer
|
||||
contains "^OnCalendar=daily$"
|
||||
run "cert forget" 0 $J cert forget mail --config-dir $CGD/conf
|
||||
run "nothing saved now" 0 $J cert saved --config-dir $CGD/conf
|
||||
contains "No saved certificates"
|
||||
run "the owner may delete it" 0 $J $CG cert delete mail --yes
|
||||
# Encrypted DNS set up by Jarl (docs/encrypted-dns.md), next to the
|
||||
# hand-written listener above.
|
||||
EDOT=${EDOT_PORT:-25855}; EDOH=${EDOH_PORT:-25856}
|
||||
|
||||
Reference in new issue
Block a user