There's quite a few options/roles in ansible-galaxy or github to generate a Let's Encrypt Certificate, before running a random role it's good practice to review the tasks to get an idea of what it's doing. Jeff Geerling is a very reputable source for ansible roles however his certbot role by default is for Internet facing systems, that is a server that can directly respond to a either a standalone challenge or apache/nginx with .well-known
setup correctly.
A DNS challenge allows Certbot to issue a cert from behind a firewall, like at home, without creating any DMZ or port-forwarding; after reviewing a few roles on offer to do this with ansible I realized it's actually quite straightforward!
To start with, use ansible-galaxy to install geerlingguy.certbot:
$ ansible-galaxy install geerlingguy.certbot
Now the plan here is to make your system / playbook require the role, but not issue the certificate as part of the role, instead issue the cert as a follow up task. Here's the initial playbook:
---
- hosts: internal_servers
become: true
#check_mode: yes
vars:
certbot_auto_renew: true
certbot_auto_renew_user: root
certbot_email: "[email protected]"
certbot_cloudflare_api_token: 'xxxxx'
roles:
- geerlingguy.certbot
tasks:
- name: Install Certbot Cloudflare
package:
name: python3-certbot-dns-cloudflare
state: present
- name: Create Certbot folder - /etc/letsencrypt
file:
path: /etc/letsencrypt
state: directory
owner: root
group: root
mode: 0700
- name: Certbot Template
template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
owner: root
group: root
mode: 0600
with_items:
- { src: 'dnscloudflare.ini.j2', dest: '/etc/letsencrypt/dnscloudflare.ini' }
- name: Certbot | Generate Certificate
command: certbot certonly --non-interactive --agree-tos --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/dnscloudflare.ini -m {{certbot_email}} -d {{ansible_host}}
args:
creates: /etc/letsencrypt/renewal/{{ansible_host}}.conf
What's gonna happen here is geerlingguy.certbot
will install the certbot package and setup the renewal cron task... and that's it. Once the role is done, it'll move into our tasks, so let's step thru those:
1. Install Certbot Cloudflare
Cloudflare support in Certbot is an optional add0on that you need to install. I'm running this on Redhat Enterprise Linux 8, for me the package for certbot-dns-cloudflare is called python3-certbot-dns-cloudflare
, so if you're running this on Ubuntu/Alpine etc you will need to change that.
2/3. Create Certbot folder & Template
The certbot-dns-cloudflare
plug-in needs credentials, since we haven't issued any certs the files & folders are not in place. So first ensure the folder is there and then you need a template file: dnscloudflare.ini.j2
# Cloudflare API credentials used by Certbot
dns_cloudflare_api_token = {{certbot_cloudflare_api_token}}
As above it's actually just one line, so probably could do it with line-in-file task but I like the template approach when the file is being created by me and I'm not just editing an existing file 😉
As you can see, the template simply writes your API Token in a format that the plug-in expects.
4. Generate Certificate
Finally we run the command manually in a way that calls the DNS challenge with all the information it needs. In my example, the certificate domain matches the ansible hostname but you can change that to what ever you need!
...Final thoughts & Renewal
As you can see, it was simple no need to install multiple roles/etc to achieve our goal. A quick point will be as we've issued the cert manually we might need to manage a renewal hook, on option is to drop a script in /etc/letsencrypt/renewal-hooks/post
that will be run each time certbot runs, the other is a task to put a line in the file...
- name: Check Certbot File - post_hook
lineinfile:
dest: /etc/letsencrypt/renewal/{{ansible_host}}.conf
line: "post_hook = /bin/systemctl restart nginx.service"
regexp: "^post_hook ="
Enjoy!