Skip to content

Scout — proof collection (Trust Radar)

Beyond scanning, Scout gathers proof attestations: local, on-host evidence the Hub uses for Trust Radar. Every attestation carries a status from a small vocabulary — proven, partial, conflict, missing — and Scout filters out any attestation whose kind isn't in the wire vocabulary before publishing (a soft guard against collector drift, proof_collectors.go:20).

There are exactly four proof kinds. Each is fed by one or more collectors.

Read the maturity notes

Several collectors are narrower than their name suggests. This page is deliberately honest about what is and isn't proven, because the distinctions matter when you rely on the evidence. Everything below describes Scout on Linux x86-64 — the supported platform (see Install → Supported platforms).

local_trust_store_membership

"Is this CA certificate present in a trust store on this host?" Two sub-collectors feed it, both using the same membership test: parse the store, keep certs where x509.Certificate.IsCA == true, fingerprint by SHA-256.

This is a presence scan, not a trust-chain proof

Membership means "a CA-flagged cert sits in a known store path" — it does not verify the anchor is self-signed, that it actually roots any chain, or that the OS/app truly consults that store at runtime. It reads the files the OS would build trust from; it doesn't ask the OS "do you trust this?"

Sub-collector Inspects
OS store The distro CA bundles (/etc/ssl/certs/ca-certificates.crt, /etc/pki/tls/certs/ca-bundle.crt, /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem, /etc/ssl/ca-bundle.pem) + anchor dirs (/etc/ssl/certs, /etc/pki/ca-trust/source/anchors, /usr/local/share/ca-certificates) — the Debian/Ubuntu, RHEL/Fedora, and SUSE layouts. The agent flag --trust-store-path <abs-path> (repeatable) adds an extra bundle file or anchor directory on top of these defaults; a path already in the defaults is deduped, and anchors found via a configured path carry source_kind: operator_configured in their metadata. PEM / raw-DER / JKS.
Application stores Java cacerts at the JDK default paths, app-configured Java truststores (javax.net.ssl.trustStore-style directives parsed out of Tomcat server.xml, Kafka/Elasticsearch/Logstash configs, systemd units, /etc/default, /etc/sysconfig), service-mesh bundles (Istio/Linkerd/SPIFFE), and CA-file directives parsed out of nginx/Apache/HAProxy/Caddy/Traefik/Envoy configs.

Gaps worth knowing:

  • A missed path yields no attestation. The default OS-store paths are probed silently: one that is absent produces no attestation, not an error. A CA bundle at a non-standard location (a hardened image, a container with a custom trust path) is invisible until an operator declares it with --trust-store-path. A configured path that is missing or unreadable at collection time logs a WARN naming the path (a bad configured path is a signal, an absent default is not).
  • PKCS12/JCEKS Java stores are unsupported (no parser). An unreadable Java store emits an explicit missing proof; an unreadable nginx/Apache/etc. trust-store reference emits nothing — a silent gap, not a negative proof.
  • Config discovery is glob-based on conventional paths, not real parsing — no nginx include resolution, no YAML/Caddyfile grammar. Non-standard layouts are invisible.

installed_vs_served

"Does the cert configured/installed here match what's actually served on the wire?" Two collectors emit this kind:

  • Generic scan-vs-local — cross-references live scan fingerprints against filesystem-discovered certs. proven when fingerprints agree, conflict when they differ, partial when only one side is present.
  • Server-binding (nginx / Apache / HAProxy / Caddy / Traefik / Envoy) — reads the configured leaf cert file off disk, fingerprints it, and compares to the live scan for the name-matched vhost. Never reads private-key material (private_key_material_collected: false). Config paths are the conventional /etc locations for each server.

Coverage gaps

Server-binding proofs don't cover Tomcat/Kafka/Elasticsearch/Logstash's own served certs (those apps are only scanned for trust-store references), and there is no Kubernetes / cert-manager collector anywhere in Scout — if the product surfaces K8s cert-manager evidence, that lives outside Scout.

local_renewal_controller

"Which local automation renews this cert, and via which CA?" It parses certbot (/etc/letsencrypt/renewal/*.conf), acme.sh, lego (path heuristic), and generic systemd/cron definitions, and matches each to a discovered cert. If two different controllers claim the same cert, all are downgraded to conflict. Every discovered cert gets at least one attestation — an explicit missing if no controller is found. Full 4-state vocabulary.

key_custody_metadata

"Is there a plausible private-key file beside this cert, and what's its file hygiene?"

This never reads keys and never checks pairing

Despite the name, this collector is filename-convention guessing + stat() — it records the candidate key path, octal permission mode, a coarse permission summary, symlink flag, size, and owner/group UID/GID. It explicitly sets private_key_material_collected: false, key_match_validated: false — it does not open the key or verify it pairs with the cert. Describe it as "candidate key-file discovery + file hygiene," not "key custody verification."

Maturity at a glance

Collector Kind Maturity
Generic installed-vs-served installed_vs_served Live
Server binding installed_vs_served Live
OS trust store local_trust_store_membership Live
Application trust store local_trust_store_membership Live
Renewal controller local_renewal_controller Live
Key custody metadata key_custody_metadata Live