Skip to main content

Firewall Rules

Firewall rules in Synapse provide stateless packet filtering via TC (Traffic Control) BPF programs, enabling fine-grained control over both incoming and outgoing traffic. Unlike access rules (which use XDP for inbound-only, pre-stack filtering), firewall rules operate at the TC layer to filter traffic in both directions with support for port, IP, and protocol matching.

Overview

Synapse's TC-based firewall rules allow you to:

  • Filter ingress and egress traffic at the TC hook
  • Match on source/destination IP, port ranges, and IP protocol
  • Enforce ordered rules with a configurable default action (allow or drop)
  • Distinguish IPv4 and IPv6 traffic per rule
  • Distribute active rules dynamically via the Gen0Sec control plane and API feeds

How It Works

Synapse loads firewall rules into a TC/BPF program attached to network interfaces. When a packet is processed:

  1. TC BPF program inspects the packet at the traffic control hook
  2. Rules are evaluated in order (ascending order value) against the packet's IP, port, and protocol fields
  3. First matching rule wins — the packet is allowed or dropped according to the rule's action
  4. Default action applies if no rule matches, as defined in the workspace FirewallRulesConfig
  5. Statistics are collected for monitoring and analysis

TC vs XDP

FeatureXDP (Access Rules)TC (Firewall Rules)
Hook pointBefore network stackAfter network stack ingress / before egress
DirectionsIngress onlyIngress and egress
MatchingIP addressIP, port, protocol
LatencySub-microsecondLow microsecond
Use caseFast IP blocklistingFine-grained stateless firewalling

Configuration

Workspace Firewall Config

Each workspace has a single FirewallRulesConfig that controls global firewall behaviour:

FieldTypeDescription
enabledboolWhen false, the BPF program passes all traffic without evaluating rules
default_actionstringAction applied when no rule matches — "allow" or "drop"

Firewall Rule Fields

Each FirewallRule has the following fields:

FieldTypeDescription
idUUIDInternal rule identifier (UUID)
namestringHuman-readable rule name
descriptionstringOptional description
src_low_port*intSource port range lower bound (null = any port)
src_high_port*intSource port range upper bound (null = any port)
dst_low_port*intDestination port range lower bound (null = any port)
dst_high_port*intDestination port range upper bound (null = any port)
src_ip*stringSource IP to match (null = any)
dst_ip*stringDestination IP to match (null = any)
src_ip_prefixintSource CIDR prefix length (0 = host match, 32/128 = exact)
dst_ip_prefixintDestination CIDR prefix length
ip_versionint4 for IPv4, 6 for IPv6
ip_protocolintIP protocol number (e.g. 6 = TCP, 17 = UDP, 0 = any)
actionstring"allow" or "drop"
directionstring"ingress" or "egress"
orderintEvaluation order — lower values are matched first
is_activeboolOnly active rules are loaded into the BPF map
statusstringRule lifecycle status (e.g. "active", "disabled")
created_attimestampCreation time
updated_attimestampLast modification time

API Reference

Feeds API

GET /v1/firewall/feeds

Returns the firewall configuration and active rules for the authenticated workspace, formatted for direct consumption by the Synapse agent's BPF map loader. Active rules are pre-split into ingress and egress lists, ordered by order ASC.

Authentication: API key via auth middleware (org_id, workspace_id in context)

Response (200 OK):

{
"config": {
"enabled": true,
"default_action": "drop"
},
"ingress": [
{
"id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
"name": "Allow HTTP from trusted range",
"src_low_port": 0,
"src_high_port": 65535,
"dst_low_port": 80,
"dst_high_port": 80,
"src_ip": "10.0.0.0",
"dst_ip": null,
"src_ip_prefix": 8,
"dst_ip_prefix": 0,
"ip_version": 4,
"ip_protocol": 6,
"action": "allow",
"order": 10,
"is_active": true
}
],
"egress": []
}

When the workspace has no FirewallRulesConfig row, the response still returns 200 OK with config: null and empty rule lists — the agent treats this as firewalling disabled.

Cache: L1 only, TTL 30 seconds. Cache hit/miss status is reported via X-Cache and X-Cache-Level response headers.


Remediation API

GET /v1/firewall-rules

Returns a paginated list of all firewall rules for the authenticated workspace.

Query Parameters:

ParameterDefaultMaximumDescription
page1Page number (1-indexed)
limit20100Rules per page

Response (200 OK):

{
"success": true,
"data": [
{
"id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
"name": "Block outbound DNS to untrusted resolvers",
"description": "Prevents DNS exfiltration",
"src_low_port": 0,
"src_high_port": 65535,
"dst_low_port": 53,
"dst_high_port": 53,
"src_ip": null,
"dst_ip": null,
"src_ip_prefix": 0,
"dst_ip_prefix": 0,
"ip_version": 4,
"ip_protocol": 17,
"action": "drop",
"direction": "egress",
"order": 5,
"is_active": true,
"status": "active",
"created_at": "2024-01-15T10:00:00Z",
"updated_at": "2024-01-15T10:00:00Z"
}
],
"total": 1,
"page": 1,
"limit": 20
}

