From 4105262f2218771fa3b0a09d2b0cbe8647670ab5 Mon Sep 17 00:00:00 2001 From: Foohoo Date: Sun, 10 Aug 2025 13:25:48 +0100 Subject: [PATCH] update for https traffic and setup --- .env.production | 5 + DEPLOYMENT.md | 25 +++-- HTTPS_SETUP.md | 241 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 264 insertions(+), 7 deletions(-) create mode 100644 HTTPS_SETUP.md diff --git a/.env.production b/.env.production index 2b70fff..0e3eca1 100644 --- a/.env.production +++ b/.env.production @@ -10,8 +10,13 @@ MONGODB_DATABASE=scoffer JWT_SECRET=your-super-secret-jwt-key-change-this-in-production # Frontend API URL for server deployment +# IMPORTANT: This should point to your EXTERNAL domain with HTTPS +# The reverse proxy handles SSL termination and forwards HTTP to containers # Replace 'your-domain.com' with your actual domain REACT_APP_API_URL=https://your-domain.com/api # Alternative: If backend runs on different port/subdomain # REACT_APP_API_URL=https://api.your-domain.com + +# For local testing with containers (no SSL) +# REACT_APP_API_URL=http://localhost:5000/api diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 0b15f41..db168a1 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -42,11 +42,18 @@ docker-compose logs -f ### 3. Server Configuration +⚠️ **IMPORTANT**: If you get `net::ERR_SSL_PROTOCOL_ERROR`, see `HTTPS_SETUP.md` for detailed SSL configuration. + +#### SSL Architecture +``` +Internet (HTTPS) → Reverse Proxy (SSL Termination) → Containers (HTTP) +``` + #### Option A: Single Domain Setup If serving everything from one domain (e.g., `https://yourapp.com`): -1. Set up reverse proxy (nginx/traefik) to route: - - `/` → Frontend container (port 3000) +1. Set up reverse proxy (nginx/traefik) with SSL termination to route: + - `/` → Frontend container (port 3000) - `/api/*` → Backend container (port 5000) 2. Update your `.env`: @@ -58,17 +65,21 @@ If serving everything from one domain (e.g., `https://yourapp.com`): If using subdomains (e.g., `api.yourapp.com`): 1. Set up DNS for subdomains -2. Update your `.env`: +2. Configure SSL for both domains +3. Update your `.env`: ``` REACT_APP_API_URL=https://api.yourapp.com ``` ### 4. SSL/HTTPS Setup -For production, ensure HTTPS is configured: -- Use Let's Encrypt with certbot -- Configure your reverse proxy for SSL termination -- Update `REACT_APP_API_URL` to use `https://` +**Critical**: SSL termination must happen at the reverse proxy level, NOT at individual containers. + +- **Containers**: Serve HTTP internally (port 5000 for backend, port 80 for frontend) +- **Reverse Proxy**: Handles HTTPS externally, forwards HTTP to containers +- **Frontend Config**: Points to external HTTPS URL (`https://your-domain.com/api`) + +For detailed SSL setup instructions, see `HTTPS_SETUP.md`. ## Container Port Mapping diff --git a/HTTPS_SETUP.md b/HTTPS_SETUP.md new file mode 100644 index 0000000..3e99d2b --- /dev/null +++ b/HTTPS_SETUP.md @@ -0,0 +1,241 @@ +# HTTPS Setup Guide for Container Deployment + +## Understanding the SSL Error + +The `net::ERR_SSL_PROTOCOL_ERROR` occurs because: + +1. **Frontend** tries to make HTTPS requests to the backend +2. **Backend container** only serves HTTP (port 5000) +3. **SSL termination** should happen at the reverse proxy, not individual containers + +## Correct Architecture + +``` +Internet (HTTPS) → Reverse Proxy (SSL Termination) → Containers (HTTP) +``` + +- **External traffic**: HTTPS to reverse proxy +- **Internal traffic**: HTTP between containers +- **SSL termination**: Handled by reverse proxy (nginx/traefik) + +## Solution Options + +### Option 1: Nginx Reverse Proxy with SSL + +#### 1. Create nginx configuration + +Create `/etc/nginx/sites-available/recipe-app`: + +```nginx +server { + listen 80; + server_name your-domain.com; + + # Redirect HTTP to HTTPS + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl http2; + server_name your-domain.com; + + # SSL Configuration + ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; + + # SSL Security Settings + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + + # Frontend (React App) + location / { + proxy_pass http://localhost:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Backend API + location /api/ { + proxy_pass http://localhost:5000/api/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # CORS headers (if needed) + add_header Access-Control-Allow-Origin "https://your-domain.com" always; + add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always; + add_header Access-Control-Allow-Headers "Authorization, Content-Type" always; + + if ($request_method = 'OPTIONS') { + return 204; + } + } +} +``` + +#### 2. Enable the site +```bash +sudo ln -s /etc/nginx/sites-available/recipe-app /etc/nginx/sites-enabled/ +sudo nginx -t +sudo systemctl reload nginx +``` + +#### 3. Get SSL certificate with Let's Encrypt +```bash +sudo certbot --nginx -d your-domain.com +``` + +### Option 2: Docker Compose with Nginx Container + +Create an nginx container in your docker-compose.yml: + +```yaml + # Nginx Reverse Proxy + nginx: + image: nginx:alpine + container_name: scoffer-nginx + restart: unless-stopped + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/ssl:/etc/nginx/ssl:ro + - /etc/letsencrypt:/etc/letsencrypt:ro + depends_on: + - frontend + - backend + networks: + - recipe-network +``` + +### Option 3: Traefik (Automatic SSL) + +Add traefik to your docker-compose.yml: + +```yaml + # Traefik Reverse Proxy + traefik: + image: traefik:v2.10 + container_name: scoffer-traefik + restart: unless-stopped + command: + - "--api.dashboard=true" + - "--providers.docker=true" + - "--providers.docker.exposedbydefault=false" + - "--entrypoints.web.address=:80" + - "--entrypoints.websecure.address=:443" + - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true" + - "--certificatesresolvers.letsencrypt.acme.email=your-email@example.com" + - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" + ports: + - "80:80" + - "443:443" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - traefik-ssl-certs:/letsencrypt + networks: + - recipe-network + + # Update frontend service + frontend: + # ... existing config ... + labels: + - "traefik.enable=true" + - "traefik.http.routers.frontend.rule=Host(\`your-domain.com\`)" + - "traefik.http.routers.frontend.entrypoints=websecure" + - "traefik.http.routers.frontend.tls.certresolver=letsencrypt" + - "traefik.http.services.frontend.loadbalancer.server.port=80" + + # Update backend service + backend: + # ... existing config ... + labels: + - "traefik.enable=true" + - "traefik.http.routers.backend.rule=Host(\`your-domain.com\`) && PathPrefix(\`/api\`)" + - "traefik.http.routers.backend.entrypoints=websecure" + - "traefik.http.routers.backend.tls.certresolver=letsencrypt" + - "traefik.http.services.backend.loadbalancer.server.port=5000" + +volumes: + traefik-ssl-certs: +``` + +## Environment Configuration + +### For Production (.env) +```bash +# Frontend points to EXTERNAL HTTPS URL +REACT_APP_API_URL=https://your-domain.com/api + +# Backend and DB use internal container names (HTTP) +MONGODB_HOST=mongodb +MONGODB_PORT=27017 +JWT_SECRET=your-production-secret +``` + +### For Local Testing (.env.local) +```bash +# Frontend points to localhost (HTTP) +REACT_APP_API_URL=http://localhost:5000/api + +# Backend and DB use container names +MONGODB_HOST=mongodb +MONGODB_PORT=27017 +JWT_SECRET=your-dev-secret +``` + +## Troubleshooting + +### Still getting SSL errors? + +1. **Check your .env file**: + ```bash + cat .env | grep REACT_APP_API_URL + # Should show: REACT_APP_API_URL=https://your-domain.com/api + ``` + +2. **Verify reverse proxy is working**: + ```bash + curl -I https://your-domain.com/api/health + # Should return 200 OK + ``` + +3. **Check container logs**: + ```bash + docker-compose logs frontend + docker-compose logs backend + docker-compose logs nginx # if using nginx container + ``` + +4. **Test internal container communication**: + ```bash + docker-compose exec frontend ping backend + docker-compose exec backend ping mongodb + ``` + +### Common Issues + +- **Mixed content errors**: Ensure all API calls use HTTPS in production +- **CORS errors**: Configure CORS headers in nginx or backend +- **Certificate issues**: Verify SSL certificate is valid and not expired +- **Port conflicts**: Ensure ports 80/443 are not used by other services + +## Security Best Practices + +1. **Use strong SSL configuration** (TLS 1.2+) +2. **Enable HSTS headers** +3. **Configure proper CORS policies** +4. **Use environment-specific secrets** +5. **Regular certificate renewal** (certbot auto-renewal) + +## Quick Fix Summary + +1. **Set up reverse proxy** (nginx/traefik) with SSL termination +2. **Point frontend to HTTPS domain**: `REACT_APP_API_URL=https://your-domain.com/api` +3. **Keep container communication as HTTP**: containers talk to each other via HTTP +4. **SSL happens at proxy level**: not at individual containers