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:
parent
1f83456199
commit
80d8d40f06
1 changed files with 546 additions and 0 deletions
546
docs/WEBHOOK_REVERSE_PROXY.md
Normal file
546
docs/WEBHOOK_REVERSE_PROXY.md
Normal 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!
|
||||
Loading…
Add table
Add a link
Reference in a new issue