Created
March 29, 2017 14:47
-
-
Save skylander86/0fb8a62c7e9def953c46c6c97ec5d5a8 to your computer and use it in GitHub Desktop.
Get SSL certificates using Certbot and complete challenge automatically using Route 53. A pure Python solution.
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 python | |
from argparse import ArgumentParser | |
import logging | |
import os | |
import sys | |
import subprocess | |
import time | |
import boto3 | |
logger = logging.getLogger(__name__) | |
def main(): | |
parser = ArgumentParser(description='Script to get new certificates using certbot.') | |
parser.add_argument('-D', '--letsencrypt-dir', type=str, metavar='<dir>', default='letsencrypt', help='Directory to store letsencrypt configs and keys.') | |
parser.add_argument('-e', '--email', type=str, metavar='<email>', default='[email protected]', help='Email address linked with these keys.') | |
parser.add_argument('-d', '--domains', type=str, metavar='<domain>', required=True, nargs='+', help='Domain names to get certificate for.') | |
logging.basicConfig(format=u'%(asctime)-15s [%(name)s-%(process)d] %(levelname)s: %(message)s', level=logging.INFO) | |
logging.getLogger('botocore.vendored.requests.packages.urllib3.connectionpool').setLevel(logging.WARNING) | |
logging.getLogger('botocore.credentials').setLevel(logging.WARNING) | |
script_path = os.path.realpath(__file__) | |
CERTBOT_DOMAIN = os.environ.get('CERTBOT_DOMAIN') | |
CERTBOT_VALIDATION = os.environ.get('CERTBOT_VALIDATION') | |
CERTBOT_AUTH_OUTPUT = os.environ.get('CERTBOT_AUTH_OUTPUT') | |
if CERTBOT_AUTH_OUTPUT: return | |
# CERTBOT_TOKEN = os.environ.get('CERTBOT_TOKEN') | |
if CERTBOT_DOMAIN: | |
client = boto3.client('route53') | |
response = client.list_hosted_zones_by_name() | |
zone_id = None | |
for zone in response['HostedZones']: | |
if CERTBOT_DOMAIN.endswith(zone['Name'][:-1]): | |
zone_id = zone['Id'] | |
break | |
#end if | |
#end for | |
if zone_id is None: | |
logger.error('Hosted zone not found for {}'.format(CERTBOT_DOMAIN)) | |
sys.exit(-1) | |
#end if | |
client.change_resource_record_sets( | |
HostedZoneId=zone_id, | |
ChangeBatch=dict( | |
Changes=[ | |
dict( | |
Action='UPSERT', | |
ResourceRecordSet=dict( | |
Name='_acme-challenge.{}.'.format(CERTBOT_DOMAIN), | |
Type='TXT', TTL=3600, | |
ResourceRecords=[dict(Value='"{}"'.format(CERTBOT_VALIDATION))] | |
) | |
) | |
] | |
) | |
) | |
tries = 0 | |
while tries < 10: | |
response = client.test_dns_answer(HostedZoneId=zone_id, RecordName='_acme-challenge.{}.'.format(CERTBOT_DOMAIN), RecordType='TXT', ResolverIP='8.8.8.8') | |
tries += 1 | |
if response['RecordData'] and response['RecordData'][0] == '"{}"'.format(CERTBOT_VALIDATION): | |
logger.info('Updated TXT record for {} to "{}".'.format(CERTBOT_DOMAIN, CERTBOT_VALIDATION)) | |
break | |
#end if | |
time.sleep(tries * 2) | |
#end while | |
if tries >= 10: | |
logger.error('Failed to set TXT record for {} to "{}".'.format(CERTBOT_DOMAIN, CERTBOT_VALIDATION)) | |
sys.exit(-1) | |
#end if | |
else: | |
A = parser.parse_args() | |
for domain in A.domains: | |
logger.info('Getting certificates for "{}"...'.format(domain)) | |
certbot_args = ['certbot', 'certonly', '--config-dir', A.letsencrypt_dir, '--work-dir', A.letsencrypt_dir, '--logs-dir', A.letsencrypt_dir, '--manual', '--preferred-challenge', 'dns', '--manual-auth-hook', script_path, '--manual-cleanup-hook', script_path, '--agree-tos', '-m', A.email, '--non-interactive', '--manual-public-ip-logging-ok', '-d', domain] | |
subprocess.call(certbot_args) | |
#end for | |
#end if | |
#end def | |
if __name__ == '__main__': | |
main() | |
sys.exit(0) | |
#end if | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment