Skip to content

Instantly share code, notes, and snippets.

@jerm
Created January 30, 2021 19:59
Show Gist options
  • Save jerm/dbd2ea7f28beb73bf4723aa7700802a9 to your computer and use it in GitHub Desktop.
Save jerm/dbd2ea7f28beb73bf4723aa7700802a9 to your computer and use it in GitHub Desktop.
Sync Tailscale hosts to route53
#!/usr/bin/env python
#
# Sync your tailscale hosts to a route53 hosted zone.
#
import json
import platform
import subprocess
import boto3
import click
ZONE_ID = ""
DOMAIN = ""
if platform.system() == "Darwin":
TSCMD = "/Applications/Tailscale.app/Contents/MacOS/Tailscale"
else:
TSCMD = "tailscale"
def get_ts_hosts():
"""Get hosts list from taillscale. Slight gotcha that this is
divided into "Self" and "Peers", so we grab both."""
tshosts = {}
tsjson = json.loads(
subprocess.run(
["{} status --json".format(TSCMD)], shell=True, capture_output=True
).stdout
)
for key, value in tsjson.items():
if key == "Self":
tshosts[value.get("HostName").lower()] = value.get("TailAddr")
elif key == "Peer":
for _, peerdict in value.items():
tshosts[peerdict.get("HostName").lower()] = peerdict.get("TailAddr")
return tshosts
def get_r53_hosts(zone_id):
""" in which we get a list of A records in our zone """
client = boto3.client("route53")
rrsets = client.list_resource_record_sets(HostedZoneId=zone_id)
r53_records = {}
for rrset in rrsets["ResourceRecordSets"]:
if rrset["Type"] == "A":
r53_records[rrset["Name"]] = rrset["ResourceRecords"][0]["Value"]
return r53_records
@click.command()
@click.option("--domain", type=str, default=DOMAIN)
@click.option("--zone-id", type=str, default=ZONE_ID)
def main(domain, zone_id):
""" main logic """
client = boto3.client("route53")
changes = []
tshosts = get_ts_hosts()
r53_records = get_r53_hosts(zone_id)
for host, ip in tshosts.items():
hostname = "{}.{}.".format(host, domain)
if hostname in r53_records:
if not ip == r53_records[hostname]:
print(
"{} exists, but ips don't match r53 {} vs ts {}".format(
hostname, r53_records[hostname], ip
)
)
changes.append(
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": hostname,
"Type": "A",
"TTL": 300,
"ResourceRecords": [
{"Value": ip},
],
},
}
)
else:
print(hostname, "is correct")
else:
print("Need to add", hostname)
changes.append(
{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": hostname,
"Type": "A",
"TTL": 300,
"ResourceRecords": [
{"Value": ip},
],
},
}
)
if changes:
_ = client.change_resource_record_sets(
HostedZoneId=zone_id, ChangeBatch={"Comment": "string", "Changes": changes}
)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment