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