wulf-pulse/docs/WEBHOOK_IP_WHITELISTING.md
root a093f8787c feat: add IP address logging to webhooks for whitelisting
Implements comprehensive IP logging for webhook requests to enable
IP whitelisting and security monitoring.

Features:
- Capture source IP from webhook requests (x-forwarded-for, x-real-ip)
- Capture user agent for identification
- Store in webhook_logs table
- New API endpoint: GET /api/webhooks/ips
- View unique IPs with request counts and statistics
- Identify Autotask IPs for whitelisting

Database Changes:
- Added source_ip column (VARCHAR 45) to webhook_logs
- Added user_agent column (TEXT) to webhook_logs
- Added index on source_ip for efficient queries
- Migration 005 for existing installations

API Endpoints:
- GET /api/webhooks/ips?hours=168&entityType=Tickets
  Returns unique IPs with:
  * Request counts (total, successful, failed)
  * First/last seen timestamps
  * Entity types accessed
  * User agent strings

Use Cases:
1. Identify Autotask webhook IPs
2. Configure IP whitelist in nginx/Pangolin/Cloudflare
3. Monitor for unauthorized webhook attempts
4. Audit webhook sources
5. Detect IP changes from Autotask

Security Benefits:
- Enable IP whitelisting for webhook endpoint
- Block unauthorized webhook attempts
- Monitor for suspicious activity
- Audit trail of webhook sources

Documentation:
- Complete IP whitelisting guide (WEBHOOK_IP_WHITELISTING.md)
- Configuration examples for nginx, Pangolin, Cloudflare
- Monitoring queries and best practices
- Troubleshooting guide

Files Modified:
- migrations/004_webhook_support.sql - Added IP columns
- migrations/005_add_webhook_ip_logging.sql - Migration for existing installs
- lib/types/webhook.ts - Added IP fields to WebhookLog
- lib/services/webhook-service.ts - Capture and log IPs
- app/api/webhooks/autotask/route.ts - Extract IP from headers
- app/api/webhooks/ips/route.ts - New IP viewing endpoint
- docs/WEBHOOK_IP_WHITELISTING.md - Complete guide

Next Steps:
1. Run migration (004 for new, 005 for existing)
2. Deploy updated code
3. Receive webhooks from Autotask
4. View IPs via /api/webhooks/ips
5. Configure IP whitelist in proxy/tunnel
2026-01-24 17:28:56 -05:00

12 KiB

Webhook IP Whitelisting Guide

Overview

This guide explains how to identify Autotask's webhook source IPs and configure IP whitelisting to secure your webhook endpoint.


Why IP Whitelisting?

Benefits:

  • Only allow webhooks from Autotask's servers
  • Block unauthorized webhook attempts
  • Additional security layer beyond HTTPS
  • Prevent abuse and DDoS attacks

When to Use:

  • Production environments
  • High-security requirements
  • After identifying Autotask's IP ranges

Step 1: Identify Autotask IPs

After receiving a few webhooks from Autotask, check the logged IPs:

Via API:

curl https://your-domain.com/api/webhooks/ips?hours=168

Response:

{
  "success": true,
  "ips": [
    {
      "ip": "52.1.2.3",
      "userAgent": "Autotask-Webhook/1.0",
      "requestCount": 150,
      "successfulCount": 148,
      "failedCount": 2,
      "firstSeen": "2026-01-20T10:00:00Z",
      "lastSeen": "2026-01-24T15:00:00Z",
      "entityTypes": ["Tickets", "Companies", "Tasks"]
    },
    {
      "ip": "52.5.6.7",
      "userAgent": "Autotask-Webhook/1.0",
      "requestCount": 75,
      "successfulCount": 75,
      "failedCount": 0,
      "firstSeen": "2026-01-22T08:00:00Z",
      "lastSeen": "2026-01-24T14:30:00Z",
      "entityTypes": ["TimeEntries", "Projects"]
    }
  ],
  "count": 2,
  "period": "168 hours"
}

Via Database:

