Skip to content
FreeBSDDeep Dive Published Updated 5 min readViews unavailable

FreeBSD certctl: Reconcile Trusted and Untrusted OpenSSL Certificates

Operate FreeBSD certctl as a trust-store reconciler, separate trusted inputs from generated outputs, and avoid assuming every TLS client uses OpenSSL.

FreeBSD’s certctl is best understood as a trust-store reconciler, not as a universal switch for every TLS application on the machine. Its manual says that it manages trusted Certificate Authorities for applications that use OpenSSL. A browser, language runtime, or application with a private trust bundle may follow a different path. Before changing a certificate, identify the client and the trust source it actually reads.

The second important boundary is between input directories and generated output. A successful certctl trust file or certctl untrust file command is not necessarily a durable policy change: the next rehash removes an entry unless a copy of that certificate also exists under the corresponding configured search path. Durable operations therefore start with understanding TRUSTPATH and UNTRUSTPATH, then end by rebuilding and inspecting the derived store.

The certctl(8) manual describes four primary operations. list displays trusted certificates, untrusted displays untrusted certificates, trust file adds a file to the trusted list unless it is already untrusted, and untrust file adds it to the untrusted list. rehash scans every directory in TRUSTPATH and UNTRUSTPATH, then writes per-certificate files under the configured destination paths and a combined bundle at BUNDLE.

This is a source-to-output relationship. The search paths are the maintained inputs; the rehashed links and bundle are derived data. If an administrator removes a generated link by hand but leaves the original certificate in an input directory, a later rehash can recreate it. Conversely, a one-off trust operation can appear to work and then disappear at the next rebuild if the certificate was not installed in a searched input directory. Treat the input directories as policy and the generated directories as cache-like material.

FreeBSD’s certificate-root maintenance notes add another useful distinction: the base root set is curated and periodically updated from Mozilla’s root program. The certctl commands operate on a host’s configured trust data; they are not an authenticity check for a certificate downloaded from an arbitrary source. Verify a certificate’s identity and provenance independently before making it trusted. Do not add an expired leaf certificate as though it were a root CA, and do not infer that a successful rehash makes every application trust it.

Inspect before changing anything

Start with read-only inventory and preserve the release-specific configuration:

freebsd-version -ku
certctl list
certctl untrusted

The -l option can show full paths and distinguished names when listing installed certificates. That is more useful than relying on a subject name alone: multiple certificates can have similar names, while a path identifies the file that will be reconciled. Review certctl(8) for the exact FreeBSD release rather than copying a command from another BSD. The utility has similarly named counterparts elsewhere, but their trust-store conventions and behavior are not interchangeable.

Before a planned change, inspect the effective environment and site configuration for TRUSTPATH, UNTRUSTPATH, TRUSTDESTDIR, UNTRUSTDESTDIR, and BUNDLE. The FreeBSD 15.1 defaults include trusted inputs under /usr/share/certs/trusted and /usr/local/share/certs/trusted, with /usr/local/share/certs retained as a compatibility input; untrusted inputs are under /usr/share/certs/untrusted and /usr/local/share/certs/untrusted. Generated links go under /etc/ssl/certs and /etc/ssl/untrusted, and the default bundle is /etc/ssl/cert.pem. DESTDIR, DISTBASE, and LOCALBASE can change how these paths resolve, so verify the effective values on the target host. The manual documents -n as a dry-run mode; use verbose dry-run output to review the files the utility proposes to create or remove. A dry run does not test whether a particular program reads the resulting bundle, and it is not a substitute for backing up a locally managed input directory.

Make a durable trust or distrust change

For a persistent local trust decision, stage the reviewed certificate in a directory that is part of the system’s configured trusted input path, then run rehash during a maintenance window. For a persistent distrust decision, place the certificate in a configured untrusted input directory. The next rehash uses those directories to reconstruct the respective outputs. Exact paths vary with release and configuration, so do not assume a path from another host is present in TRUSTPATH or UNTRUSTPATH.

The operational sequence should be reversible:

  1. Record the current release, environment, configured paths, and certctl list / certctl untrusted output.
  2. Preserve the original certificate bytes and their provenance. Compute a local digest for change tracking, but do not mistake a self-computed digest for authentication.
  3. Review the dry-run diff and confirm that it affects only the expected certificate and derived outputs.
  4. Install the input file in the intended trusted or untrusted directory with restrictive ownership and permissions appropriate to the host.
  5. Run certctl rehash, then inspect both lists and the expected generated bundle or links.
  6. Test one known OpenSSL-based client with a controlled endpoint and verify its trust result. Separately test any browser or runtime whose trust-store behavior matters.

Avoid the tempting shortcut of putting the same certificate in both trusted and untrusted inputs. The manual’s command semantics explicitly prevent trust from overriding an existing untrusted classification, and a confusing input set is harder to reason about during incident response. If a local policy is reversed, remove the obsolete input from the appropriate source directory, rehash, and verify that the generated result matches the intended policy.

Build-system staging is a different use case

The manual also exposes destination and METALOG options for staged builds. -D selects a destination root, -M selects a METALOG file, and -U avoids changing ownership when used with the documented METALOG/rehash combination. Those options support packaging and image construction; they are not a routine recipe for a live host. Test staged installation in a disposable root with the intended DESTDIR and DISTBASE values, and verify the resulting paths before packaging. Never point a staging invocation at / merely because a command copied from a build script contains -D.

certctl can update the OpenSSL-oriented CA store it manages, but cannot repair a TLS failure caused by a stale application-specific bundle, a hostname mismatch, an invalid certificate chain, a proxy interception policy, or a client that does not use OpenSSL. Record the client, trust file, error, and certificate chain before changing host-wide trust. That evidence keeps a narrow trust-store repair from turning into an unjustified machine-wide trust exception.

Related:

Sources:

Comments