UNPKG

firewalla-mcp-server

Version:

Model Context Protocol (MCP) server for Firewalla MSP API - Provides real-time network monitoring, security analysis, and firewall management through 28 specialized tools compatible with any MCP client

532 lines 18.8 kB
/** * @fileoverview Firewalla API Client for MSP Integration * * Provides comprehensive access to Firewalla MSP APIs with enterprise-grade features: * - **Authentication**: Token-based MSP API authentication with error handling * - **Caching**: Intelligent response caching with configurable TTL * - **Rate Limiting**: Built-in protection against API rate limits * - **Error Handling**: Comprehensive error mapping and recovery strategies * - **Optimization**: Automatic response optimization for token efficiency * - **Monitoring**: Request/response logging and performance tracking * * The client supports all major Firewalla data types including alarms, flows, * devices, rules, bandwidth analytics, and advanced search capabilities with * cross-reference correlation and trend analysis. * * @version 1.0.0 * @author Alex Mittell <mittell@me.com> (https://github.com/amittell) * @since 2025-06-21 */ import { FirewallaConfig, Alarm, Flow, Device, BandwidthUsage, NetworkRule, TargetList, Box, SearchResult, SearchQuery, SearchOptions, CrossReferenceResult, Trend, SimpleStats, Statistics } from '../types.js'; import { type GeographicCacheStats } from '../utils/geographic.js'; /** * Firewalla API Client for MSP Integration * * Main client class providing authenticated access to Firewalla MSP APIs. * Handles authentication, caching, rate limiting, error handling, and response * optimization for efficient integration with Claude through the MCP protocol. * * Features: * - Automatic token-based authentication with the MSP API * - Intelligent caching with configurable TTL policies * - Built-in rate limiting and retry mechanisms * - Comprehensive error handling with meaningful error messages * - Response optimization for MCP protocol constraints * - Request/response logging for debugging and monitoring * * @example * ```typescript * const config = getConfig(); * const client = new FirewallaClient(config); * * // Get recent alarms * const alarms = await client.getActiveAlarms({ limit: 50 }); * * // Search for high-severity flows * const flows = await client.searchFlows({ * query: 'severity:high AND bytes:>1000000', * limit: 100 * }); * ``` * * @class * @public */ export declare class FirewallaClient { private config; /** @private Axios instance configured for Firewalla MSP API access */ private api; /** @private In-memory cache for API responses with TTL management */ private cache; /** @private Geographic cache for IP geolocation lookups */ private geoCache; /** * Creates a new Firewalla API client instance * * @param config - Configuration object containing MSP credentials and settings * @throws {Error} If configuration is invalid or authentication fails */ constructor(config: FirewallaConfig); /** * Sets up Axios request and response interceptors for logging and error handling * * Configures interceptors to: * - Log all API requests and responses for debugging * - Transform HTTP error codes into meaningful error messages * - Handle authentication and authorization failures * - Provide specific guidance for common error scenarios * * @private * @returns {void} */ private setupInterceptors; /** * Generates a unique cache key for API requests with enhanced collision prevention * * Creates a cache key that includes the box ID, endpoint, method, and sorted parameters * to ensure uniqueness across different boxes and API calls. * * @param endpoint - API endpoint path * @param params - Optional request parameters * @param method - HTTP method (default: 'GET') * @returns Unique cache key string with collision prevention * @private */ private getCacheKey; /** * Retrieves data from cache if available and not expired * * @template T - The expected return type * @param key - Cache key to look up * @returns Cached data if available and valid, otherwise null * @private */ private getFromCache; private setCache; private sanitizeInput; /** * Filter parameters for GET requests to /v2/* endpoints to only include allowed scalar fields * Fixes issue where complex objects get serialized as [object Object] causing "Bad Request" errors */ private filterParametersForDataEndpoints; private request; /** * Retrieves active security alarms from the Firewalla system * * Fetches current security alerts, alarms, and notifications with support for * advanced filtering, grouping, and pagination. Results are automatically * optimized for token efficiency while preserving essential security context. * * @param query - Optional search query for filtering alarms * @param groupBy - Optional field to group results by (e.g., 'type', 'box') * @param sortBy - Sort order specification (default: 'timestamp:desc') * @param limit - Maximum number of results to return (required for pagination) * @param cursor - Pagination cursor from previous response * @returns Promise resolving to paginated alarm results with metadata * * @example * ```typescript * // Get recent high-severity alarms * const highSeverityAlarms = await client.getActiveAlarms( * 'severity:high', * undefined, * 'timestamp:desc', * 50 * ); * * // Get alarms grouped by type * const groupedAlarms = await client.getActiveAlarms( * undefined, * 'type', * 'timestamp:desc', * 100 * ); * ``` * * @public * @optimizeResponse('alarms') - Automatically optimizes response for token efficiency */ getActiveAlarms(query?: string, groupBy?: string, sortBy?: string, limit?: number, cursor?: string, force_refresh?: boolean): Promise<{ count: number; results: Alarm[]; next_cursor?: string; }>; getFlowData(query?: string, groupBy?: string, sortBy?: string, limit?: number, cursor?: string): Promise<{ count: number; results: Flow[]; next_cursor?: string; }>; getDeviceStatus(deviceId?: string, includeOffline?: boolean, limit?: number, cursor?: string): Promise<{ count: number; results: Device[]; next_cursor?: string; total_count: number; has_more: boolean; }>; getOfflineDevices(sortByLastSeen?: boolean): Promise<{ count: number; results: Device[]; next_cursor?: string; }>; private transformDevice; /** * Get top bandwidth consuming devices on the network * * @param period - Time period for analysis ('1h', '24h', '7d', '30d') * @param top - Maximum number of devices to return (default: 10, max: 500) * @returns Promise resolving to bandwidth usage data with device details * @throws {Error} If period is invalid or API request fails * @example * ```typescript * const usage = await client.getBandwidthUsage('24h', 50); * usage.results.forEach(device => { * console.log(`${device.name}: ${device.total_bytes} bytes`); * }); * ``` */ getBandwidthUsage(period: string, top?: number): Promise<{ count: number; results: BandwidthUsage[]; next_cursor?: string; }>; getNetworkRules(query?: string, limit?: number): Promise<{ count: number; results: NetworkRule[]; next_cursor?: string; }>; getTargetLists(listType?: string, limit?: number): Promise<{ count: number; results: TargetList[]; next_cursor?: string; }>; /** * Get a specific target list by ID */ getSpecificTargetList(id: string): Promise<TargetList>; /** * Create a new target list */ createTargetList(targetListData: { name: string; owner: string; targets: string[]; category?: string; notes?: string; }): Promise<TargetList>; /** * Update an existing target list */ updateTargetList(id: string, updateData: { name?: string; targets?: string[]; category?: string; notes?: string; }): Promise<TargetList>; /** * Delete a target list */ deleteTargetList(id: string): Promise<{ success: boolean; message: string; }>; getFirewallSummary(): Promise<{ status: string; uptime: number; cpu_usage: number; memory_usage: number; active_connections: number; blocked_attempts: number; last_updated: string; }>; getSecurityMetrics(): Promise<{ total_alarms: number; active_alarms: number; blocked_connections: number; suspicious_activities: number; threat_level: 'low' | 'medium' | 'high' | 'critical'; last_threat_detected: string; }>; getNetworkTopology(): Promise<{ subnets: Array<{ id: string; name: string; cidr: string; device_count: number; }>; connections: Array<{ source: string; destination: string; type: string; bandwidth: number; }>; }>; getRecentThreats(hours?: number): Promise<Array<{ timestamp: string; type: string; source_ip: string; destination_ip: string; action_taken: string; severity: string; }>>; getBoxes(groupId?: string): Promise<{ count: number; results: Box[]; next_cursor?: string; }>; getSpecificAlarm(alarmId: string, gid?: string): Promise<{ count: number; results: Alarm[]; next_cursor?: string; }>; deleteAlarm(alarmId: string, gid?: string): Promise<any>; getSimpleStatistics(): Promise<{ count: number; results: SimpleStats[]; next_cursor?: string; }>; getStatisticsByRegion(): Promise<{ count: number; results: Statistics[]; next_cursor?: string; }>; getFlowTrends(period?: '1h' | '24h' | '7d' | '30d', interval?: number): Promise<{ count: number; results: Trend[]; next_cursor?: string; }>; getAlarmTrends(period?: '1h' | '24h' | '7d' | '30d'): Promise<{ count: number; results: Trend[]; next_cursor?: string; }>; getRuleTrends(period?: '1h' | '24h' | '7d' | '30d'): Promise<{ count: number; results: Trend[]; next_cursor?: string; }>; getStatisticsByBox(): Promise<{ count: number; results: Statistics[]; next_cursor?: string; }>; clearCache(): void; getCacheStats(): { size: number; keys: string[]; }; /** * Get geographic cache statistics */ getGeographicCacheStats(): GeographicCacheStats; /** * Clear geographic cache */ clearGeographicCache(): void; /** * Get geographic data for an IP address with caching * @param ip - IP address to geolocate * @returns GeographicData object or null if lookup fails or IP is private */ private getGeographicData; /** * Set field value in object using dot notation * @param obj - Object to modify * @param fieldPath - Dot notation path (e.g., 'destination.geo') * @param value - Value to set */ private setFieldValue; /** * Generic method to enrich object with geographic data based on IP paths * @param obj - Object to enrich * @param ipPaths - Array of dot notation paths to IP fields (optional, defaults to common flow/alarm paths) * @returns Enriched object with geographic data */ private enrichWithGeographicData; /** * Backward compatibility method for alarm enrichment * @param alarm - Alarm object to enrich * @returns Enriched alarm with geographic data */ enrichAlarmWithGeographicData(alarm: any): any; /** * Advanced search for network flows with complex query syntax * Supports: severity:high AND source_ip:192.168.* NOT resolved:true */ searchFlows(searchQuery: SearchQuery, options?: SearchOptions): Promise<SearchResult<Flow>>; /** * Advanced search for security alarms with severity, time, and IP filters */ searchAlarms(searchQuery: SearchQuery, options?: SearchOptions): Promise<SearchResult<Alarm>>; /** * Advanced search for firewall rules with target, action, and status filters */ searchRules(searchQuery: SearchQuery, options?: SearchOptions): Promise<SearchResult<NetworkRule>>; /** * Advanced search for network devices with network, status, and usage filters */ searchDevices(searchQuery: SearchQuery, options?: SearchOptions): Promise<SearchResult<Device>>; /** * Advanced search for target lists with category and ownership filters */ searchTargetLists(searchQuery: SearchQuery, options?: SearchOptions): Promise<SearchResult<TargetList>>; /** * Multi-entity searches with correlation across different data types * Enhanced with proper entity type handling */ searchCrossReference(primaryQuery: SearchQuery, secondaryQueries: Record<string, SearchQuery>, correlationField: string, options?: SearchOptions, primaryEntityType?: 'flows' | 'alarms' | 'rules' | 'devices'): Promise<CrossReferenceResult>; /** * Execute search based on entity type */ private executeSearchByEntityType; /** * Infer entity type from query name (fallback for backward compatibility) */ private inferEntityTypeFromName; /** * Get overview statistics and counts of network rules by category */ getNetworkRulesSummary(activeOnly?: boolean, ruleType?: string): Promise<{ count: number; results: any[]; next_cursor?: string; }>; /** * Get rules with highest hit counts for traffic analysis */ getMostActiveRules(limit?: number, minHits?: number, ruleType?: string): Promise<{ count: number; results: NetworkRule[]; next_cursor?: string; }>; /** * Get recently created or modified firewall rules */ getRecentRules(hours?: number, includeModified?: boolean, limit?: number, ruleType?: string): Promise<{ count: number; results: NetworkRule[]; next_cursor?: string; }>; /** * Temporarily disable a specific firewall rule for a specified duration * * @param ruleId - The unique identifier of the rule to pause * @param durationMinutes - Duration in minutes to pause the rule (default: 60, max: 1440) * @returns Promise resolving to operation result with success status and message * @throws {Error} If rule ID is invalid or API request fails * @example * ```typescript * const result = await client.pauseRule('rule-123', 30); * console.log(result.message); // "Rule paused successfully" * ``` */ pauseRule(ruleId: string, durationMinutes?: number): Promise<{ success: boolean; message: string; }>; /** * Resume a previously paused firewall rule, restoring it to active state * * @param ruleId - The unique identifier of the rule to resume * @returns Promise resolving to operation result with success status and message * @throws {Error} If rule ID is invalid or rule is not paused * @example * ```typescript * const result = await client.resumeRule('rule-123'); * console.log(result.message); // "Rule resumed successfully" * ``` */ resumeRule(ruleId: string): Promise<{ success: boolean; message: string; }>; /** * Helper method to build OR queries for array-based geographic filters * * @param fieldName - The field name for the query (e.g., 'country', 'region') * @param values - Array of values to include in the OR query * @returns Query string or null if values array is empty * @private */ private buildArrayFilterQuery; /** * Helper method to add box.id qualifier to search queries * * @param query - Existing query string (optional) * @returns Query string with box.id filter added, or just box.id filter if no query * @private */ private addBoxFilter; /** * Build geographic query string from filters for Firewalla API * * Converts geographic filter objects into API-compatible query syntax. * Supports countries, continents, regions, cities, ASNs, hosting providers, * and boolean exclusion filters. * * @param filters - Geographic filter configuration * @returns Query string compatible with Firewalla API */ buildGeoQuery(filters: { countries?: string[]; continents?: string[]; regions?: string[]; cities?: string[]; asns?: string[]; hosting_providers?: string[]; exclude_cloud?: boolean; exclude_vpn?: boolean; min_risk_score?: number; high_risk_countries?: boolean; exclude_known_providers?: boolean; threat_analysis?: boolean; }): string; /** * Extract field value from object using dot notation */ private extractFieldValue; /** * Extract and validate string values with optional allowed values */ private extractValidString; /** * Public method for making raw API calls * Used by management tools for bulk operations */ makeApiCall(method: 'get' | 'post' | 'patch' | 'delete', endpoint: string, data?: any): Promise<any>; /** * Get flow insights with category-based analysis * This provides category breakdowns and bandwidth analysis for networks with high flow volumes */ getFlowInsights(period?: '1h' | '24h' | '7d' | '30d', options?: { categories?: string[]; includeBlocked?: boolean; }): Promise<{ period: string; categoryBreakdown: Array<{ category: string; count: number; bytes: number; topDomains: Array<{ domain: string; count: number; bytes: number; }>; }>; topDevices: Array<{ device: string; totalBytes: number; categories: Array<{ category: string; bytes: number; }>; }>; blockedSummary?: { totalBlocked: number; byCategory: Array<{ category: string; count: number; }>; }; }>; } //# sourceMappingURL=client.d.ts.map