SSD Nodes Learn Hosting plans →
Guides Matt ConnorBy Matt Connor

Distrust a Root CA That Ubuntu Ships

Stop trusting one root that Ubuntu's ca-certificates ships. Mark it with ! in one file, prove the change with openssl verify, then find the copies it misses.

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, October 22, 2026.

How to distrust a root CA that Ubuntu ships

To distrust a root CA that Ubuntu ships, put a ! at the start of that root's line in /etc/ca-certificates.conf and run sudo update-ca-certificates --fresh. The tool then leaves the root out of /etc/ssl/certs and out of the combined bundle at /etc/ssl/certs/ca-certificates.crt. You prove the change with openssl verify, which prints OK for the root before the edit and fails after it.

That edit changes the system store only. Java, Python's certifi, Node.js, snaps and container images can each carry their own copy of the same root. The last part of this guide shows how to check each one.

Why would you distrust a root that Ubuntu ships?

A root CA (certificate authority) is a certificate your server trusts without asking anyone else. Every TLS (transport layer security) certificate that chains up to that root is accepted. If someone steals the key behind a root, they can issue a certificate for any name, and your server will accept it.

This happened recently. On 20 October 2026 Ubuntu published fixes for what it calls an eMudhra CA compromise. Ubuntu's blog post says two roots were removed from the trusted root store in the ca-certificates package. It names them emSign Root TLS CA – G3 and Comodo AAA Certificate Services. The matching security notice, USN-8944-1, names the emSign certificate differently, as emSign ECC Root CA - G3. The same blog post says the AAA Certificate Services CA had already been removed from the bundles in Ubuntu 22.04 and newer. The two pages do not agree on the name, so do not guess which roots a server still lists. Read its file.

Distrusting a root by hand is useful when the package update has not reached a server yet. It is also the safe way to test what breaks before a root goes away for real.

Is your Ubuntu server already patched?

Check the package version first. The update may already have done the job for you.

dpkg-query -W ca-certificates

Ubuntu lists these fixed versions:

  • Ubuntu 22.04: 20260816ubuntu0.22.04.1
  • Ubuntu 24.04: 20260816ubuntu0.24.04.1
  • Ubuntu 26.04: 20260816ubuntu0.26.04.1
  • Ubuntu 26.10: 20260816ubuntu0.26.10.1

On 24.04 you can let dpkg do the comparison, so you do not misread a long version string:

dpkg --compare-versions "$(dpkg-query -W -f='${Version}' ca-certificates)" ge 20260816ubuntu0.24.04.1 && echo patched

If it prints nothing, the server is behind. Ubuntu's own upgrade command for this one package is:

sudo apt update && sudo apt install --only-upgrade ca-certificates

For 22.04, 24.04 and 26.04, the notice describes the update as a refresh to version 2.90 of the Mozilla CA bundle. Ubuntu 20.04 and older get the fix only through ESM (Expanded Security Maintenance), which needs an Ubuntu Pro subscription. On 16.04 and 14.04 it also needs the Legacy Support add-on. If you run automatic security updates with unattended-upgrades, run the version check above anyway. A check you can see is better than an assumption. A server that missed this update has probably missed others, and checking your server for known CVEs finds them in one pass.

Ubuntu also warns about the cost of the fix: "It is unlikely, but this may result in some hosts being unreachable, due to their use of the revoked certificate." The same cost applies to every root you distrust by hand.

How does /etc/ca-certificates.conf decide what is trusted?

Each line in /etc/ca-certificates.conf is the path of one CA certificate under /usr/share/ca-certificates that should be trusted. The update-ca-certificates man page for 24.04 says a line that begins with ! is deselected, which deactivates that certificate.

update-ca-certificates reads the file and links each selected certificate into /etc/ssl/certs. It also picks up local .crt files from /usr/local/share/ca-certificates, which is how adding your own CA to Ubuntu's trust store works. Then it writes every selected certificate into one bundle file, /etc/ssl/certs/ca-certificates.crt.

A deselected root stays on disk under /usr/share/ca-certificates. Only the trust goes away. That is useful, because you test against that file in the steps below.

See how many bundled roots the file lists, and whether anything is already deselected:

grep -c '^mozilla/' /etc/ca-certificates.conf
grep '^!' /etc/ca-certificates.conf

The first command prints a count. The second prints nothing on a server where nobody has deselected a root yet.

Step 1: pick the root and back up the file

Run every step in one shell session, because the later commands use these two variables.

ROOT=$(grep -m1 '^mozilla/' /etc/ca-certificates.conf)
CERT="/usr/share/ca-certificates/$ROOT"
echo "$ROOT"
ls -l "$CERT"
sudo cp -a /etc/ca-certificates.conf /etc/ca-certificates.conf.bak

