diff --git a/system/ufw b/system/ufw new file mode 100644 index 00000000000..5ac20978ec2 --- /dev/null +++ b/system/ufw @@ -0,0 +1,256 @@ +#!/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 +# (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: Manage firewall with UFW +description: + - 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: + 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(reseted) disables and resets firewall to installation defaults. + required: false + 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 + insert: + description: + - Insert the corresponding rule as rule number NUM + 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: ['from', 'src'] + default: 'any' + from_port: + description: + - Source port. + required: false + to_ip: + description: + - Destination IP address. + required: false + aliases: ['to', 'dest'] + default: 'any' + to_port: + description: + - Destination port. + required: false + aliases: ['port'] + proto: + description: + - TCP/IP protocol. + choices: ['any', 'tcp', 'udp', 'ipv6', 'esp', 'ah'] + required: false + name: + description: + - Use profile located in C(/etc/ufw/applications.d) + required: false + aliases: ['app'] + delete: + description: + - Delete rule. + required: false + choices: ['yes', 'no'] +''' + +EXAMPLES = ''' +# Allow everything and enable UFW +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. 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 +ufw: rule=allow name=OpenSSH + +# Delete OpenSSH rule +ufw: rule=allow name=OpenSSH delete=yes + +# Deny all access to port 53: +ufw: rule=deny port=53 + +# Allow all access to tcp port 80: +ufw: rule=allow port=80 proto=tcp + +# Allow all access from RFC1918 networks to this host: +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: rule=deny proto=udp src=1.2.3.4 port=514 + +# 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: rule=deny proto=tcp src=2001:db8::/32 port=25 +''' + +from operator import itemgetter + + +def main(): + module = AnsibleModule( + argument_spec = dict( + 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']), + 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), + 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', 'esp', 'ah']), + app = dict(default=None, aliases=['name']) + ), + supports_check_mode = True, + mutually_exclusive = [['app', 'proto']] + ) + + cmds = [] + + def execute(cmd): + cmd = ' '.join(map(itemgetter(-1), filter(itemgetter(0), cmd))) + cmds.append(cmd) + (rc, out, err) = module.run_command(cmd) + + if rc != 0: + module.fail_json(msg=err or out) + + params = module.params + + # 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 len(commands) < 1: + module.fail_json(msg="Not any of the command arguments %s given" % commands) + + # Ensure ufw is available + ufw_bin = module.get_bin_path('ufw', True) + + # 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(): + cmd = [[ufw_bin], [module.check_mode, '--dry-run']] + + if command == 'state': + states = { 'enabled': 'enable', 'disabled': 'disable', + 'reloaded': 'reload', 'reset': 'reset' } + execute(cmd + [['-f'], [states[value]]]) + + elif command == 'logging': + execute(cmd + [[command, value]]) + + elif command == 'default': + execute(cmd + [[command], [value], [params['direction']]]) + + 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([params['insert'], "insert %s" % params['insert']]) + cmd.append([value]) + cmd.append([module.boolean(params['log']), 'log']) + + 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'")]: + + value = params[key] + cmd.append([value, template % (value)]) + + execute(cmd) + + # Get the new state + (_, post_state, _) = module.run_command(ufw_bin + ' status verbose') + (_, 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()) + +# include magic from lib/ansible/module_common.py +#<> + +main()