A freshly-installed krill server only ever had the self-signed TLS certificate
that postinst generates. On a host with a real, public domain name there was
no supported path to a browser-trusted certificate — operators either lived
with the browser warning / manual /trust cert install, or hand-rolled their
own certbot wiring and re-discovered every time where the server actually reads
its cert from.
The certificate story was intentionally minimal (self-signed, generated once at
install), and the paths the server reads (/etc/krill/certs/krill.crt,
krill.key, .pfx_password) were only produced by postinst. There was no
first-class, documented way to swap in a real cert without knowing the
implementation detail that the running server builds its keystore from the
PEM files, not from krill.pfx (see KtorConfig.kt::envConfig /
loadKeyStoreFromPem — krill.pfx is written by postinst but never read at
runtime; only .pfx_password is).
Added server/package/usr/local/bin/krill-enable-letsencrypt: a POSIX-sh
helper an operator runs post-install (sudo krill-enable-letsencrypt <domain>
--email <addr>). It installs certbot if missing, obtains a cert via the ACME
HTTP-01 standalone challenge (port 80 is free — krill only uses 8442), copies
fullchain.pem → krill.crt and privkey.pem → krill.key with
krill:krill ownership (key 600, cert 644), refreshes krill.pfx
best-effort for coherence, and restarts krill.service. It also installs a
certbot deploy hook scoped to a stored .letsencrypt_domain, so the
90-day auto-renewals re-apply the cert and restart krill with no manual step.
postinst gained a defensive chmod +x for the new script (matching the
camera/kiosk pattern) and a banner hint pointing operators at it.
krill.pfx: postinst maintains it, but the server ignores
it and loads the PEM pair — writing only the pfx would have looked correct and
silently kept the old cert live. Grep the server for the consuming code
(CERT_DIR, loadKeyStoreFromPem) before assuming a file is load-bearing.envConfig), so any cert swap must
restart krill.service to take effect — both on first install and on every
renewal. The renewal path is easy to forget; wiring the deploy hook at
enable-time is what makes it durable.:server:jvmTest. Guarded instead by
sh -n + shellcheck on the script and by keeping the cert paths in one
place shared with postinst.