- Changed logging level from `info` to `debug` in plugin loading to reduce verbosity during runtime. - Significantly expanded `examples/static/README.md` with new sections, examples (basic, certbot, deny pages, JWT validator), and detailed usage instructions. - Improved `examples/docker/docker-compose-changed-label.yml` with clearer redirect syntax using JSON for better readability. - Fixed handling of empty or invalid JSON in labels within `easymapping` with fallback to default values and error logging. - Added additional entries to `.gitignore` to exclude dynamic and temporary files like auto-generated configuration files and certificates. |
||
|---|---|---|
| .. | ||
| conf | ||
| docker-compose.yml | ||
| README.md | ||
Static Configuration Example
This directory demonstrates EasyHAProxy using static YAML configuration instead of dynamic service discovery.
Static mode is useful for:
- Non-containerized backends (VMs, bare metal)
- Fixed infrastructure
- Explicit routing control
Prerequisites
1. Generate SSL Certificates
# From repository root
./examples/generate-keys.sh
2. Add Host Entry
echo "127.0.0.1 host1.local www.host1.local" | sudo tee -a /etc/hosts
echo "127.0.0.1 host2.local www.host2.local" | sudo tee -a /etc/hosts
Scenario 1: Basic (HTTP → HTTPS Redirect)
What it does: Simple HTTP to HTTPS redirect with SSL termination.
Getting Started
cd examples/static
# 1. Copy the basic config
cp conf/config-basic.yml conf/config.yml
# 2. Start backend container
docker run -d --name container -p 8080:8080 byjg/static-httpserver
# 3. Start EasyHAProxy
docker compose up -d
Test
# Test HTTP redirect
curl -I http://host1.local
# Expected: HTTP/1.1 301 Moved Permanently
# Expected: Location: https://host1.local
# Test HTTPS
curl -k https://host1.local
# Expected: Hello from Static HTTP Server!
# Test www redirect
curl -I http://www.host1.local
# Expected: HTTP/1.1 301 Moved Permanently
# Expected: Location: https://host1.local
Stats Interface
Open: http://localhost:1936
- Username:
admin - Password:
password
Clean Up
docker compose down
docker stop container && docker rm container
Scenario 2: Certbot (Let's Encrypt SSL)
What it does: Automatic SSL certificates from Let's Encrypt using ACME HTTP-01 challenge.
Requirements
- Public IP address
- Domain pointing to your IP
- Ports 80/443 publicly accessible
Getting Started
cd examples/static
# 1. Copy the certbot config
cp conf/config-certbot.yml conf/config.yml
# 2. Edit config.yml and change:
# - Replace "example.com" with your real domain
# - Update EASYHAPROXY_CERTBOT_EMAIL in docker-compose.yml
# 3. Start backend container
docker run -d --name container -p 8080:8080 byjg/static-httpserver
# 4. Start EasyHAProxy
docker compose up -d
# 5. Check logs for certificate generation
docker compose logs -f
What to Expect
# Certbot will:
# 1. Request certificate from Let's Encrypt
# 2. Complete HTTP-01 challenge
# 3. Save certificate in /certs/certbot/
# 4. Reload HAProxy with new certificate
Test
# Test HTTPS with real certificate
curl https://your-domain.com
# Expected: No certificate warnings (valid SSL)
# Test HTTP redirect
curl -I http://your-domain.com
# Expected: HTTP/1.1 301 Moved Permanently
Clean Up
docker compose down
docker stop container && docker rm container
Note: Certificates are stored in Docker volume certs_certbot and persist across restarts.
Scenario 3: Deny Pages (Block Specific Paths)
What it does: Blocks access to sensitive paths like /admin, /wp-login.php, etc.
Getting Started
cd examples/static
# 1. Copy the deny-pages config
cp conf/config-deny-pages.yml conf/config.yml
# 2. Start backend container
docker run -d --name container -p 8080:8080 byjg/static-httpserver
# 3. Start EasyHAProxy
docker compose up -d
Test
# Test normal page (should work)
curl -k https://host1.local/
# Expected: Hello from Static HTTP Server!
# Test blocked path (should fail)
curl -I -k https://host1.local/admin
# Expected: HTTP/1.1 404 Not Found
curl -I -k https://host1.local/wp-login.php
# Expected: HTTP/1.1 404 Not Found
curl -I -k https://host1.local/.env
# Expected: HTTP/1.1 404 Not Found
What's Blocked
The example blocks these paths:
/admin/wp-admin/wp-login.php/.env/config
Customize Blocked Paths
Edit conf/config.yml:
plugin_config:
deny_pages:
paths: /admin,/private,/internal
status_code: 403 # or 404
Clean Up
docker compose down
docker stop container && docker rm container
Scenario 4: JWT Validator (API Authentication)
What it does: Validates JWT tokens in Authorization header before allowing access.
Getting Started
cd examples/static
# 1. Copy the JWT validator config
cp conf/config-jwt-validator.yml conf/config.yml
# 2. JWT keys were already generated by generate-keys.sh
# Location: examples/docker/jwt_pubkey.pem and jwt_private.pem
# 3. Start backend container
docker run -d --name container -p 8080:8080 byjg/static-httpserver
# 4. Start EasyHAProxy
docker compose up -d
Test Without Token (Should Fail)
curl -k https://host1.local/
# Expected: Missing Authorization HTTP header
Test With Valid Token
# 1. Generate a test JWT token using jwt_private.pem
# You can use https://jwt.io or a JWT library
# 2. Example with valid token:
TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
curl -k -H "Authorization: Bearer $TOKEN" https://host1.local/
# Expected: Hello from Static HTTP Server! (if token is valid)
Generate Test Token
# Python example using PyJWT
import jwt
from datetime import datetime, timedelta
with open('examples/docker/jwt_private.pem', 'r') as f:
private_key = f.read()
payload = {
'iss': 'https://auth.example.com/',
'aud': 'https://api.example.com',
'exp': datetime.utcnow() + timedelta(hours=1)
}
token = jwt.encode(payload, private_key, algorithm='RS256')
print(token)
What's Validated
- Authorization header must be present
- Token must be valid JWT format
- Signature must match public key (
jwt_pubkey.pem) - Issuer must match:
https://auth.example.com/ - Audience must match:
https://api.example.com - Token must not be expired
Customize JWT Settings
Edit conf/config.yml:
plugin_config:
jwt_validator:
algorithm: RS256
issuer: https://your-auth-server.com/
audience: https://your-api.com
pubkey_path: /certs/haproxy/jwt_pubkey.pem
Clean Up
docker compose down
docker stop container && docker rm container
Configuration File Reference
All scenarios use /etc/haproxy/static/config.yml mounted from ./conf/config.yml.
Basic Structure
stats:
username: admin
password: password
port: 1936
customerrors: true
easymapping:
- port: 80
redirect:
host1.local: https://host1.local
- port: 443
ssl: true
hosts:
host1.local:
containers:
- container:8080
With Plugins
easymapping:
- port: 443
ssl: true
hosts:
host1.local:
containers:
- container:8080
plugins:
- deny_pages
plugin_config:
deny_pages:
paths: /admin,/private
status_code: 404
Advanced: Multiple Backends
Load balance across multiple containers:
hosts:
api.example.com:
containers:
- api1:8080
- api2:8080
- api3:8080
Advanced: External Backends
Route to non-Docker backends:
hosts:
legacy.example.com:
containers:
- 192.168.1.100:8080
- 192.168.1.101:8080
Troubleshooting
FileNotFoundError: config.yml
# Make sure config.yml exists
ls conf/config.yml
# If missing, copy from an example:
cp conf/config-basic.yml conf/config.yml
503 Service Unavailable
# Check backend is running
docker ps | grep container
curl http://localhost:8080
SSL Certificate Not Found
# Verify certificate exists
ls -la host1.local.pem
# Regenerate if needed
cd ../.. && ./examples/generate-keys.sh
Changes Not Applied
# Restart to reload config
docker compose restart