Rewrite __letsencrypt_cert type

This commit adds the following features:

 * Ability to expand existing certificate
 * Ability to manage object state
 * Ability to obtain test certificate
 * Ability to promote test certificate to production
 * Ability to specify custom certificate name
 * Ability to specify multiple domains per certificate
 * Ability to use Certbot in standalone mode
 * Messaging

This commit also introduces the following behavioral changes:

 * Attempt to install Certbot only when it is not installed
   already
 * Installation of the cron job has to be enabled using
   `--automatic-renewal` parameter

**Note:** Object ID is now treated as certificate name and new
required parameter `--domain` was added.
This commit is contained in:
lubo 2018-05-07 12:57:48 +02:00
parent 543bc8fed9
commit 22d570ae60
14 changed files with 239 additions and 105 deletions

View file

@ -0,0 +1,3 @@
#!/bin/sh -e
command -v certbot 2>/dev/null || true

View file

@ -0,0 +1,4 @@
#!/bin/sh -e
certbot certificates --cert-name "${__object_id:?}" | grep ' Domains: ' | \
cut -d ' ' -f 6- | tr ' ' '\n'

View file

@ -0,0 +1,7 @@
#!/bin/sh -e
if certbot certificates | grep -q " Certificate Name: ${__object_id:?}$"; then
echo yes
else
echo no
fi

View file

@ -0,0 +1,8 @@
#!/bin/sh -e
if certbot certificates --cert-name "${__object_id:?}" | \
grep -q 'INVALID: TEST_CERT'; then
echo yes
else
echo no
fi

View file

@ -1,5 +0,0 @@
domain=$__object_id
if [ -f "/etc/letsencrypt/live/$domain/fullchain.pem" ]; then
echo yes
fi

85
cdist/conf/type/__letsencrypt_cert/gencode-remote Normal file → Executable file
View file

@ -1,18 +1,75 @@
domain="$__object_id" #!/bin/sh -e
exists=$(cat "$__object/explorer/exists") certificate_exists=$(cat "${__object:?}/explorer/certificate-exists")
webroot="$(cat "$__object/parameter/webroot")" name="${__object_id:?}"
admin_email="$(cat "$__object/parameter/admin-email")" state=$(cat "${__object}/parameter/state")
if [ -n "$exists" ]; then case "${state}" in
exit 0 absent)
fi if [ "${certificate_exists}" = "no" ]; then
exit 0
fi
cat <<EOF echo "certbot delete --cert-name '${name}' --quiet"
if [ ! -d "$webroot" ]; then
mkdir -p "$webroot"
fi
certbot certonly -n --agree-tos --email '$admin_email' --quiet --webroot \ echo remove >> "${__messages_out:?}"
-w '$webroot' -d '$domain' ;;
EOF present)
requested_domains="${__object}/parameter/domain"
staging=no
if [ -f "${__object}/parameter/staging" ]; then
staging=yes
fi
if [ "${certificate_exists}" = "yes" ]; then
existing_domains="${__object}/explorer/certificate-domains"
certificate_is_test=$(cat "${__object}/explorer/certificate-is-test")
sort -uo "${requested_domains}" "${requested_domains}"
sort -uo "${existing_domains}" "${existing_domains}"
if [ -z "$(comm -23 "${requested_domains}" "${existing_domains}")" ] && \
[ "${certificate_is_test}" = "${staging}" ]; then
exit 0
fi
fi
admin_email="$(cat "$__object/parameter/admin-email")"
webroot="$(cat "$__object/parameter/webroot")"
cat <<-EOF
certbot certonly \
--agree-tos \
--cert-name '${name}' \
--email '${admin_email}' \
--expand \
--non-interactive \
--quiet \
$(if [ "${staging}" = "yes" ]; then
echo "--staging"
elif [ "${certificate_is_test}" != "${staging}" ]; then
echo "--force-renewal"
fi) \
$(if [ -z "${webroot}" ]; then
echo "--standalone"
else
echo "--webroot --webroot-path '${webroot}'"
fi) \
$(while read -r domain; do
echo "--domain '${domain}' \\"
done < "${requested_domains}")
EOF
if [ "${certificate_exists}" = "no" ]; then
echo create >> "${__messages_out}"
else
echo change >> "${__messages_out}"
fi
;;
*)
echo "Unsupported state: ${state}" >&2
exit 1
;;
esac

