# 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!