Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Category #144

Draft
wants to merge 11 commits into
base: latest
Choose a base branch
from
2 changes: 2 additions & 0 deletions docs/source/modules/alias.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Definition
"type","string","false","'host'","t","Type of value the alias should hold. One of: 'host', 'network', 'port', 'url', 'urltable', 'geoip', 'networkgroup', 'mac', 'dynipv6host', 'internal', 'external'"
"updatefreq_days","float","false","7.0","\-","Needed only for the alias-type 'urltable'. Interval to update its content. Per example: 0.5 for every 12 hours"
"interface","string","false","\-","int, if","Needed only for the alias-type 'dynipv6host'. Select the interface for the V6 dynamic IP"
"categories","list","false","\-","cat",":ref:`Categories <modules_category>` for this alias."
"reload","boolean","false","false","\-", .. include:: ../_include/param_reload.rst

.. include:: ../_include/param_basic.rst
Expand Down Expand Up @@ -75,6 +76,7 @@ Examples
# type: 'host' # default
# updatefreq_days: 3 # used only for type 'urltable'
# interface: lan # used only for the type 'dynipv6host'
# category: ['Ansible managed']
# ssl_ca_file: '/etc/ssl/certs/custom/ca.crt'
# ssl_verify: False
# api_key: !vault ... # alternative to 'api_credential_file'
Expand Down
85 changes: 85 additions & 0 deletions docs/source/modules/category.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
.. _modules_category:

.. include:: ../_include/head.rst

=================
Firewall Category
=================

**STATE**: unstable

**TESTS**: `category <https://github.com/ansibleguy/collection_opnsense/blob/latest/tests/category.yml>`_

**API Docs**: `Firewall - category <https://docs.opnsense.org/development/api/core/firewall.html>`_

**Service Docs**: `Categories <https://docs.opnsense.org/manual/firewall_categories.html>`_


Definition
**********

.. csv-table:: Definition
:header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment"
:widths: 15 10 10 10 10 45

"name","string","true","\-","\-","Name of the category."
"color","string","false","\-","\-","Color of the category."
"auto","boolean","false","false","\-","Mark category as Automatically added, will be removed when unused."

.. include:: ../_include/param_basic.rst

Usage
*****

Manage categories to ease maintenance of larger rulesets.

It currently just works with the 'Firewall' plugin:

- :ref:`ansibleguy.opnsense.alias <modules_alias>`, :ref:`ansibleguy.opnsense.alias_multi <modules_alias_multi>`
- :ref:`ansibleguy.opnsense.rule <modules_rule>`, :ref:`ansibleguy.opnsense.rule_multi <modules_rule_multi>`
- :ref:`ansibleguy.opnsense.source_nat <modules_source_nat>`

Examples
********

.. code-block:: yaml

- hosts: localhost
gather_facts: false
module_defaults:
group/ansibleguy.opnsense.all:
firewall: 'opnsense.template.ansibleguy.net'
api_credential_file: '/home/guy/.secret/opn.key'

ansibleguy.opnsense.list:
target: 'category'

tasks:
# add optional parameters commented-out
# required ones normally
# add their default values to get a brief overview of how the module works
- name: Example
ansibleguy.opnsense.category:
name: 'Ansible Managed'
# color: 028482
# auot: false
# state: 'absent'
# debug: false

- name: Adding something
ansibleguy.opnsense.category:
name: 'Ansible Managed'

- name: Changing something
ansibleguy.opnsense.category:
name: 'Ansible Managed'
color: 028482

- name: Listing categories
ansibleguy.opnsense.list:
# target: 'category'
register: existing_categories

- name: Printing
ansible.builtin.debug:
var: existing_categories.data
2 changes: 2 additions & 0 deletions docs/source/modules/rule.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ Definition
"gateway","string","false","\-","g, gw","Existing gateway to use"
"log","boolean","false","true","l","If rule matches should be shown in the firewall logs"
"description","string","false","\-","desc","Description for the rule"
"categories","list","false","\-","cat",":ref:`Categories <modules_category>` for this alias."
"state","string","false","'present'","st","State of the rule. One of: 'present', 'absent'"
"enabled","boolean","false","true","en","If the rule should be en- or disabled"
"uuid","string","false","\-","\-","Optionally you can supply the uuid of an existing rule"
Expand Down Expand Up @@ -138,6 +139,7 @@ Basic
# destination_invert: false
# log: true
# gateway: 'LAN_GW'
# category: ['Ansible managed']
# state: 'present'
# enabled: true
# uuid: 'a9d85c00-0aa2-4705-b855-96aae16e05d7' # optionally use uuid to identify existing rules
Expand Down
2 changes: 2 additions & 0 deletions docs/source/modules/source_nat.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Module alias: ansibleguy.opnsense.snat
"target_port","string","false","\-","np, nat_port",""
"log","boolean","false","true","l","If rule matches should be shown in the firewall logs"
"description","string","false","\-","desc","Description for the rule"
"categories","list","false","\-","cat",":ref:`Categories <modules_category>` for this alias."
"state","string","false","'present'","st","State of the rule. One of: 'present', 'absent'"
"enabled","boolean","false","true","en","If the rule should be en- or disabled"
"uuid","string","false","\-","\-","Optionally you can supply the uuid of an existing rule"
Expand Down Expand Up @@ -133,6 +134,7 @@ Examples
# target_port: none
# no_nat: false
# log: true
# category: ['Ansible managed']
# enabled: true
# debug: false
# state: 'present'
Expand Down
1 change: 1 addition & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ action_groups:
- ansibleguy.opnsense.acme_action
- ansibleguy.opnsense.acme_certificate
all:
- ansibleguy.opnsense.category
- metadata:
extend_group:
- ansibleguy.opnsense.alias
Expand Down
11 changes: 9 additions & 2 deletions plugins/module_utils/defaults/alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
'content': [],
'debug': False,
'updatefreq_days': 7.0,
'interface': None
'interface': None,
'categories': [],
}

