fix(infra): HAProxy HTTPS and stats security
P1.1 - Enable HTTPS in HAProxy for production: - HTTP to HTTPS redirect (301) - HTTPS frontend on port 443 with veza.pem - config/ssl/ structure with README and generate-ssl-cert.sh - docker-compose.prod.yml volume for certs P1.3 - Restrict HAProxy stats to internal network: - ACL from_internal (127.0.0.1, 172.20.0.0/16) - stats admin if from_internal Also: remove errorfile directives (use HAProxy built-in defaults)
This commit is contained in:
parent
66ba082788
commit
b657776892
7 changed files with 123 additions and 27 deletions
24
config/haproxy/README.md
Normal file
24
config/haproxy/README.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# HAProxy Configuration
|
||||
|
||||
## Production (haproxy.cfg)
|
||||
|
||||
- **HTTP (port 80)**: Redirects all traffic to HTTPS (301)
|
||||
- **HTTPS (port 443)**: Serves traffic with TLS. Certificates from `config/ssl/` mounted at `/etc/ssl/veza/`
|
||||
- **Stats (port 8404)**: Restricted to localhost and Docker network (172.20.0.0/16)
|
||||
|
||||
## SSL Certificates
|
||||
|
||||
Before starting production, add at least one certificate to `config/ssl/`. See `config/ssl/README.md` for instructions.
|
||||
|
||||
For quick local testing with self-signed cert:
|
||||
|
||||
```bash
|
||||
cd config/ssl
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout key.pem -out cert.pem -subj "/CN=veza.local"
|
||||
cat cert.pem key.pem > veza.pem
|
||||
```
|
||||
|
||||
## Development Without HTTPS
|
||||
|
||||
For local development without SSL, use `docker-compose.yml` (not prod) or create a `haproxy.dev.cfg` that omits the HTTPS frontend and HTTP redirect.
|
||||
|
|
@ -14,23 +14,17 @@ defaults
|
|||
timeout client 50000ms
|
||||
timeout server 50000ms
|
||||
timeout http-request 10000ms
|
||||
errorfile 400 /etc/haproxy/errors/400.http
|
||||
errorfile 403 /etc/haproxy/errors/403.http
|
||||
errorfile 408 /etc/haproxy/errors/408.http
|
||||
errorfile 500 /etc/haproxy/errors/500.http
|
||||
errorfile 502 /etc/haproxy/errors/502.http
|
||||
errorfile 503 /etc/haproxy/errors/503.http
|
||||
errorfile 504 /etc/haproxy/errors/504.http
|
||||
|
||||
# ============================================================================
|
||||
# STATS & MONITORING
|
||||
# STATS & MONITORING (P1.3: restricted to internal network)
|
||||
# ============================================================================
|
||||
frontend stats
|
||||
bind *:8404
|
||||
stats enable
|
||||
stats uri /stats
|
||||
stats refresh 30s
|
||||
stats admin if TRUE
|
||||
acl from_internal src 127.0.0.1 172.20.0.0/16
|
||||
stats admin if from_internal
|
||||
|
||||
# ============================================================================
|
||||
# HTTP FRONTEND (Port 80)
|
||||
|
|
@ -39,8 +33,8 @@ frontend http_frontend
|
|||
bind *:80
|
||||
mode http
|
||||
|
||||
# Redirect HTTP to HTTPS (uncomment for production)
|
||||
# redirect scheme https code 301 if !{ ssl_fc }
|
||||
# P1.1: Redirect HTTP to HTTPS in production
|
||||
redirect scheme https code 301 if !{ ssl_fc }
|
||||
|
||||
# ACLs for routing
|
||||
acl is_api path_beg /api/v1
|
||||
|
|
@ -55,23 +49,22 @@ frontend http_frontend
|
|||
use_backend web_frontend if is_web
|
||||
|
||||
# ============================================================================
|
||||
# HTTPS FRONTEND (Port 443) - Uncomment and configure for production
|
||||
# HTTPS FRONTEND (Port 443) - P1.1: Production HTTPS
|
||||
# Certificates from config/ssl/ mounted at /etc/ssl/veza/
|
||||
# ============================================================================
|
||||
# frontend https_frontend
|
||||
# bind *:443 ssl crt /etc/ssl/certs/veza.pem
|
||||
# mode http
|
||||
#
|
||||
# # ACLs for routing
|
||||
# acl is_api path_beg /api/v1
|
||||
# acl is_ws path_beg /ws
|
||||
# acl is_stream path_beg /stream
|
||||
# acl is_web path_beg /
|
||||
#
|
||||
# # Route to appropriate backend
|
||||
# use_backend backend_api if is_api
|
||||
# use_backend chat_ws if is_ws
|
||||
# use_backend stream_ws if is_stream
|
||||
# use_backend web_frontend if is_web
|
||||
frontend https_frontend
|
||||
bind *:443 ssl crt /etc/ssl/veza/veza.pem
|
||||
mode http
|
||||
# ACLs for routing
|
||||
acl is_api path_beg /api/v1
|
||||
acl is_ws path_beg /ws
|
||||
acl is_stream path_beg /stream
|
||||
acl is_web path_beg /
|
||||
# Route to appropriate backend
|
||||
use_backend backend_api if is_api
|
||||
use_backend chat_ws if is_ws
|
||||
use_backend stream_ws if is_stream
|
||||
use_backend web_frontend if is_web
|
||||
|
||||
# ============================================================================
|
||||
# BACKENDS
|
||||
|
|
|
|||
6
config/ssl/.gitignore
vendored
Normal file
6
config/ssl/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Never commit certificates or private keys
|
||||
*.pem
|
||||
*.crt
|
||||
*.key
|
||||
*.p12
|
||||
*.pfx
|
||||
0
config/ssl/.gitkeep
Normal file
0
config/ssl/.gitkeep
Normal file
44
config/ssl/README.md
Normal file
44
config/ssl/README.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# SSL Certificates for HAProxy
|
||||
|
||||
This directory holds SSL certificates for HTTPS in production. **Never commit certificates or private keys** (see `.gitignore`).
|
||||
|
||||
## Required for Production HTTPS
|
||||
|
||||
HAProxy expects a single combined PEM file: **`veza.pem`** containing certificate + private key (concatenated). The config uses `crt /etc/ssl/veza/veza.pem` to avoid loading non-cert files (e.g. README.md).
|
||||
|
||||
## Obtaining Certificates
|
||||
|
||||
### Option 1: Let's Encrypt (Production)
|
||||
|
||||
```bash
|
||||
# Standalone mode (stop HAProxy first)
|
||||
certbot certonly --standalone -d yourdomain.com
|
||||
|
||||
# Copy to config
|
||||
cat /etc/letsencrypt/live/yourdomain.com/fullchain.pem \
|
||||
/etc/letsencrypt/live/yourdomain.com/privkey.pem > config/ssl/veza.pem
|
||||
```
|
||||
|
||||
### Option 2: Self-Signed (Development/Staging)
|
||||
|
||||
```bash
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout config/ssl/key.pem -out config/ssl/cert.pem \
|
||||
-subj "/CN=veza.local"
|
||||
|
||||
cat config/ssl/cert.pem config/ssl/key.pem > config/ssl/veza.pem
|
||||
```
|
||||
|
||||
## Docker Volume
|
||||
|
||||
`docker-compose.prod.yml` mounts this directory to `/etc/ssl/veza` in the HAProxy container. **You must create `veza.pem` before starting production** — the HAProxy healthcheck will fail otherwise.
|
||||
|
||||
## Quick Start (First-Time Setup)
|
||||
|
||||
Run from repo root:
|
||||
|
||||
```bash
|
||||
./scripts/generate-ssl-cert.sh
|
||||
```
|
||||
|
||||
This creates a self-signed certificate for `veza.local`. For production, replace with Let's Encrypt or your CA.
|
||||
|
|
@ -239,11 +239,17 @@ services:
|
|||
image: haproxy:2.8-alpine
|
||||
container_name: veza_haproxy
|
||||
restart: unless-stopped
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 128M
|
||||
ports:
|
||||
- "${PORT_HAPROXY:-80}:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./config/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
|
||||
- ./config/ssl:/etc/ssl/veza:ro
|
||||
depends_on:
|
||||
- backend-api
|
||||
- chat-server
|
||||
|
|
|
|||
23
scripts/generate-ssl-cert.sh
Executable file
23
scripts/generate-ssl-cert.sh
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
#!/usr/bin/env bash
|
||||
# Generate a self-signed SSL certificate for local/staging HAProxy.
|
||||
# For production, use Let's Encrypt or your CA.
|
||||
# Usage: ./scripts/generate-ssl-cert.sh [domain]
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
SSL_DIR="$REPO_ROOT/config/ssl"
|
||||
DOMAIN="${1:-veza.local}"
|
||||
|
||||
mkdir -p "$SSL_DIR"
|
||||
cd "$SSL_DIR"
|
||||
|
||||
echo "Generating self-signed certificate for $DOMAIN..."
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout key.pem -out cert.pem \
|
||||
-subj "/CN=$DOMAIN"
|
||||
|
||||
cat cert.pem key.pem > veza.pem
|
||||
echo "Created config/ssl/veza.pem"
|
||||
echo "Add key.pem and cert.pem to .gitignore if not already excluded."
|
||||
Loading…
Reference in a new issue