docs: Phase 4 follow-on batch 1 — 5 issuer per-pages

Extract the first 5 issuer per-connector deep-dive pages:

- vault.md (128 lines) — Vault PKI synchronous issuance, token TTL +
  auto-renewal loop, MaxTTL enforcement, rotation playbook
- digicert.md (106 lines) — CertCentral DV/OV/EV with bounded async
  polling for vetting workflows
- aws-acm-pca.md (165 lines) — managed private CA on AWS with full
  IAM policy, IRSA wiring, troubleshooting matrix
- ejbca.md (116 lines) — open-source / Keyfactor EJBCA with mTLS or
  OAuth2 auth, mTLS keypair caching, approval-pending guidance
- adcs.md (111 lines) — Active Directory Certificate Services as
  enterprise root via Local CA sub-CA mode, sub-CA rotation playbook

Index updated with forward-list entries and the index-purpose blurb
revised so the index now positions itself as 'navigate from here;
deeper material lives in siblings' rather than 'docs to be extracted
later'.

Each per-page follows the WHAT/HOW/WHY pattern: what the connector is,
how authentication and issuance work, and when to choose this vs an
alternative. Cross-links to the connector index, async-ca-polling
primitive, and adjacent operator runbooks.

This is part 1 of 4 for the Phase 4 follow-on (per-connector page
extraction) tracked in cowork/docs-overhaul-phase-2-restructure-2026-05-04/log.md.

