Scout — certificate discovery¶
Alongside scanning the wire, Scout inventories the certificates on disk and
reports them as local_certs. This is how the Hub knows what's installed, and
it seeds several proof collectors and the auto-scan surface.
Where it looks¶
In agent mode, Scout inventories certificates from three directory sources — there are no other defaults:
{data-dir}/certs— always included.data-diris the agent's own data directory (the--data-dirflag; default/opt/cyphers/data). It also holdsagent.key/agent.crt/ca.crt/state.json; itscerts/subdirectory is where Scout writes the certs it issues or delivers ({data-dir}/certs/<domain>/), so discovery reports back what Scout itself installed.--cert-dir— a flag, repeatable. Any directory of certs to inventory; default/etc/letsencrypt/live(a standard certbot layout). Pass--cert-diragain to add more.--cert-dir-file— a flag naming an operator-maintained file (the installer wires<install-dir>/cert-dirs.conf): one absolute directory path per line,#comments and blank lines allowed. Additive to--cert-dir. Re-read onsystemctl reload cyphers-scout(SIGHUP), which also starts an immediate scan cycle (or joins one already in flight — the new directories then land in the next cycle).
/etc/ssl/certs is not a discovery source
Only the three above are walked for the certificate inventory.
/etc/ssl/certs and the OS CA bundles are read elsewhere — as trust
stores for proof collection — never as discovered
end-entity certs.
"Fully recursive" means Scout walks the entire tree beneath each source
(filepath.Walk): every nested subfolder and every regular file, not just the
top level. There's no extension filter — each file is read and fed to a PEM
decoder, keeping only CERTIFICATE blocks (keys, CSRs, and non-PEM files are
ignored), so a fullchain.pem yields one DiscoveredCert per block. Symlinked
files are followed (the reported path resolves to the real target — matters
for Let's Encrypt's live/ symlinks); symlinked directories are not
descended into.
What it extracts per cert¶
Per certificate block:
- Subject & issuer — CN
- Serial — plus its DER-hex form and the ARI cert id (RFC 9773)
- SHA-256 fingerprint
- Authority Key Identifier (AKI)
- Validity —
NotBefore/NotAfter,IsExpired, days-to-expiry - Key — type and size
- Signature algorithm
- Chain length — count of cert blocks in the same file
- SANs — DNS names only
Known extraction limits
- Only
DNSNamesare captured — IP, email, and URI SANs are silently dropped. - No leaf/CA flag is extracted;
IsCAisn't read. Downstream code infers "leaf" from a non-empty SAN list — a convention, not a parsed attribute.
What it does with them¶
- Reports every discovered cert as
local_certs(the Hub ingests these into inventory). - Localhost/SNI-probes each non-wildcard SAN against
127.0.0.1:443— to verify what's actually served locally matches what's on disk (catches behind-CDN and out-of-date or undeployed certs). - Auto-scans each non-wildcard SAN via public DNS — expanding the scan
surface beyond explicit
--targets to every domain named in an installed cert.
Quirks worth knowing (for analysis)¶
Discovery de-dups by certificate fingerprint, so the same leaf appearing in
cert.pem, fullchain.pem, and chain.pem in a Let's Encrypt live/ dir is
reported once, not 2–3 times.
Known gap
- Wildcard SANs are excluded from probing/auto-scan (a literal
*.host can't be dialed), so wildcard-covered hosts aren't auto-discovered unless listed in--target.
Maturity: the PEM walk, metadata extraction, fingerprint de-dup,
local_certs reporting, and both probing paths are all live and run every
scan cycle. The wildcard / non-DNS-SAN items are gaps, not failures.