From 73cf1f2755bf73a825dd2760a0b26669f4fddc66 Mon Sep 17 00:00:00 2001 From: Mike Liu Date: Tue, 30 Aug 2016 10:16:10 -0400 Subject: [PATCH] Iptables enhancements (#2789) * Add the flush parameter. When specified the flush parameter indicates that this module should remove all rules from the specified table. If no table parameter is specified then the default filter table is flushed. * Add support for setting chain policies. The module supports setting the policy of a given chain and table to the following target values, ACCEPT, DROP, QUEUE, and RETURN. This parameter ignores all other unrelated parameters. * Fix pep8 issues. * Fix missing quotation. * Make 'flush' and 'policy' parameters mutually exclusive. This combination is not supported by the wrapped iptables command. 'flush' and 'policy' however, can both take the 'chain' argument. --- lib/ansible/modules/extras/system/iptables.py | 113 ++++++++++++++---- 1 file changed, 93 insertions(+), 20 deletions(-) diff --git a/lib/ansible/modules/extras/system/iptables.py b/lib/ansible/modules/extras/system/iptables.py index 51089575456..5d055182367 100644 --- a/lib/ansible/modules/extras/system/iptables.py +++ b/lib/ansible/modules/extras/system/iptables.py @@ -74,8 +74,8 @@ options: description: - "Chain to operate on. This option can either be the name of a user defined chain or any of the builtin chains: 'INPUT', 'FORWARD', - 'OUTPUT', 'PREROUTING', 'POSTROUTING', 'SECMARK', 'CONNSECMARK'" - required: true + 'OUTPUT', 'PREROUTING', 'POSTROUTING', 'SECMARK', 'CONNSECMARK'." + required: false protocol: description: - The protocol of the rule or of the packet to check. The specified @@ -240,13 +240,18 @@ options: default: null ctstate: description: - - "ctstate is a list of the connection states to match in the conntrack module. - Possible states are: 'INVALID', 'NEW', 'ESTABLISHED', 'RELATED', 'UNTRACKED', 'SNAT', 'DNAT'" + - "ctstate is a list of the connection states to match in the conntrack + module. + Possible states are: 'INVALID', 'NEW', 'ESTABLISHED', 'RELATED', + 'UNTRACKED', 'SNAT', 'DNAT'" required: false default: [] limit: description: - - "Specifies the maximum average number of matches to allow per second. The number can specify units explicitly, using `/second', `/minute', `/hour' or `/day', or parts of them (so `5/second' is the same as `5/s')." + - "Specifies the maximum average number of matches to allow per second. + The number can specify units explicitly, using `/second', `/minute', + `/hour' or `/day', or parts of them (so `5/second' is the same as + `5/s')." required: false default: null limit_burst: @@ -268,10 +273,24 @@ options: icmp_type: version_added: "2.2" description: - - "This allows specification of the ICMP type, which can be a numeric ICMP type, - type/code pair, or one of the ICMP type names shown by the command - 'iptables -p icmp -h'" + - "This allows specification of the ICMP type, which can be a numeric + ICMP type, type/code pair, or one of the ICMP type names shown by the + command 'iptables -p icmp -h'" required: false + flush: + version_added: "2.2" + description: + - "Flushes the specified table and chain of all rules. If no chain is + specified then the entire table is purged. Ignores all other + parameters." + required: false + policy: + version_added: "2.2" + description: + - "Set the policy for the chain to the given target. Valid targets are + ACCEPT, DROP, QUEUE, RETURN. Only built in chains can have policies. + This parameter requires the chain parameter. Ignores all other + parameters." ''' EXAMPLES = ''' @@ -337,7 +356,11 @@ def construct_rule(params): append_param(rule, params['destination_port'], '--destination-port', False) append_param(rule, params['to_ports'], '--to-ports', False) append_param(rule, params['set_dscp_mark'], '--set-dscp', False) - append_param(rule, params['set_dscp_mark_class'], '--set-dscp-class', False) + append_param( + rule, + params['set_dscp_mark_class'], + '--set-dscp-class', + False) append_match(rule, params['comment'], 'comment') append_param(rule, params['comment'], '--comment', False) append_match(rule, params['ctstate'], 'state') @@ -353,11 +376,12 @@ def construct_rule(params): return rule -def push_arguments(iptables_path, action, params): +def push_arguments(iptables_path, action, params, make_rule=True): cmd = [iptables_path] cmd.extend(['-t', params['table']]) cmd.extend([action, params['chain']]) - cmd.extend(construct_rule(params)) + if make_rule: + cmd.extend(construct_rule(params)) return cmd @@ -382,15 +406,39 @@ def remove_rule(iptables_path, module, params): module.run_command(cmd, check_rc=True) +def flush_table(iptables_path, module, params): + cmd = push_arguments(iptables_path, '-F', params, make_rule=False) + module.run_command(cmd, check_rc=True) + + +def set_chain_policy(iptables_path, module, params): + cmd = push_arguments(iptables_path, '-P', params, make_rule=False) + cmd.append(params['policy']) + module.run_command(cmd, check_rc=True) + + def main(): module = AnsibleModule( supports_check_mode=True, argument_spec=dict( - table=dict(required=False, default='filter', choices=['filter', 'nat', 'mangle', 'raw', 'security']), - state=dict(required=False, default='present', choices=['present', 'absent']), - action=dict(required=False, default='append', type='str', choices=['append', 'insert']), - ip_version=dict(required=False, default='ipv4', choices=['ipv4', 'ipv6']), - chain=dict(required=True, default=None, type='str'), + table=dict( + required=False, + default='filter', + choices=['filter', 'nat', 'mangle', 'raw', 'security']), + state=dict( + required=False, + default='present', + choices=['present', 'absent']), + action=dict( + required=False, + default='append', + type='str', + choices=['append', 'insert']), + ip_version=dict( + required=False, + default='ipv4', + choices=['ipv4', 'ipv6']), + chain=dict(required=False, default=None, type='str'), protocol=dict(required=False, default=None, type='str'), source=dict(required=False, default=None, type='str'), to_source=dict(required=False, default=None, type='str'), @@ -406,8 +454,8 @@ def main(): source_port=dict(required=False, default=None, type='str'), destination_port=dict(required=False, default=None, type='str'), to_ports=dict(required=False, default=None, type='str'), - set_dscp_mark=dict(required=False,default=None, type='str'), - set_dscp_mark_class=dict(required=False,default=None, type='str'), + set_dscp_mark=dict(required=False, default=None, type='str'), + set_dscp_mark_class=dict(required=False, default=None, type='str'), comment=dict(required=False, default=None, type='str'), ctstate=dict(required=False, default=[], type='list'), limit=dict(required=False, default=None, type='str'), @@ -415,9 +463,16 @@ def main(): uid_owner=dict(required=False, default=None, type='str'), reject_with=dict(required=False, default=None, type='str'), icmp_type=dict(required=False, default=None, type='str'), + flush=dict(required=False, default=False, type='bool'), + policy=dict( + required=False, + default=None, + type='str', + choices=['ACCEPT', 'DROP', 'QUEUE', 'RETURN']), ), mutually_exclusive=( ['set_dscp_mark', 'set_dscp_mark_class'], + ['flush', 'policy'], ), ) args = dict( @@ -426,12 +481,30 @@ def main(): ip_version=module.params['ip_version'], table=module.params['table'], chain=module.params['chain'], + flush=module.params['flush'], rule=' '.join(construct_rule(module.params)), state=module.params['state'], ) - insert = (module.params['action'] == 'insert') + ip_version = module.params['ip_version'] iptables_path = module.get_bin_path(BINS[ip_version], True) + + # Check if chain option is required + if args['flush'] is False and args['chain'] is None: + module.fail_json( + msg="Either chain or flush parameter must be specified.") + + # Flush the table + if args['flush'] is True: + flush_table(iptables_path, module, module.params) + module.exit_json(**args) + + # Set the policy + if module.params['policy']: + set_chain_policy(iptables_path, module, module.params) + module.exit_json(**args) + + insert = (module.params['action'] == 'insert') rule_is_present = check_present(iptables_path, module, module.params) should_be_present = (args['state'] == 'present') @@ -443,7 +516,7 @@ def main(): module.exit_json(changed=args['changed']) # Target is already up to date - if args['changed'] == False: + if args['changed'] is False: module.exit_json(**args) if should_be_present: