From 3fb8d41878837ad4fb5606ceb6de738adda38a80 Mon Sep 17 00:00:00 2001 From: Andrea Dell'Amico Date: Thu, 14 Apr 2016 19:08:33 +0200 Subject: [PATCH] library/roles/letsencrypt-acmetool-client: Letsencrypt acmetool client. Better than the original one, can run as an unprivileged user. See https://support.d4science.org/issues/3164 --- letsencrypt-acmetool-client/defaults/main.yml | 31 +++++ letsencrypt-acmetool-client/tasks/main.yml | 112 ++++++++++++++++++ .../templates/acme-sudoers.j2 | 2 + .../templates/cert-requirements.j2 | 16 +++ .../templates/letsencrypt-default.j2 | 3 + .../templates/responses.j2 | 12 ++ 6 files changed, 176 insertions(+) create mode 100644 letsencrypt-acmetool-client/defaults/main.yml create mode 100644 letsencrypt-acmetool-client/tasks/main.yml create mode 100644 letsencrypt-acmetool-client/templates/acme-sudoers.j2 create mode 100644 letsencrypt-acmetool-client/templates/cert-requirements.j2 create mode 100644 letsencrypt-acmetool-client/templates/letsencrypt-default.j2 create mode 100644 letsencrypt-acmetool-client/templates/responses.j2 diff --git a/letsencrypt-acmetool-client/defaults/main.yml b/letsencrypt-acmetool-client/defaults/main.yml new file mode 100644 index 00000000..f112a77a --- /dev/null +++ b/letsencrypt-acmetool-client/defaults/main.yml @@ -0,0 +1,31 @@ +--- +letsencrypt_acme_install: False +letsencrypt_acme_ppa_repo: 'ppa:hlandau/rhea' +letsencrypt_acme_debian_repo: 'deb http://ppa.launchpad.net/hlandau/rhea/ubuntu xenial main' +letsencrypt_acme_debian_repo_key: '9862409EF124EC763B84972FF5AC9651EDB58DFA' +letsencrypt_acme_user: acme +letsencrypt_acme_user_home: /var/lib/acme + +letsencrypt_acme_command: acmetool +letsencrypt_acme_command_opts: '--batch --xlog.syslog --xlog.severity=info' +letsencrypt_acme_config_dir: '{{ letsencrypt_acme_user_home }}/conf' +letsencrypt_acme_certsconf_dir: '{{ letsencrypt_acme_user_home }}/desired' +letsencrypt_acme_certs_dir: '{{ letsencrypt_acme_config_dir }}/live/{{ ansible_fqdn }}' +# The various services maintainers need to put the reconfigure/restart scripts there +letsencrypt_acme_services_scripts_dir: /usr/lib/acme/hooks + +# responses parameters +letsencrypt_acme_rsa_key_size: 4096 +letsencrypt_acme_key_type: ecdsa +letsencrypt_acme_ecdsa_curve: nistp256 +letsencrypt_acme_email: sysadmin@example.com +# We 'listener' or 'proxy'. Use 'listener' if we need a certificate for a non web service. +# Need to set cap_net_bind_service=+ep for the acmetool binary so that it is able to bind port 80 in that case. +letsencrypt_acme_authenticator: proxy +letsencrypt_acme_agree_tos: true + +# desired parameters +letsencrypt_acme_domains: + - '{{ ansible_fqdn }}' +letsencrypt_acme_standalone_port: 9999 + diff --git a/letsencrypt-acmetool-client/tasks/main.yml b/letsencrypt-acmetool-client/tasks/main.yml new file mode 100644 index 00000000..6e0272d0 --- /dev/null +++ b/letsencrypt-acmetool-client/tasks/main.yml @@ -0,0 +1,112 @@ +--- +- name: Install the letsencrypt acmetool repo on ubuntu + apt_repository: repo={{ letsencrypt_acme_ppa_repo }} state=present update_cache=yes + when: + - letsencrypt_acme_install + - is_ubuntu + tags: letsencrypt + +- name: Install the letsencrypt acmetool repo key on debian + apt_key: keyserver=keyserver.ubuntu.com id={{ letsencrypt_acme_debian_repo_key }} + when: + - letsencrypt_acme_install + - is_debian + tags: letsencrypt + +- name: Install the letsencrypt acmetool repo on debian + apt_repository: repo={{ letsencrypt_acme_debian_repo }} state=present update_cache=yes + when: + - letsencrypt_acme_install + - is_debian + tags: letsencrypt + +- name: Create the letsencrytp acme user + user: name={{ letsencrypt_acme_user }} home={{ letsencrypt_acme_user_home }} createhome=yes shell=/bin/bash + when: letsencrypt_acme_install + tags: letsencrypt + +- name: Install the letsencrypt acmetool package + apt: pkg=acmetool state=installed + when: letsencrypt_acme_install + tags: letsencrypt + +- name: Create the letsencrypt acme config directory + become: True + become_user: '{{ letsencrypt_acme_user }}' + file: dest={{ letsencrypt_acme_config_dir }} state=directory mode=0755 + when: letsencrypt_acme_install + tags: letsencrypt + +- name: Create the letsencrypt acme desired domains directory + become: True + become_user: '{{ letsencrypt_acme_user }}' + file: dest={{ letsencrypt_acme_certsconf_dir }} state=directory mode=0755 + when: letsencrypt_acme_install + tags: letsencrypt + +- name: Create the letsencrypt acme hooks directory + file: dest={{ letsencrypt_acme_services_scripts_dir }} state=directory owner=root group=root mode=0755 + when: letsencrypt_acme_install + tags: letsencrypt + +- name: Install a default file that shell scripts can include + template: src=letsencrypt-default.j2 dest=/etc/default/letsencrypt owner=root group=root mode=0644 + when: letsencrypt_acme_install + tags: letsencrypt + +- name: Install the letsencrypt acme responses file + become: True + become_user: '{{ letsencrypt_acme_user }}' + template: src=responses.j2 dest={{ letsencrypt_acme_config_dir }}/responses mode=0644 + when: letsencrypt_acme_install + tags: letsencrypt + +- name: Install the letsencrypt acme certs config file + become: True + become_user: '{{ letsencrypt_acme_user }}' + template: src=cert-requirements.j2 dest={{ letsencrypt_acme_certsconf_dir }}/{{ ansible_fqdn }} mode=0644 + when: letsencrypt_acme_install + tags: letsencrypt + +- name: Set the cap_net_bind_service capability to the acmetool binary when we use it in listener mode + capabilities: path=/usr/bin/acmetool capability=cap_net_bind_service+ep state=present + when: + - letsencrypt_acme_install + - "'{{ letsencrypt_acme_authenticator }}' == 'listener'" + tags: letsencrypt + +- name: Remove the cap_net_bind_service capability to the acmetool binary if not needed + capabilities: path=/usr/bin/acmetool capability=cap_net_bind_service+ep state=absent + when: + - letsencrypt_acme_install + - "'{{ letsencrypt_acme_authenticator }}' != 'listener'" + tags: letsencrypt + +- name: letsencrypt acmetool initializaztion + become: True + become_user: '{{ letsencrypt_acme_user }}' + command: '{{ letsencrypt_acme_command }} {{ letsencrypt_acme_command_opts }} quickstart' + args: + creates: '{{ letsencrypt_acme_user_home }}/accounts' + when: letsencrypt_acme_install + tags: letsencrypt + +- name: letsencrypt acmetool request the first certificate + become: True + become_user: '{{ letsencrypt_acme_user }}' + command: '{{ letsencrypt_acme_command }} {{ letsencrypt_acme_command_opts }} reconcile' + args: + creates: '{{ letsencrypt_acme_certs_dir }}/cert' + when: letsencrypt_acme_install + tags: letsencrypt + +- name: Install the sudoers config needed to run the acmetool hooks + template: src=acme-sudoers.j2 dest=/etc/sudoers.d/letsencrypt-acme owner=root group=root mode=0440 + when: letsencrypt_acme_install + tags: letsencrypt + +- name: Install a daily cron job to renew the certificates when needed + cron: name="Letsencrypt certificate renewal" special_time=daily job="{{ letsencrypt_acme_command }} {{ letsencrypt_acme_command_opts }} reconcile" user={{ letsencrypt_acme_user }} + when: letsencrypt_acme_install + tags: letsencrypt + diff --git a/letsencrypt-acmetool-client/templates/acme-sudoers.j2 b/letsencrypt-acmetool-client/templates/acme-sudoers.j2 new file mode 100644 index 00000000..fff36fa6 --- /dev/null +++ b/letsencrypt-acmetool-client/templates/acme-sudoers.j2 @@ -0,0 +1,2 @@ +{{ letsencrypt_acme_user }} ALL=(root) NOPASSWD: {{ letsencrypt_acme_services_scripts_dir }} + diff --git a/letsencrypt-acmetool-client/templates/cert-requirements.j2 b/letsencrypt-acmetool-client/templates/cert-requirements.j2 new file mode 100644 index 00000000..3666cd4e --- /dev/null +++ b/letsencrypt-acmetool-client/templates/cert-requirements.j2 @@ -0,0 +1,16 @@ +satisfy: + names: +{% for d in letsencrypt_acme_domains %} + - {{ d }} +{% endfor %} + +request: + challenge: + http-ports: + - {{ letsencrypt_acme_standalone_port }} + +key: + type: {{ letsencrypt_acme_key_type }} + ecdsa-curve: {{ letsencrypt_acme_ecdsa_curve }} + + diff --git a/letsencrypt-acmetool-client/templates/letsencrypt-default.j2 b/letsencrypt-acmetool-client/templates/letsencrypt-default.j2 new file mode 100644 index 00000000..beeb1110 --- /dev/null +++ b/letsencrypt-acmetool-client/templates/letsencrypt-default.j2 @@ -0,0 +1,3 @@ +LE_EMAIL={{ letsencrypt_acme_email }} +LE_SERVICES_SCRIPT_DIR={{ letsencrypt_acme_services_scripts_dir }} +LE_CERTS_DIR={{ letsencrypt_acme_certs_dir }} diff --git a/letsencrypt-acmetool-client/templates/responses.j2 b/letsencrypt-acmetool-client/templates/responses.j2 new file mode 100644 index 00000000..1857aae2 --- /dev/null +++ b/letsencrypt-acmetool-client/templates/responses.j2 @@ -0,0 +1,12 @@ +"acme-enter-email": "{{ letsencrypt_acme_email }}" +"acme-agreement:https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf": {{ letsencrypt_acme_agree_tos }} +# This is the production site +"acmetool-quickstart-choose-server": https://acme-v01.api.letsencrypt.org/directory +"acmetool-quickstart-choose-method": {{ letsencrypt_acme_authenticator }} +"acmetool-quickstart-complete": true +"acmetool-quickstart-install-cronjob": false +"acmetool-quickstart-install-haproxy-script": false +"acmetool-quickstart-install-redirector-systemd": false +"acmetool-quickstart-key-type": {{ letsencrypt_acme_key_type }} +"acmetool-quickstart-rsa-key-size": {{ letsencrypt_acme_rsa_key_size }} +"acmetool-quickstart-ecdsa-curve": {{ letsencrypt_acme_ecdsa_curve }}