|
|
|
- name: fail if acme is not defined
|
|
|
|
fail:
|
|
|
|
msg: acme must be a mapping
|
|
|
|
when: acme is not mapping
|
|
|
|
|
|
|
|
|
|
|
|
- name: set acme_cfg
|
|
|
|
set_fact:
|
|
|
|
acme_cfg: "{{ acme_default_config | d({}) |
|
|
|
|
combine(acme_config | d({}), recursive=true) |
|
|
|
|
combine(acme2 | d({}), recursive=true) |
|
|
|
|
combine(acme, recursive=true) }}"
|
|
|
|
|
|
|
|
|
|
|
|
- name: determine host architecture
|
|
|
|
include_tasks: tasks/get_host_arch.yml
|
|
|
|
|
|
|
|
|
|
|
|
- name: create lego dirs
|
|
|
|
file:
|
|
|
|
path: "{{ item }}"
|
|
|
|
state: directory
|
|
|
|
mode: 0700
|
|
|
|
loop:
|
|
|
|
- "{{ lego_dir }}"
|
|
|
|
- "{{ lego_cert_dir }}"
|
|
|
|
|
|
|
|
|
|
|
|
- name: get and extract latest lego version
|
|
|
|
include_tasks: tasks/get_lastversion.yml
|
|
|
|
vars:
|
|
|
|
package:
|
|
|
|
name: go-acme/lego
|
|
|
|
location: github
|
|
|
|
assets: yes
|
|
|
|
asset_filter: "{{ 'linux_' ~ host_architecture ~ '.tar.gz$' }}"
|
|
|
|
file: "{{ (lego_dir, 'last_version') | path_join }}"
|
|
|
|
extract: "{{ lego_dir }}"
|
|
|
|
|
|
|
|
|
|
|
|
- block:
|
|
|
|
- name: remove unnecessary files
|
|
|
|
file:
|
|
|
|
path: "{{ (lego_dir, item) | path_join }}"
|
|
|
|
state: absent
|
|
|
|
loop:
|
|
|
|
- LICENSE
|
|
|
|
- CHANGELOG.md
|
|
|
|
rescue:
|
|
|
|
- meta: noop
|
|
|
|
|
|
|
|
|
|
|
|
- name: set job name
|
|
|
|
set_fact:
|
|
|
|
lego_job_name: "{{ acme_cfg.domains[0] ~ '-' ~ acme_cfg.type }}"
|
|
|
|
|
|
|
|
|
|
|
|
- name: set initial lego facts
|
|
|
|
set_fact:
|
|
|
|
lego_must_reissue: yes
|
|
|
|
lego_renew_script: "{{ (lego_cert_dir, 'cron-' ~ lego_job_name ~ '.sh') | path_join }}"
|
|
|
|
lego_lastrun_file: "{{ (lego_cert_dir, 'lastrun-' ~ lego_job_name) | path_join }}"
|
|
|
|
|
|
|
|
|
|
|
|
- name: create custom renewal script file
|
|
|
|
template:
|
|
|
|
src: renewal.j2
|
|
|
|
dest: "{{ lego_renew_script }}"
|
|
|
|
force: yes
|
|
|
|
mode: 0500
|
|
|
|
lstrip_blocks: yes
|
|
|
|
|
|
|
|
|
|
|
|
- name: set lego parameters
|
|
|
|
set_fact:
|
|
|
|
lego_params: "{{
|
|
|
|
[
|
|
|
|
([] | zip_longest(acme_cfg.domains | d([]) | select() | map('quote'), fillvalue='--domains ') | map('join') | list),
|
|
|
|
'--server ' ~ ((acme_cfg.endpoint_staging if acme_cfg.staging else acme_cfg.endpoint_prod) | quote),
|
|
|
|
'--accept-tos',
|
|
|
|
'--filename ' ~ (lego_job_name | quote),
|
|
|
|
'--email ' ~ (acme_cfg.email | d(maintainer_email) | quote),
|
|
|
|
'--key-type ' ~ (acme_cfg.type | quote),
|
|
|
|
'--path ' ~ (lego_cert_dir | quote),
|
|
|
|
'--dns acme-dns',
|
|
|
|
'--dns.resolvers ' ~ (acme_cfg.resolver | d('1.1.1.1') | quote),
|
|
|
|
'--dns.disable-cp'
|
|
|
|
] | flatten(levels=1) | select() | list | join(' ') }}"
|
|
|
|
lego_renewal_params: "{{
|
|
|
|
[
|
|
|
|
(('--days ' ~ (acme_cfg.renew_at_days | quote)) if acme_cfg.renew_at_days is defined else ''),
|
|
|
|
('--reuse-key' if acme_cfg.reuse_key | d(false) == true else ''),
|
|
|
|
('--no-random-sleep' if acme_cfg.no_random_sleep | d(true) == true else ''),
|
|
|
|
('--renew-hook ' ~ (lego_renew_script | quote))
|
|
|
|
] | flatten(levels=1) | select() | list | join(' ') }}"
|
|
|
|
lego_preferred_chain: "{{ '--preferred-chain ' ~ (acme_cfg.preferred_chain | quote) if acme_cfg.preferred_chain is defined else '' }}"
|
|
|
|
|
|
|
|
|
|
|
|
- name: set lego command facts
|
|
|
|
set_fact:
|
|
|
|
lego_full_command: "{{ (lego_dir, 'lego') | path_join }} {{ lego_params }} run {{ lego_preferred_chain }}"
|
|
|
|
lego_renew_command: "{{ (lego_dir, 'lego') | path_join }} {{ lego_params }} renew {{ lego_preferred_chain }} {{ lego_renewal_params }}"
|
|
|
|
|
|
|
|
|
|
|
|
- name: check if lastrun file exists
|
|
|
|
stat:
|
|
|
|
path: "{{ lego_lastrun_file }}"
|
|
|
|
get_checksum: no
|
|
|
|
get_mime: no
|
|
|
|
register: result
|
|
|
|
|
|
|
|
|
|
|
|
- block:
|
|
|
|
- name: get lastrun file contents
|
|
|
|
slurp:
|
|
|
|
path: "{{ lego_lastrun_file }}"
|
|
|
|
register: file_content
|
|
|
|
no_log: yes
|
|
|
|
|
|
|
|
- name: determine if cert should be reissued
|
|
|
|
set_fact:
|
|
|
|
lego_must_reissue: "{{ (file_content.content | b64decode) != lego_full_command }}"
|
|
|
|
|
|
|
|
when: result.stat.exists
|
|
|
|
|
|
|
|
|
|
|
|
- block:
|
|
|
|
- name: issue cert with dns mode
|
|
|
|
shell:
|
|
|
|
cmd: "{{ lego_full_command }}"
|
|
|
|
chdir: "{{ lego_dir }}"
|
|
|
|
environment:
|
|
|
|
ACME_DNS_API_BASE: "{{ acme_cfg.server }}"
|
|
|
|
ACME_DNS_STORAGE_PATH: "{{ lego_accounts_file }}"
|
|
|
|
register: result
|
|
|
|
|
|
|
|
when: lego_must_reissue
|
|
|
|
rescue:
|
|
|
|
- pause:
|
|
|
|
when: interactive | d(true) == true
|
|
|
|
|
|
|
|
- name: retry issuing cert with dns mode
|
|
|
|
shell:
|
|
|
|
cmd: "{{ lego_full_command }}"
|
|
|
|
chdir: "{{ lego_dir }}"
|
|
|
|
environment:
|
|
|
|
ACME_DNS_API_BASE: "{{ acme_cfg.server }}"
|
|
|
|
ACME_DNS_STORAGE_PATH: "{{ lego_accounts_file }}"
|
|
|
|
register: result
|
|
|
|
|
|
|
|
|
|
|
|
- block:
|
|
|
|
- name: save data to lastrun file
|
|
|
|
copy:
|
|
|
|
content: "{{ lego_full_command }}"
|
|
|
|
dest: "{{ lego_lastrun_file }}"
|
|
|
|
remote_src: yes
|
|
|
|
|
|
|
|
- name: defer service restart
|
|
|
|
debug:
|
|
|
|
msg: deferring service restart
|
|
|
|
changed_when: yes
|
|
|
|
notify: "{{ acme_cfg.notify }}"
|
|
|
|
when: acme_cfg.notify is defined
|
|
|
|
|
|
|
|
- name: copy certificates to their intended locations
|
|
|
|
copy:
|
|
|
|
src: "{{ (lego_cert_dir, 'certificates', lego_job_name ~ '.' ~ item.src_ext) | path_join }}"
|
|
|
|
dest: "{{ item.dest }}"
|
|
|
|
remote_src: yes
|
|
|
|
mode: 0600
|
|
|
|
owner: "{{ acme_cfg.owner | d(omit) }}"
|
|
|
|
group: "{{ acme_cfg.group | d(omit) }}"
|
|
|
|
when: item.dest != None
|
|
|
|
loop:
|
|
|
|
- { src_ext: 'crt', dest: "{{ acme_cfg.cert | d(None) }}" }
|
|
|
|
- { src_ext: 'key', dest: "{{ acme_cfg.key | d(None) }}" }
|
|
|
|
notify: "{{ acme_cfg.notify | d(omit) }}"
|
|
|
|
|
|
|
|
when: lego_must_reissue
|
|
|
|
|
|
|
|
|
|
|
|
- name: configure systemd service and timer
|
|
|
|
block:
|
|
|
|
- name: template systemd files
|
|
|
|
template:
|
|
|
|
src: "{{ item.src }}.j2"
|
|
|
|
dest: "{{ ('/etc/systemd/system', item.dst) | path_join }}"
|
|
|
|
force: yes
|
|
|
|
lstrip_blocks: yes
|
|
|
|
loop:
|
|
|
|
- { src: 'lego_systemd', dst: 'lego.service' }
|
|
|
|
- { src: 'lego_timer', dst: 'lego.timer' }
|
|
|
|
notify: reload systemd daemons
|
|
|
|
|
|
|
|
- name: enable lego timer
|
|
|
|
systemd:
|
|
|
|
name: lego.timer
|
|
|
|
state: started
|
|
|
|
enabled: yes
|
|
|
|
|
|
|
|
when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'
|
|
|
|
|
|
|
|
|
|
|
|
- name: configure crontab entry
|
|
|
|
cron:
|
|
|
|
name: "certificate renewal ({{ lego_job_name ~ ' on ' ~ acme_cfg.server }})"
|
|
|
|
job: "ACME_DNS_API_BASE={{ acme_cfg.server | quote }} ; \
|
|
|
|
ACME_DNS_STORAGE_PATH={{ lego_accounts_file | quote }} ; \
|
|
|
|
cd {{ lego_dir | quote }} ; \
|
|
|
|
{{ lego_renew_command }}"
|
|
|
|
hour: "{{ 4 | random(start=1, seed=(host_name ~ lego_job_name)) }}"
|
|
|
|
minute: "{{ 59 | random(seed=(host_name ~ lego_job_name)) }}"
|
|
|
|
when: ansible_distribution == 'Alpine'
|