An Ansible role that allows one to automate Let's Encrypt certificate issuance and update using [gandi](https://gandi.net)'s LiveDNS service.
Go to file
Patryk Cisek e6880ec2ed
Corrected the name of the file in cron.daily -- got rid of forbidden dots.
2023-01-25 07:53:52 -08:00
meta Added GitLab Pipeline, added meta info to meta/main.yaml. 2022-11-17 10:22:10 -08:00
tasks Corrected the name of the file in cron.daily -- got rid of forbidden dots. 2023-01-25 07:53:52 -08:00
templates Added the missing cron job to renew the certificate. 2022-12-15 09:56:06 -08:00
.ansible-lint Added GitLab Pipeline, added meta info to meta/main.yaml. 2022-11-17 10:22:10 -08:00
.gitlab-ci.yml Corrected the path to the role in the Pipeline linting job. 2022-11-17 16:45:23 -08:00
LICENSE Initial commit 2022-02-10 17:13:03 +01:00
README.md Added Pipeline status banner to the README. 2022-11-17 17:02:12 -08:00

README.md

pipeline status

certbot-gandi Ansible Role

This role allows one to automate issuance of an wildcard Let's Encrypt X.509 certificate for a domain using gandi.

For example, if you own example.com, this role will allow you to automatically provision certificate for *.example.com.

Caveat: The certificate will be valid only for subdomains of example.com!!! E.g. it will be valid for www.example.com, yourcoolsubdomain.example.com, but it will not be valid for example.com itself!

Why?

I wrote this role to address specific use-case. I want to have an easy way to manage Let's Encrypt certificates for services that I self-host in my home network, but that are completely invisible outside my home. One way to achieve it is:

  1. I register a domain with gandi. E.g. example.com
  2. At home I have my own DNS resolver. That could be for example something like pfSense, OPNSense, or even Pi-hole.
  3. On my private DNS resolver I add custom subdomains of example.com. For example mycoolservice.example.com.
  4. I use this Ansible role to generate Let's Encrypt certificate for *.example.com, I hook it up to web-server (e.g. Nginx, Apache, whatever) that is hosting mycoolservice.example.com locally.

This way mycoolservice.example.com is visible (resolvable) only from within my home network. And the certificate that my private web-server presents is valid on any device -- my computer, smartphone, tablet, TV, whatever -- without any fiddling with accepting untrusted (e.g. self-signed, or signed by my own custom CA) certificate.

How?

  1. It creates /srv/certbot directory. All activities except certbot logging will happen within subdirectories of /srv/certbot.
  2. It creates Python virtualenv in /srv/certbot/venv and installs certbot with gandi plugin in it.
  3. It generates Bash scripts for issuing (issue.sh) and renewing (renew.sh) the certificate. They are placed in /srv/certbot/example.com/workdir/ directory (assuming your domain is example.com).
  4. Then it calls issue.sh to issue the certificate the very 1st time.
  5. It'll add a post-renew hook to certbot's config for reloading web server's configuration so that after renewing the certificate the web server reloads it.
  6. Adds a cron job to run renew.sh once a day to check if the certificate needs renewing.

The generated certificate and corresponding key lands in /srv/certbot/example.com/config/live/example.com/ directory.

Quick start

Pull in the role to your system

First off you have to add proper dependency to your project. Create -- if it doesn't yet exist -- a file called requirements.yaml. Then add the dependency to it. If this role is the only one that your playbook is pulling in, then requirements.yaml should look like this:

---
- src: git+https://codeberg.org/Prezu/certbot-gandi
  version: master
  name: certbot-gandi

Having that done, use ansible-galaxy to pull the role in:

$ ansible-galaxy install -r requirements.yaml

This is a one-time thing. Once you pull the role it, it'll live in your ~/.ansible/roles subdirectory.

Using the role in a playbook

The role requires you to set up a couple of Ansible variables:

  • domain: This is the name of your domain. E.g. example.com.
  • gandi_api_token: The API token you generated on your gandi account page.
  • contact_email: Let's Encrypt requires you to provide a contact email address.
  • web_server_reload_command: A command that will be executed by certbot to reload your web server's configuration after renewing the certificate.

The simplest and least secure way

If you want to quickly test the role out, you could add the following section to your roles in your playbook:

...
roles:
  - role: certbot-gandi
    vars:
      domain: "example.com"
      contact_email: contact@example.com
      gandi_api_token: 12345SLFDKSLFKJSD # Your generated API token
      web_server_reload_command: "systemctl reload nginx.service" # Assuming you're using Nginx

But you better not check that into your VCS!!!

Using ansible-vault to store the token

You should absolutely never check the bare API key to your version control system. It's much better to use e.g. ansible-vault to encrypt all security sensitive variables. Ansible vault will maintain a file within your repository, but the file will be encrypted.

Alright, so first you create the encrypted vault file:

$ mkdir vars
$ ansible-vault create vars/secrets.yaml

You'll be asked to pick a password for your new file. Then, ansible-vault will automatically open an editor for you. You have to create the YAML with the sensitive variables. E.g.:

---
gandi_api_token: 12345SLFDKSLFKJSD # Your generated API token

Now when you save the file and quite the editor, take a look at the new file. It'll look similar to this:

$ cat vars/secrets.yaml
$ANSIBLE_VAULT;1.1;AES256
38343062636539383830616436613162316536313465373236616566343034393937363665623466
3730613361363038613438376630393930373865343433660a636437396637663239363466363665
64386434663862666664323765373838346136326238316237383736373063393432333863373430
3364396133633933630a646434353937336233653430373166396139666539326239323463613132
39316163383364333836326639616339396333313534383663393737616363333336623033373833
38626430396264666365646366363530646462356637323533323962613866623561636363303339
36656335633535353565326339623463336265363663333661316562313730643234626265353938
30646631303330393464

So this is what you add to your VCS. So now in your runbook you'll need to point to the encrypted vars file:

...
vars_files:
  - vars/secrets.yaml
...
roles:
  - role: certbot-gandi
    vars:
      domain: "example.com"
      contact_email: contact@example.com
      web_server_reload_command: "systemctl reload nginx.service" # Assuming you're using Nginx
      # That's it. gandi_api_token will be picked up from vars/secret.yaml instead of from here.

Now, from this point on, you'll need to pass a new parameter to ansible-playbook, namely --ask-vault-pass. This will make ansible ask you the Vault password each time you execute the runbook.