mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 16:21:30 +00:00
f92c997a50
Three related ACME ecosystem changes shipped as a single milestone: 1. ACME Certificate Profile Selection: Custom JWS-signed newOrder POST with `profile` field (e.g., `tlsserver`, `shortlived` for 6-day certs) bypassing acme.Client.AuthorizeOrder() since golang.org/x/crypto lacks profile support. ES256 JWS signing with kid mode, nonce management, directory discovery. Empty profile delegates to standard library path (zero behavior change). Configurable via CERTCTL_ACME_PROFILE env var. GUI: profile dropdown on ACME issuer config. 2. ARI RFC 9702 → 9773 Renumber: All 25+ references updated across Go source, docs, README, and examples. Zero remaining occurrences of RFC 9702. 3. 45-Day / Short-Lived Certificate Positioning: 5 domain tests validating renewal thresholds against SC-081v3 validity reduction timeline (200→100→47 days) and Let's Encrypt 45-day/6-day profiles. ARI (RFC 9773) is the expected renewal path for 6-day shortlived certs. New tests: 13 profile + 5 domain threshold + 1 frontend = 19 new tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
172 lines
5.6 KiB
YAML
172 lines
5.6 KiB
YAML
version: '3.8'
|
|
|
|
# ACME Wildcard DNS-01 Example
|
|
#
|
|
# This example demonstrates how to use certctl with Let's Encrypt to issue wildcard
|
|
# certificates (*.example.com) using DNS-01 challenge validation.
|
|
#
|
|
# DNS-01 is ideal for:
|
|
# - Wildcard certificates (*.domain.com)
|
|
# - Services behind NAT or non-public networks
|
|
# - Batch certificate issuance (multiple domains in parallel)
|
|
#
|
|
# It works by:
|
|
# 1. certctl creates a renewal job for a wildcard certificate
|
|
# 2. Let's Encrypt sends an ACME challenge: "create _acme-challenge TXT record with value X"
|
|
# 3. certctl runs the dns-present.sh script to create the TXT record via your DNS provider API
|
|
# 4. Let's Encrypt verifies the TXT record exists
|
|
# 5. Certificate is issued
|
|
# 6. certctl runs dns-cleanup.sh to remove the TXT record
|
|
#
|
|
# This compose file also demonstrates:
|
|
# - ACME issuer with DNS-01 challenge type
|
|
# - Pluggable DNS provider scripts (Cloudflare example included; adapt for Route53, Azure DNS, etc.)
|
|
# - Wildcard and multi-SAN certificate support
|
|
# - Agent-side key generation (production-ready)
|
|
|
|
services:
|
|
# PostgreSQL database for certctl metadata
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
container_name: certctl-postgres-dns01
|
|
environment:
|
|
POSTGRES_DB: certctl
|
|
POSTGRES_USER: certctl
|
|
POSTGRES_PASSWORD: ${DB_PASSWORD:-certctl-dev-password}
|
|
volumes:
|
|
- postgres_data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: ['CMD-SHELL', 'pg_isready -U certctl -d certctl']
|
|
interval: 5s
|
|
timeout: 5s
|
|
retries: 5
|
|
networks:
|
|
- certctl-network
|
|
restart: unless-stopped
|
|
|
|
# certctl server (control plane + ACME orchestration)
|
|
certctl-server:
|
|
image: ghcr.io/shankar0123/certctl-server:latest
|
|
container_name: certctl-server-dns01
|
|
environment:
|
|
# Database
|
|
CERTCTL_DATABASE_URL: postgres://certctl:${DB_PASSWORD:-certctl-dev-password}@postgres:5432/certctl?sslmode=disable
|
|
|
|
# Server settings
|
|
CERTCTL_SERVER_PORT: 8443
|
|
CERTCTL_SERVER_HOST: 0.0.0.0
|
|
|
|
# Auth (disabled for demo; production should use API keys with CERTCTL_AUTH_TYPE=api-key)
|
|
CERTCTL_AUTH_TYPE: none
|
|
|
|
# CORS (allow agent communication)
|
|
CERTCTL_CORS_ORIGINS: '*'
|
|
|
|
# Key generation mode (agent-side: keys never leave agents; production standard)
|
|
CERTCTL_KEYGEN_MODE: agent
|
|
|
|
# ===== ACME Issuer Configuration (DNS-01 Wildcard) =====
|
|
# Let's Encrypt production directory (ACME v2)
|
|
CERTCTL_ACME_DIRECTORY_URL: https://acme-v02.api.letsencrypt.org/directory
|
|
|
|
# Email for certificate expiration notices and account recovery
|
|
CERTCTL_ACME_EMAIL: ${ACME_EMAIL:-admin@example.com}
|
|
|
|
# Challenge type: dns-01 (not http-01, which doesn't support wildcards)
|
|
CERTCTL_ACME_CHALLENGE_TYPE: dns-01
|
|
|
|
# DNS present script: creates _acme-challenge TXT record
|
|
# The script is mounted from ./dns-hooks/cloudflare-present.sh
|
|
# Arguments: $1 = domain (e.g., "example.com"), $2 = validation token
|
|
CERTCTL_ACME_DNS_PRESENT_SCRIPT: /etc/certctl/dns-hooks/cloudflare-present.sh
|
|
|
|
# DNS cleanup script: removes _acme-challenge TXT record
|
|
# Arguments: $1 = domain, $2 = validation token
|
|
CERTCTL_ACME_DNS_CLEANUP_SCRIPT: /etc/certctl/dns-hooks/cloudflare-cleanup.sh
|
|
|
|
# Optional: DNS propagation wait time (seconds) before proceeding to next challenge
|
|
# Default is 30s; increase if your DNS propagates slowly
|
|
# Set via CERTCTL_ACME_DNS_PROPAGATION_WAIT in code, or rely on default
|
|
|
|
# Optional: Let's Encrypt Renewal Information (RFC 9773) for CA-directed renewal timing
|
|
# CERTCTL_ACME_ARI_ENABLED: "true"
|
|
|
|
# Local CA as fallback for internal services (optional)
|
|
CERTCTL_CA_CERT_PATH: /etc/certctl/ca.crt
|
|
CERTCTL_CA_KEY_PATH: /etc/certctl/ca.key
|
|
|
|
# Logging
|
|
CERTCTL_LOG_LEVEL: info
|
|
|
|
ports:
|
|
- '${SERVER_PORT:-8443}:8443'
|
|
|
|
volumes:
|
|
# Mount DNS provider scripts (adapt these for your DNS provider)
|
|
- ./dns-hooks:/etc/certctl/dns-hooks:ro
|
|
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
|
|
networks:
|
|
- certctl-network
|
|
|
|
healthcheck:
|
|
test: ['CMD-SHELL', 'curl -sf http://localhost:8443/health || exit 1']
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 3
|
|
|
|
restart: unless-stopped
|
|
|
|
# certctl agent (manages certificate deployment on target hosts)
|
|
# In production, run agents on each host that needs certificates.
|
|
# For demo, we include one agent in this compose.
|
|
certctl-agent:
|
|
image: ghcr.io/shankar0123/certctl-agent:latest
|
|
container_name: certctl-agent-dns01
|
|
environment:
|
|
# Control plane connection
|
|
CERTCTL_SERVER_URL: http://certctl-server:8443
|
|
CERTCTL_API_KEY: ${AGENT_API_KEY:-agent-demo-key}
|
|
|
|
# Key generation (agent-side keys: production-standard security model)
|
|
CERTCTL_KEYGEN_MODE: agent
|
|
CERTCTL_KEY_DIR: /var/lib/certctl/keys
|
|
|
|
# Discovery (scan existing certs so operator knows what's already deployed)
|
|
CERTCTL_DISCOVERY_DIRS: /etc/letsencrypt/live:/etc/ssl/certs
|
|
|
|
# Heartbeat interval (how often agent checks for work)
|
|
CERTCTL_HEARTBEAT_INTERVAL: 30s
|
|
|
|
# Agent metadata (self-reported to server)
|
|
CERTCTL_AGENT_NAME: wildcard-agent-01
|
|
|
|
# Logging
|
|
CERTCTL_LOG_LEVEL: info
|
|
|
|
volumes:
|
|
# Agent persistent key storage (survives restarts)
|
|
- agent_keys:/var/lib/certctl/keys
|
|
|
|
depends_on:
|
|
certctl-server:
|
|
condition: service_healthy
|
|
|
|
networks:
|
|
- certctl-network
|
|
|
|
restart: unless-stopped
|
|
|
|
networks:
|
|
certctl-network:
|
|
driver: bridge
|
|
|
|
volumes:
|
|
postgres_data:
|
|
driver: local
|
|
agent_keys:
|
|
driver: local
|