diff options
| author | Lexi Winter <ivy@FreeBSD.org> | 2025-06-03 10:49:05 +0100 |
|---|---|---|
| committer | Lexi Winter <ivy@FreeBSD.org> | 2025-06-03 10:49:05 +0100 |
| commit | 99151a2db842a850a2860af3e77532370802ca69 (patch) | |
| tree | a43f4ff44edd47a267a1a991046b26412dab00c0 | |
| parent | 29d14ef9b7b4c116e3cce031150d848d8e1c14eb (diff) | |
| download | lfacme-99151a2db842a850a2860af3e77532370802ca69.tar.gz lfacme-99151a2db842a850a2860af3e77532370802ca69.tar.bz2 | |
make the challenge handler configurable
perhaps one day we'll even support something other than Kerberos!
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Makefile | 38 | ||||
| -rw-r--r-- | acme.conf.5 | 4 | ||||
| -rw-r--r-- | domains.conf.5 | 19 | ||||
| -rw-r--r-- | domains.conf.sample | 10 | ||||
| -rw-r--r-- | init.sh | 54 | ||||
| -rw-r--r-- | kerberos.sh (renamed from kerberos-challenge.sh) | 0 | ||||
| -rw-r--r-- | lfacme-renew.sh | 38 |
8 files changed, 130 insertions, 34 deletions
@@ -1 +1,2 @@ .*.sw? +/dist @@ -9,25 +9,27 @@ MAN5DIR?= ${MANDIR}/man5 MAN8DIR?= ${MANDIR}/man8 HOOKDIR?= ${CONFDIR}/hooks -LIBMODE?= 0755 -LIB_FILES= init.sh \ - kerberos-challenge.sh +LIBMODE?= 0644 +LIB= init.sh + +CHALLENGEMODE?= 0755 +CHALLENGE= kerberos.sh BINMODE?= 0755 -BIN_FILES= lfacme-renew.sh \ +BIN= lfacme-renew.sh \ lfacme-setup.sh CONFMODE?= 0644 -CONF_FILES= acme.conf.sample \ +CONF= acme.conf.sample \ domains.conf.sample HOOKMODE?= 0755 -HOOK_FILES= example-hook.sh +HOOK= example-hook.sh MANMODE?= 0644 -MAN5FILES= acme.conf.5 \ +MAN5= acme.conf.5 \ domains.conf.5 -MAN8FILES= lfacme-renew.8 \ +MAN8= lfacme-renew.8 \ lfacme-setup.8 default: all @@ -37,26 +39,34 @@ all: install: @echo 'create ${LIBDIR}'; install -d ${LIBDIR}; \ - for lib in ${LIB_FILES}; do \ + for lib in ${LIB}; do \ echo "install ${LIBDIR}/$$lib"; \ install -C -m ${LIBMODE} "$$lib" "${LIBDIR}/$$lib"; \ done; \ \ + echo 'create ${LIBDIR}/challenge'; install -d ${LIBDIR}/challenge; \ + for challenge in ${CHALLENGE}; do \ + basename=$${challenge%*.sh}; \ + echo "install ${LIBDIR}/challenge/$$basename"; \ + install -C -m ${CHALLENGEMODE} "$$challenge" \ + "${LIBDIR}/challenge/$$basename"; \ + done; \ + \ echo 'create ${BINDIR}'; install -d ${BINDIR}; \ - for bin in ${BIN_FILES}; do \ + for bin in ${BIN}; do \ basename=$${bin%*.sh}; \ echo "install ${BINDIR}/$$basename"; \ install -C -m ${BINMODE} "$$bin" "${BINDIR}/$$basename"; \ done; \ \ echo 'create ${CONFDIR}'; install -d ${CONFDIR}; \ - for conf in ${CONF_FILES}; do \ + for conf in ${CONF}; do \ echo "install ${CONFDIR}/$$conf"; \ install -C -m ${CONFMODE} "$$conf" "${CONFDIR}/$$conf"; \ done; \ \ echo 'create ${HOOKDIR}'; install -d ${HOOKDIR}; \ - for hook in ${HOOK_FILES}; do \ + for hook in ${HOOK}; do \ basename=$${hook%*.sh}; \ echo "install ${HOOKDIR}/$$basename"; \ install -C -m ${HOOKMODE} "$$hook" "${HOOKDIR}/$$basename"; \ @@ -65,13 +75,13 @@ install: echo 'create ${MANDIR}'; install -d ${MANDIR}; \ \ echo 'create ${MAN5DIR}'; install -d ${MAN5DIR}; \ - for man in ${MAN5FILES}; do \ + for man in ${MAN5}; do \ echo "install ${MAN5DIR}/$$man"; \ install -C -m ${MANMODE} "$$man" "${MAN5DIR}/$$man"; \ done; \ \ echo 'create ${MAN8DIR}'; install -d ${MAN8DIR}; \ - for man in ${MAN8FILES}; do \ + for man in ${MAN8}; do \ echo "install ${MAN8DIR}/$$man"; \ install -C -m ${MANMODE} "$$man" "${MAN8DIR}/$$man"; \ done; \ diff --git a/acme.conf.5 b/acme.conf.5 index f03f777..8643d55 100644 --- a/acme.conf.5 +++ b/acme.conf.5 @@ -31,7 +31,9 @@ The default value is .It Va ACME_KERBEROS_PRINCIPAL The Kerberos principal to use when responding to a .Dq dns-01 -challenge. +challenge with the +.Dq kerberos +challenge handler. The default value is .Dq host/$(hostname) . .El diff --git a/domains.conf.5 b/domains.conf.5 index 0f937a6..1ad0e03 100644 --- a/domains.conf.5 +++ b/domains.conf.5 @@ -44,6 +44,23 @@ to generate a secp384r1 ECDSA key, or to generate a 3072-bit RSA key. If not specified, the default value is .Dq ec . +.It Sy challenge Ns Li = Ns Ar filename +Invoke +.Ar filename +to handle ACME challenges for this certificate. +If +.Ar filename +begins with a +.Sq / +character, then it is assumed to be an absolute path, +otherwise it will be searched for in +.Pa /usr/local/share/lfacme/challenge +and +.Pa /usr/local/etc/lfacme/challenge . +.Pp +The challenge script is passed to +.Xr uacme 1 ; +see the uacme documentation for details on the calling convention. .It Sy hook Ns Li = Ns Ar filename Invoke .Ar filename @@ -66,7 +83,7 @@ which may be one of the following: A certificate has been issued or renewed. .El .Pp -The following environment variables will be when running the hook script: +The following environment variables will be set when running the hook script: .Bl -tag -width LFACME_CERTFILE .It Sy LFACME_CERT The identifier of the certificate, i.e. the first field in diff --git a/domains.conf.sample b/domains.conf.sample index 6dace98..41de581 100644 --- a/domains.conf.sample +++ b/domains.conf.sample @@ -31,6 +31,16 @@ # If <name> begins with a '/' then it is an absolute path, # otherwise it is relative to $ACME_HOOKDIR. # This option may be given multiple times. +# +# challenge=<name> +# Use <name> as the challenge handler. If <name> begins +# with '/' then it is an absolute path, otherwise it will +# be searched for in /usr/local/share/lfacme/challenge/ +# then /usr/local/etc/lfacme/challenge/. +# +# One challenge script is supplied with lfacme, "kerberos", +# which uses Kerberized nsupdate(1) to respond to dns-01 +# challenges. # A certificate name of "*" can be used to set the default options for any # following certificates. For example, to use RSA (instead of the default @@ -25,6 +25,7 @@ _warn() { _BASEDIR="/usr/local" # Where the internal scripts are. _SHARE="${_BASEDIR}/share/lfacme" +_CHALLENGE="${_SHARE}/challenge" # Our configuration directory. This might be overridden by command-line # arguments. @@ -71,3 +72,56 @@ _UACME=/usr/local/bin/uacme _uacme() { "$_UACME" -a "$ACME_URL" -c "$_UACME_DIR" "$@" } + +# Find a challenge script and make sure it's valid. If the challenge name +# begins with a '/' it's a full path, otherwise we search $_CHALLENGE and +# $_CONFDIR/challenge. +_findchallenge() { + local identifier="$1" + local challenge="$2" + local path="" + + if [ "${challenge#/*}" != "$challenge" ]; then + path="${challenge}" + elif [ -f "${_CHALLENGE}/${challenge}" ]; then + path="${_CHALLENGE}/${challenge}" + elif [ -f "${_CONFDIR}/challenge/${challenge}" ]; then + path="${_CONFDIR}/challenge/${challenge}" + else + _error "%s: could not find challenge script '%s'" \ + "$identifier" "$challenge" + return 1 + fi + + if ! [ -x "$path" ]; then + _error "%s: challenge is not executable: %s" \ + "$identifier" "$path" + return 1 + fi + + echo "$path" +} + +# Find a hook script and make sure it's valid. If the hook name begins with a +# '/' it's a full path, otherwise it's relative to ACME_HOOKDIR. +_findhook() { + hook="$1" + + if [ "${hook#/*}" = "$hook" ]; then + hook="${ACME_HOOKDIR}/$hook" + fi + + if ! [ -f "$hook" ]; then + _error "%s: hook does not exist: %s" \ + "$identifier" "$hook" + return 1 + fi + + if ! [ -x "$hook" ]; then + _error "%s: hook is not executable: %s" \ + "$identifier" "$hook" + return 1 + fi + + echo "$hook" +} diff --git a/kerberos-challenge.sh b/kerberos.sh index bd9d9e4..bd9d9e4 100644 --- a/kerberos-challenge.sh +++ b/kerberos.sh diff --git a/lfacme-renew.sh b/lfacme-renew.sh index 0a487d8..787d8da 100644 --- a/lfacme-renew.sh +++ b/lfacme-renew.sh @@ -113,6 +113,7 @@ _docert() { local altnames="" local hooks="" local domain="" + local challenge="" # parse arguments for this cert while ! [ -z "$1" ]; do @@ -123,6 +124,7 @@ _docert() { "$identifier" "${1#type=*}" return 1;; hook=*) hooks="$hooks ${1#hook=*}";; + challenge=*) challenge="${1#challenge=*}";; *=*) _error "%s: unknown option: %s" \ "$identifier" "$1" return 1;; @@ -149,28 +151,28 @@ _docert() { keytype="ec" fi + # Default challenge is kerberos. + if [ -z "$challenge" ]; then + challenge="kerberos" + fi + + # make sure the challenge is valid. + challenge_path="$(_findchallenge "$identifier" "$challenge")" + if [ "$?" -ne 0 ]; then + return 1 + fi + # make sure all the hook scripts are valid. if the hook name - # begins with a '/' it's a full path, otherwise it's related to - # ACME_HOOKDIR. + # begins with a '/' it's a full path, otherwise it's relative + # to ACME_HOOKDIR. local _rhooks="" for hook in $hooks; do - if [ "${hook#/*}" = "$hook" ]; then - hook="${ACME_HOOKDIR}/$hook" - fi - - if ! [ -f "$hook" ]; then - _error "%s: hook does not exist: %s" \ - "$identifier" "$hook" - return 1 - fi - - if ! [ -x "$hook" ]; then - _error "%s: hook is not executable: %s" \ - "$identifier" "$hook" + local _hookpath="$(_findhook "$identifier" "$hook")" + if [ "$?" -ne 0 ]; then return 1 fi - _rhooks="$_rhooks $hook" + _rhooks="$_rhooks $_hookpath" done mkdir -p -m0700 "$dir" @@ -186,8 +188,8 @@ _docert() { return 1 fi - _uacme $_uacme_flags \ - -h "${_SHARE}/kerberos-challenge.sh" \ + _uacme $_uacme_flags \ + -h "$challenge_path" \ issue "$csrfile" _ret=$? |
