Skip to content

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 two directory sources — there are no other defaults:

  • {data-dir}/certs — always included. data-dir is the agent's own data directory (the --data-dir flag; default /opt/cyphers/data). It also holds agent.key / agent.crt / ca.crt / state.json; its certs/ 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-file — the one configurable source: a flag naming the operator-maintained file (the installer wires <install-dir>/cert-dirs.conf), one absolute directory path per line, # comments and blank lines allowed. Required — the agent refuses to start without it. Re-read on systemctl 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). Listed directories are authoritative on every surface, not discovery alone: they count toward scan-target trust, they are the write/deploy path policy under which renewal delivery may install and re-point files, and every renewal lane, adopt_key and pre-flight search exactly this list.

There is no --cert-dir flag and no built-in default: a unit still carrying --cert-dir refuses to start, naming this file. /etc/letsencrypt/live (the certbot convention) is inventoried only when the file lists it — the installer seeds that line, visibly and deletably, when no --cert-dir was given at build time.

/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 DNSNames are captured — IP, email, and URI SANs are silently dropped.
  • No leaf/CA flag is extracted; IsCA isn't read. Downstream code infers "leaf" from a non-empty SAN list — a convention, not a parsed attribute.

What it does with them

  1. Reports every discovered cert as local_certs (the Hub ingests these into inventory).
  2. 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).
  3. 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.

Discovery inventories what is on the host. For how an existing certificate file is taken over at renewal — and why its filename does not matter — see Certificate files: naming & takeover.