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
This commit is contained in:
root 2026-01-24 17:14:55 -05:00
parent 1f83456199
commit 80d8d40f06

View file

@ -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: <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**
```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!