Certificates with Ansible, Letsencrypt and Cloudflare
- December 2, 2016 in
- ansible
- letsencrypt
- cloudflare
Changes:
Please note that this article is more than 5 years old. The information provided may very well be outdatedThe example use Cloudflare for DNS, but any provider with an ansible module works.
To use the example, add your own email, api token and domain name to variables. To receive a certificate with an actual trusted root, change ACME Directory to https://acme-v02.api.letsencrypt.org/directory
1---
2- hosts: localhost
3 vars:
4 connection: local
5 cloudflare_account_email: ''
6 cloudflare_account_api_token: ''
7 domain_name: ''
8
9 tasks:
10 - name: Create folder to store everything
11 file:
12 path: '~/.ssh/letsencrypt'
13 state: directory
14 mode: 0700
15
16 - name: Create Letsencrypt Account private key
17 openssl_privatekey:
18 path: '~/.ssh/letsencrypt/letsencrypt.pem'
19
20 - name: Create Domain Private Key
21 openssl_privatekey:
22 path: '~/.ssh/letsencrypt/{{ domain_name }}.pem'
23
24 - name: Create Certificate Signing Request
25 openssl_csr:
26 path: '~/.ssh/letsencrypt/{{ domain_name }}.csr'
27 privatekey_path: '~/.ssh/letsencrypt/{{ domain_name }}.pem'
28 common_name: '{{ domain_name }}'
29 subject_alt_name: "{{ item.value | map('regex_replace', '^', 'DNS:') | list }}"
30 with_dict:
31 dns_server:
32 - '{{ domain_name }}'
33 - '*.{{ domain_name }}'
34
35 - name: Create ACME Challenge
36 acme_certificate:
37 account_key_src: '~/.ssh/letsencrypt/letsencrypt.pem'
38 acme_directory: 'https://acme-staging-v02.api.letsencrypt.org/directory'
39 acme_version: 2
40 challenge: dns-01
41 csr: '~/.ssh/letsencrypt/{{ domain_name }}.csr'
42 fullchain_dest: '~/.ssh/letsencrypt/{{ domain_name }}.fullchain.pem'
43 terms_agreed: yes
44 register: le_challenge
45
46 - name: Update DNS record
47 cloudflare_dns:
48 account_api_token: '{{ cloudflare_account_api_token }}'
49 account_email: '{{ cloudflare_account_email }}'
50 zone: '{{ domain_name }}'
51 record: "{{ item['value']['dns-01']['resource'] }}"
52 type: TXT
53 value: "{{ item['value']['dns-01']['resource_value'] }}"
54 when: le_challenge is changed
55 with_dict: "{{ le_challenge['challenge_data'] }}"
56 ignore_errors: yes
57
58 - name: Wait a bit to let DNS update
59 wait_for:
60 timeout: 30
61 when: le_challenge is changed
62
63 - name: Validate ACME Challenge
64 acme_certificate:
65 account_key_src: '~/.ssh/letsencrypt/letsencrypt.pem'
66 acme_directory: 'https://acme-staging-v02.api.letsencrypt.org/directory'
67 acme_version: 2
68 challenge: dns-01
69 csr: '~/.ssh/letsencrypt/{{ domain_name }}.csr'
70 data: '{{ le_challenge }}'
71 fullchain_dest: '~/.ssh/letsencrypt/{{ domain_name }}.fullchain.pem'
72 terms_agreed: yes
73 when: le_challenge is changed
74
75 - name: Remove TXT record after validating
76 cloudflare_dns:
77 account_api_token: '{{ cloudflare_account_api_token }}'
78 account_email: '{{ cloudflare_account_email }}'
79 zone: '{{ domain_name }}'
80 record: "{{ item['value']['dns-01']['resource'] }}"
81 type: TXT
82 value: "{{ item['value']['dns-01']['resource_value'] }}"
83 state: absent
84 when: le_challenge is changed
85 with_dict: "{{ le_challenge['challenge_data'] }}"
86 ignore_errors: yes
87