blob: b25384ef6d3be88dffb085aa829609793e4f2bfc (
plain)
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
223
224
225
226
227
228
229
230
|
#!/bin/sh /etc/rc.common
START=20
USE_PROCD=1
run_dir=/var/run/acme
export CHALLENGE_DIR=$run_dir/challenge
export CERT_DIR=/etc/ssl/acme
LAST_LISTEN_PORT=
NFT_HANDLE=
HOOK=/usr/lib/acme/hook
LOG_TAG=acme
# shellcheck source=net/acme/files/functions.sh
. "$IPKG_INSTROOT/usr/lib/acme/functions.sh"
extra_command "abort" "Abort running certificate issuances/renewals"
extra_command "renew" "Run certificate issuances/renewals"
delete_nft_rule() {
if [ "$NFT_HANDLE" ]; then
# $NFT_HANDLE contains the string 'handle XX' so pass it unquoted to nft
nft delete rule inet fw4 input $NFT_HANDLE
NFT_HANDLE=
fi
}
cleanup() {
log debug "cleaning up"
delete_nft_rule
}
load_options() {
section=$1
config_get staging "$section" staging
# compatibility for old option name
if [ -z "$staging" ]; then
config_get_bool staging "$section" use_staging 0
fi
procd_append_param env staging="$staging"
config_get calias "$section" calias
procd_append_param env calias="$calias"
config_get dalias "$section" dalias
procd_append_param env dalias="$dalias"
config_get domains "$section" domains
procd_append_param env domains="$domains"
main_domain="$(first_arg $domains)"
procd_append_param env main_domain="$main_domain"
config_get keylength "$section" keylength
if [ "$keylength" ]; then
log warn "Option \"keylength\" is deprecated, please use key_type (e.g., ec256, rsa2048) instead."
case $keylength in
ec-*) key_type=${keylength/-/} ;;
*) key_type=rsa$keylength ;;
esac
else
config_get key_type "$section" key_type ec256
fi
procd_append_param env key_type="$key_type"
config_get acme_server "$section" acme_server
procd_append_param env acme_server="$acme_server"
config_get days "$section" days
procd_append_param env days="$days"
config_get cert_profile "$section" cert_profile
procd_append_param env cert_profile="$cert_profile"
config_get dns_wait "$section" dns_wait
procd_append_param env dns_wait="$dns_wait"
config_get webroot "$section" webroot
if [ "$webroot" ]; then
log warn "Option \"webroot\" is deprecated, please remove it and change your web server's config so it serves ACME challenge requests from $CHALLENGE_DIR."
CHALLENGE_DIR=$webroot
fi
}
first_arg() {
echo "$1"
}
get_cert() {
section=$1
config_get_bool enabled "$section" enabled 1
[ "$enabled" = 1 ] || return
# load `listen_port` here rather than in `load_options` so we can
# return early without leaving a dangling `procd_open_instance`; the
# check requires loading `validation_method` as well, which in turn
# requires loading `dns` and `standalone`
config_get validation_method "$section" validation_method
config_get dns "$section" dns
config_get standalone "$section" standalone
[ -n "$standalone" ] && log warn "Option \"standalone\" is deprecated."
# if validation_method isn't set then guess it
if [ -z "$validation_method" ]; then
if [ -n "$dns" ]; then
validation_method="dns"
elif [ "$standalone" = 1 ]; then
validation_method="standalone"
else
validation_method="webroot"
fi
log warn "Please set \"option validation_method $validation_method\"."
fi
if [ "$validation_method" = "webroot" ]; then
mkdir -p "$CHALLENGE_DIR"
fi
case "$validation_method" in
standalone)
config_get listen_port "$section" listen_port 80
;;
alpn)
config_get listen_port "$section" listen_port 443
;;
*)
config_get listen_port "$section" listen_port
;;
esac
if [ "$listen_port" != "$LAST_LISTEN_PORT" ]; then
delete_nft_rule
if [ "$listen_port" ]; then
if ! NFT_HANDLE=$(nft -a -e insert rule inet fw4 input tcp dport "$listen_port" counter accept comment ACME | grep -o 'handle [0-9]\+'); then
return 1
fi
log debug "added nft rule: $NFT_HANDLE"
fi
LAST_LISTEN_PORT="$listen_port"
fi
procd_open_instance "$section"
procd_set_param command "$HOOK" get
procd_set_param stdout 1
procd_set_param stderr 1
procd_set_param env CHALLENGE_DIR="$CHALLENGE_DIR" CERT_DIR="$CERT_DIR"
procd_append_param env account_email="$account_email" state_dir="$state_dir" debug="$debug"
procd_append_param env dns="$dns" validation_method="$validation_method" listen_port="$listen_port"
load_options "$section"
load_credentials() {
# use `eval` to correctly strip quotes around credential values
eval procd_append_param env "$1"
}
config_list_foreach "$section" credentials load_credentials
procd_close_instance
}
load_globals() {
[ -z "$account_email" ] || return 1 # only read the first acme section
section=$1
config_get account_email "$section" account_email
if [ -z "$account_email" ]; then
log err "account_email option is required"
exit 1
fi
export account_email
config_get state_dir "$section" state_dir
if [ "$state_dir" ]; then
log warn "Option \"state_dir\" is deprecated, please remove it. Certificates now exist in $CERT_DIR."
mkdir -p "$state_dir"
else
state_dir=/etc/acme
fi
export state_dir
config_get_bool debug "$section" debug 0
export debug
}
start_service() {
grep -q '/etc/init.d/acme' /etc/crontabs/root 2>/dev/null || {
echo "0 0 * * * /etc/init.d/acme renew" >>/etc/crontabs/root
}
}
service_started() {
echo 'Nightly certificate renewal enabled. To renew now, run `service acme renew`.'
}
stop_service() {
sed -i '\|/etc/init.d/acme|d' /etc/crontabs/root
running && stop_aborted="Running certificate renewal(s) aborted and a"
}
service_stopped() {
if enabled; then
untilboot=' until next boot. To disable permanently, run `service acme disable`'
fi
echo "${stop_aborted:-A}utomatic nightly renewal disabled$untilboot."
echo 'To re-enable nightly renewal, run `service acme start`. To issue/renew now, run `service acme renew`.'
}
service_triggers() {
procd_add_config_trigger config.change acme \
/etc/init.d/acme renew
}
load_and_run() {
trap cleanup EXIT
config_load acme
config_foreach load_globals acme
config_foreach get_cert cert
}
renew() {
echo "Starting certificate issuance/renewal in the background; see system log for progress."
echo 'Issuances/renewals can be aborted with `service acme abort`.'
rc_procd load_and_run
}
abort() {
procd_lock
if running "$@"; then
procd_kill "$(basename ${basescript:-$initscript})" "$1"
echo "Aborting certificate issuance(s)/renewal(s); see system log for confirmation."
elif [ -z "$1" ]; then
echo "No certificate issuances/renewals running to abort!"
exit 1
else
echo "No certificate issuance/renewal \"$1\" running to abort!"
exit 1
fi
}
|