mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 16:01:30 +00:00
b9a63a2521
Bundle: ci-pipeline-cleanup, Phase 6 follow-up. Phase 5+6 commit removed the deploy-vendor-e2e-windows matrix from ci.yml; this commit closes the Phase 6 deliverables that aren't ci.yml-side: 1. NEW docs/connector-iis.md::Operator validation playbook (Windows host) — the procedure operators run pre-release to flip the IIS / WinCertStore vendor-matrix cells from 'operator-playbook' → '✓'. Mirrors the Bundle II frozen decision 0.14 third-criterion (operator manual smoke required). 2. docs/deployment-vendor-matrix.md — IIS + WinCertStore rows status updated from 'pending' → 'operator-playbook' with link to the new playbook section. 3. deploy/docker-compose.test.yml — windows-iis-test sidecar comment updated to reflect that CI no longer activates this profile; sidecar definition preserved for operator local use via 'docker compose --profile deploy-e2e-windows up -d windows-iis-test'. Operator workflow going forward: - Pre-release: run the playbook on a Windows host - Record validation date + Windows Server version in cowork/<bundle>/iis-validation-receipts.md - Update docs/deployment-vendor-matrix.md cells if applicable
710 lines
29 KiB
YAML
710 lines
29 KiB
YAML
# =============================================================================
|
|
# certctl Testing Environment — Docker Compose
|
|
# =============================================================================
|
|
#
|
|
# Spins up the full certctl platform with real CA backends for manual QA:
|
|
#
|
|
# 0. certctl-tls-init — one-shot init container; writes self-signed
|
|
# server.crt/.key/ca.crt into ./test/certs (bind
|
|
# mount, not a named volume — host-readable for
|
|
# the Go integration test binary)
|
|
# 1. PostgreSQL 16 — database (clean, no demo data)
|
|
# 2. certctl-server — control plane API + web dashboard on :8443 (HTTPS)
|
|
# 3. certctl-agent — polls for work, deploys certs to NGINX
|
|
# 4. step-ca — private CA (JWK provisioner, auto-bootstraps)
|
|
# 5. Pebble — ACME test server (simulates Let's Encrypt)
|
|
# 6. pebble-challtestsrv — DNS/HTTP challenge test server for Pebble
|
|
# 7. NGINX — TLS target server on :8080 (HTTP) / :8444 (HTTPS)
|
|
#
|
|
# Usage:
|
|
# cd deploy
|
|
# docker compose -f docker-compose.test.yml up --build
|
|
#
|
|
# Dashboard: https://localhost:8443 (self-signed — use --cacert test/certs/ca.crt)
|
|
# API key: test-key-2026
|
|
# NGINX: https://localhost:8444 (self-signed placeholder until cert deployed)
|
|
#
|
|
# Integration tests: `go test -tags integration ./deploy/test/...` picks up
|
|
# the CA bundle at ./test/certs/ca.crt automatically via CERTCTL_TEST_CA_BUNDLE.
|
|
#
|
|
# See docs/test-env.md for the full walkthrough.
|
|
# =============================================================================
|
|
|
|
services:
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# HTTPS-Everywhere Phase 6 — self-signed TLS bootstrap for the test harness.
|
|
# ---------------------------------------------------------------------------
|
|
# Mirrors the production `certctl-tls-init` (see docker-compose.yml §10-43)
|
|
# but writes into a *host bind mount* (./test/certs) instead of a named
|
|
# volume. The named-volume approach works fine inside Docker but hides the
|
|
# CA bundle from the Go integration test binary that runs on the host; the
|
|
# bind mount exposes /etc/certctl/tls/ca.crt at deploy/test/certs/ca.crt
|
|
# so `newTestClient()` can load it into an x509.CertPool and validate the
|
|
# self-signed server cert. Test-only divergence, explicitly documented.
|
|
#
|
|
# The generated cert has SAN=DNS:certctl-server,DNS:localhost,IP:127.0.0.1
|
|
# so both in-cluster traffic (agent → certctl-server:8443) and host traffic
|
|
# (go test → localhost:8443) validate cleanly. Destroy via
|
|
# `docker compose -f docker-compose.test.yml down -v` + `rm -rf test/certs`
|
|
# to force regeneration. Keys written 0600, certs 0644, owned 1000:1000
|
|
# (the UID the server binary runs as inside its container per Dockerfile:64).
|
|
certctl-tls-init:
|
|
image: alpine/openssl:latest
|
|
container_name: certctl-test-tls-init
|
|
restart: "no"
|
|
entrypoint: /bin/sh
|
|
command:
|
|
- -c
|
|
- |
|
|
set -eu
|
|
CERT=/etc/certctl/tls/server.crt
|
|
KEY=/etc/certctl/tls/server.key
|
|
CA=/etc/certctl/tls/ca.crt
|
|
if [ -f "$$CERT" ] && [ -f "$$KEY" ] && [ -f "$$CA" ]; then
|
|
echo "TLS cert already present at $$CERT — skipping generation"
|
|
else
|
|
mkdir -p /etc/certctl/tls
|
|
openssl req -x509 -newkey ec \
|
|
-pkeyopt ec_paramgen_curve:P-256 \
|
|
-nodes \
|
|
-keyout "$$KEY" \
|
|
-out "$$CERT" \
|
|
-days 3650 \
|
|
-subj "/CN=certctl-server" \
|
|
-addext "subjectAltName=DNS:certctl-server,DNS:localhost,IP:127.0.0.1,IP:::1"
|
|
cp "$$CERT" "$$CA"
|
|
echo "Generated self-signed TLS cert for certctl-test-server (ECDSA-P256/SHA-256, 3650d, CN=certctl-server)"
|
|
fi
|
|
# The test server container runs as root (see `user: "0:0"` below)
|
|
# because setup-trust.sh needs to update the system trust store, so
|
|
# the perms here are really about host-side readability — 0644 on
|
|
# the CA/cert lets `go test` on the host read the bundle without a
|
|
# chown dance.
|
|
chown 1000:1000 "$$CERT" "$$KEY" "$$CA" || true
|
|
chmod 0644 "$$CERT" "$$CA"
|
|
chmod 0600 "$$KEY"
|
|
volumes:
|
|
- ./test/certs:/etc/certctl/tls
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.9
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Database
|
|
# ---------------------------------------------------------------------------
|
|
#
|
|
# U-3 (P1, cat-u-seed_initdb_schema_drift, GitHub #10): the test stack used
|
|
# to mount a hand-curated subset of migrations + seed.sql + a never-checked-in
|
|
# seed_test.sql into postgres `/docker-entrypoint-initdb.d/`. Same hazard as
|
|
# the production compose — initdb crashed any time a new migration shipped
|
|
# that the seed depended on without the mount list being updated. Post-U-3
|
|
# the schema is built EXCLUSIVELY by the server at startup via
|
|
# internal/repository/postgres.RunMigrations + RunSeed. Postgres comes up
|
|
# empty and the server lands the full ladder + baseline seed in one shot.
|
|
# `start_period: 30s` matches the production compose and shields slow CI
|
|
# runners from healthcheck flap during initdb.
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
container_name: certctl-test-postgres
|
|
environment:
|
|
POSTGRES_DB: certctl
|
|
POSTGRES_USER: certctl
|
|
POSTGRES_PASSWORD: testpass
|
|
volumes:
|
|
- test_postgres_data:/var/lib/postgresql/data
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.2
|
|
ports:
|
|
- "5432:5432"
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U certctl -d certctl"]
|
|
interval: 5s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 30s
|
|
restart: unless-stopped
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Pebble — ACME test server (simulates Let's Encrypt)
|
|
# ---------------------------------------------------------------------------
|
|
# Pebble is the official ACME test server from Let's Encrypt (RFC 8555).
|
|
# It validates challenges via the companion challtestsrv.
|
|
# Root CA cert available at https://pebble:15000/roots/0 (management API).
|
|
pebble-challtestsrv:
|
|
image: ghcr.io/letsencrypt/pebble-challtestsrv:latest
|
|
container_name: certctl-test-challtestsrv
|
|
# ENTRYPOINT is /app (the binary). command: provides only the FLAGS.
|
|
# Matches the official Pebble docker-compose format.
|
|
# -doh "" disables DoH (default :8443 would conflict with certctl server).
|
|
# defaultIPv4 must point to the certctl-server (10.30.50.6) because that's where
|
|
# the ACME HTTP-01 challenge server runs (port 80 inside the container).
|
|
# Pebble resolves domains via challtestsrv, then connects to this IP to validate.
|
|
command: -defaultIPv4 10.30.50.6 -defaultIPv6 "" -doh ""
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.3
|
|
restart: unless-stopped
|
|
|
|
pebble:
|
|
image: ghcr.io/letsencrypt/pebble:latest
|
|
container_name: certctl-test-pebble
|
|
depends_on:
|
|
- pebble-challtestsrv
|
|
environment:
|
|
PEBBLE_VA_NOSLEEP: 1
|
|
PEBBLE_VA_ALWAYS_VALID: 0
|
|
# ENTRYPOINT is /app (the binary). command: provides only the FLAGS.
|
|
command:
|
|
- -config
|
|
- /test/config/pebble-config.json
|
|
- -dnsserver
|
|
- "10.30.50.3:8053"
|
|
- -strict
|
|
volumes:
|
|
- ./test/pebble-config.json:/test/config/pebble-config.json:ro
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.4
|
|
restart: unless-stopped
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# step-ca — Private CA (Smallstep)
|
|
# ---------------------------------------------------------------------------
|
|
# Auto-bootstraps on first run: generates root CA + JWK provisioner "admin".
|
|
# Root cert: /home/step/certs/root_ca.crt (inside stepca_data volume)
|
|
# Provisioner key: /home/step/secrets/provisioner_key (encrypted JWK)
|
|
step-ca:
|
|
image: smallstep/step-ca:latest
|
|
container_name: certctl-test-stepca
|
|
environment:
|
|
DOCKER_STEPCA_INIT_NAME: "certctl-test-ca"
|
|
DOCKER_STEPCA_INIT_DNS_NAMES: "step-ca,localhost"
|
|
DOCKER_STEPCA_INIT_PROVISIONER_NAME: "admin"
|
|
DOCKER_STEPCA_INIT_PASSWORD: "password123"
|
|
DOCKER_STEPCA_INIT_ADDRESS: ":9000"
|
|
volumes:
|
|
- stepca_data:/home/step
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.5
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-fk", "https://localhost:9000/health"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
start_period: 15s
|
|
retries: 10
|
|
restart: unless-stopped
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# certctl Server (Control Plane)
|
|
# ---------------------------------------------------------------------------
|
|
# Connects to PostgreSQL, Pebble (ACME), step-ca, and Local CA.
|
|
#
|
|
# TLS trust problem: Pebble and step-ca use self-signed root CAs that
|
|
# aren't in Alpine's trust store. The ACME and step-ca connectors use
|
|
# Go's default http.Client (no InsecureSkipVerify), so they need the
|
|
# CA certs in the system trust store.
|
|
#
|
|
# Solution: setup-trust.sh runs as root, fetches Pebble CA from its
|
|
# management API, copies step-ca root cert from the shared volume,
|
|
# runs update-ca-certificates, then execs the server binary.
|
|
certctl-server:
|
|
build:
|
|
context: ..
|
|
dockerfile: Dockerfile
|
|
# Proxy propagation (M-4, Issue #9) — forwards host shell's proxy env
|
|
# vars into the Docker build so the Node frontend stage and Go module
|
|
# download can reach the public registries behind corporate proxies.
|
|
# Defaults to empty; omit the variables from the host environment for
|
|
# un-proxied builds and the behaviour is byte-identical to the pre-fix
|
|
# tree.
|
|
args:
|
|
HTTP_PROXY: ${HTTP_PROXY:-}
|
|
HTTPS_PROXY: ${HTTPS_PROXY:-}
|
|
NO_PROXY: ${NO_PROXY:-}
|
|
container_name: certctl-test-server
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
pebble:
|
|
condition: service_started
|
|
step-ca:
|
|
condition: service_healthy
|
|
# HTTPS-Everywhere Phase 6: block server boot until the init container
|
|
# has written server.crt / server.key / ca.crt into ./test/certs. The
|
|
# init container runs once and exits 0; service_completed_successfully
|
|
# makes that a gating dependency rather than a liveness one.
|
|
certctl-tls-init:
|
|
condition: service_completed_successfully
|
|
# Run as root so update-ca-certificates can write to /etc/ssl/certs.
|
|
# Container isolation provides the security boundary.
|
|
user: "0:0"
|
|
entrypoint: ["/bin/sh", "/app/setup-trust.sh"]
|
|
environment:
|
|
# Database
|
|
CERTCTL_DATABASE_URL: postgres://certctl:testpass@postgres:5432/certctl?sslmode=disable
|
|
|
|
# Server
|
|
CERTCTL_SERVER_HOST: 0.0.0.0
|
|
CERTCTL_SERVER_PORT: 8443
|
|
# HTTPS-Everywhere Phase 6: point the server at the init-container-generated
|
|
# cert/key pair (bind-mounted from ./test/certs). Same paths as production
|
|
# compose so the server binary code path is identical; only the host-side
|
|
# storage differs (bind mount vs named volume — see §certctl-tls-init block).
|
|
CERTCTL_SERVER_TLS_CERT_PATH: /etc/certctl/tls/server.crt
|
|
CERTCTL_SERVER_TLS_KEY_PATH: /etc/certctl/tls/server.key
|
|
CERTCTL_LOG_LEVEL: debug
|
|
|
|
# Auth — API key required (production-like)
|
|
CERTCTL_AUTH_TYPE: api-key
|
|
CERTCTL_AUTH_SECRET: test-key-2026
|
|
|
|
# Key generation — agent-side (production-like)
|
|
CERTCTL_KEYGEN_MODE: agent
|
|
|
|
# Local CA issuer (iss-local) — self-signed mode (no CA cert/key paths)
|
|
# This is the simplest issuer, always available.
|
|
|
|
# ACME issuer (iss-acme-staging) — pointed at Pebble
|
|
CERTCTL_ACME_DIRECTORY_URL: https://pebble:14000/dir
|
|
CERTCTL_ACME_EMAIL: test@certctl.dev
|
|
CERTCTL_ACME_CHALLENGE_TYPE: http-01
|
|
CERTCTL_ACME_INSECURE: "true"
|
|
|
|
# step-ca issuer (iss-stepca)
|
|
CERTCTL_STEPCA_URL: https://step-ca:9000
|
|
CERTCTL_STEPCA_ROOT_CERT: /stepca-data/certs/root_ca.crt
|
|
CERTCTL_STEPCA_PROVISIONER: admin
|
|
CERTCTL_STEPCA_PASSWORD: password123
|
|
CERTCTL_STEPCA_KEY_PATH: /stepca-data/secrets/provisioner_key
|
|
|
|
# EST server (RFC 7030) — uses Local CA by default
|
|
CERTCTL_EST_ENABLED: "true"
|
|
CERTCTL_EST_ISSUER_ID: iss-local
|
|
|
|
# SCEP RFC 8894 + Intune master prompt §10.2 + §13 acceptance
|
|
# (deploy/test/scep_intune_e2e_test.go integration variant).
|
|
# Closed in the 2026-04-29 audit-closure bundle (Phase I).
|
|
#
|
|
# Publishes /scep/e2eintune?operation=... with the Intune
|
|
# dispatcher enabled. The deterministic Connector signing cert
|
|
# is bind-mounted at the path below; the matching private key
|
|
# lives ONLY on the test side (see
|
|
# deploy/test/scep_intune_e2e_test.go::generateE2EIntuneTrustAnchor).
|
|
CERTCTL_SCEP_ENABLED: "true"
|
|
CERTCTL_SCEP_PROFILES: "e2eintune"
|
|
CERTCTL_SCEP_PROFILE_E2EINTUNE_ISSUER_ID: iss-local
|
|
CERTCTL_SCEP_PROFILE_E2EINTUNE_RA_CERT_PATH: /etc/certctl/scep/ra.crt
|
|
CERTCTL_SCEP_PROFILE_E2EINTUNE_RA_KEY_PATH: /etc/certctl/scep/ra.key
|
|
CERTCTL_SCEP_PROFILE_E2EINTUNE_INTUNE_ENABLED: "true"
|
|
CERTCTL_SCEP_PROFILE_E2EINTUNE_INTUNE_CONNECTOR_CERT_PATH: /etc/certctl/scep/intune_trust_anchor.pem
|
|
CERTCTL_SCEP_PROFILE_E2EINTUNE_INTUNE_AUDIENCE: https://localhost:8443/scep/e2eintune
|
|
CERTCTL_SCEP_PROFILE_E2EINTUNE_INTUNE_CHALLENGE_VALIDITY: 60m
|
|
CERTCTL_SCEP_PROFILE_E2EINTUNE_INTUNE_CLOCK_SKEW_TOLERANCE: 60s
|
|
CERTCTL_SCEP_PROFILE_E2EINTUNE_INTUNE_PER_DEVICE_RATE_LIMIT_24H: 3
|
|
|
|
# Dynamic issuer/target config encryption (M34/M35)
|
|
CERTCTL_CONFIG_ENCRYPTION_KEY: test-encryption-key-32chars!!
|
|
|
|
# Network scanning
|
|
CERTCTL_NETWORK_SCAN_ENABLED: "true"
|
|
|
|
# Post-deployment TLS verification
|
|
CERTCTL_VERIFY_DEPLOYMENT: "true"
|
|
CERTCTL_VERIFY_TIMEOUT: "10s"
|
|
CERTCTL_VERIFY_DELAY: "3s"
|
|
ports:
|
|
- "8443:8443"
|
|
volumes:
|
|
- ./test/setup-trust.sh:/app/setup-trust.sh:ro
|
|
# step-ca data volume (root cert at /certs/root_ca.crt, key at /secrets/provisioner_key)
|
|
- stepca_data:/stepca-data:ro
|
|
# HTTPS-Everywhere Phase 6: read-only bind mount of the init-generated
|
|
# TLS material. The init container writes here; server reads here; the
|
|
# agent mounts the same host path at the same container path (see below)
|
|
# so /etc/certctl/tls/ca.crt resolves to the *same* bytes on both sides.
|
|
- ./test/certs:/etc/certctl/tls:ro
|
|
# SCEP RFC 8894 + Intune master prompt §10.2 + §13 acceptance: the
|
|
# e2eintune profile's RA cert/key + Intune Connector trust anchor
|
|
# PEM. The PEM is the deterministic public cert matching the test-
|
|
# side private key in deploy/test/scep_intune_e2e_test.go (re-run
|
|
# `go test -tags integration -run='^TestRegenerateE2EIntuneFixture$'
|
|
# -update-fixture ./deploy/test/...` to regenerate after a seed
|
|
# change). RA cert/key live alongside; tls-init container generates
|
|
# them at boot.
|
|
- ./test/fixtures:/etc/certctl/scep:ro
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.6
|
|
healthcheck:
|
|
# HTTPS-Everywhere Phase 6: healthcheck now speaks TLS with --cacert to
|
|
# verify the self-signed server cert against the init-generated bundle.
|
|
# /health requires auth when CERTCTL_AUTH_TYPE=api-key, so include the
|
|
# Bearer token. curl exits non-zero on both TLS handshake failure and
|
|
# non-2xx status — either failure keeps depends_on: {condition:
|
|
# service_healthy} from unblocking the agent, which is what we want.
|
|
test: ["CMD", "curl", "--cacert", "/etc/certctl/tls/ca.crt", "-f", "-H", "Authorization: Bearer test-key-2026", "https://localhost:8443/health"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
start_period: 30s
|
|
retries: 10
|
|
restart: unless-stopped
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# NGINX — TLS Target Server
|
|
# ---------------------------------------------------------------------------
|
|
# The agent deploys certificates here via the shared nginx_certs volume.
|
|
# nginx-entrypoint.sh generates a self-signed placeholder cert so NGINX
|
|
# can boot before the agent deploys a real cert.
|
|
#
|
|
# Ports: 8080 (HTTP) / 8444 (HTTPS) — offset to avoid conflict with server.
|
|
nginx:
|
|
image: nginx:alpine
|
|
container_name: certctl-test-nginx
|
|
entrypoint: ["/bin/sh", "/entrypoint.sh"]
|
|
volumes:
|
|
- ./test/nginx.conf:/etc/nginx/nginx.conf:ro
|
|
- ./test/nginx-entrypoint.sh:/entrypoint.sh:ro
|
|
- nginx_certs:/etc/nginx/certs
|
|
ports:
|
|
- "8080:80"
|
|
- "8444:443"
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.7
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "curl -fk https://localhost/health || exit 1"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
start_period: 15s
|
|
retries: 5
|
|
restart: unless-stopped
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# certctl Agent
|
|
# ---------------------------------------------------------------------------
|
|
# Polls the server for work, generates ECDSA P-256 keys locally,
|
|
# deploys certs to NGINX via the shared volume, and discovers existing
|
|
# certs in the NGINX cert directory.
|
|
certctl-agent:
|
|
build:
|
|
context: ..
|
|
dockerfile: Dockerfile.agent
|
|
# Proxy propagation (M-4, Issue #9) — forwards host shell's proxy env
|
|
# vars into the Docker build so the Go module download stage can reach
|
|
# the public Go module proxy behind corporate proxies. Defaults to
|
|
# empty; omit the variables from the host environment for un-proxied
|
|
# builds and the behaviour is byte-identical to the pre-fix tree.
|
|
args:
|
|
HTTP_PROXY: ${HTTP_PROXY:-}
|
|
HTTPS_PROXY: ${HTTPS_PROXY:-}
|
|
NO_PROXY: ${NO_PROXY:-}
|
|
container_name: certctl-test-agent
|
|
depends_on:
|
|
certctl-server:
|
|
condition: service_healthy
|
|
environment:
|
|
# HTTPS-Everywhere Phase 6: agent dials the server over TLS and validates
|
|
# the self-signed cert against the CA bundle pinned by
|
|
# CERTCTL_SERVER_CA_BUNDLE_PATH. Same env vars + container paths as
|
|
# production compose so the agent binary code path (loadCABundle →
|
|
# x509.CertPool → *tls.Config{RootCAs, MinVersion: TLS13}) is identical.
|
|
CERTCTL_SERVER_URL: https://certctl-server:8443
|
|
CERTCTL_SERVER_CA_BUNDLE_PATH: /etc/certctl/tls/ca.crt
|
|
CERTCTL_API_KEY: test-key-2026
|
|
CERTCTL_AGENT_NAME: test-agent-01
|
|
CERTCTL_AGENT_ID: agent-test-01
|
|
CERTCTL_KEYGEN_MODE: agent
|
|
CERTCTL_LOG_LEVEL: debug
|
|
CERTCTL_DISCOVERY_DIRS: /nginx-certs
|
|
volumes:
|
|
- agent_keys:/var/lib/certctl/keys
|
|
- nginx_certs:/nginx-certs
|
|
# HTTPS-Everywhere Phase 6: same bind mount as the server, same path,
|
|
# so /etc/certctl/tls/ca.crt resolves to the identical bytes. This is
|
|
# the only way the CN=certctl-server cert validates on the agent side.
|
|
- ./test/certs:/etc/certctl/tls:ro
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.8
|
|
restart: unless-stopped
|
|
|
|
# EST RFC 7030 hardening master bundle Phase 10.1 — libest sidecar.
|
|
#
|
|
# Cisco's libest reference RFC 7030 client. The integration test
|
|
# (deploy/test/est_e2e_test.go, build tag `integration`) docker-exec's
|
|
# into this container to drive estclient against the live certctl
|
|
# server. The container stays alive via `sleep infinity` so the test
|
|
# can do many serial exec calls without paying container-startup cost.
|
|
#
|
|
# Profile-gated (`profiles: [est-e2e]`) so the routine `docker compose
|
|
# up` for non-EST integration runs doesn't pay the libest build cost.
|
|
# Operator opts in via `docker compose --profile est-e2e up`. CI's
|
|
# est-e2e job runs:
|
|
# docker compose --profile est-e2e build libest-client
|
|
# docker compose --profile est-e2e up -d
|
|
# INTEGRATION=1 go test -tags integration -run 'TestEST_LibESTClient' ./deploy/test/...
|
|
libest-client:
|
|
build:
|
|
context: ..
|
|
dockerfile: deploy/test/libest/Dockerfile
|
|
args:
|
|
HTTP_PROXY: ${HTTP_PROXY:-}
|
|
HTTPS_PROXY: ${HTTPS_PROXY:-}
|
|
NO_PROXY: ${NO_PROXY:-}
|
|
container_name: certctl-test-libest
|
|
depends_on:
|
|
certctl-server:
|
|
condition: service_healthy
|
|
volumes:
|
|
# /config/est is the libest working directory — the integration
|
|
# test writes CSRs / reads issued certs through this mount so the
|
|
# test-side Go code can inspect estclient's outputs.
|
|
- ./test/est:/config/est:rw
|
|
# certctl's CA bundle for TLS pinning. estclient uses this to
|
|
# verify the certctl-server cert (the same self-signed bundle
|
|
# the certctl-agent verifies against).
|
|
- ./test/certs:/config/certs:ro
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.9
|
|
restart: unless-stopped
|
|
profiles: [est-e2e]
|
|
|
|
# =============================================================================
|
|
# Deploy-Hardening II Phase 1 — per-vendor sidecar matrix
|
|
# =============================================================================
|
|
# Each sidecar is a real-software target the deploy-vendor-e2e tests
|
|
# (deploy/test/<vendor>_vendor_e2e_test.go, build tag `integration`)
|
|
# exercise the connector's atomic + verify + rollback contract against.
|
|
# All gated behind `profiles: [deploy-e2e]` so routine integration runs
|
|
# don't pay the per-vendor pull cost.
|
|
#
|
|
# Image digests pinned per H-001 guard. Re-pin quarterly per
|
|
# docs/deployment-vendor-matrix.md.
|
|
|
|
apache-test:
|
|
image: httpd:2.4-alpine@sha256:f9061a65c6e8f50d5636e10806da3d5a238877c11d6bc0149dc5131be0a1a19f
|
|
container_name: certctl-test-apache
|
|
ports:
|
|
- "20443:443"
|
|
volumes:
|
|
- ./test/apache/httpd-ssl.conf:/usr/local/apache2/conf/extra/httpd-ssl.conf:ro
|
|
- ./test/apache/init-cert.sh:/docker-entrypoint-init.sh:ro
|
|
- apache_certs:/usr/local/apache2/conf/certs
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.20
|
|
profiles: [deploy-e2e]
|
|
|
|
haproxy-test:
|
|
image: haproxy:3.0-alpine@sha256:5b645ad4f3294cf5bc50ab8b201fdeb73732eca2928185df335735c698e8c3e2
|
|
container_name: certctl-test-haproxy
|
|
ports:
|
|
- "20444:443"
|
|
volumes:
|
|
- ./test/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
|
|
- haproxy_certs:/etc/haproxy/certs
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.21
|
|
profiles: [deploy-e2e]
|
|
|
|
traefik-test:
|
|
image: traefik:v3.1@sha256:8516638b18e67e999d293e4ff0e5baf7807674cd4bdd3d36d448497bcbf0a174
|
|
container_name: certctl-test-traefik
|
|
command:
|
|
- --providers.file.directory=/etc/traefik/dynamic
|
|
- --providers.file.watch=true
|
|
- --entrypoints.websecure.address=:443
|
|
- --log.level=ERROR
|
|
ports:
|
|
- "20445:443"
|
|
volumes:
|
|
- ./test/traefik/traefik-dynamic.yml:/etc/traefik/dynamic/traefik-dynamic.yml:ro
|
|
- traefik_certs:/etc/traefik/certs
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.22
|
|
profiles: [deploy-e2e]
|
|
|
|
caddy-test:
|
|
image: caddy:2.8-alpine@sha256:b95ed06fbc6d74d24a40902090c8cc6086ce7d08ba60a3a7e8e62bf164a9d7bb
|
|
container_name: certctl-test-caddy
|
|
command: caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
|
|
ports:
|
|
- "20446:443"
|
|
- "22019:2019" # admin API for ValidateOnly probe
|
|
volumes:
|
|
- ./test/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
|
|
- caddy_certs:/etc/caddy/certs
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.23
|
|
profiles: [deploy-e2e]
|
|
|
|
envoy-test:
|
|
image: envoyproxy/envoy:v1.32-latest@sha256:6ed0d4f28b8122df896062c425b34f18b8287e8c71c6badb3b84ca2e2f47c519
|
|
container_name: certctl-test-envoy
|
|
command: envoy -c /etc/envoy/envoy.yaml --log-level error
|
|
ports:
|
|
- "20447:443"
|
|
volumes:
|
|
- ./test/envoy/envoy.yaml:/etc/envoy/envoy.yaml:ro
|
|
- envoy_certs:/etc/envoy/certs
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.24
|
|
profiles: [deploy-e2e]
|
|
|
|
postfix-test:
|
|
image: boky/postfix:latest@sha256:cd7e192900bfc49a67291a572b5f645f9e7d1b8d7f2b79b0364b4b4176964e21
|
|
container_name: certctl-test-postfix
|
|
environment:
|
|
ALLOWED_SENDER_DOMAINS: "test.local"
|
|
ports:
|
|
- "20025:25"
|
|
- "20465:465"
|
|
volumes:
|
|
- postfix_certs:/etc/postfix/certs
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.25
|
|
profiles: [deploy-e2e]
|
|
|
|
dovecot-test:
|
|
image: dovecot/dovecot:latest@sha256:4046993478e8c8bcb841fdbff2d8de1b233484cc0196b3723f6c588e7eaf7301
|
|
container_name: certctl-test-dovecot
|
|
ports:
|
|
- "20993:993"
|
|
- "20995:995"
|
|
volumes:
|
|
- ./test/dovecot/dovecot.conf:/etc/dovecot/dovecot.conf:ro
|
|
- dovecot_certs:/etc/dovecot/certs
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.26
|
|
profiles: [deploy-e2e]
|
|
|
|
openssh-test:
|
|
image: lscr.io/linuxserver/openssh-server:latest@sha256:742f577d4100f5ad3b38f270d722931bbe98b997444c13b1a2a838df12a9971e
|
|
container_name: certctl-test-openssh
|
|
environment:
|
|
USER_NAME: "certctl"
|
|
PASSWORD_ACCESS: "true"
|
|
USER_PASSWORD: "test-only-do-not-use-in-prod"
|
|
SUDO_ACCESS: "true"
|
|
ports:
|
|
- "20022:2222"
|
|
volumes:
|
|
- openssh_certs:/config/certs
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.27
|
|
profiles: [deploy-e2e]
|
|
|
|
# f5-mock-icontrol: in-tree Go server implementing the iControl REST
|
|
# surface this bundle exercises (Authenticate, UploadFile, transactions,
|
|
# SSL profile CRUD). Built from deploy/test/f5-mock-icontrol/Dockerfile;
|
|
# the operator-supplied real F5 vagrant box is documented in
|
|
# docs/connector-f5.md as the validation tier above the mock.
|
|
f5-mock-icontrol:
|
|
build:
|
|
context: ..
|
|
dockerfile: deploy/test/f5-mock-icontrol/Dockerfile
|
|
container_name: certctl-test-f5-mock
|
|
ports:
|
|
- "20443:443"
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.28
|
|
profiles: [deploy-e2e]
|
|
|
|
# k8s-kind-test: a kind (Kubernetes-in-Docker) cluster used by the
|
|
# k8ssecret connector e2e tests. Per frozen decision 0.5, each K8s
|
|
# version test spins up a fresh kind cluster of the matching version.
|
|
# Tests are slow (~30-60s startup); marked t.Parallel() where independent.
|
|
# The kind binary lives in the test image; the Docker socket is mounted
|
|
# so kind can manage child containers.
|
|
k8s-kind-test:
|
|
image: kindest/node:v1.31.0@sha256:7fbc5644a803286a69ff9c5695f03bb01b512896835e15df7df17f756f7245ac
|
|
container_name: certctl-test-kind
|
|
privileged: true
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.29
|
|
profiles: [deploy-e2e]
|
|
|
|
# windows-iis-test: Windows containers run only on Windows hosts.
|
|
# CI no longer runs an IIS matrix (per ci-pipeline-cleanup bundle
|
|
# Phase 6 / frozen decision 0.5 — revises Bundle II decision 0.4).
|
|
# Two reasons the Windows matrix was deleted: (a) it couldn't
|
|
# physically work on `windows-latest` GitHub runners (Docker not
|
|
# started in Windows-containers mode by default; `bridge` network
|
|
# driver doesn't exist on Windows Docker); (b) all IIS + WinCertStore
|
|
# vendor-edge tests are t.Log placeholder stubs that exercise no
|
|
# IIS-specific behavior.
|
|
#
|
|
# Operators validate IIS + WinCertStore manually on a Windows host
|
|
# per the playbook at docs/connector-iis.md::Operator validation playbook.
|
|
#
|
|
# The sidecar definition stays here under profiles: [deploy-e2e-windows]
|
|
# so a Windows operator can opt in via:
|
|
# docker compose --profile deploy-e2e-windows up -d windows-iis-test
|
|
# Linux CI never activates this profile.
|
|
windows-iis-test:
|
|
image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022@sha256:8d0b0e651ad514e3fb05978db66f38036118812e1b9314a48f10419cad8a3462
|
|
container_name: certctl-test-iis
|
|
ports:
|
|
- "20448:443"
|
|
networks:
|
|
certctl-test:
|
|
ipv4_address: 10.30.50.30
|
|
profiles: [deploy-e2e-windows]
|
|
|
|
# =============================================================================
|
|
# Network
|
|
# =============================================================================
|
|
# Static IPs are required because:
|
|
# - Pebble needs to know the challtestsrv DNS server address (10.30.50.3)
|
|
# - challtestsrv resolves all domains to certctl-server (10.30.50.6) for HTTP-01 challenges
|
|
# - Avoids DNS race conditions during startup
|
|
networks:
|
|
certctl-test:
|
|
driver: bridge
|
|
ipam:
|
|
config:
|
|
- subnet: 10.30.50.0/24
|
|
|
|
# =============================================================================
|
|
# Volumes
|
|
# =============================================================================
|
|
volumes:
|
|
test_postgres_data:
|
|
driver: local
|
|
stepca_data:
|
|
driver: local
|
|
agent_keys:
|
|
driver: local
|
|
nginx_certs:
|
|
driver: local
|
|
# Deploy-Hardening II Phase 1 — per-vendor sidecar cert volumes.
|
|
apache_certs:
|
|
driver: local
|
|
haproxy_certs:
|
|
driver: local
|
|
traefik_certs:
|
|
driver: local
|
|
caddy_certs:
|
|
driver: local
|
|
envoy_certs:
|
|
driver: local
|
|
postfix_certs:
|
|
driver: local
|
|
dovecot_certs:
|
|
driver: local
|
|
openssh_certs:
|
|
driver: local
|