View file

@ -3,54 +3,95 @@ cdist-type__letsencrypt_cert(7)
NAME NAME
---- ----
cdist-type__letsencrypt_cert - Get an SSL certificate from Let's Encrypt
cdist-type__letsencrypt_cert - Get an SSL certificate from Let's Encrypt
DESCRIPTION DESCRIPTION
----------- -----------
Automatically obtain a Let's Encrypt SSL certificate. Uses certbot's webroot
method. You must set up your web server to work with webroot.
Automatically obtain a Let's Encrypt SSL certificate using Certbot.
REQUIRED PARAMETERS REQUIRED PARAMETERS
------------------- -------------------
webroot
The path to your webroot, as set up in your webserver config.
admin-email admin-email
Where to send Let's Encrypt emails like "certificate needs renewal". Where to send Let's Encrypt emails like "certificate needs renewal".
REQUIRED MULTIPLE PARAMETERS
----------------------------
domain
A domain to be included in the certificate.
OPTIONAL PARAMETERS OPTIONAL PARAMETERS
------------------- -------------------
None.
state
'present' or 'absent', defaults to 'present' where:
present
if the certificate does not exist, it will be obtained
absent
the certificate will be removed
webroot
The path to your webroot, as set up in your webserver config. If this
parameter is not present, Certbot will be run in standalone mode.
OPTIONAL MULTIPLE PARAMETERS OPTIONAL MULTIPLE PARAMETERS
---------------------------- ----------------------------
renew-hook renew-hook
Renew hook command directly passed to certbot in cron job. Renew hook command directly passed to Certbot in cron job.
BOOLEAN PARAMETERS
------------------
automatic-renewal
Install a cron job, which attempts to renew certificates daily.
staging
Obtain a test certificate from a staging server.
MESSAGES
--------
change
Certificte was changed.
create
Certificte was created.
remove
Certificte was removed.
EXAMPLES EXAMPLES
-------- --------
.. code-block:: sh .. code-block:: sh
__letsencrypt_cert example.com --admin-email root@example.com --webroot /data/letsencrypt/root __letsencrypt_cert example.com \
--admin-email root@example.com \
__letsencrypt_cert example.com --admin-email root@example.com --webroot /data/letsencrypt/root --renew-hook "service nginx reload" --automatic-renewal \
--domain example.com \
--domain foo.example.com \
--domain bar.example.com \
--renew-hook "service nginx reload" \
--webroot /data/letsencrypt/root
AUTHORS AUTHORS
------- -------
| Nico Schottelius <nico-cdist--@--schottelius.org> | Nico Schottelius <nico-cdist--@--schottelius.org>
| Kamila Součková <kamila--@--ksp.sk> | Kamila Součková <kamila--@--ksp.sk>
| Darko Poljak <darko.poljak--@--gmail.com> | Darko Poljak <darko.poljak--@--gmail.com>
| Ľubomír Kučera <lubomir.kucera.jr at gmail.com>
COPYING COPYING
------- -------
Copyright \(C) 2017 Nico Schottelius, Kamila Součková, Darko Poljak. You can redistribute it
and/or modify it under the terms of the GNU General Public License as Copyright \(C) 2017-2018 Nico Schottelius, Kamila Součková, Darko Poljak and
published by the Free Software Foundation, either version 3 of the Ľubomír Kučera. You can redistribute it and/or modify it under the terms of
License, or (at your option) any later version. the GNU General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.

152
cdist/conf/type/__letsencrypt_cert/manifest Normal file → Executable file
View file