-- Get unique IPs with request counts
SELECT 
  source_ip,
  user_agent,
  COUNT(*) as request_count,
  MIN(received_at) as first_seen,
  MAX(received_at) as last_seen,
  COUNT(*) FILTER (WHERE status = 'processed') as successful,
  COUNT(*) FILTER (WHERE status = 'failed') as failed
FROM webhook_logs
WHERE received_at >= NOW() - INTERVAL '7 days'
  AND source_ip IS NOT NULL
GROUP BY source_ip, user_agent
ORDER BY request_count DESC;

Identify Autotask IPs:

  • Look for IPs with Autotask-Webhook user agent
  • High request counts (if you have active webhooks)
  • Multiple entity types
  • Consistent activity pattern

Method 2: Contact Autotask Support

Request official IP ranges from Autotask:

  • Open support ticket
  • Ask for "Webhook source IP ranges"
  • They may provide CIDR blocks (e.g., 52.1.0.0/16)

Method 3: Check Autotask Documentation

Some Autotask zones publish their IP ranges:

  • Check Autotask developer documentation
  • Look for "Webhook IP ranges" or "API IP addresses"
  • May vary by zone (ww1, ww5, ww15, etc.)

Step 2: Configure IP Whitelist

Option A: Nginx

Edit your nginx configuration:

