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")
webroot="$(cat "$__object/parameter/webroot")"
admin_email="$(cat "$__object/parameter/admin-email")"
certificate_exists=$(cat "${__object:?}/explorer/certificate-exists")
name="${__object_id:?}"
state=$(cat "${__object}/parameter/state")
if [ -n "$exists" ]; then
exit 0
fi
case "${state}" in
absent)
if [ "${certificate_exists}" = "no" ]; then
exit 0
fi
cat <<EOF
if [ ! -d "$webroot" ]; then
mkdir -p "$webroot"
fi
echo "certbot delete --cert-name '${name}' --quiet"
certbot certonly -n --agree-tos --email '$admin_email' --quiet --webroot \
-w '$webroot' -d '$domain'
EOF
echo remove >> "${__messages_out:?}"
;;
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
----
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
-----------
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
-------------------
webroot
The path to your webroot, as set up in your webserver config.
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
-------------------
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
----------------------------
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
--------
.. 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 --webroot /data/letsencrypt/root --renew-hook "service nginx reload"
__letsencrypt_cert example.com \
--admin-email root@example.com \
--automatic-renewal \
--domain example.com \
--domain foo.example.com \
--domain bar.example.com \
--renew-hook "service nginx reload" \
--webroot /data/letsencrypt/root
AUTHORS
-------
| Nico Schottelius <nico-cdist--@--schottelius.org>
| Kamila Součková <kamila--@--ksp.sk>
| Darko Poljak <darko.poljak--@--gmail.com>
| Ľubomír Kučera <lubomir.kucera.jr at gmail.com>
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
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Copyright \(C) 2017-2018 Nico Schottelius, Kamila Součková, Darko Poljak and
Ľubomír Kučera. You can redistribute it and/or modify it under the terms of
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")
os_version=$(cat "$__global/explorer/os_version")
#!/bin/sh
case "$os" in
debian)
case "$os_version" in
8*)
__apt_source jessie-backports \
--uri http://http.debian.net/debian \
--distribution jessie-backports \
--component main
certbot_fullpath="$(cat "${__object:?}/explorer/certbot-path")"
require="__apt_source/jessie-backports" __package_apt python-certbot --target-release jessie-backports
require="__apt_source/jessie-backports" __package_apt certbot --target-release jessie-backports
# Seems to be a missing dependency on debian 8
__package python-ndg-httpsclient
;;
9*)
__apt_source stretch-backports \
--uri http://http.debian.net/debian \
--distribution stretch-backports \
--component main
if [ -z "${certbot_fullpath}" ]; then
os="$(cat "${__global:?}/explorer/os")"
os_version="$(cat "${__global}/explorer/os_version")"
require="__apt_source/stretch-backports" __package_apt python-certbot --target-release stretch-backports
require="__apt_source/stretch-backports" __package_apt certbot --target-release stretch-backports
;;
*)
echo "Unsupported OS version: $os_version" >&2
exit 1
;;
esac
case "$os" in
debian)
case "$os_version" in
8*)
__apt_source jessie-backports \
--uri http://http.debian.net/debian \
--distribution jessie-backports \
--component main
certbot_fullpath=/usr/bin/certbot
;;
devuan)
case "$os_version" in
jessie)
__apt_source jessie-backports \
--uri http://auto.mirror.devuan.org/merged \
--distribution jessie-backports \
--component main
require="__apt_source/jessie-backports" __package_apt python-certbot \
--target-release jessie-backports
require="__apt_source/jessie-backports" __package_apt certbot \
--target-release jessie-backports
# Seems to be a missing dependency on debian 8
__package python-ndg-httpsclient
;;
9*)
__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/jessie-backports" __package_apt certbot --target-release jessie-backports
# Seems to be a missing dependency on debian 8
__package python-ndg-httpsclient
;;
*)
echo "Unsupported OS version: $os_version" >&2
exit 1
;;
esac
require="__apt_source/stretch-backports" __package_apt python-certbot \
--target-release stretch-backports
require="__apt_source/stretch-backports" __package_apt certbot \
--target-release stretch-backports
;;
*)
echo "Unsupported OS version: $os_version" >&2
exit 1
;;
esac
certbot_fullpath=/usr/bin/certbot
;;
freebsd)
__package py27-certbot
certbot_fullpath=/usr/bin/certbot
;;
devuan)
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
;;
*)
echo "Unsupported os: $os" >&2
exit 1
;;
esac
require="__apt_source/jessie-backports" __package_apt python-certbot \
--target-release jessie-backports
require="__apt_source/jessie-backports" __package_apt certbot \
--target-release jessie-backports
# Seems to be a missing dependency on debian 8
__package python-ndg-httpsclient
;;
*)
echo "Unsupported OS version: $os_version" >&2
exit 1
;;
esac
renew_hook_param="$__object/parameter/renew-hook"
renew_hook=""
if [ -f "$renew_hook_param" ]; then
while read hook; do
renew_hook="$renew_hook --renew-hook \"$hook\""
done < "$renew_hook_param"
certbot_fullpath=/usr/bin/certbot
;;
freebsd)
__package py27-certbot
certbot_fullpath=/usr/local/bin/certbot
;;
*)
echo "Unsupported os: $os" >&2
exit 1
;;
esac
fi
__cron letsencrypt-certbot \
--user root \
--command "$certbot_fullpath renew -q $renew_hook" \
--hour 0 \
--minute 47
if [ -f "${__object}/parameter/automatic-renewal" ]; then
renew_hook_param="${__object}/parameter/renew-hook"
renew_hook=""
if [ -f "${renew_hook_param}" ]; then
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
webroot

View File

@ -0,0 +1 @@
domain