Skip to main content

AWS Certificate Manager: Create Managed TLS Certificate via AWS CLI, Test Certificate with LoadBalancer in AWS EKS Cluster

823 words·
AWS AWS CLI AWS Certificate Manager (ACM)
Table of Contents

Create Managed Certificate
#

Create Certificate
#

# Create a New Wildcard ACM Certificate
aws acm request-certificate \
  --domain-name "example-domain.jklug.work" \
  --validation-method DNS \
  --region eu-central-1

# Shell output:
{
    "CertificateArn": "arn:aws:acm:eu-central-1:012345678912:certificate/9012df67-5b6a-4f5e-a44b-74a40aa1dfac"
}

Verify Certificate
#

# List certificates
aws acm list-certificates --region eu-central-1

# Shell output
{
    "CertificateSummaryList": [
        {
            "CertificateArn": "arn:aws:acm:eu-central-1:012345678912:certificate/9012df67-5b6a-4f5e-a44b-74a40aa1dfac",
            "DomainName": "example-domain.jklug.work",
            "SubjectAlternativeNameSummaries": [
                "example-domain.jklug.work"
            ],
            "HasAdditionalSubjectAlternativeNames": false,
            "Status": "PENDING_VALIDATION",
            "Type": "AMAZON_ISSUED",
            "KeyAlgorithm": "RSA-2048",
            "KeyUsages": [],
            "ExtendedKeyUsages": [],
            "InUse": false,
            "RenewalEligibility": "INELIGIBLE",
            "CreatedAt": "2025-01-31T11:21:08.387000+00:00"
        }
    ]
}

List Certificate Details
#

# List certificate details: Define certificate ARN
aws acm describe-certificate \
  --certificate-arn arn:aws:acm:eu-central-1:012345678912:certificate/9012df67-5b6a-4f5e-a44b-74a40aa1dfac \
  --region eu-central-1

# Shell output:
{
    "Certificate": {
        "CertificateArn": "arn:aws:acm:eu-central-1:012345678912:certificate/9012df67-5b6a-4f5e-a44b-74a40aa1dfac",
        "DomainName": "example-domain.jklug.work",
        "SubjectAlternativeNames": [
            "example-domain.jklug.work"
        ],
        "DomainValidationOptions": [
            {
                "DomainName": "example-domain.jklug.work",
                "ValidationDomain": "example-domain.jklug.work",
                "ValidationStatus": "PENDING_VALIDATION",
                "ResourceRecord": {
                    "Name": "_aa49443c04657141f09802747021d3a6.example-domain.jklug.work.",
                    "Type": "CNAME",
                    "Value": "_d7cff2c2e58a9557a7250643b7838d0f.zfyfvmchrl.acm-validations.aws."
                },
                "ValidationMethod": "DNS"
            }
        ],
        "Subject": "CN=example-domain.jklug.work",
        "Issuer": "Amazon",
        "CreatedAt": "2025-01-31T11:21:08.387000+00:00",
        "Status": "PENDING_VALIDATION",
        "KeyAlgorithm": "RSA-2048",
        "SignatureAlgorithm": "SHA256WITHRSA",
        "InUseBy": [],
        "Type": "AMAZON_ISSUED",
        "KeyUsages": [],
        "ExtendedKeyUsages": [],
        "RenewalEligibility": "INELIGIBLE",
        "Options": {
            "CertificateTransparencyLoggingPreference": "ENABLED"
        }
    }
}

Take the values of the following section and create a CNAME DNS entry at Route 53:

        "DomainValidationOptions": [
            {
                "DomainName": "example-domain.jklug.work",
                "ValidationDomain": "example-domain.jklug.work",
                "ValidationStatus": "PENDING_VALIDATION",
                "ResourceRecord": {
                    "Name": "_aa49443c04657141f09802747021d3a6.example-domain.jklug.work.",
                    "Type": "CNAME",
                    "Value": "_d7cff2c2e58a9557a7250643b7838d0f.zfyfvmchrl.acm-validations.aws."
                },
                "ValidationMethod": "DNS"
            }
        ],

Route 53
#

List Hosted Zone ID
#

# Identify the hosted zone ID
aws route53 list-hosted-zones