This practice run takes the first bundled root in the file, whatever it is on your image. ls should show a real file. For a real distrust, search by a word from the root's name, for example grep -i emsign /etc/ca-certificates.conf. Copy the whole line into ROOT exactly as the file prints it. That search may print nothing on a patched server, because the update already removed the line.

Step 2: prove the root is trusted now

Test the two stores separately. The first command reads only the /etc/ssl/certs directory. The second reads only the bundle file.

openssl verify -no-CAfile -no-CAstore -CApath /etc/ssl/certs "$CERT"
openssl verify -no-CApath -no-CAstore -CAfile /etc/ssl/certs/ca-certificates.crt "$CERT"
find /etc/ssl/certs -lname "$CERT"

The -no- options switch off OpenSSL's default locations, so each test reads one store and nothing else. -CApath needs files named by the hash of each certificate's subject name, and update-ca-certificates creates those hash links for you.

A healthy result: both openssl verify lines print the certificate's path followed by OK, and find prints one .pem link in /etc/ssl/certs. If openssl verify does not print OK here, the test proves nothing. That root fails for some other reason, so pick a different line and start again.

Step 3: mark the root's line with !

sudo sed -i 's|^'"$ROOT"'$|!&|' /etc/ca-certificates.conf
grep -n -F "$ROOT" /etc/ca-certificates.conf

The quoting matters. In an interactive bash shell, a ! inside double quotes starts history expansion, so bash can replace it with a piece of your last command. That is why the ! sits inside single quotes. Only $ROOT is in double quotes. In the replacement, & stands for the whole line that matched.

The grep should print the line with a ! at the front. If it prints the line without the !, the pattern did not match the line exactly. Compare the two strings and fix ROOT.

Step 4: rebuild the store with update-ca-certificates --fresh

sudo update-ca-certificates --fresh

--fresh removes the certificate links in /etc/ssl/certs, then builds the directory again from the conf file. The run should print Clearing symlinks in /etc/ssl/certs..., then Updating certificates in /etc/ssl/certs..., then a count line in the form <N> added, <M> removed; done.. If any hooks are installed in /etc/ca-certificates/update.d, it then prints Running hooks in /etc/ca-certificates/update.d... and runs them.

Why use --fresh and not a plain run? A plain run also handles ! lines, by removing the link for each deselected certificate. In the Debian source this package comes from, though, the removal step builds the link name without the character substitutions that the add step uses for spaces, parentheses and commas. A fresh rebuild avoids that problem. Every link is cleared and only selected certificates come back.

Do not read the counts as proof. A fresh run adds back every selected certificate, so the numbers describe the rebuild, not your one change. The proof is the next step.

Step 5: prove the root is no longer trusted

Run the same three checks again.

openssl verify -no-CAfile -no-CAstore -CApath /etc/ssl/certs "$CERT"
openssl verify -no-CApath -no-CAstore -CAfile /etc/ssl/certs/ca-certificates.crt "$CERT"
find /etc/ssl/certs -lname "$CERT"

Both openssl verify lines should now fail and exit non-zero. A root signs its own certificate, so once it is out of the store OpenSSL finds no trusted issuer for it. It reports an error about a self-signed certificate at depth 0, followed by a line ending in verification failed. find should print nothing, because the link is gone.

If only one of the two checks fails, the directory and the bundle disagree. That means the rebuild did not finish. Run sudo update-ca-certificates --fresh again and read its output for an E: line from a failing hook, or a W: line about a certificate that is listed but not found.

The same difference shows on the wire. A site whose chain ends at this root now fails in curl with a certificate verification error, where before it worked. That is the trade-off in one command.

Will apt upgrade undo the distrust?

It should not. In the Debian source for this package, the upgrade script keeps the ! lines from your existing /etc/ca-certificates.conf and writes each one into the new file as ! plus the path. Ubuntu's package is derived from that source, but check it on your own servers. After the next ca-certificates upgrade, run:

grep -n '^!' /etc/ca-certificates.conf

Your line should still be there with its !. Run the two openssl verify checks again, too. They are short, so put them in the same script or playbook that applies the distrust.

How to revert the change

Put the backup back and rebuild:

sudo cp -a /etc/ca-certificates.conf.bak /etc/ca-certificates.conf
sudo update-ca-certificates --fresh
openssl verify -no-CAfile -no-CAstore -CApath /etc/ssl/certs "$CERT"

The last line should print OK again. If the package was upgraded between the backup and the revert, do not copy an old file over a new one. Remove only the ! from your line instead:

sudo sed -i 's|^!'"$ROOT"'$|'"$ROOT"'|' /etc/ca-certificates.conf
sudo update-ca-certificates --fresh

What breaks when you distrust a root?

Every site and service whose certificate chain ends at that root breaks. Your server no longer accepts their certificates, so the TLS handshake fails before any data moves. That includes outbound API calls, package mirrors, webhooks and monitoring checks. A server that was fine yesterday now fails with a verification error, and the error names the certificate, not your change.

