mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 15:32:02 +00:00
ecb8896b1c
Phase 4 structural (commit 633e440) moved 6 connector files into the
new docs/reference/connectors/ subdirectory but didn't update all
inter-doc references for the new path layout. Phase 11 caught the
high-traffic ones; this sweep gets the rest, found by the Phase 4
follow-on verification pass.
Mappings applied (relative to docs/reference/connectors/):
deployment-atomicity.md → ../deployment-model.md
deployment-vendor-matrix.md → ../vendor-matrix.md
architecture.md → ../architecture.md
est.md → ../protocols/est.md
scep-intune.md → ../protocols/scep-intune.md
async-polling.md → ../protocols/async-ca-polling.md
quickstart.md → ../../getting-started/quickstart.md
demo-advanced.md → ../../getting-started/advanced-demo.md
legacy-est-scep.md → ../protocols/scep-server.md
connectors.md → index.md
Plus prose backtick references (`docs/architecture.md` etc.) updated
to the new subdirectory paths.
Files touched: apache, f5, iis, k8s, nginx, index. 33 line changes.
Full link-check across docs/reference/connectors/*.md is now clean
(0 broken inter-doc references).
165 lines
6.1 KiB
Markdown
165 lines
6.1 KiB
Markdown
# NGINX Connector — Operator Deep-Dive
|
|
|
|
> Last reviewed: 2026-05-05
|
|
>
|
|
> Per Phase 14 of the deploy-hardening II master bundle. Operator-grade
|
|
> documentation for the NGINX target connector. For the
|
|
> connector-development context (interface contract, registry, atomic
|
|
> deploy primitive shared across all targets), see the
|
|
> [connector index](index.md).
|
|
|
|
## Overview
|
|
|
|
The NGINX connector (`internal/connector/target/nginx/`) is the
|
|
canonical implementation of the deploy-hardening I atomic + verify
|
|
+ rollback contract (Bundle I Phase 4). Every other file-based
|
|
connector models on this one.
|
|
|
|
## Vendor versions tested
|
|
|
|
- **NGINX 1.25 LTS** (current LTS branch)
|
|
- **NGINX 1.27 stable** (current stable branch)
|
|
|
|
Older versions (1.18 EOL'd 2021, 1.20 EOL'd 2022) are explicitly
|
|
out of scope per frozen decision 0.1.
|
|
|
|
## Deploy contract
|
|
|
|
Every cert deploy follows the Bundle I `deploy.Apply(ctx, plan)`
|
|
flow:
|
|
|
|
1. **Idempotency check** — SHA-256 over cert+chain+key bytes; skip
|
|
if all match destination.
|
|
2. **Pre-deploy backup** — copy existing files to
|
|
`<path>.certctl-bak.<unix-nanos>`.
|
|
3. **Atomic write** — temp-file + chown + atomic rename per
|
|
destination.
|
|
4. **PreCommit (validate)** — runs `nginx -t` per the operator's
|
|
`validate_command`. Failure aborts; no live cert touched.
|
|
5. **Atomic rename** — temp → final for every File entry.
|
|
6. **PostCommit (reload)** — runs `nginx -s reload` per the
|
|
operator's `reload_command`.
|
|
7. **Post-deploy TLS verify** — dials the configured endpoint;
|
|
pulls leaf cert SHA-256; compares against deployed bytes.
|
|
Mismatch triggers automatic rollback.
|
|
|
|
## Per-quirk operator guidance
|
|
|
|
### SSL session cache holds old cert
|
|
|
|
`TestVendorEdge_NGINX_SSLSessionCacheHoldsOldCert_E2E`
|
|
|
|
NGINX's `ssl_session_cache` (default `shared:SSL:10m`) keeps TLS
|
|
session IDs valid for `ssl_session_timeout` (default 5min). Clients
|
|
that resume via session ID see the OLD cert until their session
|
|
expires.
|
|
|
|
**Operator action:** this is documented behavior, not a bug.
|
|
Tune via `ssl_session_timeout 5m;` (default) or shorter if your
|
|
cert rotation cadence demands. Post-deploy verify in certctl will
|
|
return the NEW cert from a fresh handshake (no session resumption);
|
|
warm clients see the OLD cert until session-cache eviction.
|
|
|
|
### SNI multi-server-name binding
|
|
|
|
`TestVendorEdge_NGINX_SNIMultiServerName_DeployBindsCorrectVhost_E2E`
|
|
|
|
When NGINX has multiple `server { server_name a.example b.example; }`
|
|
blocks, the operator deploys with metadata pointing at the
|
|
specific vhost. Connector binds to that vhost only; other vhosts
|
|
remain unchanged.
|
|
|
|
### IPv6 dual-stack
|
|
|
|
`TestVendorEdge_NGINX_IPv6DualStackBindsBoth_E2E`
|
|
|
|
NGINX listening on `0.0.0.0:443` + `[::]:443` serves the new cert
|
|
on both stacks after a single deploy.
|
|
|
|
**Operator action:** if your post-deploy verify endpoint resolves
|
|
to IPv6 only on some networks but IPv4 only on others, configure
|
|
`PostDeployVerifyAttempts: 5` to cover both paths.
|
|
|
|
### Reload vs restart
|
|
|
|
`TestVendorEdge_NGINX_ReloadVsRestart_NoConnectionDrop_E2E`
|
|
|
|
`nginx -s reload` (graceful) preserves in-flight TLS connections
|
|
via worker handoff. `nginx -s stop && nginx` drops them.
|
|
|
|
**Operator action:** never use restart for cert rotation. The
|
|
connector's default `reload_command: nginx -s reload` is correct.
|
|
|
|
### Binary upgrade
|
|
|
|
`TestVendorEdge_NGINX_UpgradeBinaryHotReload_E2E`
|
|
|
|
`nginx -s upgrade` rolls out a new binary without dropping
|
|
connections. Not commonly used; documented for ops teams that do
|
|
rolling NGINX binary upgrades.
|
|
|
|
### Config syntax error → rollback
|
|
|
|
`TestVendorEdge_NGINX_ConfigSyntaxError_RollbackRestoresPreviousCert_E2E`
|
|
|
|
If `nginx -t` rejects the staged config, the deploy package's
|
|
PreCommit gate fires before the atomic rename — no live file is
|
|
touched. The cert directory is exactly as it was.
|
|
|
|
### Missing intermediate
|
|
|
|
`TestVendorEdge_NGINX_MissingIntermediate_DeployedButValidationCatchesAtPostVerify_E2E`
|
|
|
|
If the operator deploys a leaf-only cert (no intermediate), NGINX
|
|
will start serving it but downstream clients fail chain validation.
|
|
The connector's post-deploy TLS verify catches this via cert chain
|
|
walk; rollback fires automatically.
|
|
|
|
### Access log privacy
|
|
|
|
`TestVendorEdge_NGINX_AccessLogPrivacy_NoCertBytesLeakInLogs_E2E`
|
|
|
|
NGINX's default `access_log` and `error_log` formats do NOT include
|
|
SSL key bytes. The connector does not modify NGINX's logging config.
|
|
|
|
**Operator action:** if you've customized `log_format` to include
|
|
`$ssl_*` variables, audit the format string for sensitive fields.
|
|
|
|
### Per-version reload-command compat
|
|
|
|
`TestVendorEdge_NGINX_NGINX125_vs_127_ReloadCommandCompatible_E2E`
|
|
|
|
`nginx -s reload` semantics are identical between 1.25 LTS and
|
|
1.27 stable. No per-version branch needed in operator config.
|
|
|
|
### High-concurrency deploy under load
|
|
|
|
`TestVendorEdge_NGINX_HighConcurrencyDeployUnderLoad_E2E`
|
|
|
|
NGINX's worker handoff during reload is graceful; concurrent TLS
|
|
handshakes during a deploy succeed without 5xx errors.
|
|
|
|
## Troubleshooting matrix
|
|
|
|
| Symptom | Test name | Root cause | Operator action |
|
|
|---|---|---|---|
|
|
| Old cert returned 5min after deploy | `SSLSessionCacheHoldsOldCert_E2E` | session cache TTL | tune `ssl_session_timeout` |
|
|
| Wrong vhost serves new cert | `SNIMultiServerName_E2E` | misconfigured server_name selector | verify vhost metadata |
|
|
| Post-verify fails on IPv6 | `IPv6DualStackBindsBoth_E2E` | flaky DNS resolution | `PostDeployVerifyAttempts: 5` |
|
|
| Connection drops on cert change | n/a | using restart instead of reload | use `nginx -s reload` |
|
|
| Deploy aborts with `nginx -t` error | `ConfigSyntaxError_RollbackRestoresPreviousCert_E2E` | bad config (not deploy's fault) | fix config; redeploy |
|
|
| Chain-validation failure post-deploy | `MissingIntermediate_E2E` | leaf-only cert | include full chain in deploy |
|
|
|
|
## V3-Pro deferrals
|
|
|
|
- Pin NGINX `ssl_session_ticket_key` rotation interaction with cert
|
|
rotation (rare; documented but not tested).
|
|
- NGINX Plus `dyn_pem` API integration (commercial; not V2 scope).
|
|
|
|
## Related docs
|
|
|
|
- [Atomic deploy + post-verify + rollback](../deployment-model.md)
|
|
— the Bundle I primitive every connector consumes.
|
|
- [Vendor compatibility matrix](../vendor-matrix.md)
|
|
- [Connectors reference](index.md)
|