server {
    listen 443 ssl http2;
    server_name webhooks.yourdomain.com;
    
    # ... SSL configuration ...
    
    location = /api/webhooks/autotask {
        # IP Whitelist - Autotask webhook IPs
        allow 52.1.2.3;      # Autotask IP 1
        allow 52.5.6.7;      # Autotask IP 2
        allow 52.10.0.0/16;  # Autotask IP range (CIDR)
        deny all;            # Block everything else
        
        # Rate limiting
        limit_req zone=webhook_limit burst=20 nodelay;
        
        # Proxy to Pulse app
        proxy_pass http://pulse_app;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Test and reload:

sudo nginx -t
sudo systemctl reload nginx

Option B: Pangolin

Update your Pangolin configuration:

# /etc/pangolin/config.yml

name: pulse-webhooks

ingress:
  - hostname: webhooks.yourdomain.com
    path: /api/webhooks/autotask
    service: http://localhost:3100

security:
  # IP Whitelist
  ipWhitelist:
    - "52.1.2.3"        # Autotask IP 1
    - "52.5.6.7"        # Autotask IP 2
    - "52.10.0.0/16"    # Autotask IP range
  
  # Rate limiting
  rateLimit:
    enabled: true
    requestsPerMinute: 100
    burstSize: 20

Restart tunnel:

sudo systemctl restart pangolin-tunnel

Option C: Cloudflare Tunnel

Use Cloudflare Access rules:

  1. Go to Cloudflare Dashboard → Zero Trust → Access → Applications
  2. Create Application:
    • Name: Pulse Webhooks
    • Domain: webhooks.yourdomain.com
    • Path: /api/webhooks/autotask
  3. Add Policy:
    • Name: Autotask IPs Only
    • Action: Allow
    • Rule: IP ranges
    • Values: 52.1.2.3, 52.5.6.7, 52.10.0.0/16
  4. Save

Option D: Firewall (UFW)

If using UFW on the server:

# Allow specific IPs to port 443
sudo ufw allow from 52.1.2.3 to any port 443
sudo ufw allow from 52.5.6.7 to any port 443
sudo ufw allow from 52.10.0.0/16 to any port 443

# Deny all other traffic to port 443 (if not already blocked)
# Be careful with this - ensure you have other access methods!
# sudo ufw deny 443

# Check rules
sudo ufw status numbered

Warning: Be careful with firewall rules. Ensure you don't lock yourself out!


Step 3: Test IP Whitelist

1. Test from Allowed IP (Autotask)

Trigger a webhook from Autotask:

  • Create a test ticket or company
  • Check webhook logs to verify it was received
# Check recent webhooks
curl https://your-domain.com/api/webhooks/logs?limit=5

2. Test from Blocked IP

From a different IP (your computer):

curl -X POST https://webhooks.yourdomain.com/api/webhooks/autotask \
  -H "Content-Type: application/json" \
  -d '{"eventId":"test","eventType":"create","entityType":"Tickets","entityId":1}'

Expected result:

  • Nginx: 403 Forbidden
  • Pangolin/Cloudflare: Connection refused or 403
  • Firewall: Connection timeout

3. Verify Logs

Check that only Autotask IPs are getting through:

-- Recent webhook IPs
SELECT source_ip, COUNT(*) as count
FROM webhook_logs
WHERE received_at >= NOW() - INTERVAL '1 hour'
GROUP BY source_ip;

Should only show Autotask IPs.


Monitoring IP Changes

Set Up Alerts for New IPs

Create a monitoring query to detect new source IPs:

-- Find IPs seen in last 24 hours that weren't seen before
SELECT DISTINCT wl.source_ip, wl.user_agent, MIN(wl.received_at) as first_seen
FROM webhook_logs wl
WHERE wl.received_at >= NOW() - INTERVAL '24 hours'
  AND wl.source_ip NOT IN (
    SELECT DISTINCT source_ip 
    FROM webhook_logs 
    WHERE received_at < NOW() - INTERVAL '24 hours'
      AND received_at >= NOW() - INTERVAL '30 days'
  )
GROUP BY wl.source_ip, wl.user_agent;

Regular IP Audit

Run weekly to check for IP changes:

# Get IPs from last 7 days
curl https://your-domain.com/api/webhooks/ips?hours=168 > webhook-ips-$(date +%Y%m%d).json

# Compare with previous week
diff webhook-ips-20260117.json webhook-ips-20260124.json

If new IPs appear:

  1. Verify they're legitimate Autotask IPs
  2. Check user agent matches Autotask-Webhook
  3. Update whitelist configuration
  4. Reload/restart proxy or tunnel

Troubleshooting

Webhooks Stopped Working After Whitelisting

Check 1: Verify Autotask IP

-- Check recent failed webhooks
SELECT source_ip, user_agent, error_message, received_at
FROM webhook_logs
WHERE status = 'failed'
  AND received_at >= NOW() - INTERVAL '1 hour'
ORDER BY received_at DESC;

If you see legitimate Autotask IPs being blocked, add them to whitelist.

Check 2: Proxy Logs

Nginx:

tail -f /var/log/nginx/pulse-webhooks-error.log | grep 403

Pangolin:

sudo journalctl -u pangolin-tunnel -f | grep denied

Check 3: Test Without Whitelist

Temporarily disable IP whitelist to confirm that's the issue:

Nginx: Comment out allow/deny lines and reload Pangolin: Remove ipWhitelist section and restart

If webhooks work without whitelist, the issue is IP configuration.

Autotask Changed IPs

Autotask may change their webhook source IPs:

  1. Check webhook logs for new IPs
  2. Verify they're legitimate (user agent, request pattern)
  3. Update whitelist with new IPs
  4. Keep old IPs for a few days (in case of rollback)
  5. Remove old IPs after confirming they're no longer used

Getting Blocked Yourself

If you accidentally block legitimate traffic:

Quick Fix (Nginx):

# Edit config to remove IP whitelist
sudo nano /etc/nginx/sites-available/pulse-webhooks

# Comment out the deny rules
# deny all;

# Reload
sudo systemctl reload nginx

Quick Fix (Pangolin):

# Edit config
sudo nano /etc/pangolin/config.yml

# Remove or comment ipWhitelist section

# Restart
sudo systemctl restart pangolin-tunnel

Best Practices

1. Use CIDR Blocks When Possible

Instead of individual IPs:

# Better
allow 52.10.0.0/16;

# Less flexible
allow 52.10.1.1;
allow 52.10.1.2;
allow 52.10.1.3;

2. Document Your Whitelist

Keep a record of:

  • Which IPs are whitelisted
  • When they were added
  • Source of information (Autotask support, logs, etc.)
  • Last verified date

Example:

# IP Whitelist Documentation
# Last updated: 2026-01-24

ipWhitelist:
  - "52.1.2.3"        # Added: 2026-01-20, Source: Webhook logs
  - "52.5.6.7"        # Added: 2026-01-22, Source: Webhook logs  
  - "52.10.0.0/16"    # Added: 2026-01-24, Source: Autotask support ticket #12345

3. Monitor Regularly

  • Weekly: Check for new IPs in logs
  • Monthly: Audit whitelist against active IPs
  • After Autotask updates: Verify webhooks still work

4. Combine with Other Security

IP whitelisting is one layer. Also use:

  • HTTPS/TLS encryption
  • Rate limiting
  • Path-based access control
  • Request validation
  • Monitoring and alerting

5. Have a Rollback Plan

Keep configuration backups:

# Backup before changes
sudo cp /etc/nginx/sites-available/pulse-webhooks \
       /etc/nginx/sites-available/pulse-webhooks.backup-$(date +%Y%m%d)

# Or for Pangolin
sudo cp /etc/pangolin/config.yml \
       /etc/pangolin/config.yml.backup-$(date +%Y%m%d)

API Reference

GET /api/webhooks/ips

Get unique source IPs from webhook logs.

Parameters:

  • hours (optional): Number of hours to look back (default: 168 = 7 days)
  • entityType (optional): Filter by entity type

Example:

# Last 7 days
curl https://your-domain.com/api/webhooks/ips?hours=168

# Last 24 hours, tickets only
curl https://your-domain.com/api/webhooks/ips?hours=24&entityType=Tickets

Response:

{
  "success": true,
  "ips": [
    {
      "ip": "52.1.2.3",
      "userAgent": "Autotask-Webhook/1.0",
      "requestCount": 150,
      "successfulCount": 148,
      "failedCount": 2,
      "firstSeen": "2026-01-20T10:00:00Z",
      "lastSeen": "2026-01-24T15:00:00Z",
      "entityTypes": ["Tickets", "Companies"]
    }
  ],
  "count": 1,
  "period": "168 hours"
}

Database Queries

Get All Unique IPs

SELECT DISTINCT source_ip, user_agent
FROM webhook_logs
WHERE source_ip IS NOT NULL
ORDER BY source_ip;

Get IP Activity Summary

SELECT 
  source_ip,
  COUNT(*) as total_requests,
  COUNT(DISTINCT entity_type) as entity_types_count,
  MIN(received_at) as first_seen,
  MAX(received_at) as last_seen,
  ROUND(AVG(processing_time_ms), 2) as avg_processing_ms
FROM webhook_logs
WHERE source_ip IS NOT NULL
GROUP BY source_ip
ORDER BY total_requests DESC;

Detect Suspicious IPs

-- IPs with high failure rates
SELECT 
  source_ip,
  user_agent,
  COUNT(*) as total,
  COUNT(*) FILTER (WHERE status = 'failed') as failed,
  ROUND(100.0 * COUNT(*) FILTER (WHERE status = 'failed') / COUNT(*), 2) as failure_rate
FROM webhook_logs
WHERE source_ip IS NOT NULL
  AND received_at >= NOW() - INTERVAL '24 hours'
GROUP BY source_ip, user_agent
HAVING COUNT(*) FILTER (WHERE status = 'failed') > 5
ORDER BY failure_rate DESC;

Summary

Steps to Enable IP Whitelisting:

  1. Run migration to add IP logging columns
  2. Deploy updated code with IP capture
  3. Receive webhooks from Autotask (let it run for a day)
  4. Identify Autotask IPs via /api/webhooks/ips endpoint
  5. Configure whitelist in your proxy/tunnel
  6. Test that webhooks still work
  7. Monitor for new IPs regularly

Result:

  • Only Autotask's IPs can send webhooks
  • Unauthorized requests are blocked
  • Additional security layer for your webhook endpoint
  • Logged IP data for audit and troubleshooting