Sectigo AWS Certificate Management solution

Overview

The Sectigo AWS Certificate Management tool (hereafter referred to as SectigoAWSCM) is an AWS serverless application created using Lambda function which provides a secure automation layer that enrolls Sectigo SSL/TLS certificates using ACME protocol, and imports the Sectigo certificates in AWS Certificate Manager (ACM).

Once the Sectigo certificates are available in ACM, they can be used to enable secure communication with AWS load balancers, CloudFront, or any other Amazon Web Services over HTTPS. This tool supports automatic certificate renewal before the certificate expiry date and upon revocation.

The SectigoAWSCM tool manages only the certificate enrollment with Sectigo backend ACME servers, and uploads to the ACM. Integration between the ACM and listeners of the web services is not in the scope of this tool.

Scope

This document covers topics relevant to the Sectigo ACME servers and ACM only. Configuration of listeners for SSL/TLS enablement is out of scope of this document.

Assumptions

It is assumed that customers will create the listeners for the load balancers and attach the certificates to the listeners. Example scripts for Terraform and Ansible are provided for customer reference.

Audience

This document is intended for IT administrators and system administrators who manage AWS Web Services for an organization and are also familiar with Sectigo Certificate Manager (SCM).

Architecture

Sectigo AWSCM architecture

The SectigoAWSCM tool execution is controlled using the AWS resource policies. Only the specified principals (typically IAM user or role) can invoke it.

Users of this tool are responsible for configuring the "SectigoAWSCM", with information about each ACME account that will be used. Each account is identified by an alias. Some configuration information, such as EAB parameters, can be collected from SCM. We will explain in detail the configuration parameters. When enrolling certificates using SectigoAWSCM, users control which ACME account settings to use for enrollment, by passing the "ACME account alias" as a parameter.

Since SectigoAWSCM is using Certbot as the ACME client, the current iteration of the product only allows EAB information corresponding to one ACME account to be used for each ACME endpoint. You can define multiple aliases for the SAME acme account, to control other parameters, such as the key size.

Since certificates in AWS Certificate Manager are regional resources, SectigoAWSCM is region specific, at least in the current iteration of the product. When you have multiple AWS regions, a separate script should be run for each region.

SectigoAWSCM supports only the pre-validated domains in Sectigo Certificate Manager (SCM). The domains for which certificates are requested should go through Domain Control Validation (DCV) in SCM as per the instructions provided in Section 6.4.2 How To Validate Domains of the SCM administrator’s guide. For more information on DCV methods, see Domain Control Validation Methods in Sectigo’s Knowledge Base.

Deploying SectigoAWSCM

Deploying the Lambda function to AWS with Terraform CLI

The following diagram illustrates the steps to deploy the "SectigoAWSCM" (Sectigo AWS Certificate Management) Lambda function to AWS with Terraform CLI.

Deploying SectigoAWSCM Lambda function
Nested module structure is preferred, one module per resource.

Module list

  • s3: Used for securely storing the acme-account.yaml file

  • iam: IAM roles and policies for interaction between other resources

  • lambda: Lambda function that provides core actions - enroll/renew

  • api-gateway: Used to allow Lambda function to be invoked via REST API

  • dynamodb: To keep track of Lambda requests instances

AWS resources used

Resource Name Description

aws_iam_policy.sectigoCM-lambda-policy

IAM policy for access to S3 bucket from Lambda

aws_cloudwatch_log_group.sectigoAWSCM-lambda-lg

Creates CloudWatch log group for Lambda

aws_iam_policy.lambda_logging

IAM policy resource for creating and accessing Cloudwatch logs for Lambda

aws_api_gateway_api_key.SectigoAWSCM-AG-api-key

Creates API key (x-api-key) for API Gateway

aws_s3_bucket.sectigoBucket

S3 bucket for acme_accounts.yaml

aws_iam_role.sectigoCM-lambda-role

IAM role for the Lambda function

aws_iam_role_policy_attachment.sectigocm_policy_attach_to_sectigo_role

Attaches S3 Lambda policy to Lambda role

aws_iam_role_policy_attachment.lambda_logs

Attaches Lambda log policy to Lambda role

aws_lambda_function.sectigoSCM-main-python

Creates Lambda function

aws_api_gateway_rest_api.SectigoAWSCM_ag

