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.
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: "./certificatestls.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 and domain in dns_challenge.challenge_data and 'dns-01' in dns_challenge.challenge_data[domain]
- 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 and domain in dns_challenge.challenge_data and 'dns-01' in dns_challenge.challenge_data[domain]
- 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. The challenge data is stored in the dns_challenge variable.
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.The record name is necessary to specify the DNS-01 challenge, while the value obtained is crucial for updating the DNS record itself.
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. After pause for another five minutes.
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. This step ensures that the certificate is issued and stored correctly.
To automate the update process for SSL/TLS certificates on multiple remote hosts. It consists of two main tasks. The first task involves copying the fullchain.crt file from the local directory ./certificates/
to the destination /etc/domain/ssl/fullchain.crt
on each remote host. The Second task involves copying the tls.key file from the local directory ./certificates/
to the destination /etc/domain/ssl/tls.key
on each remote host. During this process, the file permissions (mode) are set to 0644, ensuring that the certificate file is readable by hosts.
Atlast the SSL/TLS certificate update process and verify its completion across all hosts, you can use a post-task approach in your Ansible playbook. After copying the certificate files, 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.
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.