From 651c04a3ecffb5f0838c125a1e5d54d9389c7944 Mon Sep 17 00:00:00 2001 From: Aleksey Ovcharenko Date: Mon, 2 Dec 2013 16:41:17 +0200 Subject: [PATCH 1/6] New module [ufw]: this module handles Ubuntu UFW operations * Updated documentation related to IPv6 usage. BugFixes: * Solved the default_policy and state mutual exclusive status. * Fixed changed status for IPv6 addresses. Added @otnateos patch. --- library/system/ufw | 268 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 library/system/ufw diff --git a/library/system/ufw b/library/system/ufw new file mode 100644 index 0000000000..0857c2e7c9 --- /dev/null +++ b/library/system/ufw @@ -0,0 +1,268 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2013, Aleksey Ovcharenko +# (c) 2013, James Martin +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +DOCUMENTATION = ''' +--- +module: ufw +short_description: This module handles Ubuntu UFW operations +description: + - This module handles Ubuntu UFW operations +options: + default_policy: + description: + - Change the default policy for incoming traffic. + required: false + choices: ['allow', 'deny', 'reject'] + default: None + delete: + description: + - Delete rule instead of creation. + required: false + choices: ['yes', 'no'] + default: 'no' + state: + description: | + I(enable) reloads firewall and enables firewall on boot. + I(disable) unloads firewall and disables firewall on boot. + I(reload) reloads firewall. + I(reset) disables and resets firewall to installation defaults. + I(allow) adds allow rule. See B(EXAMPLES). + I(deny) adds deny rule. See B(EXAMPLES). + I(reject) adds reject rule. See B(EXAMPLES). + I(limit) adds limit rule. Currently only IPv4 is supported. See B(EXAMPLES). + required: false + choices: ['enable', 'disable', 'reload', 'reset', 'allow', 'deny', 'reject', 'limit'] + aliases: ['rule'] + default: 'allow' + name: + description: + - Use profile located in /etc/ufw/applications.d + required: false + default: None + version_added: "2.1" + from_ip: + description: + - Source IP address. + required: false + aliases: ['src'] + default: 'any' + from_port: + description: + - Source port. + required: false + default: 'any' + to_ip: + description: + - Destination IP address. + required: false + aliases: ['dest'] + default: 'any' + to_port: + description: + - Destination port. + required: false + default: 'any' + aliases: ['port'] + proto: + description: + - TCP/IP protocol. + choices: ['any', 'tcp', 'udp', 'ipv6'] + required: false + log: + description: + - Toggles logging. Logged packets use the LOG_KERN syslog facility. + choices: ['yes', 'no'] + required: false + default: 'no' +version_added: 2.0 +notes: + - See C(man 8 ufw) for more example. +requirements: [ ] +author: Aleksey Ovcharenko +''' + +EXAMPLES = ''' +# Allow everything and enable UFW +ufw: state={{ item }} +with_items: +- allow +- enable + +# 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. For example: +ufw: state=reject port=auth + +# 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: state=limit port=ssh proto=tcp + +# Allow OpenSSH +ufw: state=allow name=OpenSSH + +# Deny all access to port 53: +ufw: state=deny port=53 + +# Allow all access to tcp port 80: +ufw: state=allow to_port=80 proto=tcp + +# Allow all access from RFC1918 networks to this host: +ufw: state=allow from_ip={{ item }} +with_items: +- 10.0.0.0/8 +- 172.16.0.0/12 +- 192.168.0.0/16 + +# Deny access to udp port 514 from host 1.2.3.4: +ufw: state=deny proto=udp from_ip=1.2.3.4 to_port=514 + +# Allow access to udp 1.2.3.4 port 5469 from 1.2.3.5 port 5469: +ufw: state=allow proto=udp from_ip=1.2.3.5 from_port=5469 to_ip=1.2.3.4 to_port=5469 + +# Deny all traffic from the IPv6 2001:db8::/32 to tcp port 25 on this host. +# Note that IPv6 must be enabled in /etc/default/ufw for IPv6 firewalling to work. +ufw: state=deny proto=tcp src=2001:db8::/32 port=25 +''' + +import platform + +def main(): + module = AnsibleModule( + argument_spec = dict( + default_policy = dict(default=None, choices=['allow', 'deny', 'reject'], required=False), + state = dict(default=None, aliases=['rule'], choices=['enable', 'disable', 'reload', 'reset', 'allow', 'deny', 'reject', 'limit'], required=False), + name = dict(default=None, required=False), + from_ip = dict(default='any', aliases=['src'], required=False), + from_port = dict(default='any', required=False), + to_ip = dict(default='any', aliases=['dest'], required=False), + to_port = dict(default='any', aliases=['port'], required=False), + proto = dict(default='any', choices=['any', 'tcp', 'udp', 'ipv6'], required=False), + delete = dict(default=False, choices=BOOLEANS, required=False), + log = dict(default=False, choices=BOOLEANS, required=False) + ), + supports_check_mode = True + ) + + default_policy = module.params.get('default_policy') + state = module.params.get('state') + name = module.params.get('name') + from_ip = module.params.get('from_ip') + from_port = module.params.get('from_port') + to_ip = module.params.get('to_ip') + to_port = module.params.get('to_port') + proto = module.params.get('proto') + delete = module.params['delete'] + log = module.params['log'] + + system = platform.system() + + if "Linux" not in system: + module.exit_json(msg="Not implemented for system %s. Only Linux (Ubuntu) is supported" % (system), changed=False) + else: + dist = platform.dist() + if dist and 'Ubuntu' not in dist[0]: + module.exit_json(msg="Not implemented for distrubution %s. Only Ubuntu is supported" % (dist[0]), changed=False) + + result = {} + result['state'] = state + + cmd = module.get_bin_path('ufw') + + if module.check_mode: + cmd = cmd + ' --dry-run' + + if default_policy: + if state: + module.fail_json(msg="'default_policy' and 'state' are mutually exclusive options.") + else: + if default_policy in ['allow', 'deny', 'reject']: + cmd = cmd + ' default %s' % (default_policy) + changed_marker = "Default incoming policy changed to '%s'\n(be sure to update your rules accordingly)" % (default_policy) + else: + module.fail_json(msg="Wrong default policy %s. See 'ansible-doc ufw' for usage." % (default_policy)) + + if not default_policy: + if not state: + module.fail_json(msg="You must specify either 'default_policy' or 'state' option.") + else: + if state in 'enable': + cmd = cmd + ' -f %s' % (state) + changed_marker = 'Firewall is active and enabled on system startup' + elif state in 'disable': + cmd = cmd + ' -f %s' % (state) + changed_marker = 'Firewall stopped and disabled on system startup' + elif state in 'reload': + cmd = cmd + ' -f %s' % (state) + changed_marker = 'Firewall reloaded' + elif state in 'reset': + cmd = cmd + ' -f %s' % (state) + changed_marker = 'Backing up' + elif state in ['allow', 'deny', 'reject', 'limit']: + changed_marker = ['Rules updated', 'Rules updated (v6)', 'Rule added', 'Rule added (v6)', 'Rule deleted', 'Rule deleted (v6)' ] + if delete: + cmd = cmd + ' delete' + + cmd = cmd + ' %s' % (state) + if log: + cmd = cmd + ' log' + if name: + cmd = cmd + ' %s' % (name) + else: + if proto and proto not in 'any': + cmd = cmd + ' proto %s' % (proto) + if from_ip and from_ip not in 'any': + cmd = cmd + ' from %s' % (from_ip) + if from_port and from_port not in 'any': + cmd = cmd + ' port %s' % (from_port) + elif from_port and from_port not in 'any': + cmd = cmd + ' from port %s' % (from_port) + + if to_ip: + cmd = cmd + ' to %s' % (to_ip) + if to_port and to_port not in 'any': + cmd = cmd + ' port %s' % (to_port) + elif to_port and to_port not in 'any': + cmd = cmd + ' to port %s' % (to_port) + else: + module.fail_json(msg="Wrong rule %s. See 'ansible-doc ufw' for usage." % (state)) + + (rc, out, err) = module.run_command(cmd) + + if rc != 0: + if err: + module.fail_json(msg=err) + else: + module.fail_json(msg=out) + + result['cmd'] = cmd + result['msg'] = out.rstrip() + + if isinstance(changed_marker, basestring): + result['changed'] = result['msg'] in changed_marker + else: + result['changed'] = any(item in result['msg'] for item in changed_marker) + + return module.exit_json(**result) + +# include magic from lib/ansible/module_common.py +#<> + +main() From f4e8a86c87a170267d1c8b48ae8c66393579d9f8 Mon Sep 17 00:00:00 2001 From: Jarno Keskikangas Date: Mon, 6 Jan 2014 22:44:25 +0200 Subject: [PATCH 2/6] Add functionality and reduce complexity. * Separate 'state', 'policy' and 'rule' commands * Support for 'logging' command * Support for 'direction' and 'interface' attributes * Reliable change notifications based on 'ufw status verbose' diff * Update documentation * Cleanup --- library/system/ufw | 294 +++++++++++++++++++++------------------------ 1 file changed, 137 insertions(+), 157 deletions(-) diff --git a/library/system/ufw b/library/system/ufw index 0857c2e7c9..cafebffba9 100644 --- a/library/system/ufw +++ b/library/system/ufw @@ -1,9 +1,12 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +# (c) 2014, Jarno Keskikangas # (c) 2013, Aleksey Ovcharenko # (c) 2013, James Martin # +# This file is part of Ansible +# # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or @@ -16,251 +19,228 @@ # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . + DOCUMENTATION = ''' --- module: ufw -short_description: This module handles Ubuntu UFW operations +short_description: Manage firewall with UFW description: - - This module handles Ubuntu UFW operations + - Manage firewall with UFW. +version_added: 1.5 +author: Aleksey Ovcharenko, Jarno Keskikangas +notes: + - See C(man ufw) for more examples. +requirements: + - C(ufw) package options: - default_policy: - description: - - Change the default policy for incoming traffic. - required: false - choices: ['allow', 'deny', 'reject'] - default: None - delete: - description: - - Delete rule instead of creation. - required: false - choices: ['yes', 'no'] - default: 'no' state: - description: | - I(enable) reloads firewall and enables firewall on boot. - I(disable) unloads firewall and disables firewall on boot. - I(reload) reloads firewall. - I(reset) disables and resets firewall to installation defaults. - I(allow) adds allow rule. See B(EXAMPLES). - I(deny) adds deny rule. See B(EXAMPLES). - I(reject) adds reject rule. See B(EXAMPLES). - I(limit) adds limit rule. Currently only IPv4 is supported. See B(EXAMPLES). - required: false - choices: ['enable', 'disable', 'reload', 'reset', 'allow', 'deny', 'reject', 'limit'] - aliases: ['rule'] - default: 'allow' - name: description: - - Use profile located in /etc/ufw/applications.d + - C(enabled) reloads firewall and enables firewall on boot. + - C(disabled) unloads firewall and disables firewall on boot. + - C(reloaded) reloads firewall. + - C(reseted) disables and resets firewall to installation defaults. required: false - default: None - version_added: "2.1" + choices: ['enabled', 'disabled', 'reloaded', 'reseted'] + policy: + description: + - Change the default policy for incoming or outgoing traffic. + required: false + alias: default + choices: ['allow', 'deny', 'reject'] + direction: + description: + - Select direction for a rule or default policy command. + required: false + choices: ['in', 'out', 'incoming', 'outgoing'] + logging: + description: + - Toggles logging. Logged packets use the LOG_KERN syslog facility. + choices: ['on', 'off', 'low', 'medium', 'high', 'full'] + required: false + rule: + description: + - Add firewall rule + required: false + choises: ['allow', 'deny', 'reject', 'limit'] + log: + description: + - Log new connections matched to this rule + required: false + choises: ['yes', 'no'] from_ip: description: - Source IP address. required: false - aliases: ['src'] + aliases: ['from', 'src'] default: 'any' from_port: description: - Source port. required: false - default: 'any' to_ip: description: - Destination IP address. required: false - aliases: ['dest'] + aliases: ['to', 'dest'] default: 'any' to_port: description: - Destination port. required: false - default: 'any' aliases: ['port'] proto: description: - TCP/IP protocol. choices: ['any', 'tcp', 'udp', 'ipv6'] required: false - log: + name: description: - - Toggles logging. Logged packets use the LOG_KERN syslog facility. - choices: ['yes', 'no'] + - Use profile located in C(/etc/ufw/applications.d) required: false - default: 'no' -version_added: 2.0 -notes: - - See C(man 8 ufw) for more example. -requirements: [ ] -author: Aleksey Ovcharenko + aliases: ['app'] + delete: + description: + - Delete rule. + required: false + choices: ['yes', 'no'] ''' EXAMPLES = ''' # Allow everything and enable UFW -ufw: state={{ item }} -with_items: -- allow -- enable +ufw: state=enable policy=allow 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. For example: -ufw: state=reject port=auth +# 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: state=limit port=ssh proto=tcp +ufw: rule=limit port=ssh proto=tcp # Allow OpenSSH -ufw: state=allow name=OpenSSH +ufw: rule=allow name=OpenSSH + +# Delete OpenSSH rule +ufw: rule=allow name=OpenSSH delete=yes # Deny all access to port 53: -ufw: state=deny port=53 +ufw: rule=deny port=53 # Allow all access to tcp port 80: -ufw: state=allow to_port=80 proto=tcp +ufw: rule=allow port=80 proto=tcp # Allow all access from RFC1918 networks to this host: -ufw: state=allow from_ip={{ item }} +ufw: rule=allow src={{ item }} with_items: - 10.0.0.0/8 - 172.16.0.0/12 - 192.168.0.0/16 # Deny access to udp port 514 from host 1.2.3.4: -ufw: state=deny proto=udp from_ip=1.2.3.4 to_port=514 +ufw: rule=deny proto=udp src=1.2.3.4 port=514 -# Allow access to udp 1.2.3.4 port 5469 from 1.2.3.5 port 5469: -ufw: state=allow proto=udp from_ip=1.2.3.5 from_port=5469 to_ip=1.2.3.4 to_port=5469 +# 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 # Deny all traffic from the IPv6 2001:db8::/32 to tcp port 25 on this host. # Note that IPv6 must be enabled in /etc/default/ufw for IPv6 firewalling to work. -ufw: state=deny proto=tcp src=2001:db8::/32 port=25 +ufw: rule=deny proto=tcp src=2001:db8::/32 port=25 ''' -import platform +from operator import itemgetter + def main(): module = AnsibleModule( argument_spec = dict( - default_policy = dict(default=None, choices=['allow', 'deny', 'reject'], required=False), - state = dict(default=None, aliases=['rule'], choices=['enable', 'disable', 'reload', 'reset', 'allow', 'deny', 'reject', 'limit'], required=False), - name = dict(default=None, required=False), - from_ip = dict(default='any', aliases=['src'], required=False), - from_port = dict(default='any', required=False), - to_ip = dict(default='any', aliases=['dest'], required=False), - to_port = dict(default='any', aliases=['port'], required=False), - proto = dict(default='any', choices=['any', 'tcp', 'udp', 'ipv6'], required=False), - delete = dict(default=False, choices=BOOLEANS, required=False), - log = dict(default=False, choices=BOOLEANS, required=False) + state = dict(default=None, choices=['enabled', 'disabled', 'reloaded', 'reseted']), + default = dict(default=None, aliases=['policy'], choices=['allow', 'deny', 'reject']), + logging = dict(default=None, choises=['on', 'off', 'low', 'medium', 'high', 'full']), + direction = dict(default=None, choises=['in', 'incoming', 'out', 'outgoing']), + delete = dict(default=False, choices=BOOLEANS), + rule = dict(default=None, choices=['allow', 'deny', 'reject', 'limit']), + interface = dict(default=None, aliases=['if']), + log = dict(default=False, choices=BOOLEANS), + from_ip = dict(default='any', aliases=['src', 'from']), + from_port = dict(default=None), + to_ip = dict(default='any', aliases=['dest', 'to']), + to_port = dict(default=None, aliases=['port']), + proto = dict(default=None, aliases=['protocol'], choices=['any', 'tcp', 'udp', 'ipv6']), + app = dict(default=None, aliases=['name']) ), - supports_check_mode = True + supports_check_mode = True, + mutually_exclusive = [['app', 'proto']] ) - default_policy = module.params.get('default_policy') - state = module.params.get('state') - name = module.params.get('name') - from_ip = module.params.get('from_ip') - from_port = module.params.get('from_port') - to_ip = module.params.get('to_ip') - to_port = module.params.get('to_port') - proto = module.params.get('proto') - delete = module.params['delete'] - log = module.params['log'] + cmds = [] - system = platform.system() + def execute(cmd): + cmd = ' '.join(map(itemgetter(-1), filter(itemgetter(0), cmd))) + cmds.append(cmd) + (rc, out, err) = module.run_command(cmd) - if "Linux" not in system: - module.exit_json(msg="Not implemented for system %s. Only Linux (Ubuntu) is supported" % (system), changed=False) - else: - dist = platform.dist() - if dist and 'Ubuntu' not in dist[0]: - module.exit_json(msg="Not implemented for distrubution %s. Only Ubuntu is supported" % (dist[0]), changed=False) + if rc != 0: + module.fail_json(msg=err or out) - result = {} - result['state'] = state + params = module.params - cmd = module.get_bin_path('ufw') + # Ensure at least one of the command arguments are given + command_keys = ['state', 'default', 'rule', 'logging'] + commands = dict((key, params[key]) for key in command_keys if params[key]) - if module.check_mode: - cmd = cmd + ' --dry-run' + if len(commands) < 1: + module.fail_json(msg="Not any of the command arguments %s given" % commands) - if default_policy: - if state: - module.fail_json(msg="'default_policy' and 'state' are mutually exclusive options.") - else: - if default_policy in ['allow', 'deny', 'reject']: - cmd = cmd + ' default %s' % (default_policy) - changed_marker = "Default incoming policy changed to '%s'\n(be sure to update your rules accordingly)" % (default_policy) - else: - module.fail_json(msg="Wrong default policy %s. See 'ansible-doc ufw' for usage." % (default_policy)) + # Ensure ufw is available + ufw_bin = module.get_bin_path('ufw', True) - if not default_policy: - if not state: - module.fail_json(msg="You must specify either 'default_policy' or 'state' option.") - else: - if state in 'enable': - cmd = cmd + ' -f %s' % (state) - changed_marker = 'Firewall is active and enabled on system startup' - elif state in 'disable': - cmd = cmd + ' -f %s' % (state) - changed_marker = 'Firewall stopped and disabled on system startup' - elif state in 'reload': - cmd = cmd + ' -f %s' % (state) - changed_marker = 'Firewall reloaded' - elif state in 'reset': - cmd = cmd + ' -f %s' % (state) - changed_marker = 'Backing up' - elif state in ['allow', 'deny', 'reject', 'limit']: - changed_marker = ['Rules updated', 'Rules updated (v6)', 'Rule added', 'Rule added (v6)', 'Rule deleted', 'Rule deleted (v6)' ] - if delete: - cmd = cmd + ' delete' + # Save the pre state in order to recognize changes reliably + (_, pre_state, _) = module.run_command(ufw_bin + ' status verbose') - cmd = cmd + ' %s' % (state) - if log: - cmd = cmd + ' log' - if name: - cmd = cmd + ' %s' % (name) - else: - if proto and proto not in 'any': - cmd = cmd + ' proto %s' % (proto) - if from_ip and from_ip not in 'any': - cmd = cmd + ' from %s' % (from_ip) - if from_port and from_port not in 'any': - cmd = cmd + ' port %s' % (from_port) - elif from_port and from_port not in 'any': - cmd = cmd + ' from port %s' % (from_port) + # Execute commands + for (command, value) in commands.iteritems(): + cmd = [[ufw_bin], [module.check_mode, '--dry-run']] - if to_ip: - cmd = cmd + ' to %s' % (to_ip) - if to_port and to_port not in 'any': - cmd = cmd + ' port %s' % (to_port) - elif to_port and to_port not in 'any': - cmd = cmd + ' to port %s' % (to_port) - else: - module.fail_json(msg="Wrong rule %s. See 'ansible-doc ufw' for usage." % (state)) + if command == 'state': + states = { 'enabled': 'enable', 'disabled': 'disable', + 'reloaded': 'reload', 'reseted': 'reset' } + execute(cmd + [['-f'], [states[value]]]) - (rc, out, err) = module.run_command(cmd) + elif command == 'logging': + execute(cmd + [[command, value]]) - if rc != 0: - if err: - module.fail_json(msg=err) - else: - module.fail_json(msg=out) + elif command == 'default': + execute(cmd + [[command], [value], [params['direction']]]) - result['cmd'] = cmd - result['msg'] = out.rstrip() + elif command == 'rule': + # Rules are constructed according to the long format + # + # ufw [--dry-run] [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] + cmd.append([module.boolean(params['delete']), 'delete']) + cmd.append([value]) + cmd.append([module.boolean(params['log']), 'log']) - if isinstance(changed_marker, basestring): - result['changed'] = result['msg'] in changed_marker - else: - result['changed'] = any(item in result['msg'] for item in changed_marker) + for (key, template) in [('direction', "%s" ), ('interface', "on %s" ), + ('from_ip', "from %s" ), ('from_port', "port %s" ), + ('to_ip', "to %s" ), ('to_port', "port %s" ), + ('proto', "proto %s"), ('app', "app '%s'")]: - return module.exit_json(**result) + value = params[key] + cmd.append([value, template % (value)]) + + execute(cmd) + + # Get the new state + (_, post_state, _) = module.run_command(ufw_bin + ' status verbose') + changed = pre_state != post_state + + return module.exit_json(changed=changed, commands=cmds, msg=post_state.rstrip()) # include magic from lib/ansible/module_common.py #<> From 2613e6fb14de76545401b8970a194e7c4a156586 Mon Sep 17 00:00:00 2001 From: Jarno Keskikangas Date: Sun, 9 Feb 2014 12:09:12 +0200 Subject: [PATCH 3/6] Add protocol options 'esp' and 'ah'. --- library/system/ufw | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/system/ufw b/library/system/ufw index cafebffba9..55bcea0b22 100644 --- a/library/system/ufw +++ b/library/system/ufw @@ -91,7 +91,7 @@ options: proto: description: - TCP/IP protocol. - choices: ['any', 'tcp', 'udp', 'ipv6'] + choices: ['any', 'tcp', 'udp', 'ipv6', 'esp', 'ah'] required: false name: description: @@ -169,7 +169,7 @@ def main(): from_port = dict(default=None), to_ip = dict(default='any', aliases=['dest', 'to']), to_port = dict(default=None, aliases=['port']), - proto = dict(default=None, aliases=['protocol'], choices=['any', 'tcp', 'udp', 'ipv6']), + proto = dict(default=None, aliases=['protocol'], choices=['any', 'tcp', 'udp', 'ipv6', 'esp', 'ah']), app = dict(default=None, aliases=['name']) ), supports_check_mode = True, From 024770819931d1b70738d6535e8220adc6cb9dcc Mon Sep 17 00:00:00 2001 From: Jarno Keskikangas Date: Sun, 9 Feb 2014 12:29:00 +0200 Subject: [PATCH 4/6] Fix typo: 'reseted' -> 'reset'. --- library/system/ufw | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/system/ufw b/library/system/ufw index 55bcea0b22..b1386ff602 100644 --- a/library/system/ufw +++ b/library/system/ufw @@ -157,7 +157,7 @@ from operator import itemgetter def main(): module = AnsibleModule( argument_spec = dict( - state = dict(default=None, choices=['enabled', 'disabled', 'reloaded', 'reseted']), + state = dict(default=None, choices=['enabled', 'disabled', 'reloaded', 'reset']), default = dict(default=None, aliases=['policy'], choices=['allow', 'deny', 'reject']), logging = dict(default=None, choises=['on', 'off', 'low', 'medium', 'high', 'full']), direction = dict(default=None, choises=['in', 'incoming', 'out', 'outgoing']), @@ -207,7 +207,7 @@ def main(): if command == 'state': states = { 'enabled': 'enable', 'disabled': 'disable', - 'reloaded': 'reload', 'reseted': 'reset' } + 'reloaded': 'reload', 'reset': 'reset' } execute(cmd + [['-f'], [states[value]]]) elif command == 'logging': From 4d5340587e0e6ad8a020bbca6515484ad993df4c Mon Sep 17 00:00:00 2001 From: Jarno Keskikangas Date: Sat, 8 Mar 2014 14:01:26 +0200 Subject: [PATCH 5/6] Insert rule to a specific num. --- library/system/ufw | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/system/ufw b/library/system/ufw index b1386ff602..e343f93428 100644 --- a/library/system/ufw +++ b/library/system/ufw @@ -57,6 +57,10 @@ options: - Toggles logging. Logged packets use the LOG_KERN syslog facility. choices: ['on', 'off', 'low', 'medium', 'high', 'full'] required: false + insert: + description: + - Insert the corresponding rule as rule number NUM + required: false rule: description: - Add firewall rule @@ -162,6 +166,7 @@ def main(): logging = dict(default=None, choises=['on', 'off', 'low', 'medium', 'high', 'full']), direction = dict(default=None, choises=['in', 'incoming', 'out', 'outgoing']), delete = dict(default=False, choices=BOOLEANS), + insert = dict(default=None), rule = dict(default=None, choices=['allow', 'deny', 'reject', 'limit']), interface = dict(default=None, aliases=['if']), log = dict(default=False, choices=BOOLEANS), @@ -223,6 +228,7 @@ def main(): # [from ADDRESS [port PORT]] [to ADDRESS [port PORT]] \ # [proto protocol] [app application] cmd.append([module.boolean(params['delete']), 'delete']) + cmd.append([params['insert'], "insert %s" % params['insert']]) cmd.append([value]) cmd.append([module.boolean(params['log']), 'log']) From 37ef39d0fa1a86039b94b51c491653ea40a72f64 Mon Sep 17 00:00:00 2001 From: Jarno Keskikangas Date: Sun, 9 Mar 2014 12:55:58 +0200 Subject: [PATCH 6/6] Recognize rule changes even if ufw is in disabled state. See http://askubuntu.com/questions/30781/see-configured-rules-even-when-inactive for the details. --- library/system/ufw | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/system/ufw b/library/system/ufw index e343f93428..5ac20978ec 100644 --- a/library/system/ufw +++ b/library/system/ufw @@ -203,8 +203,9 @@ def main(): # Ensure ufw is available ufw_bin = module.get_bin_path('ufw', True) - # Save the pre state in order to recognize changes reliably + # Save the pre state and rules in order to recognize changes (_, pre_state, _) = module.run_command(ufw_bin + ' status verbose') + (_, pre_rules, _) = module.run_command("grep '^### tuple' /lib/ufw/user*.rules") # Execute commands for (command, value) in commands.iteritems(): @@ -244,7 +245,8 @@ def main(): # Get the new state (_, post_state, _) = module.run_command(ufw_bin + ' status verbose') - changed = pre_state != post_state + (_, post_rules, _) = module.run_command("grep '^### tuple' /lib/ufw/user*.rules") + changed = (pre_state != post_state) or (pre_rules != post_rules) return module.exit_json(changed=changed, commands=cmds, msg=post_state.rstrip())