feat: port forwards, read from destination NAT on the WAN #4

Merged
christianmanivong merged 1 commits from feature/port-forwards into master 2026-10-03 14:31:04 +00:00
Owner

get_port_forwards reads /api/firewall/d_nat/search_rule, the interface overview and the aliases.

What is kept: only rules on an interface with an upstream gateway count. Internal redirects, nordr (anti-lockout) rules and captive-portal rules are left out.

How rules are resolved:

  • Targets resolve through host/network aliases, one entry per address.
  • Ports resolve as numbers, the start of a range, port aliases or service names.
  • No port at all means every port, written as 0.
  • A tcp/udp rule becomes two entries.

The filtering is pure, in port_forwards.py.

Tests: 34 unit tests. Checked against a live OPNsense 26.7: of 22 rules, the 2 WAN forwards came out (80 and 443 to the reverse proxy).

Needs the contract from NAPALM/napalm-device-types (port forwards on NatVpnMixin) only for typing. At runtime the method simply exists.

Related: NetOrk/netork#504

`get_port_forwards` reads `/api/firewall/d_nat/search_rule`, the interface overview and the aliases. **What is kept:** only rules on an interface with an upstream gateway count. Internal redirects, `nordr` (anti-lockout) rules and captive-portal rules are left out. **How rules are resolved:** - Targets resolve through host/network aliases, one entry per address. - Ports resolve as numbers, the start of a range, port aliases or service names. - No port at all means every port, written as `0`. - A `tcp/udp` rule becomes two entries. The filtering is pure, in `port_forwards.py`. **Tests:** 34 unit tests. Checked against a live OPNsense 26.7: of 22 rules, the 2 WAN forwards came out (80 and 443 to the reverse proxy). Needs the contract from NAPALM/napalm-device-types (port forwards on `NatVpnMixin`) only for typing. At runtime the method simply exists. Related: NetOrk/netork#504
christianmanivong added 1 commit 2026-10-03 14:30:54 +00:00
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
3506f20606
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.
christianmanivong merged commit 995282c5be into master 2026-10-03 14:31:04 +00:00
christianmanivong deleted branch feature/port-forwards 2026-10-03 14:31:04 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: NAPALM/napalm-opnsense#4