zulip/scripts/setup/setup-certbot
Alex Vandiver 07bc2f2f5e
Some checks failed
Code scanning / CodeQL (push) Has been cancelled
Zulip production suite / Ubuntu 22.04 production build (push) Has been cancelled
Zulip CI / ${{ matrix.name }} (zulip/ci:bookworm, true, false, Debian 12 (Python 3.11, backend + documentation), bookworm) (push) Has been cancelled
Zulip CI / ${{ matrix.name }} (zulip/ci:jammy, false, true, Ubuntu 22.04 (Python 3.10, backend + frontend), jammy) (push) Has been cancelled
Zulip CI / ${{ matrix.name }} (zulip/ci:noble, false, false, Ubuntu 24.04 (Python 3.12, backend), noble) (push) Has been cancelled
Zulip CI / ${{ matrix.name }} (zulip/ci:trixie, false, false, Debian 13 (Python 3.13, backend), trixie) (push) Has been cancelled
Zulip production suite / ${{ matrix.name }} (zulip/ci:bookworm, --test-custom-db, Debian 12 production install with custom db name and user, bookworm) (push) Has been cancelled
Zulip production suite / ${{ matrix.name }} (zulip/ci:jammy, , Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga, jammy) (push) Has been cancelled
Zulip production suite / ${{ matrix.name }} (zulip/ci:noble, , Ubuntu 24.04 production install, noble) (push) Has been cancelled
Zulip production suite / ${{ matrix.name }} (zulip/ci:trixie, , Debian 13 production install, trixie) (push) Has been cancelled
Zulip production suite / ${{ matrix.name }} (zulip/ci:bookworm-7.0, 7.0 Version Upgrade, bookworm) (push) Has been cancelled
Zulip production suite / ${{ matrix.name }} (zulip/ci:bookworm-8.0, 8.0 Version Upgrade, bookworm) (push) Has been cancelled
Zulip production suite / ${{ matrix.name }} (zulip/ci:jammy-6.0, 6.0 Version Upgrade, jammy) (push) Has been cancelled
Zulip production suite / ${{ matrix.name }} (zulip/ci:noble-10.0, 10.0 Version Upgrade, noble) (push) Has been cancelled
Zulip production suite / ${{ matrix.name }} (zulip/ci:noble-9.0, 9.0 Version Upgrade, noble) (push) Has been cancelled
Zulip production suite / ${{ matrix.name }} (zulip/ci:trixie-11.0, 11.0 Version Upgrade, trixie) (push) Has been cancelled
setup-certbot: Run hooks, instead of "service nginx reload".
`certbot certonly`, when obtaining a new certificate, does not
run deploy hooks that were installed into
`/etc/letsencrypt/renewal-hooks` (certbot/certbot#9978).  Because of
this, we added an explicit `service nginx reload`.

This turns out to be awkward for a few reasons -- firstly, this does
not handle the restarting of our SMTP server, which also needs to pick
up the new certificates.  It requires that Docker installs explicitly
restart nginx their own way.  And it also means that if certbot
already had a certificate, we reload nginx twice.

We would ideally just have certbot just run all of the deploy hooks,
as usual, no matter if the certificate is new or a renewal; however,
it has no flag to do so.

We move to approximating running the deploy hooks, if the certificate
is new.  This makes it easier to have identical behaviour between
initial install and later renewals.

This requires moving the symlinking step itself to a deploy hook, so
it can run before nginx and the email server are restarted.  Our one
consumer of the `--skip-symlink` flag, Docker, can work around its
removal.  The symlink step is a no-op if `$ZULIP_DOMAIN` is unset,
i.e. during automated renewals.  This ensures that system that
installed certbot, but has since changed to manual certificate
management, does not have those certificates overridden next time
certbot renews itself.

(cherry picked from commit 0c953f9870)
2025-11-26 14:18:48 -05:00

137 lines
2.8 KiB
Bash
Executable File

#!/usr/bin/env bash
set -e
usage() {
cat <<EOF >&2
Usage: $0 --email=admin@example.com [--method={webroot|standalone}] \
hostname.example.com [another.example.com]
EOF
exit 1
}
if [ "$EUID" -ne 0 ]; then
echo "Error: This script must be run as root" >&2
exit 1
fi
method=webroot
args="$(getopt -o '' --long help,email:,method:,agree-tos -n "$0" -- "$@")"
eval "set -- $args"
agree_tos=()
while true; do
case "$1" in
--email)
EMAIL="$2"
shift
shift
;;
--method)
method="$2"
shift
shift
;;
--agree-tos)
agree_tos=(--agree-tos)
shift
;;
--help)
show_help=1
shift
;;
--)
shift
break
;;
esac
done
# Parse the remaining arguments as Subject Alternative Names to pass to certbot
HOSTNAMES=()
for arg; do
HOSTNAMES+=(-d "$arg")
done
DOMAIN=$1
if [ -n "$show_help" ]; then
usage
fi
if [ -z "$DOMAIN" ] || [ -z "$EMAIL" ]; then
usage
fi
case "$method" in
standalone)
method_args=(--standalone --no-directory-hooks)
;;
webroot)
method_args=(--webroot '--webroot-path=/var/lib/zulip/certbot-webroot/')
;;
*)
usage
;;
esac
# Check for a supported OS release.
if [ -f /etc/os-release ]; then
os_info="$(
. /etc/os-release
printf '%s\n' "$ID" "$ID_LIKE"
)"
{
read -r os_id
read -r os_id_like || true
} <<<"$os_info"
fi
set -x
case " $os_id $os_id_like " in
*' debian '*)
apt-get update
apt-get install -y certbot
;;
*' rhel '*)
yum install -y certbot
;;
esac
# If we aren't being run interactively, default to keeping the
# existing certificate (rather than burning through a renewal)
# If run interactively, certbot will prompt.
default_keep=()
if [ ! -t 0 ]; then
default_keep=(--keep-until-expiring)
fi
# We need to know _which_ domain is Zulip's, in the symlink deploy
# hook, so we pass this down
export ZULIP_DOMAIN="$DOMAIN"
# Certbot does not run deploy hooks on new certificates
# (certbot/certbot#9978) so we will need to fake it if so
if [ -d "/etc/letsencrypt/live/$DOMAIN/" ]; then
needs_hooks=0
else
needs_hooks=1
fi
certbot certonly "${method_args[@]}" \
"${HOSTNAMES[@]}" -m "$EMAIL" \
"${agree_tos[@]}" \
"${default_keep[@]}" \
--no-eff-email
# "certbot certonly" before version 3.2.0 does not run deploy hooks,
# so we fake running them.
if [ "$needs_hooks" = "1" ]; then
export RENEWED_DOMAINS="$*"
if [ "$method" == "webroot" ]; then
for deploy_hook in /etc/letsencrypt/renewal-hooks/deploy/*; do
"$deploy_hook"
done
fi
fi
echo "Certbot SSL certificate configuration succeeded."