From 15010d062ae276a92065cd6ea7dc94b749e20756 Mon Sep 17 00:00:00 2001 From: Lexi Winter Date: Wed, 4 Jun 2025 10:42:19 +0100 Subject: allow PREFIX to be customised --- .gitignore | 3 + 900.lfacme.sh | 29 ------ 900.lfacme.sh.in | 29 ++++++ Makefile | 106 +++++++++++-------- README | 3 - acme.conf.5 | 50 --------- acme.conf.5.in | 50 +++++++++ acme.conf.sample | 86 ---------------- acme.conf.sample.in | 86 ++++++++++++++++ dns.sh | 78 -------------- dns.sh.in | 78 ++++++++++++++ dnsutils.sh | 84 --------------- dnsutils.sh.in | 84 +++++++++++++++ domains.conf.5 | 164 ------------------------------ domains.conf.5.in | 164 ++++++++++++++++++++++++++++++ domains.conf.sample | 33 ------ domains.conf.sample.in | 33 ++++++ example-hook.sh | 27 ----- example-hook.sh.in | 27 +++++ http.sh | 51 ---------- http.sh.in | 51 ++++++++++ init.sh | 158 ----------------------------- init.sh.in | 158 +++++++++++++++++++++++++++++ kerberos.sh | 86 ---------------- kerberos.sh.in | 86 ++++++++++++++++ lfacme-dns.7 | 61 ----------- lfacme-dns.7.in | 61 +++++++++++ lfacme-http.7 | 57 ----------- lfacme-http.7.in | 57 +++++++++++ lfacme-kerberos.7 | 75 -------------- lfacme-kerberos.7.in | 75 ++++++++++++++ lfacme-renew.8 | 48 --------- lfacme-renew.8.in | 48 +++++++++ lfacme-renew.sh | 269 ------------------------------------------------- lfacme-renew.sh.in | 269 +++++++++++++++++++++++++++++++++++++++++++++++++ lfacme-setup.8 | 45 --------- lfacme-setup.8.in | 45 +++++++++ lfacme-setup.sh | 34 ------- lfacme-setup.sh.in | 34 +++++++ lfacme.7 | 76 -------------- lfacme.7.in | 76 ++++++++++++++ 41 files changed, 1579 insertions(+), 1555 deletions(-) delete mode 100644 900.lfacme.sh create mode 100644 900.lfacme.sh.in delete mode 100644 acme.conf.5 create mode 100644 acme.conf.5.in delete mode 100644 acme.conf.sample create mode 100644 acme.conf.sample.in delete mode 100644 dns.sh create mode 100644 dns.sh.in delete mode 100644 dnsutils.sh create mode 100644 dnsutils.sh.in delete mode 100644 domains.conf.5 create mode 100644 domains.conf.5.in delete mode 100644 domains.conf.sample create mode 100644 domains.conf.sample.in delete mode 100644 example-hook.sh create mode 100644 example-hook.sh.in delete mode 100644 http.sh create mode 100644 http.sh.in delete mode 100644 init.sh create mode 100644 init.sh.in delete mode 100644 kerberos.sh create mode 100644 kerberos.sh.in delete mode 100644 lfacme-dns.7 create mode 100644 lfacme-dns.7.in delete mode 100644 lfacme-http.7 create mode 100644 lfacme-http.7.in delete mode 100644 lfacme-kerberos.7 create mode 100644 lfacme-kerberos.7.in delete mode 100644 lfacme-renew.8 create mode 100644 lfacme-renew.8.in delete mode 100644 lfacme-renew.sh create mode 100644 lfacme-renew.sh.in delete mode 100644 lfacme-setup.8 create mode 100644 lfacme-setup.8.in delete mode 100644 lfacme-setup.sh create mode 100644 lfacme-setup.sh.in delete mode 100644 lfacme.7 create mode 100644 lfacme.7.in diff --git a/.gitignore b/.gitignore index 89c1dbe..5ddfa4c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ .*.sw? /dist +*.[12345678] +*.sh +*.sample diff --git a/900.lfacme.sh b/900.lfacme.sh deleted file mode 100644 index dabb890..0000000 --- a/900.lfacme.sh +++ /dev/null @@ -1,29 +0,0 @@ -#! /bin/sh -# This source code is released into the public domain. -# -# Run lfacme-renew to renew ACME certificates. - -if [ -r /etc/defaults/periodic.conf ]; then - . /etc/defaults/periodic.conf - source_periodic_confs -fi - -PATH=$PATH:/usr/local/bin:/usr/local/sbin -export PATH - -# Exit if lfacme isn't installed but the periodic script was left over -# for some reason. -if ! [ -x /usr/local/sbin/lfacme-renew ]; then - exit 0 -fi - -case "$daily_lfacme_enable" in -[Yy][Ee][Ss]) - printf 'Renewing ACME certificates with lfacme:\n' - - /usr/local/sbin/lfacme-renew - ;; - -*) - ;; -esac diff --git a/900.lfacme.sh.in b/900.lfacme.sh.in new file mode 100644 index 0000000..f1fae33 --- /dev/null +++ b/900.lfacme.sh.in @@ -0,0 +1,29 @@ +#! /bin/sh +# This source code is released into the public domain. +# +# Run lfacme-renew to renew ACME certificates. + +if [ -r /etc/defaults/periodic.conf ]; then + . /etc/defaults/periodic.conf + source_periodic_confs +fi + +PATH=$PATH:__PREFIX__/bin:__PREFIX__/sbin +export PATH + +# Exit if lfacme isn't installed but the periodic script was left over +# for some reason. +if ! [ -x __BINDIR__/lfacme-renew ]; then + exit 0 +fi + +case "$daily_lfacme_enable" in +[Yy][Ee][Ss]) + printf 'Renewing ACME certificates with lfacme:\n' + + __BINDIR__/lfacme-renew + ;; + +*) + ;; +esac diff --git a/Makefile b/Makefile index 007b02c..13e4895 100644 --- a/Makefile +++ b/Makefile @@ -3,10 +3,11 @@ PREFIX?= /usr/local DESTDIR?= -LIBDIR?= ${DESTDIR}${PREFIX}/share/lfacme -BINDIR?= ${DESTDIR}${PREFIX}/sbin -CONFDIR?= ${DESTDIR}${PREFIX}/etc -MANDIR?= ${DESTDIR}${PREFIX}/share/man +LIBDIR?= ${PREFIX}/share/lfacme +BINDIR?= ${PREFIX}/sbin +CONFDIR?= ${PREFIX}/etc +MANDIR?= ${PREFIX}/share/man +PERIODICDIR= ${PREFIX}/etc/periodic/daily MAN5DIR?= ${MANDIR}/man5 MAN7DIR?= ${MANDIR}/man7 MAN8DIR?= ${MANDIR}/man8 @@ -41,80 +42,103 @@ MAN7= lfacme.7 \ MAN8= lfacme-renew.8 \ lfacme-setup.8 -PERIODICDIR= /usr/local/etc/periodic/daily PERIODICMODE?= 0755 PERIODIC= 900.lfacme.sh +SED?= sed +REPLACE= sed -e 's,__PREFIX__,${PREFIX},g' \ + -e 's,__CONFDIR__,${CONFDIR},g' \ + -e 's,__LIBDIR__,${LIBDIR},g' \ + -e 's,__BINDIR__,${BINDIR},g' + default: all -all: - @echo "Nothing to do." +all: ${MAN5} ${MAN7} ${MAN8} ${LIB} ${BIN} ${CHALLENGE} ${HOOK} ${PERIODIC} ${CONF} + +clean: + rm -f ${MAN5} ${MAN7} ${MAN8} ${LIB} ${BIN} + rm -f ${HOOK} ${CHALLENGE} ${PERIODIC} ${CONF} + +%.sh: %.sh.in + ${REPLACE} <$< >$@ + +%.sample: %.sample.in + ${REPLACE} <$< >$@ + +%.5: %.5.in + ${REPLACE} <$< >$@ + +%.7: %.7.in + ${REPLACE} <$< >$@ + +%.8: %.8.in + ${REPLACE} <$< >$@ install: install-lib install-bin install-conf install-hook install-man install-periodic -install-lib: - @echo 'create ${LIBDIR}'; install -d ${LIBDIR} +install-lib: all + @echo 'create ${DESTDIR}${LIBDIR}'; install -d ${DESTDIR}${LIBDIR} @for lib in ${LIB}; do \ - echo "install ${LIBDIR}/$$lib"; \ - install -C -m ${LIBMODE} "$$lib" "${LIBDIR}/$$lib"; \ + echo "install ${DESTDIR}${LIBDIR}/$$lib"; \ + install -C -m ${LIBMODE} "$$lib" "${DESTDIR}${LIBDIR}/$$lib"; \ done - @echo 'create ${LIBDIR}/challenge'; install -d ${LIBDIR}/challenge; + @echo 'create ${DESTDIR}${LIBDIR}/challenge'; install -d ${DESTDIR}${LIBDIR}/challenge; @for challenge in ${CHALLENGE}; do \ basename=$${challenge%*.sh}; \ - echo "install ${LIBDIR}/challenge/$$basename"; \ + echo "install ${DESTDIR}${LIBDIR}/challenge/$$basename"; \ install -C -m ${CHALLENGEMODE} "$$challenge" \ - "${LIBDIR}/challenge/$$basename"; \ + "${DESTDIR}${LIBDIR}/challenge/$$basename"; \ done -install-bin: - @echo 'create ${BINDIR}'; install -d ${BINDIR} +install-bin: all + @echo 'create ${DESTDIR}${BINDIR}'; install -d ${DESTDIR}${BINDIR} @for bin in ${BIN}; do \ basename=$${bin%*.sh}; \ - echo "install ${BINDIR}/$$basename"; \ - install -C -m ${BINMODE} "$$bin" "${BINDIR}/$$basename"; \ + echo "install ${DESTDIR}${BINDIR}/$$basename"; \ + install -C -m ${BINMODE} "$$bin" "${DESTDIR}${BINDIR}/$$basename"; \ done -install-conf: - @echo 'create ${CONFDIR}'; install -d ${CONFDIR}; +install-conf: all + @echo 'create ${DESTDIR}${CONFDIR}'; install -d ${DESTDIR}${CONFDIR}; @for conf in ${CONF}; do \ - echo "install ${CONFDIR}/$$conf"; \ - install -C -m ${CONFMODE} "$$conf" "${CONFDIR}/$$conf"; \ + echo "install ${DESTDIR}${CONFDIR}/$$conf"; \ + install -C -m ${CONFMODE} "$$conf" "${DESTDIR}${CONFDIR}/$$conf"; \ done -install-hook: - @echo 'create ${HOOKDIR}'; install -d ${HOOKDIR}; +install-hook: all + @echo 'create ${DESTDIR}${HOOKDIR}'; install -d ${DESTDIR}${HOOKDIR}; @for hook in ${HOOK}; do \ basename=$${hook%*.sh}; \ - echo "install ${HOOKDIR}/$$basename"; \ - install -C -m ${HOOKMODE} "$$hook" "${HOOKDIR}/$$basename"; \ + echo "install ${DESTDIR}${HOOKDIR}/$$basename"; \ + install -C -m ${HOOKMODE} "$$hook" "${DESTDIR}${HOOKDIR}/$$basename"; \ done -install-man: - @echo 'create ${MANDIR}'; install -d ${MANDIR} - @echo 'create ${MAN5DIR}'; install -d ${MAN5DIR} +install-man: all + @echo 'create ${DESTDIR}${MANDIR}'; install -d ${DESTDIR}${MANDIR} + @echo 'create ${DESTDIR}${MAN5DIR}'; install -d ${DESTDIR}${MAN5DIR} @for man in ${MAN5}; do \ - echo "install ${MAN5DIR}/$$man"; \ - install -C -m ${MANMODE} "$$man" "${MAN5DIR}/$$man"; \ + echo "install ${DESTDIR}${MAN5DIR}/$$man"; \ + install -C -m ${MANMODE} "$$man" "${DESTDIR}${MAN5DIR}/$$man"; \ done - @echo 'create ${MAN7DIR}'; install -d ${MAN7DIR} + @echo 'create ${DESTDIR}${MAN7DIR}'; install -d ${DESTDIR}${MAN7DIR} @for man in ${MAN7}; do \ - echo "install ${MAN7DIR}/$$man"; \ - install -C -m ${MANMODE} "$$man" "${MAN7DIR}/$$man"; \ + echo "install ${DESTDIR}${MAN7DIR}/$$man"; \ + install -C -m ${MANMODE} "$$man" "${DESTDIR}${MAN7DIR}/$$man"; \ done - @echo 'create ${MAN8DIR}'; install -d ${MAN8DIR} + @echo 'create ${DESTDIR}${MAN8DIR}'; install -d ${DESTDIR}${MAN8DIR} @for man in ${MAN8}; do \ - echo "install ${MAN8DIR}/$$man"; \ - install -C -m ${MANMODE} "$$man" "${MAN8DIR}/$$man"; \ + echo "install ${DESTDIR}${MAN8DIR}/$$man"; \ + install -C -m ${MANMODE} "$$man" "${DESTDIR}${MAN8DIR}/$$man"; \ done -install-periodic: +install-periodic: all @if [ $$(uname) = "FreeBSD" ]; then \ - echo 'create ${PERIODICDIR}'; install -d ${PERIODICDIR}; \ + echo 'create ${DESTDIR}${PERIODICDIR}'; install -d ${DESTDIR}${PERIODICDIR}; \ for periodic in ${PERIODIC}; do \ basename=$${periodic%*.sh}; \ - echo "install ${PERIODICDIR}/$$basename"; \ + echo "install ${DESTDIR}${PERIODICDIR}/$$basename"; \ install -C -m ${PERIODICMODE} "$$periodic" \ - "${PERIODICDIR}/$$basename"; \ + "${DESTDIR}${PERIODICDIR}/$$basename"; \ done; \ fi diff --git a/README b/README index 328d5d3..8a57484 100644 --- a/README +++ b/README @@ -60,9 +60,6 @@ usage known issues ------------ -+ lfacme assumes it's installed in /usr/local. if you want to change this, - you'll need to edit the scripts. - + we disable ARI in uacme (uacme --no-ari) because it's broken on non-glibc platforms. this is a uacme bug: https://github.com/ndilieto/uacme/issues/91. the only impact of this is that certificates will be renewed 30 days before diff --git a/acme.conf.5 b/acme.conf.5 deleted file mode 100644 index f1f2638..0000000 --- a/acme.conf.5 +++ /dev/null @@ -1,50 +0,0 @@ -.\" This source code is released into the public domain. -.Dd June 3, 2025 -.Dt ACME.CONF 5 -.Os -.Sh NAME -.Nm acme.conf -.Nd lfacme global configuration file -.Sh SYNOPSIS -.Pa /usr/local/etc/lfacme/acme.conf -.Sh DESCRIPTION -The -.Nm -file is used to configure the global behaviour of -.Nm lfacme . -Each option should be configured as a -.Xr sh 1 -variable assignment, i.e. -.Dq Ar option Ns = Ns Ar value . -.Pp -Alternatively, options may be set as environment variables prior to running -.Nm lfacme -utilities. -If all required options are set in the environment, then creating the -.Nm -file is not required. -.Pp -The following configuration variables are supported: -.Bl -tag -width indent -.It Va ACME_URL -(Required.) -The URL of the ACME server. -.It Va ACME_DATADIR -The path to the runtime data directory, where the ACME account key and any -issued certificates will be stored. -The default value is -.Pa /var/db/lfacme . -.It Va ACME_HOOKDIR -The path to a directory containing hooks to invoke when issuing certificates -(see -.Xr domains.conf 5 ) . -The default value is -.Pa /usr/local/etc/lfacme/hooks . -.El -.Pp -Additional configuration variables may be used by the ACME validation hooks; -refer to the manual page for each hook for more details. -.Sh SEE ALSO -.Xr domains.conf 5 , -.Xr lfacme-renew 8 , -.Xr lfacme-setup 8 diff --git a/acme.conf.5.in b/acme.conf.5.in new file mode 100644 index 0000000..2210cad --- /dev/null +++ b/acme.conf.5.in @@ -0,0 +1,50 @@ +.\" This source code is released into the public domain. +.Dd June 3, 2025 +.Dt ACME.CONF 5 +.Os +.Sh NAME +.Nm acme.conf +.Nd lfacme global configuration file +.Sh SYNOPSIS +.Pa __CONFDIR__/acme.conf +.Sh DESCRIPTION +The +.Nm +file is used to configure the global behaviour of +.Nm lfacme . +Each option should be configured as a +.Xr sh 1 +variable assignment, i.e. +.Dq Ar option Ns = Ns Ar value . +.Pp +Alternatively, options may be set as environment variables prior to running +.Nm lfacme +utilities. +If all required options are set in the environment, then creating the +.Nm +file is not required. +.Pp +The following configuration variables are supported: +.Bl -tag -width indent +.It Va ACME_URL +(Required.) +The URL of the ACME server. +.It Va ACME_DATADIR +The path to the runtime data directory, where the ACME account key and any +issued certificates will be stored. +The default value is +.Pa /var/db/lfacme . +.It Va ACME_HOOKDIR +The path to a directory containing hooks to invoke when issuing certificates +(see +.Xr domains.conf 5 ) . +The default value is +.Pa __CONFDIR__/hooks . +.El +.Pp +Additional configuration variables may be used by the ACME validation hooks; +refer to the manual page for each hook for more details. +.Sh SEE ALSO +.Xr domains.conf 5 , +.Xr lfacme-renew 8 , +.Xr lfacme-setup 8 diff --git a/acme.conf.sample b/acme.conf.sample deleted file mode 100644 index f1d0906..0000000 --- a/acme.conf.sample +++ /dev/null @@ -1,86 +0,0 @@ -# This is a sample configuration file for lfacme. It is a shell script, -# so you can include other files or call programs here if you like. -# -# See acme.conf(5) for documentation on this file. - -####################################################################### -# Base options. -# -# These options are used by lfacme itself. - - -### ACME_URL -# The URL of the ACME server. -# No default, you must set this. - -# Let's Encrypt production: -#ACME_URL="https://acme-v02.api.letsencrypt.org/directory" - -# Let's Encrypt staging: -#ACME_URL="https://acme-staging-v02.api.letsencrypt.org/directory" - - -### ACME_DATADIR -# Runtime data directory. -# This is where the ACME account key and the issued certificates are stored. -# The default is /var/db/lfacme. - -#ACME_DATADIR="/var/db/lfacme" - - -### ACME_HOOKDIR -# The path to the directory containing certificate hooks. -# The default is "/usr/local/etc/lfacme/hooks". -# There is usually no need to change this. - -#ACME_HOOKDIR="/some/directory" - - -####################################################################### -# lfacme-http(5) options. -# -# These options are used for the "http" challenge. - - -### ACME_HTTP_CHALLENGE_DIR -# When using the "http" challenge handler, this is the directory which contains -# ACME challenges. This must be served at /.well-known/acme-challenge on any -# domain using http validation. -# No default, you must set this if you use the "http" handler. - -#ACME_HTTP_CHALLENGE_DIR="/var/www/acme-challenge" - - -####################################################################### -# lfacme-dns(5) options. -# -# These options are used for the "dns" challenge. - - -### ACME_DNS_KEYFILE -# Path to the TSIG key nsupdate will use to authenticate the update. -# No default; you must configure this when using the dns challenge. - -#ACME_DNS_KEYFILE="/path/to/key" - - -####################################################################### -# lfacme-kerberos(5) options. -# -# These options are used for the "kerberos" challenge. - - -### ACME_KERBEROS_PRINCIPAL -# When using the "kerberos" challenge handler, this is the Kerberos principal -# we use for nsupdate. The default is "host/$(hostname)", which assumes a -# default realm is configured in /etc/krb5.conf. - -#ACME_KERBEROS_PRINCIPAL="host/server.example.org@EXAMPLE.ORG" - - -### ACME_KERBEROS_KEYTAB -# When using the "kerberos" challenge handler, this is the keytab used to -# issue the ticket. It must contain a key for $ACME_KERBEROS_PRINCIPAL. -# The default is /etc/krb5.keytab. - -#ACME_KERBEROS_KEYTAB="/etc/krb5.keytab" diff --git a/acme.conf.sample.in b/acme.conf.sample.in new file mode 100644 index 0000000..d6cea21 --- /dev/null +++ b/acme.conf.sample.in @@ -0,0 +1,86 @@ +# This is a sample configuration file for lfacme. It is a shell script, +# so you can include other files or call programs here if you like. +# +# See acme.conf(5) for documentation on this file. + +####################################################################### +# Base options. +# +# These options are used by lfacme itself. + + +### ACME_URL +# The URL of the ACME server. +# No default, you must set this. + +# Let's Encrypt production: +#ACME_URL="https://acme-v02.api.letsencrypt.org/directory" + +# Let's Encrypt staging: +#ACME_URL="https://acme-staging-v02.api.letsencrypt.org/directory" + + +### ACME_DATADIR +# Runtime data directory. +# This is where the ACME account key and the issued certificates are stored. +# The default is /var/db/lfacme. + +#ACME_DATADIR="/var/db/lfacme" + + +### ACME_HOOKDIR +# The path to the directory containing certificate hooks. +# The default is "__CONFDIR__/hooks". +# There is usually no need to change this. + +#ACME_HOOKDIR="/some/directory" + + +####################################################################### +# lfacme-http(5) options. +# +# These options are used for the "http" challenge. + + +### ACME_HTTP_CHALLENGE_DIR +# When using the "http" challenge handler, this is the directory which contains +# ACME challenges. This must be served at /.well-known/acme-challenge on any +# domain using http validation. +# No default, you must set this if you use the "http" handler. + +#ACME_HTTP_CHALLENGE_DIR="/var/www/acme-challenge" + + +####################################################################### +# lfacme-dns(5) options. +# +# These options are used for the "dns" challenge. + + +### ACME_DNS_KEYFILE +# Path to the TSIG key nsupdate will use to authenticate the update. +# No default; you must configure this when using the dns challenge. + +#ACME_DNS_KEYFILE="/path/to/key" + + +####################################################################### +# lfacme-kerberos(5) options. +# +# These options are used for the "kerberos" challenge. + + +### ACME_KERBEROS_PRINCIPAL +# When using the "kerberos" challenge handler, this is the Kerberos principal +# we use for nsupdate. The default is "host/$(hostname)", which assumes a +# default realm is configured in /etc/krb5.conf. + +#ACME_KERBEROS_PRINCIPAL="host/server.example.org@EXAMPLE.ORG" + + +### ACME_KERBEROS_KEYTAB +# When using the "kerberos" challenge handler, this is the keytab used to +# issue the ticket. It must contain a key for $ACME_KERBEROS_PRINCIPAL. +# The default is /etc/krb5.keytab. + +#ACME_KERBEROS_KEYTAB="/etc/krb5.keytab" diff --git a/dns.sh b/dns.sh deleted file mode 100644 index 9b26bd3..0000000 --- a/dns.sh +++ /dev/null @@ -1,78 +0,0 @@ -#! /bin/sh -# This source code is released into the public domain. - -. /usr/local/share/lfacme/init.sh -. /usr/local/share/lfacme/dnsutils.sh - -# begin, done or failed -ACTION=$1 -# ACME method, must be dns-01. -METHOD=$2 -# This is the full domain name we're authorising. -DOMAIN=$3 -# Token name, not used for dns-01. -TOKEN=$4 -# The token value we need to create. -AUTH=$5 - -if [ "$#" -ne 5 ]; then - _fatal "missing arguments" -fi - -if [ "$METHOD" != "dns-01" ]; then - exit 1 -fi - -if [ -z "$ACME_DNS_KEYFILE" ]; then - _fatal "ACME_DNS_KEYFILE not configured" -fi - -# Add a new record using nsupdate. -_add_record() { - local domain="$1" - local auth="$2" - - nsupdate -k "$ACME_DNS_KEYFILE" <"$_file" - exit $? - ;; - - done|failed) - _verbose "deleting validation token %s" "$_file" - rm -f "$_file" - exit $? - ;; - - *) - _fatal "unknown action: %s" "$ACTION" - ;; -esac diff --git a/http.sh.in b/http.sh.in new file mode 100644 index 0000000..048870e --- /dev/null +++ b/http.sh.in @@ -0,0 +1,51 @@ +#! /bin/sh +# This source code is released into the public domain. + +. __LIBDIR__/init.sh + +# begin, done or failed +ACTION=$1 +# ACME method, must be http-01. +METHOD=$2 +# The full domain name we're authorising. +DOMAIN=$3 +# Token name. +TOKEN=$4 +# The token value we need to create. +AUTH=$5 + +if [ "$#" -ne 5 ]; then + _fatal "missing arguments" +fi + +if [ "$METHOD" != "http-01" ]; then + exit 1 +fi + +if [ -z "$ACME_HTTP_CHALLENGE_DIR" ]; then + _fatal "must set ACME_HTTP_CHALLENGE_DIR" +fi + +if ! [ -d "$ACME_HTTP_CHALLENGE_DIR" ]; then + _fatal "missing $ACME_HTTP_CHALLENGE_DIR" +fi + +_file="${ACME_HTTP_CHALLENGE_DIR}/${TOKEN}" + +case "$ACTION" in + begin) + _verbose "creating validation token %s" "$_file" + echo "$AUTH" >"$_file" + exit $? + ;; + + done|failed) + _verbose "deleting validation token %s" "$_file" + rm -f "$_file" + exit $? + ;; + + *) + _fatal "unknown action: %s" "$ACTION" + ;; +esac diff --git a/init.sh b/init.sh deleted file mode 100644 index c48e942..0000000 --- a/init.sh +++ /dev/null @@ -1,158 +0,0 @@ -# This source code is released into the public domain. - -_PROGNAME="$0" - -_fatal() { - local _fmt=$1; shift - local _msg="$(printf "$_fmt" "$@")" - printf >&2 '%s: FATAL: %s\n' "$_PROGNAME" "$_msg" - exit 1 -} - -_error() { - local _fmt=$1; shift - local _msg="$(printf "$_fmt" "$@")" - printf >&2 '%s: ERROR: %s\n' "$_PROGNAME" "$_msg" -} - -_warn() { - local _fmt=$1; shift - local _msg="$(printf "$_fmt" "$@")" - printf >&2 '%s: WARNING: %s\n' "$_PROGNAME" "$_msg" -} - -_info() { - local _fmt=$1; shift - local _msg="$(printf "$_fmt" "$@")" - printf '%s: %s\n' "$_PROGNAME" "$_msg" -} - -_verbose() { - if [ -z "$LFACME_VERBOSE" ]; then - return - fi - - local _fmt=$1; shift - local _msg="$(printf "$_fmt" "$@")" - printf '%s: %s\n' "$_PROGNAME" "$_msg" -} - -# The prefix we're installed in. -_BASEDIR="/usr/local" -# Where the internal scripts are. -_SHARE="${_BASEDIR}/share/lfacme" -_CHALLENGE="${_SHARE}/challenge" - -# Our configuration directory. If $_CONFDIR is already set, then the script -# wants to provide its own config directory, probably from a command line -# argument. Otherwise if $LFACME_CONFDIR is set, we're running in a hook -# script, so use that as the config directory. Otherwise, use the default. -if [ -z "$_CONFDIR" ]; then - if ! [ -z "$LFACME_CONFDIR" ]; then - _CONFDIR="$LFACME_CONFDIR" - else - _CONFDIR="${_BASEDIR}/etc/lfacme" - fi -fi - -# Our configuration file. -_CONFIG="${_CONFDIR}/acme.conf" - -# Read and validate the configuration file. - -if [ -f "$_CONFIG" ]; then - . "$_CONFIG" -fi - -if [ -z "$ACME_URL" ]; then - _fatal "missing configuration setting: ACME_URL" -fi - -if [ -z "$ACME_DATADIR" ]; then - ACME_DATADIR="/var/db/lfacme" -fi - -if [ -z "$ACME_HOOKDIR" ]; then - ACME_HOOKDIR="${_CONFDIR}/hooks" -fi - -# Create our data directory. -if [ ! -d "$ACME_DATADIR" ]; then - _info "creating directory %s" "$ACME_DATADIR" - mkdir -p "$ACME_DATADIR" -fi - -# The domains.conf file. -_DOMAINS="${_CONFDIR}/domains.conf" - -# uacme's base directory; this is where it puts certificates. -_UACME_DIR="${ACME_DATADIR}/certs" - -# The uacme executable. -_UACME=/usr/local/bin/uacme - -_LFACME_UACME_FLAGS="" -if ! [ -z "$LFACME_VERBOSE" ]; then - _LFACME_UACME_FLAGS="$_LFACME_UACME_FLAGS -v" -fi - -_uacme() { - env "LFACME_CONFDIR=${_CONFDIR}" \ - "LFACME_VERBOSE=${LFACME_VERBOSE}" \ - "$_UACME" $_LFACME_UACME_FLAGS \ - -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() { - local identifier="$1" - local hook="$2" - - 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/init.sh.in b/init.sh.in new file mode 100644 index 0000000..c70285c --- /dev/null +++ b/init.sh.in @@ -0,0 +1,158 @@ +# This source code is released into the public domain. + +_PROGNAME="$0" + +_fatal() { + local _fmt=$1; shift + local _msg="$(printf "$_fmt" "$@")" + printf >&2 '%s: FATAL: %s\n' "$_PROGNAME" "$_msg" + exit 1 +} + +_error() { + local _fmt=$1; shift + local _msg="$(printf "$_fmt" "$@")" + printf >&2 '%s: ERROR: %s\n' "$_PROGNAME" "$_msg" +} + +_warn() { + local _fmt=$1; shift + local _msg="$(printf "$_fmt" "$@")" + printf >&2 '%s: WARNING: %s\n' "$_PROGNAME" "$_msg" +} + +_info() { + local _fmt=$1; shift + local _msg="$(printf "$_fmt" "$@")" + printf '%s: %s\n' "$_PROGNAME" "$_msg" +} + +_verbose() { + if [ -z "$LFACME_VERBOSE" ]; then + return + fi + + local _fmt=$1; shift + local _msg="$(printf "$_fmt" "$@")" + printf '%s: %s\n' "$_PROGNAME" "$_msg" +} + +# The prefix we're installed in. +_BASEDIR="__PREFIX__" +# Where the internal scripts are. +_SHARE="${_BASEDIR}/share/lfacme" +_CHALLENGE="${_SHARE}/challenge" + +# Our configuration directory. If $_CONFDIR is already set, then the script +# wants to provide its own config directory, probably from a command line +# argument. Otherwise if $LFACME_CONFDIR is set, we're running in a hook +# script, so use that as the config directory. Otherwise, use the default. +if [ -z "$_CONFDIR" ]; then + if ! [ -z "$LFACME_CONFDIR" ]; then + _CONFDIR="$LFACME_CONFDIR" + else + _CONFDIR="${_BASEDIR}/etc/lfacme" + fi +fi + +# Our configuration file. +_CONFIG="${_CONFDIR}/acme.conf" + +# Read and validate the configuration file. + +if [ -f "$_CONFIG" ]; then + . "$_CONFIG" +fi + +if [ -z "$ACME_URL" ]; then + _fatal "missing configuration setting: ACME_URL" +fi + +if [ -z "$ACME_DATADIR" ]; then + ACME_DATADIR="/var/db/lfacme" +fi + +if [ -z "$ACME_HOOKDIR" ]; then + ACME_HOOKDIR="${_CONFDIR}/hooks" +fi + +# Create our data directory. +if [ ! -d "$ACME_DATADIR" ]; then + _info "creating directory %s" "$ACME_DATADIR" + mkdir -p "$ACME_DATADIR" +fi + +# The domains.conf file. +_DOMAINS="${_CONFDIR}/domains.conf" + +# uacme's base directory; this is where it puts certificates. +_UACME_DIR="${ACME_DATADIR}/certs" + +# The uacme executable. +_UACME=uacme + +_LFACME_UACME_FLAGS="" +if ! [ -z "$LFACME_VERBOSE" ]; then + _LFACME_UACME_FLAGS="$_LFACME_UACME_FLAGS -v" +fi + +_uacme() { + env "LFACME_CONFDIR=${_CONFDIR}" \ + "LFACME_VERBOSE=${LFACME_VERBOSE}" \ + "$_UACME" $_LFACME_UACME_FLAGS \ + -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() { + local identifier="$1" + local hook="$2" + + 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.sh b/kerberos.sh deleted file mode 100644 index 543abc6..0000000 --- a/kerberos.sh +++ /dev/null @@ -1,86 +0,0 @@ -#! /bin/sh -# This source code is released into the public domain. - -. /usr/local/share/lfacme/init.sh -. /usr/local/share/lfacme/dnsutils.sh - -# begin, done or failed -ACTION=$1 -# ACME method, must be dns-01. -METHOD=$2 -# This is the full domain name we're authorising. -DOMAIN=$3 -# Token name, not used for dns-01. -TOKEN=$4 -# The token value we need to create. -AUTH=$5 - -if [ "$#" -ne 5 ]; then - _fatal "missing arguments" -fi - -if [ "$METHOD" != "dns-01" ]; then - exit 1 -fi - -if [ -z "$ACME_KERBEROS_PRINCIPAL" ]; then - ACME_KERBEROS_PRINCIPAL="host/$(hostname)" -fi - -if [ -z "$ACME_KERBEROS_KEYTAB" ]; then - ACME_KERBEROS_KEYTAB="/etc/krb5.keytab" -fi - -if ! kinit -k -t "$ACME_KERBEROS_KEYTAB" "$ACME_KERBEROS_PRINCIPAL"; then - _fatal "failed to obtain a Kerberos ticket" -fi - -# Add a new record using nsupdate. -_add_record() { - local domain="$1" - local auth="$2" - - nsupdate -g <"$csrconf" <>"$csrconf" 'DNS.%d = %s\n' "$_i" "$altname" - _i=$((_i + 1)) - done - - # Generate the CSR - openssl req -new -key "$keyfile" -out "$csrfile" -config "$csrconf" - return $? -} - -# Process a single cert. -_docert() { - local identifier="$1"; shift - - _verbose "checking certificate '%s'" "$identifier" - - # uacme creates the cert name by stripping the extension from the - # CSR filename, so the basename has to match the identifier. - local dir="${_UACME_DIR}/${identifier}" - local keyfile="${dir}/${identifier}-key.pem" - local csrfile="${dir}/${identifier}.csr" - local certfile="${dir}/${identifier}-cert.pem" - - # these can be overridden by args - local keytype="" - local altnames="" - local hooks="" - local domain="" - local challenge="" - - # parse arguments for this cert - while ! [ -z "$1" ]; do - case "$1" in - type=rsa) - keytype=rsa - ;; - type=ec) - keytype=ec - ;; - type=*) - _error "%s: unknown key type: %s" \ - "$identifier" "${1#type=*}" - return 1 - ;; - hook=*) - hooks="$hooks ${1#hook=*}" - ;; - challenge=*) - challenge="${1#challenge=*}" - ;; - *=*) - _error "%s: unknown option: %s" "$identifier" "$1" - return 1 - ;; - *.*) - altnames="$altnames $1" - # Take the domain from the first altname. - if [ -z "$domain" ]; then - domain="$1" - fi - ;; - *) - _error "%s: unknown option: %s" "$identifier" "$1" - return 1 - ;; - esac - shift - done - - # If no altnames were given, the identifier is the domain. - if [ -z "$domain" ]; then - domain="$identifier" - fi - - # Default key type is ec. - if [ -z "$keytype" ]; then - keytype="ec" - fi - - # Default challenge is http. - if [ -z "$challenge" ]; then - challenge="http" - fi - - # make sure the challenge is valid. - challenge_path="$(_findchallenge "$identifier" "$challenge")" - if [ -z "$challenge_path" ]; 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 relative - # to ACME_HOOKDIR. - local _rhooks="" - for hook in $hooks; do - local _hookpath="$(_findhook "$identifier" "$hook")" - if [ -z "$_hookpath" ]; then - return 1 - fi - - _rhooks="$_rhooks $_hookpath" - done - - mkdir -p -m0700 "$dir" - - if ! _make_key "$keytype" "$keyfile"; then - _error "%s: could not create a new private key" "$identifier" - return 1 - fi - - if ! _make_csr "$csrfile" "$keyfile" "$domain" "$altnames"; then - _error "%s: could not create the certificate signing request" \ - "$identifier" - return 1 - fi - - _uacme $_uacme_flags \ - -h "$challenge_path" \ - issue "$csrfile" - _ret=$? - - # exit 1 means the cert wasn't reissued - if [ "$_ret" -eq 1 ]; then - return 0 - fi - - # exit 2 means an actual error - if [ "$_ret" -eq 2 ]; then - _error "%s: failed to issue certificate" "$identifier" - return 1 - fi - - # any other non-zero exit code is unexpected - if [ "$_ret" -ne 0 ]; then - _error "%s: unexpected exit code from uacme: %d" \ - "$identifier" "$_ret" - return 1 - fi - - # otherwise, exit code is 0 which means we (re)issued the cert, - # so run the hooks. - for hook in $_rhooks; do - _verbose "running hook: %s" "$hook" - env "LFACME_CONFDIR=${_CONFDIR}" \ - "LFACME_VERBOSE=${LFACME_VERBOSE}" \ - "LFACME_CERT=${identifier}" \ - "LFACME_KEYFILE=${keyfile}" \ - "LFACME_CERTFILE=${certfile}" \ - "$hook" newcert - if [ "$?" -ne 0 ]; then - _warn "%s: hook script '%s' failed" \ - "$identifier" "$hook" - fi - # should we do anything if the hook failed? - done - - return $? -} - -cat "$_DOMAINS" \ -| egrep -v '^(#|[[:space:]]*$)' \ -| ( - _default_args="" - _exit=0 - - while read identifier args; do - if [ "$identifier" = "*" ]; then - _default_args="$args" - continue - fi - - if ! _docert "$identifier" $_default_args $args; then - _exit=1 - fi - done - - exit $_exit -) - -exit $? diff --git a/lfacme-renew.sh.in b/lfacme-renew.sh.in new file mode 100644 index 0000000..5b471c7 --- /dev/null +++ b/lfacme-renew.sh.in @@ -0,0 +1,269 @@ +#! /bin/sh +# This source code is released into the public domain. + +# Parse command-line arguments. +args=$(getopt c:v $*) +if [ $? -ne 0 ]; then + exit 1 +fi +set -- $args + +# ARI is broken due to https://github.com/ndilieto/uacme/issues/91 +_uacme_flags="--no-ari" + +while :; do + case "$1" in + -c) + _CONFDIR="$2" + shift; shift;; + -v) + LFACME_VERBOSE=1 + shift;; + --) + shift; break;; + esac +done + +# Initialise. +. __LIBDIR__/init.sh + +if ! [ -f "$_UACME_DIR/private/key.pem" ]; then + _fatal "run lfacme-setup first" +fi + +if ! [ -f "$_DOMAINS" ]; then + _fatal "missing $_DOMAINS" +fi + +# Create a key if it doesn't already exist. It would be better to always +# create a new key here, but currently uacme doesn't have a way to tell us +# that we need to do that. +_make_key() { + local keytype="$1" + local keyfile="$2" + + if [ -s "$keyfile" ]; then + return 0 + fi + + local _umask=$(umask) + umask 077 + + case $keytype in + ec) openssl ecparam -name secp384r1 -genkey -noout -out "$keyfile";; + rsa) openssl genrsa -out "$keyfile" 3072;; + *) _error "%s: unknown key type %s?" "$keyfile" "$keytype" + return 1;; + esac + + local _ret=$? + umask $_umask + + return $_ret +} + +# Create a new CSR for a domain. +_make_csr() { + local csrfile="$1" + local keyfile="$2" + local domain="$3" + local altnames="$4" + local csrconf="${csrfile}.cnf" + + cat >"$csrconf" <>"$csrconf" 'DNS.%d = %s\n' "$_i" "$altname" + _i=$((_i + 1)) + done + + # Generate the CSR + openssl req -new -key "$keyfile" -out "$csrfile" -config "$csrconf" + return $? +} + +# Process a single cert. +_docert() { + local identifier="$1"; shift + + _verbose "checking certificate '%s'" "$identifier" + + # uacme creates the cert name by stripping the extension from the + # CSR filename, so the basename has to match the identifier. + local dir="${_UACME_DIR}/${identifier}" + local keyfile="${dir}/${identifier}-key.pem" + local csrfile="${dir}/${identifier}.csr" + local certfile="${dir}/${identifier}-cert.pem" + + # these can be overridden by args + local keytype="" + local altnames="" + local hooks="" + local domain="" + local challenge="" + + # parse arguments for this cert + while ! [ -z "$1" ]; do + case "$1" in + type=rsa) + keytype=rsa + ;; + type=ec) + keytype=ec + ;; + type=*) + _error "%s: unknown key type: %s" \ + "$identifier" "${1#type=*}" + return 1 + ;; + hook=*) + hooks="$hooks ${1#hook=*}" + ;; + challenge=*) + challenge="${1#challenge=*}" + ;; + *=*) + _error "%s: unknown option: %s" "$identifier" "$1" + return 1 + ;; + *.*) + altnames="$altnames $1" + # Take the domain from the first altname. + if [ -z "$domain" ]; then + domain="$1" + fi + ;; + *) + _error "%s: unknown option: %s" "$identifier" "$1" + return 1 + ;; + esac + shift + done + + # If no altnames were given, the identifier is the domain. + if [ -z "$domain" ]; then + domain="$identifier" + fi + + # Default key type is ec. + if [ -z "$keytype" ]; then + keytype="ec" + fi + + # Default challenge is http. + if [ -z "$challenge" ]; then + challenge="http" + fi + + # make sure the challenge is valid. + challenge_path="$(_findchallenge "$identifier" "$challenge")" + if [ -z "$challenge_path" ]; 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 relative + # to ACME_HOOKDIR. + local _rhooks="" + for hook in $hooks; do + local _hookpath="$(_findhook "$identifier" "$hook")" + if [ -z "$_hookpath" ]; then + return 1 + fi + + _rhooks="$_rhooks $_hookpath" + done + + mkdir -p -m0700 "$dir" + + if ! _make_key "$keytype" "$keyfile"; then + _error "%s: could not create a new private key" "$identifier" + return 1 + fi + + if ! _make_csr "$csrfile" "$keyfile" "$domain" "$altnames"; then + _error "%s: could not create the certificate signing request" \ + "$identifier" + return 1 + fi + + _uacme $_uacme_flags \ + -h "$challenge_path" \ + issue "$csrfile" + _ret=$? + + # exit 1 means the cert wasn't reissued + if [ "$_ret" -eq 1 ]; then + return 0 + fi + + # exit 2 means an actual error + if [ "$_ret" -eq 2 ]; then + _error "%s: failed to issue certificate" "$identifier" + return 1 + fi + + # any other non-zero exit code is unexpected + if [ "$_ret" -ne 0 ]; then + _error "%s: unexpected exit code from uacme: %d" \ + "$identifier" "$_ret" + return 1 + fi + + # otherwise, exit code is 0 which means we (re)issued the cert, + # so run the hooks. + for hook in $_rhooks; do + _verbose "running hook: %s" "$hook" + env "LFACME_CONFDIR=${_CONFDIR}" \ + "LFACME_VERBOSE=${LFACME_VERBOSE}" \ + "LFACME_CERT=${identifier}" \ + "LFACME_KEYFILE=${keyfile}" \ + "LFACME_CERTFILE=${certfile}" \ + "$hook" newcert + if [ "$?" -ne 0 ]; then + _warn "%s: hook script '%s' failed" \ + "$identifier" "$hook" + fi + # should we do anything if the hook failed? + done + + return $? +} + +cat "$_DOMAINS" \ +| egrep -v '^(#|[[:space:]]*$)' \ +| ( + _default_args="" + _exit=0 + + while read identifier args; do + if [ "$identifier" = "*" ]; then + _default_args="$args" + continue + fi + + if ! _docert "$identifier" $_default_args $args; then + _exit=1 + fi + done + + exit $_exit +) + +exit $? diff --git a/lfacme-setup.8 b/lfacme-setup.8 deleted file mode 100644 index f34e518..0000000 --- a/lfacme-setup.8 +++ /dev/null @@ -1,45 +0,0 @@ -.\" This source code is released into the public domain. -.Dd June 3, 2025 -.Dt LFACME-SETUP 8 -.Os -.Sh NAME -.Nm lfacme-setup -.Nd create a new ACME account -.Sh SYNOPSIS -.Nm -.Op Fl vy -.Op Fl c Ar confdir -.Sh DESCRIPTION -The -.Nm -utility will register a new account with the ACME provider configured in -.Xr acme.conf 5 . -If the provider requires accepting terms of service to create an account, -the ToS URL will be printed and -.Nm -will prompt the user to accept them. -.Pp -The follow options are accepted: -.Bl -tag -width indent -.It Fl c Ar confdir -Use -.Ar confdir -as the configuration directory instead of the default -.Pa /usr/local/etc/lfacme . -.It Fl v -Produce more output when running. -This also passes the -.Fl v -option to uacme. -.It Fl y -If the ACME provider requires accepting terms of service, -accept the provided terms automatically. -.El -.Sh ENVIRONMENT -Refer to -.Xr lfacme 8 -for a list of environment variables which affect the operation of -.Nm . -.Sh SEE ALSO -.Xr acme.conf 5 , -.Xr lfacme 7 diff --git a/lfacme-setup.8.in b/lfacme-setup.8.in new file mode 100644 index 0000000..893c0b2 --- /dev/null +++ b/lfacme-setup.8.in @@ -0,0 +1,45 @@ +.\" This source code is released into the public domain. +.Dd June 3, 2025 +.Dt LFACME-SETUP 8 +.Os +.Sh NAME +.Nm lfacme-setup +.Nd create a new ACME account +.Sh SYNOPSIS +.Nm +.Op Fl vy +.Op Fl c Ar confdir +.Sh DESCRIPTION +The +.Nm +utility will register a new account with the ACME provider configured in +.Xr acme.conf 5 . +If the provider requires accepting terms of service to create an account, +the ToS URL will be printed and +.Nm +will prompt the user to accept them. +.Pp +The follow options are accepted: +.Bl -tag -width indent +.It Fl c Ar confdir +Use +.Ar confdir +as the configuration directory instead of the default +.Pa __CONFDIR__ . +.It Fl v +Produce more output when running. +This also passes the +.Fl v +option to uacme. +.It Fl y +If the ACME provider requires accepting terms of service, +accept the provided terms automatically. +.El +.Sh ENVIRONMENT +Refer to +.Xr lfacme 8 +for a list of environment variables which affect the operation of +.Nm . +.Sh SEE ALSO +.Xr acme.conf 5 , +.Xr lfacme 7 diff --git a/lfacme-setup.sh b/lfacme-setup.sh deleted file mode 100644 index f690cba..0000000 --- a/lfacme-setup.sh +++ /dev/null @@ -1,34 +0,0 @@ -#! /bin/sh -# This source code is released into the public domain. - -# Parse command-line arguments. -args=$(getopt c:vy $*) -if [ $? -ne 0 ]; then - exit 1 -fi -set -- $args - -_uacme_flags="" - -while :; do - case "$1" in - -c) - _CONFDIR="$2" - shift; shift;; - -v) - LFACME_VERBOSE=1 - shift;; - -y) - _uacme_flags="$_uacme_flags $1" - shift;; - --) - shift; break;; - esac -done - -# Initialise. -. /usr/local/share/lfacme/init.sh - -# Run uacme. -mkdir -p "$_UACME_DIR" -_uacme $_uacme_flags new diff --git a/lfacme-setup.sh.in b/lfacme-setup.sh.in new file mode 100644 index 0000000..4b60263 --- /dev/null +++ b/lfacme-setup.sh.in @@ -0,0 +1,34 @@ +#! /bin/sh +# This source code is released into the public domain. + +# Parse command-line arguments. +args=$(getopt c:vy $*) +if [ $? -ne 0 ]; then + exit 1 +fi +set -- $args + +_uacme_flags="" + +while :; do + case "$1" in + -c) + _CONFDIR="$2" + shift; shift;; + -v) + LFACME_VERBOSE=1 + shift;; + -y) + _uacme_flags="$_uacme_flags $1" + shift;; + --) + shift; break;; + esac +done + +# Initialise. +. __LIBDIR__/init.sh + +# Run uacme. +mkdir -p "$_UACME_DIR" +_uacme $_uacme_flags new diff --git a/lfacme.7 b/lfacme.7 deleted file mode 100644 index bed5a08..0000000 --- a/lfacme.7 +++ /dev/null @@ -1,76 +0,0 @@ -.\" This source code is released into the public domain. -.Dd June 4, 2025 -.Dt LFACME 7 -.Os -.Sh NAME -.Nm lfacme -.Nd issue, renew and manage ACME certificates -.Sh SYNOPSIS -.Nm lfacme-setup -.Op opts -.Nm lfacme-renew -.Op opts -.Sh DESCRIPTION -The -.Nm -software package supports management of TLS certificates using an ACME server. -Certificates can be automatically issued and renewed, and a hook system allows -software using those certificates to be automatically (re)configured. -.Pp -Prior to using -.Nm , -two configuration files must be created: -.Pa /usr/local/etc/lfacme/acme.conf -and -.Pa /usr/local/etc/lfacme/domains.conf . -Samples of both files are provided in -.Pa /usr/local/etc/lfacme . -Refer to -.Xr acme.conf 5 -and -.Xr domains.conf 5 -for more detailed documentation on these files. -.Pp -To perform initial setup, run -.Xr lfacme-setup 8 . -This will register an account on the ACME server, and create any required -local data. -Running -.Xr lfacme-setup 8 -will not issue any certificates. -.Pp -To issue or renew certificates, run -.Xr lfacme-renew 8 . -This will examine the certificates configured in -.Xr domains.conf 5 ; -new certificates will be issued, while existing certificates will be renewed -if necessary. -To ensure certificates are automatically renewed when required, -.Xr lfacme-renew 8 -should be run regularly, e.g. using -.Xr cron 8 . -.Sh ENVIRONMENT -The following environment variables affect the executation of the -.Nm -utilities: -.Bl -tag -width LFACME_VERBOSE -.It Ev LFACME_CONFDIR -Override the default configuration directory. -This is equivalent to specifying the -.Fl c -flag on the command line. -.It Ev LFACME_VERBOSE -If set to a non-empty string, run in verbose mode. -This is equivalent to specifying the -.Fl v -flag on the command line. -.El -.Pp -Additionally, any configuration settings described in -.Xr acme.conf 5 -may also be set in the environment. -.Sh SEE ALSO -.Xr acme.conf 5 , -.Xr domains.conf 5 , -.Xr lfacme-renew 8 , -.Xr lfacme-setup 8 diff --git a/lfacme.7.in b/lfacme.7.in new file mode 100644 index 0000000..3f3fa34 --- /dev/null +++ b/lfacme.7.in @@ -0,0 +1,76 @@ +.\" This source code is released into the public domain. +.Dd June 4, 2025 +.Dt LFACME 7 +.Os +.Sh NAME +.Nm lfacme +.Nd issue, renew and manage ACME certificates +.Sh SYNOPSIS +.Nm lfacme-setup +.Op opts +.Nm lfacme-renew +.Op opts +.Sh DESCRIPTION +The +.Nm +software package supports management of TLS certificates using an ACME server. +Certificates can be automatically issued and renewed, and a hook system allows +software using those certificates to be automatically (re)configured. +.Pp +Prior to using +.Nm , +two configuration files must be created: +.Pa __CONFDIR__/acme.conf +and +.Pa __CONFDIR__/domains.conf . +Samples of both files are provided in +.Pa __CONFDIR__lfacme . +Refer to +.Xr acme.conf 5 +and +.Xr domains.conf 5 +for more detailed documentation on these files. +.Pp +To perform initial setup, run +.Xr lfacme-setup 8 . +This will register an account on the ACME server, and create any required +local data. +Running +.Xr lfacme-setup 8 +will not issue any certificates. +.Pp +To issue or renew certificates, run +.Xr lfacme-renew 8 . +This will examine the certificates configured in +.Xr domains.conf 5 ; +new certificates will be issued, while existing certificates will be renewed +if necessary. +To ensure certificates are automatically renewed when required, +.Xr lfacme-renew 8 +should be run regularly, e.g. using +.Xr cron 8 . +.Sh ENVIRONMENT +The following environment variables affect the executation of the +.Nm +utilities: +.Bl -tag -width LFACME_VERBOSE +.It Ev LFACME_CONFDIR +Override the default configuration directory. +This is equivalent to specifying the +.Fl c +flag on the command line. +.It Ev LFACME_VERBOSE +If set to a non-empty string, run in verbose mode. +This is equivalent to specifying the +.Fl v +flag on the command line. +.El +.Pp +Additionally, any configuration settings described in +.Xr acme.conf 5 +may also be set in the environment. +.Sh SEE ALSO +.Xr acme.conf 5 , +.Xr domains.conf 5 , +.Xr lfacme-renew 8 , +.Xr lfacme-setup 8 -- cgit v1.2.3