Skip to content

Commit

Permalink
Merge pull request #109 from scsitteam/unbound_dnsbl
Browse files Browse the repository at this point in the history
Feature:  Add unbound DNSBL support
  • Loading branch information
ansibleguy authored Oct 25, 2024
2 parents f0c1d6d + 547b6f9 commit d6350b2
Show file tree
Hide file tree
Showing 7 changed files with 345 additions and 2 deletions.
2 changes: 1 addition & 1 deletion docs/source/modules/2_list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ In most cases the returned type of this module ist a list of dictionaries.
:header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment"
:widths: 15 10 10 10 10 45

"target","string","true","\-","tgt, t","What part of the running config should be queried/listed. One of: 'alias', 'rule', 'route', 'cron', 'syslog', 'package', 'unbound_general', 'unbound_acl', 'unbound_host', 'unbound_domain', 'unbound_dot', 'unbound_forward', 'unbound_host_alias', 'ipsec_cert', 'shaper_pipe', 'shaper_queue', 'shaper_rule', 'monit_service', 'monit_test', 'monit_alert', 'wireguard_server', 'wireguard_peer', 'interface_lagg', 'interface_vlan', 'interface_vxlan', 'source_nat', 'frr_bfd', 'frr_bgp_general', 'frr_bgp_neighbor', 'frr_bgp_prefix_list', 'frr_bgp_community_list', 'frr_bgp_as_path', 'frr_bgp_route_map', 'frr_ospf_general', 'frr_ospf_prefix_list', 'frr_ospf_interface', 'frr_ospf_route_map', 'frr_ospf_network', 'frr_ospf3_general', 'frr_ospf3_interface', 'frr_rip', 'bind_general', 'bind_blocklist', 'bind_acl', 'bind_domain', 'bind_record', 'interface_vip', 'webproxy_general', 'webproxy_cache', 'webproxy_parent', 'webproxy_traffic', 'webproxy_forward', 'webproxy_acl', 'webproxy_icap', 'webproxy_auth', 'webproxy_remote_acl', 'webproxy_pac_proxy', 'webproxy_pac_match', 'webproxy_pac_rule'"
"target","string","true","\-","tgt, t","What part of the running config should be queried/listed. One of: 'alias', 'rule', 'route', 'cron', 'syslog', 'package', 'unbound_general', 'unbound_acl', 'unbound_host', 'unbound_domain', 'unbound_dot', 'unbound_forward', 'unbound_host_alias', 'ipsec_cert', 'shaper_pipe', 'shaper_queue', 'shaper_rule', 'monit_service', 'monit_test', 'monit_alert', 'wireguard_server', 'wireguard_peer', 'interface_lagg', 'interface_vlan', 'interface_vxlan', 'source_nat', 'frr_bfd', 'frr_bgp_general', 'frr_bgp_neighbor', 'frr_bgp_prefix_list', 'frr_bgp_community_list', 'frr_bgp_as_path', 'frr_bgp_route_map', 'frr_ospf_general', 'frr_ospf_prefix_list', 'frr_ospf_interface', 'frr_ospf_route_map', 'frr_ospf_network', 'frr_ospf3_general', 'frr_ospf3_interface', 'frr_rip', 'bind_general', 'bind_blocklist', 'bind_acl', 'bind_domain', 'bind_record', 'interface_vip', 'webproxy_general', 'webproxy_cache', 'webproxy_parent', 'webproxy_traffic', 'webproxy_forward', 'webproxy_acl', 'webproxy_icap', 'webproxy_auth', 'webproxy_remote_acl', 'webproxy_pac_proxy', 'webproxy_pac_match', 'webproxy_pac_rule', 'unbound_dnsbl'"

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

Expand Down
88 changes: 88 additions & 0 deletions docs/source/modules/unbound_dnsbl.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
.. _modules_unbound_dnsbl:

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

============
MODULE TITLE
============

**STATE**: unstable

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

**API Docs**: `unbound_dnsbl <https://docs.opnsense.org/development/api/core/unbound.html>`_

**Service Docs**: `Unbound DNS - Blocklists <https://docs.opnsense.org/manual/unbound.html#blocklists>`_


Definition
**********

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

"parameter name","parameter type","if is required","default value","aliases","description"
"safesearch","boolean","false","false","\-","Force the usage of SafeSearch on Google, DuckDuckGo, Bing, Qwant, PixaBay and YouTube"
"type","list of strings","false","[]","\-","Select which kind of DNSBL you want to use"
"whitelists","list of strings","false","[]","whitelist, allowlist, allowlists","List of domains to whitelist. You can use regular expressions"
"blocklists","list of strings","false","[]","blocklist","List of domains to blocklist. Only exact matches are supported"
"wildcards","list of strings","false","[]","wildcard","List of wildcard domains to blocklist. All subdomains of the given domain will be blocked. Blocking first-level domains is not supported"
"address","strings","false","","\-","Destination ip address for entries in the blocklist (leave empty to use default: 0.0.0.0). Not used when "Return NXDOMAIN" is checked"
"nxdomain","bool","false","false","\-","Use the DNS response code NXDOMAIN instead of a destination address"
"enabled","boolean","false","true","\-","Enable the usage of DNS blocklists"
"reload","boolean","false","true","\-", .. include:: ../_include/param_reload.rst

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