Net add: 5 files, 626 lines. No content removed from index.md (the
index keeps its inline reference; per-pages add operator depth on
top, matching the pattern set by apache/f5/iis/k8s/nginx in Phase 4
structural).
This commit is contained in:
shankar0123
2026-05-05 03:53:52 +00:00
parent b452013dd9
commit fd94205cfa
6 changed files with 643 additions and 6 deletions
+111
View File
@@ -0,0 +1,111 @@
# Active Directory Certificate Services (ADCS) Integration — Operator Deep-Dive
> Last reviewed: 2026-05-05
>
> Operator-grade documentation for integrating certctl with Microsoft
> ADCS as the enterprise root. For the connector-development context
> (interface contract, registry, ports/adapters), see the
> [connector index](index.md).
## Overview
ADCS integration is **not** a separate connector. certctl integrates
with ADCS via the **sub-CA mode** of the Local CA issuer: certctl
operates as a subordinate CA whose signing certificate was issued by
ADCS, so all certctl-issued certificates chain back to the enterprise
ADCS root.
This is the canonical pattern for Windows-shop deployments where
ADCS is already the root of trust and operators want certctl to
handle automation (lifecycle, renewal, deployment, alerts) without
ADCS having to support a non-Microsoft REST API surface.
## When to use this integration
Use ADCS sub-CA mode when:
- ADCS is your enterprise root and you don't want to introduce a
parallel root of trust.
- You want all certctl-issued certificates to validate against the
ADCS chain that's already in your Windows trust stores, mobile
device profiles, and load-balancer configurations.
- You need certctl's automation surface (ACME, SCEP, EST, profile
policy, scheduler, deployment connectors) but want ADCS to remain
the signing authority for the root.
Look elsewhere when:
- You want certctl to issue from its own root of trust — use the
Local CA issuer in self-signed mode.
- ADCS is being decommissioned or replaced — the migration path
from ADCS to Vault PKI / step-ca / Local CA needs its own
rollout plan; that's not what this connector covers.
## How sub-CA mode works
The Local CA issuer loads a pre-signed CA certificate and key from
disk:
- `CERTCTL_CA_CERT_PATH` — path to the certctl signing cert PEM
(the one ADCS issued).
- `CERTCTL_CA_KEY_PATH` — path to the matching private key PEM.
Every leaf certctl issues is signed with this key, and the chain
returned to clients includes both the certctl signing cert and the
ADCS root (so verifying clients see a complete chain to the
enterprise root).
The signing certificate certctl uses is just a normal CA cert with
`Basic Constraints: CA=true` and an appropriate path-length
constraint. ADCS issues this certificate using its standard
"Subordinate Certification Authority" template; the operator just
takes the resulting cert + key and points certctl at them.
## Operator playbook
### Provisioning the certctl sub-CA
1. Generate a new keypair for certctl on the host that will run it
(or in the HSM / KMS the operator wants to delegate signing to,
via the `internal/crypto/signer/` driver interface when alternate
drivers are configured).
2. Build a CSR with `Basic Constraints: CA=true`, the operator's
chosen path-length constraint, and key usages including
`keyCertSign` and `cRLSign`.
3. Submit the CSR to ADCS using the Subordinate Certification
Authority template (or a custom template that grants those key
usages).
4. Place the signed certctl-cert and the matching key at
`CERTCTL_CA_CERT_PATH` / `CERTCTL_CA_KEY_PATH`.
5. Restart certctl-server (or Rebuild the issuer via the API).
Subsequent issuance chains to the ADCS root.
### Rotating the sub-CA cert
When the certctl sub-CA cert is approaching expiry:
1. Generate a new keypair (re-keying is recommended at sub-CA
rotation time).
2. CSR + ADCS signing cycle as above.
3. Stage the new cert and key at fresh paths
(`CERTCTL_CA_CERT_PATH_NEW` etc.) and follow the
[intermediate-CA hierarchy
runbook](../intermediate-ca-hierarchy.md) for the cutover. The
key concern is overlap: both the old and new sub-CA certs must
chain to the ADCS root during the rollover so existing leaves
keep validating.
### Revocation chain
CRL and OCSP for ADCS-rooted leaves are handled by certctl's CRL
distribution point and OCSP responder
([crl-ocsp.md](../protocols/crl-ocsp.md)). The ADCS root publishes
its own CRL covering the certctl sub-CA cert; relying parties walk
both CDP entries to determine the full revocation status.
## Related docs
- [Local CA issuer](index.md#built-in-local-ca) — the connector this integration uses
- [Intermediate CA hierarchy](../intermediate-ca-hierarchy.md) — how certctl manages multi-level CA trees, including ADCS-rooted setups
- [CRL and OCSP](../protocols/crl-ocsp.md) — how relying parties validate ADCS-rooted leaves
- [Architecture](../architecture.md) — `internal/crypto/signer/` driver interface for HSM / KMS / cloud-KMS alternatives to file-on-disk for the certctl sub-CA private key
+165
View File
@@ -0,0 +1,165 @@
# AWS ACM Private CA Issuer Connector — Operator Deep-Dive
> Last reviewed: 2026-05-05
>
> Operator-grade documentation for the AWS Certificate Manager
> Private Certificate Authority (ACM PCA) issuer connector. For the
> connector-development context (interface contract, registry,
> ports/adapters), see the [connector index](index.md).
## Overview
AWS ACM Private CA is a managed private CA on AWS. The connector
calls `IssueCertificate` (which is asynchronous at the ACM PCA API
level), then runs the SDK's `NewCertificateIssuedWaiter` until the
cert reaches `CERTIFICATE_ISSUED` state, then `GetCertificate` to
retrieve the PEM. Default waiter timeout is 5 minutes; tune by
editing `defaultWaiterTimeout` in
`internal/connector/issuer/awsacmpca/awsacmpca.go`.
Implementation lives at `internal/connector/issuer/awsacmpca/`.
## When to use this connector
Use the AWS ACM PCA connector when:
- Your workloads are AWS-native and you want the CA to live inside
your AWS account (for blast-radius, IAM, and audit reasons).
- You need ACM PCA's CRL distribution and OCSP responder to serve
status to relying parties without certctl being in the OCSP path.
- You want IAM-based access control (no API keys to rotate) for
certctl's signing path.
Look elsewhere when:
- You're not on AWS — Google CAS or Azure Key Vault are the cloud-
native equivalents on those platforms.
- You need public-trust certificates — ACM PCA is private only.
- You don't already pay for ACM PCA (it has a non-trivial monthly
cost). Vault, step-ca, or the Local CA issuer are free
self-hosted alternatives.
## Configuration
| Setting | Required | Default | Description |
|---|---|---|---|
| `CERTCTL_AWS_PCA_REGION` | Yes | — | AWS region (e.g. `us-east-1`) |
| `CERTCTL_AWS_PCA_CA_ARN` | Yes | — | ARN of the ACM Private CA |
| `CERTCTL_AWS_PCA_SIGNING_ALGORITHM` | No | `SHA256WITHRSA` | Signing algorithm |
| `CERTCTL_AWS_PCA_VALIDITY_DAYS` | No | `365` | Certificate validity in days |
| `CERTCTL_AWS_PCA_TEMPLATE_ARN` | No | — | Optional certificate template ARN |
Supported signing algorithms: `SHA256WITHRSA`, `SHA384WITHRSA`,
`SHA512WITHRSA`, `SHA256WITHECDSA`, `SHA384WITHECDSA`,
`SHA512WITHECDSA`.
## Authentication
Standard AWS credential chain via
`aws-sdk-go-v2/config.LoadDefaultConfig()`. Resolves credentials in
this order:
1. Environment variables (`AWS_ACCESS_KEY_ID`,
`AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`).
2. Shared config files (`~/.aws/config`, `~/.aws/credentials`,
profile via `AWS_PROFILE`).
3. IAM Roles for Service Accounts (IRSA) on EKS.
4. EC2 instance profiles.
5. ECS task roles.
6. SSO.
certctl never stores AWS credentials directly — set them in the
certctl process's environment or via the IAM role attached to the
host.
## Minimal IAM policy
The IAM principal that certctl authenticates as needs the following
actions against the CA's ARN:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"acm-pca:IssueCertificate",
"acm-pca:GetCertificate",
"acm-pca:RevokeCertificate",
"acm-pca:GetCertificateAuthorityCertificate"
],
"Resource": "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012"
}
]
}
```
Replace the `Resource` ARN with your own CA ARN. If you use a
`TemplateArn` (subordinate-CA template), the policy needs no
additional permissions — `IssueCertificate` covers it.
## Worked example: add the issuer via API
```bash
curl -k -X POST https://localhost:8443/api/v1/issuers \
-H 'Content-Type: application/json' \
-d '{
"id": "iss-aws-prod",
"name": "AWS ACM PCA (prod)",
"type": "AWSACMPCA",
"config": {
"region": "us-east-1",
"ca_arn": "arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012",
"signing_algorithm": "SHA256WITHRSA",
"validity_days": 90
}
}'
```
The certctl server process must have AWS credentials available
before the issuer is created (or before any subsequent issuance
call). For a local dev run with shared-config creds:
`export AWS_PROFILE=my-profile` before `docker compose up`. For an
EKS deployment: attach an IRSA-bound IAM role to the certctl pod's
service account.
## Troubleshooting
### `AccessDeniedException: User ... is not authorized to perform: acm-pca:IssueCertificate`
The IAM principal certctl is using lacks the required actions.
Apply the IAM policy above (scoped to your CA ARN) to the
role/user. The principal can be inspected with
`aws sts get-caller-identity` from the certctl host.
### `ResourceNotFoundException: Could not find Certificate Authority`
The `CAArn` doesn't match any CA in the configured region. Common
causes: region mismatch (CA is in `us-west-2`, certctl region is
set to `us-east-1`), CA was deleted, ARN typo. Verify with
`aws acm-pca describe-certificate-authority --certificate-authority-arn <arn> --region <region>`.
### `acmpca waiter (waiting for issuance): exceeded max wait time`
The cert was submitted but didn't reach `CERTIFICATE_ISSUED` state
within 5 minutes. Check the CA's CloudWatch metrics for backlog;
check the CA's audit reports for any policy violations on the
request. If the wait is consistently slow, edit
`defaultWaiterTimeout` in
`internal/connector/issuer/awsacmpca/awsacmpca.go` and rebuild.
## Revocation
CRL and OCSP are managed by AWS ACM PCA directly. certctl records
revocations locally and notifies AWS via the `RevokeCertificate`
API with RFC 5280 reason mapping (e.g. `keyCompromise`
`KEY_COMPROMISE`). AWS ACM PCA's CRL distribution point and OCSP
responder serve the resulting status to verifying clients —
certctl is **not** in the OCSP path for this connector.
## Related docs
- [Connector index](index.md) — interface contract, registry, port/adapter wiring
- [Async CA polling](../protocols/async-ca-polling.md) — bounded-polling primitive (ACM PCA uses the SDK waiter, not certctl's polling, but the same operator concerns apply)
- [Disaster recovery runbook](../../operator/runbooks/disaster-recovery.md) — what happens to ACM PCA-issued certs if the CA is deleted
+106
View File
@@ -0,0 +1,106 @@
# DigiCert CertCentral Issuer Connector — Operator Deep-Dive
> Last reviewed: 2026-05-05
>
> Operator-grade documentation for the DigiCert CertCentral issuer
> connector. For the connector-development context (interface
> contract, registry, ports/adapters), see the
> [connector index](index.md).
## Overview
The DigiCert connector integrates with DigiCert's CertCentral REST
API for ordering and managing certificates from DigiCert's commercial
public CA. It supports Domain Validated (DV), Organization Validated
(OV), and Extended Validated (EV) certificates, with async order
processing for OV/EV.
Implementation lives at `internal/connector/issuer/digicert/`.
## When to use this connector
Use the DigiCert connector when:
- You're already a DigiCert CertCentral customer and want certctl to
drive issuance, renewal, and deployment from the same platform that
manages your internal PKI.
- You need OV or EV certificates that require DigiCert to validate
organization details before issuance.
- You want one tool that covers both internal CAs (Vault, Local,
step-ca) and a public-trust commercial CA.
Look elsewhere when:
- You only need DV certificates and Let's Encrypt / ZeroSSL is an
acceptable issuer — use the ACME connector instead.
- You need self-hosted PKI with no commercial CA dependency — use
Vault PKI, step-ca, or the Local CA issuer.
## Configuration
| Variable | Default | Description |
|---|---|---|
| `CERTCTL_DIGICERT_API_KEY` | — | DigiCert API key (sent in `X-DC-DEVKEY` header) |
| `CERTCTL_DIGICERT_ORG_ID` | — | DigiCert organization ID |
| `CERTCTL_DIGICERT_PRODUCT_TYPE` | `ssl_basic` | Certificate product (e.g. `ssl_basic`, `ssl_plus`, `ssl_ev`) |
| `CERTCTL_DIGICERT_BASE_URL` | `https://www.digicert.com/services/v2` | DigiCert API base URL |
| `CERTCTL_DIGICERT_POLL_MAX_WAIT_SECONDS` | `600` | Bounded-polling deadline for `GetOrderStatus` |
## Authentication
API key passed via `X-DC-DEVKEY` header. The organization ID is sent
in the request body (not the header). No mTLS or OAuth2 required.
## Issuance model
- **DV certificates** — typically issue immediately; the
`/order/certificate/create` API may return the PEM in the same
response.
- **OV / EV certificates** — require DigiCert-side validation
(vetting org documents, checking domain ownership). The API
returns 201 with an order ID; certctl's `GetOrderStatus` polls
until the certificate is retrievable.
`GetOrderStatus` runs bounded internal polling (5s/15s/45s/2m/5m
capped, ±20% jitter, default 10-minute deadline). For OV/EV orders
where humans approve enrollments, bump
`CERTCTL_DIGICERT_POLL_MAX_WAIT_SECONDS` to a value that comfortably
covers the approval window — see
[async-ca-polling.md](../protocols/async-ca-polling.md) for the
schedule shape and tuning guidance.
## Revocation
CRL and OCSP are managed by DigiCert. Clients should validate
certificate status against DigiCert's infrastructure. certctl
records the revocation locally (audit row + cert state) but does
**not** call DigiCert's revoke endpoint — operators revoke through
DigiCert's dashboard or the CertCentral REST API directly. This
keeps the certctl revocation flow simple at the cost of one extra
manual step on revocation.
## Operator playbook
### API key rotation
Rotate the API key in DigiCert's dashboard, then either restart
certctl-server with the new value in `CERTCTL_DIGICERT_API_KEY` or
hot-swap via `PUT /api/v1/issuers/{id}` so the registry's Rebuild
path replaces the connector with the new key. No certificate
state is invalidated by the rotation — the new key just signs
future API calls.
### Diagnosing slow OV/EV issuance
DigiCert's OV/EV vetting is a human process and can take hours to
days. Bumping `CERTCTL_DIGICERT_POLL_MAX_WAIT_SECONDS` lets a
single tick wait through the full approval window, but the better
operational pattern is to issue OV/EV certs well ahead of expiry
so the bounded poll deadline is short. The renewal scheduler's
"alert at T-30 days" default exists for exactly this reason.
## Related docs
- [Connector index](index.md) — interface contract, registry, port/adapter wiring
- [Async CA polling](../protocols/async-ca-polling.md) — the bounded-polling primitive
- [ACME server](../protocols/acme-server.md) — alternative issuer for DV-only workflows
+116
View File
@@ -0,0 +1,116 @@
# EJBCA (Keyfactor) Issuer Connector — Operator Deep-Dive
> Last reviewed: 2026-05-05
>
> Operator-grade documentation for the EJBCA issuer connector. For
> the connector-development context (interface contract, registry,
> ports/adapters), see the [connector index](index.md).
## Overview
The EJBCA connector calls the EJBCA REST API for self-hosted
open-source and Keyfactor enterprise CAs. It supports dual
authentication: mTLS (default) or OAuth2 Bearer token, selectable
via configuration.
Implementation lives at `internal/connector/issuer/ejbca/`.
## When to use this connector
Use the EJBCA connector when:
- You already run EJBCA Community Edition or Keyfactor EJBCA
Enterprise as your internal CA and want certctl to drive the
lifecycle automation (renewal, deployment, alerts) on top.
- You need EJBCA's certificate-profile and end-entity-profile
policy enforcement — those policies stay in EJBCA and certctl
passes the profile names through.
- You need approval-pending workflows (humans approve enrollments)
— EJBCA supports the 201-Accepted async path.
Look elsewhere when:
- You want a simpler internal CA without EJBCA's operational weight
— Vault PKI, step-ca, or the Local CA issuer are lighter.
- You need a managed CA (no servers to run) — Google CAS or AWS
ACM PCA on cloud, or DigiCert / Sectigo for commercial PKI.
## Configuration
| Setting | Required | Default | Description |
|---|---|---|---|
| `CERTCTL_EJBCA_API_URL` | Yes | — | EJBCA REST API base URL |
| `CERTCTL_EJBCA_AUTH_MODE` | No | `mtls` | Auth mode: `mtls` or `oauth2` |
| `CERTCTL_EJBCA_CLIENT_CERT_PATH` | mTLS | — | Path to client certificate PEM (mTLS mode) |
| `CERTCTL_EJBCA_CLIENT_KEY_PATH` | mTLS | — | Path to client key PEM (mTLS mode) |
| `CERTCTL_EJBCA_TOKEN` | OAuth2 | — | Bearer token (oauth2 mode) |
| `CERTCTL_EJBCA_CA_NAME` | Yes | — | EJBCA CA name |
| `CERTCTL_EJBCA_CERT_PROFILE` | No | — | EJBCA certificate profile |
| `CERTCTL_EJBCA_EE_PROFILE` | No | — | EJBCA end-entity profile |
## Authentication
Configurable via `auth_mode`:
- **`mtls`** — client certificate and key are loaded for the TLS
handshake. This is the default and the more common deployment
mode for EJBCA.
- **`oauth2`** — the token is sent as `Authorization: Bearer
{token}`. Use when EJBCA is fronted by an OAuth2-aware reverse
proxy or when integrating with Keyfactor's identity provider.
The mTLS keypair is cached on the connector after the first API
call and reused for the lifetime of the process; rotation is
picked up automatically via mtime polling on the cert file (see
the mtls keypair caching note in the [connector
index](index.md#built-in-ejbca-keyfactor)).
## Issuance model
`POST /v1/certificate/pkcs10enroll` with base64-encoded CSR.
Returns base64-encoded certificate PEM. EJBCA 9.3+ creates
end-entity and issues cert in a single call. Approval-pending
enrollments return 201 with a tracking ID; certctl's
`GetOrderStatus` polls until the certificate is available.
## Revocation
EJBCA requires both issuer DN and serial number for revocation.
The connector stores these as a composite `OrderID` in
`issuer_dn::serial` format.
CRL and OCSP are managed by the EJBCA instance. certctl records
revocations locally and notifies EJBCA via
`PUT /v1/certificate/{issuer_dn}/{serial}/revoke`.
## Operator playbook
### mTLS rotation without downtime
`mv -f new.crt /etc/certctl/ejbca/client.crt` (mtime changes), no
process restart required. The next API call re-parses the file
and rebuilds the `*http.Transport`. `os.Stat` errors during
rotation surface as connector errors rather than silently serving
stale credentials.
### Switching from mTLS to OAuth2
Update the issuer config via `PUT /api/v1/issuers/{id}` with the
new `auth_mode: oauth2` and `token`. The registry's Rebuild path
replaces the connector without restart. Prior issuance state
(serial numbers, cert state) is unaffected.
### Diagnosing approval-pending hangs
If `GetOrderStatus` consistently times out, the operator approval
queue in EJBCA is the most common cause. Bump
`CERTCTL_EJBCA_POLL_MAX_WAIT_SECONDS` so a single tick can wait
through the full approval window — see
[async-ca-polling.md](../protocols/async-ca-polling.md) for the
schedule shape.
## Related docs
- [Connector index](index.md) — interface contract, registry, port/adapter wiring
- [Async CA polling](../protocols/async-ca-polling.md) — bounded-polling primitive
- [Approval workflow](../../operator/approval-workflow.md) — certctl-side two-person integrity (separate from EJBCA's approval queue, but addresses the same shape of risk on the certctl side)
+17 -6
View File
@@ -2,17 +2,28 @@
> Last reviewed: 2026-05-05 > Last reviewed: 2026-05-05
> >
> This is the canonical connector reference. Per-connector deep-dive > This is the canonical connector reference: interface contracts,
> pages exist for the targets that have unique operational quirks > registry, deployment primitive, network scanner, cloud discovery.
> (apache, f5, iis, k8s, nginx — see siblings in this directory). Other > Each built-in connector below has a sibling per-page that goes
> connectors are documented inline in this file and will be extracted > deeper on operator-grade material (vendor edges, troubleshooting,
> to their own pages as operational depth grows. Cross-link from this > rotation playbooks, when-to-use vs alternatives). Use this index
> index to per-connector pages where they exist. > to navigate; jump to the sibling pages for hands-on operator
> material.
Connectors extend certctl to integrate with external systems for certificate issuance, deployment, and notifications. This guide covers the connector interfaces, built-in implementations, and how to build your own. Connectors extend certctl to integrate with external systems for certificate issuance, deployment, and notifications. This guide covers the connector interfaces, built-in implementations, and how to build your own.
**Per-connector deep-dive pages** (siblings in this directory): **Per-connector deep-dive pages** (siblings in this directory):
Issuer connectors:
- [ADCS integration](adcs.md) — Active Directory Certificate Services as enterprise root via Local CA sub-CA mode
- [AWS ACM Private CA](aws-acm-pca.md) — managed private CA on AWS, IAM-authenticated
- [DigiCert CertCentral](digicert.md) — commercial public CA (DV / OV / EV)
- [EJBCA (Keyfactor)](ejbca.md) — self-hosted open-source / Keyfactor enterprise CA
- [Vault PKI](vault.md) — HashiCorp Vault PKI engine, synchronous issuance
Target connectors:
- [Apache](apache.md) — Apache httpd connector deep dive - [Apache](apache.md) — Apache httpd connector deep dive
- [F5 BIG-IP](f5.md) — F5 connector deep dive (proxy agent + iControl REST) - [F5 BIG-IP](f5.md) — F5 connector deep dive (proxy agent + iControl REST)
- [IIS](iis.md) — Microsoft IIS connector deep dive (local PowerShell + WinRM modes) - [IIS](iis.md) — Microsoft IIS connector deep dive (local PowerShell + WinRM modes)
+128
View File
@@ -0,0 +1,128 @@
# Vault PKI Issuer Connector — Operator Deep-Dive
> Last reviewed: 2026-05-05
>
> Operator-grade documentation for the HashiCorp Vault PKI issuer
> connector. For the connector-development context (interface contract,
> registry, ports/adapters), see the
> [connector index](index.md).
## Overview
The Vault PKI connector integrates with HashiCorp Vault's PKI secrets
engine using its native `/sign` API with token-based authentication.
The flow is purely synchronous — Vault returns the signed certificate
in the same HTTP response that submits the CSR — so there is no
challenge-solving or async polling on the certctl side.
Implementation lives at `internal/connector/issuer/vault/`. The
factory key is `Vault`; the registry binds it under whatever issuer
ID the operator picks (e.g. `iss-vault`).
## When to use this connector
Use the Vault PKI connector when:
- Your organization already runs Vault as the system of record for
internal certificates.
- You want a synchronous, low-latency issuance path with no challenge
flow (no DNS records, no HTTP-01).
- You want certctl to manage the lifecycle (renewal scheduling,
deployment, alerts) while Vault keeps the signing material.
Look elsewhere when:
- Public-trust certificates are required — Vault PKI is internal-only.
Use ACME (Let's Encrypt, ZeroSSL, Sectigo) or DigiCert / Sectigo SCM
for public-trust workloads.
- The Vault PKI engine is not already deployed and you don't want to
run Vault. The Local CA issuer is a simpler self-contained path for
small internal CAs.
## Configuration
| Variable | Default | Description |
|---|---|---|
| `CERTCTL_VAULT_ADDR` | — | Vault server address (e.g. `https://vault.internal:8200`) |
| `CERTCTL_VAULT_TOKEN` | — | Vault auth token with permissions on the PKI mount |
| `CERTCTL_VAULT_MOUNT` | `pki` | PKI secrets engine mount path |
| `CERTCTL_VAULT_ROLE` | — | PKI role name for certificate signing |
| `CERTCTL_VAULT_TTL` | `8760h` | Certificate validity period (TTL) |
Vault issues certificates synchronously via the
`/v1/{mount}/sign/{role}` API with `X-Vault-Token` header
authentication. The issued certificate is parsed to extract serial
number, validity dates, and chain information.
## Token TTL and automatic renewal
This was Top-10 fix #5 from the 2026-05-03 issuer-coverage audit.
certctl-server periodically calls `POST /v1/auth/token/renew-self` at
half the token's TTL to keep the integration alive without manual
rotation. The cadence is read from a one-shot `lookup-self` at
startup and re-derived on every successful renewal — so a short
bootstrap token that gets renewed up to a longer Max TTL shifts to
the longer cadence automatically.
The renewal loop emits the
`certctl_vault_token_renewals_total{result="success"|"failure"|"not_renewable"}`
Prometheus counter so operators see expiry trouble in Grafana before
issuance breaks.
When Vault returns `renewable: false` (configured Max TTL reached),
the loop logs a WARN, increments `{result="not_renewable"}`, and
exits. The operator must rotate the Vault token and either restart
certctl-server or use the GUI / MCP issuer-update path to swap the
token in place — the registry's Rebuild path re-Starts the lifecycle
on the new connector.
Per-tick failures (e.g. transient 5xx, brief network blips) bump
`{result="failure"}` and the loop keeps ticking. Only the explicit
`renewable: false` case stops it.
## MaxTTL enforcement (M11c)
When a certificate profile defines a maximum TTL, the Vault connector
overrides the TTL string in the signing request to ensure the issued
certificate does not exceed the profile limit. This is applied
**before** Vault's own role-level max TTL — so the effective limit is
the minimum of (profile.MaxTTL, role.MaxLeaseTTL).
## Revocation and CRL/OCSP
CRL and OCSP are managed by Vault itself. Clients should validate
certificate status against Vault's own CRL/OCSP endpoints
(`GET /v1/{mount}/crl` and Vault's OCSP responder). certctl does not
generate local CRL/OCSP for Vault-issued certificates. Revocation is
recorded locally (audit row + cert state) but Vault is the
authoritative source for relying parties.
## Operator playbook
### Token rotation without downtime
Two paths:
1. **Restart-driven.** Update `CERTCTL_VAULT_TOKEN` env var on the
server, restart certctl-server. The renewal loop picks up the new
token's lookup-self response and resumes ticking.
2. **Hot-swap via API/GUI.** `PUT /api/v1/issuers/{id}` with the
updated config; the registry's Rebuild path replaces the connector
without restart. Use this when Vault's Max TTL has been reached
and the existing token can no longer be renewed.
### Diagnosing renewal failures
Watch
`certctl_vault_token_renewals_total{result="not_renewable"}` and
`{result="failure"}`. Sustained failures with no `not_renewable`
generally indicate Vault unreachability or token-policy drift; a
spike in `not_renewable` is the canonical signal that a Max TTL
boundary was hit and operator action is required.
## Related docs
- [Connector index](index.md) — interface contract, registry, port/adapter wiring
- [Issuer hierarchy primitive](../intermediate-ca-hierarchy.md) — how Vault sits as a sub-CA under another issuer
- [Async CA polling](../protocols/async-ca-polling.md) — the bounded-polling primitive used by other issuers; Vault is synchronous so does not consume it