Skip to content

Commit d4560ab

Browse files
authored
Merge pull request #146 from scsitteam/one_to_one
Implement one_to_one NAT/BINAT module
2 parents 140c7c5 + be45846 commit d4560ab

16 files changed

+483
-24
lines changed

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ not implemented => development => [testing](https://github.com/ansibleguy/collec
114114
| **DNS** | ansibleguy.opnsense.unbound_dot | [Docs](https://opnsense.ansibleguy.net/modules/unbound_dot.html) | stable |
115115
| **DNS** | ansibleguy.opnsense.unbound_host | [Docs](https://opnsense.ansibleguy.net/modules/unbound_host.html) | stable |
116116
| **DNS** | ansibleguy.opnsense.unbound_host_alias | [Docs](https://opnsense.ansibleguy.net/modules/unbound_host_alias.html) | stable |
117-
| **DNS** | ansibleguy.opnsense.unbound_dnsbl | [Docs](https://opnsense.ansibleguy.net/modules/unbound_host_alias.html) | unstable || **Syslog** | ansibleguy.opnsense.syslog | [Docs](https://opnsense.ansibleguy.net/modules/syslog.html) | stable |
117+
| **DNS** | ansibleguy.opnsense.unbound_dnsbl | [Docs](https://opnsense.ansibleguy.net/modules/unbound_host_alias.html) | unstable |
118+
| **Syslog** | ansibleguy.opnsense.syslog | [Docs](https://opnsense.ansibleguy.net/modules/syslog.html) | stable |
118119
| **IPSec** | ansibleguy.opnsense.ipsec_connection, ansibleguy.opnsense.ipsec_tunnel | [Docs](https://opnsense.ansibleguy.net/modules/ipsec.html) | stable |
119120
| **IPSec** | ansibleguy.opnsense.ipsec_pool, ansibleguy.opnsense.ipsec_network | [Docs](https://opnsense.ansibleguy.net/modules/ipsec.html) | stable |
120121
| **IPSec** | ansibleguy.opnsense.ipsec_auth_local | [Docs](https://opnsense.ansibleguy.net/modules/ipsec.html) | stable |
@@ -138,7 +139,8 @@ not implemented => development => [testing](https://github.com/ansibleguy/collec
138139
| **Interfaces** | ansibleguy.opnsense.interface_vip | [Docs](https://opnsense.ansibleguy.net/modules/interface.html) | stable |
139140
| **Interfaces** | ansibleguy.opnsense.interface_lagg | [Docs](https://opnsense.ansibleguy.net/modules/interface.html) | unstable |
140141
| **Interfaces** | ansibleguy.opnsense.interface_loopback | [Docs](https://opnsense.ansibleguy.net/modules/interface.html) | unstable |
141-
| **NAT** | ansibleguy.opnsense.source_nat, ansibleguy.opnsense.snat | [Docs](https://opnsense.ansibleguy.net/modules/source_nat.html) | stable |
142+
| **NAT** | ansibleguy.opnsense.nat_source | [Docs](https://opnsense.ansibleguy.net/modules/source_nat.html) | stable |
143+
| **NAT** | ansibleguy.opnsense.nat_one_to_one | [Docs](https://opnsense.ansibleguy.net/modules/one_to_one.html) | unstable |
142144
| **Dynamic Routing** | ansibleguy.opnsense.frr_diagnostic | [Docs](https://opnsense.ansibleguy.net/modules/frr_diagnostic.html) | stable |
143145
| **Dynamic Routing** | ansibleguy.opnsense.frr_general | [Docs](https://opnsense.ansibleguy.net/modules/frr_general.html) | stable |
144146
| **Dynamic Routing** | ansibleguy.opnsense.frr_bfd_general | [Docs](https://opnsense.ansibleguy.net/modules/frr_bfd.html#ansibleguy-opnsense-frr-bfd-general) | stable |

docs/source/modules/2_list.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ In most cases the returned type of this module ist a list of dictionaries.
2121
:header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment"
2222
:widths: 15 10 10 10 10 45
2323

24-
"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_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', , 'interface_gre', 'postfix_general', 'postfix_domain', 'postfix_recipient', 'postfix_recipientbcc', 'postfix_sender', 'postfix_senderbcc', 'postfix_sendercanonical', 'postfix_headercheck', 'postfix_address'"
24+
"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_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', 'nat_source', 'nat_one_to_one', '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', 'interface_gre', 'postfix_general', 'postfix_domain', 'postfix_recipient', 'postfix_recipientbcc', 'postfix_sender', 'postfix_senderbcc', 'postfix_sendercanonical', 'postfix_headercheck', 'postfix_address'"
2525

2626
.. include:: ../_include/param_basic.rst
2727

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
.. _modules_nat_one_to_one:
2+
3+
.. include:: ../_include/head.rst
4+
5+
==============
6+
NAT One-To-One
7+
==============
8+
9+
**STATE**: unstable
10+
11+
**TESTS**: `Playbook <https://github.com/ansibleguy/collection_opnsense/blob/latest/tests/nat_one_to_one.yml>`_
12+
13+
**API Docs**: `one_to_one <https://docs.opnsense.org/development/api/core/firewall.html>`_
14+
15+
**Service Docs**: `one_to_one <https://docs.opnsense.org/manual/nat.html#one-to-one>`_
16+
17+
Contribution
18+
************
19+
20+
Thanks to `@jiuka <https://github.com/jiuka>`_ for developing this module!
21+
22+
----
23+
24+
Info
25+
****
26+
27+
Savepoint
28+
=========
29+
30+
You can prevent lockout-situations using the savepoint systems:
31+
32+
- :ref:`ansibleguy.opnsense.savepoint <modules_savepoint>`
33+
34+
35+
Definition
36+
**********
37+
38+
.. csv-table:: Definition
39+
:header: "Parameter", "Type", "Required", "Default", "Aliases", "Comment"
40+
:widths: 15 10 10 10 10 45
41+
42+
"match_fields","list","false","['interface', 'external'']","\-","Fields that are used to match configured rules with the running config - if any of those fields are changed, the module will think it's a new rule. At least one of: 'sequence', 'action', 'interface', 'direction', 'ip_protocol', 'protocol', 'source_invert', 'source_net', 'source_port', 'destination_invert', 'destination_net', 'destination_port', 'gateway', 'description', 'uuid'"
43+
"sequence","int","false","1","seq","Sequence for rule processing, Integer between 1 and 1000000"
44+
"log","boolean","false","true","l","If rule matches should be shown in the firewall logs"
45+
"interface","string","false for deletion, else true","\-","i, int","The interface to match this rule on"
46+
"type","string","false","binnat","\-","NAT type to use. ONE of: binat or nat. See `Some terms explained <https://docs.opnsense.org/manual/nat.html#some-terms-explained>`_."
47+
"external","string","false for deletion, else true","\-","external_net, ext, e","External subnet's starting address for the 1:1 mapping or network. This is the address or network the traffic will translate to/from."
48+
"source_net","string","false for deletion, else true","\-","s, src, source","Internal subnet for the 1:1 mapping."
49+
"source_invert","boolean","false","false","si, src_inv, src_not","Inverted matching of the source."
50+
"destination_net","string","false","'any'","d, dest, destination","The 1:1 mapping will only be used for connections to or from the specified destination. Hint: this is usually 'any'."
51+
"destination_invert","boolean","false","false","di, dest_inv, dest_not","Inverted matching of the destination"
52+
"nat_reflection","string","false","''","\-","One of: '', enable, disable. See `Some terms explained <https://docs.opnsense.org/manual/nat.html#some-terms-explained>`_."
53+
"description","string","false","\-","desc","Description for the rule"
54+
"state","string","false","'present'","st","State of the rule. One of: 'present', 'absent'"
55+
"enabled","boolean","false","true","en","If the rule should be en- or disabled"
56+
"uuid","string","false","\-","\-","Optionally you can supply the uuid of an existing rule"
57+
"reload","boolean","false","true","apply", .. include:: ../_include/param_reload.rst
58+
59+
.. include:: ../_include/param_basic.rst
60+
61+
----
62+
63+
Usage
64+
*****
65+
66+
To add One-to-One NAT rules - see: `OPNSense Documentation <https://docs.opnsense.org/manual/nat.html#one-to-one>`_
67+
68+
Examples
69+
********
70+
71+
.. code-block:: yaml
72+
73+
- hosts: localhost
74+
gather_facts: false
75+
module_defaults:
76+
group/ansibleguy.opnsense.all:
77+
firewall: 'opnsense.template.ansibleguy.net'
78+
api_credential_file: '/home/guy/.secret/opn.key'
79+
80+
ansibleguy.opnsense.list:
81+
target: 'nat_one_to_one'
82+
83+
tasks:
84+
# add optional parameters commented-out
85+
# required ones normally
86+
# add their default values to get a brief overview of how the module works
87+
- name: Example
88+
ansibleguy.opnsense.nat_one_to_one:
89+
#sequence: 1
90+
interface: 'lan'
91+
#type: binnat
92+
external: '8.8.8.8'
93+
source_net: '192.168.0.1'
94+
#source_invert: false
95+
#destination_net: 'any'
96+
#destination_invert: false
97+
#nat_reflection: ''
98+
description: 'Map External IP 8.8.8.8 to Internal 192.168.0.1'
99+
# enabled: true
100+
# state: 'absent'
101+
# debug: false
102+
103+
- name: Listing jobs
104+
ansibleguy.opnsense.list:
105+
# target: 'nat_one_to_one'
106+
register: existing_one_to_one
107+
108+
- name: Printing
109+
ansible.builtin.debug:
110+
var: existing_one_to_one.data

docs/source/modules/source_nat.rst renamed to docs/source/modules/nat_source.rst

+13-10
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
.. _modules_source_nat:
1+
.. _modules_nat_source:
22

33
.. include:: ../_include/head.rst
44

55
==========
6-
Source NAT
6+
NAT Source
77
==========
88

99
**STATE**: stable
1010

11-
**TESTS**: `Playbook <https://github.com/ansibleguy/collection_opnsense/blob/latest/tests/source_nat.yml>`_
11+
**TESTS**: `Playbook <https://github.com/ansibleguy/collection_opnsense/blob/latest/tests/nat_source.yml>`_
1212

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

@@ -32,6 +32,7 @@ This plugin has some limitations you need to know of:
3232
* per example see menu: 'Interface - Assignments - Interface ID (in brackets)'
3333
* this brings problems if the interface-names are not the same on both nodes when using HA-setups
3434

35+
----
3536

3637
Info
3738
****
@@ -81,6 +82,8 @@ Module alias: ansibleguy.opnsense.snat
8182

8283
.. include:: ../_include/param_basic.rst
8384

85+
----
86+
8487
Usage
8588
*****
8689

@@ -107,15 +110,15 @@ Examples
107110
firewall: 'opnsense.template.ansibleguy.net'
108111
api_credential_file: '/home/guy/.secret/opn.key'
109112
110-
ansibleguy.opnsense.source_nat:
113+
ansibleguy.opnsense.nat_source:
111114
match_fields: ['description']
112115
113116
ansibleguy.opnsense.list:
114-
target: 'source_nat'
117+
target: 'nat_source'
115118
116119
tasks:
117120
- name: Example
118-
ansibleguy.opnsense.source_nat:
121+
ansibleguy.opnsense.nat_source:
119122
description: 'example'
120123
match_fields: ['description']
121124
target: '192.168.0.1'
@@ -139,7 +142,7 @@ Examples
139142
# reload: true
140143
141144
- name: Adding rule
142-
ansibleguy.opnsense.source_nat:
145+
ansibleguy.opnsense.nat_source:
143146
description: 'test1'
144147
source: '192.168.0.0/24'
145148
destination: '10.0.0.0/24'
@@ -148,7 +151,7 @@ Examples
148151
# match_fields: ['description']
149152
150153
- name: Disabling rule
151-
ansibleguy.opnsense.source_nat:
154+
ansibleguy.opnsense.nat_source:
152155
description: 'test1'
153156
source: '192.168.0.0/24'
154157
destination: '10.0.0.0/24'
@@ -159,15 +162,15 @@ Examples
159162
160163
- name: Listing
161164
ansibleguy.opnsense.list:
162-
# target: 'source_nat'
165+
# target: 'nat_source'
163166
register: existing_entries
164167
165168
- name: Printing peers
166169
ansible.builtin.debug:
167170
var: existing_entries.data
168171
169172
- name: Removing rule
170-
ansibleguy.opnsense.source_nat:
173+
ansibleguy.opnsense.nat_source:
171174
description: 'test1'
172175
state: 'absent'
173176
# match_fields: ['description']

docs/source/modules/savepoint.rst

+3-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ Here is the basic process:
2727
It currently just works with the 'Firewall' plugin:
2828

2929
- :ref:`ansibleguy.opnsense.rule <modules_rule>`
30-
- :ref:`ansibleguy.opnsense.source_nat <modules_source_nat>`
30+
- :ref:`ansibleguy.opnsense.nat_source <modules_nat_source>`
31+
- :ref:`ansibleguy.opnsense.nat_one_to_one <modules_nat_one_to_one>`
3132

3233
Definition
3334
**********
@@ -38,7 +39,7 @@ Definition
3839

3940
"name","string","false","'create'","Action to execute. One of: 'create', 'revert', 'apply', 'cancel_rollback', 'cancel'"
4041
"revision","string","false, true if action is one of 'apply', 'revert' or 'cancel_rollback'","\-","Savepoint revision to apply, revert or cancel_rollback"
41-
"controller","string","false","'filter'","Controller to manage the savepoint of. One of: 'source_nat', 'filter'"
42+
"controller","string","false","'filter'","Controller to manage the savepoint of. One of: 'source_nat', 'filter', 'one_to_one'"
4243
"api_module","string","false","'firewall'","Module to manage the savepoint of. Currently only supports 'firewall'"
4344

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

meta/runtime.yml

+6-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ action_groups:
9595
- ansibleguy.opnsense.route
9696
- ansibleguy.opnsense.gateway
9797
nat:
98-
- ansibleguy.opnsense.source_nat
98+
- ansibleguy.opnsense.nat_source
99+
- ansibleguy.opnsense.nat_one_to_one
99100
system:
100101
- ansibleguy.opnsense.list
101102
- ansibleguy.opnsense.reload
@@ -222,3 +223,7 @@ plugin_routing:
222223
redirect: ansibleguy.opnsense.acme_validation
223224
acme_automation:
224225
redirect: ansibleguy.opnsense.acme_action
226+
source_nat:
227+
redirect: ansibleguy.opnsense.nat_source
228+
one_to_one:
229+
redirect: ansibleguy.opnsense.nat_one_to_one
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from ansible.module_utils.basic import AnsibleModule
2+
3+
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.api import \
4+
Session
5+
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.main import \
6+
validate_int_fields, is_unset
7+
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.cls import BaseModule
8+
9+
10+
class OneToOne(BaseModule):
11+
FIELD_ID = 'name'
12+
CMDS = {
13+
'add': 'addRule',
14+
'del': 'delRule',
15+
'set': 'setRule',
16+
'search': 'get',
17+
'toggle': 'toggleRule',
18+
}
19+
API_KEY_PATH = 'filter.onetoone.rule'
20+
API_MOD = 'firewall'
21+
API_CONT = 'one_to_one'
22+
FIELDS_CHANGE = [
23+
'log', 'sequence', 'interface', 'type', 'source_net', 'source_invert', 'destination_net', 'destination_invert',
24+
'external', 'nat_reflection', 'description'
25+
26+
]
27+
FIELDS_ALL = ['enabled']
28+
FIELDS_ALL.extend(FIELDS_CHANGE)
29+
FIELDS_TRANSLATE = {
30+
'source_invert': 'source_not',
31+
'destination_invert': 'destination_not',
32+
'nat_reflection': 'natreflection',
33+
}
34+
FIELDS_TYPING = {
35+
'bool': ['enabled', 'log', 'source_invert', 'destination_invert'],
36+
'list': [],
37+
'select': ['interface', 'type', 'nat_reflection'],
38+
'int': [],
39+
}
40+
INT_VALIDATIONS = {
41+
'sequence': {'min': 1, 'max': 99999},
42+
}
43+
EXIST_ATTR = 'rule'
44+
API_CMD_REL = 'apply'
45+
46+
def __init__(self, module: AnsibleModule, result: dict, session: Session = None):
47+
BaseModule.__init__(self=self, m=module, r=result, s=session)
48+
self.rule = {}
49+
self.existing_additionalstuff = None
50+
51+
def check(self) -> None:
52+
if self.p['state'] == 'present':
53+
if is_unset(self.p['interface']):
54+
self.m.fail_json(
55+
"You need to provide an 'interface' to create a one-to-one"
56+
)
57+
58+
validate_int_fields(module=self.m, data=self.p, field_minmax=self.INT_VALIDATIONS)
59+
60+
self.b.find(match_fields=self.p['match_fields'])
61+
62+
if self.p['state'] == 'present':
63+
self.r['diff']['after'] = self.b.build_diff(data=self.p)

plugins/modules/list.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
'interface_lagg', 'interface_loopback', 'unbound_dnsbl', 'dhcp_reservation', 'acme_general', 'acme_account',
4040
'acme_validation', 'acme_action', 'acme_certificate', 'postfix_general', 'postfix_domain', 'postfix_recipient',
4141
'postfix_recipientbcc', 'postfix_sender', 'postfix_senderbcc', 'postfix_sendercanonical', 'postfix_headercheck',
42-
'postfix_address', 'dhcp_subnet', 'dhcp_general', 'interface_gre',
42+
'postfix_address', 'dhcp_subnet', 'dhcp_general', 'interface_gre', 'nat_one_to_one', 'nat_source',
4343
]
4444

4545

@@ -190,10 +190,14 @@ def run_module():
190190
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.interface_gre import \
191191
Gre as Target_Obj
192192

193-
elif target == 'source_nat':
194-
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.source_nat import \
193+
elif target in ['source_nat', 'nat_source']:
194+
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.nat_source import \
195195
SNat as Target_Obj
196196

197+
elif target == 'nat_one_to_one':
198+
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.nat_one_to_one import \
199+
OneToOne as Target_Obj
200+
197201
elif target == 'frr_general':
198202
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.main.frr_general \
199203
import General as Target_Obj

0 commit comments

Comments
 (0)