forked from dokku/dokku-letsencrypt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
functions
executable file
·222 lines (178 loc) · 7.49 KB
/
functions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#!/usr/bin/env bash
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
source "$PLUGIN_CORE_AVAILABLE_PATH/config/functions"
source "$PLUGIN_CORE_AVAILABLE_PATH/certs/functions"
source "$PLUGIN_CORE_AVAILABLE_PATH/domains/functions"
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/config"
set -eo pipefail
[[ $DOKKU_TRACE ]] && set -x
letsencrypt_create_root() {
#shellcheck disable=SC2034
declare desc="Ensure the let's encrypt root directory exists"
declare APP="$1"
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"
local NGINX_ACCESS_LOG_FORMAT="$(fn-nginx-access-log-format "$APP")"
local NGINX_ACCESS_LOG_PATH="$(fn-nginx-access-log-path "$APP")"
local NGINX_ERROR_LOG_PATH="$(fn-nginx-error-log-path "$APP")"
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
}
letsencrypt_format_timediff() {
#shellcheck disable=SC2034
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"
}
letsencrypt_get_expirydate() {
#shellcheck disable=SC2034
declare desc="print SSL certificate expiry date as UNIX timestamp"
local 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
}
letsencrypt_is_active() {
#shellcheck disable=SC2034
declare desc="checks if app is secured by let's encrypt"
local app=$1
# check if SSL is enabled on per-app level
is_ssl_enabled "$app" || return 1
local 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"
if [[ -f "$DOKKU_ROOT/$app/letsencrypt/certs/current/certificates/$domain.pem" ]]; then
le_sha1=$( (cat "$DOKKU_ROOT/$app/letsencrypt/certs/current/certificates/$domain.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
[[ "$cert_sha1" == "$le_sha1" ]] || return 2
echo "$app"
}
letsencrypt_list_apps_and_expiry() {
#shellcheck disable=SC2034
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 [[ "$(letsencrypt_is_active "$app")" ]]; then
local expiry=$(letsencrypt_get_expirydate "$app")
local grace_period=$(config_get --global DOKKU_LETSENCRYPT_GRACEPERIOD || config_get "$app" DOKKU_LETSENCRYPT_GRACEPERIOD || echo $((60 * 60 * 24 * 30)))
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
}
letsencrypt_configure_and_get_dir() {
#shellcheck disable=SC2034
declare desc="assemble lego command line arguments and create a config hash directory for them"
local app="$1" acme_port="$2"
local app_root="$DOKKU_ROOT/$app"
local le_root="$app_root/letsencrypt"
mkdir -p "$DOKKU_ROOT/$app/letsencrypt/account"
eval "$(config_export global)"
eval "$(config_export app "$app")"
# 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
local server=${DOKKU_LETSENCRYPT_SERVER:-default}
if [ -z "$server" ] || [ "$server" == "default" ]; then
server="https://acme-v02.api.letsencrypt.org/directory"
elif [ "$server" == "staging" ]; then
server="https://acme-staging-v02.api.letsencrypt.org/directory"
fi
# construct domain arguments
local domains="$(get_app_domains "$app")"
local domain_args=''
for domain in $domains; do
dokku_log_verbose " - Domain '$domain'" >&2
domain_args="$domain_args --domains $domain"
done
eval "$(config_export global)"
eval "$(config_export app "$app")"
local graceperiod="${DOKKU_LETSENCRYPT_GRACEPERIOD:-$((60 * 60 * 24 * 30))}"
local extra_args="$(config_get --global DOKKU_LETSENCRYPT_ARGS || config_get "$app" DOKKU_LETSENCRYPT_ARGS || echo '')"
local config="--pem --http --accept-tos --cert.timeout $graceperiod --path /certs --server $server --email $DOKKU_LETSENCRYPT_EMAIL $extra_args $domain_args"
local config_hash=$(echo "$config" | sha1sum | awk '{print $1}')
local config_dir="$le_root/certs/$config_hash"
mkdir -p "$config_dir"
# store config settings
echo "--http.port :$acme_port $config" >"$config_dir/config"
# re-implement entire path to respect mapped DOKKU_ROOT when running in a container
echo "$DOKKU_HOST_ROOT/$app/letsencrypt/certs/$config_hash"
}
letsencrypt_get_email() {
#shellcheck disable=SC2034
declare desc="Check if an e-mail address is provided globally or for the app"
declare APP="$1"
eval "$(config_export global)"
if [[ -n "$DOKKU_LETSENCRYPT_EMAIL" ]]; then
echo "$DOKKU_LETSENCRYPT_EMAIL"
return
fi
eval "$(config_export app "$APP")"
echo "$DOKKU_LETSENCRYPT_EMAIL"
}
letsencrypt_check_email() {
#shellcheck disable=SC2034
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 "$(letsencrypt_get_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 config:set --no-restart $APP DOKKU_LETSENCRYPT_EMAIL=<e-mail>"
return 1
fi
}