# Shell output:
{
    "HostedZones": [
        {
            "Id": "/hostedzone/Z05838622LmyzoneID",
            "Name": "jklug.work.",
            "CallerReference": "9e7f187b-3129-424b-9cf6-c50141743c23",
            "Config": {
                "Comment": "",
                "PrivateZone": false
            },
            "ResourceRecordSetCount": 19
        }

Create DNS Entry / CNAME Record
#

# Create a CNAME DNS entry at Route 53: Define 
aws route53 change-resource-record-sets --hosted-zone-id Z05838622LmyzoneID --change-batch '{
    "Changes": [
        {
            "Action": "UPSERT",
            "ResourceRecordSet": {
                "Name": "_aa49443c04657141f09802747021d3a6.example-domain.jklug.work.",
                "Type": "CNAME",
                "TTL": 300,
                "ResourceRecords": [
                    {
                        "Value": "_d7cff2c2e58a9557a7250643b7838d0f.zfyfvmchrl.acm-validations.aws."
                    }
                ]
            }
        }
    ]
}'

# Shell output:
{
    "ChangeInfo": {
        "Id": "/change/C09097723HVQ9R4V6UQVH",
        "Status": "PENDING",
        "SubmittedAt": "2025-01-31T11:25:08.521000+00:00"
    }
}

Verify CNAME Entry with nslookup
#

# Verify the CNAME Entry
nslookup _aa49443c04657141f09802747021d3a6.example-domain.jklug.work

# Shell output:
Server:         127.0.0.53
Address:        127.0.0.53#53

Non-authoritative answer:
_aa49443c04657141f09802747021d3a6.example-domain.jklug.work     canonical name = _d7cff2c2e58a9557a7250643b7838d0f.zfyfvmchrl.acm-validations.aws.

Verify Certificate Status
#

Wait till the “ValidationStatus” changes to “SUCCESS”:

# List certificate details: Define certificate ARN
aws acm describe-certificate \
  --certificate-arn arn:aws:acm:eu-central-1:012345678912:certificate/9012df67-5b6a-4f5e-a44b-74a40aa1dfac \
  --region eu-central-1

# Shell output:
{
    "Certificate": {
        "CertificateArn": "arn:aws:acm:eu-central-1:012345678912:certificate/9012df67-5b6a-4f5e-a44b-74a40aa1dfac",
        "DomainName": "example-domain.jklug.work",
        "SubjectAlternativeNames": [
            "example-domain.jklug.work"
        ],
        "DomainValidationOptions": [
            {
                "DomainName": "example-domain.jklug.work",
                "ValidationDomain": "example-domain.jklug.work",
                "ValidationStatus": "SUCCESS",
                "ResourceRecord": {
                    "Name": "_aa49443c04657141f09802747021d3a6.example-domain.jklug.work.",
                    "Type": "CNAME",
                    "Value": "_d7cff2c2e58a9557a7250643b7838d0f.zfyfvmchrl.acm-validations.aws."
                },
                "ValidationMethod": "DNS"
            }
        ],
        "Serial": "04:32:8c:2c:46:97:97:48:49:16:99:8f:00:2e:4a:18",
        "Subject": "CN=example-domain.jklug.work",
        "Issuer": "Amazon",
        "CreatedAt": "2025-01-31T11:21:08.387000+00:00",
        "IssuedAt": "2025-01-31T11:25:36.724000+00:00",
        "Status": "ISSUED",
        "NotBefore": "2025-01-31T00:00:00+00:00",
        "NotAfter": "2026-03-01T23:59:59+00:00",
        "KeyAlgorithm": "RSA-2048",
        "SignatureAlgorithm": "SHA256WITHRSA",
        "InUseBy": [],
        "Type": "AMAZON_ISSUED",
        "KeyUsages": [
            {
                "Name": "DIGITAL_SIGNATURE"
            },
            {
                "Name": "KEY_ENCIPHERMENT"
            }
        ],
        "ExtendedKeyUsages": [
            {
                "Name": "TLS_WEB_SERVER_AUTHENTICATION",
                "OID": "1.3.6.1.5.5.7.3.1"
            },
            {
                "Name": "TLS_WEB_CLIENT_AUTHENTICATION",
                "OID": "1.3.6.1.5.5.7.3.2"
            }
        ],
        "RenewalEligibility": "INELIGIBLE",
        "Options": {
            "CertificateTransparencyLoggingPreference": "ENABLED"
        }
    }
}



Test Certificate (EKS Cluster)
#

Example Deployment & LoadBalancer
#

# Create a configuration for a deployment and LoadBalancer
vi example-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-container
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-container
  template:
    metadata:
      labels:
        app: my-container
    spec:
      containers:
      - name: container-2
        image: jueklu/container-2
        ports:
        - containerPort: 8080
---

apiVersion: v1
kind: Service
metadata:
  name: my-container-lb
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:eu-central-1:012345678912:certificate/9012df67-5b6a-4f5e-a44b-74a40aa1dfac
    service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https"
spec:
  selector:
    app: my-container
  ports:
  - name: http
    port: 80
    targetPort: 8080
  - name: https
    port: 443
    targetPort: 8080
  type: LoadBalancer
# Apply the deployment
kubectl apply -f example-deployment.yaml

List LoadBalancer External DNS Name
#

Note: The “EXTERNAL-IP” / DNS name of a load balancer service is listed as “” it’s meaning there is a problem with the configuration. It should only take a view second till the extern IP is shown.

# List load balancer service details: List DNS name
kubectl get svc my-container-lb

# Shell output:
NAME              TYPE           CLUSTER-IP      EXTERNAL-IP                                                                  PORT(S)                      AGE
my-container-lb   LoadBalancer   10.100.27.225   ad852fb1f900047608cbeb2b3ca5e44c-1305577960.eu-central-1.elb.amazonaws.com   80:32003/TCP,443:31468/TCP   27s

Create DNS Entry for LoadBalancer
#

# Create a DNS entry for the LoadBalancer endpoint
aws route53 change-resource-record-sets --hosted-zone-id Z05838622LmyzoneID --change-batch '{
    "Changes": [
        {
            "Action": "UPSERT",
            "ResourceRecordSet": {
                "Name": "example-domain.jklug.work",
                "Type": "CNAME",
                "TTL": 300,
                "ResourceRecords": [
                    {
                        "Value": "ad852fb1f900047608cbeb2b3ca5e44c-1305577960.eu-central-1.elb.amazonaws.com"
                    }
                ]
            }
        }
    ]
}'

