# Traefik SSL Configuration for MerchBay Admin This document explains how to configure paid SSL certificates for production and automatic Let's Encrypt for development. ## Network Configuration All deployments use the external network: `traefik-public` ```bash # Ensure the network exists docker network inspect traefik-public >/dev/null 2>&1 || docker network create traefik-public ``` ## Development (dev-admin.merchbay.app) - Automatic SSL Development uses Let's Encrypt for automatic SSL certificate generation. ### Traefik Configuration Ensure your Traefik has Let's Encrypt configured: ```yaml # traefik.yml or dynamic config certificatesResolvers: letsencrypt: acme: email: admin@merchbay.app storage: /letsencrypt/acme.json httpChallenge: entryPoint: web ``` ### Application Labels (Already configured in docker-compose) ```yaml labels: - "traefik.http.routers.merchbay-admin-dev.tls.certresolver=letsencrypt" ``` ## Production (merchbay.app) - Paid SSL Certificate Production uses a paid SSL certificate (e.g., from GoDaddy, Namecheap, Cloudflare). ### Step 1: Prepare SSL Certificate Files You should have these files from your SSL provider: - `merchbay.app.crt` - Certificate file - `merchbay.app.key` - Private key file - `ca-bundle.crt` - CA bundle (optional, for chain) Create a combined certificate file: ```bash # Your SSL certificates are in /srv/certs # Verify files exist ls -la /srv/certs/ # If you have a CA bundle, create a full chain cd /srv/certs cat merchbay.app.crt ca-bundle.crt > merchbay.app-fullchain.crt # Set proper permissions sudo chmod 600 /srv/certs/*.key sudo chmod 644 /srv/certs/*.crt ``` ### Step 2: Configure Traefik File Provider Create a dynamic configuration file for Traefik: ```bash sudo nano /opt/traefik/dynamic/certs.yml ``` Add: ```yaml # /opt/traefik/dynamic/certs.yml or your Traefik dynamic config location tls: certificates: - certFile: /srv/certs/merchbay.app-fullchain.crt keyFile: /srv/certs/merchbay.app.key stores: - default stores: default: defaultCertificate: certFile: /srv/certs/merchbay.app-fullchain.crt keyFile: /srv/certs/merchbay.app.key ``` ### Step 3: Update Traefik docker-compose.yml Ensure Traefik has file provider enabled and certificates mounted: ```yaml services: traefik: image: traefik:v2.10 command: - "--providers.docker=true" - "--providers.docker.network=traefik-public" - "--providers.file.directory=/etc/traefik/dynamic" - "--providers.file.watch=true" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" # Let's Encrypt for dev - "--certificatesresolvers.letsencrypt.acme.email=admin@merchbay.app" - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - /srv/certs:/srv/certs:ro - /opt/traefik/dynamic:/etc/traefik/dynamic:ro - traefik-letsencrypt:/letsencrypt networks: - traefik-public ``` ### Step 4: Restart Traefik ```bash cd /path/to/traefik docker compose restart traefik # Verify certificates are loaded docker compose logs traefik | grep -i cert ``` ## Application Configuration ### Development Branch (dev) File: `docker-compose.portainer.dev.yml` - Domain: `dev-admin.merchbay.app` - SSL: Let's Encrypt (automatic) - Certificate Resolver: `letsencrypt` ```yaml labels: - "traefik.http.routers.merchbay-admin-dev.rule=Host(`dev-admin.merchbay.app`)" - "traefik.http.routers.merchbay-admin-dev.tls.certresolver=letsencrypt" ``` ### Production Branch (main) File: `docker-compose.portainer.yml` - Domain: `merchbay.app` - SSL: Paid certificate (via Traefik file provider) - No certresolver label (uses default store) ```yaml labels: - "traefik.http.routers.merchbay-admin.rule=Host(`merchbay.app`)" - "traefik.http.routers.merchbay-admin.tls=true" # No certresolver - uses file provider certificate ``` ## Gitea Secrets Configuration ### Development Secrets ``` DEV_DB_HOST=dev-mysql-host DEV_DB_PORT=3306 DEV_DB_DATABASE=merchbay_admin_dev DEV_DB_USERNAME=dev_user DEV_DB_PASSWORD=dev_password ``` ### Production Secrets ``` PROD_DEPLOY_HOST=prod-server-ip PROD_DEPLOY_USER=deploy PROD_DEPLOY_SSH_KEY=-----BEGIN RSA PRIVATE KEY-----... PROD_DEPLOY_PORT=22 PROD_DB_HOST=prod-mysql-host PROD_DB_PORT=3306 PROD_DB_DATABASE=merchbay_admin PROD_DB_USERNAME=prod_user PROD_DB_PASSWORD=prod_password ``` ### Shared Secrets (if using same server) ``` DEPLOY_HOST=your-server-ip DEPLOY_USER=deploy DEPLOY_SSH_KEY=-----BEGIN RSA PRIVATE KEY-----... DEPLOY_PORT=22 ``` ## Verification ### Check Development SSL ```bash # Check certificate issuer (should be Let's Encrypt) echo | openssl s_client -servername dev-admin.merchbay.app -connect dev-admin.merchbay.app:443 2>/dev/null | openssl x509 -noout -issuer # Should show: issuer=C = US, O = Let's Encrypt, CN = R3 ``` ### Check Production SSL ```bash # Check certificate issuer (should be your SSL provider) echo | openssl s_client -servername merchbay.app -connect merchbay.app:443 2>/dev/null | openssl x509 -noout -issuer # Should show your paid SSL provider # Check certificate validity echo | openssl s_client -servername merchbay.app -connect merchbay.app:443 2>/dev/null | openssl x509 -noout -dates ``` ### Verify in Browser 1. Visit https://dev-admin.merchbay.app - Certificate should be issued by "Let's Encrypt Authority X3" 2. Visit https://merchbay.com - Certificate should be issued by your paid SSL provider ## Troubleshooting ### Development SSL Not Working ```bash # Check Let's Encrypt logs docker logs traefik | grep letsencrypt # Verify acme.json permissions ls -l /path/to/letsencrypt/acme.json # Should be: -rw------- (600) # Check DNS dig dev-admin.merchbay.app +short # Should return your server IP ``` ### Production SSL Not Working ```bash # Verify Traefik can read certificates docker exec traefik ls -l /srv/certs/ # Check dynamic configuration is loaded docker exec traefik cat /etc/traefik/dynamic/certs.yml # Verify certificate format openssl x509 -in /srv/certs/merchbay.app-fullchain.crt -text -noout # Check private key openssl rsa -in /srv/certs/merchbay.app.key -check ``` ### Certificate Mismatch ```bash # Verify certificate and key match openssl x509 -noout -modulus -in /srv/certs/merchbay.app.crt | openssl md5 openssl rsa -noout -modulus -in /srv/certs/merchbay.app.key | openssl md5 # Both should output the same hash ``` ## Renewing Certificates ### Development (Let's Encrypt) Automatic renewal every 60 days. No action needed. ### Production (Paid SSL) Before certificate expiration: 1. Download new certificate from your SSL provider 2. Update files in `/srv/certs/` 3. Restart Traefik: `docker compose restart traefik` 4. Verify: `curl -vI https://merchbay.app` ## DNS Configuration ### Development ``` Type: A Name: dev-admin.merchbay.app Value: YOUR_SERVER_IP TTL: 3600 ``` ### Production ``` Type: A Name: merchbay.app (or @) Value: YOUR_SERVER_IP TTL: 3600 Type: A Name: www.merchbay.app Value: YOUR_SERVER_IP TTL: 3600 ``` ## Security Best Practices 1. **Keep certificates private**: Never commit SSL keys to git 2. **Use strong permissions**: 600 for private keys, 644 for certificates 3. **Monitor expiration**: Set reminders 30 days before expiration 4. **Use HSTS**: Add header after SSL is working correctly 5. **Enable OCSP stapling**: Improves SSL performance 6. **Regular updates**: Keep Traefik updated for security patches