hashicorp/consul
Connect CA
Consul's service mesh issues short-lived SPIFFE certificates to every mesh-enabled workload. The PKI machinery — root CA selection, root rotation, leaf issuance, cross-signing during provider migration — is collectively the Connect CA.
Purpose
Each mesh service has a SPIFFE URI like spiffe://<trust-domain>/ns/<ns>/dc/<dc>/svc/<service>. Envoy proxies present a leaf cert with that URI, and peers verify it against the root CA(s). Consul:
- Picks a CA provider (built-in, Vault, AWS PCA).
- Maintains the active root and any cross-signed predecessor roots in the state store.
- Issues short-lived (default ~72h) leaf certs to local proxies on demand.
- Rotates roots on schedule or on operator command.
- Replicates roots to all DCs over WAN federation and to peers via peering.
Directory layout
agent/connect/
├── ca/ # CA provider implementations (consul, vault, aws-pca, ...)
├── csr.go, sni.go, uri.go # SPIFFE URI parsing + CSR helpers
├── parsing.go, testing.go
agent/leafcert/ # Leaf cert cache: the agent-side of "issue and renew"
agent/consul/
├── connect_ca_endpoint.go # RPC endpoint for CA root reads + leaf signing
├── leader_connect_ca.go # Leader-only loop: rotation, cross-sign, intermediate refresh (58 KB)
├── leader_connect_ca_ce.go # CE-specific bits
├── leader_connect.go # Connect bootstrap on leadership change
├── server_connect.go # Server-side init for connect features
├── state/connect_ca.go # MemDB tables: connect-ca-config, roots, leaf logs, builtin
├── state/connect_ca_events.go # Streaming events on root changes
connect/ # Public Go module: small Connect helper library for client apps
proto-public/pbconnectca/ # gRPC public API (SignCertificate, WatchRoots)Key abstractions
| Type | File | Purpose |
|---|---|---|
ca.Provider |
agent/connect/ca/provider.go |
Interface every CA backend implements |
ca.ConsulProvider |
agent/connect/ca/provider_consul.go |
Built-in self-hosted CA backed by connect-ca-builtin table |
ca.VaultProvider |
agent/connect/ca/provider_vault.go |
Vault PKI backend |
ca.AWSProvider |
agent/connect/ca/provider_aws.go |
AWS Private Certificate Authority backend |
CARoot / IndexedCARoots |
agent/structs/connect_ca.go |
The persisted root certificate model |
CASignRequest / IssuedCert |
agent/structs/connect.go |
Leaf signing RPC contract |
leafcert.Manager |
agent/leafcert/leafcert.go |
Per-agent cache of leaf certs with proactive renewal |
caManager |
agent/consul/leader_connect_ca.go |
Leader-side CA orchestrator: provider lifecycle, rotation, cross-sign |
How it works
sequenceDiagram
participant Leader as Server (leader)
participant Provider as ca.Provider
participant State as state store
participant Agent as Client agent
participant Leaf as leafcert.Manager
participant Proxy as Envoy
Note over Leader,Provider: Bootstrap on leadership
Leader->>Provider: GenerateRoot()
Provider-->>Leader: root + signing key
Leader->>State: Apply CARootSet (Raft)
Note over Agent,Leaf: First proxy registration
Proxy->>Agent: xDS request (needs cert)
Agent->>Leaf: GetLeaf(serviceID)
Leaf->>Leader: ConnectCA.Sign(CSR)
Leader->>Provider: Sign(CSR)
Provider-->>Leader: leaf cert
Leader-->>Leaf: IssuedCert
Leaf-->>Agent: cache + return
Agent-->>Proxy: SDS secret
Note over Leader,State: Periodic / triggered rotation
Leader->>Provider: RotateRootCA()
Provider-->>Leader: new root
Leader->>Provider: CrossSignCA(oldRoot, newRoot)
Provider-->>Leader: cross-signed bundle
Leader->>State: Apply CARootSet
State-->>Agent: streaming root update
Agent->>Leaf: re-issue leafs against new chainleader_connect_ca.go is the leader-side orchestrator — it owns the CA provider lifecycle, schedules root rotation (at 60% of root lifetime by default), runs the cross-sign dance when migrating from one provider to another, and rotates intermediates (Vault).
agent/leafcert/ is the agent-side cache. It pre-emptively re-issues leafs at half their TTL so steady-state Envoy traffic never sees an expired cert.
Provider plug-ins
| Provider | Backed by | Notes |
|---|---|---|
| Consul (default) | connect-ca-builtin MemDB table |
Self-signed root in Raft; intermediates per DC. The simplest setup, suitable for most operators. |
| Vault | HashiCorp Vault PKI engine | Vault holds the signing key; Consul calls Vault on every leaf signing. Supports both intermediate-CA and full-PKI mounts. |
| AWS Private CA | AWS Certificate Manager Private CA | Long-lived intermediates from AWS, leaves issued via AWS PCA SDK. |
Adding a new provider means implementing the ca.Provider interface and registering it in agent/consul/connect_ca_endpoint.go's factory map.
ACL and auth
Leaf signing is gated by ACL policies tied to the proxy's service identity. The CSR validates that the requested SPIFFE URI matches the agent's authenticated identity (token + service registration). Identity logic lives in agent/connect/uri.go and agent/structs/identity.go.
Federation and peering
- WAN federation: roots replicate via the federation state replication loop (
agent/consul/leader_federation_state_ae.go). Each DC's primary CA cross-signs the others' roots so a single trust chain spans the federation. - Cluster peering: trust bundles are exchanged out-of-band when peering is established and refreshed by
leader_peering.go. Peers don't share signing authority — they just trust each other's roots.
Public gRPC API
proto-public/pbconnectca/ defines:
Sign(CertificateSigningRequest) returns (Certificate)WatchRoots(stream)— long-lived stream of root changes, used by Consul Dataplane.
The server-side handler is in agent/grpc-external/services/connectca/.
Integration points
- xDS / proxycfg: roots and leaf go into every snapshot, surface as Envoy
secretresources viaagent/xds/secrets.go. - Cache types:
agent/cache-types/connect_ca_*.goandagent/cache-types/connect_ca_root.goallow blocking-query reads of roots through the agent cache. - Auto-config: the
auto-config/bootstrap path uses the CA to mint TLS certs for client agents joining the cluster. - Operator control:
consul connect ca get|set(incommand/connect/ca/) reads/writes the active provider configuration.
Entry points for modification
- Add a CA provider: implement
ca.Providerinagent/connect/ca/, register it, add config decode inagent/structs/connect_ca.go. - Adjust rotation policy: see the
caRotateRootandcaRenewIntermediatefunctions inagent/consul/leader_connect_ca.go. - Change SPIFFE URI semantics:
agent/connect/uri.gois the parser; downstream code uses the parsed URIs everywhere. - Tune leaf TTL or proactive renewal: see
agent/leafcert/leafcert.go's*Manager.refresh*paths.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.