ALIAS_MOD_ARG_ALIASES = {
Expand All @@ -19,7 +20,8 @@
'description': ['desc'],
'state': ['st'],
'enabled': ['en'],
'interface': ['int', 'if']
'interface': ['int', 'if'],
'categories': ['cat'],
}

ALIAS_MOD_ARGS = dict(
Expand All @@ -46,6 +48,11 @@
aliases=ALIAS_MOD_ARG_ALIASES['interface'], required=False,
description=' Select the interface for the V6 dynamic IP.',
),
categories=dict(
type='list', requird=False, default=ALIAS_DEFAULTS['categories'],
aliases=ALIAS_MOD_ARG_ALIASES['categories'], elements='str',
description='Select the categories for the alias.',
),
**STATE_MOD_ARG,
**OPN_MOD_ARGS,
)
7 changes: 7 additions & 0 deletions plugins/module_utils/defaults/rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
'enabled': True,
'description': '',
'debug': False,
'categories': [],
}

RULE_MOD_ARG_ALIASES = {
Expand All @@ -42,6 +43,7 @@
'description': ['desc'],
'state': ['st'],
'enabled': ['en'],
'categories': ['cat'],
}

RULE_MATCH_FIELDS_ARG = dict(
Expand Down Expand Up @@ -119,6 +121,11 @@
aliases=RULE_MOD_ARG_ALIASES['description']
),
uuid=dict(type='str', required=False, description='Optionally you can supply the uuid of an existing rule'),
categories=dict(
type='list', requird=False, default=RULE_DEFAULTS['categories'],
aliases=RULE_MOD_ARG_ALIASES['categories'], elements='str',
description='Select the categories for the rule.',
),
**STATE_MOD_ARG,
**RULE_MATCH_FIELDS_ARG,
**OPN_MOD_ARGS,
Expand Down
23 changes: 23 additions & 0 deletions plugins/module_utils/helper/category.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from ansible.module_utils.basic import AnsibleModule


def resolve_categories(module: AnsibleModule, params: dict) -> None:
if not hasattr(module, 'existing_categories'):
categories = module.s.get(cnf={
'module': 'firewall',
'controller': 'category',
'command': 'get',
})

module.existing_categories = {
category['name']: uuid
for uuid, category in categories['category']['categories']['category'].items()
}

if not isinstance(params['categories'], list):
params['categories'] = [params['categories']]

params['categories'] = [
module.existing_categories.get(name, name)
for name in params['categories']
]
38 changes: 38 additions & 0 deletions plugins/module_utils/helper/category_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# pylint: disable=C0415
import pytest


DUMMY_CATEGORIES = {"category": {"categories": {"category": {
"c355696d-f464-43ff-950c-d55ed87559e6": {"name": "ANSIBLE_TEST_1_1"},
"006849af-893d-4c62-9a0d-b1858071238f": {"name": "ANSIBLE_TEST_1_2"},
}}}}


class DummySession:
@staticmethod
def get(**_kw):
return DUMMY_CATEGORIES


class DummyModule:
def __init__(self, **params):
self.p = params
self.s = DummySession


@pytest.mark.parametrize('categories, result', [
('ANSIBLE_TEST_1_1', ['c355696d-f464-43ff-950c-d55ed87559e6']),
(['ANSIBLE_TEST_1_1'], ['c355696d-f464-43ff-950c-d55ed87559e6']),
(
['ANSIBLE_TEST_1_2', 'ANSIBLE_TEST_1_1'],
['006849af-893d-4c62-9a0d-b1858071238f', 'c355696d-f464-43ff-950c-d55ed87559e6']
),
(['uuid-3', 'ANSIBLE_TEST_1_1'], ['uuid-3', 'c355696d-f464-43ff-950c-d55ed87559e6']),
], ids=str)
def test_resolve_categories(categories, result):
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.category import resolve_categories

m = DummyModule(categories=categories)
resolve_categories(m, m.p)

assert m.p['categories'] == result
35 changes: 13 additions & 22 deletions plugins/module_utils/helper/purge.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,22 @@ def purge(


def check_purge_filter(module: AnsibleModule, item: dict) -> bool:
to_purge = True
matched = not module.params['filter_invert']

for filter_key, filter_value in module.params['filters'].items():
if module.params['filter_invert']:
# purge all except matches
if isinstance(filter_value, list):
if module.params['filter_partial']:
if str(item[filter_key]).find(filter_value) != -1:
to_purge = False
break

else:
if item[filter_key] == filter_value:
to_purge = False
break

if any(fv not in item[filter_key] for fv in filter_value):
return not matched
elif isinstance(filter_value, list):
if sorted(item[filter_key]) != sorted(filter_value):
return not matched
else:
# purge only matches
if module.params['filter_partial']:
if str(item[filter_key]).find(filter_value) == -1:
to_purge = False
break

else:
if item[filter_key] != filter_value:
to_purge = False
break
if module.params['filter_partial']:
if filter_value not in str(item[filter_key]):
return not matched
elif item[filter_key] != filter_value:
return not matched

return to_purge
return matched
Loading
Loading