diff --git a/deploy/docker/docker-compose.yml b/deploy/docker/docker-compose.yml index dd137dc..da6cf90 100644 --- a/deploy/docker/docker-compose.yml +++ b/deploy/docker/docker-compose.yml @@ -26,15 +26,12 @@ services: - "443:443/tcp" - "1936:1936/tcp" - networks: - - easyhaproxy - volumes: certs_certbot: - external: true +# external: true certs_haproxy: - external: true +# external: true networks: easyhaproxy: - external: true +# external: true diff --git a/docs/acme.md b/docs/acme.md index 5aa3ac2..f290306 100644 --- a/docs/acme.md +++ b/docs/acme.md @@ -54,19 +54,21 @@ Tips ## Environment Variables -To enable the ACME protocol we need to enable Certbot in EasyHAProxy by setting up to the following environment variables: +To enable the ACME protocol we need to enable Certbot in EasyHAProxy by setting up the following environment variables: | Environment Variable | Required? | Description | |------------------------------------------|-----------|----------------------------------------------------------------------------------------------------------------------------------| -| EASYHAPROXY_CERTBOT_EMAIL | YES | Your email in the certificate authority. | -| EASYHAPROXY_CERTBOT_AUTOCONFIG | - | Will use pre-sets for your Certificate Authority (CA). See table below. | -| EASYHAPROXY_CERTBOT_SERVER | - | The ACME Endpoint of your certificate authority. If you use AUTOCONFIG, it is set automatically. See table below. | +| EASYHAPROXY_CERTBOT_EMAIL | **YES** | Your email for the certificate authority. Required for certificate issuance. | +| EASYHAPROXY_CERTBOT_AUTOCONFIG | **YES\*** | Pre-configured settings for your Certificate Authority (CA). See table below. **Required if CERTBOT_SERVER is not set.** | +| EASYHAPROXY_CERTBOT_SERVER | **YES\*** | The ACME endpoint URL of your certificate authority. **Required if AUTOCONFIG is not set.** Auto-set when using AUTOCONFIG. | | EASYHAPROXY_CERTBOT_EAB_KID | - | External Account Binding (EAB) Key Identifier (KID) provided by your certificate authority. Some CA require it. See table below. | | EASYHAPROXY_CERTBOT_EAB_HMAC_KEY | - | External Account Binding (EAB) HMAC Key provided by your certificate authority. Some CA require it. See table below. | | EASYHAPROXY_CERTBOT_RETRY_COUNT | - | Wait 'n' requests before retrying issue invalid requests. Default 60. | | EASYHAPROXY_CERTBOT_PREFERRED_CHALLENGES | - | The preferred challenges for Certbot. Available: `http` | | EASYHAPROXY_CERTBOT_MANUAL_AUTH_HOOK | - | The path to a script that will be executed (default: None) | +**\*Important:** You must set **either** `EASYHAPROXY_CERTBOT_AUTOCONFIG` **or** `EASYHAPROXY_CERTBOT_SERVER` (not both). Using `AUTOCONFIG` is recommended as it automatically configures the server URL for popular certificate authorities. + ## Auto Config Certificate Authority (CA) Here are detailed instructions per Certificate Authority (CA). If anyone is missing, please let's know. @@ -147,5 +149,146 @@ docker run \ - Do not set port 443 for the container when using ACME, because EasyHAProxy will create the HTTPS binding automatically once the certificate is issued. ::: +## Complete Docker Compose Example + +Here's a complete `docker-compose.yml` showing proper ACME configuration: + +```yaml +services: + easyhaproxy: + image: byjg/easy-haproxy:5.0.0 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + # REQUIRED: Persist Certbot certificates (ACME) + - certs_certbot:/etc/easyhaproxy/certs/certbot + # OPTIONAL: For manual certificates (see SSL documentation) + - certs_haproxy:/etc/easyhaproxy/certs/haproxy + environment: + # Service discovery + EASYHAPROXY_DISCOVER: docker + EASYHAPROXY_LABEL_PREFIX: easyhaproxy + + # ACME/Certbot Configuration (Method 1: Recommended) + EASYHAPROXY_CERTBOT_EMAIL: your-email@example.com + EASYHAPROXY_CERTBOT_AUTOCONFIG: letsencrypt + + # ACME/Certbot Configuration (Method 2: Manual) + # EASYHAPROXY_CERTBOT_EMAIL: your-email@example.com + # EASYHAPROXY_CERTBOT_SERVER: https://acme-v02.api.letsencrypt.org/directory + + # Other settings + EASYHAPROXY_SSL_MODE: "default" + HAPROXY_CUSTOMERRORS: "true" + HAPROXY_USERNAME: admin + HAPROXY_PASSWORD: password + HAPROXY_STATS_PORT: 1936 + ports: + - "80:80/tcp" + - "443:443/tcp" + - "1936:1936/tcp" + healthcheck: + test: ["CMD", "curl", "-f", "-u", "admin:password", "http://localhost:1936"] + interval: 10s + timeout: 5s + start_period: 30s + retries: 3 + + # Example backend service with ACME enabled + myapp: + image: nginx:alpine + labels: + easyhaproxy.http.host: example.com + easyhaproxy.http.port: 80 + easyhaproxy.http.localport: 80 + easyhaproxy.http.certbot: "true" # Enable ACME for this domain + +volumes: + certs_certbot: + # This volume MUST be persisted to avoid rate limits + certs_haproxy: + # Optional: only needed if using manual certificates +``` + +## Certificate Storage Paths + +EasyHAProxy uses different paths for different certificate types: + +| Path | Purpose | When to Mount | +|----------------------------------|-------------------------------------|----------------------------------------------| +| `/etc/easyhaproxy/certs/certbot` | ACME/Certbot automatic certificates | **Required** when using ACME | +| `/etc/easyhaproxy/certs/haproxy` | Manual/custom certificates | Optional - only if using custom certificates | + +Both volumes can be mounted simultaneously. Per-domain certificate selection: +- If a domain has `certbot=true` label, ACME certificate is used +- Otherwise, manual certificate from `/etc/easyhaproxy/certs/haproxy` is used (if present) + +## Troubleshooting + +### Warning: "ACME environment not ready: ACME server not configured" + +**Cause:** You set `EASYHAPROXY_CERTBOT_EMAIL` but forgot to configure the ACME server. + +**Solution:** Add one of these to your environment variables: + +```yaml +# Option 1: Use AUTOCONFIG (recommended) +EASYHAPROXY_CERTBOT_AUTOCONFIG: letsencrypt + +# Option 2: Set server manually +EASYHAPROXY_CERTBOT_SERVER: https://acme-v02.api.letsencrypt.org/directory +``` + +### Certificates Not Being Issued + +**Common causes:** +1. Port 80 is not publicly accessible +2. DNS doesn't point to your server +3. Container label missing `certbot=true` +4. Container port is not 80 (`easyhaproxy..port` must be 80) +5. Rate limits hit (check `/etc/easyhaproxy/certs/certbot` volume) + +**Debug steps:** +```bash +# Check EasyHAProxy logs +docker logs easyhaproxy + +# Check if Certbot volume is persisted +docker volume inspect certs_certbot + +# Verify port 80 is accessible +curl -I http://your-domain.com/.well-known/acme-challenge/test +``` + +### Rate Limit Errors + +If you hit Let's Encrypt rate limits: +- Wait for the limit window to reset (usually 1 week) +- Use staging server for testing: `EASYHAPROXY_CERTBOT_AUTOCONFIG: letsencrypt_test` +- Ensure `/etc/easyhaproxy/certs/certbot` volume is properly persisted +- See: https://letsencrypt.org/docs/rate-limits/ + +### Using Both ACME and Manual Certificates + +You can use both simultaneously: +1. Mount both volumes (`certs_certbot` and `certs_haproxy`) +2. Use `certbot=true` label for domains that should use ACME +3. Omit the label for domains using manual certificates + +Example: +```yaml +services: + # This service uses ACME + app1: + labels: + easyhaproxy.http.host: auto.example.com + easyhaproxy.http.certbot: "true" + + # This service uses manual certificate + app2: + labels: + easyhaproxy.http.host: manual.example.com + # No certbot label - will use /etc/easyhaproxy/certs/haproxy/manual.example.com.pem +``` + ---- [Open source ByJG](http://opensource.byjg.com) \ No newline at end of file diff --git a/docs/ssl.md b/docs/ssl.md index 40ceaac..e4b978d 100644 --- a/docs/ssl.md +++ b/docs/ssl.md @@ -4,42 +4,31 @@ sidebar_position: 9 # Setup custom certificates -You can use your own certificates with EasyHAProxy. You just need to let EasyHAProxy know that certificate. +You can use your own certificates with EasyHAProxy instead of (or in addition to) automatic ACME/Certbot certificates. -There are two ways to do that. +:::info How SSL Termination Works +SSL termination happens at the **HAProxy level**, NOT in your backend containers. + +- Your backend containers should **only** expose HTTP (port 80), not HTTPS +- HAProxy handles all SSL/TLS encryption and decryption +- Backend containers receive plain HTTP traffic from HAProxy +- Do NOT configure SSL in your backend application when using EasyHAProxy + +This is the **correct design** - it centralizes SSL management at the proxy layer. +::: + +:::info Certificate Types +EasyHAProxy supports two certificate sources: +- **ACME/Certbot automatic certificates** - Issued automatically via Let's Encrypt or other ACME providers (see [ACME documentation](./acme.md)) +- **Manual/custom certificates** - Your own certificates loaded via volume mount (recommended) or labels (this page) + +Both can be used simultaneously. Per domain, ACME certificates (if `certbot=true` label is set) take precedence over manual certificates. +::: + +There are two ways to provide custom certificates: -- [Setup certificate as a label definition in docker container](#setup-certificate-as-a-label-definition-in-docker-container) - [Map the certificate as a docker volume](#map-the-certificate-as-a-docker-volume) - -## Setup certificate as a label definition in docker container - -1. Create a single PEM from the certificate and key. - -```bash title="Combine certificate and key" -cat example.com.crt example.com.key > single.pem - -cat single.pem - ------BEGIN CERTIFICATE----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC5ZheHqmBnEJP+ -U9r1gxYWKLzdqrMrcxtQN6M1hIH9n0peuJeIrybdcV7sMbStMXI= ------END CERTIFICATE----- - ------BEGIN PRIVATE KEY----- -MIIEojCCA4qgAwIBAgIUegW2BimwuL4RzRZ2WYkHA6U5nkAwDQYJKoZIhvcNAQEL -3j4wz8/I5fdsk090j4s5KA== ------END PRIVATE KEY----- -``` - -2. Convert the `single.pem` to BASE64 in a single line: - -```bash title="Convert to BASE64" -cat single.pem | base64 -w0 -``` - -3. Define a label in yout container - -Add the Base64 string you generated before to the label `easyhaproxy.[definition].sslcert` +- [Setup certificate as a label definition](#setup-certificate-as-a-label-definition-in-docker-container) ## Map the certificate as a docker volume @@ -77,8 +66,79 @@ MIIEojCCA4qgAwIBAgIUegW2BimwuL4RzRZ2WYkHA6U5nkAwDQYJKoZIhvcNAQEL 3. Copy this certificate to EasyHAProxy volume: ```bash title="Copy certificate to container" -docker cp single.pem easyhaproxy:/etc/easyhaproxy/certs/haproxy +# IMPORTANT: Filename must match the domain! +docker cp single.pem easyhaproxy:/etc/easyhaproxy/certs/haproxy/example.com.pem ``` +:::warning Important Notes +- The filename **must match the domain name**: `example.com.pem` for domain `example.com` +- When using volume-mounted certificates, **do NOT** use the `easyhaproxy.[definition].sslcert` label +- The volume mount method and the label method are **mutually exclusive** per domain +- SSL termination happens at HAProxy - your backend containers should only serve HTTP +::: + +4. Configure your backend container (no sslcert label needed): + +```yaml +services: + easyhaproxy: + image: byjg/easy-haproxy:5.0.0 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - certs_haproxy:/etc/easyhaproxy/certs/haproxy + ports: + - "80:80" + - "443:443" + + myapp: + image: nginx + labels: + easyhaproxy.web.host: example.com + easyhaproxy.web.port: 80 # Frontend port (HAProxy listens here) + easyhaproxy.web.localport: 80 # Backend port (your container) + # NO sslcert label when using volume method! + +volumes: + certs_haproxy: +``` + +## Setup certificate as a label definition in docker container + +:::info Alternative Method +This method embeds certificates directly in container labels. Use it when you want certificates in version control or don't want to manage external files. **Volume method is recommended for most use cases.** +::: + +1. Create a single PEM from the certificate and key: + +```bash title="Combine certificate and key" +cat example.com.crt example.com.key > single.pem +``` + +2. Convert the `single.pem` to BASE64 in a single line: + +```bash title="Convert to BASE64" +cat single.pem | base64 -w0 +``` + +3. Add the Base64 string to your container label: + +```yaml +services: + myapp: + image: nginx + labels: + easyhaproxy.web.host: example.com + easyhaproxy.web.port: 80 + easyhaproxy.web.localport: 80 + easyhaproxy.web.sslcert: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t..." # Base64 certificate +``` + +:::warning When Using Label Method +- **There is no necessary to** mount the `/etc/easyhaproxy/certs/haproxy` volume for this domain +- Using `sslcert` label means the volume-mounted certificate will be **ignored** +- Certificate is visible in `docker inspect` output (less secure) +- Updating requires container redeployment +::: + ---- [Open source ByJG](http://opensource.byjg.com)