You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
202 lines
7.8 KiB
202 lines
7.8 KiB
- name: install certbot
|
|
include_tasks: tasks/install_packages.yml
|
|
vars:
|
|
package: certbot
|
|
|
|
|
|
- name: create certbot directories
|
|
file:
|
|
path: "{{ item }}"
|
|
state: directory
|
|
loop:
|
|
- "{{ acme_directory }}"
|
|
- "{{ acme_directory }}/cron"
|
|
|
|
|
|
- name: change certbot directory permissions
|
|
file:
|
|
path: "{{ acme_directory ~ '/' ~ item }}"
|
|
state: directory
|
|
mode: "g+rx,o+rx"
|
|
loop:
|
|
- archive
|
|
- live
|
|
|
|
|
|
- name: check if acme-dns auth hook already exists
|
|
stat:
|
|
path: "{{ acme_directory }}/acme-dns-auth.py"
|
|
register: result
|
|
|
|
|
|
- name: download acme-dns auth hook
|
|
get_url:
|
|
url: "https://raw.githubusercontent.com/RangeForce/acme-dns-certbot-joohoi/master/acme-dns-auth.py"
|
|
dest: "{{ acme_directory }}/acme-dns-auth.py"
|
|
force: no
|
|
mode: "+x"
|
|
when: result.stat.exists == false
|
|
|
|
|
|
- name: update python interpreter in acme-dns-auth to python3
|
|
lineinfile:
|
|
path: "{{ acme_directory }}/acme-dns-auth.py"
|
|
regexp: '^#!\/usr\/bin\/env python\s*$'
|
|
line: '#!/usr/bin/env python3'
|
|
|
|
|
|
- name: clear acme fqdn list
|
|
set_fact:
|
|
acme_domain_list: "{{ [] }}"
|
|
|
|
|
|
- name: build acme fqdn list
|
|
set_fact:
|
|
acme_domain_list: "{{ (acme_domain_list | d([])) +
|
|
([item.fqdn | d((item.hostname | d(host_name)) ~ '.' ~ (item.tld | d(host_tld)))] if item is mapping else
|
|
[item]) }}"
|
|
loop: "{{ acme_hosts if (acme_hosts | type_debug == 'list') else [] }}"
|
|
|
|
|
|
- name: build single acme fqdn
|
|
set_fact:
|
|
acme_domain_list: "{%- if acme_fqdn is defined and acme_fqdn != None -%}\
|
|
{{ [ acme_fqdn ] }}\
|
|
{%- elif (acme_hostname is defined and acme_hostname != None) or (acme_tld is defined and acme_tld != None) -%}\
|
|
{{ [((acme_hostname is defined and acme_hostname != None) | ternary(acme_hostname, host_name)) ~ '.' ~
|
|
((acme_tld is defined and acme_tld != None) | ternary(acme_tld, host_tld))] }}\
|
|
{%- else -%}\
|
|
{{ [ host_fqdn ] }}\
|
|
{%- endif -%}"
|
|
when: (acme_hosts is not defined) or (acme_hosts | type_debug != 'list')
|
|
|
|
|
|
- name: set acme parameters
|
|
set_fact:
|
|
acme_cert_name: "{{ acme_id if (acme_id is defined) and (acme_id != None) else (host_name ~ ('-ecc' if (acme_ecc | d(false) == true) else '')) }}"
|
|
acme_target_server: "{%- if (acme_server is defined) and (acme_server != None) -%}\
|
|
{{ acme_server }}\
|
|
{%- else -%}\
|
|
{{ (services.acme_dns.protocol | d('https')) ~ '://' ~ services.acme_dns.hostname ~ '.' ~ (services.acme_dns.tld | d(int_tld)) ~
|
|
((':' ~ services.acme_dns.port) if services.acme_dns.port is defined else '') }}\
|
|
{%- endif -%}"
|
|
|
|
|
|
- name: set certbot parameters
|
|
set_fact:
|
|
acme_params: "{{ ['--manual',
|
|
'--manual-auth-hook ' ~ ((acme_directory ~ '/acme-dns-auth.py') | quote),
|
|
'--preferred-challenges dns',
|
|
'--debug-challenges',
|
|
('--key-type ecdsa' if (acme_ecc | d(false) == true) else ''),
|
|
('--staging' if (acme_staging | d(false) == true) else ''),
|
|
('--force-renewal' if (acme_force | d(false) == true) else ''),
|
|
('--must-staple' if (acme_stapling | d(false) == true) else ''),
|
|
'--cert-name ' ~ (acme_cert_name | quote),
|
|
'--non-interactive',
|
|
'--agree-tos',
|
|
'--email ' ~ ((acme_email | d(maintainer_email)) | quote),
|
|
'--no-eff-email',
|
|
(('--preferred-chain ' ~ (acme_preferred_chain | quote)) if acme_preferred_chain is defined else ''),
|
|
'--max-log-backups ' ~ (acme_max_log_backups | quote)
|
|
] | select() | list | join(' ') }}"
|
|
|
|
|
|
- block:
|
|
- name: issue cert with dns mode
|
|
shell:
|
|
cmd: "certbot certonly {{ acme_params }} -d {{ acme_domain_list | map('quote') | join(' -d ') }}"
|
|
chdir: /usr/bin
|
|
environment:
|
|
ACMEDNS_URL: "{{ acme_target_server }}"
|
|
register: result
|
|
changed_when: ('Successfully received certificate' in result.stdout)
|
|
notify: "{{ acme_notify if (acme_notify is defined) and (acme_notify != None) else omit }}"
|
|
|
|
rescue:
|
|
- name: wait for user interaction (CNAME record must be set manually)
|
|
pause:
|
|
prompt: "{{ result.stdout }}"
|
|
|
|
- name: try again to issue cert with dns mode
|
|
shell:
|
|
cmd: "certbot certonly {{ acme_params }} -d {{ acme_domain_list | map('quote') | join(' -d ') }}"
|
|
chdir: /usr/bin
|
|
environment:
|
|
ACMEDNS_URL: "{{ acme_target_server }}"
|
|
register: result
|
|
changed_when: ('Successfully received certificate' in result.stdout)
|
|
notify: "{{ acme_notify if (acme_notify is defined) and (acme_notify != None) else omit }}"
|
|
|
|
|
|
- name: create symlinks
|
|
file:
|
|
path: "{{ item.dest }}"
|
|
src: "{{ acme_directory ~ '/live/' ~ acme_cert_name ~ '/' ~ item.src }}"
|
|
state: link
|
|
force: yes
|
|
when: (item.dest is string) and (item.dest | length > 0) and (acme_use_symlinks | d(true) == true)
|
|
loop:
|
|
- { src: 'fullchain.pem', dest: "{{ acme_cert | d(None) }}" }
|
|
- { src: 'privkey.pem', dest: "{{ acme_key | d(None) }}" }
|
|
- { src: 'cert.pem', dest: "{{ acme_cert_single | d(None) }}" }
|
|
- { src: 'chain.pem', dest: "{{ acme_chain | d(None) }}" }
|
|
notify: "{{ acme_notify if (acme_notify is defined) and (acme_notify != None) else omit }}"
|
|
|
|
|
|
- name: fix ownership on archive dir
|
|
file:
|
|
path: "{{ acme_directory ~ '/archive/' ~ acme_cert_name }}"
|
|
follow: no
|
|
recurse: yes
|
|
owner: "{{ acme_owner if (acme_owner is defined) and (acme_owner != None) else omit }}"
|
|
group: "{{ acme_group if (acme_group is defined) and (acme_group != None) else omit }}"
|
|
|
|
|
|
- name: copy certs
|
|
copy:
|
|
src: "{{ acme_directory ~ '/live/' ~ acme_cert_name ~ '/' ~ item.src }}"
|
|
dest: "{{ item.dest }}"
|
|
remote_src: yes
|
|
mode: 0600
|
|
owner: "{{ acme_owner if (acme_owner is defined) and (acme_owner != None) else omit }}"
|
|
group: "{{ acme_group if (acme_group is defined) and (acme_group != None) else omit }}"
|
|
when: (item.dest is string) and (item.dest | length > 0) and (acme_use_symlinks | d(true) == false)
|
|
loop:
|
|
- { src: 'fullchain.pem', dest: "{{ acme_cert | d(None) }}" }
|
|
- { src: 'privkey.pem', dest: "{{ acme_key | d(None) }}" }
|
|
- { src: 'cert.pem', dest: "{{ acme_cert_single | d(None) }}" }
|
|
- { src: 'chain.pem', dest: "{{ acme_chain | d(None) }}" }
|
|
notify: "{{ acme_notify | d(omit) }}"
|
|
|
|
|
|
- name: edit renewal file
|
|
lineinfile:
|
|
path: "{{ acme_directory ~ '/renewal/' ~ acme_cert_name ~ '.conf' }}"
|
|
regexp: '^{{ item.name | regex_escape }}(\s+)='
|
|
line: '{{ item.name }} = {{ item.value }}'
|
|
insertafter: '^\[renewalparams\]'
|
|
create: no
|
|
firstmatch: yes
|
|
when: (item.value is string) and (item.value | length > 0) and
|
|
((item.extra_condition is not defined) or (item.extra_condition | d(true)))
|
|
loop:
|
|
- { name: 'renew_hook', value: "{{ acme_directory ~ '/cron/' ~ acme_cert_name ~ '.sh' }}" }
|
|
|
|
|
|
- name: create custom renewal hook file
|
|
template:
|
|
src: renewal.j2
|
|
dest: "{{ acme_directory ~ '/cron/' ~ acme_cert_name ~ '.sh' }}"
|
|
force: yes
|
|
mode: 0500
|
|
lstrip_blocks: yes
|
|
|
|
|
|
- name: add certbot to crontab
|
|
cron:
|
|
name: "certbot renewal ({{ acme_cert_name ~ ' on ' ~ acme_target_server }})"
|
|
job: "ACMEDNS_URL={{ acme_target_server | quote }} \
|
|
/usr/bin/certbot renew --cert-name {{ acme_cert_name | quote }} --max-log-backups {{ acme_max_log_backups | quote }}"
|
|
hour: "{{ 4 | random(start=1, seed=(host_name ~ acme_cert_name)) }}"
|
|
minute: "{{ 59 | random(seed=(host_name ~ acme_cert_name)) }}"
|
|
|