diff --git a/test/integration/targets/argspec/library/argspec.py b/test/integration/targets/argspec/library/argspec.py index d05a89ce676..d14adf24f7e 100644 --- a/test/integration/targets/argspec/library/argspec.py +++ b/test/integration/targets/argspec/library/argspec.py @@ -58,6 +58,13 @@ def main(): 'thing': {}, }, }, + 'choices_with_strings_like_bools': { + 'type': 'str', + 'choices': [ + 'on', + 'off', + ], + }, }, required_if=( ('state', 'present', ('path', 'content'), True), diff --git a/test/integration/targets/argspec/tasks/main.yml b/test/integration/targets/argspec/tasks/main.yml index 87d9a7b6026..9f89cfc0be5 100644 --- a/test/integration/targets/argspec/tasks/main.yml +++ b/test/integration/targets/argspec/tasks/main.yml @@ -126,6 +126,18 @@ - thing: foo 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: that: - argspec_required_if_fail is failed @@ -163,3 +175,7 @@ - >- 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' \ No newline at end of file diff --git a/test/integration/targets/incidental_ufw/aliases b/test/integration/targets/incidental_ufw/aliases deleted file mode 100644 index 7407abe60a4..00000000000 --- a/test/integration/targets/incidental_ufw/aliases +++ /dev/null @@ -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 diff --git a/test/integration/targets/incidental_ufw/tasks/main.yml b/test/integration/targets/incidental_ufw/tasks/main.yml deleted file mode 100644 index 28198cd600f..00000000000 --- a/test/integration/targets/incidental_ufw/tasks/main.yml +++ /dev/null @@ -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 diff --git a/test/integration/targets/incidental_ufw/tasks/run-test.yml b/test/integration/targets/incidental_ufw/tasks/run-test.yml deleted file mode 100644 index e9c5d2929cb..00000000000 --- a/test/integration/targets/incidental_ufw/tasks/run-test.yml +++ /dev/null @@ -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 diff --git a/test/integration/targets/incidental_ufw/tasks/tests/basic.yml b/test/integration/targets/incidental_ufw/tasks/tests/basic.yml deleted file mode 100644 index 3c625112f36..00000000000 --- a/test/integration/targets/incidental_ufw/tasks/tests/basic.yml +++ /dev/null @@ -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 diff --git a/test/integration/targets/incidental_ufw/tasks/tests/global-state.yml b/test/integration/targets/incidental_ufw/tasks/tests/global-state.yml deleted file mode 100644 index 69b2cde938e..00000000000 --- a/test/integration/targets/incidental_ufw/tasks/tests/global-state.yml +++ /dev/null @@ -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" diff --git a/test/integration/targets/incidental_ufw/tasks/tests/insert_relative_to.yml b/test/integration/targets/incidental_ufw/tasks/tests/insert_relative_to.yml deleted file mode 100644 index 3bb44a0e270..00000000000 --- a/test/integration/targets/incidental_ufw/tasks/tests/insert_relative_to.yml +++ /dev/null @@ -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" diff --git a/test/integration/targets/incidental_ufw/tasks/tests/interface.yml b/test/integration/targets/incidental_ufw/tasks/tests/interface.yml deleted file mode 100644 index a99f07d7001..00000000000 --- a/test/integration/targets/incidental_ufw/tasks/tests/interface.yml +++ /dev/null @@ -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' diff --git a/test/support/integration/plugins/modules/ufw.py b/test/support/integration/plugins/modules/ufw.py deleted file mode 100644 index 6452f7c9109..00000000000 --- a/test/support/integration/plugins/modules/ufw.py +++ /dev/null @@ -1,598 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2014, Ahti Kitsik -# Copyright: (c) 2014, Jarno Keskikangas -# Copyright: (c) 2013, Aleksey Ovcharenko -# Copyright: (c) 2013, James Martin -# 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()