Skip to content

Instantly share code, notes, and snippets.

@rmarchei
Last active June 16, 2022 04:11
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save rmarchei/98489c05f0898abe612eec916508f2bf to your computer and use it in GitHub Desktop.
Save rmarchei/98489c05f0898abe612eec916508f2bf to your computer and use it in GitHub Desktop.
route53 hook for dehydrated - python2 / python3 + boto2 version. Tested on Ubuntu 16.04
#!/usr/bin/env python
# How to use:
#
# Ubuntu 16.04: apt install -y python-boto OR apt install -y python3-boto
#
# Specify the default profile on aws/boto profile files or use the optional AWS_PROFILE env var:
# AWS_PROFILE=example ./dehydrated -c -d example.com -t dns-01 -k /etc/dehydrated/hooks/route53.py
#
# Manually specify hosted zone:
# HOSTED_ZONE=example.com AWS_PROFILE=example ./dehydrated -c -d example.com -t dns-01 -k /etc/dehydrated/hooks/route53.py
#
# More info about dehaydrated and dns challenge: https://github.com/lukas2511/dehydrated/wiki/Examples-for-DNS-01-hooks
# Using AWS Profiles: http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-multiple-profiles
import os
import sys
from boto.route53 import *
from time import sleep
def route53_dns(domain, txt_challenge, action='upsert'):
conn = connection.Route53Connection()
action = action.upper()
if 'HOSTED_ZONE' in os.environ:
hosted_zone = os.environ['HOSTED_ZONE']
if not domain.endswith(hosted_zone):
raise Exception("Incorrect hosted zone for domain {0}".format(domain))
zone = conn.get_hosted_zone_by_name("{0}.".format(hosted_zone))
zone_id = zone['GetHostedZoneResponse']['HostedZone']['Id'].replace('/hostedzone/', '')
else:
zones = conn.get_all_hosted_zones()
for zone in zones['ListHostedZonesResponse']['HostedZones']:
if "{0}.".format(domain).endswith(zone['Name']):
zone_id = zone['Id'].replace('/hostedzone/', '')
break
else:
raise Exception("Hosted zone not found for domain {0}".format(domain))
name = u'_acme-challenge.{0}.'.format(domain)
record_set = conn.get_all_rrsets(zone_id, name=name, type='TXT')
challenges = [u'"{0}"'.format(txt_challenge)]
for r in record_set:
if r.name == name and r.type == 'TXT':
challenges += r.resource_records
change_set = record.ResourceRecordSets(conn, zone_id)
change = change_set.add_change("{0}".format(action), '_acme-challenge.{0}'.format(domain), type='TXT', ttl=60)
for c in set(challenges):
change.add_value(c)
try:
response = change_set.commit()
except Exception as e:
if action == "DELETE":
pass
else:
print(e.message, e.args)
if action in ('CREATE', 'UPSERT'):
# wait for DNS update
timeout = 300
sleep_time = 5
time_elapsed = 0
st = status.Status(conn, response['ChangeResourceRecordSetsResponse']['ChangeInfo'])
while st.update() != 'INSYNC' and time_elapsed <= timeout:
print("Waiting for DNS change to complete... (Elapsed {0} seconds)".format(time_elapsed))
sleep(sleep_time)
time_elapsed += sleep_time
if st.update() != 'INSYNC' and time_elapsed > timeout:
raise Exception("Timed out while waiting for DNS record to be ready. Waited {0} seconds".format(time_elapsed))
print("DNS change completed")
if __name__ == "__main__":
hook = sys.argv[1]
if len(sys.argv) > 2:
domain = sys.argv[2]
txt_challenge = sys.argv[4]
else:
domain = None
txt_challenge = None
if hook == "deploy_challenge":
action = 'upsert'
elif hook == "clean_challenge":
action = 'delete'
else:
sys.exit(0)
print("hook: {0}".format(hook))
print("domain: {0}".format(domain))
print("txt_challenge: {0}".format(txt_challenge))
route53_dns(domain, txt_challenge, action)
@mirath
Copy link

mirath commented May 26, 2018

@niall-byrne Thanks!!!

@nh2
Copy link

nh2 commented May 27, 2018

Is any of the versions above able to use wildcards? I'm still getting the error but the values provided do not match the current values as mentioned above (https://gist.github.com/rmarchei/98489c05f0898abe612eec916508f2bf#gistcomment-2594451).

@nh2
Copy link

nh2 commented May 27, 2018

I've written a fork of this gist to:

  • Implement wildcard support
  • Allow HOOK_CHAIN="yes" for much faster batch confirmation (only need to wait for DNS propagation once, not once per domain)

https://gist.github.com/nh2/f744ac591e95f0c25b501db00cf7c71a

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment