Page MenuHomeVyOS Platform

show nat source rules` crashes with `KeyError: 0` for rules without `inbound-interface`
Closed, ResolvedPublicBUG

Description

    1. Environment
  • VyOS 1.5.1/circinus, build 2026-07-13 (also checked against current branch source on GitHub — the vulnerable code is still present there)

Summary

show nat source rules (and likely show nat destination rules) throws an unhandled Python exception instead of rendering output when at least one configured NAT rule has no inbound-interface set.

Steps to reproduce

set nat source rule 10 description 'example'
set nat source rule 10 destination address '1.1.1.1'
set nat source rule 10 destination port '53'
set nat source rule 10 protocol 'tcp_udp'
set nat source rule 10 translation address '<own-address>'
commit
run show nat source rules

No inbound-interface is set on this rule — that's the trigger. Rules that do set inbound-interface render fine; an empty ruleset also works fine. The crash is specific to interface-less rules.

Observed traceback

Traceback (most recent call last):
  File "/usr/libexec/vyos/op_mode/nat.py", line 355, in <module>
    res = vyos.opmode.run(sys.modules[__name__])
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/vyos/opmode.py", line 313, in run
    res = func(**args)
          ^^^^^^^^^^^^
  File "/usr/libexec/vyos/op_mode/nat.py", line 314, in _wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/libexec/vyos/op_mode/nat.py", line 324, in show_rules
    return _get_formatted_output_rules(nat_rules, direction, family)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/libexec/vyos/op_mode/nat.py", line 133, in _get_formatted_output_rules
    if interface[0] == '@':
       ~~~~~~~~~^^^
KeyError: 0

Root cause (from reading src/op_mode/nat.py on the current branch)

interface = rule.get('rule').get('expr')[0].get('match').get('right') \
    if jmespath.search('rule.expr[*].match.left.meta', rule) else 'any'
...
if interface[0] == '@':
    interface = interface[3:]

For a rule with no inbound-interface, the underlying nftables JSON representation of the rule's expr list doesn't have the shape this code expects, but the jmespath.search(...) guard still evaluates truthy in some cases, so the fallback to the literal string 'any' is skipped. .get('right') then returns something that is not a plain string (looks like a dict/mapping from the nftables JSON output), so interface[0] attempts a dict key lookup for key 0 instead of string indexing, raising KeyError: 0 instead of IndexError (which the code might at least partially guard against elsewhere).

Related but distinct existing tickets

Not a duplicate of, but same general area/script as:

  • T4531 — NAT op-mode errors with exclude rules
  • T3435 — NAT rules show corruption
  • T4545 — Rewrite show nat source rules

None of those specifically cover the interface-less-rule case reproduced here; the vulnerable interface[0] code path is still present on current as of 2026-08-04.

Suggested fix direction

Validate that interface is a non-empty string before indexing (e.g. if isinstance(interface, str) and interface and interface[0] == '@':), and make the jmespath.search(...) guard actually correspond to "does this rule have an interface match" rather than falling through to a shape assumption that doesn't hold for interface-less rules.

Details

Version
1.5.1
Is it a breaking change?
Perfectly compatible
Issue type
Bug (incorrect behavior)