Usage
*****

Manage Unbound DNS blocklists and exceptions.

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: 'unbound_dnsbl'
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.unbound_dnsbl:
type: atl
# safesearch: false
# lists: ['https://example.com/dns.blocklist']
# whitelists: ['ansibleguy.net']
# blocklists: ['example.net']
# wildcards: ['example.net']
# address: 192.168.254.254
# nxdomain: false
# enable: false
# state: 'absent'
# debug: false
- name: Configuring DNS Blocklists
ansibleguy.opnsense.unbound_dnsbl:
type: atl
enable: true
- name: Listing current config
ansibleguy.opnsense.list:
# target: 'unbound_dnsbl'
register: dnsbl_config
- name: Printing
ansible.builtin.debug:
var: dnsbl_config.data
1 change: 1 addition & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ action_groups:
- ansibleguy.opnsense.unbound_host
- ansibleguy.opnsense.unbound_domain
- ansibleguy.opnsense.unbound_host_alias
- ansibleguy.opnsense.unbound_dnsbl
ipsec:
- ansibleguy.opnsense.ipsec_auth_local
- ansibleguy.opnsense.ipsec_auth_remote
Expand Down
36 changes: 36 additions & 0 deletions plugins/module_utils/main/unbound_dnsbl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from ansible.module_utils.basic import AnsibleModule

from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.api import \
Session
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.cls import GeneralModule


# Supported as of OPNsense 23.7
class DnsBL(GeneralModule):
CMDS = {
'set': 'set',
'search': 'get',
}
API_KEY_PATH = 'unbound.dnsbl'
API_KEY_PATH_REQ = API_KEY_PATH
API_MOD = 'unbound'
API_CONT = 'settings'
API_CONT_REL = 'service'
API_CMD_REL = 'reconfigureGeneral'
FIELDS_CHANGE = [
'enabled', 'safesearch', 'type', 'lists', 'whitelists', 'blocklists', 'wildcards', 'address', 'nxdomain'
]
FIELDS_ALL = FIELDS_CHANGE
FIELDS_TYPING = {
'bool': ['enabled', 'safesearch', 'nxdomain'],
'list': ['type', 'lists', 'whitelists', 'blocklists', 'wildcards'],
}

def __init__(self, module: AnsibleModule, result: dict, session: Session = None):
GeneralModule.__init__(self=self, m=module, r=result, s=session)

def check(self) -> None:
# pylint: disable=W0201
self.settings = self._search_call()

self._build_diff()
6 changes: 5 additions & 1 deletion plugins/modules/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
'ipsec_child', 'ipsec_vti', 'ipsec_auth_local', 'ipsec_auth_remote', 'frr_general', 'unbound_general',
'unbound_acl', 'ids_general', 'ids_policy', 'ids_rule', 'ids_ruleset', 'ids_user_rule', 'ids_policy_rule',
'openvpn_instance', 'openvpn_static_key', 'openvpn_client_override', 'dhcrelay_destination', 'dhcrelay_relay',
'interface_lagg', 'interface_loopback',
'interface_lagg', 'interface_loopback', 'unbound_dnsbl',
]


Expand Down Expand Up @@ -119,6 +119,10 @@ def run_module():
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.unbound_forward \
import Forward as Target_Obj

elif target == 'unbound_dnsbl':
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.unbound_dnsbl import \
DnsBL as Target_Obj

elif target == 'syslog':
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.syslog import \
Syslog as Target_Obj
Expand Down
91 changes: 91 additions & 0 deletions plugins/modules/unbound_dnsbl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (C) 2024, AnsibleGuy <[email protected]>
# GNU General Public License v3.0+ (see https://www.gnu.org/licenses/gpl-3.0.txt)

# see: https://docs.opnsense.org/development/api/plugins/unbound.html

from ansible.module_utils.basic import AnsibleModule

from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.handler import \
module_dependency_error, MODULE_EXCEPTIONS

try:
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.wrapper import module_wrapper
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.defaults.main import \
OPN_MOD_ARGS, EN_ONLY_MOD_ARG, RELOAD_MOD_ARG
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.unbound_dnsbl import DnsBL

except MODULE_EXCEPTIONS:
module_dependency_error()


# DOCUMENTATION = 'https://opnsense.ansibleguy.net/en/latest/modules/unbound_general.html'
# EXAMPLES = 'https://opnsense.ansibleguy.net/en/latest/modules/unbound_general.html'


