kemal-waf Documentation

Web Application Firewall built with Kemal framework

View the Project on GitHub kursadaltan/kemalwaf

Nginx Reverse Proxy

Run the admin panel behind Nginx (e.g. at https://yourdomain.com/admin/).

You need to:

  1. Build the image with subpath support (VITE_BASE_PATH=/admin/)
  2. Configure Nginx to proxy to the WAF and admin ports

Build with subpath support

Option 1: Using Make

make docker-build-nginx

Option 2: Using Docker Compose

docker compose build --build-arg VITE_BASE_PATH=/admin/

Option 3: Using Docker Build

docker build --build-arg VITE_BASE_PATH=/admin/ -t kemal-waf:latest .

Nginx Configuration

Option A: Nginx Proxy Manager (GUI)

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;
}

Option B: Standard Nginx Config

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;
    }
}

Alternative: Subdomain Setup

If you prefer a subdomain instead of a subpath (e.g., admin.yourdomain.com):

  1. Build with default settings (no VITE_BASE_PATH needed):
    make docker-build
    
  2. Create separate Nginx server block:
    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";
        }
    }
    

Docker Compose with Nginx

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

Important Notes

Trailing Slashes

WebSocket Support

The 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";

Headers

Always forward these headers:

Testing

After configuration:

  1. Test Admin Panel:
    curl -I https://yourdomain.com/admin/
    
  2. Test Admin API:
    curl https://yourdomain.com/admin/api/health
    
  3. Test WAF:
    curl https://yourdomain.com/
    

Troubleshooting

404 Errors

WebSocket Not Working

API Not Found

Security Considerations

  1. Restrict Admin Access:
    location /admin/ {
        allow 192.168.1.0/24;  # Your office IP range
        deny all;
        # ... proxy settings
    }
    
  2. Use HTTPS:
    • Always use SSL/TLS in production
    • Redirect HTTP to HTTPS
  3. Rate Limiting:
    limit_req_zone $binary_remote_addr zone=admin:10m rate=10r/m;
       
    location /admin/ {
        limit_req zone=admin burst=5;
        # ... proxy settings
    }