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
This commit is contained in:
parent
8e83347138
commit
a093f8787c
7 changed files with 672 additions and 7 deletions
|
|
@ -13,10 +13,16 @@ import { AutotaskWebhookPayload } from '@/lib/types/webhook';
|
|||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
// Extract source IP and user agent for logging
|
||||
const sourceIp = request.headers.get('x-forwarded-for')?.split(',')[0].trim()
|
||||
|| request.headers.get('x-real-ip')
|
||||
|| 'unknown';
|
||||
const userAgent = request.headers.get('user-agent') || 'unknown';
|
||||
|
||||
// Parse webhook payload
|
||||
const payload: AutotaskWebhookPayload = await request.json();
|
||||
|
||||
console.log(`[WEBHOOK API] Received ${payload.eventType} event for ${payload.entityType} #${payload.entityId}`);
|
||||
console.log(`[WEBHOOK API] Received ${payload.eventType} event for ${payload.entityType} #${payload.entityId} from IP: ${sourceIp}`);
|
||||
|
||||
// Validate required fields
|
||||
if (!payload.eventId || !payload.eventType || !payload.entityType || !payload.entityId) {
|
||||
|
|
@ -29,7 +35,7 @@ export async function POST(request: NextRequest) {
|
|||
// Process the webhook asynchronously
|
||||
// Note: We return 200 immediately to Autotask, then process in background
|
||||
// This prevents timeouts for slow processing
|
||||
const result = await webhookService.processWebhook(payload);
|
||||
const result = await webhookService.processWebhook(payload, sourceIp, userAgent);
|
||||
|
||||
if (result.success) {
|
||||
return NextResponse.json({
|
||||
|
|
|
|||
77
app/api/webhooks/ips/route.ts
Normal file
77
app/api/webhooks/ips/route.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* Webhook Source IPs API
|
||||
* View unique source IPs from webhook logs for whitelisting
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { postgresClient } from '@/lib/services/postgres-client';
|
||||
|
||||
/**
|
||||
* GET /api/webhooks/ips
|
||||
* Get unique source IPs from webhook logs
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const hours = parseInt(searchParams.get('hours') || '168'); // Default 7 days
|
||||
const entityType = searchParams.get('entityType') || undefined;
|
||||
|
||||
// Build query to get unique IPs with counts
|
||||
let query = `
|
||||
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,
|
||||
COUNT(*) FILTER (WHERE status = 'failed') as failed_count,
|
||||
array_agg(DISTINCT entity_type) as entity_types
|
||||
FROM webhook_logs
|
||||
WHERE received_at >= NOW() - INTERVAL '${hours} hours'
|
||||
AND source_ip IS NOT NULL
|
||||
`;
|
||||
|
||||
const params: any[] = [];
|
||||
|
||||
if (entityType) {
|
||||
query += ` AND entity_type = $1`;
|
||||
params.push(entityType);
|
||||
}
|
||||
|
||||
query += `
|
||||
GROUP BY source_ip, user_agent
|
||||
ORDER BY request_count DESC
|
||||
`;
|
||||
|
||||
const result = await postgresClient.query(query, params);
|
||||
|
||||
// Format the results
|
||||
const ips = result.rows.map((row: any) => ({
|
||||
ip: row.source_ip,
|
||||
userAgent: row.user_agent,
|
||||
requestCount: parseInt(row.request_count),
|
||||
successfulCount: parseInt(row.successful_count),
|
||||
failedCount: parseInt(row.failed_count),
|
||||
firstSeen: row.first_seen,
|
||||
lastSeen: row.last_seen,
|
||||
entityTypes: row.entity_types,
|
||||
}));
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
ips,
|
||||
count: ips.length,
|
||||
period: `${hours} hours`,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error('[WEBHOOK IPS API] Error fetching IPs:', errorMessage);
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch webhook IPs', details: errorMessage },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue