Add VoIP/SIP ACL template for Asterisk PBX, port range support

ACL backend:
- AclRule gains optional port_end field; build_acl generates
  "range X Y" when both port and port_end are set (needed for RTP)

New ACL template — "SIP Phone VLAN — Asterisk / FreePBX access":
- Permits SIP signaling UDP/TCP 5060 to PBX IP
- Permits SIP/TLS TCP 5061 to PBX IP
- Permits RTP audio UDP range 10000-20000 to PBX IP (uses new range syntax)
- Blocks management VLAN 99
- Permits internet and all other traffic
- Requires entering the Asterisk server IP (restricts SIP/RTP to that
  exact host, not the whole VLAN subnet)

Template description explains:
- Why OPNsense firewall rules are also needed (inter-VLAN routing)
- Exactly which OPNsense rules to add (including return RTP)
- Remote access options: WebRTC via Caddy reverse proxy (recommended)
  and SIP/TLS with fail2ban for traditional SIP clients

Template modal:
- New pbxIp param field shown for VoIP template
- Description box scrollable for longer template descriptions
- Preview renders "range X Y" for port range rules

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
This commit is contained in:
Claude
2026-03-23 16:15:38 +00:00
parent 7fe42416d8
commit 2ebdb22e0a
3 changed files with 77 additions and 8 deletions
+7 -1
View File
@@ -550,6 +550,7 @@ class AclRule(BaseModel):
dst_mask: Optional[str] = "0.0.0.255"
dst_any: Optional[bool] = True
port: Optional[str] = ""
port_end: Optional[str] = "" # when set, generates "range port port_end"
@field_validator("action")
@classmethod
def ca(cls, v): return _san(v, _RE_ACTION, "action")
@@ -640,7 +641,12 @@ def build_acl(acl: AclCreate) -> list[str]:
for i, r in enumerate(acl.rules):
src = "any" if r.src_any else f"{r.src} {r.src_mask}"
dst = "any" if r.dst_any else f"{r.dst} {r.dst_mask}"
port_str = f" eq {r.port}" if r.port else ""
if r.port and r.port_end:
port_str = f" range {r.port} {r.port_end}"
elif r.port:
port_str = f" eq {r.port}"
else:
port_str = ""
cmds.append(f" {i+1} {r.action} {r.proto} {src} {dst}{port_str}")
vid = san_vid(acl.apply_vlan, "apply_vlan")
cmds += [f"interface vlan {vid}",