wulf-pulse/docs/WEBHOOK_REVERSE_PROXY.md
root 80d8d40f06 docs: add reverse proxy configuration guide for webhook endpoint
Since Pulse is not publicly accessible, added comprehensive guide for
exposing only the webhook endpoint to Autotask via reverse proxy.

Covers 4 approaches:
1. Nginx + Let's Encrypt (recommended for production)
2. Cloudflare Tunnel (easiest, no firewall changes)
3. Caddy (automatic HTTPS)
4. Traefik (for Docker users)

Each includes:
- Complete configuration examples
- Setup steps
- Security recommendations (rate limiting, IP whitelisting)
- Testing procedures
- Troubleshooting guides

Security features:
- Only /api/webhooks/autotask exposed
- SSL/TLS encryption required
- Rate limiting (100 req/min, burst 20)
- Optional IP whitelisting for Autotask
- All other paths return 404

File: docs/WEBHOOK_REVERSE_PROXY.md
2026-01-24 17:14:55 -05:00

11 KiB

Webhook Reverse Proxy Configuration

Overview

Since your Pulse application is not publicly accessible, you need to expose only the webhook endpoint to Autotask. This guide covers secure reverse proxy configurations.


Architecture

Internet → Nginx (Public) → Pulse App (Internal)
           Port 443          Port 3100
           
Only /api/webhooks/autotask is exposed

Nginx Configuration

Create /etc/nginx/sites-available/pulse-webhooks:

# Rate limiting zone - prevents abuse
limit_req_zone $binary_remote_addr zone=webhook_limit:10m rate=100r/m;

# Upstream to Pulse application
upstream pulse_app {
    server localhost:3100;  # Adjust to your Pulse app port
}

server {
    listen 80;
    server_name webhooks.yourdomain.com;
    
    # Redirect HTTP to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name webhooks.yourdomain.com;
    
    # SSL Configuration
    ssl_certificate /etc/letsencrypt/live/webhooks.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/webhooks.yourdomain.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    
    # Security headers
    add_header X-Frame-Options "DENY" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    
    # Logging
    access_log /var/log/nginx/pulse-webhooks-access.log;
    error_log /var/log/nginx/pulse-webhooks-error.log;
    
    # Health check endpoint
    location = /api/webhooks/autotask {
        # Rate limiting - 100 requests per minute, burst of 20
        limit_req zone=webhook_limit burst=20 nodelay;
        
        # Only allow POST and GET
        limit_except GET POST {
            deny all;
        }
        
        # Optional: IP whitelist for Autotask
        # Get Autotask IP ranges from their documentation
        # allow 1.2.3.4/24;  # Autotask IP range
        # deny all;
        
        # Proxy to Pulse app
        proxy_pass http://pulse_app;
        proxy_http_version 1.1;
        
        # Headers
        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;
        
        # Timeouts
        proxy_connect_timeout 5s;
        proxy_send_timeout 10s;
        proxy_read_timeout 10s;
        
        # Buffer settings
        proxy_buffering off;
        proxy_request_buffering off;
    }
    
    # Block all other paths
    location / {
        return 404;
    }
}

Setup Steps

1. Install Nginx

sudo apt update
sudo apt install nginx

2. Install Certbot (for SSL)

sudo apt install certbot python3-certbot-nginx

3. Create Configuration

sudo nano /etc/nginx/sites-available/pulse-webhooks

Paste the configuration above, adjusting:

  • server_name to your domain
  • upstream pulse_app to your Pulse app address

4. Enable Site

sudo ln -s /etc/nginx/sites-available/pulse-webhooks /etc/nginx/sites-enabled/
sudo nginx -t  # Test configuration

5. Get SSL Certificate

sudo certbot --nginx -d webhooks.yourdomain.com

6. Start Nginx

sudo systemctl enable nginx
sudo systemctl restart nginx

7. Test

curl https://webhooks.yourdomain.com/api/webhooks/autotask
# Should return: {"status":"active"...}

Option 2: Cloudflare Tunnel (Zero Trust)

Architecture

Internet → Cloudflare → Cloudflared (Tunnel) → Pulse App
                        No open ports needed!

Setup Steps

1. Install Cloudflared

wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb

2. Login to Cloudflare

cloudflared tunnel login

3. Create Tunnel

cloudflared tunnel create pulse-webhooks
# Note the tunnel ID shown

4. Create Configuration

Create ~/.cloudflared/config.yml:

tunnel: <TUNNEL_ID>
credentials-file: /root/.cloudflared/<TUNNEL_ID>.json

