Browse the docs
Self-hosting
TLS and the root certificate
How an on-premises Crunu server gets a TLS certificate without the internet, how to hand out the root certificate to browsers and agents, and what to protect.
Last updated
On this page
An on-premises server needs a TLS certificate that the computers of your network trust, without asking a public certificate authority on the internet. The installer gives you two ways: the internal certificate authority of Caddy, which is the default, or a certificate your company issues.
Available from Crunu 6.3.0. Read Install Crunu in your own network first.
The internal certificate authority
With --tls internal, Caddy, the proxy in front of the server, runs its own certificate authority. It issues the certificate for the server's name, or for its IP address, and renews it by itself. Nothing goes to the internet.
Browsers and agents trust the server only once they trust the root certificate of that authority. The installer exports it to deploy/crunu-root.crt. To export it again, or into another file:
sh deploy/export-ca.sh --env-file deploy/.env --out crunu-root.crtIt prints the SHA-256 fingerprint of the root and the commands that install it on each system:
Root certificate: crunu-root.crt
SHA-256 fingerprint: 3F:A9:52:0B:…:6E:C2:71 — compare it on every machine before you install the certificate there.Install the root on each system
Compare the fingerprint on every machine before you install the file there:
openssl x509 -in crunu-root.crt -noout -fingerprint -sha256| System | Command |
|---|---|
| Debian, Ubuntu | sudo cp crunu-root.crt /usr/local/share/ca-certificates/crunu-root.crt && sudo update-ca-certificates |
| RHEL, Fedora | sudo cp crunu-root.crt /etc/pki/ca-trust/source/anchors/ && sudo update-ca-trust |
| macOS | sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain crunu-root.crt |
| Windows, as administrator | certutil -addstore -f Root crunu-root.crt |
| Chrome on Linux | certutil -d sql:$HOME/.pki/nssdb -A -t C,, -n crunu-root -i crunu-root.crt |
| Firefox | import it in the certificate settings, or set security.enterprise_roots.enabled to use the system roots |
| crunu agent | install.sh … --ca-file crunu-root.crt, or CRUNU_CA_FILE |
| The server itself | CA_FILE in deploy/.env |
Check from any computer of the network:
curl --cacert crunu-root.crt https://vault.kiteline.example/releases/latestIt prints the version. Without --cacert, before the root is installed, curl stops with error 60: the certificate can't be verified.
Clocks matter
The internal certificate authority issues certificates for 12 hours and renews them well before they expire. A machine whose clock is off by hours rejects a valid certificate. Keep the server and every machine synchronized with the time server of your network.
doctor judges the certificate by its lifetime: for a 12-hour certificate it warns under about three hours left and fails under about an hour, and shows the time left to the minute. Sign-in also fails when the server clock is more than 2 minutes off; the Server clock line of doctor reports it. See Monitoring and doctor.
A certificate of your company
When your company issues server certificates, give the installer the certificate and its key:
sh install.sh --origin https://vault.kiteline.example \
--tls file --tls-cert /etc/crunu/tls/cert.pem --tls-key /etc/crunu/tls/key.pem \
--ca-file /etc/crunu/tls/company-ca.pem- The certificate is a PEM file with its chain, and it must cover the name of
--origin. The settings check reports it otherwise. - The key is a PEM file without a passphrase. It must belong to root and have mode 600.
- Caddy doesn't renew a certificate you supply. Replace the files before it expires and restart Caddy with
docker compose restart caddy. --ca-filenames the root of your company's certificate authority. The server trusts it for its own HTTPS requests, such as webhooks and the keys of a CI system in your network, and the agent command the installer prints carries it.
Hand out the root of your company only if the machines don't trust it yet.
Passkeys need a name
If --origin uses an IP address, everything works except passkeys: browsers allow them only for a DNS name. People sign in with their password and an authenticator app instead. Give the server a name in your network's DNS to use passkeys.