Private CAs are a separate case. If you use client certificates for mutual TLS in nginx, the CA you trust for those clients is usually a private one you control, so a public root change does not touch it. If you only need TLS for testing, a self-signed certificate on Ubuntu is trusted only where you put it, and deselecting a bundled root does not change it.

Before you distrust a root on a production server, test the services that server calls. Run curl -sS -o /dev/null https://<host> for each one after Step 5, on a staging server first.

Which copies of the root does this not touch?

The system store is one copy of the root among several. Each of the following can trust the root on its own, and the ! line does nothing to it. The store-by-store details of how each one finds certificates are in the guide to adding your own CA to Ubuntu's trust store. The checks below show where each copy lives on your server.

Java. The ca-certificates-java package uses the ca-certificates hooks to update the cacerts JKS keystore that many Java runtimes read. Do not assume that a deselection reaches that keystore. Check it. keytool -list -cacerts lists the keystore your default Java runtime uses, and it may ask for the keystore password. Search the output for words from the root's name.

Python certifi. certifi ships its own copy of Mozilla's root bundle. Its project page says it does not support adding or removing certificates. Find out which file each Python environment reads:

python3 -c "import certifi; print(certifi.where())"

A path under site-packages is a private bundle that the ! line does not change. Run the same command with the Python binary of every virtual environment on the server, because each one can carry its own certifi. A ModuleNotFoundError means that interpreter has no certifi installed.

Node.js. Node has a bundled CA store, a snapshot of Mozilla's store fixed at release time. --use-openssl-ca switches Node to OpenSSL's store, which on Linux the distribution maintains. --use-system-ca (added in v23.8.0) and NODE_USE_SYSTEM_CA=1 (added in v24.6.0 and v22.19.0) load the system certificates in addition to the bundled ones. Node's own documentation says it does not support distrust of certificates based on system settings. So a root that Node bundles stays trusted by Node until you upgrade Node itself. Check which version you run and which store options your build offers:

node -p process.versions.node
node --help | grep -i -- '-ca'

Snaps. A strictly confined snap reaches files on your system only through interfaces, so it may carry its own CA bundle. List your snaps, then look inside each one:

snap list
find /snap/<name>/current \( -name '*.crt' -o -name '*.pem' \) 2>/dev/null | head

Any bundle you find there belongs to the snap, and only a snap refresh changes it.

Container images. The layers of an image do not change once they are built. A host update does not change the files inside an image, so an image built before the update can still hold the old bundle. Check what an image carries, then rebuild it from a fresh base:

docker run --rm --entrypoint dpkg-query myapp:latest -W ca-certificates
docker build --pull --no-cache -t myapp:latest .

A host-side ! line does not reach a running container either, unless that container mounts the host's /etc/ssl/certs.

FAQ

How do I stop Ubuntu from trusting one root certificate?

Add a ! to the start of that root's line in /etc/ca-certificates.conf, then run sudo update-ca-certificates --fresh. The certificate file stays under /usr/share/ca-certificates, but it leaves /etc/ssl/certs and the /etc/ssl/certs/ca-certificates.crt bundle. Prove it with openssl verify -no-CAfile -no-CAstore -CApath /etc/ssl/certs against the root's file. It prints OK before the change and fails after it.

Does update-ca-certificates remove the root from Java, Python and Node?

Not by itself. Python's certifi ships its own bundle and does not support removing certificates. Node bundles a snapshot of Mozilla's store and does not support distrust based on system settings. Java's keystore is updated through ca-certificates hooks, so check it with keytool -list -cacerts and do not assume. Snaps and container images can also carry their own bundles.

Will apt upgrade put the distrusted root back?

The package's upgrade script is written to keep ! lines from your existing /etc/ca-certificates.conf. Confirm it on your servers. After the next ca-certificates upgrade, run grep -n '^!' /etc/ca-certificates.conf and repeat the openssl verify check against the root's file.

Why did a site stop working after I distrusted a root?

Its certificate chain ends at the root you distrusted, so your server rejects its certificate and the TLS handshake fails. That is the cost of every distrust. Put the root back by removing the ! and running sudo update-ca-certificates --fresh, or ask the site's operator to move to a certificate from another CA.

Is my Ubuntu server patched for the October 2026 eMudhra advisory?

Run dpkg-query -W ca-certificates. On Ubuntu 24.04 the fixed version is 20260816ubuntu0.24.04.1, and 22.04, 26.04 and 26.10 have matching 20260816ubuntu0 builds. Ubuntu 20.04 and older get the fix only through ESM with an Ubuntu Pro subscription. Upgrade with sudo apt update && sudo apt install --only-upgrade ca-certificates.

#tls#ca-certificates#ubuntu#openssl#trust-store