Firewall Management

Comprehensive guide to Infinibay's firewall system including rules, service presets, and security best practices

Firewall Management

Firewall system for VM network security using libvirt nwfilters.

Overview

Two-tier architecture using libvirt network filters (nwfilter):

  • Department filters (infinibay-dept-{deptId}): Priority 100
  • VM filters (infinibay-vm-{machineId}): Priority 200
  • Filter inheritance via <filterref> elements
  • Rule evaluation by priority (lower number = higher priority)

Technical implementation: Uses FirewallRule Prisma model with ServicePreset configuration. Rules tracked by origin (DEPARTMENT, VM, SYSTEM) with automatic risk calculation and conflict detection.

Service Presets

Defined in backend/app/config/servicePresets.ts. Presets create FirewallRule records with predefined port/protocol combinations that map to nwfilter XML rules.

Available presets:

  • Web: https (443/TCP), http (80/TCP)
  • Remote Access: ssh (22/TCP), rdp (3389/TCP)
  • Databases: mysql (3306/TCP), postgresql (5432/TCP), redis (6379/TCP), mongodb (27017/TCP)
  • Email: smtp (25/TCP), pop3 (110/TCP), imap (143/TCP)
  • File Sharing: ftp (21/TCP), sftp (22/TCP), nfs (2049/TCP), smb (445/TCP)
  • Other: dns (53/UDP)

Custom Firewall Rules

You can create custom firewall rules for specific use cases:

mutation CreateCustomRule {
  createFirewallRule(input: {
    vmId: "vm-123"
    description: "Allow custom web app"
    action: ACCEPT
    direction: INBOUND
    protocol: TCP
    destPortStart: 8080
    destPortEnd: 8080
    priority: 500
  }) {
    id
    riskLevel
    status
  }
}

Risk Assessment

Risk calculation algorithm (in FirewallManager service):

  1. Base score from port number (0-21: HIGH, 22-1023: MEDIUM, 1024+: LOW)
  2. Protocol factor (UDP +10 points, TCP +0)
  3. Direction factor (INBOUND +20, OUTBOUND +0)
  4. Source IP factor (ANY +30, specific IP +0)
  5. Final mapping: 0-20=MINIMAL, 21-40=LOW, 41-60=MEDIUM, 61+=HIGH

Example: HTTPS inbound from any source = 0 (well-known) + 0 (TCP) + 20 (inbound) + 30 (any) = 50 (MEDIUM)

Conflict Detection

Validation logic in FirewallManager.validateRule():

Hard conflicts (prevented):

  • ACCEPT + DROP rules for same port/protocol/direction/source combination
  • Query: Compare new rule against existing rules with action !== newRule.action

Redundancy detection (warning):

  • Exact duplicate rules (same port range, protocol, direction, source, action)
  • Suggestion: Delete redundant rule

Implementation: Database query compares rule parameters before insert. Conflicts return error with details.

Blocked Connection Tracking

Data flow:

  1. InfiniService guest agent captures blocked connections via firewall logs
  2. Sends data to backend via VirtIO channel
  3. Backend creates BlockedConnection model records
  4. Suggestion algorithm analyzes patterns (frequent port/IP combinations)
  5. Auto-generate suggested rules (available via firewall analysis queries)

Retention: 7-day TTL (configurable). Cron job CleanupBlockedConnections.ts runs daily at midnight.

Suggestion algorithm: Group by destPort + protocol, count occurrences, suggest ACCEPT rule if count >10 in 24h.

Department vs VM Rules

Filter Hierarchy

Department filter: infinibay-dept-{deptId} (priority 100, evaluated first) VM filter: infinibay-vm-{machineId} (priority 200, inherits from department via <filterref>)

Why single filterref: libvirt does NOT support multiple <filterref> per interface. VM filter includes:

<filter name="infinibay-vm-{machineId}" priority="200">
  <filterref filter="infinibay-dept-{deptId}"/>
  <!-- VM-specific rules here -->
</filter>

Override Mechanism

