diff --git a/type/__uacme_account/gencode-remote b/type/__uacme_account/gencode-remote new file mode 100644 index 0000000..e1d9551 --- /dev/null +++ b/type/__uacme_account/gencode-remote @@ -0,0 +1,32 @@ +#!/bin/sh + +os="$(cat "${__global:?}"/explorer/os)" + +case "$os" in + alpine|ubuntu|debian) + default_confdir=/etc/ssl/uacme + ;; + *) + echo "This type currently has no implementation for $os. Aborting." >&2; + exit 1 + ;; +esac + +admin_mail= +if [ -f "${__object:?}/parameter/admin-mail" ]; +then + admin_mail="$(cat "${__object:?}/parameter/admin-mail")"; +fi + +confdir="${default_confdir:?}" +if [ -f "${__object:?}/parameter/confdir" ]; +then + confdir="$(cat "${__object:?}/parameter/confdir")" +fi + +cat << EOF +if ! [ -f "${confdir}/private/key.pem" ]; +then + uacme -y new ${admin_mail} +fi +EOF diff --git a/type/__uacme_account/man.rst b/type/__uacme_account/man.rst new file mode 100644 index 0000000..a2539ff --- /dev/null +++ b/type/__uacme_account/man.rst @@ -0,0 +1,50 @@ +cdist-type__uacme_account(7) +============================ + +NAME +---- +cdist-type__uacme_account - Install uacme and register Let's Encrypt account. + + +DESCRIPTION +----------- +This type is used to bootstrap acquiring certificates from the Let's Encrypt +C.A. This type is expected to be used internally, by the `__uacme_obtain`. + + +OPTIONAL PARAMETERS +------------------- +confdir + An alternative configuration directory for uacme's private keys and + certificates. + +admin-mail + Administrative contact email to register the account with. + +EXAMPLES +-------- + +.. code-block:: sh + + # Create account with default settings for the OS. + __uacme_account + + # Create an account with email and custom location. + __uacme_account --confdir /opt/custom/uacme --admin-mail admin@domain.tld + + +SEE ALSO +-------- +:strong:`cdist-type__letsencrypt_cert`\ (7) +:strong:`cdist-type__uacme_obtain`\ (7) + +AUTHORS +------- +Joachim Desroches + +COPYING +------- +Copyright \(C) 2020 Joachim Desroches. 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. diff --git a/type/__uacme_account/manifest b/type/__uacme_account/manifest new file mode 100644 index 0000000..3f17d5e --- /dev/null +++ b/type/__uacme_account/manifest @@ -0,0 +1,3 @@ +#!/bin/sh + +__package uacme diff --git a/type/__uacme_account/parameter/optional b/type/__uacme_account/parameter/optional new file mode 100644 index 0000000..0eaba67 --- /dev/null +++ b/type/__uacme_account/parameter/optional @@ -0,0 +1,2 @@ +confdir +admin-mail diff --git a/type/__uacme_account/singleton b/type/__uacme_account/singleton new file mode 100644 index 0000000..e69de29 diff --git a/type/__uacme_obtain/files/renew.sh.sh b/type/__uacme_obtain/files/renew.sh.sh new file mode 100755 index 0000000..163ae4b --- /dev/null +++ b/type/__uacme_obtain/files/renew.sh.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +cat << EOF +#!/bin/sh + +set -x + +CERT_SOURCE=$CONFDIR/$MAIN_DOMAIN/cert.pem +KEY_SOURCE=$CONFDIR/private/$MAIN_DOMAIN/key.pem + +export UACME_CHALLENGE_PATH=$CHALLENGEDIR + +# Issue certificate. +uacme issue -c $CONFDIR -h $HOOKSCRIPT $DISABLE_OCSP $MUST_STABLE $KEYTYPE \ +$DOMAIN +if [ $? -eq 2 ]; then + # Note: exit code 0 means that certificate was issued. + # Note: exit code 1 means that certificate was still valid, hence not renewed. + # Note: exit code 2 means that something went wrong. + echo "Failed to renew certificate - exiting." >&2 + exit 1 +fi + +# Re-deploy, if needed. +if [ -n "$KEY_TARGET" ] && [ -n "$CERT_TARGET" ]; then + set -e + + mkdir -p $(dirname "$CERT_TARGET") $(dirname "$KEY_TARGET") + + if ! cmp \$CERT_SOURCE $CERT_TARGET >/dev/null 2>&1; then + install -m 0640 \$KEY_SOURCE $KEY_TARGET + install -m 0644 \$CERT_SOURCE $CERT_TARGET + chown $OWNER $KEY_TARGET $CERT_TARGET + + $RENEW_HOOK + fi +fi +EOF diff --git a/type/__uacme_obtain/gencode-remote b/type/__uacme_obtain/gencode-remote new file mode 100644 index 0000000..10d0644 --- /dev/null +++ b/type/__uacme_obtain/gencode-remote @@ -0,0 +1,23 @@ +#!/bin/sh + +# FIXME: this code is duplicated from the manifest -> let's share it somehow. + +os="$(cat "${__global:?}"/explorer/os)" +case "$os" in + alpine|ubuntu|debian) + default_confdir=/etc/ssl/uacme + ;; + *) + echo "__uacme_obtain currently has no implementation for $os. Aborting." >&2; + exit 1; + ;; +esac + +confdir="${default_confdir:?}" +if [ -f "${__object:?}/parameter/confdir" ]; +then + confdir="$(cat "${__object:?}/parameter/confdir")" +fi + +# Run renew script 'by hand' - renews will be handled by the cronjob. +echo "$confdir/${__object_id:?}/renew.sh" diff --git a/type/__uacme_obtain/man.rst b/type/__uacme_obtain/man.rst new file mode 100644 index 0000000..4063107 --- /dev/null +++ b/type/__uacme_obtain/man.rst @@ -0,0 +1,81 @@ +cdist-type__uacme_obtain(7) +======================== + +NAME +---- +cdist-type__uacme_obtain - obtain, renew and deploy Let's Encrypt certificates + +DESCRIPTION +----------- + +This type leverage uacme to issue abd renew Let's Encrypt certificates and +provides a simple deployment mechanism. It is expected to be called after +`__uacme_account`. + +REQUIRED PARAMETERS +------------------- +None. + +OPTIONAL PARAMETERS +------------------- +challengedir + Path to publicly available (served by a third-party HTTP server, under + `$DOMAIN/.well-known/acme-challenge`) challenge directory. + +confdir + uacme configuration directory. + +hookscript + Path to challenge hook program. + +owner + Owner of installed certificate (e.g. `www-data`), pass as argument to `chown`. + +install-cert-to + Installation path of the issued certificate. + +install-key-to + Installation path of the certificate's private key. + +renew-hook + Renew hook executed on certificate renewal (e.g. `service nginx reload`). + +force-cert-ownership-to + Override default ownership for TLS certificate, passed as argument to chown. + +OPTIONAL MULTIPLE PARAMETERS +------------------- +altdomains + Alternative domain names for this certificate. + +BOOLEAN PARAMETERS +------------------ +no-ocsp + When this flag is *not* specified and the certificate has an Authority + Information Access extension with an OCSP server location *uacme* makes an + OCSP request to the server; if the certificate is reported as revoked *uacme* + forces reissuance regardless of the expiration date. + +must-staple + Request certificates with the RFC7633 Certificate Status Request + TLS Feature Extension, informally also known as "OCSP Must-Staple". + +use-rsa + Use RSA instead of EC for the private key. Only applies to newly generated keys. + +SEE ALSO +-------- +:strong:`cdist-type__letsencrypt_cert`\ (7) +:strong:`cdist-type__uacme_account`\ (7) + +AUTHORS +------- +Joachim Desroches +Timothée Floure + +COPYING +------- +Copyright \(C) 2020 Joachim Desroches. 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. diff --git a/type/__uacme_obtain/manifest b/type/__uacme_obtain/manifest new file mode 100644 index 0000000..59ddce8 --- /dev/null +++ b/type/__uacme_obtain/manifest @@ -0,0 +1,121 @@ +#!/bin/sh + +os="$(cat "${__global:?}"/explorer/os)" +case "$os" in + alpine|ubuntu|debian) + __package uacme + + default_challengedir=/var/www/.well-known/acme-challenge + default_hookscript=/usr/share/uacme/uacme.sh + default_confdir=/etc/ssl/uacme + ;; + *) + echo "__uacme_obtain currently has no implementation for $os. Aborting." >&2; + exit 1; + ;; +esac + +CHALLENGEDIR=$default_challengedir +if [ -f "${__object:?}/parameter/challengedir" ]; +then + CHALLENGEDIR="$(cat "${__object:?}/parameter/challengedir")" +fi +export CHALLENGEDIR + +CONFDIR="${default_confdir:?}" +if [ -f "${__object:?}/parameter/confdir" ]; +then + CONFDIR="$(cat "${__object:?}/parameter/confdir")" +fi +export CONFDIR + +DISABLE_OCSP= +if [ -f "${__object:?}/parameter/no-ocsp" ]; +then + DISABLE_OCSP="--no-ocsp" +fi +export DISABLE_OCSP + +MAIN_DOMAIN=${__object_id:?} +DOMAIN=$MAIN_DOMAIN +if [ -f "${__object:?}/parameter/altdomains" ]; +then + # shellcheck disable=SC2013 + for altdomain in $(cat "${__object:?}/parameter/altdomains"); + do + DOMAIN="$DOMAIN $altdomain" + done +fi +export MAIN_DOMAIN DOMAIN + +HOOKSCRIPT=${default_hookscript} +if [ -f "${__object:?}/parameter/hookscript" ]; +then + HOOKSCRIPT="$(cat "${__object:?}/parameter/hookscript")" +fi +export HOOKSCRIPT + +KEYTYPE="-t EC" +if [ -f "${__object:?}/parameter/use-rsa" ]; +then + KEYTYPE="-t RSA" +fi +export KEYTYPE + +MUST_STAPLE= +if [ -f "${__object:?}/parameter/must-staple" ]; +then + MUST_STAPLE="--must-staple" +fi +export MUST_STAPLE + +OWNER=root +if [ -f "${__object:?}/parameter/owner" ]; +then + OWNER="$(cat "${__object:?}/parameter/owner")" +fi +export OWNER + +KEY_TARGET= +if [ -f "${__object:?}/parameter/install-key-to" ]; +then + KEY_TARGET="$(cat "${__object:?}/parameter/install-key-to")" +fi +export KEY_TARGET + +CERT_TARGET= +if [ -f "${__object:?}/parameter/install-cert-to" ]; +then + CERT_TARGET="$(cat "${__object:?}/parameter/install-cert-to")" +fi +export CERT_TARGET + +RENEW_HOOK= +if [ -f "${__object:?}/parameter/renew-hook" ]; +then + RENEW_HOOK="$(cat "${__object:?}/parameter/renew-hook")" +fi +export RENEW_HOOK + +if [ -n "$KEY_TARGET" ] && [ -z "$CERT_TARGET" ]; then + echo "You cannot specify --install-key-to without --install-cert-to." >&2 + exit 1 +elif [ -z "$KEY_TARGET" ] && [ -n "$CERT_TARGET" ]; then + echo "You cannot specify --install-cert-to without --install-key-to." >&2 + exit 1 +fi + +# Make sure challengedir exist. +__directory "$CHALLENGEDIR" --parents + +# Generate and deploy renew script. +mkdir -p "${__object:?}/files" +"${__type:?}/files/renew.sh.sh" > "${__object:?}/files/uacme-renew.sh" + +__directory "$CONFDIR/$MAIN_DOMAIN" +require="__directory/$CONFDIR/$MAIN_DOMAIN" __file "$CONFDIR/$MAIN_DOMAIN/renew.sh" \ + --mode 0755 --source "${__object:?}/files/uacme-renew.sh" + +# Set up renew cronjob - initial issue done in gencode-remote. +__cron "uacme-$MAIN_DOMAIN" --user root --hour 2 --minute 0 \ + --command "$CONFDIR/$MAIN_DOMAIN/renew.sh" diff --git a/type/__uacme_obtain/parameter/boolean b/type/__uacme_obtain/parameter/boolean new file mode 100644 index 0000000..44c04f4 --- /dev/null +++ b/type/__uacme_obtain/parameter/boolean @@ -0,0 +1,3 @@ +no-ocsp +must-staple +use-rsa diff --git a/type/__uacme_obtain/parameter/optional b/type/__uacme_obtain/parameter/optional new file mode 100644 index 0000000..fd721af --- /dev/null +++ b/type/__uacme_obtain/parameter/optional @@ -0,0 +1,7 @@ +challengedir +confdir +hookscript +owner +install-cert-to +install-key-to +renew-hook diff --git a/type/__uacme_obtain/parameter/optional_multiple b/type/__uacme_obtain/parameter/optional_multiple new file mode 100644 index 0000000..d3ebd88 --- /dev/null +++ b/type/__uacme_obtain/parameter/optional_multiple @@ -0,0 +1 @@ +altdomains