Skip to content

Commit

Permalink
Implement a post-renewal hook script
Browse files Browse the repository at this point in the history
Sometimes, after a certificate is renewed, you may want to do something
with the new certificate. For example, you may want to change the
ownership of the certificate or key files, use these files for some
other cryptographic purpose (eg. creating a `.p12` file) or some other
action.

This change allows for a renewal script to be created by specifying
commands with the `step_acme_cert_post_renewal_commands` variable.

An example of this is for provisioning a certificate for UniFi's
Controller. The following configuration will update unifi's jks and
restart the service after the certificate is renewed:

```yaml
step_acme_cert_post_renewal_commands:
  - openssl pkcs12 -export -in "${CERT_FILE}" -inkey "${KEY_FILE}" -out /etc/ssl/cert.p12 -name unifi -password pass:aircontrolenterprise
  - keytool -importkeystore -deststorepass aircontrolenterprise -destkeypass aircontrolenterprise -destkeystore /usr/lib/unifi/data/keystore -srckeystore /etc/ssl/cert.p12 -srcstoretype PKCS12 -srcstorepass aircontrolenterprise -alias unifi
  - systemctl restart unifi
```

`systemctl try-reload-or-restart {{step_acme_cert_renewal_reload_services}}`
has been removed from the `ExecStart` command in the systemd unit file,
and is appended to the end of this post-renewal hook script.

In the example above, I am using `systemctl restart unifi` as the last
command, because I have experienced issues with
`systemctl try-reload-or-restart` for this specific service. For a more
'normal' service, the following should work:

```yaml
step_acme_cert_post_renewal_commands:
  - do_something ${CERT_FILE} ${KEY_FILE}
step_acme_cert_renewal_reload_services:
  - some_service
```

The variables `${STEP_CLI}`, `${CERT_FILE}`, and `${KEY_FILE}` are all
exported in the script by default, and are available for use in the
commands.

If no `step_acme_cert_renewal_reload_services` or
`step_acme_cert_post_renewal_commands` are provided, the post-renew-hook
script is not created.

This change is backwards compatible, and will not break existing
configurations.

Signed-off-by: Tom Whitwell <[email protected]>
  • Loading branch information
whi-tw committed Sep 14, 2022
1 parent 7113e87 commit 8671ada
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 1 deletion.
12 changes: 12 additions & 0 deletions roles/step_acme_cert/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ before setting up a renewal service using `step-cli ca renew`s `--daemon` mode.
- Renew the cert when its remaining valid time crosses this threshold
- Default: undefined (uses the smallstep default: 1/3 of the certificates valid duration, i.e. 8 hours for a 24h cert)

##### `step_acme_cert_post_renewal_shell`
- Shell with which to run `step_acme_cert_post_renewal_commands`
- Must be a valid shell
- Default: `/bin/sh`

##### `step_acme_cert_post_renewal_commands`
- Run these commands after a cert renewal
- Must be a list of commands executable with the shell specified in `step_acme_cert_post_renewal_shell`
- `${STEP_CLI}`, `${CERT_FILE}` and `${KEY_FILE}` are available for use. All are absolute paths to files.
- Example: `["cp ${CERT_FILE} /path/to/somewhere/", "chmod 400 ${KEY_FILE}"]`
- Default: `[]`

##### `step_acme_cert_renewal_reload_services`
- Reload or restart these systemd services after a cert renewal
- Must be a list of systemd units
Expand Down
2 changes: 2 additions & 0 deletions roles/step_acme_cert/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ step_acme_cert_keyfile_defaults:

step_acme_cert_renewal_service: step-renew
#step_acme_cert_renewal_when: 8h
step_acme_cert_post_renewal_shell: "/bin/sh"
step_acme_cert_post_renewal_commands: []
step_acme_cert_renewal_reload_services: []
15 changes: 15 additions & 0 deletions roles/step_acme_cert/meta/argument_specs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,21 @@ argument_specs:
description:
- Renew the cert when its remaining valid time crosses this threshold
- Uses the smallstep default (1/3 of the certs valid duration) if left undefined
step_acme_cert_post_renewal_shell:
type: str
default: /bin/sh
description:
- Shell with which to run commands specified in "step_acme_cert_post_renewal_commands"
- Must be a valid shell
step_acme_cert_post_renewal_commands:
type: list
elements: str
default: []
description:
- Run these commands after a cert renewal
- Must be a list of commands executable with the shell specified in "step_acme_cert_post_renewal_shell"
- "${STEP_CLI}, ${CERT_FILE} and ${KEY_FILE} are available for use. All will be absolute paths to files."
- "Example: C(['cp ${CERT_FILE} /path/to/somewhere/', 'chmod 400 ${KEY_FILE}'])"
step_acme_cert_renewal_reload_services:
type: list
elements: str
Expand Down
8 changes: 8 additions & 0 deletions roles/step_acme_cert/tasks/renewal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
changed_when: no
check_mode: no

- name: Post renewal hook script is present
template:
src: step-post-renew-hook.sh.j2
dest: "{{step_cli_steppath}}/{{ step_acme_cert_renewal_service }}_post.sh"
owner: root
group: root
mode: 0744

- name: Renewal service is installed
template:
src: step-renew.service.j2
Expand Down
12 changes: 12 additions & 0 deletions roles/step_acme_cert/templates/step-post-renew-hook.sh.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!{{ step_acme_cert_post_renewal_shell }}
####### added by ansible: maxhoesel.smallstep.step_acme_cert - changes will be overwritten #######
set -eu
export STEP_CLI="{{ step_cli_executable_absolute.stdout }}"
export CERT_FILE="{{ step_acme_cert_certfile_full.path }}"
export KEY_FILE="{{ step_acme_cert_keyfile_full.path }}"
{% for command in step_acme_cert_post_renewal_commands -%}
{{ command }}
{% endfor -%}
{% if step_acme_cert_renewal_reload_services -%}
systemctl try-reload-or-restart {{ step_acme_cert_renewal_reload_services | join(' ') }}
{% endif -%}
2 changes: 1 addition & 1 deletion roles/step_acme_cert/templates/step-renew.service.j2
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Type=simple
Restart=always
RestartSec=1
Environment=STEPPATH={{ step_cli_steppath }}
ExecStart={{ step_cli_executable_absolute.stdout }} ca renew {{ step_acme_cert_certfile_full.path }} {{ step_acme_cert_keyfile_full.path }} --daemon --force{% if step_acme_cert_renewal_when is defined %} --expires-in {{ step_acme_cert_renewal_when }}{% endif %}{% if step_acme_cert_renewal_reload_services %} --exec "systemctl try-reload-or-restart {{ step_acme_cert_renewal_reload_services | join(' ') }}"{% endif %}
ExecStart={{ step_cli_executable_absolute.stdout }} ca renew {{ step_acme_cert_certfile_full.path }} {{ step_acme_cert_keyfile_full.path }} --daemon --force{% if step_acme_cert_renewal_when is defined %} --expires-in {{ step_acme_cert_renewal_when }}{% endif %}{% if step_acme_cert_post_renewal_commands or step_acme_cert_renewal_reload_services %} --exec "{{ step_acme_cert_post_renewal_shell }} {{ step_cli_steppath }}/{{ step_acme_cert_renewal_service }}_post.sh"{% endif %}

[Install]
WantedBy=multi-user.target

0 comments on commit 8671ada

Please sign in to comment.