@ -1,79 +1,93 @@
os=$(cat "$__global/explorer/os") #!/bin/sh
os_version=$(cat "$__global/explorer/os_version")
case "$os" in certbot_fullpath="$(cat "${__object:?}/explorer/certbot-path")"
debian)
case "$os_version" in
8*)
__apt_source jessie-backports \
--uri http://http.debian.net/debian \
--distribution jessie-backports \
--component main
require="__apt_source/jessie-backports" __package_apt python-certbot --target-release jessie-backports if [ -z "${certbot_fullpath}" ]; then
require="__apt_source/jessie-backports" __package_apt certbot --target-release jessie-backports os="$(cat "${__global:?}/explorer/os")"
# Seems to be a missing dependency on debian 8 os_version="$(cat "${__global}/explorer/os_version")"
__package python-ndg-httpsclient
;;
9*)
__apt_source stretch-backports \
--uri http://http.debian.net/debian \
--distribution stretch-backports \
--component main
require="__apt_source/stretch-backports" __package_apt python-certbot --target-release stretch-backports case "$os" in
require="__apt_source/stretch-backports" __package_apt certbot --target-release stretch-backports debian)
;; case "$os_version" in
*) 8*)
echo "Unsupported OS version: $os_version" >&2 __apt_source jessie-backports \
exit 1 --uri http://http.debian.net/debian \
;; --distribution jessie-backports \
esac --component main
certbot_fullpath=/usr/bin/certbot require="__apt_source/jessie-backports" __package_apt python-certbot \
;; --target-release jessie-backports
devuan) require="__apt_source/jessie-backports" __package_apt certbot \
case "$os_version" in --target-release jessie-backports
jessie) # Seems to be a missing dependency on debian 8
__apt_source jessie-backports \ __package python-ndg-httpsclient
--uri http://auto.mirror.devuan.org/merged \ ;;
--distribution jessie-backports \ 9*)
--component main __apt_source stretch-backports \
--uri http://http.debian.net/debian \
--distribution stretch-backports \
--component main
require="__apt_source/jessie-backports" __package_apt python-certbot --target-release jessie-backports require="__apt_source/stretch-backports" __package_apt python-certbot \
require="__apt_source/jessie-backports" __package_apt certbot --target-release jessie-backports --target-release stretch-backports
# Seems to be a missing dependency on debian 8 require="__apt_source/stretch-backports" __package_apt certbot \
__package python-ndg-httpsclient --target-release stretch-backports
;; ;;
*) *)
echo "Unsupported OS version: $os_version" >&2 echo "Unsupported OS version: $os_version" >&2
exit 1 exit 1
;; ;;
esac esac
certbot_fullpath=/usr/bin/certbot certbot_fullpath=/usr/bin/certbot
;; ;;
freebsd) devuan)
__package py27-certbot case "$os_version" in
jessie)
__apt_source jessie-backports \
--uri http://auto.mirror.devuan.org/merged \
--distribution jessie-backports \
--component main
certbot_fullpath=/usr/local/bin/certbot require="__apt_source/jessie-backports" __package_apt python-certbot \
;; --target-release jessie-backports
*) require="__apt_source/jessie-backports" __package_apt certbot \
echo "Unsupported os: $os" >&2 --target-release jessie-backports
exit 1 # Seems to be a missing dependency on debian 8
;; __package python-ndg-httpsclient
esac ;;
*)
echo "Unsupported OS version: $os_version" >&2
exit 1
;;
esac
renew_hook_param="$__object/parameter/renew-hook" certbot_fullpath=/usr/bin/certbot
renew_hook="" ;;
if [ -f "$renew_hook_param" ]; then freebsd)
while read hook; do __package py27-certbot
renew_hook="$renew_hook --renew-hook \"$hook\""
done < "$renew_hook_param" certbot_fullpath=/usr/local/bin/certbot
;;
*)
echo "Unsupported os: $os" >&2
exit 1
;;
esac
fi fi
__cron letsencrypt-certbot \ if [ -f "${__object}/parameter/automatic-renewal" ]; then
--user root \ renew_hook_param="${__object}/parameter/renew-hook"
--command "$certbot_fullpath renew -q $renew_hook" \ renew_hook=""
--hour 0 \ if [ -f "${renew_hook_param}" ]; then
--minute 47 while read hook; do
renew_hook="${renew_hook} --renew-hook \"${hook}\""
done < "${renew_hook_param}"
fi
__cron letsencrypt-certbot \
--user root \
--command "${certbot_fullpath} renew -q ${renew_hook}" \
--hour 0 \
--minute 47
fi

View file

@ -0,0 +1,2 @@
automatic-renewal
staging

View file

@ -0,0 +1 @@
present

View file

@ -0,0 +1,2 @@
state
webroot

View file

@ -1,2 +1 @@
admin-email admin-email
webroot

View file

@ -0,0 +1 @@
domain