Creates REST API Gateway for Lambda

aws_lambda_permission.ag-invoke-lambda

Permission for invoking Lambda via API Gateway

aws_api_gateway_deployment.SectigoAWSCM_ag_deployment

Deploys swagger.json to API Gateway

aws_api_gateway_stage.rest_api_stage

Creates API stage

aws_api_gateway_usage_plan.sectigoawscm-usageplan

Usage plan for API Gateway

aws_api_gateway_usage_plan_key.main

Binds API key (x-api-key) with usage plan

aws_s3_bucket_public_access_block.sectigoBucketRestrictions

S3 bucket public access restrictions

aws_dynamodb_table.basic-dynamodb-table

DynamoDB table for storing requests

IAM policies list and description

Service Action Resource

ACM

Add tags to certificate

All certificates

ACM

List tags for certificate

All certificates

ACM

Describe certificate

All certificates

ACM

Get certificate

All certificates

ACM

List certificates

All certificates

ACM

Import certificate

All certificates

S3

Get object

The bucket for account

S3

Get object version

The bucket for account

Lambda

Invoke function

Function itself

Lambda

Invoke function

API Gateway

DynamoDB

All actions

SectigoAWSCM table

Logs

Create log group

arn:aws:logs:*:*:*

Logs

Create log stream

arn:aws:logs:*:*:*

Logs

Put log events

arn:aws:logs:*:*:*

Installation script

Installation is provided as a bash(sh) script. The script supports multi-region installation of SectigoAWSCM. The number of regions is not limited. The script also creates the backend configuration.

The installation script does the following during execution:

  • Checks the AWS credentials

  • Configures one S3 bucket for the Terraform backend configuration and another S3 bucket for the acme_accounts.yaml file

  • Configures the workspace

  • Executes Terraform commands

The script will create the following items on AWS for the certificate management to work seamlessly:

  • Enable policies for the IAM role

  • Install configuration file(s) in the S3 bucket

  • Install the AWS Lambda function and API Gateway module

  • Install the AWS Cloudwatch module for logging and auto-renewal

  • Install API Gateway as an additional option for invoking Lambda

An S3 bucket will be created for the backend configuration of Terraform. When you run terraform init for the first time, Terraform will create the first state file in the bucket. For every subsequent action (apply, change, destroy), these state files will be updated. Terraform needs access to that bucket for proper operation. If the .terraform.lock.hcl file is removed accidentally, run terraform init again.

Prerequisites

The following requirements must be met before running the script:

  • AWS account: An active AWS account with the Required permissions on AWS resources.

  • Client machine: A client machine to install the Terraform script and AWS CLI

  • ACME account: An ACME account from SCM (the ACME URL and EAB values)

  • Terraform script: If you encounter any problems during the script installation, please contact Sectigo Support or the Onboarding Team.

  • Terraform v1.0.8+: The SectigoAWS solution is deployed using the Terraform CLI commands. For Terraform installation instructions, see Install Terraform.

  • AWS CLI v1+: AWS CLI is used to invoke the Lambda function. AWS CLI commands are also used in the install.sh and destroy.sh scripts.

    All examples of the Lambda function invocation in this document use AWS CLI v1. However, you can also invoke the function with AWS CLI v2 by adding --cli-binary-format raw-in-base64-out to the command.

    aws lambda invoke --function-name SectigoAWSCM \
    --payload '{"domains": "ccmqa.com", "account": "demo2", "action": "enroll"}' \
    --cli-binary-format raw-in-base64-out response.json

    The acme-account.yaml file is uploaded to the S3 bucket - this file contains EAB information for the ACME accounts, which is sensitive data and must be protected. Use the AWS CLI commands to edit or redeploy the file. You can also work with the file using the AWS console.

  • AWS programmatic access: After installing AWS CLI you need to configure AWS programmatic access variables to connect to your AWS account:

    1. Obtain access credentials for your user in the AWS Console:

      1. Navigate to the IAM section.

      2. Select Users in the left menu.

      3. Select the user and navigate to the Security credentials tab.

        AWS security credentials
      4. Click Create access key and copy Access key ID and Secret access key (you can also download a .csv file with the values).

        AWS access keys
    2. Run aws configure and specify your access keys and default region name (you can change the default region at any time).

      aws configure
      AWS Access Key ID [None]: "Your access key ID"
      AWS Secret Access Key [None]: "Your secret access key"
      Default region name [None]: "Your preferred region name"
      Default output format [None]: json

