Untitled

From anonymous, 2 Years ago, written in Python, viewed 79 times.
URL https://paste.codexterous.com/view/40e59f2e Embed
Download Paste or View Raw
  1. >>>> cat /usr/local/mimas/bin/ipupdate.py
  2. #!/usr/bin/env python
  3.  
  4. import urllib2
  5. import urllib
  6. import ssl
  7. import re
  8. import os
  9. import subprocess
  10. import json
  11. import time
  12. import base64
  13.  
  14.  
  15. VULTR_API_KEY = "FELCKHE4UPWOOLAMCXGW26VON4GOKIFY2ONA"
  16. VULTR_API_ENDPOINT = "https://api.vultr.com"
  17. USER_AGENT = "Mimas vultr api communicator"
  18. DOMAIN_NAME = "codexterous.com"
  19. CACHE_FILE = "/tmp/cached_ipupdate"
  20. STATIC_IPS = ['108.58.4.226',]
  21. PROTECTED_PORTS = [9876,]
  22.  
  23. def fetch(url, data=None, auth=None, hostname=None):
  24.   """
  25.  Fetch Vultr API. url should not include starting endpoint (eg.
  26.  https://api.vultr.com).
  27.  
  28.  auth will be None if no auth, or "username:password" if "basic" authorization
  29.  is desired.
  30.  
  31.  If auth is non-None, an "API-Key" header will be sent with the VULTR_API_KEY,
  32.  so yeah.
  33.  
  34.  hostname overrides VULTR_API_ENDPOINT.
  35.  """
  36.   code, body = None, None
  37.  
  38.   if data:
  39.     data = urllib.urlencode(data)
  40.  
  41.   try:
  42.     req = None
  43.     if hostname:
  44.       req = urllib2.Request(hostname + url, data)
  45.     else:
  46.       req = urllib2.Request(VULTR_API_ENDPOINT + url, data)
  47.     req.add_header('User-Agent', USER_AGENT)
  48.     if not auth:
  49.       req.add_header('API-Key', VULTR_API_KEY)
  50.     elif auth:
  51.       req.add_header(
  52.           'Authorization',
  53.           'Basic ' +
  54.           base64.encodestring(auth).replace('\n', '')
  55.       )
  56.  
  57.     if data:
  58.       req.add_header('Content-Type', "application/x-www-form-urlencoded")
  59.  
  60.     # TODO: Python 2.7.9 broke handling of SSL. Haven't fully investigated.
  61.     #       Had to turn off cert validation for now.
  62.     context = ssl.create_default_context()
  63.     context.check_hostname = False
  64.     context.verify_mode = ssl.CERT_NONE
  65.  
  66.     response = urllib2.urlopen(req, context=context)
  67.  
  68.     # Avoid 503 rate limit (rate limit is avg. 2 req's/second, but even though
  69.     # we're not very active, we wind up exceeding that if we look up an IP,
  70.     # change DNS, and delete/add a few firewall rules all in one shot).
  71.     time.sleep(2)
  72.  
  73.     code = response.getcode()
  74.     body = response.read()
  75.  
  76.   except urllib2.HTTPError as e:
  77.     code = e.getcode()
  78.     body = e.read()
  79.  
  80.   return code, body
  81.  
  82.  
  83. def get_vultr_dns_ip(subdomain, domain):
  84.   code, body = fetch("/v1/dns/records?domain=%s" % (domain.lower()))
  85.   if code == 200:
  86.     data = json.loads(body)
  87.     for r in data:
  88.       if r['name'].lower() == subdomain.lower():
  89.         return {'data': r['data'], 'recordid': r['RECORDID']}
  90.   print " *** VULTR FAILURE: code: %s body: %s" % (code, body)
  91.  
  92.  
  93. def get_cached_dns_ip():
  94.   """
  95.  Get cached IP from /tmp file.
  96.  """
  97.   if os.path.exists(CACHE_FILE):
  98.     with open(CACHE_FILE, 'r') as f:
  99.       return f.read().strip()
  100.  
  101.  
  102. def set_cached_dns_ip(ip):
  103.   with open(CACHE_FILE, 'wb') as f:
  104.     f.write(ip + "\n")
  105.  
  106.  
  107. def update_hurricane():
  108.   """
  109.  Update our tunnelbroker with our new IP.
  110.  """
  111.   code, body = fetch("/nic/update?hostname=403254",
  112.                      auth="aftli:rN404dq4nzJGBKm7",
  113.                      hostname="https://ipv4.tunnelbroker.net")
  114.  
  115.   if code == 200:
  116.     print "** Successfully updated our IP with Hurricane Electric"
  117.     print "** Restarting gif0...."
  118.  
  119.     child = subprocess.Popen("ifconfig gif0 down",
  120.                              stdout=subprocess.PIPE, shell=True)
  121.     print "--", child.communicate()[0].strip()
  122.     child = subprocess.Popen("ifconfig gif0 up",
  123.                              stdout=subprocess.PIPE, shell=True)
  124.     print "--", child.communicate()[0].strip()
  125.  
  126.  
  127. def get_wan_ip():
  128.   """
  129.  Get our IP from WAN. Instead of messing with parsing ifconfig output,
  130.  use OpenDNS to get it using dig.
  131.  """
  132.   child = subprocess.Popen("dig +short myip.opendns.com @resolver1.opendns.com",
  133.                            stdout=subprocess.PIPE, shell=True)
  134.   return child.communicate()[0].strip()
  135.  
  136.  
  137. def update_dns(wan_ip, recordid):
  138.   code, body = fetch("/v1/dns/update_record",
  139.                      {'RECORDID': recordid,
  140.                       'name': "hq",
  141.                       'domain': DOMAIN_NAME,
  142.                       'data': wan_ip})
  143.   if code == 200:
  144.     print "** Successfully updated DNS record for hq.%s => %s" % (DOMAIN_NAME, wan_ip)
  145.     return True
  146.  
  147.   print "** ERROR in /v1/dns/update_record, retcode=%s, body=%s" % (code, body)
  148.   return False
  149.  
  150.  
  151. def update_firewall(wan_ip):
  152.   """
  153.  Add our new WAN IP to all vultr firewall groups, and remove and old IPs
  154.  from protected ports that aren't in STATIC_IPS.
  155.  """
  156.   # First, get a list of firewall groups.
  157.   existing_rules = []
  158.   code, body = fetch('/v1/firewall/group_list')
  159.   if code == 200:
  160.     groups = json.loads(body)
  161.     for group_id, group in groups.iteritems():
  162.       # Cycle through firewall rules.
  163.       code, body = fetch("/v1/firewall/rule_list?FIREWALLGROUPID=%s&direction=in&ip_type=v4" % (group_id))
  164.       if code == 200:
  165.         rules = json.loads(body)
  166.         for rule_id, rule in rules.iteritems():
  167.           # Append to existing rules so we don't try to re-create it later.
  168.           existing_rules.append((group_id, rule['port'], rule['subnet']))
  169.  
  170.           # Find all protected ports.
  171.           for p in PROTECTED_PORTS:
  172.             if rule['port'] == str(p):
  173.               # This is a rule we're concerned with.
  174.               if rule['subnet'] not in STATIC_IPS and rule['subnet'] != wan_ip:
  175.                 # This will be one we delete.
  176.                 code, body = fetch("/v1/firewall/rule_delete",
  177.                                    {'FIREWALLGROUPID': group_id,
  178.                                     'rulenumber': rule['rulenumber']})
  179.                 if code == 200:
  180.                   print "-- Deleted firewall rule for IP %s for port %s in group %s" % (rule['subnet'], rule['port'], group['description'])
  181.                 else:
  182.                   print "** FAILED deleting firewall rule (code=%s) - IP %s for port %s in group %s" % (code, rule['subnet'], rule['port'], group['description'])
  183.  
  184.       # Now add our new IP to firwall rules.
  185.       for p in PROTECTED_PORTS:
  186.         # Ensure we're not creating an already existing rule.
  187.         if (group_id, str(p), wan_ip) not in existing_rules:
  188.           code, body = fetch("/v1/firewall/rule_create",
  189.                              {'FIREWALLGROUPID': group_id,
  190.                               'direction': "in",
  191.                               'ip_type': "v4",
  192.                               'protocol': "tcp",
  193.                               'subnet': wan_ip,
  194.                               'subnet_size': '32',
  195.                               'port': p})
  196.           if code == 200:
  197.             success = json.loads(body)
  198.             print "-- Successfully created firewall rule for WAN IP %s for port %s in group %s" % (wan_ip, p, group['description'])
  199.           else:
  200.             print "** FAILED creating firewall rule (code=%s) for WAN IP %s for port %s in group %s" % (code, wan_ip, p, group['description'])
  201.  
  202.  
  203. def main():
  204.   # First figure out what we/vultr thinks our IP is. Try local cache first.
  205.   vultr_ip = get_cached_dns_ip()
  206.   ip_recordid = None
  207.  
  208.   # Failing that, get it from vultr.
  209.   if not vultr_ip:
  210.     vultr_data = get_vultr_dns_ip('hq', DOMAIN_NAME)
  211.     if vultr_data:
  212.       vultr_ip = vultr_data['data']
  213.       ip_recordid = vultr_data['recordid']
  214.  
  215.       if vultr_ip:
  216.         set_cached_dns_ip(vultr_ip)
  217.     else:
  218.       # Vultr API is down. This happens sometimes unfortunately. Just fail
  219.       # silently.
  220.       return -1
  221.  
  222.   if not vultr_ip:
  223.     print "** ERROR: Unable to determine current remote IP (vultr failure?)"
  224.     return -1
  225.  
  226.   wan_ip = get_wan_ip()
  227.  
  228.   # Determine if we need to update.
  229.   if vultr_ip != wan_ip:
  230.     # Get a final decision on what *vultr* thinks our IP is for purposes of
  231.     # erasing the correct records. We also need the record ID to update the
  232.     # DNS entry.
  233.     if not ip_recordid:
  234.       vultr_data = get_vultr_dns_ip('hq', DOMAIN_NAME)
  235.       vultr_ip = vultr_data['data']
  236.       ip_recordid = vultr_data['recordid']
  237.  
  238.     # Update the 'hq' DNS record.
  239.     update_dns(wan_ip, ip_recordid)
  240.  
  241.     # Update all firewall rules too.
  242.     update_firewall(wan_ip)
  243.  
  244.     # Set up our cache so we know we're done.
  245.     set_cached_dns_ip(wan_ip)
  246.  
  247.     # Finally, update Hurricane Electric with our new IPV4 address.
  248.     update_hurricane()
  249.  
  250.  
  251. if __name__ == "__main__":
  252.   main()
  253.  

Reply to "Untitled"

Here you can reply to the paste above