Guides
ACLs & firewall
Stateful packet-filter rules bound to interfaces.
SiHA’s firewall is built on two resource types:
ACL— an ordered list of match-and-verdict rules with a default action applied to any packet that falls through the list.ACLBinding— attaches one ACL to one direction (INPUTorOUTPUT) of one interface.
The ACL engine is VPP’s acl-plugin, which is stateful: an ACCEPT_STATEFUL rule permits the matched flow and automatically allows its return traffic, so you never write asymmetric rules for established sessions.
Defining an ACL
type: ACLs.acl.siha
metadata: { namespace: acl, id: ext-in }
spec:
family: DUAL # IPv4 | IPv6 | DUAL (default when omitted)
defaultAction: DROP # DROP or ACCEPT — the catch-all when no rule matches
rules:
- description: "Allow established return traffic"
action: ACCEPT_STATEFUL
protocol: tcp
- description: "Allow HTTPS from any"
action: ACCEPT_STATEFUL
protocol: tcp
destinationPortFirst: 443
destinationPortLast: 443
- description: "Allow ICMP echo"
action: ACCEPT
protocol: icmp
icmpType: echo-request # a name (or 'any', or a number)
icmpCode: 0 # codes are numeric (or 'any')
- description: "Block a bad prefix explicitly"
action: DROP
sourceCidr: 192.0.2.0/24
Rule fields
| Field | Notes |
|---|---|
action | ACCEPT, DROP, or ACCEPT_STATEFUL |
protocol | Protocol name — tcp, udp, icmp, icmp6, sctp (also gre/esp/ah; a raw number works too). Omit or any = all protocols. sihactl reads and prints the name. |
sourceCidr / destinationCidr | CIDR string; omit = any |
sourcePortFirst / sourcePortLast | Port range (inclusive); TCP, UDP or SCTP only |
destinationPortFirst / destinationPortLast | Port range (inclusive); TCP, UDP or SCTP only |
icmpType | A name (echo-request, echo-reply, destination-unreachable, time-exceeded, …), any, or a number. ICMP/ICMPv6 only. |
icmpCode | 0–255 or any. ICMP/ICMPv6 only. |
tcpFlagsSet / tcpFlagsUnset | TCP flag names — any of syn, ack, fin, rst, psh, urg, ece, cwr (a list like [syn], or comma-separated; a number also works). Match packets with those flags set / cleared. TCP only. |
defaultAction accepts ACCEPT or DROP only — ACCEPT_STATEFUL is not valid as a catch-all because it would gate the implicit fall-through path.
Validation rejects cross-field mistakes at apply time (e.g. port fields on a non-TCP/UDP rule), so a permissive-by-typo rule never reaches the dataplane.
Binding an ACL to an interface
type: ACLBindings.acl.siha
metadata: { namespace: acl, id: ext-in }
spec:
acl: ext-in # id of the ACL resource above
interface: ext # id of the Interface resource (any kind)
direction: INPUT # INPUT or OUTPUT
priority: 10 # lower = earlier in the evaluation list
What you can bind to
An ACLBinding references an Interfaces.link.siha resource id. Any interface kind works: pmd, host-interface, tap, bond, vlan, qinq, vxlan, or loopback. To filter traffic on a bridge segment, bind to the bridge’s BVI interface, which carries the L3 addresses.
You can attach multiple ACLs to the same (interface, direction) pair — priority sets their evaluation order within that list. Lower priority numbers run first; first match wins.
Applying the config
# Write your ACL and binding into a config file, then apply:
sihactl --sihaconfig ~/.siha/config --addr <node-ip>:6443 config apply -f my-acls.yaml
Or use the S= alias for brevity:
S="sihactl --sihaconfig ~/.siha/config --addr <node-ip>:6443"
$S config apply -f my-acls.yaml
Operating
S="sihactl --sihaconfig ~/.siha/config --addr <node-ip>:6443"
$S get acl # list ACLs (family, default action, rule count, VPP index)
$S get acl ext-in -o yaml # full spec + status (VPP acl_index, lastError)
$S get aclbinding # list bindings (acl, interface, direction, priority)
$S acl counters ext-in # per-rule packet and byte hit counters
acl counters fetches the ACL’s vpp_acl_index from status, then queries VPP’s stats segment for per-rule counters. It prints one row per rule (including the synthetic default rule appended by the controller) with packet count, byte count, and a short description of the action and match.
Gotchas
Rule order matters. Rules are evaluated top-to-bottom; the first match wins. Put your most specific rules before your broader catch-alls, and put ACCEPT_STATEFUL rules before DROP rules when you want return traffic to succeed.
Default action is mandatory. defaultAction must be DROP or ACCEPT. There is no implicit default — forgetting it is a validation error at apply time.
Stateful tracking is per-ACL, not global. ACCEPT_STATEFUL in one ACL does not automatically open return paths for traffic governed by a different ACL. If you filter both directions of the same flow, make sure the ACCEPT_STATEFUL rule appears in the direction where the initiating packet arrives.
ACCEPT_STATEFUL on the default action is rejected. Use ACCEPT if you want an open-by-default ACL. ACCEPT_STATEFUL only makes sense as a per-rule action, where there is a specific flow to track.
Multiple bindings share state within a direction. When several ACLs are bound to the same (interface, direction), VPP evaluates them in priority order as a single list. The session table is shared, so an ACCEPT_STATEFUL match in ACL #1 opens return paths that ACL #2 would also see.