diff options
Diffstat (limited to 'roles/colo-router/files/manage_iptables.py')
-rwxr-xr-x | roles/colo-router/files/manage_iptables.py | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/roles/colo-router/files/manage_iptables.py b/roles/colo-router/files/manage_iptables.py new file mode 100755 index 00000000..ffa9b68e --- /dev/null +++ b/roles/colo-router/files/manage_iptables.py @@ -0,0 +1,116 @@ +#!/usr/bin/python3 +import argparse +import configparser + +import iptc + + +def _get_in_out_chains(): + '''Return the inbound and outbound rule chains''' + table = iptc.Table(iptc.Table.NAT) + table.autocommit = False + return iptc.Chain(table, 'PREROUTING'), iptc.Chain(table, 'POSTROUTING') + + +def inbound_rules(chain): + for rule in chain.rules: + if rule.target.name == 'DNAT': + public, _ = rule.dst.split('/') + private = rule.target.get_all_parameters()['to-destination'][0] + yield rule, public, private + + +def outbound_rules(chain): + for rule in chain.rules: + if rule.target.name == 'SNAT': + private, _ = rule.src.split('/') + public = rule.target.get_all_parameters()['to-source'][0] + yield rule, public, private + + +def _ls(args): + inchain, outchain = _get_in_out_chains() + inbound = [(pub, priv) for rule, pub, priv in inbound_rules(inchain)] + outbound = [(pub, priv) for rule, pub, priv in outbound_rules(outchain)] + + if inbound != outbound: + print('ERROR: Mismatch between inbound and outbound rules!') + + print('= Inbound public->private:') + for public, private in inbound: + print('%s -> %s' % (public, private)) + + print('\n= Outbound private->public:') + for public, private in outbound: + print('%s -> %s' % (private, public)) + + +def _sync_inbound(chain, pub_to_priv): + found = [] + for rule, public, private in inbound_rules(chain): + mapping = (public, private) + if mapping in pub_to_priv: + found.append(mapping) + else: + print('Removing inbound mapping: %s->%s' % mapping) + chain.delete_rule(rule) + for public, private in pub_to_priv - set(found): + print('Adding inbound mapping: %s->%s' % (public, private)) + rule = iptc.Rule() + rule.dst = '%s/255.255.255.255' % public + rule.protocol = 'ip' + t = rule.create_target('DNAT') + t.set_parameter('to-destination', [private]) + chain.insert_rule(rule) + + +def _sync_outbound(chain, pub_to_priv): + found = [] + for rule, public, private in outbound_rules(chain): + mapping = (public, private) + if mapping in pub_to_priv: + found.append(mapping) + else: + print('Removing outbound mapping: %s->%s' % mapping) + chain.delete_rule(rule) + for public, private in pub_to_priv - set(found): + print('Adding outbound mapping: %s->%s' % (public, private)) + rule = iptc.Rule() + rule.src = '%s/255.255.255.255' % private + rule.protocol = 'ip' + t = rule.create_target('SNAT') + t.set_parameter('to-source', [public]) + chain.insert_rule(rule) + + +def _sync(args): + cp = configparser.ConfigParser() + cp.read(args.file) + config = cp['public/private mappings'] + pub_to_priv = set(config.items()) + + inchain, outchain = _get_in_out_chains() + _sync_inbound(inchain, pub_to_priv) + _sync_outbound(outchain, pub_to_priv) + + if not args.dryrun: + inchain.table.commit() + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description='Manage public/private IP address mappings') + sub = parser.add_subparsers(title='Commands', metavar='') + + p = sub.add_parser('ls', help='list mappgings') + p.set_defaults(func=_ls) + + p = sub.add_parser('sync', help='sync rules defined in mapping file') + p.add_argument('--dryrun', action='store_true') + p.add_argument('--file', '-f', default='/etc/iptables.conf', + help='IP configuration mappings. default=%(default)s') + p.set_defaults(func=_sync) + + args = parser.parse_args() + if getattr(args, 'func', None): + args.func(args) |