overridesDepartmentRule: true flag on VM FirewallRule:

  • Adds explicit DROP/ACCEPT rule in VM filter
  • Takes precedence due to more specific match
  • Example: Department allows SSH (22/TCP), VM blocks it with overridesDepartmentRule: true

Effective Rule Calculation

FirewallManager.getEffectiveRules(vmId):

  1. Fetch department rules
  2. Fetch VM rules
  3. For each VM rule with overridesDepartmentRule=true, suppress matching dept rule
  4. Merge lists: VM rules + non-overridden dept rules
  5. Sort by priority
  6. Return effective ruleset

Filter Architecture

Nwfilter XML Generation

FirewallManager.generateFilterXML() converts FirewallRule records to libvirt nwfilter XML:

Rule translation:

  • protocol: Maps to <rule> protocol attribute (tcp, udp, icmp)
  • destPortStart/End: Maps to <tcp> or <udp> dstportstart/dstportend attributes
  • sourceIP: Maps to srcipaddr attribute (or omitted for ANY)
  • action: Maps to <rule> action attribute (accept, drop, reject)
  • priority: Maps to <rule> priority attribute

Example:

// FirewallRule
{
  action: 'ACCEPT',
  protocol: 'TCP',
  destPortStart: 443,
  destPortEnd: 443,
  direction: 'INBOUND',
  priority: 500
}

// Becomes nwfilter XML
<rule action='accept' direction='in' priority='500'>
  <tcp dstportstart='443' dstportend='443'/>
</rule>

Prisma Callbacks

backend/app/utils/modelCallbacks/machine.ts:

On machine create:

  1. afterCreate hook triggers
  2. FirewallManager.createFiltersForVM(machineId, departmentId)
  3. Creates department filter if not exists
  4. Creates VM filter with <filterref> to department
  5. Calls nwfilter.defineXML() via libvirt

On machine delete:

  1. beforeDelete hook triggers
  2. FirewallManager.deleteFiltersForVM(machineId)
  3. Undefines VM filter via nwfilter.undefine()
  4. Checks if department filter still needed (other VMs in dept)

On rule change:

  • Manual trigger via GraphQL mutation
  • Regenerate filter XML
  • Call nwfilter.undefine() + nwfilter.defineXML() (libvirt requires undefine before redefine)
  • If VM running: mark rules as status: PENDING, apply on next restart

GraphQL API

Mutations

createFirewallRule: Creates new rule, validates conflicts, generates risk level, returns rule with status updateFirewallRule: Updates existing rule, re-validates, marks PENDING if VM running deleteFirewallRule: Deletes rule, regenerates filter XML toggleServicePreset: Enables/disables preset (creates/deletes associated rules)

Queries

getVMFirewallRules(vmId: ID!): Returns FirewallRuleSetType with:

  • id: RuleSet ID
  • type: RuleSetType (VM or DEPARTMENT)
  • entityId: VM ID
  • rules: Array of FirewallRuleType

getDepartmentFirewallRules(departmentId: ID!): Returns FirewallRuleSetType for department

getEffectiveFirewallRules(vmId: ID!): Returns EffectiveRuleSetType with:

  • vmRules: VM-specific rules
  • departmentRules: Inherited department rules
  • effectiveRules: Computed effective ruleset (VM rules + non-overridden dept rules)
  • conflicts: Array of detected conflicts
  • hasPendingChanges: Boolean if VM running with uncommitted changes

See backend/graphql-api.md and backend/app/schema.graphql for authoritative schema.

Pending Changes

Rules created while VM running have status='PENDING':

  1. GraphQL mutation creates rule in database
  2. Service checks VM state via libvirt
  3. If VM running: Set status='PENDING', skip nwfilter regeneration
  4. UI shows "Restart VM to apply changes" indicator
  5. On VM restart: FirewallManager regenerates nwfilter XML from all rules, sets status='ACTIVE'

Rationale: libvirt requires VM shutdown to apply nwfilter changes safely. Hot-reload risks packet drops or connection issues.