640 lines
19 KiB
Bash
Executable File
640 lines
19 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
|
|
source "$PLUGIN_CORE_AVAILABLE_PATH/certs/functions"
|
|
source "$PLUGIN_CORE_AVAILABLE_PATH/domains/functions"
|
|
source "$PLUGIN_CORE_AVAILABLE_PATH/nginx-vhosts/functions"
|
|
if [[ -f "$PLUGIN_CORE_AVAILABLE_PATH/nginx-vhosts/internal-functions" ]]; then
|
|
source "$PLUGIN_CORE_AVAILABLE_PATH/nginx-vhosts/internal-functions"
|
|
fi
|
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/config"
|
|
set -eo pipefail
|
|
[[ $DOKKU_TRACE ]] && set -x
|
|
|
|
fn-letsencrypt-acme-execute-challenge() {
|
|
declare desc="perform actual ACME validation procedure"
|
|
declare APP="$1"
|
|
local FAKE_NGINX_CONF=false
|
|
local challenge_mode config_dir
|
|
|
|
if [[ ! -f "$DOKKU_ROOT/$APP/nginx.conf" ]]; then
|
|
FAKE_NGINX_CONF=true
|
|
fi
|
|
|
|
fn-letsencrypt-create-root "$APP"
|
|
|
|
challenge_mode="HTTP-01"
|
|
dns_provider="$(fn-letsencrypt-computed-dns-provider "$APP")"
|
|
if [[ -n "$dns_provider" ]]; then
|
|
challenge_mode="DNS-01"
|
|
fi
|
|
|
|
dokku_log_info1 "Getting letsencrypt certificate for ${APP} via ${challenge_mode}"
|
|
|
|
# read arguments from appropriate config file into the config array
|
|
config_dirs="$(fn-letsencrypt-configure-and-get-dir "$APP")"
|
|
host_config_dir="$(echo "$config_dirs" | cut -d: -f1)"
|
|
container_config_dir="$(echo "$config_dirs" | cut -d: -f2)"
|
|
read -r -a config <"$container_config_dir/config"
|
|
|
|
# run letsencrypt as a docker container using "certonly" mode
|
|
# port 80 of the standalone webserver will be forwarded by the proxy
|
|
set +e
|
|
export DOKKU_UID=$(id -u)
|
|
export DOKKU_GID=$(id -g)
|
|
mkdir -p "$DOKKU_LIB_ROOT/data/letsencrypt/$APP"
|
|
docker run --rm \
|
|
--env-file "$host_config_dir/docker.env" \
|
|
--user $DOKKU_UID:$DOKKU_GID \
|
|
-v "$host_config_dir:/certs" \
|
|
-v "$DOKKU_LIB_ROOT/data/letsencrypt/$APP:/webroot" \
|
|
"${PLUGIN_IMAGE}:${PLUGIN_IMAGE_VERSION}" \
|
|
"${config[@]}" run | sed "s/^/ /"
|
|
|
|
local exit_code=$?
|
|
set -e
|
|
|
|
if [[ "$FAKE_NGINX_CONF" == "true" ]]; then
|
|
rm "$DOKKU_ROOT/$APP/nginx.conf"
|
|
fi
|
|
|
|
if [[ $exit_code != 0 ]]; then
|
|
dokku_log_info1 "Certificate retrieval failed!"
|
|
return $exit_code
|
|
fi
|
|
|
|
# got certificate
|
|
dokku_log_info1 "Certificate retrieved successfully."
|
|
fn-letsencrypt-symlink-certs "$APP" "$container_config_dir"
|
|
plugn trigger proxy-build-config "$APP"
|
|
}
|
|
|
|
fn-letsencrypt-acme-revoke() {
|
|
declare desc="perform actual certificate revocation"
|
|
local APP="$1"
|
|
|
|
fn-letsencrypt-create-root "$APP"
|
|
|
|
challenge_mode="HTTP-01"
|
|
dns_provider="$(fn-letsencrypt-computed-dns-provider "$APP")"
|
|
if [[ -n "$dns_provider" ]]; then
|
|
challenge_mode="DNS-01"
|
|
fi
|
|
|
|
dokku_log_info1 "Revoking letsencrypt certificate for ${APP} via ${challenge_mode}"
|
|
local acme_port="$(plugn trigger ports-get-available)"
|
|
if [[ -z "$acme_port" ]]; then
|
|
acme_port="$(get_available_port)"
|
|
fi
|
|
|
|
# read arguments from appropriate config file into the config array
|
|
config_dirs="$(fn-letsencrypt-configure-and-get-dir "$APP")"
|
|
host_config_dir="$(echo "$config_dirs" | cut -d: -f1)"
|
|
container_config_dir="$(echo "$config_dirs" | cut -d: -f2)"
|
|
read -r -a config <"$container_config_dir/config"
|
|
|
|
# run letsencrypt as a docker container using "certonly" mode
|
|
# port 80 of the standalone webserver will be forwarded by the proxy
|
|
set +e
|
|
export DOKKU_UID=$(id -u)
|
|
export DOKKU_GID=$(id -g)
|
|
mkdir -p "$DOKKU_LIB_ROOT/data/letsencrypt/$APP"
|
|
docker run --rm \
|
|
--env-file "$host_config_dir/docker.env" \
|
|
--user $DOKKU_UID:$DOKKU_GID \
|
|
-p "$acme_port:$acme_port" \
|
|
-v "$host_config_dir:/certs" \
|
|
-v "$DOKKU_LIB_ROOT/data/letsencrypt/$APP:/webroot" \
|
|
"${PLUGIN_IMAGE}:${PLUGIN_IMAGE_VERSION}" \
|
|
"${config[@]}" revoke | sed "s/^/ /"
|
|
|
|
local exit_code=$?
|
|
set -e
|
|
|
|
# handle return codes
|
|
if [[ $exit_code == 0 ]]; then
|
|
# certificate revoked
|
|
dokku_log_info1 "Certificate revoked successfully."
|
|
else
|
|
# error - don't try to link certificates
|
|
dokku_log_info1 "Certificate revocation failed (code $simple_result)!"
|
|
return
|
|
fi
|
|
|
|
local domain="$(get_app_domains "$APP" | xargs | awk '{print $1}')"
|
|
|
|
# removing the certificate will automatically reconfigure nginx
|
|
if [[ -z $DOKKU_APP_NAME ]]; then
|
|
dokku certs:remove "$APP"
|
|
else
|
|
dokku certs:remove
|
|
fi
|
|
}
|
|
|
|
fn-letsencrypt-acme-proxy-disable() {
|
|
declare desc="disable ACME proxy for an app"
|
|
local APP="$1"
|
|
|
|
local app_root="$DOKKU_ROOT/$APP"
|
|
local app_config_dir="$app_root/nginx.conf.d"
|
|
|
|
dokku_log_info1 "Disabling ACME proxy for $APP..."
|
|
|
|
[[ -f "$app_config_dir/letsencrypt.conf" ]] && rm "$app_config_dir/letsencrypt.conf"
|
|
|
|
restart_nginx | sed "s/^/ /"
|
|
}
|
|
|
|
fn-letsencrypt-acme-proxy-enable() {
|
|
declare desc="enable ACME proxy for an app"
|
|
local APP="$1"
|
|
|
|
local app_root="$DOKKU_ROOT/$APP"
|
|
local app_config_dir="$app_root/nginx.conf.d"
|
|
|
|
dokku_log_info1 "Enabling ACME proxy for ${APP}..."
|
|
|
|
# ensure the nginx.conf.d directory exists
|
|
[[ -d "$app_config_dir" ]] || mkdir "$app_config_dir"
|
|
|
|
# generate letsencrypt config
|
|
sigil -f "$PLUGIN_AVAILABLE_PATH/letsencrypt/templates/letsencrypt.conf.sigil" \
|
|
APP="$APP" \
|
|
DOKKU_LIB_ROOT="$DOKKU_LIB_ROOT" \
|
|
>"$app_config_dir/letsencrypt.conf"
|
|
|
|
restart_nginx | sed "s/^/ /"
|
|
}
|
|
|
|
fn-letsencrypt-computed-dns-provider() {
|
|
declare desc="get configured dns provider"
|
|
declare APP="$1"
|
|
|
|
value="$(fn-letsencrypt-dns-provider "$APP")"
|
|
if [[ -z "$value" ]]; then
|
|
value="$(fn-letsencrypt-global-dns-provider)"
|
|
fi
|
|
|
|
echo "$value"
|
|
}
|
|
|
|
fn-letsencrypt-global-dns-provider() {
|
|
declare desc="get configured dns provider"
|
|
|
|
fn-plugin-property-get-default "letsencrypt" "--global" "dns-provider" ""
|
|
}
|
|
|
|
fn-letsencrypt-dns-provider() {
|
|
declare desc="get configured dns provider"
|
|
declare APP="$1"
|
|
|
|
fn-plugin-property-get-default "letsencrypt" "$APP" "dns-provider" ""
|
|
}
|
|
|
|
fn-letsencrypt-computed-lego-docker-args() {
|
|
declare desc="get configured lego docker args"
|
|
declare APP="$1"
|
|
|
|
value="$(fn-letsencrypt-lego-docker-args "$APP")"
|
|
if [[ -z "$value" ]]; then
|
|
value="$(fn-letsencrypt-global-lego-docker-args)"
|
|
fi
|
|
|
|
echo "$value"
|
|
}
|
|
|
|
fn-letsencrypt-global-lego-docker-args() {
|
|
declare desc="get configured lego docker args"
|
|
|
|
fn-plugin-property-get-default "letsencrypt" "--global" "lego-docker-args" ""
|
|
}
|
|
|
|
fn-letsencrypt-lego-docker-args() {
|
|
declare desc="get configured lego docker args"
|
|
declare APP="$1"
|
|
|
|
fn-plugin-property-get-default "letsencrypt" "$APP" "lego-docker-args" ""
|
|
}
|
|
|
|
fn-letsencrypt-check-email() {
|
|
declare desc="Check if an e-mail address is provided globally or for the app"
|
|
declare APP="$1"
|
|
|
|
# check we have a valid e-mail address
|
|
if [[ -z "$(fn-letsencrypt-computed-email "$APP")" ]]; then
|
|
dokku_log_warn "ERROR: Cannot request a certificate without an e-mail address!"
|
|
dokku_log_warn " please provide your e-mail address using"
|
|
dokku_log_warn " dokku letsencrypt:set $APP email <e-mail>"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
fn-letsencrypt-configure-and-get-dir() {
|
|
declare desc="assemble lego command line arguments and create a config hash directory for them"
|
|
declare APP="$1"
|
|
local config config_dir config_hash dns_provider domain_args domains email extra_args cert_timeout key server value
|
|
|
|
local app_root="$DOKKU_ROOT/$APP"
|
|
local le_root="$app_root/letsencrypt"
|
|
mkdir -p "$DOKKU_ROOT/$APP/letsencrypt/account"
|
|
|
|
# build up a string of all certificate-controlling configuration settings.
|
|
# this will be used to determine the folder name for the account key and certificates
|
|
|
|
# get the selected ACME server
|
|
server="$(fn-letsencrypt-computed-server "$APP")"
|
|
|
|
# construct domain arguments
|
|
domains="$(get_app_domains "$APP")"
|
|
domain_args=''
|
|
for domain in $domains; do
|
|
dokku_log_verbose " - Domain '$domain'" >&2
|
|
domain_args="$domain_args --domains $domain"
|
|
done
|
|
|
|
# lego --cert.timeout is in seconds and defaults to 30
|
|
cert_timeout=30
|
|
email="$(fn-letsencrypt-computed-email "$APP")"
|
|
extra_args="$(fn-letsencrypt-computed-lego-docker-args "$APP")"
|
|
config="--pem --accept-tos --cert.timeout $cert_timeout --path /certs --server $server --email $email $extra_args $domain_args"
|
|
|
|
dns_provider="$(fn-letsencrypt-computed-dns-provider "$APP")"
|
|
if [[ -n "$dns_provider" ]]; then
|
|
config="--dns $dns_provider $config"
|
|
else
|
|
config="--http --http.webroot /webroot $config"
|
|
fi
|
|
|
|
config_hash=$(echo "$config" | sha1sum | awk '{print $1}')
|
|
config_dir="$le_root/certs/$config_hash"
|
|
mkdir -p "$config_dir"
|
|
|
|
rm -f "$config_dir/docker.env"
|
|
touch "$config_dir/docker.env"
|
|
if [[ -n "$dns_provider" ]]; then
|
|
fn-plugin-property-get-all "letsencrypt" "--global" | while read -r line; do
|
|
[[ -n "$line" ]] || continue
|
|
key="$(cut -d" " -f1 <<<"$line")"
|
|
if [[ "$key" == dns-provider-* ]]; then
|
|
value="$(cut -d" " -f2 <<<"$line")"
|
|
echo "${key#"dns-provider-"}=$value" >>"$config_dir/docker.env"
|
|
fi
|
|
done
|
|
fn-plugin-property-get-all "letsencrypt" "$APP" | while read -r line; do
|
|
[[ -n "$line" ]] || continue
|
|
key="$(cut -d" " -f1 <<<"$line")"
|
|
if [[ "$key" == dns-provider-* ]]; then
|
|
value="$(cut -d" " -f2 <<<"$line")"
|
|
echo "${key#"dns-provider-"}=$value" >>"$config_dir/docker.env"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# ensure the permissions are set correctly on anything that may expose api keys
|
|
chmod 0755 "$config_dir/docker.env"
|
|
|
|
# store config settings
|
|
echo "$config" >"$config_dir/config"
|
|
|
|
# send both host and container path
|
|
# to respect mapped DOKKU_ROOT when running in a container
|
|
echo "$DOKKU_HOST_ROOT/$APP/letsencrypt/certs/$config_hash:$config_dir"
|
|
}
|
|
|
|
fn-letsencrypt-cron-job-enabled() {
|
|
declare desc="Check if the cron plugin is available"
|
|
|
|
if [[ ! -f "$PLUGIN_AVAILABLE_PATH/cron/cron-write" ]]; then
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
fn-letsencrypt-cron-job-add() {
|
|
declare desc="Add auto-renew cronjob to dokku user's crontab"
|
|
|
|
touch "${DOKKU_LIB_ROOT}/data/letsencrypt/autorenew"
|
|
if fn-letsencrypt-cron-job-enabled; then
|
|
plugn trigger cron-write
|
|
else
|
|
crons="$(crontab -l || true)"
|
|
crons="$(grep -v -F "$LETSENCRYPT_CRON_CMD" <<<"$crons")"
|
|
printf "%s\n%s\n" "$crons" "$LETSENCRYPT_CRON_JOB" | crontab -
|
|
fi
|
|
|
|
dokku_log_info1 "Added cron job to dokku's crontab."
|
|
}
|
|
|
|
fn-letsencrypt-cron-job-remove() {
|
|
declare desc="Remove auto-renew cronjob from dokku user's crontab"
|
|
|
|
rm -f "${DOKKU_LIB_ROOT}/data/letsencrypt/autorenew"
|
|
if fn-letsencrypt-cron-job-enabled; then
|
|
plugn trigger cron-write
|
|
else
|
|
crons="$(crontab -l || true)"
|
|
crons="$(grep -v -F "$LETSENCRYPT_CRON_CMD" <<<"$crons")"
|
|
printf "%s" "$crons" | crontab -
|
|
fi
|
|
dokku_log_info1 "Removed cron job from dokku's crontab."
|
|
}
|
|
|
|
fn-letsencrypt-create-root() {
|
|
declare desc="Ensure the let's encrypt root directory exists"
|
|
declare APP="$1"
|
|
local NGINX_ACCESS_LOG_FORMAT NGINX_ACCESS_LOG_PATH NGINX_ERROR_LOG_PATH
|
|
|
|
local app_root="$DOKKU_ROOT/$APP"
|
|
local le_root="$app_root/letsencrypt"
|
|
|
|
mkdir -p "$le_root"
|
|
|
|
if [[ ! -f "$DOKKU_ROOT/$APP/nginx.conf" ]]; then
|
|
dokku_log_info1 "Setting temporary site"
|
|
NGINX_ACCESS_LOG_FORMAT="$(fn-nginx-access-log-format "$APP")"
|
|
NGINX_ACCESS_LOG_PATH="$(fn-nginx-access-log-path "$APP")"
|
|
NGINX_ERROR_LOG_PATH="$(fn-nginx-error-log-path "$APP")"
|
|
if [[ -z "$NGINX_ACCESS_LOG_FORMAT" ]]; then
|
|
NGINX_ACCESS_LOG_FORMAT="$(fn-nginx-computed-access-log-format "$APP")"
|
|
fi
|
|
if [[ -z "$NGINX_ACCESS_LOG_PATH" ]]; then
|
|
NGINX_ACCESS_LOG_PATH="$(fn-nginx-computed-access-log-path "$APP")"
|
|
fi
|
|
if [[ -z "$NGINX_ERROR_LOG_PATH" ]]; then
|
|
NGINX_ERROR_LOG_PATH="$(fn-nginx-computed-error-log-path "$APP")"
|
|
fi
|
|
|
|
sigil -f "$PLUGIN_AVAILABLE_PATH/letsencrypt/templates/default-nginx.conf.sigil" \
|
|
DOMAINS="$(get_app_domains "$APP" | xargs)" DOKKU_ROOT="$DOKKU_ROOT" APP="$APP" \
|
|
NGINX_ACCESS_LOG_FORMAT="$NGINX_ACCESS_LOG_FORMAT" NGINX_ACCESS_LOG_PATH="$NGINX_ACCESS_LOG_PATH" \
|
|
NGINX_ERROR_LOG_PATH="$NGINX_ERROR_LOG_PATH" \
|
|
>"$DOKKU_ROOT/$APP/nginx.conf"
|
|
restart_nginx | sed "s/^/ /"
|
|
fi
|
|
}
|
|
|
|
fn-letsencrypt-is-autorenew-enabled() {
|
|
declare desc="check if autorenew is enabled"
|
|
local enabled=false
|
|
|
|
if [[ -f "${DOKKU_LIB_ROOT}/data/letsencrypt/autorenew" ]]; then
|
|
enabled=true
|
|
fi
|
|
|
|
echo "$enabled"
|
|
}
|
|
|
|
fn-letsencrypt-computed-email() {
|
|
declare desc="get configured email address"
|
|
declare APP="$1"
|
|
|
|
value="$(fn-letsencrypt-email "$APP")"
|
|
if [[ -z "$value" ]]; then
|
|
value="$(fn-letsencrypt-global-email)"
|
|
fi
|
|
|
|
echo "$value"
|
|
}
|
|
|
|
fn-letsencrypt-global-email() {
|
|
declare desc="get configured email address"
|
|
|
|
fn-plugin-property-get-default "letsencrypt" "--global" "email" ""
|
|
}
|
|
|
|
fn-letsencrypt-email() {
|
|
declare desc="get configured email address"
|
|
declare APP="$1"
|
|
|
|
fn-plugin-property-get-default "letsencrypt" "$APP" "email" ""
|
|
}
|
|
|
|
fn-letsencrypt-enable() {
|
|
declare APP="$1"
|
|
local EXIT_CODE=0
|
|
local domain
|
|
|
|
verify_app_name "$APP"
|
|
|
|
domain="$(get_app_domains "$APP" | xargs | awk '{print $1}')"
|
|
if [[ -z "$domain" ]]; then
|
|
dokku_log_warn "No domains detected for $APP"
|
|
return 1
|
|
fi
|
|
|
|
dokku_log_info2 "Enabling letsencrypt for $APP"
|
|
|
|
fn-letsencrypt-check-email "$APP"
|
|
fn-letsencrypt-acme-proxy-enable "$APP"
|
|
fn-letsencrypt-acme-execute-challenge "$APP" || EXIT_CODE=$? # remove ACME proxy even if this fails
|
|
fn-letsencrypt-acme-proxy-disable "$APP"
|
|
|
|
if [[ "$EXIT_CODE" == 0 ]]; then
|
|
dokku_log_info1 "Done"
|
|
return
|
|
fi
|
|
|
|
dokku_log_warn "Failed to setup letsencrypt"
|
|
DOKKU_FAIL_EXIT_CODE="$EXIT_CODE" dokku_log_fail "Check log output for further information on failure"
|
|
}
|
|
|
|
fn-letsencrypt-expiration() {
|
|
declare desc="prints expiration time"
|
|
declare APP="$1"
|
|
|
|
if [[ -f "$DOKKU_ROOT/$APP/tls/server.letsencrypt.crt" ]]; then
|
|
date -u -d "$(openssl x509 -in "$DOKKU_ROOT/$APP/tls/server.letsencrypt.crt" -enddate -noout | sed -e "s/^notAfter=//")" "+%s"
|
|
else
|
|
date -u -d "$(openssl x509 -in "$DOKKU_ROOT/$APP/tls/server.crt" -enddate -noout | sed -e "s/^notAfter=//")" "+%s"
|
|
fi
|
|
}
|
|
|
|
fn-letsencrypt-computed-graceperiod() {
|
|
declare desc="get configured graceperiod"
|
|
declare APP="$1"
|
|
local value
|
|
|
|
value="$(fn-letsencrypt-graceperiod "$APP")"
|
|
if [[ -z "$value" ]]; then
|
|
value="$(fn-letsencrypt-global-graceperiod)"
|
|
fi
|
|
|
|
if [[ -z "$value" ]]; then
|
|
value="$((60 * 60 * 24 * 30))"
|
|
fi
|
|
|
|
echo "$value"
|
|
}
|
|
|
|
fn-letsencrypt-global-graceperiod() {
|
|
declare desc="get configured graceperiod"
|
|
|
|
fn-plugin-property-get-default "letsencrypt" "--global" "graceperiod" ""
|
|
}
|
|
|
|
fn-letsencrypt-graceperiod() {
|
|
declare desc="get configured graceperiod"
|
|
declare APP="$1"
|
|
|
|
fn-plugin-property-get-default "letsencrypt" "$APP" "graceperiod" ""
|
|
}
|
|
|
|
fn-letsencrypt-format-timediff() {
|
|
declare desc="format a time difference in seconds into a human-readable string"
|
|
local td="$1"
|
|
local negative_td=0
|
|
|
|
if [ "$td" -lt 0 ]; then
|
|
negative_td=1
|
|
td=$((-td))
|
|
fi
|
|
|
|
local days=$((td / (24 * 60 * 60)))
|
|
td=$((td % (24 * 60 * 60)))
|
|
|
|
local hours=$((td / (60 * 60)))
|
|
td=$((td % (60 * 60)))
|
|
|
|
local minutes=$((td / 60))
|
|
local secs=$((td % 60))
|
|
|
|
local res=""
|
|
if [ $days -gt 0 ]; then
|
|
res="${days}d, "
|
|
fi
|
|
|
|
if [ $hours -gt 0 ]; then
|
|
res="${res}${hours}h, "
|
|
fi
|
|
|
|
if [ $minutes -gt 0 ]; then
|
|
res="${res}${minutes}m, "
|
|
fi
|
|
|
|
if [ $secs -gt 0 ]; then
|
|
res="${res}${secs}s, "
|
|
fi
|
|
|
|
# remove trailing comma
|
|
res="$(echo "$res" | sed -re 's/, ?$//g')"
|
|
|
|
if [[ $negative_td == 1 ]]; then
|
|
res="${res} ago"
|
|
fi
|
|
|
|
echo "$res"
|
|
}
|
|
|
|
fn-letsencrypt-is-active() {
|
|
declare desc="outputs true if active, false otherwise"
|
|
declare APP=$1
|
|
local domain
|
|
|
|
# check if SSL is enabled on per-app level
|
|
if ! is_ssl_enabled "$APP"; then
|
|
echo "false"
|
|
return
|
|
fi
|
|
|
|
domain="$(get_app_domains "$APP" | xargs | awk '{print $1}')"
|
|
|
|
# check if certificate is identical to the current let's encrypt certificate by comparing SHA1 hashes
|
|
local cert_sha1
|
|
if [[ -f "$DOKKU_ROOT/$APP/tls/server.letsencrypt.crt" ]]; then
|
|
cert_sha1=$( (cat "$DOKKU_ROOT/$APP/tls/server.letsencrypt.crt" 2>/dev/null) | sha1sum || echo "not_found")
|
|
else
|
|
cert_sha1=$( (cat "$DOKKU_ROOT/$APP/tls/server.crt" 2>/dev/null) | sha1sum || echo "not_found")
|
|
fi
|
|
|
|
local le_sha1="not_found"
|
|
local fileSafeDomain
|
|
fileSafeDomain="${domain/\*/_}" # wildcards are using *.example.com which have certificates named _.example.com
|
|
if [[ -f "$DOKKU_ROOT/$APP/letsencrypt/certs/current/certificates/$fileSafeDomain.pem" ]]; then
|
|
le_sha1=$( (cat "$DOKKU_ROOT/$APP/letsencrypt/certs/current/certificates/$fileSafeDomain.crt" 2>/dev/null) | sha1sum || echo "not_found")
|
|
elif [[ -f "$DOKKU_ROOT/$APP/letsencrypt/certs/current/fullchain.pem" ]]; then
|
|
le_sha1=$( (cat "$DOKKU_ROOT/$APP/letsencrypt/certs/current/fullchain.pem" 2>/dev/null) | sha1sum || echo "not_found")
|
|
fi
|
|
|
|
if [[ "$cert_sha1" != "$le_sha1" ]]; then
|
|
echo "false"
|
|
return
|
|
fi
|
|
|
|
echo "true"
|
|
}
|
|
|
|
fn-letsencrypt-list-apps-with-expiry() {
|
|
declare desc="list all letsencrypt-secured apps together with their expiry date"
|
|
|
|
# prints a tab-separated list of
|
|
# * app name
|
|
# * expiry dates as UNIX timestamp (seconds since epoch)
|
|
# * selected renewal grace period (in seconds)
|
|
# * time left on certificate (in seconds)
|
|
# * time until renewal (in seconds)
|
|
|
|
for APP in $(dokku_apps); do
|
|
if [[ "$APP" == "=====>" ]] || [[ "$APP" == "My" ]] || [[ "$APP" == "Apps" ]]; then continue; fi
|
|
if [[ "$(fn-letsencrypt-is-active "$APP")" == "true" ]]; then
|
|
local expiry=$(fn-letsencrypt-expiration "$APP")
|
|
local grace_period="$(fn-letsencrypt-computed-graceperiod "$APP")"
|
|
local time_to_expiry=$((expiry - $(date +%s)))
|
|
local time_to_renewal=$((expiry - grace_period - $(date +%s)))
|
|
echo -e "$APP\t$expiry\t$grace_period\t$time_to_expiry\t$time_to_renewal"
|
|
fi
|
|
done
|
|
}
|
|
|
|
fn-letsencrypt-computed-server() {
|
|
declare desc="get configured server"
|
|
declare APP="$1"
|
|
|
|
value="$(fn-letsencrypt-server "$APP")"
|
|
if [[ -z "$value" ]]; then
|
|
value="$(fn-letsencrypt-global-server)"
|
|
fi
|
|
|
|
if [[ -z "$value" ]] || [[ "$value" == "default" ]]; then
|
|
value="https://acme-v02.api.letsencrypt.org/directory"
|
|
elif [[ "$value" == "staging" ]]; then
|
|
value="https://acme-staging-v02.api.letsencrypt.org/directory"
|
|
fi
|
|
|
|
echo "$value"
|
|
}
|
|
|
|
fn-letsencrypt-global-server() {
|
|
declare desc="get configured server"
|
|
|
|
fn-plugin-property-get-default "letsencrypt" "--global" "server" ""
|
|
}
|
|
|
|
fn-letsencrypt-server() {
|
|
declare desc="get configured server"
|
|
declare APP="$1"
|
|
|
|
fn-plugin-property-get-default "letsencrypt" "$APP" "server" ""
|
|
}
|
|
|
|
fn-letsencrypt-symlink-certs() {
|
|
declare desc="symlink let's encrypt certificates so they can be found by dokku"
|
|
declare APP="$1" config_dir="$2"
|
|
|
|
local app_root="$DOKKU_ROOT/$APP"
|
|
local le_root="$app_root/letsencrypt"
|
|
local domain
|
|
|
|
dokku_log_info1 "Installing let's encrypt certificates"
|
|
|
|
# link the current config directory to 'current'
|
|
ln -nsf "$config_dir" "$le_root/certs/current"
|
|
|
|
# install the let's encrypt certificate for the app
|
|
unset DOKKU_APP_NAME
|
|
domain="$(get_app_domains "$APP" | xargs | awk '{print $1}')"
|
|
local fileSafeDomain
|
|
fileSafeDomain="${domain/\*/_}" # wildcards are using *.example.com which have certificates named _.example.com
|
|
dokku certs:add "$APP" "$config_dir/certificates/$fileSafeDomain.pem" "$config_dir/certificates/$fileSafeDomain.key"
|
|
rm -f "$app_root/tls/server.letsencrypt.crt" "$app_root/tls/server.crt"
|
|
cp "$config_dir/certificates/$fileSafeDomain.crt" "$app_root/tls/server.letsencrypt.crt"
|
|
cp "$config_dir/certificates/$fileSafeDomain.crt" "$app_root/tls/server.crt"
|
|
}
|