summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoromnom62 <omnom62@outlook.com>2026-07-04 15:57:46 +1000
committerJohn Estabrook <jestabro@vyos.io>2026-08-21 10:18:35 -0500
commit28d515a94fc21bdbb5ff0e9727ea574a7112c188 (patch)
tree75f504f449a6d758f2a6ef22c6b862242a1aec3b
parent5bf0e3f422282e5f5bccaba75704a52937e50713 (diff)
downloadrest.vyos-28d515a94fc21bdbb5ff0e9727ea574a7112c188.tar.gz
rest.vyos-28d515a94fc21bdbb5ff0e9727ea574a7112c188.zip
T8989: vyos_firewall_interfaces module
-rw-r--r--plugins/modules/vyos_firewall_interfaces454
1 files changed, 0 insertions, 454 deletions
diff --git a/plugins/modules/vyos_firewall_interfaces b/plugins/modules/vyos_firewall_interfaces
deleted file mode 100644
index ca115c1..0000000
--- a/plugins/modules/vyos_firewall_interfaces
+++ /dev/null
@@ -1,454 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-# GNU General Public License v3.0+
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-DOCUMENTATION = r"""
----
-module: vyos_firewall_interfaces
-short_description: Manage firewall hook filters on VyOS devices using REST API
-description:
- - Manages firewall hook filter configuration on VyOS devices via the REST API.
- - In VyOS 1.5+, firewall hook filters (input/output/forward) replace the
- per-interface firewall assignments used in VyOS 1.4.
- - Hook filters apply globally to all traffic traversing that hook point.
- - Uses REST API (C(connection=httpapi)) instead of CLI.
-version_added: "1.0.0"
-author:
- - VyOS Community (@vyos)
-options:
- config:
- description: Firewall hook filter configuration.
- type: list
- elements: dict
- suboptions:
- afi:
- description: Address family.
- type: str
- choices: [ipv4, ipv6]
- required: true
- hooks:
- description: Hook filter configurations for this address family.
- type: list
- elements: dict
- suboptions:
- hook:
- description: Netfilter hook point.
- type: str
- choices: [input, output, forward]
- required: true
- default_action:
- description: Default action when no rule matches.
- type: str
- choices: [accept, drop, reject]
- description:
- description: Filter description.
- type: str
- rules:
- description: Rules in this hook filter.
- type: list
- elements: dict
- suboptions:
- number:
- description: Rule number.
- type: int
- required: true
- action:
- description: Rule action.
- type: str
- choices: [accept, drop, reject, return, queue, continue]
- description:
- description: Rule description.
- type: str
- disable:
- description: Disable this rule.
- type: bool
- protocol:
- description: Protocol to match.
- type: str
- state:
- description: Connection state to match.
- type: str
- choices: [established, invalid, new, related]
- log:
- description: Enable logging.
- type: bool
- source:
- description: Source match criteria.
- type: dict
- suboptions:
- address:
- description: Source IP address or prefix.
- type: str
- port:
- description: Source port or range.
- type: str
- destination:
- description: Destination match criteria.
- type: dict
- suboptions:
- address:
- description: Destination IP address or prefix.
- type: str
- port:
- description: Destination port or range.
- type: str
- state:
- description:
- - Desired state of the firewall hook filter configuration.
- - C(merged) adds or updates without removing existing config.
- - C(replaced) replaces hook filter config for named hooks in config.
- - C(overridden) replaces all firewall hook filter config.
- - C(deleted) removes firewall hook filter config.
- - C(gathered) returns current configuration as structured data.
- type: str
- choices: [merged, replaced, overridden, deleted, gathered]
- default: merged
-notes:
- - Requires C(ansible_connection=httpapi) with the VyOS httpapi plugin.
- - C(ansible_network_os) must be set to C(vyos.rest.vyos).
- - In VyOS 1.5+, hook filters apply globally rather than per-interface.
- Use named rule sets (M(vyos.rest.vyos_firewall_rules)) for more granular
- per-traffic control.
-"""
-
-EXAMPLES = r"""
-- name: Merge firewall hook filter configuration
- vyos.rest.vyos_firewall_interfaces:
- config:
- - afi: ipv4
- hooks:
- - hook: input
- default_action: accept
- rules:
- - number: 10
- action: accept
- state: established
- - number: 20
- action: drop
- state: invalid
- - hook: forward
- default_action: accept
- - hook: output
- default_action: accept
- - afi: ipv6
- hooks:
- - hook: input
- default_action: accept
- state: merged
-
-- name: Delete all firewall hook filter configuration
- vyos.rest.vyos_firewall_interfaces:
- state: deleted
-
-- name: Gather firewall hook filter configuration
- vyos.rest.vyos_firewall_interfaces:
- state: gathered
-"""
-
-RETURN = r"""
-before:
- description: Firewall hook filter configuration before this module ran.
- returned: always
- type: list
-after:
- description: Firewall hook filter configuration after this module ran.
- returned: when changed
- type: list
-commands:
- description: List of API command tuples sent to the device.
- returned: always
- type: list
-gathered:
- description: Current firewall hook filter configuration as structured data.
- returned: when state is gathered
- type: list
-saved:
- description: Whether the config was saved after changes.
- returned: when changes are applied
- type: bool
-response:
- description: Raw API response.
- returned: when changes are applied
- type: dict
-"""
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.vyos.rest.plugins.module_utils.vyos import VyOSModule
-
-_BASE = ["firewall"]
-_AFIS = ["ipv4", "ipv6"]
-_HOOKS = ["input", "output", "forward"]
-
-
-def _parse_rule(rule_num, data):
- rule = {"number": int(rule_num)}
- data = data or {}
- if "action" in data:
- rule["action"] = data["action"]
- if "description" in data:
- rule["description"] = data["description"]
- if "disable" in data:
- rule["disable"] = True
- if "protocol" in data:
- rule["protocol"] = data["protocol"]
- if "state" in data:
- rule["state"] = data["state"]
- if "log" in data:
- rule["log"] = True
-
- for endpoint in ["source", "destination"]:
- ep = data.get(endpoint, {}) or {}
- if ep:
- rule[endpoint] = {}
- if "address" in ep:
- rule[endpoint]["address"] = ep["address"]
- if "port" in ep:
- rule[endpoint]["port"] = ep["port"]
-
- return rule
-
-
-def _parse_hook_filter(hook, data):
- entry = {"hook": hook}
- data = data or {}
- filter_data = data.get("filter", {}) or {}
- if "default-action" in filter_data:
- entry["default_action"] = filter_data["default-action"]
- if "description" in filter_data:
- entry["description"] = filter_data["description"]
- rules_raw = filter_data.get("rule", {}) or {}
- if rules_raw and isinstance(rules_raw, dict):
- rules = [_parse_rule(num, rdata) for num, rdata in sorted(
- rules_raw.items(), key=lambda x: int(x[0])
- )]
- if rules:
- entry["rules"] = rules
- return entry
-
-
-def get_running_config(vyos):
- result = []
- for afi in _AFIS:
- raw = vyos.get_config(_BASE + [afi])
- if not raw or not isinstance(raw, dict):
- continue
- hooks = []
- for hook in _HOOKS:
- if hook in raw:
- parsed = _parse_hook_filter(hook, raw[hook])
- if len(parsed) > 1: # more than just hook key
- hooks.append(parsed)
- if hooks:
- result.append({"afi": afi, "hooks": hooks})
- return result
-
-
-def _rule_cmds(afi, hook, rule, have_rule):
- cmds = []
- rbase = _BASE + [afi, hook, "filter", "rule", str(rule["number"])]
- have_rule = have_rule or {}
-
- if rule.get("action") and rule["action"] != have_rule.get("action"):
- cmds.append(("set", rbase + ["action", rule["action"]]))
- if rule.get("description") and rule["description"] != have_rule.get("description"):
- cmds.append(("set", rbase + ["description", rule["description"]]))
- if rule.get("disable") and not have_rule.get("disable"):
- cmds.append(("set", rbase + ["disable"]))
- if rule.get("protocol") and rule["protocol"] != have_rule.get("protocol"):
- cmds.append(("set", rbase + ["protocol", rule["protocol"]]))
- if rule.get("state") and rule["state"] != have_rule.get("state"):
- cmds.append(("set", rbase + ["state", rule["state"]]))
- if rule.get("log") and not have_rule.get("log"):
- cmds.append(("set", rbase + ["log"]))
-
- for endpoint in ["source", "destination"]:
- want_ep = rule.get(endpoint) or {}
- have_ep = have_rule.get(endpoint) or {}
- if want_ep.get("address") and want_ep["address"] != have_ep.get("address"):
- cmds.append(("set", rbase + [endpoint, "address", want_ep["address"]]))
- if want_ep.get("port") and want_ep["port"] != have_ep.get("port"):
- cmds.append(("set", rbase + [endpoint, "port", str(want_ep["port"])]))
-
- return cmds
-
-
-def _hook_cmds(afi, hook_entry, have_hook, state):
- cmds = []
- hook = hook_entry["hook"]
- hbase = _BASE + [afi, hook, "filter"]
- have_hook = have_hook or {}
-
- if hook_entry.get("default_action") and \
- hook_entry["default_action"] != have_hook.get("default_action"):
- cmds.append(("set", hbase + ["default-action", hook_entry["default_action"]]))
- if hook_entry.get("description") and \
- hook_entry["description"] != have_hook.get("description"):
- cmds.append(("set", hbase + ["description", hook_entry["description"]]))
-
- have_rules = {r["number"]: r for r in (have_hook.get("rules") or [])}
- want_rules = {r["number"]: r for r in (hook_entry.get("rules") or [])}
-
- if state == "replaced":
- for num in set(have_rules) - set(want_rules):
- cmds.append(("delete", hbase + ["rule", str(num)]))
-
- for num, rule in want_rules.items():
- cmds += _rule_cmds(afi, hook, rule, have_rules.get(num))
-
- return cmds
-
-
-def build_commands(config, have_list, state):
- cmds = []
-
- if state == "deleted":
- if not config:
- if have_list:
- for entry in have_list:
- afi = entry["afi"]
- for hook_entry in entry.get("hooks", []):
- cmds.append(("delete", _BASE + [afi, hook_entry["hook"], "filter"]))
- else:
- have_map = {(e["afi"], h["hook"]): h
- for e in have_list
- for h in e.get("hooks", [])}
- for entry in config:
- afi = entry["afi"]
- for hook_entry in (entry.get("hooks") or []):
- if (afi, hook_entry["hook"]) in have_map:
- cmds.append(("delete", _BASE + [afi, hook_entry["hook"], "filter"]))
- return cmds
-
- have_map = {e["afi"]: {h["hook"]: h for h in e.get("hooks", [])}
- for e in have_list}
-
- if state == "overridden":
- want_keys = {(e["afi"], h["hook"])
- for e in (config or [])
- for h in e.get("hooks", [])}
- for e in have_list:
- for h in e.get("hooks", []):
- if (e["afi"], h["hook"]) not in want_keys:
- cmds.append(("delete", _BASE + [e["afi"], h["hook"], "filter"]))
-
- for entry in (config or []):
- afi = entry["afi"]
- have_afi = have_map.get(afi, {})
-
- for hook_entry in (entry.get("hooks") or []):
- hook = hook_entry["hook"]
- have_hook = have_afi.get(hook)
-
- if state == "replaced" and have_hook:
- want_cmds = _hook_cmds(afi, hook_entry, {}, "merged")
- have_cmds = _hook_cmds(afi,
- {"hook": hook,
- "default_action": have_hook.get("default_action"),
- "rules": have_hook.get("rules", [])},
- {}, "merged")
- if want_cmds != have_cmds:
- cmds.append(("delete", _BASE + [afi, hook, "filter"]))
- have_hook = None
-
- cmds += _hook_cmds(afi, hook_entry, have_hook,
- state if state not in ("replaced", "overridden")
- else "merged")
-
- return cmds
-
-
-ARGUMENT_SPEC = dict(
- config=dict(
- type="list",
- elements="dict",
- options=dict(
- afi=dict(type="str", choices=["ipv4", "ipv6"], required=True),
- hooks=dict(
- type="list",
- elements="dict",
- options=dict(
- hook=dict(type="str",
- choices=["input", "output", "forward"],
- required=True),
- default_action=dict(type="str",
- choices=["accept", "drop", "reject"]),
- description=dict(type="str"),
- rules=dict(
- type="list",
- elements="dict",
- options=dict(
- number=dict(type="int", required=True),
- action=dict(type="str",
- choices=["accept", "drop", "reject",
- "return", "queue", "continue"]),
- description=dict(type="str"),
- disable=dict(type="bool"),
- protocol=dict(type="str"),
- state=dict(type="str",
- choices=["established", "invalid",
- "new", "related"]),
- log=dict(type="bool"),
- source=dict(
- type="dict",
- options=dict(
- address=dict(type="str"),
- port=dict(type="str"),
- ),
- ),
- destination=dict(
- type="dict",
- options=dict(
- address=dict(type="str"),
- port=dict(type="str"),
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- state=dict(
- default="merged",
- choices=["merged", "replaced", "overridden", "deleted", "gathered"],
- ),
-)
-
-
-def main():
- module = AnsibleModule(ARGUMENT_SPEC, supports_check_mode=True)
- vyos = VyOSModule(module)
-
- state = module.params["state"]
- config = module.params.get("config") or []
-
- have = get_running_config(vyos)
-
- if state == "gathered":
- module.exit_json(changed=False, gathered=have)
-
- commands = build_commands(config, have, state)
-
- if module.check_mode:
- module.exit_json(changed=bool(commands), commands=commands, before=have)
-
- if commands:
- response = vyos.apply_commands(commands)
- saved = vyos.save_config()
- module.exit_json(
- changed=True,
- before=have,
- after=get_running_config(vyos),
- commands=commands,
- saved=saved,
- response=response,
- )
-
- module.exit_json(changed=False, before=have, after=have, commands=[])
-
-
-if __name__ == "__main__":
- main()