Automating the management of Let's Encrypt SSL certificates using DNS-01 challenge and AWS

The post outlines the steps for automating the management of SSL certificates from Let’s Encrypt CA using DNS-01 challenge and AWS.

acme-dns-route53 — is a tool that will allow us to implement this feature. It works with SSL certificates from Let’s Encrypt, saves them in Amazon Certificate Manager, uses Route53 API to perform the DNS-01 challenge, and finally, pushes notifications to SNS. Additionally, acme-dns-route53 it has built-in functionality for use within AWS Lambda, which is exactly what we need.

This article is divided into 4 sections:

  • creating a zip file;
  • creating an IAM role;
  • creating a Lambda function that runs acme-dns-route53;
  • creating a CloudWatch timer that triggers the function twice a day;

Note: before starting, you need to install GoLang 1.9+ and Create a serverless function through the web interface or using the utility.

Creating a zip file

acme-dns-route53 is written in GoLang and supports version 1.9 or higher.

We need to create a zip file containing the binary acme-dns-route53 inside. For this, you need to install acme-dns-route53 from the GitHub repository using the command go install:

$ env GOOS=linux GOARCH=amd64 go install github.com/begmaroman/acme-dns-route53

The binary is installed in $GOPATH/bin directory. Note that during installation we set two environment variables: GOOS=linux and GOARCH=amd64. They inform the Go compiler that a binary suitable for Linux OS and amd64 architecture should be created — this is what runs in AWS.
AWS requires deploying our program in a zip file, so let's create acme-dns-route53.zip an archive that will contain the recently installed binary:

$ zip -j ~/acme-dns-route53.zip $GOPATH/bin/acme-dns-route53

Note: the binary should be at the root of the zip archive. For this, we use -j the flag.

Now our zip file is ready for deployment, we just need to create a role with the necessary permissions.

Creating an IAM Role

We need to set up an IAM role with the permissions required by our Lambda during its execution.
Let's name this policy lambda-acme-dns-route53-executor and immediately give it the basic role AWSLambdaBasicExecutionRole. This will allow our Lambda to run and write logs to the AWS CloudWatch service.
First, we create a JSON file that describes our permissions. This essentially allows the Lambda services to use the role. lambda-acme-dns-route53-executor:

$ touch ~/lambda-acme-dns-route53-executor-policy.json

The contents of our file are as follows:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup"
            ],
            "Resource": "arn:aws:logs:::*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:PutLogEvents",
                "logs:CreateLogStream"
            ],
            "Resource": "arn:aws:logs:::log-group:/aws/lambda/acme-dns-route53:*"
        },
        {
            "Sid": "",
            "Effect": "Allow",
            "Action": [
                "route53:ListHostedZones",
                "cloudwatch:PutMetricData",
                "acm:ImportCertificate",
                "acm:ListCertificates"
            ],
            "Resource": "*"
        },
        {
            "Sid": "",
            "Effect": "Allow",
            "Action": [
                "sns:Publish",
                "route53:GetChange",
                "route53:ChangeResourceRecordSets",
                "acm:ImportCertificate",
                "acm:DescribeCertificate"
            ],
            "Resource": [
                "arn:aws:sns:${var.region}::",
                "arn:aws:route53:::hostedzone/*",
                "arn:aws:route53:::change/*",
                "arn:aws:acm:::certificate/*"
            ]
        }
    ]
}

Now let's execute the command aws iam create-role to create the role:

$ aws iam create-role --role-name lambda-acme-dns-route53-executor 
 --assume-role-policy-document ~/lambda-acme-dns-route53-executor-policy.json

Note: remember the policy ARN (Amazon Resource Name) — we will need it in the next steps.

The role lambda-acme-dns-route53-executor has been created, now we need to specify permissions for it. The easiest way to do this is to use the command aws iam attach-role-policy, passing the policy ARN AWSLambdaBasicExecutionRole as follows:

$ aws iam attach-role-policy --role-name lambda-acme-dns-route53-executor 
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

Note: a list of the remaining policies can be found here.

Creating a Lambda function that runs acme-dns-route53

