update certbot-ocsp-fetcher

This commit is contained in:
Daniel Micay 2023-07-09 18:16:59 -04:00
parent 462bdc8599
commit 5f339efb2d
3 changed files with 293 additions and 85 deletions

View File

@ -10,50 +10,94 @@ set \
IFS=$'\n\t' IFS=$'\n\t'
shopt -s inherit_errexit shopt -s inherit_errexit
determine_colored_output() {
declare -gl COLORED_STDOUT COLORED_STDERR
readonly GREEN='\033[0;32m'
readonly RED='\033[0;31m'
readonly COLOR_DEFAULT='\033[0m'
if [[ -v NO_COLOR || ${TERM-} == dumb ]]; then
COLORED_STDOUT=false COLORED_STDERR=false
else
[[ -t 1 ]] || COLORED_STDOUT=false
[[ -t 2 ]] || COLORED_STDERR=false
fi
}
exit_with_error() { exit_with_error() {
echo "${@}" >&2 local error_prefix=error:$'\t\t'
[[ ${COLORED_STDERR-} != false ]] &&
local -r COLORED_ERROR_MSG=${RED}${error_prefix}${*}${COLOR_DEFAULT}
# We will have closed file descriptor 2 unless verbosity was requested, so we
# will try to use FD5 (the FD that stderr was likely redirected to), and
# fallback to FD2 if FD5 wasn't opened yet.
if [[ -f /dev/fd/5 ]]; then
exec >&5
else
exec >&2
fi
printf '%b\n' "${COLORED_ERROR_MSG:-${error_prefix}${@}}"
exit 1 exit 1
} }
check_for_dependencies() { check_for_dependencies() {
if ((BASH_VERSINFO[0] == 4 && \ if ((BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 3 || BASH_VERSINFO[0] < 4)); then
BASH_VERSINFO[1] < 3 || \ exit_with_error "${0##*/} requires Bash 4.3+."
BASH_VERSINFO[0] < 4)); then
exit_with_error \
error:$'\t\t'"${0##*/} requires Bash 4.3+."
fi fi
if ! { command -v openssl >&- && if ! { command -v openssl >&- &&
[[ $(openssl version) =~ ^OpenSSL\ ([[:digit:]]+)\.([[:digit:]]+) ]] && [[ $(openssl version || true) =~ ^OpenSSL\ ([[:digit:]]+)\.([[:digit:]]+) ]] &&
((BASH_REMATCH[1] == 1 && \ ((BASH_REMATCH[1] == 1 && BASH_REMATCH[2] >= 1 || BASH_REMATCH[1] > 1)); }; then
BASH_REMATCH[2] >= 1 || \
BASH_REMATCH[1] > 1)); }; then
# shellcheck disable=2016 # shellcheck disable=2016
exit_with_error \ exit_with_error \
error:$'\t\t'"${0##*/} requires OpenSSL 1.1.0+," \ "${0##*/} requires OpenSSL 1.1.0+," \
'but it is not available on $PATH.' 'but it is not available on $PATH.'
fi fi
} }
parse_cli_arguments() { parse_cli_options() {
local -r usage=( local -r cli_options="
"USAGE: ${0}" Usage: ${0} [-c/--certbot-dir DIRECTORY] [-f/--force-update] \\
"[-c/--certbot-dir DIRECTORY]" [-h/--help] [-l/--no-color] [-n/--cert-name NAME[,NAME...] \\
"[-f/--force-update]" [-u/--ocsp-responder URL]] [-o/--output-dir DIRECTORY] \\
"[-h/--help]" [-q/--quiet|-v/--verbose] [-w/--no-reload-webserver]
"[-n/--cert-name NAME[,NAME...] [-u/--ocsp-responder URL]]" "
"[-o/--output-dir DIRECTORY]"
"[-q/--quiet]" print_option_error() {
"[-v/--verbose]" local reason=${1} option=${2}
"[-w/--no-reload-webserver]" shift 2
) local option_error="${option}: "
case ${reason} in
--conflict)
local second_option=${1}
shift
option_error+="This option cannot be combined with the option ${second_option}."
;;
--duplicate)
option_error+="This option cannot be specified multiple times."
;;
--unknown)
option_error+="Invalid option."
;;
--value)
option_error+="This option requires a value."
;;
*)
exit 1
;;
esac
exit_with_error "${option_error}" "${cli_options}"
}
declare -gl ERROR_ENCOUNTERED declare -gl ERROR_ENCOUNTERED
declare -gi VERBOSITY=1 declare -gi VERBOSITY=${VERBOSITY:-1}
local -r verbosity_error=(
"error: -q/--quiet cannot be specified in conjunction with -v/--verbose."
)
while ((${#} > 0)); do while ((${#} > 0)); do
local parameter=${1} local parameter=${1}
@ -64,17 +108,17 @@ parse_cli_arguments() {
;; ;;
-c | --certbot-dir | --certbot-dir=?*) -c | --certbot-dir | --certbot-dir=?*)
if [[ -v CERTBOT_DIR ]]; then if [[ -v CERTBOT_DIR ]]; then
exit_with_error "${usage[@]}" print_option_error --duplicate "${parameter}"
fi fi
if [[ ${parameter} =~ --certbot-dir=(.+) ]]; then if [[ ${parameter} =~ --certbot-dir=(.+) ]]; then
CERTBOT_DIR=${BASH_REMATCH[1]} CERTBOT_DIR=${BASH_REMATCH[1]}
else else
if [[ -n ${2:-} ]]; then if [[ -n ${2-} ]]; then
CERTBOT_DIR=${2} CERTBOT_DIR=${2}
shift shift
else else
exit_with_error "${usage[@]}" print_option_error --value "${parameter}"
fi fi
fi fi
@ -95,19 +139,59 @@ parse_cli_arguments() {
shift shift
;; ;;
-h | --help) -h | --help)
echo >&2 "${usage[@]}" {
printf '%s\n' certbot-ocsp-fetcher
printf '%s\n' "${cli_options}"
local absolute_tool_path
absolute_tool_path=$(realpath --no-symlinks -- "${0}")
readonly absolute_tool_path
cat <<EOSTRING
certbot-ocsp-fetcher helps you setup OCSP stapling in nginx. The tool primes
nginx's OCSP cache to work around nginx's flawed OCSP stapling implementation.
The tool does this by fetching and saving OCSP responses for TLS certificates
issued with Certbot.
---
Example:
1. Fetch OCSP responses for all certificates managed by Certbot, and save
them in the current working directory. This should usually be run on a
schedule, e.g. as a cronjob or systemd timer.
$ ${0}
2. Add the path(s) to the resulting OCSP response(s) as the value of the
ssl_stapling_file directive in the corresponding vhosts in Nginx. Don't
forget to reload Nginx afterwards.
3. Re-issue all certificates managed by Certbot, to add the OCSP Must-Staple
flag to the certs and automatically run certbot-ocsp-fetcher during renewals:
$ certbot renew --deploy-hook ${absolute_tool_path} --force-renewal --must-staple
---
See the online README for an explanation of all the CLI options:
https://github.com/tomwassenberg/certbot-ocsp-fetcher/blob/main/README.md
EOSTRING
} >&2
exit exit
;; ;;
-l | --no-color)
readonly COLORED_STDOUT=false COLORED_STDERR=false
;;
-n | --cert-name | --cert-name=?*) -n | --cert-name | --cert-name=?*)
if [[ ${parameter} =~ --cert-name=(.+) ]]; then if [[ ${parameter} =~ --cert-name=(.+) ]]; then
local cert_lineages_value=${BASH_REMATCH[1]} local cert_lineages_value=${BASH_REMATCH[1]}
shift shift
else else
if [[ -n ${2:-} ]]; then if [[ -n ${2-} ]]; then
local cert_lineages_value=${2} local cert_lineages_value=${2}
shift 2 shift 2
else else
exit_with_error "${usage[@]}" print_option_error --value "${parameter}"
fi fi
fi fi
@ -117,15 +201,15 @@ parse_cli_arguments() {
declare -Ag CERT_LINEAGES declare -Ag CERT_LINEAGES
# Check if a hardcoded OCSP responder was specified for this set of # Check if a hardcoded OCSP responder was specified for this set of
# lineages. # lineages.
case ${1:-} in case ${1-} in
-u | --ocsp-responder) -u | --ocsp-responder)
if [[ -n ${2:-} ]]; then if [[ -n ${2-} ]]; then
for lineage_name in ${cert_lineages_value}; do for lineage_name in ${cert_lineages_value}; do
CERT_LINEAGES["${lineage_name}"]=${2} CERT_LINEAGES["${lineage_name}"]=${2}
done done
shift shift
else else
exit_with_error "${usage[@]}" print_option_error --value "${parameter}"
fi fi
shift shift
;; ;;
@ -149,17 +233,17 @@ parse_cli_arguments() {
;; ;;
-o | --output-dir | --output-dir=?*) -o | --output-dir | --output-dir=?*)
if [[ -v OUTPUT_DIR ]]; then if [[ -v OUTPUT_DIR ]]; then
exit_with_error "${usage[@]}" print_option_error --duplicate "${parameter}"
fi fi
if [[ ${parameter} =~ --output-dir=(.+) ]]; then if [[ ${parameter} =~ --output-dir=(.+) ]]; then
OUTPUT_DIR=${BASH_REMATCH[1]} OUTPUT_DIR=${BASH_REMATCH[1]}
else else
if [[ -n ${2:-} ]]; then if [[ -n ${2-} ]]; then
OUTPUT_DIR=${2} OUTPUT_DIR=${2}
shift shift
else else
exit_with_error "${usage[@]}" print_option_error --value "${parameter}"
fi fi
fi fi
@ -175,7 +259,7 @@ parse_cli_arguments() {
;; ;;
-q | --quiet) -q | --quiet)
if ((VERBOSITY != 1)); then if ((VERBOSITY != 1)); then
exit_with_error "${verbosity_error[@]}" print_option_error --conflict "${parameter}" -v/--verbose
else else
readonly VERBOSITY=0 readonly VERBOSITY=0
shift shift
@ -183,7 +267,7 @@ parse_cli_arguments() {
;; ;;
-v | --verbose) -v | --verbose)
if ((VERBOSITY == 0)); then if ((VERBOSITY == 0)); then
exit_with_error "${verbosity_error[@]}" print_option_error --conflict "${parameter}" -q/--quiet
else else
VERBOSITY+=1 VERBOSITY+=1
shift shift
@ -196,11 +280,19 @@ parse_cli_arguments() {
shift shift
;; ;;
*) *)
exit_with_error "${usage[@]}" print_option_error --unknown "${parameter}"
;; ;;
esac esac
done done
# Respect the common "DEBUG" environment variable if set, unless the --quiet
# or --verbose flag has been passed as well.
if ((${DEBUG:-0} >= 1)) && ((VERBOSITY == 1)); then
# We set VERBOSITY to 0 in case of --quiet, so use the value of $DEBUG
# incremented with 1 to match it with $VERBOSITY.
VERBOSITY=$((DEBUG + 1))
fi
# When not parsed, the stdout and/or stderr output of all external commands # When not parsed, the stdout and/or stderr output of all external commands
# we call in the script is redirected to file descriptor 3. Depending on the # we call in the script is redirected to file descriptor 3. Depending on the
# desired verbosity, we redirect this file descriptor to either stderr or to # desired verbosity, we redirect this file descriptor to either stderr or to
@ -210,6 +302,13 @@ parse_cli_arguments() {
else else
exec 3>/dev/null exec 3>/dev/null
fi fi
# First copy file descriptor 2 to a new FD, so stderr can still be used
# (unconditionally) in the exit_with_error function.
exec 5>&2
if ((VERBOSITY < 1)); then
exec 2>/dev/null
fi
} }
# Set output directory if necessary and check if it's writeable # Set output directory if necessary and check if it's writeable
@ -223,12 +322,13 @@ prepare_output_dir() {
-- "${OUTPUT_DIR}" || true -- "${OUTPUT_DIR}" || true
fi fi
else else
readonly OUTPUT_DIR=. # Use $CACHE_DIRECTORY if set (e.g. when run as a systemd service),
# otherwise the working directory
readonly OUTPUT_DIR=${CACHE_DIRECTORY:-.}
fi fi
if [[ ! -w ${OUTPUT_DIR} ]]; then if [[ ! -w ${OUTPUT_DIR} ]]; then
exit_with_error \ exit_with_error "no write access to output directory (\"${OUTPUT_DIR}\")"
error:$'\t\t'"no write access to output directory (\"${OUTPUT_DIR}\")"
fi fi
} }
@ -255,26 +355,26 @@ start_in_correct_mode() {
# Run in "check one or all certificate lineage(s) managed by Certbot" mode # Run in "check one or all certificate lineage(s) managed by Certbot" mode
# $1 - Path to temporary output directory # $1 - Path to temporary output directory
run_standalone() { run_standalone() {
printf >&2 '%s\n\n' "Running in stand-alone mode..."
readonly CERTBOT_DIR=${CERTBOT_DIR:-/etc/letsencrypt} readonly CERTBOT_DIR=${CERTBOT_DIR:-/etc/letsencrypt}
if [[ ! -r ${CERTBOT_DIR} || (-d ${CERTBOT_DIR}/live && ! -r ${CERTBOT_DIR}/live) ]]; then if [[ ! -r ${CERTBOT_DIR} || (-d ${CERTBOT_DIR}/live && ! -r ${CERTBOT_DIR}/live) ]]; then
exit_with_error \ exit_with_error "can't access ${CERTBOT_DIR}/live"
error:$'\t\t'"can't access ${CERTBOT_DIR}/live"
fi fi
# Check specific lineage if passed on CLI, # Check specific lineage if passed on CLI,
# or otherwise all lineages in Certbot's dir # or otherwise all lineages in Certbot's dir
if [[ -v CERT_LINEAGES[@] ]]; then if [[ -n ${!CERT_LINEAGES[*]} ]]; then
for lineage_name in "${!CERT_LINEAGES[@]}"; do for lineage_name in "${!CERT_LINEAGES[@]}"; do
if [[ -r ${CERTBOT_DIR}/live/${lineage_name} ]]; then if [[ -r ${CERTBOT_DIR}/live/${lineage_name} ]]; then
fetch_ocsp_response \ fetch_ocsp_response \
"--standalone" \ --standalone \
"${temp_output_dir}" \ "${temp_output_dir}" \
"${lineage_name}" \ "${lineage_name}" \
"${CERT_LINEAGES["${lineage_name}"]}" "${CERT_LINEAGES["${lineage_name}"]}"
else else
exit_with_error \ exit_with_error "can't access ${CERTBOT_DIR}/live/${lineage_name}"
"error:"$'\t\t'"can't access ${CERTBOT_DIR}/live/${lineage_name}"
fi fi
done done
else else
@ -287,7 +387,7 @@ run_standalone() {
[[ -d ${lineage_dir} ]] || continue [[ -d ${lineage_dir} ]] || continue
fetch_ocsp_response \ fetch_ocsp_response \
"--standalone" "${temp_output_dir}" "${lineage_dir##*/}" --standalone "${temp_output_dir}" "${lineage_dir##*/}"
done done
unset lineage_dir unset lineage_dir
fi fi
@ -296,11 +396,13 @@ run_standalone() {
# Run in deploy-hook mode, only processing the passed lineage # Run in deploy-hook mode, only processing the passed lineage
# $1 - Path to temporary output directory # $1 - Path to temporary output directory
run_as_deploy_hook() { run_as_deploy_hook() {
printf >&2 '%s\n\n' "Running as a deploy hook of Certbot..."
if [[ -v CERTBOT_DIR ]]; then if [[ -v CERTBOT_DIR ]]; then
# The directory is already inferred from the environment variable that # The directory is already inferred from the environment variable that
# Certbot passes # Certbot passes
exit_with_error \ exit_with_error \
error:$'\t\t'"-c/--certbot-dir cannot be passed" \ "-c/--certbot-dir cannot be passed" \
"when run as Certbot hook" "when run as Certbot hook"
fi fi
@ -308,15 +410,14 @@ run_as_deploy_hook() {
# When run as deploy hook the behavior of this flag is used by default. # When run as deploy hook the behavior of this flag is used by default.
# Therefore passing this flag would not have any effect. # Therefore passing this flag would not have any effect.
exit_with_error \ exit_with_error \
error:$'\t\t'"-f/--force-update cannot be passed" \ "-f/--force-update cannot be passed" \
"when run as Certbot hook" "when run as Certbot hook"
fi fi
if [[ -v CERT_LINEAGES[@] ]]; then if [[ -n ${!CERT_LINEAGES[*]} ]]; then
# The certificate lineage is already inferred from the environment # The certificate lineage is already inferred from the environment
# variable that Certbot passes # variable that Certbot passes
exit_with_error \ exit_with_error "-n/--cert-name cannot be passed when run as Certbot hook"
error:$'\t\t'"-n/--cert-name cannot be passed when run as Certbot hook"
fi fi
fetch_ocsp_response \ fetch_ocsp_response \
@ -349,14 +450,23 @@ check_for_existing_ocsp_staple_file() {
local -r next_update=${BASH_REMATCH[1]} local -r next_update=${BASH_REMATCH[1]}
fi fi
done done
[[ -n ${this_update:-} && -n ${next_update:-} ]] || return 1 [[ -n ${this_update-} && -n ${next_update-} ]] || return 1
# Only continue fetching OCSP response if existing response expires within # Only continue fetching OCSP response if existing response expires within
# half of its lifetime. # half of its lifetime.
local -ri response_lifetime_in_seconds=$((\ {
$(date +%s --date "${next_update}") - $(date +%s --date "${this_update}"))) # The command substitutions here don't respect `set -o errexit`, but in
(($(date +%s) < \ # case any of them fail, the total command still fails unless both
$(date +%s --date "${this_update}") + response_lifetime_in_seconds / 2)) || return 1 # substitutions print an integer. This seems very unlikely to occur, so
# let's ignore this.
# shellcheck disable=2312
local -ri response_lifetime_in_seconds=$(($(date +%s --date "${next_update}") - $(date +%s --date "${this_update}")))
# `set -o errexit` isn't respected here either, but we default to renewing
# the OCSP response, so this is fine.
# shellcheck disable=2312
(($(date +%s) < $(date +%s --date "${this_update}") + response_lifetime_in_seconds / 2)) || return 1
}
} }
# Generate file used by ssl_stapling_file in nginx config of websites # Generate file used by ssl_stapling_file in nginx config of websites
@ -367,11 +477,27 @@ check_for_existing_ocsp_staple_file() {
fetch_ocsp_response() { fetch_ocsp_response() {
local -r temp_output_dir=${2} local -r temp_output_dir=${2}
local -r lineage_name=${3} local -r lineage_name=${3}
# This validation should be revisited once
# https://github.com/certbot/certbot/issues/6127 is fixed.
if [[ ${lineage_name} =~ ($'\n')|($'\t') ]]; then
ERROR_ENCOUNTERED=true
exit_with_error \
"Unsupported characters encountered in the following" \
"lineage name: ${lineage_name}$'\n\n'" \
"Lineage names with embedded tabs or newlines are not supported," \
"because Certbot (as of version 1.18.0) does not have well-defined" \
'behavior on handling any "unconventional" lineage names.'
fi
case ${1} in case ${1} in
--standalone) --standalone)
local -r lineage_dir=${CERTBOT_DIR}/live/${lineage_name} local -r lineage_dir=${CERTBOT_DIR}/live/${lineage_name}
if [[ ${FORCE_UPDATE:-} != true ]] && # `set -o errexit` is not respected here, but in case of failure we still
# err on the safe side by renewing the OCSP staple file.
# shellcheck disable=2310
if [[ ${FORCE_UPDATE-} != true ]] &&
check_for_existing_ocsp_staple_file; then check_for_existing_ocsp_staple_file; then
lineages_processed["${lineage_name}"]="not updated"$'\t'"valid staple file on disk" lineages_processed["${lineage_name}"]="not updated"$'\t'"valid staple file on disk"
return return
@ -398,7 +524,7 @@ fetch_ocsp_response() {
set -e set -e
if ((cert_expiry_rc != 0)); then if ((cert_expiry_rc != 0)); then
ERROR_ENCOUNTERED=true ERROR_ENCOUNTERED=true
lineages_processed["${lineage_name}"]="not updated" lineages_processed["${lineage_name}"]="failed to update"
if [[ ${cert_expiry_output} == "Certificate will expire" ]]; then if [[ ${cert_expiry_output} == "Certificate will expire" ]]; then
lineages_processed["${lineage_name}"]+=$'\t'"leaf certificate expired" lineages_processed["${lineage_name}"]+=$'\t'"leaf certificate expired"
fi fi
@ -429,13 +555,13 @@ fetch_ocsp_response() {
-respout "${temp_output_dir}/${lineage_name}.der" 2>&3) -respout "${temp_output_dir}/${lineage_name}.der" 2>&3)
local -ir ocsp_call_rc=${?} local -ir ocsp_call_rc=${?}
set -e set -e
readonly ocsp_call_output=${ocsp_call_output#${lineage_dir}/cert.pem: } readonly ocsp_call_output=${ocsp_call_output#"${lineage_dir}"/cert.pem: }
local -r cert_status=${ocsp_call_output%%$'\n'*} local -r cert_status=${ocsp_call_output%%$'\n'*}
if [[ ${ocsp_call_rc} != 0 || ${cert_status} != good ]]; then if [[ ${ocsp_call_rc} != 0 || ${cert_status} != good ]]; then
ERROR_ENCOUNTERED=true ERROR_ENCOUNTERED=true
lineages_processed["${lineage_name}"]="not updated" lineages_processed["${lineage_name}"]="failed to update"
if ((VERBOSITY >= 2)); then if ((VERBOSITY >= 2)); then
lineages_processed["${lineage_name}"]+=$'\t'"${ocsp_call_output//[[:space:]]/ }" lineages_processed["${lineage_name}"]+=$'\t'"${ocsp_call_output//[[:space:]]/ }"
else else
@ -455,44 +581,80 @@ fetch_ocsp_response() {
print_and_handle_result() { print_and_handle_result() {
local -r header=LINEAGE$'\t'RESULT$'\t'REASON local -r header=LINEAGE$'\t'RESULT$'\t'REASON
local lineages_processed_marked_up
for lineage_name in "${!lineages_processed[@]}"; do for lineage_name in "${!lineages_processed[@]}"; do
local lineages_processed_formatted+=$'\n'"${lineage_name}"$'\t'"${lineages_processed["${lineage_name}"]}" lineages_processed_marked_up+=$'\n'"${lineage_name}"$'\t'
if [[ ${COLORED_STDOUT-} != false ]]; then
if [[ ${lineages_processed["${lineage_name}"]} =~ ^updated ]]; then
lineages_processed_marked_up+=${GREEN}
elif [[ ${lineages_processed["${lineage_name}"]} =~ ^"failed to update" ]]; then
lineages_processed_marked_up+=${RED}
fi
lineages_processed_marked_up+=${lineages_processed["${lineage_name}"]}${COLOR_DEFAULT}
else
lineages_processed_marked_up+=${lineages_processed["${lineage_name}"]}
fi
done done
unset lineage_name unset lineage_name
lineages_processed_formatted=$(sort <<<"${lineages_processed_formatted:-}") lineages_processed_marked_up=$(sort <<<"${lineages_processed_marked_up-}")
readonly lineages_processed_formatted readonly lineages_processed_marked_up
if [[ ${RELOAD_WEBSERVER:-} != false ]]; then if [[ ${RELOAD_WEBSERVER-} != false ]]; then
reload_webserver reload_webserver
fi fi
local -r output=${header}${lineages_processed_formatted:-}${nginx_status-} local output=${header}${lineages_processed_marked_up-}${nginx_status-}
if ((VERBOSITY >= 1)); then if ((VERBOSITY >= 1)); then
if command -v column >&-; then local output_table
column -ts$'\t' <<<"${output}"
else
# shellcheck disable=2016 # shellcheck disable=2016
echo >&2 \ output_table=$(column \
'Install the BSD utility `column` for properly formatted output.'$'\n' --output-separator $'\t' \
echo "${output}" --separator $'\t' \
--table \
<<<"${output}" \
2>/dev/null) ||
output_table=$(column -s$'\t' -t <<<"${output}" 2>/dev/null) ||
local -r column_error=($'\n'
'Install the BSD utility `column` for properly formatted output.'
'If the version of `column` supports the `--output-separator` flag,'
'the output will be formatted as TSV.'
$'\n'
)
readonly output=${output_table:-${output}}
unset output_table
# Extract header to direct it to stderr
printf '%s\n' "${output%%$'\n'*}" >&2
# Remove header before printing everything else to stdout
[[ -n ${!lineages_processed[*]} ]] && printf '%b\n' "${output#*$'\n'}"
if [[ ${COLORED_STDERR-} != false ]]; then
printf %b "${RED}${column_error[*]-}${COLOR_DEFAULT}" >&2
else
printf %b "${column_error[*]-}" >&2
fi fi
fi fi
[[ ${ERROR_ENCOUNTERED:-} != true ]] [[ ${ERROR_ENCOUNTERED-} != true ]]
} }
reload_webserver() { reload_webserver() {
for lineage_name in "${!lineages_processed[@]}"; do for lineage_name in "${!lineages_processed[@]}"; do
if [[ ${lineages_processed["${lineage_name}"]} == updated ]]; then if [[ ${lineages_processed["${lineage_name}"]} == updated ]]; then
local nginx_status
if nginx -s reload >&3 2>&1; then if nginx -s reload >&3 2>&1; then
[[ ${COLORED_STDERR-} != false ]] && nginx_status=${GREEN}
# The last line includes a leading space, to workaround the lack of the # The last line includes a leading space, to workaround the lack of the
# `-n` flag in later versions of `column`. # `-n` flag in later versions of `column`.
local -r nginx_status=$'\n\n \t'"nginx reloaded" nginx_status+=$'\n\n \t'"nginx reloaded"
else else
ERROR_ENCOUNTERED=true ERROR_ENCOUNTERED=true
local -r nginx_status=$'\n\n \t'"nginx not reloaded"$'\t'"unable to reload nginx service, try manually" [[ ${COLORED_STDERR-} != false ]] && nginx_status=${RED}
nginx_status=$'\n\n \t'"nginx not reloaded"$'\t'"unable to reload nginx service, try manually"
fi fi
[[ ${COLORED_STDERR-} != false ]] &&
readonly nginx_status+=${COLOR_DEFAULT}
break break
fi fi
done done
@ -502,7 +664,9 @@ reload_webserver() {
main() { main() {
check_for_dependencies check_for_dependencies
parse_cli_arguments "${@}" determine_colored_output
parse_cli_options "${@}"
prepare_output_dir prepare_output_dir

View File

@ -4,10 +4,54 @@ Description=Fetch OCSP responses for all certificates issued with Certbot
[Service] [Service]
Type=oneshot Type=oneshot
# When systemd v244+ is available, this should be uncommented to enable retries
# on failure.
Restart=on-failure Restart=on-failure
CacheDirectory=%N
User=root User=root
Group=root Group=root
ExecStart=/usr/local/bin/certbot-ocsp-fetcher -o /etc/nginx/ocsp-cache ExecStart=%N --no-reload-webserver
ExecStartPost=systemctl reload nginx.service
RestartSec=5
PrivateDevices=true
PrivateTmp=yes
PrivateUsers=yes
PrivateIPC=true
NoNewPrivileges=true
LockPersonality=true
CapabilityBoundingSet=
ProtectHome=yes
ProtectControlGroups=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectClock=true
ProtectProc=invisible
ProcSubset=pid
ProtectHostname=true
RemoveIPC=true
RestrictAddressFamilies=AF_INET6 AF_INET AF_UNIX
MemoryDenyWriteExecute=true
RestrictRealtime=true
RestrictNamespaces=true
RestrictSUIDSGID=true
DevicePolicy=strict
DeviceAllow=/dev/random r
DeviceAllow=/dev/urandom r
DeviceAllow=/dev/stdin r
DeviceAllow=/dev/stdout r
DeviceAllow=/dev/null w
ProtectSystem=strict
InaccessiblePaths=/root/
ReadOnlyPaths=/etc/letsencrypt
UMask=0077
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@clock @debug @module @mount @reboot @swap @resources @cpu-emulation @raw-io @obsolete @keyring @privileged

View File

@ -1,5 +1,5 @@
[Unit] [Unit]
Description=Nightly run certbot-ocsp-fetcher Description=Nightly run %N
[Timer] [Timer]
OnCalendar=*-*-* 01:00:00 OnCalendar=*-*-* 01:00:00