Pre-installation

The following steps must be met before running the script:

  1. Extract the contents of the SectigoAWSCM to the current path.

  2. Navigate to the ./sectigo_awscm_iac directory.

  3. Give the execute permission to the install.sh file using the chmod +x install.sh command.

S3 bucket for the Terraform backend configuration will be created via install.sh in the region specified in the AWS_DEFAULT_REGION variable. For example, if your default region is ca-central-1, your bucket will be created in this region. The backend bucket will not be changed (created) again for the same region.

Installation

Execute the install.sh file in your shell to run the installation script. You can install the script for a default or specific region, including multiple regions.

  • Default region

  • Specific region

Run ./install.sh (if you have not provided the execute permission to the script, run bash install.sh). The script will install SectigoAWSCM to your default AWS region specified in AWS_DEFAULT_REGION. The script does the following:

  • Creates a backend S3 bucket for the default region and initializes state files in this bucket.

  • Creates an S3 bucket for the accounts in the default region.

  • Creates the Terraform workspace as the AWS region.

  • Executes terraform plan && terraform apply.

Run ./install.sh <region-name>. For example, to install SectigoAWSCM to the us-east-1 region, you would run ./install.sh us-east-1. For muliple regions installation, repeat this step for each region. The script does the following:

  • Creates a backend S3 bucket for a specific region and initializes state files in this bucket.

  • Creates an S3 bucket for the accounts in a specific region.

  • Creates the Terraform workspace as a specific AWS region.

  • Executes terraform plan && terraform apply.

Post-installation

After the script has executed, AWS resources will be created with a specific naming convention to allow for multi-region installation. The naming convention is resource_name-RegionName. The script appends the region-name to all resource names. For example, if you install SectigoAWSCM in the eu-central-1 region, then the AWS resources will be given the following names:

  • Lambda function: SectigoAWSCM-eu-central-1

  • S3 Bucket: sectigo-awscm-eu-central-1-$date (AWS S3 bucket’s name must be unique, therefore a timesptamp is appended)

  • API Gateway: sectigoAWSCM-ag-eu-central-1

After installing SectigoAWSCM, the names of all important resources will be printed into your console (Lambda function name, API Gateway URL, S3 bucket name, and more). Make sure to write the Lambda function name correctly when you invoke the function for your region.

aws lambda invoke --function-name SectigoAWSCM-eu-central-1 \
--payload '{"domains": "<your domain>", "account": "<account name>", "action": "enroll"}' \
response.json

The logs will be stored in the install-<region-name>.log file.

Uninstallation

To uninstall SectigoAWSCM, you can either run terraform destroy manually or have the resources destroyed automatically by our destroy.sh script. We recommend using the script, which automatically deletes all AWS components that are used for SectigoAWSCM.

The script also supports destroying resources in a specific region (other than the default one) or multiple regions. For example, if you have two separate SectigoAWSCM in different AWS regions and you want to uninstall only one, run the script with the region name as an argument.

Make sure to give the execute permission to the destroy.sh file using the chmod +x destroy.sh command.

./destroy.sh                   # destroy with the default region
./destroy.sh <region name>     # destoy with a specific region

The logs will be stored in the terraform-destroy.txt file.

Using SectigoAWSCM

Locating the ACME account information and EAB keys

After creating an ACME account in SCM, you can register an ACME agent of your choice with the Sectigo ACME server. You will need the External Account binding (EAB) information--Key ID and HMAC Key, for agent configuration.

To obtain these values:

  1. Log in to SCM at https://cert-manager.com/customer/<customer_uri> with your credentials.

  2. Expand the left pane and navigate to Enrollment  ACME.

  3. On the ACME page, select your ACME URL, and click Accounts.

  4. Select your account and click Details.

EAB details

Configuring the acme_accounts.yaml file

Make sure to validate your YAML file using a YAML validator—​for example, Code Beautify. If you are getting a 200 status code response, but the tool is not performing the requested operation, it is likely that there’s a YAML syntax error in your file.

The acme_accounts.yaml file contains the following fields.

Field Description

a_30_days

An alias for your ACME account. This value will identify the ACME account when invoking SectigoAWSCM.

