diff --git a/docs/WEBHOOK_REVERSE_PROXY.md b/docs/WEBHOOK_REVERSE_PROXY.md new file mode 100644 index 0000000..5c5d19f --- /dev/null +++ b/docs/WEBHOOK_REVERSE_PROXY.md @@ -0,0 +1,546 @@ +# 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. + +--- + +## Option 1: Nginx Reverse Proxy (Recommended) + +### **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`: + +```nginx +# 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** +```bash +sudo apt update +sudo apt install nginx +``` + +**2. Install Certbot (for SSL)** +```bash +sudo apt install certbot python3-certbot-nginx +``` + +**3. Create Configuration** +```bash +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** +```bash +sudo ln -s /etc/nginx/sites-available/pulse-webhooks /etc/nginx/sites-enabled/ +sudo nginx -t # Test configuration +``` + +**5. Get SSL Certificate** +```bash +sudo certbot --nginx -d webhooks.yourdomain.com +``` + +**6. Start Nginx** +```bash +sudo systemctl enable nginx +sudo systemctl restart nginx +``` + +**7. Test** +```bash +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** +```bash +wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb +sudo dpkg -i cloudflared-linux-amd64.deb +``` + +**2. Login to Cloudflare** +```bash +cloudflared tunnel login +``` + +**3. Create Tunnel** +```bash +cloudflared tunnel create pulse-webhooks +# Note the tunnel ID shown +``` + +**4. Create Configuration** + +Create `~/.cloudflared/config.yml`: + +```yaml +tunnel: +credentials-file: /root/.cloudflared/.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** +```bash +cloudflared tunnel route dns pulse-webhooks webhooks.yourdomain.com +``` + +**6. Run Tunnel** +```bash +# 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** +```bash +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`: + +```caddy +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:** +```bash +# 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** + +```yaml +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** + +```yaml +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** + +```yaml +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:** +```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: + +```bash +# 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** + +```bash +# 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** +```bash +curl https://webhooks.yourdomain.com/api/webhooks/autotask +# Expected: {"status":"active"...} +``` + +### **2. Test Webhook POST** +```bash +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** +```bash +curl https://webhooks.yourdomain.com/ +# Expected: 404 Not Found + +curl https://webhooks.yourdomain.com/admin +# Expected: 404 Not Found +``` + +### **4. Test Rate Limiting** +```bash +# 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** +```bash +nslookup webhooks.yourdomain.com +``` + +**Check 2: SSL Certificate** +```bash +curl -v https://webhooks.yourdomain.com/api/webhooks/autotask +# Look for SSL handshake errors +``` + +**Check 3: Proxy Logs** +```bash +# Nginx +tail -f /var/log/nginx/pulse-webhooks-error.log + +# Cloudflare Tunnel +sudo journalctl -u cloudflared -f +``` + +**Check 4: Firewall** +```bash +sudo ufw status +# Ensure 80/443 are allowed +``` + +### **502 Bad Gateway** + +Pulse app is not reachable: +```bash +# 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 +``` + +--- + +## Recommended Approach + +**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!