From 7c309492eaed0e625f94d9c0a93a6f6733c47008 Mon Sep 17 00:00:00 2001 From: Andy Doan Date: Mon, 29 Feb 2016 16:13:55 -0600 Subject: colo: add a way to better manage public/private ip mappings This just does public/private ips for now, but could probably be expanded to also handle our subnet logic as well. Change-Id: If0648142ee91d04b102078cdf2a93b84780f6f4a --- roles/colo-router/files/iptables | 13 +--- roles/colo-router/files/iptables.conf | 19 +++++ roles/colo-router/files/manage_iptables.py | 116 +++++++++++++++++++++++++++++ roles/colo-router/handlers/main.yml | 3 + roles/colo-router/tasks/main.yml | 16 +++- 5 files changed, 153 insertions(+), 14 deletions(-) create mode 100644 roles/colo-router/files/iptables.conf create mode 100755 roles/colo-router/files/manage_iptables.py (limited to 'roles/colo-router') diff --git a/roles/colo-router/files/iptables b/roles/colo-router/files/iptables index 4fda4f76..4960691a 100644 --- a/roles/colo-router/files/iptables +++ b/roles/colo-router/files/iptables @@ -8,18 +8,7 @@ subnets="10.10.0.0/16 10.20.0.0/16 10.30.0.0/16 10.33.0.0/16" modprobe iptable_nat -# perform routing of r1-a1 with a public ip -iptables -t nat -A PREROUTING -d 64.28.108.83 -j DNAT --to-destination 10.64.0.101 -iptables -t nat -A POSTROUTING -s 10.64.0.101 -j SNAT --to 64.28.108.83 -# perform routing of r1-a2 with a public ip -iptables -t nat -A PREROUTING -d 64.28.108.84 -j DNAT --to-destination 10.64.0.102 -iptables -t nat -A POSTROUTING -s 10.64.0.102 -j SNAT --to 64.28.108.84 -# perform routing of r1-a3 with a public ip -iptables -t nat -A PREROUTING -d 64.28.108.85 -j DNAT --to-destination 10.64.0.103 -iptables -t nat -A POSTROUTING -s 10.64.0.103 -j SNAT --to 64.28.108.85 -# perform routing of r1-a21 (git-atx) with a public ip -iptables -t nat -A PREROUTING -d 64.28.108.189 -j DNAT --to-destination 10.10.0.121 -iptables -t nat -A POSTROUTING -s 10.10.0.121 -j SNAT --to 64.28.108.189 +/usr/local/bin/manage_iptables.py sync for subnet in $subnets ; do iptables -t nat -A POSTROUTING -s $subnet -o $ext -j MASQUERADE diff --git a/roles/colo-router/files/iptables.conf b/roles/colo-router/files/iptables.conf new file mode 100644 index 00000000..85a500a8 --- /dev/null +++ b/roles/colo-router/files/iptables.conf @@ -0,0 +1,19 @@ +[public/private mappings] +# r1-a1 +64.28.108.83 = 10.64.0.101 +# r1-a2 +64.28.108.84 = 10.64.0.102 +# r1-a3 +64.28.108.85 = 10.64.0.103 + +# r1-a21 (weechat.linaro.org) +64.28.108.189 = 10.10.0.121 + +#64.28.99.27 +#64.28.99.28 +#64.28.99.29 +#64.28.99.30 +#64.28.99.31 +#64.28.99.32 +#64.28.99.33 +#64.28.99.34 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) diff --git a/roles/colo-router/handlers/main.yml b/roles/colo-router/handlers/main.yml index 892aff61..20cef224 100644 --- a/roles/colo-router/handlers/main.yml +++ b/roles/colo-router/handlers/main.yml @@ -6,3 +6,6 @@ - name: reload squid shell: /etc/init.d/squid3 reload + +- name: reload iptables + shell: /usr/local/bin/manage_iptables.py sync diff --git a/roles/colo-router/tasks/main.yml b/roles/colo-router/tasks/main.yml index 98c4f0b2..bc0ffa5c 100644 --- a/roles/colo-router/tasks/main.yml +++ b/roles/colo-router/tasks/main.yml @@ -65,12 +65,24 @@ regexp="^\/etc\/rc.aus-colo-nat #ADDED BY ANSIBLE" insertbefore="exit 0"' +- name: Copy manage_iptables.py + copy: src=manage_iptables.py dest=/usr/local/bin/manage_iptables.py mode=0655 + +- name: Copy iptables.conf + copy: src=iptables.conf dest=/etc/iptables.conf mode=0655 + tags: + - dns + notify: + - reload iptables + - name: Set up network interfaces copy: src=router-interfaces dest=/etc/network/interfaces owner=root group=root mode=0644 + tags: + - dns - name: Set up hosts file template: src=hosts.j2 @@ -79,7 +91,7 @@ group=root mode=0644 tags: - - dnsmasq + - dns - name: Install dnsmasq action: apt pkg=dnsmasq @@ -93,7 +105,7 @@ notify: - reload dnsmasq tags: - - dnsmasq + - dns - name: Install pdu_power script action: copy src=pdu_power -- cgit v1.2.3