feat: add optional letsencrypt tls

This commit is contained in:
bisco
2026-06-24 11:06:33 +02:00
parent 1e3685bab8
commit 719cdce9c1
19 changed files with 561 additions and 62 deletions
+71
View File
@@ -0,0 +1,71 @@
# ADR-0003: Optional Let's Encrypt termination for direct deployments
Date: 2026-06-24
Status: Accepted
## Context
Azione!Lab may be deployed either behind a load balancer that already terminates TLS or
directly on a public host. Running an ACME client unconditionally would duplicate edge
responsibilities in the first topology, while direct exposure still needs automated
certificate issuance and renewal.
## Decision
Add an opt-in Certbot service controlled by `LETSENCRYPT_ENABLED`. Docker Compose gives
that service zero replicas by default and one replica only when the value is `1`.
Certbot uses the HTTP-01 webroot method and shares separate challenge and certificate
volumes with NGINX.
NGINX always serves the ACME challenge path over HTTP. When TLS is enabled but no
certificate exists, application traffic remains available over HTTP. An entrypoint
watcher detects certificate creation or renewal, renders the HTTPS virtual host,
validates the configuration, reloads NGINX, and redirects non-ACME HTTP traffic to
HTTPS. No Docker socket or container-control privilege is required.
The direct-deployment operator is responsible for public DNS, inbound ports 80 and 443,
a valid contact email, and selecting the staging or production ACME endpoint. Behind a
load balancer, Certbot stays disabled and the load balancer owns certificates.
## Consequences
- One Compose definition supports both deployment topologies.
- Direct deployments gain automated issue and renewal without manual certificate copy.
- The first direct request can use HTTP until issuance completes; operators must not
publish sensitive workflows before TLS has been verified.
- Certificate and challenge volumes add persistent operational state.
- HTTP-01 cannot issue when port 80 or DNS is controlled by another edge.
## Alternatives considered
- Always running Certbot was rejected because it conflicts with load-balancer-managed
certificates and creates unnecessary ACME traffic.
- Separate Compose files per topology were rejected as avoidable configuration drift
for this small stack.
- Giving Certbot access to the Docker socket to reload NGINX was rejected because that
privilege is disproportionate; NGINX can safely watch its read-only certificate
volume.
- DNS-01 was not selected because it requires provider-specific credentials and
dependencies. It remains a future option for wildcard certificates or closed port 80.
## Security impact
The certificate private key is writable only by Certbot and read-only to NGINX. Certbot
uses a read-only root filesystem, temporary runtime mounts, `no-new-privileges`, and no
Docker socket. Production must restrict application ports and the proxy trust boundary;
Django accepts the secure forwarded-protocol header for load-balancer deployments.
## Operational impact
Operators must monitor both Certbot issue/renew logs and NGINX reload logs, protect the
certificate volume, and test DNS/firewall changes with the staging CA. Renewals are
checked every 12 hours and NGINX detects certificate changes every 30 seconds by
default. These intervals are configurable.
## Rollback
Set `LETSENCRYPT_ENABLED=0` and recreate the stack to stop Certbot while retaining
certificate state. Terminate TLS at a load balancer if public service must continue.
The certificate volumes can be removed only after confirming they are no longer needed;
database and media volumes are independent.