#!/usr/bin/env python import os import json import boto3 import flask import hashlib import functools # pip install flask boto3 # This is purely for documentation purposes __REQUIRED_IAM_POLICY__ = """ { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "ssm:GetParameter", "Resource": "arn:aws:ssm:us-west-2::parameter/DDNS_CLIENTS" }, { "Effect": "Allow", "Action": [ "route53:ListHostedZones", "route53:ChangeResourceRecordSets" ], "Resource": "*" } ] } """ app = flask.Flask(__name__) def returns_plain_text(f): @functools.wraps(f) def wrapper(*args, **kwargs): return flask.Response(f(*args, **kwargs), content_type="text/plain") return wrapper def get_ip(): if "X-Forwarded-For" in flask.request.headers: return flask.request.headers["X-Forwarded-For"] else: return flask.request.remote_addr def update_record(zone, record, ip): client = boto3.client("route53") zones = client.list_hosted_zones()["HostedZones"] client.change_resource_record_sets( HostedZoneId=[z["Id"] for z in zones if z["Name"] == zone][0], ChangeBatch={ "Changes": [{ "Action": "UPSERT", "ResourceRecordSet": { "Name": ".".join((record, zone)), "Type": "A", "TTL": 60, "ResourceRecords": [{ "Value": ip, }] } }] } ) @app.errorhandler(404) @app.errorhandler(405) @app.errorhandler(500) def handle_error(ex): response = flask.Response("Error", content_type="text/plain") response.status_code = getattr(ex, "code", 500) return response @app.route("/new-secret", methods=["GET"]) @returns_plain_text def new_secret(): return hashlib.sha256(os.urandom(100)).hexdigest() def get_client_config(client): ssm = boto3.client("ssm") clients = ssm.get_parameter(Name="DDNS_CLIENTS", WithDecryption=True) config = json.loads(clients["Parameter"]["Value"]) return config.get(client) @app.route("/update", methods=["POST"]) def update_ip(): key = flask.request.form.get("key") config = get_client_config(key) if not config: flask.abort(404) resource, zone = config.split(".", 1) try: update_record(zone, resource, get_ip()) return "OK" except: flask.abort(500) @app.route("/", methods=["GET"]) @returns_plain_text def handle_home(): return get_ip() if __name__ == "__main__": app.debug = True app.run()