summaryrefslogtreecommitdiff
path: root/roles/colo-router/files/manage_iptables.py
diff options
context:
space:
mode:
Diffstat (limited to 'roles/colo-router/files/manage_iptables.py')
-rwxr-xr-xroles/colo-router/files/manage_iptables.py116
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)