acme-endpoint

The ACME URL

eab-hmac-key

The HMAC key

eab-key

The key ID

email

Your email, which will be used when enrolling certificates via ACME

RenewBeforeDays

If certificateExpirationDate-RenewBeforeDays >= currentDate, then the certificate will be renewed during the script execution with the renewal action

KeyType

The key type which will be used for certificate enrollment. Can be RSA or ECDSA.

KeySize

The key size which will be used for certificate enrollment.

Using SectigoAWSCM with AWS CLI

The following are example commands for enrolling and renewing certificates using the AWS CLI.

All examples of the Lambda function invocation in this document use AWS CLI v1. However, you can also invoke the function with AWS CLI v2 by adding --cli-binary-format raw-in-base64-out to the command.

aws lambda invoke --function-name SectigoAWSCM \
--payload '{"domains": "ccmqa.com", "account": "demo2", "action": "enroll"}' \
--cli-binary-format raw-in-base64-out response.json
If an attempt is made to enroll a certificate for a domain that already has a certificate, the ARN of the existing certificate will be returned.

Enroll a certificate for a domain

aws lambda invoke --function-name SectigoAWSCM-eu-central-1 \
--payload '{"domains": "ccmqa.com", "account": "demo2", "action": "enroll"}' \
response.json

Enroll multi-domain (SAN) and wildcard certificates

aws lambda invoke --function-name SectigoAWSCM-eu-central-1 \
--payload '{"domains": "*.ccmqa.com,d2.ccmqa.com,d3.ccmqa.com", \
"account": "demo2", "action": "enroll"}' \
response.json

Renew revoked certificates for a specific alias

aws lambda invoke --function-name SectigoAWSCM-eu-central-1 \
--payload '{"account": "d365_tuesday", "action": "renew"}' \
response.json

Renew all revoked certificates (for all aliases)

aws lambda invoke --function-name SectigoAWSCM-eu-central-1 \
--payload '{"action": "renew"}' \
response.json
Issued certificates in AWS CM
Certificate status in AWS CM

Using SectigoAWSCM with API Gateway

The following are example commands for enrolling and renewing certificates using the Amazon API Gateway (scroll right to view the full command).

If you need to renew more than 30 certificates, please use SectigoAWSCM with AWS CLI.

Enroll a certificate for a domain

curl --location --request GET \
'https://kxiinwdhxk.execute-api.ca-central-1.amazonaws.com/SectigoAWSCM/queries?domains=d1_124.ccmqa.com&account=demo2&action=enroll' \
--header 'x-api-key: GKIbu7ukcY4gRdahVK70x2lP4ANKMubJ2WBNHISG'

Enroll multi-domain (SAN) and wildcard certificates

curl --location --request GET \
'https://kxiinwdhxk.execute-api.ca-central-1.amazonaws.com/SectigoAWSCM/queries?domains=d1.ccmqa.com,d2.ccmqa.com&account=demo2&action=enroll' \
--header 'x-api-key: GKIbu7ukcY4gRdahVK70x2lP4ANKMubJ2WBNHISG'

Renew revoked certificates for a specific alias

curl --location --request GET \
'https://kxiinwdhxk.execute-api.ca-central-1.amazonaws.com/SectigoAWSCM/queries?account=demo2&action=renew' \
--header 'x-api-key: GKIbu7ukcY4gRdahVK70x2lP4ANKMubJ2WBNHISG'

Renew all revoked certificates (for all aliases)

curl --location --request GET \
'https://kxiinwdhxk.execute-api.ca-central-1.amazonaws.com/SectigoAWSCM/queries?action=renew' \
--header 'x-api-key: GKIbu7ukcY4gRdahVK70x2lP4ANKMubJ2WBNHISG'

Notes

  • Duplicate certificates for a specific domain are not allowed for a specific ACME account alias.

  • Multiple aliases can be defined for the same ACME account.

  • If you need different key sizes, you can create two aliases with the needed key sizes.

  • Make sure the signature algorithm of the server certificate matches the CA certificate. For example, the RSA server certificate should have the RSA CA chain, and the ECDSA server certificate should have the ECDSA CA chain. Please contact Sectigo support for more details.

Enabling logging and auto-renewal using AWS CloudWatch

