import boto3 from datetime import datetime from datetime import timedelta def lambda_handler(event, context): region = 'us-east-1' days_old = 60 client_ssm = boto3.client('ssm', region_name=region) client_ec2 = boto3.client('ec2', region_name=region) client_sns = boto3.client('sns', region_name=region) #amzn2_ami_parameter = '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2' amzn2_ami_parameter = '/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64' current_ami = client_ssm.get_parameter(Name=amzn2_ami_parameter)['Parameter']['Value'] #print(current_ami) ec2_role_name = event['EC2_ROLE_NAME'] domain_name = event['DOMAIN_NAME'] certgen_bucket = event['CERTGEN_BUCKET'] sns_topic_arn = event['SNS_TOPIC_ARN'] if 'DNS_SERVICE' in event: dns_service = event['DNS_SERVICE'] else: dns_service = 'Route53' if 'IMPORT_ACM' in event: if event['IMPORT_ACM'] == 'Yes': import_acm = 'Yes' else: import_acm = 'No' else: import_acm = 'No' user_data = '''#!/bin/bash sudo yum -y install python3 sudo python3 -m ensurepip --upgrade python3 -m pip install certbot-route53 certbot-dns-cloudflare --user #pip3 install certbot-route53 --user AWS_DEFAULT_REGION="{}" DOMAINNAME="{}" echo "$DOMAINNAME" > /root/certgen-$DOMAINNAME.log CERTGENBUCKETNAME="{}" SNSTOPICARN="{}" IMPORT_ACM="{}" CERT_REGION="us-east-1" echo "$CERTGENBUCKETNAME" >> /root/certgen-$DOMAINNAME.log if [[ $DNS_SERVICE == "cloudflare" ]]; then echo "dns service is cloudflare" >> /root/certgen-$DOMAINNAME.log echo "dns_cloudflare_api_token = $(aws --region $CERT_REGION ssm get-parameter --name /certgen/$DOMAINNAME/cloudflare --with-decryption --query 'Parameter.Value' --output text)" > /root/cloudflare.ini chmod 600 /root/cloudflare.ini /root/.local/bin/certbot --config-dir /root/certbot/config --work-dir /root/certbot/work --logs-dir /root/certbot/logs certonly --dns-cloudflare --dns-cloudflare-credentials /root/cloudflare.ini --dns-cloudflare-propagation-seconds 30 -d *.$DOMAINNAME,$DOMAINNAME -n --email hostmaster@$DOMAINNAME --no-eff-email --agree-tos >> /root/certgen-$DOMAINNAME.log fi if [[ $DNS_SERVICE == "Route53" ]]; then echo "dns service is Route53" >> /root/certgen-$DOMAINNAME.log /root/.local/bin/certbot --config-dir /root/certbot/config --work-dir /root/certbot/work --logs-dir /root/certbot/logs certonly --dns-route53 --dns-route53-propagation-seconds 30 -d *.$DOMAINNAME,$DOMAINNAME -n --email hostmaster@$DOMAINNAME --no-eff-email --agree-tos >> /root/certgen-$DOMAINNAME.log fi ls -l /root/certgen/config/live/$DOMAINNAME/ >> /root/certgen-$DOMAINNAME.log P12PASSWORD="$(openssl rand -base64 32)" openssl pkcs12 -export -inkey /root/certgen/config/live/$DOMAINNAME/privkey.pem -in /root/certgen/config/live/$DOMAINNAME/fullchain.pem -out /root/certgen/$DOMAINNAME.p12 -password pass:"$P12PASSWORD" ## To encrypt and backup certbot configuration # tar zcf - /root/certgen | openssl enc -e -aes256 -out /root/certgen.tar.gz -pass pass:"$P12PASSWORD" ## To decrypt certbot configuration backup file # openssl enc -d -aes256 -in /root/certgen.tar.gz -pass pass:"$P12PASSWORD" | tar zxv aws --region $CERT_REGION ssm put-parameter --name /certgen/$DOMAINNAME/p12password --value "$P12PASSWORD" --type SecureString --overwrite >> /root/certgen-$DOMAINNAME.log aws --region $CERT_REGION s3 cp /root/certgen/$DOMAINNAME.p12 s3://$CERTGENBUCKETNAME/$DOMAINNAME/$DOMAINNAME.p12 >> /root/certgen-$DOMAINNAME.log ## To backup encrypted certbot configuration file # aws --region $CERT_REGION s3 cp /root/certgen.tar.gz s3://$CERTGENBUCKETNAME/$DOMAINNAME/certgen-$DOMAINNAME.tar.gz >> /root/certgen-$DOMAINNAME.log if [[ $IMPORT_ACM == "Yes" ]]; then CERT_ARN=$(aws --region $CERT_REGION acm list-certificates --query 'CertificateSummaryList[?DomainName==`'*.$DOMAINNAME'`].CertificateArn' --output text) echo $CERT_ARN >> /root/certgen-$DOMAINNAME.log if [[ $CERT_ARN != "" ]]; then echo "Existing certificate for $DOMAINNAME was found with ARN $CERT_ARN. Updating." >> /root/certgen-$DOMAINNAME.log aws --region $CERT_REGION acm import-certificate \ --certificate file:///root/certgen/config/live/$DOMAINNAME/cert.pem \ --private-key file:///root/certgen/config/live/$DOMAINNAME/privkey.pem \ --certificate-chain file:///root/certgen/config/live/$DOMAINNAME/chain.pem \ --certificate-arn $CERT_ARN >> /root/certgen-$DOMAINNAME.log else echo "Existing certificate for $DOMAINNAME was not found. Importing." >> /root/certgen-$DOMAINNAME.log aws --region $CERT_REGION acm import-certificate \ --certificate file:///root/certgen/config/live/$DOMAINNAME/cert.pem \ --private-key file:///root/certgen/config/live/$DOMAINNAME/privkey.pem \ --certificate-chain file:///root/certgen/config/live/$DOMAINNAME/chain.pem >> /root/certgen-$DOMAINNAME.log fi fi aws --region $CERT_REGION s3 cp /root/certgen-$DOMAINNAME.log s3://$CERTGENBUCKETNAME/certgen-$DOMAINNAME.log aws --region us-east-1 sns publish --topic-arn $SNSTOPICARN --subject "Status $DOMAINNAME" --message file:///root/certgen-$DOMAINNAME.log sleep 60 sudo shutdown -h now '''.format(region, domain_name, certgen_bucket, sns_topic_arn, import_acm) temp_instance_paramaters = { 'ImageId': current_ami, 'InstanceType': 't3.micro', 'Monitoring': {'Enabled':False}, 'IamInstanceProfile': {'Name':ec2_role_name}, 'InstanceInitiatedShutdownBehavior': 'terminate', 'CreditSpecification': {'CpuCredits':'standard'}, 'MinCount': 1, 'MaxCount': 1, 'UserData': user_data } #print(event) #print(ec2_role_name) print(domain_name) print(certgen_bucket) #print(user_data) #print(temp_instance_paramaters) try: cert_password = client_ssm.get_parameter(Name='/certgen/{}/p12password'.format(domain_name)) #print(cert_password) parameter_date = cert_password['Parameter']['LastModifiedDate'].astimezone() current_date = datetime.utcnow().astimezone() expiration_date = current_date - timedelta(days = days_old) if expiration_date < parameter_date: print('Certificate for {} does not need to be updated'.format(domain_name)) if 'FOURCE_RUN' in event: if event['FOURCE_RUN'] == 'Yes': print('Force run requested, generating certificate anyways.') else: return { 'statusCode': 200, 'body': 'Certificate for {} does not need to be updated'.format(domain_name) } else: return { 'statusCode': 200, 'body': 'Certificate for {} does not need to be updated'.format(domain_name) } print('Certificate is about to expire and needs to be updated') client_sns.publish( TopicArn=sns_topic_arn, Subject='Renewing {} certificate'.format(domain_name), Message=''' Certificate for {} will expire in {} days. Launching instance to renew certificate. '''.format(domain_name, 90 - days_old) ) except: print('Certificate for domain {} was not found'.format(domain_name)) client_sns.publish( TopicArn=sns_topic_arn, Subject='Creating {} certificate'.format(domain_name), Message=''' Certificate for {} was not found. Launching instance to create certificate. '''.format(domain_name) ) temp_instance = client_ec2.run_instances(**temp_instance_paramaters) #print(temp_instance) print('Launched instance {}'.format(temp_instance['Instances'][0]['InstanceId'])) return { 'statusCode': 200, 'body': 'Launched instance {}'.format(temp_instance['Instances'][0]['InstanceId']) }