# Shell output:
{
    "ChangeInfo": {
        "Id": "/change/C03029662M5AX3SXDU3XU",
        "Status": "PENDING",
        "SubmittedAt": "2025-01-31T11:31:58.098000+00:00"
    }
}

Curl the DNS Name
#

# Curl the DNS name
curl example-domain.jklug.work

# Shell output:
Container runs on: my-container-7df9db746-pmbxq



Cleanup
#

Delete Deployment
#

# Delete the deployment and LoadBalancer
kubectl delete -f example-deployment.yaml

Delete Route53 Entries
#

# Delete the LoadBalancer entry
aws route53 change-resource-record-sets --hosted-zone-id Z05838622LmyzoneID --change-batch '{
    "Changes": [
        {
            "Action": "DELETE",
            "ResourceRecordSet": {
                "Name": "example-domain.jklug.work",
                "Type": "CNAME",
                "TTL": 300,
                "ResourceRecords": [
                    {
                        "Value": "ad852fb1f900047608cbeb2b3ca5e44c-1305577960.eu-central-1.elb.amazonaws.com"
                    }
                ]
            }
        }
    ]
}'
# Delete the certificate entry
aws route53 change-resource-record-sets --hosted-zone-id Z05838622LmyzoneID --change-batch '{
    "Changes": [
        {
            "Action": "DELETE",
            "ResourceRecordSet": {
                "Name": "_aa49443c04657141f09802747021d3a6.example-domain.jklug.work.",
                "Type": "CNAME",
                "TTL": 300,
                "ResourceRecords": [
                    {
                        "Value": "_d7cff2c2e58a9557a7250643b7838d0f.zfyfvmchrl.acm-validations.aws."
                    }
                ]
            }
        }
    ]
}'

Delete the Certificate
#

# Delete the certificate
aws acm delete-certificate --certificate-arn arn:aws:acm:eu-central-1:012345678912:certificate/9012df67-5b6a-4f5e-a44b-74a40aa1dfac --region eu-central-1
# Verify the certificate was deleted
aws acm list-certificates --region eu-central-1

# Shell output:
{
    "CertificateSummaryList": []
}