Last active
March 17, 2021 09:01
-
-
Save joshgarnett/02920846fea35f738d3370fd991bb0e0 to your computer and use it in GitHub Desktop.
Ruby script to use as a hook for the letsencrypt.sh client
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'aws-sdk' | |
# | |
# This script requires you to have the following environment variables set: | |
# AWS_REGION="us-west-2" | |
# AWS_ACCESS_KEY_ID="<YOUR_KEY>" | |
# AWS_SECRET_ACCESS_KEY="<YOUR_SECRET_KEY>" | |
# | |
# Based on https://gist.github.com/asimihsan/d8d8f0f10bdc85fc6f8a | |
# | |
def find_hosted_zone(route53, domain) | |
route53 = Aws::Route53::Client.new | |
hosted_zones = route53.list_hosted_zones_by_name.hosted_zones | |
index = hosted_zones.index { |zone| domain.end_with?(zone.name.chop) } | |
if index.nil? | |
puts 'Unable to find matching zone.' | |
exit 1 | |
end | |
hosted_zones[index] | |
end | |
def wait_for_change(route53, change_id) | |
status = '' | |
until status == 'INSYNC' | |
resp = route53.get_change(id: change_id) | |
status = resp.change_info.status | |
if status != 'INSYNC' | |
puts 'Waiting for dns change to complete' | |
sleep 5 | |
end | |
end | |
end | |
def setup_dns(domain, txt_challenge) | |
route53 = Aws::Route53::Client.new | |
hosted_zone = find_hosted_zone(route53, domain) | |
changes = [] | |
changes << { | |
action: 'UPSERT', | |
resource_record_set: { | |
name: "_acme-challenge.#{domain}.", | |
type: 'TXT', | |
ttl: 60, | |
resource_records: [ | |
value: "\"#{txt_challenge}\"" | |
] | |
} | |
} | |
resp = route53.change_resource_record_sets( | |
hosted_zone_id: hosted_zone.id, | |
change_batch: { | |
changes: changes | |
} | |
) | |
wait_for_change(route53, resp.change_info.id) | |
end | |
def delete_dns(domain, txt_challenge) | |
route53 = Aws::Route53::Client.new | |
hosted_zone = find_hosted_zone(route53, domain) | |
changes = [] | |
changes << { | |
action: 'DELETE', | |
resource_record_set: { | |
name: "_acme-challenge.#{domain}.", | |
type: 'TXT', | |
ttl: 60, | |
resource_records: [ | |
value: "\"#{txt_challenge}\"" | |
] | |
} | |
} | |
resp = route53.change_resource_record_sets( | |
hosted_zone_id: hosted_zone.id, | |
change_batch: { | |
changes: changes | |
} | |
) | |
wait_for_change(route53, resp.change_info.id) | |
end | |
if __FILE__ == $PROGRAM_NAME | |
hook_stage = ARGV[0] | |
domain = ARGV[1] | |
txt_challenge = ARGV[3] | |
puts "stage: #{hook_stage} domain: #{domain} txt_challenge: #{txt_challenge}" | |
if hook_stage == 'deploy_challenge' | |
setup_dns(domain, txt_challenge) | |
elsif hook_stage == 'clean_challenge' | |
delete_dns(domain, txt_challenge) | |
end | |
end |
@reinhard-brandstaedter what I did to fix the same issue:
index = hosted_zones.rindex { |zone| domain.end_with?(zone.name.chop) }
It will then use the "earliest match"
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I found that this script has problems when you have sub domain zones in AWS. Eg.
index = hosted_zones.index { |zone| domain.end_with?(zone.name.chop) }
This will cause the script to create the challenge TXT record in the example.com zone but a DNS query will try to find it in the subdomain zone. To work around this I changed the comparison to the other way around. Instead of checking if the domain ends with the zone name I check if the zone name ends with the domain latter part:
index = hosted_zones.index { |zone| zone.name.chop.end_with?(domain.split(".",2)[1]) }
Something to consider!