mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
ufw: allow to insert rules relative to first/last IPv4/IPv6 rules (#49796)
* Insert should have type int. * Add insert_relative_to option. * Add changelog. * Add tests. * Improve comment.
This commit is contained in:
parent
2e3964b474
commit
09f78d2f6c
4 changed files with 166 additions and 3 deletions
3
changelogs/fragments/49796-ufw-insert-relative-to.yml
Normal file
3
changelogs/fragments/49796-ufw-insert-relative-to.yml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
minor_changes:
|
||||||
|
- "ufw - type of option ``insert`` is now enforced to be ``int``."
|
||||||
|
- "ufw - new ``insert_relative_to`` option allows to specify rule insertion position relative to first/last IPv4/IPv6 address."
|
|
@ -52,7 +52,34 @@ options:
|
||||||
choices: [ on, off, low, medium, high, full ]
|
choices: [ on, off, low, medium, high, full ]
|
||||||
insert:
|
insert:
|
||||||
description:
|
description:
|
||||||
- Insert the corresponding rule as rule number NUM
|
- Insert the corresponding rule as rule number NUM.
|
||||||
|
- Note that ufw numbers rules starting with 1.
|
||||||
|
type: int
|
||||||
|
insert_relative_to:
|
||||||
|
description:
|
||||||
|
- Allows to interpret the index in I(insert) relative to a position.
|
||||||
|
- C(zero) interprets the rule number as an absolute index (i.e. 1 is
|
||||||
|
the first rule).
|
||||||
|
- C(first-ipv4) interprets the rule number relative to the index of the
|
||||||
|
first IPv4 rule, or relative to the position where the first IPv4 rule
|
||||||
|
would be if there is currently none.
|
||||||
|
- C(last-ipv4) interprets the rule number relative to the index of the
|
||||||
|
last IPv4 rule, or relative to the position where the last IPv4 rule
|
||||||
|
would be if there is currently none.
|
||||||
|
- C(first-ipv6) interprets the rule number relative to the index of the
|
||||||
|
first IPv6 rule, or relative to the position where the first IPv6 rule
|
||||||
|
would be if there is currently none.
|
||||||
|
- C(last-ipv6) interprets the rule number relative to the index of the
|
||||||
|
last IPv6 rule, or relative to the position where the last IPv6 rule
|
||||||
|
would be if there is currently none.
|
||||||
|
choices:
|
||||||
|
- zero
|
||||||
|
- first-ipv4
|
||||||
|
- last-ipv4
|
||||||
|
- first-ipv6
|
||||||
|
- last-ipv6
|
||||||
|
default: zero
|
||||||
|
version_added: "2.8"
|
||||||
rule:
|
rule:
|
||||||
description:
|
description:
|
||||||
- Add firewall rule
|
- Add firewall rule
|
||||||
|
@ -197,6 +224,30 @@ EXAMPLES = '''
|
||||||
src: 2001:db8::/32
|
src: 2001:db8::/32
|
||||||
port: 25
|
port: 25
|
||||||
|
|
||||||
|
- name: Deny all IPv6 traffic to tcp port 20 on this host
|
||||||
|
# this should be the first IPv6 rule
|
||||||
|
ufw:
|
||||||
|
rule: deny
|
||||||
|
proto: tcp
|
||||||
|
port: 20
|
||||||
|
to_ip: "::"
|
||||||
|
insert: 0
|
||||||
|
insert_relative_to: first-ipv6
|
||||||
|
|
||||||
|
- name: Deny all IPv4 traffic to tcp port 20 on this host
|
||||||
|
# This should be the third to last IPv4 rule
|
||||||
|
# (insert: -1 addresses the second to last IPv4 rule;
|
||||||
|
# so the new rule will be inserted before the second
|
||||||
|
# to last IPv4 rule, and will be come the third to last
|
||||||
|
# IPv4 rule.)
|
||||||
|
ufw:
|
||||||
|
rule: deny
|
||||||
|
proto: tcp
|
||||||
|
port: 20
|
||||||
|
to_ip: "::"
|
||||||
|
insert: -1
|
||||||
|
insert_relative_to: last-ipv4
|
||||||
|
|
||||||
# Can be used to further restrict a global FORWARD policy set to allow
|
# Can be used to further restrict a global FORWARD policy set to allow
|
||||||
- name: Deny forwarded/routed traffic from subnet 1.2.3.0/24 to subnet 4.5.6.0/24
|
- name: Deny forwarded/routed traffic from subnet 1.2.3.0/24 to subnet 4.5.6.0/24
|
||||||
ufw:
|
ufw:
|
||||||
|
@ -247,7 +298,8 @@ def main():
|
||||||
direction=dict(type='str', choices=['in', 'incoming', 'out', 'outgoing', 'routed']),
|
direction=dict(type='str', choices=['in', 'incoming', 'out', 'outgoing', 'routed']),
|
||||||
delete=dict(type='bool', default=False),
|
delete=dict(type='bool', default=False),
|
||||||
route=dict(type='bool', default=False),
|
route=dict(type='bool', default=False),
|
||||||
insert=dict(type='str'),
|
insert=dict(type='int'),
|
||||||
|
insert_relative_to=dict(choices=['zero', 'first-ipv4', 'last-ipv4', 'first-ipv6', 'last-ipv6'], default='zero'),
|
||||||
rule=dict(type='str', choices=['allow', 'deny', 'limit', 'reject']),
|
rule=dict(type='str', choices=['allow', 'deny', 'limit', 'reject']),
|
||||||
interface=dict(type='str', aliases=['if']),
|
interface=dict(type='str', aliases=['if']),
|
||||||
log=dict(type='bool', default=False),
|
log=dict(type='bool', default=False),
|
||||||
|
@ -427,7 +479,32 @@ def main():
|
||||||
# [proto protocol] [app application] [comment COMMENT]
|
# [proto protocol] [app application] [comment COMMENT]
|
||||||
cmd.append([module.boolean(params['route']), 'route'])
|
cmd.append([module.boolean(params['route']), 'route'])
|
||||||
cmd.append([module.boolean(params['delete']), 'delete'])
|
cmd.append([module.boolean(params['delete']), 'delete'])
|
||||||
cmd.append([params['insert'], "insert %s" % params['insert']])
|
if params['insert'] is not None:
|
||||||
|
relative_to_cmd = params['insert_relative_to']
|
||||||
|
if relative_to_cmd == 'zero':
|
||||||
|
insert_to = params['insert']
|
||||||
|
else:
|
||||||
|
(_, numbered_state, _) = module.run_command([ufw_bin, 'status', 'numbered'])
|
||||||
|
numbered_line_re = re.compile(R'^\[ *([0-9]+)\] ')
|
||||||
|
lines = [(numbered_line_re.match(line), '(v6)' in line) for line in numbered_state.splitlines()]
|
||||||
|
lines = [(int(matcher.group(1)), ipv6) for (matcher, ipv6) in lines if matcher]
|
||||||
|
last_number = max([no for (no, ipv6) in lines]) if lines else 0
|
||||||
|
has_ipv4 = any([not ipv6 for (no, ipv6) in lines])
|
||||||
|
has_ipv6 = any([ipv6 for (no, ipv6) in lines])
|
||||||
|
if relative_to_cmd == 'first-ipv4':
|
||||||
|
relative_to = 1
|
||||||
|
elif relative_to_cmd == 'last-ipv4':
|
||||||
|
relative_to = max([no for (no, ipv6) in lines if not ipv6]) if has_ipv4 else 1
|
||||||
|
elif relative_to_cmd == 'first-ipv6':
|
||||||
|
relative_to = max([no for (no, ipv6) in lines if not ipv6]) + 1 if has_ipv4 else 1
|
||||||
|
elif relative_to_cmd == 'last-ipv6':
|
||||||
|
relative_to = last_number if has_ipv6 else last_number + 1
|
||||||
|
insert_to = params['insert'] + relative_to
|
||||||
|
if insert_to > last_number:
|
||||||
|
# ufw does not like it when the insert number is larger than the
|
||||||
|
# maximal rule number for IPv4/IPv6.
|
||||||
|
insert_to = None
|
||||||
|
cmd.append([insert_to is not None, "insert %s" % insert_to])
|
||||||
cmd.append([value])
|
cmd.append([value])
|
||||||
cmd.append([params['direction'], "%s" % params['direction']])
|
cmd.append([params['direction'], "%s" % params['direction']])
|
||||||
cmd.append([params['interface'], "on %s" % params['interface']])
|
cmd.append([params['interface'], "on %s" % params['interface']])
|
||||||
|
|
|
@ -16,3 +16,6 @@
|
||||||
state: disabled
|
state: disabled
|
||||||
- name: "Loading tasks from {{ item }}"
|
- name: "Loading tasks from {{ item }}"
|
||||||
include_tasks: "{{ item }}"
|
include_tasks: "{{ item }}"
|
||||||
|
- name: Reset to factory defaults
|
||||||
|
ufw:
|
||||||
|
state: reset
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
---
|
||||||
|
- name: Enable
|
||||||
|
ufw:
|
||||||
|
state: enabled
|
||||||
|
register: enable
|
||||||
|
|
||||||
|
# ## CREATE RULES ############################
|
||||||
|
- name: ipv4
|
||||||
|
ufw:
|
||||||
|
rule: deny
|
||||||
|
port: 22
|
||||||
|
to_ip: 0.0.0.0
|
||||||
|
- name: ipv4
|
||||||
|
ufw:
|
||||||
|
rule: deny
|
||||||
|
port: 23
|
||||||
|
to_ip: 0.0.0.0
|
||||||
|
|
||||||
|
- name: ipv6
|
||||||
|
ufw:
|
||||||
|
rule: deny
|
||||||
|
port: 122
|
||||||
|
to_ip: "::"
|
||||||
|
- name: ipv6
|
||||||
|
ufw:
|
||||||
|
rule: deny
|
||||||
|
port: 123
|
||||||
|
to_ip: "::"
|
||||||
|
|
||||||
|
- name: first-ipv4
|
||||||
|
ufw:
|
||||||
|
rule: deny
|
||||||
|
port: 10
|
||||||
|
to_ip: 0.0.0.0
|
||||||
|
insert: 0
|
||||||
|
insert_relative_to: first-ipv4
|
||||||
|
- name: last-ipv4
|
||||||
|
ufw:
|
||||||
|
rule: deny
|
||||||
|
port: 11
|
||||||
|
to_ip: 0.0.0.0
|
||||||
|
insert: 0
|
||||||
|
insert_relative_to: last-ipv4
|
||||||
|
|
||||||
|
- name: first-ipv6
|
||||||
|
ufw:
|
||||||
|
rule: deny
|
||||||
|
port: 110
|
||||||
|
to_ip: "::"
|
||||||
|
insert: 0
|
||||||
|
insert_relative_to: first-ipv6
|
||||||
|
- name: last-ipv6
|
||||||
|
ufw:
|
||||||
|
rule: deny
|
||||||
|
port: 111
|
||||||
|
to_ip: "::"
|
||||||
|
insert: 0
|
||||||
|
insert_relative_to: last-ipv6
|
||||||
|
|
||||||
|
# ## CHECK RESULT ############################
|
||||||
|
- name: Get rules
|
||||||
|
shell: |
|
||||||
|
ufw status | grep DENY | cut -f 1-2 -d ' ' | grep -E "^(0\.0\.0\.0|::) [123]+"
|
||||||
|
# Note that there was also a rule "ff02::fb mDNS" on at least one CI run;
|
||||||
|
# to ignore these, the extra filtering (grepping for DENY and the regex) makes
|
||||||
|
# sure to remove all rules not added here.
|
||||||
|
register: ufw_status
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- ufw_status.stdout_lines == expected_stdout
|
||||||
|
vars:
|
||||||
|
expected_stdout:
|
||||||
|
- "0.0.0.0 10"
|
||||||
|
- "0.0.0.0 22"
|
||||||
|
- "0.0.0.0 11"
|
||||||
|
- "0.0.0.0 23"
|
||||||
|
- ":: 110"
|
||||||
|
- ":: 122"
|
||||||
|
- ":: 111"
|
||||||
|
- ":: 123"
|
Loading…
Reference in a new issue