← Back to Engineering

Automate TLS/SSL Certificate Renewal Seamlessly with Ansible

2024-07-095 min readSelvaraj Kuppusamy

Automate TLS/SSL Certificate Renewal Seamlessly with Ansible

Automate SSL/TLS Certificate Management with Ansible:

Our organization manually renews SSL/TLS certificates for multiple hosts on a monthly or yearly basis, which sometimes causes downtime and errors in updating certificates on host machines, posing a risk of installing incorrect TLS/SSL certificates. Recognizing these challenges, we have opted to automate the renewal process.

TLS/SSL Certificate Preparation:

In an Ansible playbook focused on certificate preparation, specific tasks are orchestrated to generate essential components for secure communication, including private keys and complete chain certificates.

---
- name: TLS/SSL Automation
  hosts: localhost
  tasks:
    - name: Generate an acme_account_key
      community.crypto.openssl_privatekey:
        path: "./certificates/acme_account_key"

    - name: Generate an OpenSSL private key if necessary
      community.crypto.openssl_privatekey:
        path: "./certificates/tls.key"

    - name: Generate an OpenSSL Certificate Signing Request if necessary
      community.crypto.openssl_csr:
        path: "./certificates/tls.csr"
        privatekey_path: "./certificates/tls.key"
        country_name: "IN"
        organization_name: "Organization name"
        email_address: "admin@domain.com"
        common_name: "domain.com"

    - name: Create a challenge for the domain
      community.crypto.acme_certificate:
        account_key_src: "./certificates/acme_account_key"
        csr: "./certificates/tls.csr"
        dest: "./certificates/tls.crt"
        challenge: "dns-01"
        acme_version: "2"
        acme_directory: "https://acme-v02.api.letsencrypt.org/directory"
        terms_agreed: true
        force: true
      register: dns_challenge

    - name: Get TXT record for the challenge
      ansible.builtin.set_fact:
        record: "{{ dns_challenge.challenge_data[domain]['dns-01'].record }}"
      when: dns_challenge.challenge_data is defined

    - name: Get value of the TXT record
      ansible.builtin.set_fact:
        record_value: "{{ dns_challenge.challenge_data[domain]['dns-01'].resource_value }}"
      when: dns_challenge.challenge_data is defined

    - name: Update the AWS route53 DNS TXT record
      amazon.aws.route53:
        command: present
        zone: "domain.com"
        record: "{{ record }}"
        type: TXT
        ttl: 300
        value: '"{{ record_value }}"'
        overwrite: true

    - name: Pause for five minutes
      ansible.builtin.pause:
        minutes: 5

    - name: Validate challenge
      community.crypto.acme_certificate:
        account_key_src: "./certificates/acme_account_key"
        account_email: "admin@domain.com"
        csr: "./certificates/tls.csr"
        dest: "./certificates/tls.crt"
        chain_dest: "./certificates/intermediate.crt"
        fullchain_dest: "./certificates/fullchain.crt"
        challenge: "dns-01"
        acme_directory: "https://acme-v02.api.letsencrypt.org/directory"
        remaining_days: 15
        acme_version: 2
        force: true
      when: dns_challenge is changed
  • Generate ACME Account Key: The community.crypto.openssl_privatekey module is used to create a private key for the ACME account. This key is crucial for the ACME protocol to issue and manage SSL certificates.
  • Generate Private Key: The community.crypto.openssl_privatekey module creates an OpenSSL private key. This key is used later to generate the CSR and certificate.
  • Generate CSR: The community.crypto.openssl_csr module creates a Certificate Signing Request (CSR) using the private key. The CSR includes information such as country name, organization name, email address, and the common name (domain). This CSR is required by the CA (Let's Encrypt) to issue the certificate.
  • Create ACME Challenge: The community.crypto.acme_certificate module initiates a DNS-01 challenge with Let's Encrypt to prove domain ownership. It registers the challenge data, which will be used to update the DNS records.
  • Retrieve DNS TXT Record Name and Value: Using ansible.builtin.set_fact, the playbook retrieves both the DNS TXT record name and its corresponding value from the dns_challenge data.
  • Update DNS Record: The amazon.aws.route53 module updates the AWS Route53 DNS TXT record with the challenge information. This step is critical for completing the DNS-01 challenge by proving ownership of the domain to Let's Encrypt.
  • Validate Challenge: The community.crypto.acme_certificate module validates the DNS challenge with Let's Encrypt. Once the challenge is validated, it retrieves the SSL certificate, saving it along with the intermediate and full chain certificates in the specified directory.

TLS/SSL Certificate Updation:

To automate the update process for SSL/TLS certificates on multiple remote hosts, the playbook copies the fullchain.crt file from the local directory ./certificates/ to the destination /etc/domain/ssl/fullchain.crt on each remote host. Similarly, it copies the tls.key file. During this process, the file permissions (mode) are set to 0644, ensuring that the certificate file is readable by hosts.

After the SSL/TLS certificate update process, you can implement a post-task that checks the existence or validity of the updated certificates on each host. This ensures that the update was successful across all targeted machines.

Conclusion:

By automating the SSL/TLS certificate updates with this Ansible playbook, you reduce the risk of downtime and errors, ensuring that your systems remain secure and up-to-date. Whether integrated into your CI/CD pipeline, executed manually, or scheduled via cron job, this approach streamlines the certificate management process, providing a reliable solution for maintaining your infrastructure.