Web Application Firewall built with Kemal framework
Run the admin panel behind Nginx (e.g. at https://yourdomain.com/admin/).
You need to:
VITE_BASE_PATH=/admin/)make docker-build-nginx
docker compose build --build-arg VITE_BASE_PATH=/admin/
docker build --build-arg VITE_BASE_PATH=/admin/ -t kemal-waf:latest .
In your Proxy Host configuration for yourdomain.com, add these location blocks in the Custom Nginx Configuration section:
# Admin Panel UI - /admin path
location /admin/ {
proxy_pass http://kemal-waf:8888/;
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;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Admin API - /admin/api path
location /admin/api/ {
proxy_pass http://kemal-waf:8888/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;
}
# Main WAF - proxy to backend
location / {
proxy_pass http://kemal-waf:3030;
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;
}
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# Admin Panel
location /admin/ {
proxy_pass http://127.0.0.1:8888/;
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;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Admin API
location /admin/api/ {
proxy_pass http://127.0.0.1:8888/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;
}
# Main application (proxied through WAF)
location / {
proxy_pass http://127.0.0.1:3030;
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;
}
}
If you prefer a subdomain instead of a subpath (e.g., admin.yourdomain.com):
VITE_BASE_PATH needed):
make docker-build
server {
listen 443 ssl http2;
server_name admin.yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://127.0.0.1:8888;
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;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- waf
waf:
image: kemal-waf:latest
build:
context: .
args:
VITE_BASE_PATH: /admin/
expose:
- "3030"
- "3443"
- "8888"
volumes:
- ./config/waf.yml:/app/config/waf.yml:ro
- ./rules:/app/rules:ro
/admin/ (with trailing slash)/admin/api/ (with trailing slash)proxy_pass URLThe Admin Panel uses WebSockets for real-time updates. Ensure your Nginx config includes:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Always forward these headers:
Host - Original host headerX-Real-IP - Client IPX-Forwarded-For - Forwarded IP chainX-Forwarded-Proto - Original protocol (http/https)After configuration:
curl -I https://yourdomain.com/admin/
curl https://yourdomain.com/admin/api/health
curl https://yourdomain.com/
VITE_BASE_PATH matches your Nginx location pathtail -f /var/log/nginx/error.logproxy_http_version 1.1 is setUpgrade and Connection headers/admin/api/proxy_pass URL has trailing slashlocation /admin/ {
allow 192.168.1.0/24; # Your office IP range
deny all;
# ... proxy settings
}
limit_req_zone $binary_remote_addr zone=admin:10m rate=10r/m;
location /admin/ {
limit_req zone=admin burst=5;
# ... proxy settings
}