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.
This commit is contained in:
Aleksey Ovcharenko 2013-12-02 16:41:17 +02:00 committed by Jarno Keskikangas
parent 04a6dc6d12
commit 651c04a3ec

268
library/system/ufw Normal file
View file

@ -0,0 +1,268 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2013, Aleksey Ovcharenko <aleksey.ovcharenko@gmail.com>
# (c) 2013, James Martin <jmartin@basho.com>
#
# 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 <http://www.gnu.org/licenses/>.
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
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()