2 # Wrapper for acme.sh to work on openwrt.
4 # This program is free software; you can redistribute it and/or modify it under
5 # the terms of the GNU General Public License as published by the Free Software
6 # Foundation; either version 3 of the License, or (at your option) any later
9 # Author: Toke Høiland-Jørgensen <toke@toke.dk>
12 ACME
=/usr
/lib
/acme
/acme.sh
13 export CURL_CA_BUNDLE
=/etc
/ssl
/certs
/ca-certificates.crt
28 [ -f "/etc/crontabs/root" ] && grep -q '/etc/init.d/acme' /etc
/crontabs
/root
&& return
29 echo "0 0 * * * /etc/init.d/acme start" >> /etc
/crontabs
/root
30 /etc
/init.d
/cron start
35 logger
-t acme
-s -p daemon.info
"$@"
40 logger
-t acme
-s -p daemon.err
"$@"
45 [ "$DEBUG" -eq "1" ] && logger
-t acme
-s -p daemon.debug
"$@"
49 local proto rq
sq listen remote state program
50 netstat
-nptl 2>/dev
/null |
while read proto rq
sq listen remote state program
; do
51 case "$proto#$listen#$program" in
52 tcp
#*:80#[0-9]*/*) echo -n "${program%% *} " ;;
61 log
"Running pre checks for $main_domain."
63 listeners
="$(get_listeners)"
65 debug
"port80 listens: $listeners"
67 for listener
in $
(get_listeners
); do
73 debug
"Found uhttpd listening on port 80; trying to disable."
75 UHTTPD_LISTEN_HTTP
=$
(uci get uhttpd.main.listen_http
)
77 if [ -z "$UHTTPD_LISTEN_HTTP" ]; then
78 err
"$main_domain: Unable to find uhttpd listen config."
79 err
"Manually disable uhttpd or set webroot to continue."
83 uci
set uhttpd.main.listen_http
=''
84 uci commit uhttpd ||
return 1
85 if ! /etc
/init.d
/uhttpd reload
; then
86 uci
set uhttpd.main.listen_http
="$UHTTPD_LISTEN_HTTP"
92 debug
"Found nginx listening on port 80; trying to disable."
95 while grep -sq "$cmd" "/proc/$pid/cmdline" && kill -0 "$pid"; do
96 /etc
/init.d
/nginx stop
97 if [ $tries -gt 10 ]; then
98 debug
"Can't stop nginx. Terminating script."
101 debug
"Waiting for nginx to stop..."
107 debug
"Nothing listening on port 80."
110 err
"$main_domain: Cannot run in standalone mode; another daemon is listening on port 80."
111 err
"Disable other daemon or set webroot to continue."
117 iptables
-I input_rule
-p tcp
--dport 80 -j ACCEPT
-m comment
--comment "ACME" ||
return 1
118 ip6tables
-I input_rule
-p tcp
--dport 80 -j ACCEPT
-m comment
--comment "ACME" ||
return 1
119 debug
"v4 input_rule: $(iptables -nvL input_rule)"
120 debug
"v6 input_rule: $(ip6tables -nvL input_rule)"
126 log
"Running post checks (cleanup)."
127 # The comment ensures we only touch our own rules. If no rules exist, that
128 # is fine, so hide any errors
129 iptables
-D input_rule
-p tcp
--dport 80 -j ACCEPT
-m comment
--comment "ACME" 2>/dev
/null
130 ip6tables
-D input_rule
-p tcp
--dport 80 -j ACCEPT
-m comment
--comment "ACME" 2>/dev
/null
132 if [ -e /etc
/init.d
/uhttpd
] && ( [ -n "$UHTTPD_LISTEN_HTTP" ] ||
[ "$UPDATE_UHTTPD" -eq 1 ] ); then
133 if [ -n "$UHTTPD_LISTEN_HTTP" ]; then
134 uci
set uhttpd.main.listen_http
="$UHTTPD_LISTEN_HTTP"
138 /etc
/init.d
/uhttpd reload
141 if [ -e /etc
/init.d
/nginx
] && ( [ "$NGINX_WEBSERVER" -eq 1 ] ||
[ "$UPDATE_NGINX" -eq 1 ] ); then
143 /etc
/init.d
/nginx restart
162 local main_domain
="$1"
164 grep -q "acme-staging" "$STATE_DIR/$main_domain/${main_domain}.conf"
179 local moved_staging
=0
185 config_get_bool enabled
"$section" enabled
0
186 config_get_bool use_staging
"$section" use_staging
187 config_get_bool update_uhttpd
"$section" update_uhttpd
188 config_get_bool update_nginx
"$section" update_nginx
189 config_get domains
"$section" domains
190 config_get keylength
"$section" keylength
191 config_get webroot
"$section" webroot
192 config_get dns
"$section" dns
194 UPDATE_NGINX
=$update_nginx
195 UPDATE_UHTTPD
=$update_uhttpd
197 [ "$enabled" -eq "1" ] ||
return
199 [ "$DEBUG" -eq "1" ] && acme_args
="$acme_args --debug"
204 [ -n "$webroot" ] ||
[ -n "$dns" ] || pre_checks
"$main_domain" ||
return 1
206 log
"Running ACME for $main_domain"
208 handle_credentials
() {
209 local credential
="$1"
210 eval export $credential
212 config_list_foreach
"$section" credentials handle_credentials
214 if [ -e "$STATE_DIR/$main_domain" ]; then
215 if [ "$use_staging" -eq "0" ] && is_staging
"$main_domain"; then
216 log
"Found previous cert issued using staging server. Moving it out of the way."
217 mv "$STATE_DIR/$main_domain" "$STATE_DIR/$main_domain.staging"
220 log
"Found previous cert config. Issuing renew."
221 $ACME --home "$STATE_DIR" --renew -d "$main_domain" $acme_args && ret
=0 || ret
=1
228 acme_args
="$acme_args $(for d in $domains; do echo -n "-d $d "; done)"
229 acme_args
="$acme_args --keylength $keylength"
230 [ -n "$ACCOUNT_EMAIL" ] && acme_args
="$acme_args --accountemail $ACCOUNT_EMAIL"
231 [ "$use_staging" -eq "1" ] && acme_args
="$acme_args --staging"
233 if [ -n "$dns" ]; then
235 acme_args
="$acme_args --dns $dns"
236 elif [ -z "$webroot" ]; then
237 log
"Using standalone mode"
238 acme_args
="$acme_args --standalone --listen-v6"
240 if [ ! -d "$webroot" ]; then
241 err
"$main_domain: Webroot dir '$webroot' does not exist!"
245 log
"Using webroot dir: $webroot"
246 acme_args
="$acme_args --webroot $webroot"
249 if ! $ACME --home "$STATE_DIR" --issue $acme_args; then
250 failed_dir
="$STATE_DIR/${main_domain}.failed-$(date +%s)"
251 err
"Issuing cert for $main_domain failed. Moving state to $failed_dir"
252 [ -d "$STATE_DIR/$main_domain" ] && mv "$STATE_DIR/$main_domain" "$failed_dir"
253 if [ "$moved_staging" -eq "1" ]; then
254 err
"Restoring staging certificate"
255 mv "$STATE_DIR/${main_domain}.staging" "$STATE_DIR/${main_domain}"
261 if [ -e /etc
/init.d
/uhttpd
] && [ "$update_uhttpd" -eq "1" ]; then
262 uci
set uhttpd.main.key
="$STATE_DIR/${main_domain}/${main_domain}.key"
263 uci
set uhttpd.main.cert
="$STATE_DIR/${main_domain}/fullchain.cer"
264 # commit and reload is in post_checks
267 if [ -e /etc
/init.d
/nginx
] && [ "$update_nginx" -eq "1" ]; then
268 sed -i "s#ssl_certificate\ .*#ssl_certificate $STATE_DIR/${main_domain}/fullchain.cer;#g" /etc
/nginx
/nginx.conf
269 sed -i "s#ssl_certificate_key\ .*#ssl_certificate_key $STATE_DIR/${main_domain}/${main_domain}.key;#g" /etc
/nginx
/nginx.conf
270 # commit and reload is in post_checks
280 STATE_DIR
=$
(config_get
"$section" state_dir
)
281 ACCOUNT_EMAIL
=$
(config_get
"$section" account_email
)
282 DEBUG
=$
(config_get
"$section" debug
)
286 [ -n "$CHECK_CRON" ] && exit 0
287 [ -e "/var/run/acme_boot" ] && rm -f "/var/run/acme_boot" && exit 0
290 config_foreach load_vars acme
292 if [ -z "$STATE_DIR" ] ||
[ -z "$ACCOUNT_EMAIL" ]; then
293 err
"state_dir and account_email must be set"
297 [ -d "$STATE_DIR" ] || mkdir
-p "$STATE_DIR"
299 trap err_out HUP TERM
302 config_foreach issue_cert cert