Wildcard distribution¶
A fleet-wide *.zone certificate the broker holds and serves to granted
agents. It keeps internal hostnames out of Certificate Transparency logs and
cuts rate-limit pressure — at the cost of a shared key.
Why wildcards for internal fleets¶
Every publicly-issued certificate is published to
Certificate Transparency logs,
forever. A per-host certificate for scada-hmi.plant.example.com broadcasts
that hostname to the world — reconnaissance gold. A single
*.plant.example.com wildcard leaks only the zone. For internal
infrastructure that's usually the right trade.
The cost: one private key shared across many hosts means a bigger blast radius if a host is compromised. gapcert bounds this by renewing the wildcard at 1/3 of its lifetime (frequent key rotation) and serving the key only to agents an explicit policy grants.
Broker policy¶
Mark a policy strategy: wildcard. It must name an explicit agents list
— an empty or match-all list is refused, because it would hand the shared
private key to the whole fleet:
policies:
- match: "*.plant.example.com"
strategy: wildcard
agents: ["plant-*"] # required — who may receive the shared key
solver: challenge-zone
The broker generates the wildcard key, obtains the certificate, and stores the
key encrypted at rest (AES-256-GCM). The encryption key comes from
GAPCERT_MASTER_KEY (base64 of 32 bytes) or a persisted per-install random key
— never a hardcoded default; if neither is available the broker fails closed.
Agent side¶
The agent marks the certificate shared and names the wildcard:
# ~/.config/gapcert/agent.yaml
data_dir: /var/lib/gapcert
certificates:
- name: plant-wildcard
domains: ["*.plant.example.com"]
shared: true
hook: "systemctl reload nginx"
$ gapcert agent obtain
plant-wildcard fetching shared *.plant.example.com from broker ...
plant-wildcard stored /var/lib/gapcert/certs/plant-wildcard (expires 2026-10-24)
POST /v1/shared is the only endpoint that returns a private key — mTLS-gated,
policy-gated, and audited on every fetch. Before installing, the agent verifies
the returned certificate and key form a matching pair and cover exactly the
requested wildcard.
Per-host or wildcard, per policy¶
The strategy is per policy, so one broker can serve both: wildcards for the zones where CT exposure matters and per-host certificates elsewhere. A per-host CSR request can never mint a wildcard, and a wildcard fetch can never yield a per-host name — the two paths are strictly separated and default-deny.
This path serves private keys, so it went through its own security cadence; see the security model.