Add explicit coverage of argspec choices with strings that shadow YAML bools (#72122)
* Add explicit coverage of argspec choices with strings that shadow YAML bools * ci_complete ci_coverage * Remove incidental_ufw * ci_complete ci_coverage
This commit is contained in:
parent
865113aa29
commit
cfa41898c4
10 changed files with 23 additions and 1379 deletions
|
@ -58,6 +58,13 @@ def main():
|
||||||
'thing': {},
|
'thing': {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
'choices_with_strings_like_bools': {
|
||||||
|
'type': 'str',
|
||||||
|
'choices': [
|
||||||
|
'on',
|
||||||
|
'off',
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required_if=(
|
required_if=(
|
||||||
('state', 'present', ('path', 'content'), True),
|
('state', 'present', ('path', 'content'), True),
|
||||||
|
|
|
@ -126,6 +126,18 @@
|
||||||
- thing: foo
|
- thing: foo
|
||||||
register: argspec_suboptions_list_no_elements
|
register: argspec_suboptions_list_no_elements
|
||||||
|
|
||||||
|
- argspec:
|
||||||
|
choices_with_strings_like_bools: on
|
||||||
|
register: argspec_choices_with_strings_like_bools_true
|
||||||
|
|
||||||
|
- argspec:
|
||||||
|
choices_with_strings_like_bools: 'on'
|
||||||
|
register: argspec_choices_with_strings_like_bools_true_bool
|
||||||
|
|
||||||
|
- argspec:
|
||||||
|
choices_with_strings_like_bools: off
|
||||||
|
register: argspec_choices_with_strings_like_bools_false
|
||||||
|
|
||||||
- assert:
|
- assert:
|
||||||
that:
|
that:
|
||||||
- argspec_required_if_fail is failed
|
- argspec_required_if_fail is failed
|
||||||
|
@ -163,3 +175,7 @@
|
||||||
|
|
||||||
- >-
|
- >-
|
||||||
argspec_suboptions_list_no_elements.suboptions_list_no_elements.0 == {'thing': 'foo'}
|
argspec_suboptions_list_no_elements.suboptions_list_no_elements.0 == {'thing': 'foo'}
|
||||||
|
|
||||||
|
- argspec_choices_with_strings_like_bools_true.choices_with_strings_like_bools == 'on'
|
||||||
|
- argspec_choices_with_strings_like_bools_true_bool.choices_with_strings_like_bools == 'on'
|
||||||
|
- argspec_choices_with_strings_like_bools_false.choices_with_strings_like_bools == 'off'
|
|
@ -1,13 +0,0 @@
|
||||||
shippable/posix/incidental
|
|
||||||
skip/aix
|
|
||||||
skip/power/centos
|
|
||||||
skip/osx
|
|
||||||
skip/macos
|
|
||||||
skip/freebsd
|
|
||||||
skip/rhel8.0
|
|
||||||
skip/rhel8.0b
|
|
||||||
skip/rhel8.1b
|
|
||||||
skip/docker
|
|
||||||
needs/root
|
|
||||||
destructive
|
|
||||||
needs/target/setup_epel
|
|
|
@ -1,34 +0,0 @@
|
||||||
---
|
|
||||||
# Make sure ufw is installed
|
|
||||||
- name: Install EPEL repository (RHEL only)
|
|
||||||
include_role:
|
|
||||||
name: setup_epel
|
|
||||||
when: ansible_distribution == 'RedHat'
|
|
||||||
- name: Install iptables (SuSE only)
|
|
||||||
package:
|
|
||||||
name: iptables
|
|
||||||
become: yes
|
|
||||||
when: ansible_os_family == 'Suse'
|
|
||||||
- name: Install ufw
|
|
||||||
become: yes
|
|
||||||
package:
|
|
||||||
name: ufw
|
|
||||||
|
|
||||||
# Run the tests
|
|
||||||
- block:
|
|
||||||
- include_tasks: run-test.yml
|
|
||||||
with_fileglob:
|
|
||||||
- "tests/*.yml"
|
|
||||||
become: yes
|
|
||||||
|
|
||||||
# Cleanup
|
|
||||||
always:
|
|
||||||
- pause:
|
|
||||||
# ufw creates backups of the rule files with a timestamp; if reset is called
|
|
||||||
# twice in a row fast enough (so that both timestamps are taken in the same second),
|
|
||||||
# the second call will notice that the backup files are already there and fail.
|
|
||||||
# Waiting one second fixes this problem.
|
|
||||||
seconds: 1
|
|
||||||
- name: Reset ufw to factory defaults and disable
|
|
||||||
ufw:
|
|
||||||
state: reset
|
|
|
@ -1,21 +0,0 @@
|
||||||
---
|
|
||||||
- pause:
|
|
||||||
# ufw creates backups of the rule files with a timestamp; if reset is called
|
|
||||||
# twice in a row fast enough (so that both timestamps are taken in the same second),
|
|
||||||
# the second call will notice that the backup files are already there and fail.
|
|
||||||
# Waiting one second fixes this problem.
|
|
||||||
seconds: 1
|
|
||||||
- name: Reset ufw to factory defaults
|
|
||||||
ufw:
|
|
||||||
state: reset
|
|
||||||
- name: Disable ufw
|
|
||||||
ufw:
|
|
||||||
# Some versions of ufw have a bug which won't disable on reset.
|
|
||||||
# That's why we explicitly deactivate here. See
|
|
||||||
# https://bugs.launchpad.net/ufw/+bug/1810082
|
|
||||||
state: disabled
|
|
||||||
- name: "Loading tasks from {{ item }}"
|
|
||||||
include_tasks: "{{ item }}"
|
|
||||||
- name: Reset to factory defaults
|
|
||||||
ufw:
|
|
||||||
state: reset
|
|
|
@ -1,402 +0,0 @@
|
||||||
---
|
|
||||||
# ############################################
|
|
||||||
- name: Make sure it is off
|
|
||||||
ufw:
|
|
||||||
state: disabled
|
|
||||||
- name: Enable (check mode)
|
|
||||||
ufw:
|
|
||||||
state: enabled
|
|
||||||
check_mode: yes
|
|
||||||
register: enable_check
|
|
||||||
- name: Enable
|
|
||||||
ufw:
|
|
||||||
state: enabled
|
|
||||||
register: enable
|
|
||||||
- name: Enable (idempotency)
|
|
||||||
ufw:
|
|
||||||
state: enabled
|
|
||||||
register: enable_idem
|
|
||||||
- name: Enable (idempotency, check mode)
|
|
||||||
ufw:
|
|
||||||
state: enabled
|
|
||||||
check_mode: yes
|
|
||||||
register: enable_idem_check
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- enable_check is changed
|
|
||||||
- enable is changed
|
|
||||||
- enable_idem is not changed
|
|
||||||
- enable_idem_check is not changed
|
|
||||||
|
|
||||||
# ############################################
|
|
||||||
- name: ipv4 allow (check mode)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: 0.0.0.0
|
|
||||||
check_mode: yes
|
|
||||||
register: ipv4_allow_check
|
|
||||||
- name: ipv4 allow
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: 0.0.0.0
|
|
||||||
register: ipv4_allow
|
|
||||||
- name: ipv4 allow (idempotency)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: 0.0.0.0
|
|
||||||
register: ipv4_allow_idem
|
|
||||||
- name: ipv4 allow (idempotency, check mode)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: 0.0.0.0
|
|
||||||
check_mode: yes
|
|
||||||
register: ipv4_allow_idem_check
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- ipv4_allow_check is changed
|
|
||||||
- ipv4_allow is changed
|
|
||||||
- ipv4_allow_idem is not changed
|
|
||||||
- ipv4_allow_idem_check is not changed
|
|
||||||
|
|
||||||
# ############################################
|
|
||||||
- name: delete ipv4 allow (check mode)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: 0.0.0.0
|
|
||||||
delete: yes
|
|
||||||
check_mode: yes
|
|
||||||
register: delete_ipv4_allow_check
|
|
||||||
- name: delete ipv4 allow
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: 0.0.0.0
|
|
||||||
delete: yes
|
|
||||||
register: delete_ipv4_allow
|
|
||||||
- name: delete ipv4 allow (idempotency)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: 0.0.0.0
|
|
||||||
delete: yes
|
|
||||||
register: delete_ipv4_allow_idem
|
|
||||||
- name: delete ipv4 allow (idempotency, check mode)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: 0.0.0.0
|
|
||||||
delete: yes
|
|
||||||
check_mode: yes
|
|
||||||
register: delete_ipv4_allow_idem_check
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- delete_ipv4_allow_check is changed
|
|
||||||
- delete_ipv4_allow is changed
|
|
||||||
- delete_ipv4_allow_idem is not changed
|
|
||||||
- delete_ipv4_allow_idem_check is not changed
|
|
||||||
|
|
||||||
# ############################################
|
|
||||||
- name: ipv6 allow (check mode)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: "::"
|
|
||||||
check_mode: yes
|
|
||||||
register: ipv6_allow_check
|
|
||||||
- name: ipv6 allow
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: "::"
|
|
||||||
register: ipv6_allow
|
|
||||||
- name: ipv6 allow (idempotency)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: "::"
|
|
||||||
register: ipv6_allow_idem
|
|
||||||
- name: ipv6 allow (idempotency, check mode)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: "::"
|
|
||||||
check_mode: yes
|
|
||||||
register: ipv6_allow_idem_check
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- ipv6_allow_check is changed
|
|
||||||
- ipv6_allow is changed
|
|
||||||
- ipv6_allow_idem is not changed
|
|
||||||
- ipv6_allow_idem_check is not changed
|
|
||||||
|
|
||||||
# ############################################
|
|
||||||
- name: delete ipv6 allow (check mode)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: "::"
|
|
||||||
delete: yes
|
|
||||||
check_mode: yes
|
|
||||||
register: delete_ipv6_allow_check
|
|
||||||
- name: delete ipv6 allow
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: "::"
|
|
||||||
delete: yes
|
|
||||||
register: delete_ipv6_allow
|
|
||||||
- name: delete ipv6 allow (idempotency)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: "::"
|
|
||||||
delete: yes
|
|
||||||
register: delete_ipv6_allow_idem
|
|
||||||
- name: delete ipv6 allow (idempotency, check mode)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: "::"
|
|
||||||
delete: yes
|
|
||||||
check_mode: yes
|
|
||||||
register: delete_ipv6_allow_idem_check
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- delete_ipv6_allow_check is changed
|
|
||||||
- delete_ipv6_allow is changed
|
|
||||||
- delete_ipv6_allow_idem is not changed
|
|
||||||
- delete_ipv6_allow_idem_check is not changed
|
|
||||||
|
|
||||||
|
|
||||||
# ############################################
|
|
||||||
- name: ipv4 allow (check mode)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: 0.0.0.0
|
|
||||||
check_mode: yes
|
|
||||||
register: ipv4_allow_check
|
|
||||||
- name: ipv4 allow
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: 0.0.0.0
|
|
||||||
register: ipv4_allow
|
|
||||||
- name: ipv4 allow (idempotency)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: 0.0.0.0
|
|
||||||
register: ipv4_allow_idem
|
|
||||||
- name: ipv4 allow (idempotency, check mode)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: 0.0.0.0
|
|
||||||
check_mode: yes
|
|
||||||
register: ipv4_allow_idem_check
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- ipv4_allow_check is changed
|
|
||||||
- ipv4_allow is changed
|
|
||||||
- ipv4_allow_idem is not changed
|
|
||||||
- ipv4_allow_idem_check is not changed
|
|
||||||
|
|
||||||
# ############################################
|
|
||||||
- name: delete ipv4 allow (check mode)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: 0.0.0.0
|
|
||||||
delete: yes
|
|
||||||
check_mode: yes
|
|
||||||
register: delete_ipv4_allow_check
|
|
||||||
- name: delete ipv4 allow
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: 0.0.0.0
|
|
||||||
delete: yes
|
|
||||||
register: delete_ipv4_allow
|
|
||||||
- name: delete ipv4 allow (idempotency)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: 0.0.0.0
|
|
||||||
delete: yes
|
|
||||||
register: delete_ipv4_allow_idem
|
|
||||||
- name: delete ipv4 allow (idempotency, check mode)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: 0.0.0.0
|
|
||||||
delete: yes
|
|
||||||
check_mode: yes
|
|
||||||
register: delete_ipv4_allow_idem_check
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- delete_ipv4_allow_check is changed
|
|
||||||
- delete_ipv4_allow is changed
|
|
||||||
- delete_ipv4_allow_idem is not changed
|
|
||||||
- delete_ipv4_allow_idem_check is not changed
|
|
||||||
|
|
||||||
# ############################################
|
|
||||||
- name: ipv6 allow (check mode)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: "::"
|
|
||||||
check_mode: yes
|
|
||||||
register: ipv6_allow_check
|
|
||||||
- name: ipv6 allow
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: "::"
|
|
||||||
register: ipv6_allow
|
|
||||||
- name: ipv6 allow (idempotency)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: "::"
|
|
||||||
register: ipv6_allow_idem
|
|
||||||
- name: ipv6 allow (idempotency, check mode)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: "::"
|
|
||||||
check_mode: yes
|
|
||||||
register: ipv6_allow_idem_check
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- ipv6_allow_check is changed
|
|
||||||
- ipv6_allow is changed
|
|
||||||
- ipv6_allow_idem is not changed
|
|
||||||
- ipv6_allow_idem_check is not changed
|
|
||||||
|
|
||||||
# ############################################
|
|
||||||
- name: delete ipv6 allow (check mode)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: "::"
|
|
||||||
delete: yes
|
|
||||||
check_mode: yes
|
|
||||||
register: delete_ipv6_allow_check
|
|
||||||
- name: delete ipv6 allow
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: "::"
|
|
||||||
delete: yes
|
|
||||||
register: delete_ipv6_allow
|
|
||||||
- name: delete ipv6 allow (idempotency)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: "::"
|
|
||||||
delete: yes
|
|
||||||
register: delete_ipv6_allow_idem
|
|
||||||
- name: delete ipv6 allow (idempotency, check mode)
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 23
|
|
||||||
to_ip: "::"
|
|
||||||
delete: yes
|
|
||||||
check_mode: yes
|
|
||||||
register: delete_ipv6_allow_idem_check
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- delete_ipv6_allow_check is changed
|
|
||||||
- delete_ipv6_allow is changed
|
|
||||||
- delete_ipv6_allow_idem is not changed
|
|
||||||
- delete_ipv6_allow_idem_check is not changed
|
|
||||||
|
|
||||||
# ############################################
|
|
||||||
- name: Reload ufw
|
|
||||||
ufw:
|
|
||||||
state: reloaded
|
|
||||||
register: reload
|
|
||||||
- name: Reload ufw (check mode)
|
|
||||||
ufw:
|
|
||||||
state: reloaded
|
|
||||||
check_mode: yes
|
|
||||||
register: reload_check
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- reload is changed
|
|
||||||
- reload_check is changed
|
|
||||||
|
|
||||||
# ############################################
|
|
||||||
- name: Disable (check mode)
|
|
||||||
ufw:
|
|
||||||
state: disabled
|
|
||||||
check_mode: yes
|
|
||||||
register: disable_check
|
|
||||||
- name: Disable
|
|
||||||
ufw:
|
|
||||||
state: disabled
|
|
||||||
register: disable
|
|
||||||
- name: Disable (idempotency)
|
|
||||||
ufw:
|
|
||||||
state: disabled
|
|
||||||
register: disable_idem
|
|
||||||
- name: Disable (idempotency, check mode)
|
|
||||||
ufw:
|
|
||||||
state: disabled
|
|
||||||
check_mode: yes
|
|
||||||
register: disable_idem_check
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- disable_check is changed
|
|
||||||
- disable is changed
|
|
||||||
- disable_idem is not changed
|
|
||||||
- disable_idem_check is not changed
|
|
||||||
|
|
||||||
# ############################################
|
|
||||||
- name: Re-enable
|
|
||||||
ufw:
|
|
||||||
state: enabled
|
|
||||||
- name: Reset (check mode)
|
|
||||||
ufw:
|
|
||||||
state: reset
|
|
||||||
check_mode: yes
|
|
||||||
register: reset_check
|
|
||||||
- pause:
|
|
||||||
# Should not be needed, but since ufw is ignoring --dry-run for reset
|
|
||||||
# (https://bugs.launchpad.net/ufw/+bug/1810082) we have to wait here as well.
|
|
||||||
seconds: 1
|
|
||||||
- name: Reset
|
|
||||||
ufw:
|
|
||||||
state: reset
|
|
||||||
register: reset
|
|
||||||
- pause:
|
|
||||||
# ufw creates backups of the rule files with a timestamp; if reset is called
|
|
||||||
# twice in a row fast enough (so that both timestamps are taken in the same second),
|
|
||||||
# the second call will notice that the backup files are already there and fail.
|
|
||||||
# Waiting one second fixes this problem.
|
|
||||||
seconds: 1
|
|
||||||
- name: Reset (idempotency)
|
|
||||||
ufw:
|
|
||||||
state: reset
|
|
||||||
register: reset_idem
|
|
||||||
- pause:
|
|
||||||
# Should not be needed, but since ufw is ignoring --dry-run for reset
|
|
||||||
# (https://bugs.launchpad.net/ufw/+bug/1810082) we have to wait here as well.
|
|
||||||
seconds: 1
|
|
||||||
- name: Reset (idempotency, check mode)
|
|
||||||
ufw:
|
|
||||||
state: reset
|
|
||||||
check_mode: yes
|
|
||||||
register: reset_idem_check
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- reset_check is changed
|
|
||||||
- reset is changed
|
|
||||||
- reset_idem is changed
|
|
||||||
- reset_idem_check is changed
|
|
|
@ -1,150 +0,0 @@
|
||||||
---
|
|
||||||
- name: Enable ufw
|
|
||||||
ufw:
|
|
||||||
state: enabled
|
|
||||||
|
|
||||||
# ############################################
|
|
||||||
- name: Make sure logging is off
|
|
||||||
ufw:
|
|
||||||
logging: no
|
|
||||||
- name: Logging (check mode)
|
|
||||||
ufw:
|
|
||||||
logging: yes
|
|
||||||
check_mode: yes
|
|
||||||
register: logging_check
|
|
||||||
- name: Logging
|
|
||||||
ufw:
|
|
||||||
logging: yes
|
|
||||||
register: logging
|
|
||||||
- name: Get logging
|
|
||||||
shell: |
|
|
||||||
ufw status verbose | grep "^Logging:"
|
|
||||||
register: ufw_logging
|
|
||||||
environment:
|
|
||||||
LC_ALL: C
|
|
||||||
- name: Logging (idempotency)
|
|
||||||
ufw:
|
|
||||||
logging: yes
|
|
||||||
register: logging_idem
|
|
||||||
- name: Logging (idempotency, check mode)
|
|
||||||
ufw:
|
|
||||||
logging: yes
|
|
||||||
check_mode: yes
|
|
||||||
register: logging_idem_check
|
|
||||||
- name: Logging (change, check mode)
|
|
||||||
ufw:
|
|
||||||
logging: full
|
|
||||||
check_mode: yes
|
|
||||||
register: logging_change_check
|
|
||||||
- name: Logging (change)
|
|
||||||
ufw:
|
|
||||||
logging: full
|
|
||||||
register: logging_change
|
|
||||||
- name: Get logging
|
|
||||||
shell: |
|
|
||||||
ufw status verbose | grep "^Logging:"
|
|
||||||
register: ufw_logging_change
|
|
||||||
environment:
|
|
||||||
LC_ALL: C
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- logging_check is changed
|
|
||||||
- logging is changed
|
|
||||||
- "ufw_logging.stdout == 'Logging: on (low)'"
|
|
||||||
- logging_idem is not changed
|
|
||||||
- logging_idem_check is not changed
|
|
||||||
- "ufw_logging_change.stdout == 'Logging: on (full)'"
|
|
||||||
- logging_change is changed
|
|
||||||
- logging_change_check is changed
|
|
||||||
|
|
||||||
# ############################################
|
|
||||||
- name: Default (check mode)
|
|
||||||
ufw:
|
|
||||||
default: reject
|
|
||||||
direction: incoming
|
|
||||||
check_mode: yes
|
|
||||||
register: default_check
|
|
||||||
- name: Default
|
|
||||||
ufw:
|
|
||||||
default: reject
|
|
||||||
direction: incoming
|
|
||||||
register: default
|
|
||||||
- name: Get defaults
|
|
||||||
shell: |
|
|
||||||
ufw status verbose | grep "^Default:"
|
|
||||||
register: ufw_defaults
|
|
||||||
environment:
|
|
||||||
LC_ALL: C
|
|
||||||
- name: Default (idempotency)
|
|
||||||
ufw:
|
|
||||||
default: reject
|
|
||||||
direction: incoming
|
|
||||||
register: default_idem
|
|
||||||
- name: Default (idempotency, check mode)
|
|
||||||
ufw:
|
|
||||||
default: reject
|
|
||||||
direction: incoming
|
|
||||||
check_mode: yes
|
|
||||||
register: default_idem_check
|
|
||||||
- name: Default (change, check mode)
|
|
||||||
ufw:
|
|
||||||
default: allow
|
|
||||||
direction: incoming
|
|
||||||
check_mode: yes
|
|
||||||
register: default_change_check
|
|
||||||
- name: Default (change)
|
|
||||||
ufw:
|
|
||||||
default: allow
|
|
||||||
direction: incoming
|
|
||||||
register: default_change
|
|
||||||
- name: Get defaults
|
|
||||||
shell: |
|
|
||||||
ufw status verbose | grep "^Default:"
|
|
||||||
register: ufw_defaults_change
|
|
||||||
environment:
|
|
||||||
LC_ALL: C
|
|
||||||
- name: Default (change again)
|
|
||||||
ufw:
|
|
||||||
default: deny
|
|
||||||
direction: incoming
|
|
||||||
register: default_change_2
|
|
||||||
- name: Default (change incoming implicitly, check mode)
|
|
||||||
ufw:
|
|
||||||
default: allow
|
|
||||||
check_mode: yes
|
|
||||||
register: default_change_implicit_check
|
|
||||||
- name: Default (change incoming implicitly)
|
|
||||||
ufw:
|
|
||||||
default: allow
|
|
||||||
register: default_change_implicit
|
|
||||||
- name: Get defaults
|
|
||||||
shell: |
|
|
||||||
ufw status verbose | grep "^Default:"
|
|
||||||
register: ufw_defaults_change_implicit
|
|
||||||
environment:
|
|
||||||
LC_ALL: C
|
|
||||||
- name: Default (change incoming implicitly, idempotent, check mode)
|
|
||||||
ufw:
|
|
||||||
default: allow
|
|
||||||
check_mode: yes
|
|
||||||
register: default_change_implicit_idem_check
|
|
||||||
- name: Default (change incoming implicitly, idempotent)
|
|
||||||
ufw:
|
|
||||||
default: allow
|
|
||||||
register: default_change_implicit_idem
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- default_check is changed
|
|
||||||
- default is changed
|
|
||||||
- "'reject (incoming)' in ufw_defaults.stdout"
|
|
||||||
- default_idem is not changed
|
|
||||||
- default_idem_check is not changed
|
|
||||||
- default_change_check is changed
|
|
||||||
- default_change is changed
|
|
||||||
- "'allow (incoming)' in ufw_defaults_change.stdout"
|
|
||||||
- default_change_2 is changed
|
|
||||||
- default_change_implicit_check is changed
|
|
||||||
- default_change_implicit is changed
|
|
||||||
- default_change_implicit_idem_check is not changed
|
|
||||||
- default_change_implicit_idem is not changed
|
|
||||||
- "'allow (incoming)' in ufw_defaults_change_implicit.stdout"
|
|
|
@ -1,80 +0,0 @@
|
||||||
---
|
|
||||||
- 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"
|
|
|
@ -1,81 +0,0 @@
|
||||||
- name: Enable
|
|
||||||
ufw:
|
|
||||||
state: enabled
|
|
||||||
|
|
||||||
- name: Route with interface in and out
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
route: yes
|
|
||||||
interface_in: foo
|
|
||||||
interface_out: bar
|
|
||||||
proto: tcp
|
|
||||||
from_ip: 10.1.1.1
|
|
||||||
to_ip: 10.8.8.8
|
|
||||||
from_port: 1111
|
|
||||||
to_port: 2222
|
|
||||||
|
|
||||||
- name: Route with interface in
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
route: yes
|
|
||||||
interface_in: foo
|
|
||||||
proto: tcp
|
|
||||||
from_ip: 10.1.1.1
|
|
||||||
from_port: 1111
|
|
||||||
|
|
||||||
- name: Route with interface out
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
route: yes
|
|
||||||
interface_out: bar
|
|
||||||
proto: tcp
|
|
||||||
from_ip: 10.1.1.1
|
|
||||||
from_port: 1111
|
|
||||||
|
|
||||||
- name: Non-route with interface in
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
interface_in: foo
|
|
||||||
proto: tcp
|
|
||||||
from_ip: 10.1.1.1
|
|
||||||
from_port: 3333
|
|
||||||
|
|
||||||
- name: Non-route with interface out
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
interface_out: bar
|
|
||||||
proto: tcp
|
|
||||||
from_ip: 10.1.1.1
|
|
||||||
from_port: 4444
|
|
||||||
|
|
||||||
- name: Check result
|
|
||||||
shell: ufw status |grep -E '(ALLOW|DENY|REJECT|LIMIT)' |sed -E 's/[ \t]+/ /g'
|
|
||||||
register: ufw_status
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- '"10.8.8.8 2222/tcp on bar ALLOW FWD 10.1.1.1 1111/tcp on foo " in stdout'
|
|
||||||
- '"Anywhere ALLOW FWD 10.1.1.1 1111/tcp on foo " in stdout'
|
|
||||||
- '"Anywhere on bar ALLOW FWD 10.1.1.1 1111/tcp " in stdout'
|
|
||||||
- '"Anywhere on foo ALLOW 10.1.1.1 3333/tcp " in stdout'
|
|
||||||
- '"Anywhere ALLOW OUT 10.1.1.1 4444/tcp on bar " in stdout'
|
|
||||||
vars:
|
|
||||||
stdout: '{{ ufw_status.stdout_lines }}'
|
|
||||||
|
|
||||||
- name: Non-route with interface_in and interface_out
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
interface_in: foo
|
|
||||||
interface_out: bar
|
|
||||||
proto: tcp
|
|
||||||
from_ip: 10.1.1.1
|
|
||||||
from_port: 1111
|
|
||||||
to_ip: 10.8.8.8
|
|
||||||
to_port: 2222
|
|
||||||
ignore_errors: yes
|
|
||||||
register: ufw_non_route_iface
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- ufw_non_route_iface is failed
|
|
||||||
- '"Only route rules" in ufw_non_route_iface.msg'
|
|
|
@ -1,598 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright: (c) 2014, Ahti Kitsik <ak@ahtik.com>
|
|
||||||
# Copyright: (c) 2014, Jarno Keskikangas <jarno.keskikangas@gmail.com>
|
|
||||||
# Copyright: (c) 2013, Aleksey Ovcharenko <aleksey.ovcharenko@gmail.com>
|
|
||||||
# Copyright: (c) 2013, James Martin <jmartin@basho.com>
|
|
||||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
__metaclass__ = type
|
|
||||||
|
|
||||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
|
||||||
'status': ['preview'],
|
|
||||||
'supported_by': 'community'}
|
|
||||||
|
|
||||||
DOCUMENTATION = r'''
|
|
||||||
---
|
|
||||||
module: ufw
|
|
||||||
short_description: Manage firewall with UFW
|
|
||||||
description:
|
|
||||||
- Manage firewall with UFW.
|
|
||||||
version_added: 1.6
|
|
||||||
author:
|
|
||||||
- Aleksey Ovcharenko (@ovcharenko)
|
|
||||||
- Jarno Keskikangas (@pyykkis)
|
|
||||||
- Ahti Kitsik (@ahtik)
|
|
||||||
notes:
|
|
||||||
- See C(man ufw) for more examples.
|
|
||||||
requirements:
|
|
||||||
- C(ufw) package
|
|
||||||
options:
|
|
||||||
state:
|
|
||||||
description:
|
|
||||||
- C(enabled) reloads firewall and enables firewall on boot.
|
|
||||||
- C(disabled) unloads firewall and disables firewall on boot.
|
|
||||||
- C(reloaded) reloads firewall.
|
|
||||||
- C(reset) disables and resets firewall to installation defaults.
|
|
||||||
type: str
|
|
||||||
choices: [ disabled, enabled, reloaded, reset ]
|
|
||||||
default:
|
|
||||||
description:
|
|
||||||
- Change the default policy for incoming or outgoing traffic.
|
|
||||||
type: str
|
|
||||||
choices: [ allow, deny, reject ]
|
|
||||||
aliases: [ policy ]
|
|
||||||
direction:
|
|
||||||
description:
|
|
||||||
- Select direction for a rule or default policy command. Mutually
|
|
||||||
exclusive with I(interface_in) and I(interface_out).
|
|
||||||
type: str
|
|
||||||
choices: [ in, incoming, out, outgoing, routed ]
|
|
||||||
logging:
|
|
||||||
description:
|
|
||||||
- Toggles logging. Logged packets use the LOG_KERN syslog facility.
|
|
||||||
type: str
|
|
||||||
choices: [ 'on', 'off', low, medium, high, full ]
|
|
||||||
insert:
|
|
||||||
description:
|
|
||||||
- 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.
|
|
||||||
type: str
|
|
||||||
choices: [ first-ipv4, first-ipv6, last-ipv4, last-ipv6, zero ]
|
|
||||||
default: zero
|
|
||||||
version_added: "2.8"
|
|
||||||
rule:
|
|
||||||
description:
|
|
||||||
- Add firewall rule
|
|
||||||
type: str
|
|
||||||
choices: [ allow, deny, limit, reject ]
|
|
||||||
log:
|
|
||||||
description:
|
|
||||||
- Log new connections matched to this rule
|
|
||||||
type: bool
|
|
||||||
from_ip:
|
|
||||||
description:
|
|
||||||
- Source IP address.
|
|
||||||
type: str
|
|
||||||
default: any
|
|
||||||
aliases: [ from, src ]
|
|
||||||
from_port:
|
|
||||||
description:
|
|
||||||
- Source port.
|
|
||||||
type: str
|
|
||||||
to_ip:
|
|
||||||
description:
|
|
||||||
- Destination IP address.
|
|
||||||
type: str
|
|
||||||
default: any
|
|
||||||
aliases: [ dest, to]
|
|
||||||
to_port:
|
|
||||||
description:
|
|
||||||
- Destination port.
|
|
||||||
type: str
|
|
||||||
aliases: [ port ]
|
|
||||||
proto:
|
|
||||||
description:
|
|
||||||
- TCP/IP protocol.
|
|
||||||
type: str
|
|
||||||
choices: [ any, tcp, udp, ipv6, esp, ah, gre, igmp ]
|
|
||||||
aliases: [ protocol ]
|
|
||||||
name:
|
|
||||||
description:
|
|
||||||
- Use profile located in C(/etc/ufw/applications.d).
|
|
||||||
type: str
|
|
||||||
aliases: [ app ]
|
|
||||||
delete:
|
|
||||||
description:
|
|
||||||
- Delete rule.
|
|
||||||
type: bool
|
|
||||||
interface:
|
|
||||||
description:
|
|
||||||
- Specify interface for the rule. The direction (in or out) used
|
|
||||||
for the interface depends on the value of I(direction). See
|
|
||||||
I(interface_in) and I(interface_out) for routed rules that needs
|
|
||||||
to supply both an input and output interface. Mutually
|
|
||||||
exclusive with I(interface_in) and I(interface_out).
|
|
||||||
type: str
|
|
||||||
aliases: [ if ]
|
|
||||||
interface_in:
|
|
||||||
description:
|
|
||||||
- Specify input interface for the rule. This is mutually
|
|
||||||
exclusive with I(direction) and I(interface). However, it is
|
|
||||||
compatible with I(interface_out) for routed rules.
|
|
||||||
type: str
|
|
||||||
aliases: [ if_in ]
|
|
||||||
version_added: "2.10"
|
|
||||||
interface_out:
|
|
||||||
description:
|
|
||||||
- Specify output interface for the rule. This is mutually
|
|
||||||
exclusive with I(direction) and I(interface). However, it is
|
|
||||||
compatible with I(interface_in) for routed rules.
|
|
||||||
type: str
|
|
||||||
aliases: [ if_out ]
|
|
||||||
version_added: "2.10"
|
|
||||||
route:
|
|
||||||
description:
|
|
||||||
- Apply the rule to routed/forwarded packets.
|
|
||||||
type: bool
|
|
||||||
comment:
|
|
||||||
description:
|
|
||||||
- Add a comment to the rule. Requires UFW version >=0.35.
|
|
||||||
type: str
|
|
||||||
version_added: "2.4"
|
|
||||||
'''
|
|
||||||
|
|
||||||
EXAMPLES = r'''
|
|
||||||
- name: Allow everything and enable UFW
|
|
||||||
ufw:
|
|
||||||
state: enabled
|
|
||||||
policy: allow
|
|
||||||
|
|
||||||
- name: Set logging
|
|
||||||
ufw:
|
|
||||||
logging: 'on'
|
|
||||||
|
|
||||||
# Sometimes it is desirable to let the sender know when traffic is
|
|
||||||
# being denied, rather than simply ignoring it. In these cases, use
|
|
||||||
# reject instead of deny. In addition, log rejected connections:
|
|
||||||
- ufw:
|
|
||||||
rule: reject
|
|
||||||
port: auth
|
|
||||||
log: yes
|
|
||||||
|
|
||||||
# ufw supports connection rate limiting, which is useful for protecting
|
|
||||||
# against brute-force login attacks. ufw will deny connections if an IP
|
|
||||||
# address has attempted to initiate 6 or more connections in the last
|
|
||||||
# 30 seconds. See http://www.debian-administration.org/articles/187
|
|
||||||
# for details. Typical usage is:
|
|
||||||
- ufw:
|
|
||||||
rule: limit
|
|
||||||
port: ssh
|
|
||||||
proto: tcp
|
|
||||||
|
|
||||||
# Allow OpenSSH. (Note that as ufw manages its own state, simply removing
|
|
||||||
# a rule=allow task can leave those ports exposed. Either use delete=yes
|
|
||||||
# or a separate state=reset task)
|
|
||||||
- ufw:
|
|
||||||
rule: allow
|
|
||||||
name: OpenSSH
|
|
||||||
|
|
||||||
- name: Delete OpenSSH rule
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
name: OpenSSH
|
|
||||||
delete: yes
|
|
||||||
|
|
||||||
- name: Deny all access to port 53
|
|
||||||
ufw:
|
|
||||||
rule: deny
|
|
||||||
port: '53'
|
|
||||||
|
|
||||||
- name: Allow port range 60000-61000
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: 60000:61000
|
|
||||||
proto: tcp
|
|
||||||
|
|
||||||
- name: Allow all access to tcp port 80
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: '80'
|
|
||||||
proto: tcp
|
|
||||||
|
|
||||||
- name: Allow all access from RFC1918 networks to this host
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
src: '{{ item }}'
|
|
||||||
loop:
|
|
||||||
- 10.0.0.0/8
|
|
||||||
- 172.16.0.0/12
|
|
||||||
- 192.168.0.0/16
|
|
||||||
|
|
||||||
- name: Deny access to udp port 514 from host 1.2.3.4 and include a comment
|
|
||||||
ufw:
|
|
||||||
rule: deny
|
|
||||||
proto: udp
|
|
||||||
src: 1.2.3.4
|
|
||||||
port: '514'
|
|
||||||
comment: Block syslog
|
|
||||||
|
|
||||||
- name: Allow incoming access to eth0 from 1.2.3.5 port 5469 to 1.2.3.4 port 5469
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
interface: eth0
|
|
||||||
direction: in
|
|
||||||
proto: udp
|
|
||||||
src: 1.2.3.5
|
|
||||||
from_port: '5469'
|
|
||||||
dest: 1.2.3.4
|
|
||||||
to_port: '5469'
|
|
||||||
|
|
||||||
# Note that IPv6 must be enabled in /etc/default/ufw for IPv6 firewalling to work.
|
|
||||||
- name: Deny all traffic from the IPv6 2001:db8::/32 to tcp port 25 on this host
|
|
||||||
ufw:
|
|
||||||
rule: deny
|
|
||||||
proto: tcp
|
|
||||||
src: 2001:db8::/32
|
|
||||||
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
|
|
||||||
- name: Deny forwarded/routed traffic from subnet 1.2.3.0/24 to subnet 4.5.6.0/24
|
|
||||||
ufw:
|
|
||||||
rule: deny
|
|
||||||
route: yes
|
|
||||||
src: 1.2.3.0/24
|
|
||||||
dest: 4.5.6.0/24
|
|
||||||
'''
|
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
from operator import itemgetter
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
|
|
||||||
|
|
||||||
def compile_ipv4_regexp():
|
|
||||||
r = r"((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}"
|
|
||||||
r += r"(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])"
|
|
||||||
return re.compile(r)
|
|
||||||
|
|
||||||
|
|
||||||
def compile_ipv6_regexp():
|
|
||||||
"""
|
|
||||||
validation pattern provided by :
|
|
||||||
https://stackoverflow.com/questions/53497/regular-expression-that-matches-
|
|
||||||
valid-ipv6-addresses#answer-17871737
|
|
||||||
"""
|
|
||||||
r = r"(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:"
|
|
||||||
r += r"|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}"
|
|
||||||
r += r"(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4})"
|
|
||||||
r += r"{1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]"
|
|
||||||
r += r"{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]"
|
|
||||||
r += r"{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4})"
|
|
||||||
r += r"{0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]"
|
|
||||||
r += r"|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}"
|
|
||||||
r += r"[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}"
|
|
||||||
r += r"[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))"
|
|
||||||
return re.compile(r)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
command_keys = ['state', 'default', 'rule', 'logging']
|
|
||||||
|
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec=dict(
|
|
||||||
state=dict(type='str', choices=['enabled', 'disabled', 'reloaded', 'reset']),
|
|
||||||
default=dict(type='str', aliases=['policy'], choices=['allow', 'deny', 'reject']),
|
|
||||||
logging=dict(type='str', choices=['full', 'high', 'low', 'medium', 'off', 'on']),
|
|
||||||
direction=dict(type='str', choices=['in', 'incoming', 'out', 'outgoing', 'routed']),
|
|
||||||
delete=dict(type='bool', default=False),
|
|
||||||
route=dict(type='bool', default=False),
|
|
||||||
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']),
|
|
||||||
interface=dict(type='str', aliases=['if']),
|
|
||||||
interface_in=dict(type='str', aliases=['if_in']),
|
|
||||||
interface_out=dict(type='str', aliases=['if_out']),
|
|
||||||
log=dict(type='bool', default=False),
|
|
||||||
from_ip=dict(type='str', default='any', aliases=['from', 'src']),
|
|
||||||
from_port=dict(type='str'),
|
|
||||||
to_ip=dict(type='str', default='any', aliases=['dest', 'to']),
|
|
||||||
to_port=dict(type='str', aliases=['port']),
|
|
||||||
proto=dict(type='str', aliases=['protocol'], choices=['ah', 'any', 'esp', 'ipv6', 'tcp', 'udp', 'gre', 'igmp']),
|
|
||||||
name=dict(type='str', aliases=['app']),
|
|
||||||
comment=dict(type='str'),
|
|
||||||
),
|
|
||||||
supports_check_mode=True,
|
|
||||||
mutually_exclusive=[
|
|
||||||
['name', 'proto', 'logging'],
|
|
||||||
# Mutual exclusivity with `interface` implied by `required_by`.
|
|
||||||
['direction', 'interface_in'],
|
|
||||||
['direction', 'interface_out'],
|
|
||||||
],
|
|
||||||
required_one_of=([command_keys]),
|
|
||||||
required_by=dict(
|
|
||||||
interface=('direction', ),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
cmds = []
|
|
||||||
|
|
||||||
ipv4_regexp = compile_ipv4_regexp()
|
|
||||||
ipv6_regexp = compile_ipv6_regexp()
|
|
||||||
|
|
||||||
def filter_line_that_not_start_with(pattern, content):
|
|
||||||
return ''.join([line for line in content.splitlines(True) if line.startswith(pattern)])
|
|
||||||
|
|
||||||
def filter_line_that_contains(pattern, content):
|
|
||||||
return [line for line in content.splitlines(True) if pattern in line]
|
|
||||||
|
|
||||||
def filter_line_that_not_contains(pattern, content):
|
|
||||||
return ''.join([line for line in content.splitlines(True) if not line.contains(pattern)])
|
|
||||||
|
|
||||||
def filter_line_that_match_func(match_func, content):
|
|
||||||
return ''.join([line for line in content.splitlines(True) if match_func(line) is not None])
|
|
||||||
|
|
||||||
def filter_line_that_contains_ipv4(content):
|
|
||||||
return filter_line_that_match_func(ipv4_regexp.search, content)
|
|
||||||
|
|
||||||
def filter_line_that_contains_ipv6(content):
|
|
||||||
return filter_line_that_match_func(ipv6_regexp.search, content)
|
|
||||||
|
|
||||||
def is_starting_by_ipv4(ip):
|
|
||||||
return ipv4_regexp.match(ip) is not None
|
|
||||||
|
|
||||||
def is_starting_by_ipv6(ip):
|
|
||||||
return ipv6_regexp.match(ip) is not None
|
|
||||||
|
|
||||||
def execute(cmd, ignore_error=False):
|
|
||||||
cmd = ' '.join(map(itemgetter(-1), filter(itemgetter(0), cmd)))
|
|
||||||
|
|
||||||
cmds.append(cmd)
|
|
||||||
(rc, out, err) = module.run_command(cmd, environ_update={"LANG": "C"})
|
|
||||||
|
|
||||||
if rc != 0 and not ignore_error:
|
|
||||||
module.fail_json(msg=err or out, commands=cmds)
|
|
||||||
|
|
||||||
return out
|
|
||||||
|
|
||||||
def get_current_rules():
|
|
||||||
user_rules_files = ["/lib/ufw/user.rules",
|
|
||||||
"/lib/ufw/user6.rules",
|
|
||||||
"/etc/ufw/user.rules",
|
|
||||||
"/etc/ufw/user6.rules",
|
|
||||||
"/var/lib/ufw/user.rules",
|
|
||||||
"/var/lib/ufw/user6.rules"]
|
|
||||||
|
|
||||||
cmd = [[grep_bin], ["-h"], ["'^### tuple'"]]
|
|
||||||
|
|
||||||
cmd.extend([[f] for f in user_rules_files])
|
|
||||||
return execute(cmd, ignore_error=True)
|
|
||||||
|
|
||||||
def ufw_version():
|
|
||||||
"""
|
|
||||||
Returns the major and minor version of ufw installed on the system.
|
|
||||||
"""
|
|
||||||
out = execute([[ufw_bin], ["--version"]])
|
|
||||||
|
|
||||||
lines = [x for x in out.split('\n') if x.strip() != '']
|
|
||||||
if len(lines) == 0:
|
|
||||||
module.fail_json(msg="Failed to get ufw version.", rc=0, out=out)
|
|
||||||
|
|
||||||
matches = re.search(r'^ufw.+(\d+)\.(\d+)(?:\.(\d+))?.*$', lines[0])
|
|
||||||
if matches is None:
|
|
||||||
module.fail_json(msg="Failed to get ufw version.", rc=0, out=out)
|
|
||||||
|
|
||||||
# Convert version to numbers
|
|
||||||
major = int(matches.group(1))
|
|
||||||
minor = int(matches.group(2))
|
|
||||||
rev = 0
|
|
||||||
if matches.group(3) is not None:
|
|
||||||
rev = int(matches.group(3))
|
|
||||||
|
|
||||||
return major, minor, rev
|
|
||||||
|
|
||||||
params = module.params
|
|
||||||
|
|
||||||
commands = dict((key, params[key]) for key in command_keys if params[key])
|
|
||||||
|
|
||||||
# Ensure ufw is available
|
|
||||||
ufw_bin = module.get_bin_path('ufw', True)
|
|
||||||
grep_bin = module.get_bin_path('grep', True)
|
|
||||||
|
|
||||||
# Save the pre state and rules in order to recognize changes
|
|
||||||
pre_state = execute([[ufw_bin], ['status verbose']])
|
|
||||||
pre_rules = get_current_rules()
|
|
||||||
|
|
||||||
changed = False
|
|
||||||
|
|
||||||
# Execute filter
|
|
||||||
for (command, value) in commands.items():
|
|
||||||
|
|
||||||
cmd = [[ufw_bin], [module.check_mode, '--dry-run']]
|
|
||||||
|
|
||||||
if command == 'state':
|
|
||||||
states = {'enabled': 'enable', 'disabled': 'disable',
|
|
||||||
'reloaded': 'reload', 'reset': 'reset'}
|
|
||||||
|
|
||||||
if value in ['reloaded', 'reset']:
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
if module.check_mode:
|
|
||||||
# "active" would also match "inactive", hence the space
|
|
||||||
ufw_enabled = pre_state.find(" active") != -1
|
|
||||||
if (value == 'disabled' and ufw_enabled) or (value == 'enabled' and not ufw_enabled):
|
|
||||||
changed = True
|
|
||||||
else:
|
|
||||||
execute(cmd + [['-f'], [states[value]]])
|
|
||||||
|
|
||||||
elif command == 'logging':
|
|
||||||
extract = re.search(r'Logging: (on|off)(?: \(([a-z]+)\))?', pre_state)
|
|
||||||
if extract:
|
|
||||||
current_level = extract.group(2)
|
|
||||||
current_on_off_value = extract.group(1)
|
|
||||||
if value != "off":
|
|
||||||
if current_on_off_value == "off":
|
|
||||||
changed = True
|
|
||||||
elif value != "on" and value != current_level:
|
|
||||||
changed = True
|
|
||||||
elif current_on_off_value != "off":
|
|
||||||
changed = True
|
|
||||||
else:
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
if not module.check_mode:
|
|
||||||
execute(cmd + [[command], [value]])
|
|
||||||
|
|
||||||
elif command == 'default':
|
|
||||||
if params['direction'] not in ['outgoing', 'incoming', 'routed', None]:
|
|
||||||
module.fail_json(msg='For default, direction must be one of "outgoing", "incoming" and "routed", or direction must not be specified.')
|
|
||||||
if module.check_mode:
|
|
||||||
regexp = r'Default: (deny|allow|reject) \(incoming\), (deny|allow|reject) \(outgoing\), (deny|allow|reject|disabled) \(routed\)'
|
|
||||||
extract = re.search(regexp, pre_state)
|
|
||||||
if extract is not None:
|
|
||||||
current_default_values = {}
|
|
||||||
current_default_values["incoming"] = extract.group(1)
|
|
||||||
current_default_values["outgoing"] = extract.group(2)
|
|
||||||
current_default_values["routed"] = extract.group(3)
|
|
||||||
v = current_default_values[params['direction'] or 'incoming']
|
|
||||||
if v not in (value, 'disabled'):
|
|
||||||
changed = True
|
|
||||||
else:
|
|
||||||
changed = True
|
|
||||||
else:
|
|
||||||
execute(cmd + [[command], [value], [params['direction']]])
|
|
||||||
|
|
||||||
elif command == 'rule':
|
|
||||||
if params['direction'] not in ['in', 'out', None]:
|
|
||||||
module.fail_json(msg='For rules, direction must be one of "in" and "out", or direction must not be specified.')
|
|
||||||
if not params['route'] and params['interface_in'] and params['interface_out']:
|
|
||||||
module.fail_json(msg='Only route rules can combine '
|
|
||||||
'interface_in and interface_out')
|
|
||||||
# Rules are constructed according to the long format
|
|
||||||
#
|
|
||||||
# ufw [--dry-run] [route] [delete] [insert NUM] allow|deny|reject|limit [in|out on INTERFACE] [log|log-all] \
|
|
||||||
# [from ADDRESS [port PORT]] [to ADDRESS [port PORT]] \
|
|
||||||
# [proto protocol] [app application] [comment COMMENT]
|
|
||||||
cmd.append([module.boolean(params['route']), 'route'])
|
|
||||||
cmd.append([module.boolean(params['delete']), 'delete'])
|
|
||||||
if params['insert'] is not None:
|
|
||||||
relative_to_cmd = params['insert_relative_to']
|
|
||||||
if relative_to_cmd == 'zero':
|
|
||||||
insert_to = params['insert']
|
|
||||||
else:
|
|
||||||
(dummy, numbered_state, dummy) = 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([params['direction'], "%s" % params['direction']])
|
|
||||||
cmd.append([params['interface'], "on %s" % params['interface']])
|
|
||||||
cmd.append([params['interface_in'], "in on %s" % params['interface_in']])
|
|
||||||
cmd.append([params['interface_out'], "out on %s" % params['interface_out']])
|
|
||||||
cmd.append([module.boolean(params['log']), 'log'])
|
|
||||||
|
|
||||||
for (key, template) in [('from_ip', "from %s"), ('from_port', "port %s"),
|
|
||||||
('to_ip', "to %s"), ('to_port', "port %s"),
|
|
||||||
('proto', "proto %s"), ('name', "app '%s'")]:
|
|
||||||
value = params[key]
|
|
||||||
cmd.append([value, template % (value)])
|
|
||||||
|
|
||||||
ufw_major, ufw_minor, dummy = ufw_version()
|
|
||||||
# comment is supported only in ufw version after 0.35
|
|
||||||
if (ufw_major == 0 and ufw_minor >= 35) or ufw_major > 0:
|
|
||||||
cmd.append([params['comment'], "comment '%s'" % params['comment']])
|
|
||||||
|
|
||||||
rules_dry = execute(cmd)
|
|
||||||
|
|
||||||
if module.check_mode:
|
|
||||||
|
|
||||||
nb_skipping_line = len(filter_line_that_contains("Skipping", rules_dry))
|
|
||||||
|
|
||||||
if not (nb_skipping_line > 0 and nb_skipping_line == len(rules_dry.splitlines(True))):
|
|
||||||
|
|
||||||
rules_dry = filter_line_that_not_start_with("### tuple", rules_dry)
|
|
||||||
# ufw dry-run doesn't send all rules so have to compare ipv4 or ipv6 rules
|
|
||||||
if is_starting_by_ipv4(params['from_ip']) or is_starting_by_ipv4(params['to_ip']):
|
|
||||||
if filter_line_that_contains_ipv4(pre_rules) != filter_line_that_contains_ipv4(rules_dry):
|
|
||||||
changed = True
|
|
||||||
elif is_starting_by_ipv6(params['from_ip']) or is_starting_by_ipv6(params['to_ip']):
|
|
||||||
if filter_line_that_contains_ipv6(pre_rules) != filter_line_that_contains_ipv6(rules_dry):
|
|
||||||
changed = True
|
|
||||||
elif pre_rules != rules_dry:
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
# Get the new state
|
|
||||||
if module.check_mode:
|
|
||||||
return module.exit_json(changed=changed, commands=cmds)
|
|
||||||
else:
|
|
||||||
post_state = execute([[ufw_bin], ['status'], ['verbose']])
|
|
||||||
if not changed:
|
|
||||||
post_rules = get_current_rules()
|
|
||||||
changed = (pre_state != post_state) or (pre_rules != post_rules)
|
|
||||||
return module.exit_json(changed=changed, commands=cmds, msg=post_state.rstrip())
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
Loading…
Reference in a new issue