How-to guide · Trust a CA

How to trust a private root CA on Linux

In one sentence

Your Linux server, desktop or container image trusts your organisation's private root CA system-wide, and you know which programs keep their own list.

Task
Trust a CA
Platforms
  • Linux
  • Docker
Who it is for
Level
Practitioner · 6 steps
On this page
  1. Before you start
  2. Steps
  3. 1. Check the file
  4. 2. Debian, Ubuntu and Alpine
  5. 3. RHEL, Fedora, CentOS Stream, Rocky, AlmaLinux and Amazon Linux
  6. 4. SUSE and openSUSE
  7. 5. Container images
  8. 6. Point the programs that keep their own list
  9. Check it worked
  10. If something goes wrong
  11. Why this works
  12. Go deeper

Before you start

  • The root certificate of your private CA in PEM format (text starting with -----BEGIN CERTIFICATE-----). Never a private key.
  • Its SHA-256 fingerprint, from a channel you trust.
  • Root access (sudo).
  • Your distribution family: cat /etc/os-release tells you.

Why it matters: every root in the system trust store can vouch for any name, for curl, git, Go programs, package managers and most other tools on the machine. Add only roots your organisation controls.

Steps

1. Check the file

openssl x509 -in corp-root.crt -noout -subject -issuer -enddate -fingerprint -sha256 -ext basicConstraints
subject=C=BE, O=Example Corp, CN=Example Corp Root CA
issuer=C=BE, O=Example Corp, CN=Example Corp Root CA
notAfter=Oct 17 01:43:50 2036 GMT
sha256 Fingerprint=BC:02:A4:F5:DC:68:B3:FD:CD:C4:78:03:0E:30:8C:B5:9D:6D:A0:84:5A:A0:B4:33:36:60:78:8E:96:C2:2E:01
X509v3 Basic Constraints: critical
    CA:TRUE

The fingerprint must match the published value, subject and issuer must be equal, and CA:TRUE must be set. If the file is DER (binary), convert it first: openssl x509 -inform DER -in corp-root.cer -out corp-root.crt (DER and PEM).

2. Debian, Ubuntu and Alpine

The file must be PEM, hold one certificate, and end in .crt, or update-ca-certificates ignores it:

sudo cp corp-root.crt /usr/local/share/ca-certificates/example-corp-root.crt
sudo update-ca-certificates

On Ubuntu 24.04 with Java installed, the output was:

Updating certificates in /etc/ssl/certs...
rehash: warning: skipping ca-certificates.crt,it does not contain exactly one certificate or CRL
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
Processing triggers for ca-certificates-java (20240118) ...
Adding debian:example-corp-root.pem
done.
done.

1 added is what you want. The rehash warning is harmless. The Adding debian:… line is a hook that also put the root into the distribution’s Java cacerts. The result is the bundle /etc/ssl/certs/ca-certificates.crt plus a hashed directory, /etc/ssl/certs. Alpine uses the same folder and command once the ca-certificates package is installed.

3. RHEL, Fedora, CentOS Stream, Rocky, AlmaLinux and Amazon Linux

Here the folder accepts PEM or DER, and the anchors are trusted “for all purposes” (update-ca-trust):

sudo cp corp-root.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust

This rebuilds /etc/pki/ca-trust/extracted/, including the PEM bundle behind /etc/pki/tls/certs/ca-bundle.crt and the Java keystore behind /etc/pki/java/cacerts. The p11-kit tool does the same in one step: sudo trust anchor corp-root.crt (Red Hat).

4. SUSE and openSUSE

Local roots go in /etc/pki/trust/anchors/ (openSUSE ca-certificates):

sudo cp corp-root.crt /etc/pki/trust/anchors/
sudo update-ca-certificates

5. Container images

A container does not inherit the host’s trust store. Bake the root into the image at build time:

FROM debian:trixie-slim
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
    && rm -rf /var/lib/apt/lists/*
COPY corp-root.crt /usr/local/share/ca-certificates/example-corp-root.crt
RUN update-ca-certificates

For Alpine, install ca-certificates with apk add --no-cache ca-certificates and use the same two lines. For Red Hat UBI images, copy to /etc/pki/ca-trust/source/anchors/ and run update-ca-trust. In Kubernetes you can also mount the bundle from a ConfigMap, but the image approach works everywhere.

6. Point the programs that keep their own list

Program Uses the system store? What to do
curl, wget, git, OpenSSL, Go, the distribution’s Python and requests Yes Nothing
Distribution OpenJDK Yes, through the hook (Debian, Ubuntu) or extracted/java (RHEL family) Nothing; other JDKs: see Java trust stores
Node.js, npm No node --use-system-ca, or NODE_EXTRA_CA_CERTS: see Python, Node.js and npm
Python packages from pip (requests with certifi) No See Python, Node.js and npm
Chrome, Chromium No, a per-user NSS database Use NSS certutil (below)
Firefox Its own store, except where the distribution links it to the system store (the Firefox packages of Fedora and RHEL do) Import it under Settings > Privacy & Security > View Certificates > Authorities, or with an enterprise policy

For Chrome on Linux, the Chromium documentation uses NSS certutil (package libnss3-tools on Debian and Ubuntu, nss-tools on Fedora). Recent versions use ~/.local/share/pki/nssdb, or ~/.pki/nssdb if that already exists:

DB="$HOME/.pki/nssdb"; [ -d "$DB" ] || DB="$HOME/.local/share/pki/nssdb"
certutil -d "sql:$DB" -A -t "C,," -n "Example Corp Root CA" -i corp-root.crt
certutil -d "sql:$DB" -L

-t "C,," trusts the CA for TLS server certificates only. On a test database the listing showed Example Corp Root CA C,,.

Check it worked

Before the change, curl on our Ubuntu test machine refused a server whose chain ends at the private root:

curl -sS https://intranet.example.com:18443/ -o /dev/null
curl: (60) SSL certificate problem: unable to get local issuer certificate

After update-ca-certificates, the same command printed the status code 200, and OpenSSL verified the whole chain:

openssl s_client -connect intranet.example.com:18443 -servername intranet.example.com -verify_hostname intranet.example.com </dev/null
depth=2 C = BE, O = Example Corp, CN = Example Corp Root CA
verify return:1
depth=1 C = BE, O = Example Corp, CN = Example Corp Issuing CA 1
verify return:1
depth=0 CN = intranet.example.com
verify return:1
...
Verification: OK
Verify return code: 0 (ok)

On the RHEL family, trust list --filter=ca-anchors | grep -A3 "Example Corp" shows the new anchor.

If something goes wrong

Try it: Chain checkerOpen the full tool

Runs in your browser. Nothing you paste is sent anywhere.

Every certificate the server sends, leaf first. The output of openssl s_client -showcerts -connect example.com:443 works as it is: we ignore the text around the certificates.

Why this works

OpenSSL-based programs check a server’s certificate chain against a CA bundle file or a hashed directory, and accept it only if it ends at a certificate in there. update-ca-certificates and update-ca-trust rebuild those files from the distribution’s roots plus your local ones, so every program that reads them trusts the new root at the next connection. A root is trusted because it is in the store, not because it is self-signed (Who do we trust?). The checks themselves are in Certificate chains and path validation, and designing a private CA that is safe to trust is in A CA of your own.

Go deeper

Early edition. Checked against the standards; an independent expert review is in progress. Found a mistake? Tell us.

Last reviewed Edition 0.9