#!/usr/bin/env bash set -e usage() { cat <&2 Usage: $0 --email=admin@example.com [--method={webroot|standalone}] \ [--no-zulip-conf] 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:,deploy-hook:,no-zulip-conf,agree-tos -n "$0" -- "$@")" eval "set -- $args" while true; do case "$1" in --email) EMAIL="$2" shift shift ;; --method) method="$2" shift shift ;; --deploy-hook) deploy_hook=(--deploy-hook "$2") shift shift ;; --agree-tos) agree_tos=--agree-tos shift ;; --no-zulip-conf) no_zulip_conf=1 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) ;; 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 # We don't use --no-interactive, because certbot needs to ask the user # to agree to the Let's Encrypt Subscriber Agreement (aka ToS). # Passing --force-interactive suppresses a warning, but also brings up # an annoying prompt we stifle with --no-eff-email. certbot certonly "${method_args[@]}" \ "${HOSTNAMES[@]}" -m "$EMAIL" \ $agree_tos \ "${deploy_hook[@]}" \ --force-interactive --no-eff-email symlink_with_backup() { if [ -e "$2" ]; then # If the user is setting up our automatic certbot-management on a # system that already has certs for Zulip, use some extra caution # to keep the old certs available. mv -f --backup=numbered "$2" "$2".setup-certbot || true fi ln -nsf "$1" "$2" } if [ ${#deploy_hook} -eq 0 ]; then # If no deploy hook was specified, assume we're deploying to the default # location Zulip wants. CERT_DIR=/etc/letsencrypt/live/"$DOMAIN" symlink_with_backup "$CERT_DIR"/privkey.pem /etc/ssl/private/zulip.key symlink_with_backup "$CERT_DIR"/fullchain.pem /etc/ssl/certs/zulip.combined-chain.crt fi case "$method" in webroot) service nginx reload ;; esac if [ -z "$no_zulip_conf" ]; then crudini --set /etc/zulip/zulip.conf certbot auto_renew yes fi echo "Certbot SSL certificate configuration succeeded."