Hooray! Now we can deploy our function on AWS using the command aws lambda create-function. The Lambda should be configured using the following environment variables:

  • AWS_LAMBDA — indicates acme-dns-route53 that the execution takes place within AWS Lambda.
  • DOMAINS — a list of domains separated by commas.
  • LETSENCRYPT_EMAIL — contains Let’s Encrypt Email.
  • NOTIFICATION_TOPIC — the name of the SNS Notification Topic (optional).
  • STAGING — when set to 1 uses the staging environment.
  • 1024 MB — memory limit, can be changed.
  • 900 secs (15 min) — timeout.
  • acme-dns-route53 — the name of our binary that sits in the archive.
  • fileb://~/acme-dns-route53.zip — the path to the archive we created.

Now let's deploy:

$ aws lambda create-function 
 --function-name acme-dns-route53 
 --runtime go1.x 
 --role arn:aws:iam:::role/lambda-acme-dns-route53-executor 
 --environment Variables="{AWS_LAMBDA=1,DOMAINS="example1.com,example2.com",LETSENCRYPT_EMAIL=begmaroman@gmail.com,STAGING=0,NOTIFICATION_TOPIC=acme-dns-route53-obtained}" 
 --memory-size 1024 
 --timeout 900 
 --handler acme-dns-route53 
 --zip-file fileb://~/acme-dns-route53.zip

 {
     "FunctionName": "acme-dns-route53", 
     "LastModified": "2019-05-03T19:07:09.325+0000", 
     "RevisionId": "e3fadec9-2180-4bff-bb9a-999b1b71a558", 
     "MemorySize": 1024, 
     "Environment": {
         "Variables": {
            "DOMAINS": "example1.com,example2.com", 
            "STAGING": "1", 
            "LETSENCRYPT_EMAIL": "your@email.com", 
            "NOTIFICATION_TOPIC": "acme-dns-route53-obtained", 
            "AWS_LAMBDA": "1"
         }
     }, 
     "Version": "$LATEST", 
     "Role": "arn:aws:iam:::role/lambda-acme-dns-route53-executor", 
     "Timeout": 900, 
     "Runtime": "go1.x", 
     "TracingConfig": {
         "Mode": "PassThrough"
     }, 
     "CodeSha256": "+2KgE5mh5LGaOsni36pdmPP9O35wgZ6TbddspyaIXXw=", 
     "Description": "", 
     "CodeSize": 8456317,
"FunctionArn": "arn:aws:lambda:us-east-1::function:acme-dns-route53", 
     "Handler": "acme-dns-route53"
 }

Creating a CloudWatch timer that triggers the function twice a day

The last step is to set up a cron that calls our function twice a day:

  • create a CloudWatch rule with the value schedule_expression.
  • create a target for the rule (what needs to be executed) by specifying the ARN of the Lambda function.
  • grant permission to the rule to invoke the Lambda function.

Below, I have attached my Terraform config, but it's actually very simple to do this using the AWS console or AWS CLI.

# Cloudwatch event rule that runs acme-dns-route53 lambda every 12 hours
resource "aws_cloudwatch_event_rule" "acme_dns_route53_sheduler" {
  name                = "acme-dns-route53-issuer-scheduler"
  schedule_expression = "cron(0 */12 * * ? *)"
}

# Specify the lambda function to run
resource "aws_cloudwatch_event_target" "acme_dns_route53_sheduler_target" {
  rule = "${aws_cloudwatch_event_rule.acme_dns_route53_sheduler.name}"
  arn  = "${aws_lambda_function.acme_dns_route53.arn}"
}

# Give CloudWatch permission to invoke the function
resource "aws_lambda_permission" "permission" {
  action        = "lambda:InvokeFunction"
  function_name = "${aws_lambda_function.acme_dns_route53.function_name}"
  principal     = "events.amazonaws.com"
  source_arn    = "${aws_cloudwatch_event_rule.acme_dns_route53_sheduler.arn}"
}

Now you have automatic creation and renewal of SSL certificates configured as well

Source: habr.com

Buy reliable website hosting with DDoS protection, VPS VDS servers 🔥 Buy reliable website hosting with DDoS protection, VPS VDS servers | ProHoster