Cache: L1 + L2, TTL 60 seconds, keyed by org_id, workspace_id, page, and limit.


GET /v1/firewall-rules/{id}

Returns a single firewall rule by its id (UUID).

Path Parameters:

ParameterDescription
idUUID (id field) of the firewall rule

Response (200 OK):

{
"success": true,
"data": {
"id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
"name": "Allow HTTP from trusted range",
"description": "",
"src_low_port": 0,
"src_high_port": 65535,
"dst_low_port": 80,
"dst_high_port": 80,
"src_ip": "10.0.0.0",
"dst_ip": null,
"src_ip_prefix": 8,
"dst_ip_prefix": 0,
"ip_version": 4,
"ip_protocol": 6,
"action": "allow",
"direction": "ingress",
"order": 10,
"is_active": true,
"status": "active",
"created_at": "2024-01-15T10:00:00Z",
"updated_at": "2024-01-15T10:00:00Z"
}
}

Error responses: 400 if id is not a valid UUID, 404 if the rule does not exist.

Cache: L1 + L2, TTL 60 seconds, keyed by org_id and id.


GET /v1/firewall-rules/config

Returns the workspace-level firewall configuration.

Response (200 OK):

{
"success": true,
"data": {
"id": "dddddddd-dddd-dddd-dddd-dddddddddddd",
"org_id": "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee",
"workspace_id": "ffffffff-ffff-ffff-ffff-ffffffffffff",
"enabled": true,
"default_action": "drop",
"created_at": "2024-01-15T10:00:00Z",
"updated_at": "2024-01-15T10:00:00Z"
}
}

Error responses: 404 if no config has been created for the workspace.

Cache: L1 + L2, TTL 60 seconds, keyed by org_id and workspace_id.

OpenAPI and Swagger

The firewall read endpoints are included in the generated Swagger documents for the relevant services.

  • Feeds API UI: /docs/feeds/swagger/
  • Feeds API JSON: /docs/feeds/swagger/doc.json
  • Remediation API UI: /docs/remediation/swagger/
  • Remediation API JSON: /docs/remediation/swagger/doc.json

The generated service specs describe the currently implemented read routes:

  • GET /v1/firewall/feeds
  • GET /v1/firewall-rules
  • GET /v1/firewall-rules/{id}
  • GET /v1/firewall-rules/config

Write Path Status

The current firewall-rules HTTP implementation is also read-only.

These services currently expose:

  • Agent feed retrieval from the feeds API
  • Paginated rule listing from the remediation API
  • Single-rule inspection from the remediation API
  • Workspace config retrieval from the remediation API

The following write operations are not implemented in these HTTP services today:

  • Create firewall rule
  • Update firewall rule
  • Delete firewall rule
  • Create or update workspace firewall config
  • Activate or deactivate a rule

If write operations exist in the broader control plane, they are outside the scope of these two service APIs.

Rule Evaluation

Rules are ordered by the order field (ascending). The agent inserts rules into the BPF map in this order so that lower order values have higher priority:

order=1 → evaluated first (highest priority)
order=10 → evaluated second
order=99 → evaluated last (lowest priority)

default_action (fallback if no rule matches)

Only rules where is_active = true are included in the feeds response and loaded into the BPF map.

Performance Characteristics

TC Hook Benefits

  • Bidirectional filtering — single BPF program handles both ingress and egress
  • Rich match criteria — port, IP, CIDR, and protocol matching
  • Ordered evaluation — deterministic first-match semantics
  • Low overhead — BPF map lookup at ~200ns per packet

Resource Usage

  • Memory: ~200 bytes per rule in BPF map
  • CPU: Less than 2% for typical rule counts and traffic patterns
  • Cache TTL: 30s (feeds), 60s (management API)

Best Practices

Rule Design

  • Keep order values sparse (e.g. 10, 20, 30) to allow rule insertion without renumbering
  • Place most-specific rules first (lower order) to avoid shadowing
  • Use default_action: drop with explicit allow rules for a deny-by-default posture
  • Separate ingress/egress chains — use direction to keep rule sets focused

Security Considerations

  • TC BPF requires CAP_NET_ADMIN — ensure the Synapse agent container has this capability
  • Test rule ordering before deploying to production — incorrect order can allow or block unintended traffic
  • Monitor the feeds endpoint cache — a 30s staleness window means rule changes propagate within ~30 seconds to agents
  • Use is_active = false to disable a rule without deleting it, preserving audit history