The Ur Project

Documenting the craft of stacking things so they remain stacked. For a very long time.

ca-trust(8)

NAME

ca-trust - build the system TLS CA trust store

SYNOPSIS

ca-trust generate [--root DIR] [--force] [--dry-run] [--keep N]

ca-trust check [--root DIR] [--strict]

ca-trust [-h | --help] [-V | --version]

DESCRIPTION

Every TLS client on the system reads the same set of trusted CA certificates. That set is built from a few source directories, and somebody has to rebuild it whenever those change. If the rebuild dies halfway, or a reader catches it halfway, you get a system that trusts nothing, or trusts the wrong things. That's bad.

ca-trust is that rebuild, and it publishes the result in one step. It does no certificate parsing of its own. p11-kit is the trust authority, and ca-trust execs trust(1) to do the extraction. What ca-trust owns is the part p11-kit doesn't: deciding when the store needs rebuilding, and swapping it into place so that nothing ever reads a half-written store.

How does the swap work?

Each build lands in its own directory, /var/lib/ca-trust/store-<id>/. The <id> is a sha256 digest of the tree itself, so the name is the content. Then the live symlink is repointed onto it with a rename, which POSIX makes atomic. A reader either sees the old store or the new one. Never a mix.

Everything that can fail happens before that rename. If any of it fails, live stays where it was, so a broken run is a no-op rather than a broken trust store.

Because the id is the content, a rebuild that produces the same bytes is recognised as such. generate reports unchanged and leaves live alone.

What if the result is empty?

Then it's refused, not published. An empty or incomplete store is the signature of a misfired source redirect, and pushing it live would make every TLS connection on the box fail. Not even --force gets it through.

A complete store has all three outputs listed under FILES, and none of them empty.

Do I need root?

On a live system, yes. generate must run as root, and it never drops privileges, so the trust bytes stay root-owned end to end. Running it unprivileged without --root is refused:

ca-trust: must run as root to build the live trust store; pass --root <dir> to build a staged tree unprivileged

To build a store for a tree you own -- an image being composed, say -- run it unprivileged, with --root. As root, --root is refused too. p11-kit ignores user configuration as root, so it would read the host's certificates instead of the tree's. To get a store into a tree as root, install into it with pkg install --root, whose trigger builds the store inside it.

check without --strict only reads, so anyone can run it. check --strict recomputes the store, so the same rules as for generate apply.

COMMANDS

generate

Extract anchors via p11-kit and atomically repoint live. Run it after changing anything under the source directories listed in FILES. Running it again with nothing changed does nothing.

It prints one line saying what happened, for example swapped: live -> store-<id> (newly built), or unchanged: live already store-<id>.

Concurrent runs serialize on a flock(2) of /var/lib/ca-trust itself. There's no lock file. A second generate waits until the first finishes, because a run that swapped last with an older trust set would undo the newer one. --dry-run takes no lock.

check
Verify that live resolves to a complete store, and print the id it resolves to. It never writes anything. With --strict it also recomputes the store from the current sources, and fails if live is out of date.

OPTIONS

--root DIR
Treat DIR as / for both sources and destination, instead of the running system. Unprivileged only. ca-trust writes a transient p11-kit user configuration naming the relocated sources, so stock p11-kit needs no configuration of its own. As root this is refused, since p11-kit would ignore that configuration and read the host's anchors.
--force
(generate) Repoint live even when the freshly built store is identical to the one already live. It doesn't relax the completeness check.
--dry-run
(generate) Build the store in a temporary directory, compute its id, report whether a real run would swap, and write nothing under /var/lib/ca-trust.
--keep N
(generate) Keep the N most recently modified previous store directories around for rollback. The live store is always kept, and doesn't count against N. Default 1.
--strict
(check) Also recompute the store from the current sources, and fail if it differs from what is live. In other words: fail if the store is stale.
-h, --help
Print usage and exit. Running ca-trust with no arguments does the same.
-V, --version
Print the program name and version and exit. Accepted before or after a subcommand.

EXIT STATUS

0
Success. For check: live exists and is complete, and with --strict it's also up to date.
1
Failure, with the reason on standard error. For check this covers a missing, dangling or incomplete live, as well as a --strict run that finds the store stale.

FILES

Sources

/usr/share/pki/ca-trust-source/anchors/
Certificates to trust, shipped by the distribution.
/usr/share/pki/ca-trust-source/blocklist/
Certificates to reject, shipped by the distribution.
/etc/pki/ca-trust/source/anchors/
Certificates the administrator adds.
/etc/pki/ca-trust/source/blocklist/
Certificates the administrator rejects.

These four are p11-kit-trust's own compiled-in source directories, operator before vendor, so on a live root system p11-kit finds them with no configuration at all. Whether a distribution fills them with real files or with symlinks out of its own CA package is that package's business, not this tool's.

Output

/var/lib/ca-trust/store-<id>/
A built store, named for the digest of its own contents.
/var/lib/ca-trust/live
Symlink to the current store. Programs read through this.
/var/lib/ca-trust/live/ca-roots.pem
Every trusted anchor in one PEM bundle.
/var/lib/ca-trust/live/certs/
The same anchors one per file, hashed-directory style.
/var/lib/ca-trust/live/openssl/
Anchors plus the reject list, in OpenSSL's directory format.

ENVIRONMENT

CA_TRUST_SOURCE_DIRS
Colon-separated list of absolute source directories, replacing the compiled-in defaults above. For a p11-kit built with different defaults, and for testing. A relative entry is an error.
CA_TRUST_TRUST_BIN
The p11-kit trust executable to run. Defaults to /usr/bin/trust. PATH is not searched.
CA_TRUST_MODULE
Path to the p11-kit-trust.so module, for the unprivileged --root path where a p11-kit user configuration is written. Used when it exists; otherwise the usual module locations are tried.
SOURCE_DATE_EPOCH
Passed through to trust(1) for reproducible output.

The extraction otherwise runs with a cleared environment: only PATH is kept, and LC_ALL is set to C.

The CA_TRUST_* overrides are trusted like the rest of the caller's environment. A caller who sets them for root is already root. Package triggers run ca-trust with a fixed environment, so a trigger never carries them.

EXAMPLES

You added a certificate and want the system to trust it:

cp mycorp-root.crt /etc/pki/ca-trust/source/anchors/ ca-trust generate

Is the store up to date? Ask without touching it:

ca-trust check --strict

Without --strict, check only looks at live:

$ ca-trust check ok: live -> store-749c128fb80aa630...9c3ab9eda54dc8c3

See what a rebuild would do, without doing it:

ca-trust generate --dry-run

Build a store for an image being composed, unprivileged:

ca-trust generate --root image/

Keep more history to roll back through:

ca-trust generate --keep 5

NOTES

ca-trust needs p11-kit at run time: the trust(1) command and the p11-kit-trust.so module. Nothing else.

SEE ALSO

trust(1), p11-kit(8), update-ca-trust(8), openssl(1)

AUTHOR

Auke Kok <sofar@foo-projects.org>