From 48ad71ad1e508e54dbf57ef6ad61e9a84c707a59 Mon Sep 17 00:00:00 2001 From: Alexander Kukushkin Date: Wed, 12 Jul 2017 14:38:28 +0200 Subject: [PATCH 1/2] Custom routing to predefined NAT when run in DMZ without public ip We have a use-case when we want to run instances in DMZ or public subnet but without public ips. Elastic ip would be assigned later to one of the instances. Without public ip instance will not be able to initialize (download docker, push logs, use AWS api, etc...). To solve this problem we need to create a separate routing table for outgoing https traffic. Such traffic would be routed to a custom nat gateway. Mappings between subnets and nat gateways would come from UserData and populated by senza. --- .../init.d/00-create-custom-routing.py | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100755 runtime/opt/taupage/init.d/00-create-custom-routing.py diff --git a/runtime/opt/taupage/init.d/00-create-custom-routing.py b/runtime/opt/taupage/init.d/00-create-custom-routing.py new file mode 100755 index 0000000..ef45eea --- /dev/null +++ b/runtime/opt/taupage/init.d/00-create-custom-routing.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +import logging +import requests +import sys +import subprocess + +from taupage import configure_logging, get_config + + +def subprocess_call(args): + cmd = ' '.join(args) + try: + exitcode = subprocess.call(args) + if exitcode == 0: + return logging.info("Successfully executed '%s'", cmd) + logging.error("Executing of '%s' failed with exitcode=%s", cmd, exitcode) + except Exception: + logging.exception("Failed to execute '%s'", cmd) + sys.exit(1) + + +def main(): + """Confugure custom routing if necessary""" + + configure_logging() + config = get_config() + + nat_gateways = config.get('nat_gateways') + + if not nat_gateways or not isinstance(nat_gateways, dict): # nat gateways must be non empty dictionary + sys.exit(0) + + METADATA_URL = 'http://169.254.169.254/latest/meta-data/network/interfaces/macs/' + try: + r = requests.get(METADATA_URL) + mac = r.text.split()[0] + r = requests.get(METADATA_URL + mac + 'subnet-id') + subnet = r.text + if subnet not in nat_gateways: + logging.warning('Can not find subnet %s in the nat_gateways mapping', subnet) + sys.exit(0) + logging.info('Will use %s nat gateway for outgoing https traffic', nat_gateways[subnet]) + except Exception: + logging.exception('Failed to read metadata') + sys.exit(1) + + RT_TABLES = '/etc/iproute2/rt_tables' + + try: + with open(RT_TABLES, 'a') as f: + f.write('\n150 https\n') + logging.info('Created new routing table for https traffic') + except Exception: + logging.exception('Failed to write into %s', RT_TABLES) + sys.exit(1) + + subprocess_call(['iptables', '-t', 'mangle', '-A', 'OUTPUT', '-p', 'tcp', '!', + '-d', '172.16.0.0/12', '--dport', '443', '-j', 'MARK', '--set-mark', '443']) + + subprocess_call(['ip', 'rule', 'add', 'fwmark', '443', 'lookup', 'https']) + + subprocess_call(['ip', 'route', 'add', 'default', 'via', nat_gateways[subnet], 'table', 'https']) + + +if __name__ == '__main__': + main() From 340e10f3c29df7fe5b8143128a3e4ecd7eab0395 Mon Sep 17 00:00:00 2001 From: Alexander Kukushkin Date: Fri, 14 Jul 2017 15:02:32 +0200 Subject: [PATCH 2/2] Don't route S3 traffic to a nat-gateway We have S3 endpoint configured in every account. --- .../init.d/00-create-custom-routing.py | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/runtime/opt/taupage/init.d/00-create-custom-routing.py b/runtime/opt/taupage/init.d/00-create-custom-routing.py index ef45eea..2c37b27 100755 --- a/runtime/opt/taupage/init.d/00-create-custom-routing.py +++ b/runtime/opt/taupage/init.d/00-create-custom-routing.py @@ -31,15 +31,21 @@ def main(): if not nat_gateways or not isinstance(nat_gateways, dict): # nat gateways must be non empty dictionary sys.exit(0) - METADATA_URL = 'http://169.254.169.254/latest/meta-data/network/interfaces/macs/' + METADATA_URL = 'http://169.254.169.254/latest/meta-data/' try: - r = requests.get(METADATA_URL) - mac = r.text.split()[0] - r = requests.get(METADATA_URL + mac + 'subnet-id') + r = requests.get(METADATA_URL + 'placement/availability-zone') + region = r.text.strip()[:-1] + logging.info('Region=%s', region) + + r = requests.get(METADATA_URL + 'mac') + mac = r.text.strip() + + r = requests.get(METADATA_URL + 'network/interfaces/macs/' + mac + '/subnet-id') subnet = r.text if subnet not in nat_gateways: logging.warning('Can not find subnet %s in the nat_gateways mapping', subnet) sys.exit(0) + logging.info('Will use %s nat gateway for outgoing https traffic', nat_gateways[subnet]) except Exception: logging.exception('Failed to read metadata') @@ -62,6 +68,18 @@ def main(): subprocess_call(['ip', 'route', 'add', 'default', 'via', nat_gateways[subnet], 'table', 'https']) + # S3 is exceptional, it has it's own endpoint in VPC + try: + r = requests.get('https://ip-ranges.amazonaws.com/ip-ranges.json') + ranges = [e['ip_prefix'] for e in r.json()['prefixes'] + if e['service'] == 'S3' and e['region'] == region and 'ip_prefix' in e] + except Exception: + logging.exception('Failed to load ip-ranges.json') + + # Don't mark outgoing traffic to S3 + for r in ranges: + subprocess_call(['iptables', '-t', 'mangle', '-I', 'OUTPUT', '-d', r, '-j', 'ACCEPT']) + if __name__ == '__main__': main()