feat: port forwards, read from destination NAT on the WAN
CI / test (3.10) (push) Failing after 1m39s
CI / test (3.11) (push) Failing after 25s
CI / test (3.12) (push) Failing after 12s
CI / test (3.9) (push) Failing after 31s
CI / test (3.10) (pull_request) Failing after 13s
CI / test (3.11) (pull_request) Failing after 12s
CI / test (3.12) (pull_request) Failing after 13s
CI / test (3.9) (pull_request) Failing after 12s
CI / test (3.10) (push) Failing after 1m39s
CI / test (3.11) (push) Failing after 25s
CI / test (3.12) (push) Failing after 12s
CI / test (3.9) (push) Failing after 31s
CI / test (3.10) (pull_request) Failing after 13s
CI / test (3.11) (pull_request) Failing after 12s
CI / test (3.12) (pull_request) Failing after 13s
CI / test (3.9) (pull_request) Failing after 12s
get_port_forwards reads /api/firewall/d_nat/search_rule and keeps only what the contract asks for: rules on an interface with an upstream gateway (the WAN, and a second uplink as well). Internal redirects, anti-lockout rules (nordr) and rules the captive portal generates are left out -- on the first real box (OPNsense 26.7) that was 20 of 22 rules, and each would have made an internal host look reachable from the internet. Targets resolve through host/network aliases, one entry per address; an interface address or a DNS name gives no address and the rule is skipped rather than put on a guessed host. Ports resolve as numbers, the start of a range, port aliases or service names; no port is every port (0), and tcp/udp is two entries. The filtering is pure, in port_forwards.py, and the driver method does the three reads. A box without the destination-NAT API raises instead of answering "nothing forwarded", which nobody checked.
This commit is contained in:
@@ -51,6 +51,7 @@ from napalm_device_types import FingerprintRule, FirewallDriver
|
||||
from napalm.base.exceptions import ConnectionException, ConnectionClosedException, MergeConfigException
|
||||
|
||||
from napalm_opnsense.ping_mixin import OPNsensePingMixin
|
||||
from napalm_opnsense.port_forwards import alias_index, port_forwards, wan_interfaces
|
||||
|
||||
|
||||
class OPNsenseDriver(OPNsensePingMixin, FirewallDriver):
|
||||
@@ -2387,6 +2388,21 @@ class OPNsenseDriver(OPNsensePingMixin, FirewallDriver):
|
||||
|
||||
return {"success": success, "output": "\n".join(lines)}
|
||||
|
||||
def get_port_forwards(self) -> list[dict[str, Any]]:
|
||||
"""Destination NAT on the WAN interfaces, as port forwards.
|
||||
|
||||
Three reads: the rules, the interface overview (a WAN is an interface
|
||||
with an upstream gateway) and the aliases a rule may send to. The
|
||||
filtering and resolving is in :mod:`napalm_opnsense.port_forwards`.
|
||||
A box without the destination-NAT API (older than its MVC rework)
|
||||
raises: an empty list would claim "nothing forwarded", which nobody
|
||||
checked.
|
||||
"""
|
||||
rules = self._get("/api/firewall/d_nat/search_rule?current=1&rowCount=-1").get("rows", [])
|
||||
overview = self._get("/api/interfaces/overview/interfaces_info?current=1&rowCount=-1")
|
||||
aliases = self._get("/api/firewall/alias/searchItem?current=1&rowCount=-1").get("rows", [])
|
||||
return port_forwards(rules, wan_interfaces(overview), alias_index(aliases))
|
||||
|
||||
def get_firewall_aliases(self) -> list[dict[str, Any]]:
|
||||
"""Return all firewall aliases, sorted by type then name.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user