def run_module():
module_args = dict(
safesearch=dict(
type='bool', required=False, default=False,
description='Force the usage of SafeSearch on Google, DuckDuckGo, Bing, Qwant, PixaBay and YouTube'
),
type=dict(
type='list', elements='str', required=False, default=[], aliases=['bl'],
description='Select which kind of DNSBL you want to use'
),
lists=dict(
type='list', elements='str', required=False, default=[], aliases=['list'],
description='List of urls from where blocklist will be downloaded'
),
whitelists=dict(
type='list', elements='str', required=False, default=[], aliases=['whitelist', 'allowlist', 'allowlists'],
description='List of domains to whitelist. You can use regular expressions'
),
blocklists=dict(
type='list', elements='str', required=False, default=[], aliases=['blocklist'],
description='List of domains to blocklist. Only exact matches are supported'
),
wildcards=dict(
type='list', elements='str', required=False, default=[], aliases=['wildcard'],
description='List of wildcard domains to blocklist. All subdomains of the given domain will be blocked. '
'Blocking first-level domains is not supported'
),
address=dict(
type='str', required=False,
description='Destination ip address for entries in the blocklist (leave empty to use default: 0.0.0.0). '
'Not used when "Return NXDOMAIN" is checked'
),
nxdomain=dict(
type='bool', required=False, default=False,
description='Use the DNS response code NXDOMAIN instead of a destination address'
),
**EN_ONLY_MOD_ARG,
**OPN_MOD_ARGS,
**RELOAD_MOD_ARG,
)

result = dict(
changed=False,
diff={
'before': {},
'after': {},
}
)

module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True,
)

module_wrapper(DnsBL(module=module, result=result))
module.exit_json(**result)


def main():
run_module()


if __name__ == '__main__':
main()
123 changes: 123 additions & 0 deletions tests/unbound_dnsbl.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
---

Check failure on line 1 in tests/unbound_dnsbl.yml

View workflow job for this annotation

GitHub Actions / lint

1:4 [new-lines] wrong new line character: expected \n

- name: Testing Unbound DNS general settings
hosts: localhost
gather_facts: no
module_defaults:
group/ansibleguy.opnsense.all:
firewall: "{{ lookup('ansible.builtin.env', 'TEST_FIREWALL') }}"
api_credential_file: "{{ lookup('ansible.builtin.env', 'TEST_API_KEY') }}"
ssl_verify: false

ansibleguy.opnsense.list:
target: 'unbound_dnsbl'

tasks:
- name: Listing
ansibleguy.opnsense.list:
register: opn_pre1
failed_when: >
opn_pre1.failed or
'data' not in opn_pre1
- name: Configuring - failing because of invalid type
ansibleguy.opnsense.unbound_dnsbl:
type:
- ANSIBLE_TEST_1
register: opn_fail1
failed_when: not opn_fail1.failed
when: not ansible_check_mode

- name: Configuring - failing because of invalid address
ansibleguy.opnsense.unbound_dnsbl:
address: INVALID
register: opn_fail2
failed_when: not opn_fail2.failed
when: not ansible_check_mode

- name: Reset Configuring
ansibleguy.opnsense.unbound_dnsbl:
when: not ansible_check_mode

- name: Configuring
ansibleguy.opnsense.unbound_dnsbl:
type:
- atf
lists: ['https://example.com/dns.blocklist']
whitelists:
- opnsense.org
- ansibleguy.net
blocklists:
- example.tor
wildcards:
- example.tor
address: 192.168.255.255
nxdomain: false
enabled: true
register: opn1
failed_when: >
opn1.failed or
not opn1.changed
- name: Changing
ansibleguy.opnsense.unbound_dnsbl:
type:
- atf
whitelists:
- opnsense.org
- ansibleguy.net
blocklists:
- example.tor
wildcards:
- example.tor
nxdomain: true
enabled: true
register: opn2
failed_when: >
opn2.failed or
not opn2.changed
- name: Disabling 1
ansibleguy.opnsense.unbound_dnsbl:
type:
- atf
whitelists:
- opnsense.org
- ansibleguy.net
blocklists:
- example.tor
wildcards:
- example.tor
nxdomain: true
enabled: false
reload: false # speed
register: opn3
failed_when: >
opn3.failed or
not opn3.changed
when: not ansible_check_mode

- name: Nothing changed
ansibleguy.opnsense.unbound_dnsbl:
type:
- atf
whitelists:
- opnsense.org
- ansibleguy.net
blocklists:
- example.tor
wildcards:
- example.tor
nxdomain: true
enabled: false
reload: false # speed
register: opn4
failed_when: >
opn4.failed or
opn4.changed
when: not ansible_check_mode

- name: Cleanup
ansibleguy.opnsense.unbound_dnsbl:
reload: false
when: not ansible_check_mode

0 comments on commit d6350b2

Please sign in to comment.