Track tcp/udp dports via deny_port_hits, expose aggregate and per-IP ports in UI; install-link re-run refreshes nft rules. Co-authored-by: Cursor <cursoragent@cursor.com>
631 lines
16 KiB
YAML
631 lines
16 KiB
YAML
openapi: 3.0.3
|
||
info:
|
||
title: EvoFirewall API
|
||
version: 0.2.0
|
||
description: |
|
||
Centralized firewall control plane.
|
||
|
||
**HTTP SoT:** this OpenAPI document.
|
||
**Runtime validation:** Zod schemas in `@evofw/shared` (authoritative for request bodies).
|
||
servers:
|
||
- url: http://localhost:8080
|
||
description: Local API
|
||
security: []
|
||
paths:
|
||
/health:
|
||
get:
|
||
summary: Liveness
|
||
tags: [system]
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/api/v1/dashboard:
|
||
get:
|
||
summary: Dashboard KPI
|
||
tags: [ops]
|
||
security: [{ bearerAuth: [] }]
|
||
responses:
|
||
'200':
|
||
description: Stats
|
||
|
||
/api/v1/install-context:
|
||
get:
|
||
summary: Public install base URL + enroll seed hint
|
||
tags: [ops]
|
||
security: [{ bearerAuth: [] }]
|
||
responses:
|
||
'200':
|
||
description: Install context
|
||
|
||
/api/v1/install-links:
|
||
get:
|
||
summary: List install links
|
||
tags: [agents]
|
||
security: [{ bearerAuth: [] }]
|
||
responses:
|
||
'200':
|
||
description: Links
|
||
post:
|
||
summary: Create install link + invited agent
|
||
tags: [agents]
|
||
security: [{ bearerAuth: [] }]
|
||
responses:
|
||
'201':
|
||
description: Created
|
||
|
||
/api/v1/install-links/{id}:
|
||
delete:
|
||
summary: Revoke install link
|
||
tags: [agents]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Revoked
|
||
|
||
/api/v1/agents:
|
||
get:
|
||
summary: List agents
|
||
tags: [agents]
|
||
security: [{ bearerAuth: [] }]
|
||
responses:
|
||
'200':
|
||
description: Agents
|
||
|
||
/api/v1/agents/{id}:
|
||
get:
|
||
summary: Get agent
|
||
tags: [agents]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Agent
|
||
patch:
|
||
summary: Update agent
|
||
tags: [agents]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Updated
|
||
delete:
|
||
summary: Delete agent
|
||
tags: [agents]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Deleted
|
||
|
||
/api/v1/agents/{id}/approve:
|
||
post:
|
||
summary: Approve pending/invited agent
|
||
tags: [agents]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Approved
|
||
|
||
/api/v1/agents/{id}/revoke:
|
||
post:
|
||
summary: Revoke agent
|
||
tags: [agents]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Revoked
|
||
|
||
/api/v1/agents/{id}/preview:
|
||
get:
|
||
summary: Policy preview (truncated CIDRs)
|
||
tags: [agents]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
- name: limit_cidrs
|
||
in: query
|
||
schema: { type: integer, default: 50, maximum: 5000 }
|
||
responses:
|
||
'200':
|
||
description: Preview
|
||
|
||
/api/v1/agents/{id}/policy-sets:
|
||
get:
|
||
summary: Assigned policy sets
|
||
tags: [agents]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Sets
|
||
put:
|
||
summary: Replace assigned policy sets (order = sort)
|
||
tags: [agents]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Updated
|
||
|
||
/api/v1/agents/{id}/overrides:
|
||
get:
|
||
summary: List IP overrides
|
||
tags: [agents]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Overrides
|
||
post:
|
||
summary: Create override
|
||
tags: [agents]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Created
|
||
|
||
/api/v1/agents/{id}/overrides/{overrideId}:
|
||
delete:
|
||
summary: Delete override
|
||
tags: [agents]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
- name: overrideId
|
||
in: path
|
||
required: true
|
||
schema: { type: string }
|
||
responses:
|
||
'200':
|
||
description: Deleted
|
||
|
||
/api/v1/lists:
|
||
get:
|
||
summary: List IP lists
|
||
tags: [lists]
|
||
security: [{ bearerAuth: [] }]
|
||
responses:
|
||
'200':
|
||
description: Lists
|
||
post:
|
||
summary: Create IP list
|
||
tags: [lists]
|
||
security: [{ bearerAuth: [] }]
|
||
responses:
|
||
'200':
|
||
description: Created
|
||
|
||
/api/v1/lists/{id}:
|
||
get:
|
||
summary: List detail + entries
|
||
tags: [lists]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Detail
|
||
delete:
|
||
summary: Delete list
|
||
tags: [lists]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Deleted
|
||
|
||
/api/v1/lists/{id}/refresh:
|
||
post:
|
||
summary: Refresh remote/json/evobgp list
|
||
tags: [lists]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Refreshed
|
||
|
||
/api/v1/lists/{id}/entries:
|
||
post:
|
||
summary: Add manual entries
|
||
tags: [lists]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Updated
|
||
delete:
|
||
summary: Delete entry
|
||
tags: [lists]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Deleted
|
||
|
||
/api/v1/policy-sets:
|
||
get:
|
||
summary: List policy sets
|
||
tags: [policies]
|
||
security: [{ bearerAuth: [] }]
|
||
responses:
|
||
'200':
|
||
description: Sets
|
||
post:
|
||
summary: Create policy set
|
||
tags: [policies]
|
||
security: [{ bearerAuth: [] }]
|
||
responses:
|
||
'200':
|
||
description: Created
|
||
|
||
/api/v1/policy-sets/{id}:
|
||
get:
|
||
summary: Get policy set
|
||
tags: [policies]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Set
|
||
patch:
|
||
summary: Patch policy set
|
||
tags: [policies]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Updated
|
||
delete:
|
||
summary: Delete policy set
|
||
tags: [policies]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Deleted
|
||
|
||
/api/v1/policy-sets/{id}/rules:
|
||
get:
|
||
summary: Rules in set
|
||
tags: [policies]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Rules
|
||
|
||
/api/v1/policy-sets/{id}/rules/reorder:
|
||
put:
|
||
summary: Reorder rules in a set
|
||
tags: [policies]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Reordered
|
||
|
||
/api/v1/rules:
|
||
get:
|
||
summary: List policy rules (optional set filter)
|
||
tags: [policies]
|
||
security: [{ bearerAuth: [] }]
|
||
responses:
|
||
'200':
|
||
description: Rules
|
||
post:
|
||
summary: Create policy rule
|
||
tags: [policies]
|
||
security: [{ bearerAuth: [] }]
|
||
responses:
|
||
'200':
|
||
description: Created
|
||
|
||
/api/v1/rules/{id}:
|
||
patch:
|
||
summary: Patch rule
|
||
tags: [policies]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Updated
|
||
delete:
|
||
summary: Delete rule
|
||
tags: [policies]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Deleted
|
||
|
||
/api/v1/stats/recent:
|
||
get:
|
||
summary: Recent apply samples
|
||
tags: [ops]
|
||
security: [{ bearerAuth: [] }]
|
||
responses:
|
||
'200':
|
||
description: Samples
|
||
|
||
/api/v1/agents/{id}/stats:
|
||
get:
|
||
summary: Apply samples for one agent
|
||
tags: [ops]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Samples
|
||
|
||
/api/v1/agents/{id}/stats/reset:
|
||
post:
|
||
summary: Reset packet totals, samples, and per-IP block stats
|
||
tags: [ops]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Agent after reset
|
||
|
||
/api/v1/agents/{id}/blocked-ips:
|
||
get:
|
||
summary: Per-IP/CIDR drop counters (Linux nft/ipset; MikroTik HITS)
|
||
tags: [ops]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Top blocked IPs by accumulated packets (+ optional top ports per IP on Linux)
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
type: object
|
||
required: [ip, packets, first_seen_at, last_seen_at]
|
||
properties:
|
||
ip: { type: string }
|
||
packets: { type: integer }
|
||
first_seen_at: { type: string, format: date-time }
|
||
last_seen_at: { type: string, format: date-time }
|
||
ports:
|
||
type: array
|
||
description: Top destination ports for this IP (Linux nft)
|
||
items:
|
||
type: object
|
||
required: [port, protocol, packets]
|
||
properties:
|
||
port: { type: integer, minimum: 1, maximum: 65535 }
|
||
protocol: { type: string, enum: [tcp, udp] }
|
||
packets: { type: integer }
|
||
|
||
/api/v1/agents/{id}/blocked-ports:
|
||
get:
|
||
summary: Aggregate destination ports hit by denied sources (Linux nft)
|
||
tags: [ops]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Top ports by accumulated packets across all blocked IPs
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
type: object
|
||
required: [port, protocol, packets, last_seen_at]
|
||
properties:
|
||
port: { type: integer, minimum: 1, maximum: 65535 }
|
||
protocol: { type: string, enum: [tcp, udp] }
|
||
packets: { type: integer }
|
||
last_seen_at: { type: string, format: date-time }
|
||
|
||
/api/v1/integrations/evobgp/communities:
|
||
get:
|
||
summary: Proxy EvoBGP communities
|
||
tags: [integrations]
|
||
security: [{ bearerAuth: [] }]
|
||
responses:
|
||
'200':
|
||
description: Communities
|
||
|
||
/api/v1/settings:
|
||
get:
|
||
summary: Settings map (token masked)
|
||
tags: [settings]
|
||
security: [{ bearerAuth: [] }]
|
||
responses:
|
||
'200':
|
||
description: Settings
|
||
put:
|
||
summary: Update whitelisted settings keys
|
||
tags: [settings]
|
||
security: [{ bearerAuth: [] }]
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
additionalProperties:
|
||
type: string
|
||
description: Keys — enroll_seed, evobgp_api_url, evobgp_api_token, agent_sync_interval_sec, show_quick_actions
|
||
responses:
|
||
'200':
|
||
description: Saved
|
||
'400':
|
||
description: Unknown key / validation
|
||
|
||
/api/v1/audit:
|
||
get:
|
||
summary: Local audit log
|
||
tags: [ops]
|
||
security: [{ bearerAuth: [] }]
|
||
parameters:
|
||
- name: action
|
||
in: query
|
||
schema: { type: string }
|
||
- name: severity
|
||
in: query
|
||
schema: { type: string, enum: [info, warning, critical] }
|
||
- name: limit
|
||
in: query
|
||
schema: { type: integer, default: 200 }
|
||
responses:
|
||
'200':
|
||
description: Audit entries
|
||
|
||
/v1/agent/install.sh:
|
||
get:
|
||
summary: Agent install script
|
||
tags: [agent-public]
|
||
responses:
|
||
'200':
|
||
description: Shell script
|
||
|
||
/v1/agent/enroll:
|
||
post:
|
||
summary: Enroll agent (X-EvoFW-Seed)
|
||
tags: [agent-public]
|
||
responses:
|
||
'201':
|
||
description: Pending agent
|
||
|
||
/v1/agent/policy:
|
||
get:
|
||
summary: Evaluated policy for agent
|
||
tags: [agent]
|
||
security: [{ agentToken: [] }]
|
||
responses:
|
||
'200':
|
||
description: Policy
|
||
|
||
/v1/agent/policy.rsc:
|
||
get:
|
||
summary: MikroTik RSC policy
|
||
tags: [agent]
|
||
security: [{ agentToken: [] }]
|
||
responses:
|
||
'200':
|
||
description: RSC text
|
||
|
||
/v1/agent/heartbeat:
|
||
post:
|
||
summary: Agent heartbeat
|
||
tags: [agent]
|
||
security: [{ agentToken: [] }]
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
/v1/agent/apply-report:
|
||
post:
|
||
summary: Apply report + packet stats (+ optional ip_hits / port_hits)
|
||
tags: [agent]
|
||
security: [{ agentToken: [] }]
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [status]
|
||
properties:
|
||
status: { type: string }
|
||
prefix_count: { type: integer }
|
||
packets_dropped: { type: integer }
|
||
packets_accepted: { type: integer }
|
||
kernel_method: { type: string }
|
||
error: { type: string }
|
||
source: { type: string }
|
||
ip_hits:
|
||
type: array
|
||
maxItems: 200
|
||
description: Linux per-element drop counters (packets > 0)
|
||
items:
|
||
type: object
|
||
required: [ip, packets]
|
||
properties:
|
||
ip: { type: string, maxLength: 64 }
|
||
packets: { type: integer, minimum: 0 }
|
||
port_hits:
|
||
type: array
|
||
maxItems: 500
|
||
description: Linux nft deny_port_hits (ip × proto × dport, packets > 0)
|
||
items:
|
||
type: object
|
||
required: [ip, port, protocol, packets]
|
||
properties:
|
||
ip: { type: string, maxLength: 64 }
|
||
port: { type: integer, minimum: 1, maximum: 65535 }
|
||
protocol: { type: string, enum: [tcp, udp] }
|
||
packets: { type: integer, minimum: 0 }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
|
||
components:
|
||
parameters:
|
||
Id:
|
||
name: id
|
||
in: path
|
||
required: true
|
||
schema: { type: string }
|
||
securitySchemes:
|
||
bearerAuth:
|
||
type: http
|
||
scheme: bearer
|
||
description: Portal JWT (app id fw)
|
||
agentToken:
|
||
type: http
|
||
scheme: bearer
|
||
description: Agent enroll token
|
||
|
||
tags:
|
||
- name: system
|
||
- name: ops
|
||
- name: agents
|
||
- name: lists
|
||
- name: policies
|
||
- name: settings
|
||
- name: integrations
|
||
- name: agent-public
|
||
- name: agent
|