The Amazon Cloudwatch service allows you to create a cronjob that will invoke a Lambda function on a schedule. You can integrate CloudWatch with SectigoAWSCM to trigger a Lambda function that will check the certificate expiry status on a schedule. For example, if you want to create a Cloudwatch event that will fire on the first day of every month, perform the following steps:

  1. Sign in to the AWS EventBridge service.

  2. Create a new rule.

  3. Give a name to your rule, and specify a pattern and cron expression.

    Create rule in CloudWatch
  4. Select the Lambda function.

  5. Specify the action.

  6. Click Create to confirm the rule.

Create Lambda function

Cloudwatch log groups were created and integrated with Lambda when you executed the installation script. After the Lambda function is invoked, you can check the logs in AWS Cloudwatch. The following is an example of invoking a Lambda function and logging this event.

Invoke Lambda function
Lambda function response

Visit the AWS CloudWatch console to view the logs.

AWS CloudWatch console
AWS CloudWatch console output

Required permissions on AWS resources

The installation script requires the following permissions on AWS resources.

API Gateway, DynamoDB, Lambda, S3

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "apigateway:*",
                "lambda:*"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "dynamodb:DescribeContributorInsights",
                "dynamodb:RestoreTableToPointInTime",
                "dynamodb:UpdateGlobalTable",
                "dynamodb:DeleteTable",
                "dynamodb:UpdateTableReplicaAutoScaling",
                "dynamodb:DescribeTable",
                "dynamodb:PartiQLInsert",
                "dynamodb:GetItem",
                "dynamodb:DescribeContinuousBackups",
                "dynamodb:DescribeExport",
                "dynamodb:EnableKinesisStreamingDestination",
                "dynamodb:BatchGetItem",
                "dynamodb:DisableKinesisStreamingDestination",
                "dynamodb:UpdateTimeToLive",
                "dynamodb:BatchWriteItem",
                "dynamodb:PutItem",
                "dynamodb:PartiQLUpdate",
                "dynamodb:Scan",
                "dynamodb:StartAwsBackupJob",
                "dynamodb:UpdateItem",
                "dynamodb:UpdateGlobalTableSettings",
                "dynamodb:CreateTable",
                "dynamodb:RestoreTableFromAwsBackup",
                "dynamodb:GetShardIterator",
                "dynamodb:ExportTableToPointInTime",
                "dynamodb:DescribeBackup",
                "dynamodb:UpdateTable",
                "dynamodb:GetRecords",
                "dynamodb:DescribeTableReplicaAutoScaling",
                "dynamodb:DeleteItem",
                "dynamodb:CreateTableReplica",
                "dynamodb:ListTagsOfResource",
                "dynamodb:UpdateContributorInsights",
                "dynamodb:CreateBackup",
                "dynamodb:UpdateContinuousBackups",
                "dynamodb:TagResource",
                "dynamodb:PartiQLSelect",
                "dynamodb:CreateGlobalTable",
                "dynamodb:DescribeKinesisStreamingDestination",
                "dynamodb:UntagResource",
                "dynamodb:ConditionCheckItem",
                "dynamodb:Query",
                "dynamodb:DescribeStream",
                "dynamodb:DeleteTableReplica",
                "dynamodb:DescribeTimeToLive",
                "dynamodb:DescribeGlobalTableSettings",
                "dynamodb:DescribeGlobalTable",
                "dynamodb:RestoreTableFromBackup",
                "dynamodb:DeleteBackup",
                "dynamodb:PartiQLDelete"
            ],
            "Resource": "arn:aws:dynamodb:*:<account_id>:table/*"
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::*",
                "arn:aws:s3:::*/*"
            ]
        }
    ]
}

IAM, CloudWatch

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "logs:ListTagsLogGroup",
                "logs:TagLogGroup",
                "logs:DescribeLogGroups",
                "logs:UntagLogGroup",
                "logs:DeleteLogGroup",
                "logs:PutRetentionPolicy",
                "logs:CreateLogGroup"
            ],
            "Resource": "arn:aws:logs:*:<account_id>:log-group:*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "iam:*",
            "Resource": [
                "arn:aws:iam::<account_id>:role/*",
                "arn:aws:iam::<account_id>:policy/*"
            ]
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeRegions"
            ],
            "Resource": "*"
        }
    ]
}