ingress:
  # Webhook endpoint only
  - hostname: webhooks.yourdomain.com
    path: /api/webhooks/autotask
    service: http://localhost:3100
  
  # Health check
  - hostname: webhooks.yourdomain.com
    path: /api/webhooks/*
    service: http://localhost:3100
  
  # Catch-all (deny everything else)
  - service: http_status:404

5. Route DNS

cloudflared tunnel route dns pulse-webhooks webhooks.yourdomain.com

6. Run Tunnel

# Test first
cloudflared tunnel run pulse-webhooks

# If working, install as service
sudo cloudflared service install
sudo systemctl start cloudflared
sudo systemctl enable cloudflared

7. Test

curl https://webhooks.yourdomain.com/api/webhooks/autotask

Cloudflare Tunnel Benefits

  • No firewall changes needed
  • No open ports
  • Free tier available
  • Built-in DDoS protection
  • Automatic SSL/TLS
  • Easy to set up

Option 3: Caddy (Automatic HTTPS)

Caddy Configuration

Create /etc/caddy/Caddyfile:

webhooks.yourdomain.com {
    # Automatic HTTPS via Let's Encrypt
    
    # Rate limiting
    rate_limit {
        zone webhook {
            key {remote_host}
            events 100
            window 1m
        }
    }
    
    # Only allow webhook endpoint
    handle /api/webhooks/autotask {
        reverse_proxy localhost:3100
    }
    
    # Block everything else
    handle {
        respond 404
    }
    
    # Logging
    log {
        output file /var/log/caddy/pulse-webhooks.log
    }
}

Setup:

# Install Caddy
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

# Configure
sudo nano /etc/caddy/Caddyfile
# Paste configuration above

# Start
sudo systemctl restart caddy
sudo systemctl enable caddy

Option 4: Docker with Traefik

If you're using Docker Compose, add Traefik as a reverse proxy:

docker-compose.yml

services:
  # Your existing Pulse app
  app:
    image: pulse-app
    networks:
      - internal
    # No ports exposed!
    
  # Traefik reverse proxy
  traefik:
    image: traefik:v2.10
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.yml:/traefik.yml:ro
      - ./acme.json:/acme.json
    networks:
      - internal
    labels:
      - "traefik.enable=true"

networks:
  internal:
    driver: bridge

traefik.yml

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"

certificatesResolvers:
  letsencrypt:
    acme:
      email: admin@yourdomain.com
      storage: /acme.json
      httpChallenge:
        entryPoint: web

providers:
  docker:
    exposedByDefault: false

# Rate limiting
http:
  middlewares:
    webhook-ratelimit:
      rateLimit:
        average: 100
        burst: 20

Update Pulse App Labels

app:
  labels:
    - "traefik.enable=true"
    - "traefik.http.routers.webhooks.rule=Host(`webhooks.yourdomain.com`) && PathPrefix(`/api/webhooks/autotask`)"
    - "traefik.http.routers.webhooks.entrypoints=websecure"
    - "traefik.http.routers.webhooks.tls.certresolver=letsencrypt"
    - "traefik.http.routers.webhooks.middlewares=webhook-ratelimit"
    - "traefik.http.services.webhooks.loadbalancer.server.port=3100"

Security Recommendations

1. IP Whitelisting (If Possible)

Get Autotask's IP ranges and whitelist them:

Nginx:

# Inside location block
allow 1.2.3.4/24;  # Autotask IP range
deny all;

Cloudflare: Use Cloudflare Access rules to restrict by IP

2. Rate Limiting

All configurations above include rate limiting:

  • 100 requests per minute
  • Burst of 20 requests
  • Prevents abuse/DDoS

3. Monitoring

Monitor webhook endpoint:

# Nginx access log
tail -f /var/log/nginx/pulse-webhooks-access.log

# Check for suspicious activity
grep -v "200\|201" /var/log/nginx/pulse-webhooks-access.log | tail -20

4. Firewall Rules

# UFW example - only allow 80/443
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Testing Your Setup

1. Health Check

curl https://webhooks.yourdomain.com/api/webhooks/autotask
# Expected: {"status":"active"...}

2. Test Webhook POST

curl -X POST https://webhooks.yourdomain.com/api/webhooks/autotask \
  -H "Content-Type: application/json" \
  -d '{
    "eventId": "test_123",
    "eventType": "create",
    "entityType": "Tickets",
    "entityId": 99999,
    "eventTimestamp": "2026-01-24T10:00:00Z"
  }'

3. Verify Blocked Paths

curl https://webhooks.yourdomain.com/
# Expected: 404 Not Found

curl https://webhooks.yourdomain.com/admin
# Expected: 404 Not Found

4. Test Rate Limiting

# Send 150 requests quickly
for i in {1..150}; do
  curl -s https://webhooks.yourdomain.com/api/webhooks/autotask > /dev/null
done
# Should see 429 Too Many Requests after ~100 requests

Autotask Configuration

Once your endpoint is exposed:

  1. Go to Autotask → Admin → Webhooks
  2. Create webhook with URL: https://webhooks.yourdomain.com/api/webhooks/autotask
  3. Test webhook from Autotask UI
  4. Monitor logs to verify receipt

Troubleshooting

Webhook Not Received

Check 1: DNS Resolution

nslookup webhooks.yourdomain.com

Check 2: SSL Certificate

curl -v https://webhooks.yourdomain.com/api/webhooks/autotask
# Look for SSL handshake errors

Check 3: Proxy Logs

# Nginx
tail -f /var/log/nginx/pulse-webhooks-error.log

# Cloudflare Tunnel
sudo journalctl -u cloudflared -f

Check 4: Firewall

sudo ufw status
# Ensure 80/443 are allowed

502 Bad Gateway

Pulse app is not reachable:

# Check Pulse app is running
docker ps | grep pulse-app

# Check port
netstat -tlnp | grep 3100

# Test direct connection
curl http://localhost:3100/api/webhooks/autotask

For Production: Nginx + Let's Encrypt

  • Most control
  • Best performance
  • Industry standard

For Quick Setup: Cloudflare Tunnel

  • Fastest to set up
  • No firewall changes
  • Good for testing

For Docker Users: Traefik

  • Integrates with Docker
  • Automatic SSL
  • Container-native

Summary

You need to expose only /api/webhooks/autotask to the internet for Autotask to reach it. Use a reverse proxy to:

  1. Expose only the webhook endpoint
  2. Add SSL/TLS encryption
  3. Implement rate limiting
  4. Optional IP whitelisting
  5. Keep rest of application private

Choose the approach that fits your infrastructure best!