diff --git a/lego/defaults/main.yml b/lego/defaults/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..f37785cae89c7711c3736f4c09862d28fe516b50 --- /dev/null +++ b/lego/defaults/main.yml @@ -0,0 +1,44 @@ +--- + +lego_acme_server_base: acme-v02.api.letsencrypt.org + +# lego_account_mail: root@example.org + +# The following two variables are defaults for the respective entries in a +# `lego_certificates` entry. +lego_method: + type: http + subtype: webroot +lego_hooks: + services: {} + extra: {} + +lego_global_args: [] + +# lego_certificates: +# # Default case, only one domain +# foo.example.org: {} +# bar.example.org: +# domains: +# # bar.example.org is already included +# - baz.example.org +# - qux.example.org +# account_mail: other@example.org +# method: +# type: dns +# subtype: rfc2136 +# extra_args: +# - --foo +# - --bar baz +# extra_env: +# RFC2136_NAMESERVER: 127.0.0.1 +# hooks: +# services: +# nginx.service: try-restart +# httpd.service: try-reload-or-restart +# extra: +# 10-install.sh: | +# #!/bin/sh +# set -e +# install -u foo -g foo -m 0644 "$LEGO_CERT_PATH" /etc/foo/cert.pem +# install -u foo -g foo -m 0600 "$LEGO_CERT_KEY_PATH" /etc/foo/key.pem diff --git a/lego/handlers/main.yml b/lego/handlers/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..4df3968a9e8a505e41ead5def12428eea777b6d5 --- /dev/null +++ b/lego/handlers/main.yml @@ -0,0 +1,5 @@ +--- + +- name: Reload systemd + systemd: + daemon_reload: true diff --git a/lego/tasks/certificate.yml b/lego/tasks/certificate.yml new file mode 100644 index 0000000000000000000000000000000000000000..d580e2a5ed60f4cdcf05189a989a5ef0386e1e2a --- /dev/null +++ b/lego/tasks/certificate.yml @@ -0,0 +1,51 @@ +--- + +- name: Install instance config + template: + src: env-inst.j2 + dest: /etc/lego/{{ item.key }}.env + owner: root + group: root + mode: "0600" + register: install_instance_config + +- name: Create hooks directory + file: + path: /etc/lego/hooks/{{ item.key }} + state: directory + owner: root + group: root + mode: "0700" + +- name: Install service hook + template: + src: 99-services.sh.j2 + dest: /etc/lego/hooks/{{ item.key }}/99-services + owner: root + group: root + mode: "0700" + when: item.value.hooks.services != {} + +- name: Install additional hooks + copy: + content: "{{ j.value }}" + dest: /etc/lego/hooks/{{ item.key }}/{{ j.key }} + owner: root + group: root + mode: "0700" + loop: "{{ item.value.hooks.extra | dict2items }}" + loop_control: + loop_var: j + +# Yes, this should be a handler, but they cannot be as dynamic as we need it. +- name: Start lego + systemd: + name: lego-run@{{ item.key }}.service + state: started + when: install_instance_config.changed + +- name: Enable renewal timer + systemd: + name: lego-renew@{{ item.key }}.timer + state: started + enabled: true diff --git a/lego/tasks/main.yml b/lego/tasks/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..d810a4c4ab3f7c1ebb5796b70849949f4b897d34 --- /dev/null +++ b/lego/tasks/main.yml @@ -0,0 +1,41 @@ +--- + +- name: Install lego + apt: + name: lego + +- name: Create config and hooks directory + file: + path: "{{ item }}" + state: directory + owner: root + group: root + mode: "0700" + loop: + - /etc/lego + - /etc/lego/hooks + +- name: Install systemd service files + template: + src: "{{ item }}.j2" + dest: /etc/systemd/system/{{ item }} + owner: root + group: root + mode: "0644" + loop: + - lego-run@.service + - lego-renew@.service + - lego-renew@.timer + notify: + - Reload systemd + +- meta: flush_handlers + +- name: Process individual certificate instances + loop: "{{ lego_certificates | dict2items }}" + loop_control: + label: "{{ item.key }}" + loop_var: _item + vars: + item: "{{ cert_skeleton | combine(_item, recursive=True) }}" + include_tasks: certificate.yml diff --git a/lego/templates/99-services.sh.j2 b/lego/templates/99-services.sh.j2 new file mode 100644 index 0000000000000000000000000000000000000000..8d50f7b4280e73e176f24b00d348b47c0cb5e165 --- /dev/null +++ b/lego/templates/99-services.sh.j2 @@ -0,0 +1,11 @@ +#!/bin/sh + +{% for svc, action in item.value.hooks.services.items() %} +set -- systemctl "{{ action }}" "{{ svc }}" +"$@" +ret=$? +if [ $ret -ne 0 ]; then + echo "(command was: $*)" + exit $ret +fi +{% endfor %} diff --git a/lego/templates/_macros.j2 b/lego/templates/_macros.j2 new file mode 100644 index 0000000000000000000000000000000000000000..75bf0412124dd7090e5855f4f78a1e09efb67a8e --- /dev/null +++ b/lego/templates/_macros.j2 @@ -0,0 +1,52 @@ +{# -*- systemd -*- #} +{% macro execstart(typ) %} +ExecStart=/usr/bin/lego $LEGO_GLOBAL_ARGS $LEGO_ARGS \ + --server https://${LEGO_ACME_SERVER_BASE}/directory \ + --path $STATE_DIRECTORY \ + --accept-tos \ + {{ typ }} \ +{% if typ == "renew" %} + --no-random-sleep \ +{% endif %} + --{{ typ }}-hook='run-parts --report /etc/lego/hooks/%i/' +{% endmacro %} + +{% macro systemd_boilerplate(typ) %} +[Unit] +Wants=network-online.target +After=network-online.target + +[Service] +EnvironmentFile=/etc/lego/%i.env +StateDirectory=lego +StateDirectoryMode=0700 +RuntimeDirectory=lego +Type=oneshot + +{{ execstart(typ) }} + +# PrivateDevices=yes +# NoNewPrivileges=yes +# ProtectHome=yes + +TimeoutStartSec=5min +#CapabilityBoundingSet=CAP_CHOWN +# Empty = no capabilities at all +CapabilityBoundingSet= +NoNewPrivileges=yes +PrivateTmp=yes +PrivateDevices=yes +ProtectSystem=strict +ProtectHome=yes +ProtectHostname=yes +ProtectClock=yes +ProtectKernelTunables=yes +ProtectKernelModules=yes +ProtectKernelLogs=yes +ProtectControlGroups=yes +RestrictRealtime=yes +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 +LockPersonality=yes +SystemCallFilter=@system-service +SystemCallArchitectures=native +{% endmacro %} diff --git a/lego/templates/env-inst.j2 b/lego/templates/env-inst.j2 new file mode 100644 index 0000000000000000000000000000000000000000..e56f40fe8492631c9ce3f7ce492d5b08f73700eb --- /dev/null +++ b/lego/templates/env-inst.j2 @@ -0,0 +1,24 @@ +{% if lego_global_args is iterable and not lego_global_args is string -%} +LEGO_GLOBAL_ARGS="{{ lego_global_args | join(' ') }}" +{% else %} +LEGO_GLOBAL_ARGS="{{ lego_global_args }}" +{% endif %} +LEGO_ACME_SERVER_BASE="{{ lego_acme_server_base }}" +LEGO_CERT_DOMAIN="{{ item.key }}" +LEGO_ARGS="\ +--email {{ item.value.account_mail }} \ +--{{ item.value.method.type }} \ +{% if item.value.method.type == "http" and item.value.method.subtype == "webroot" %} +--http.webroot /run/lego \ +{% endif %} +--domains {{ item.key }} \ +{% for d in item.value.domains %} +--domains {{ d }} \ +{% endfor %} +{% for i in item.value.extra_args %} +{{ i }} \ +{% endfor %} +" +{% for k, v in item.value.extra_env.items() %} +{{ k }}="{{ v }}" +{% endfor %} diff --git a/lego/templates/lego-renew@.service.j2 b/lego/templates/lego-renew@.service.j2 new file mode 100644 index 0000000000000000000000000000000000000000..5142a85f7b55c722f91f8f4bcd6949471dd3ccad --- /dev/null +++ b/lego/templates/lego-renew@.service.j2 @@ -0,0 +1,6 @@ +{# -*- systemd -*- #} +{% from "_macros.j2" import systemd_boilerplate -%} +[Unit] +Description=lego ACME client -- renewal for %i + +{{ systemd_boilerplate("renew") }} diff --git a/lego/templates/lego-renew@.timer.j2 b/lego/templates/lego-renew@.timer.j2 new file mode 100644 index 0000000000000000000000000000000000000000..5e249c83b1d751bf7ac957394483aae78ca972f5 --- /dev/null +++ b/lego/templates/lego-renew@.timer.j2 @@ -0,0 +1,11 @@ +{# -*- systemd -*- #} +[Unit] +Description=lego ACME client -- renewal for %i timer + +[Timer] +OnCalendar=weekly +RandomizedDelaySec=1d +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/lego/templates/lego-run@.service.j2 b/lego/templates/lego-run@.service.j2 new file mode 100644 index 0000000000000000000000000000000000000000..db4acb1bb8de9b8189879fcf461100f24e4b2dbf --- /dev/null +++ b/lego/templates/lego-run@.service.j2 @@ -0,0 +1,6 @@ +{# -*- systemd -*- #} +{% from "_macros.j2" import systemd_boilerplate -%} +[Unit] +Description=lego ACME client -- setup for %i + +{{ systemd_boilerplate("run") }} diff --git a/lego/vars/main.yml b/lego/vars/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..e13a319c9b4b768eca8c9501d239e873338b4a96 --- /dev/null +++ b/lego/vars/main.yml @@ -0,0 +1,10 @@ +--- + +cert_skeleton: + value: + account_mail: "{{ lego_account_mail }}" + domains: [] + method: "{{ lego_method }}" + extra_args: [] + extra_env: {} + hooks: "{{ lego_hooks }}"