summaryrefslogtreecommitdiffstats
path: root/satellit_installer/static_files/lighttpd/opt/openslx/slx-cert
blob: 109a6c196ee5442b40eeeba5f055c3d182c27b7a (plain) (blame)
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
231
232
233
234
235
#!/bin/bash

# OpenSLX SSL Certificate management

if ! mkdir "/run/openslx-cert-manager"; then
	echo "Already in progress."
	exit 1
fi
trap 'rm -rf -- /run/openslx-cert-manager' EXIT

declare -rg BASE="/etc/ssl/openslx"
declare -rg PRIV="$BASE/private"
declare -rg CERT="$BASE/cert"
declare -rg LIGHT="$BASE/lighttpd"

mkdir -p "$BASE" "$PRIV" "$CERT"

chown -R root:root "$BASE" || exit 1
chmod u+rwx,go+rx-w "$BASE" "$CERT" || exit 1
chmod u+rwx,go-rwx "$PRIV" || exit 1
# Before doing anything, make sure we have a CA with enough validity left
# File name format for ca is:
# ${PRIV}/ca-FFFFFFFFFF-TTTTTTTTTT.key
# ${CERT}/ca-TTTTTTTTTT.crt
# Where TT is the unix timestamp of "validTo" of that cert
# And FF is the unix timestamp of when we should starting using a CA to
# sign our certificates. This is for a grace period between CA certs.
# We deliver a new CA certificate immediately when it was generated, but
# only start signing server certificates with it after a grace period of
# 180 days. Any client that rebooted within those 180 days will not run
# into any certificate issues, but if you wanted to cover that case too
# you could make it so the client re-downloads trusted CA-certs every
# couple days.

declare -rg NOW="$( date +%s )"
# PROD
declare -rg ca_days="$(( 10 * 365 ))" # 10y
declare -rg ca_min_remain_s="$(( 400 * 86400 ))" # bit more than 1y
declare -rg ca_new_expire_ts="$(( ca_days * 86400 + NOW ))"
declare -rg srv_days=365 # 1y
declare -rg srv_min_remain_s="$(( 180 * 86400 ))" # half a year
declare -rg srv_new_ts="$(( srv_days * 86400 + NOW ))"
# TEST
#declare -rg ca_days=1825 # 5y
#declare -rg ca_min_remain_s="$(( 1260 ))" # bit more than 1y
#declare -rg ca_new_expire_ts="$(( 1320 + NOW ))"
#declare -rg srv_days=365 # 1y
#declare -rg srv_min_remain_s="$(( 1200 ))" # half a year
#declare -rg srv_new_ts="$(( 1230 + NOW ))"


get_ts () {
	ts="${1%.*}"
	ts="${ts##*/ca-}"
	ts="${ts##*/srv-}"
	from="${ts%-*}"
	if [ "$from" = "$ts" ]; then
		from=
	else
		ts="${ts#*-}"
	fi
}

create_conf () {
	ca_dir="$( mktemp -d /tmp/bwlp-XXXXXXXX )"
	[ -z "$ca_dir" ] && exit 1
	mkdir "$ca_dir"/{certs,crl,newcerts,private}
	touch "$ca_dir"/index.txt
	ca_config="$ca_dir/openssl.cnf"
	cp -f "/etc/ssl/openssl.cnf" "$ca_config"
	cat >> "$ca_config" <<-MYCA
		[ CA_openslx ]
		dir		= $ca_dir
		certs		= \$dir/certs
		crl_dir		= \$dir/crl
		database	= \$dir/index.txt
		new_certs_dir	= \$dir/newcerts
		serial		= \$dir/serial
		crl		= \$dir/crl.pem
		x509_extensions	= usr_cert
		name_opt 	= ca_default
		cert_opt 	= ca_default
		default_md	= default
		preserve	= no
		policy		= policy_match
	MYCA
}

ca_last=
for i in "${PRIV}"/ca-??????????.key; do
	[ -s "$i" ] || continue
	get_ts "$i"
	if ! [ -s "${CERT}/ca-${ts}.crt" ] \
		|| ! [ -s "${CERT}/intermediate-${ts}.crt" ] \
		|| ! [ -s "${PRIV}/intermediate.key" ] \
		|| (( ts < NOW )); then
		# Missing cert, or expired -> delete
		rm -f -- "${CERT}/ca-${ts}.crt" "${PRIV}/ca-${ts}.key" "${CERT}/intermediate-${ts}.crt"
		continue
	fi
	ca_last="$ts"
done

mknew=
if [ -z "$ca_last" ] || (( NOW + ca_min_remain_s > ca_last )); then
	# Make new CA
	echo "Creating new CA..."
	openssl req -new -newkey rsa:4096 -x509 -days "$ca_days" -extensions v3_ca \
		-nodes -subj "/C=DE/ST=PewPew/L=HeyHey/O=bwLehrpool/CN=ca-${NOW}.bwlehrpool" \
		-keyout "${PRIV}/ca-${ca_new_expire_ts}.key" -out "${CERT}/ca-${ca_new_expire_ts}.crt" || exit 2
	mknew=1
	#
	# Create new intermediate, sign with all CAs
	csr="$( mktemp /tmp/bwlp-XXXXXXX.csr )"
	# Create request, CA:TRUE
	echo "Generate intermediate key+CSR..."
	[ -s "${PRIV}/intermediate.key" ] || openssl genrsa -out "${PRIV}/intermediate.key" 4096
	openssl req -new -key "${PRIV}/intermediate.key" \
		-nodes -subj "/C=DE/ST=PewPew/L=HeyHey/O=bwLehrpool/CN=intermediate.bwlehrpool" \
		-out "$csr" || exit 2
	create_conf
	# Sign request, CA:TRUE
	echo "Sign new intermediate key with CA..."
	openssl ca -config "$ca_config" -extensions v3_ca -create_serial \
		-policy policy_anything -days "$ca_days" \
		-cert "${CERT}/ca-${ca_new_expire_ts}.crt" -keyfile "${PRIV}/ca-${ca_new_expire_ts}.key" \
		-notext -name CA_openslx -batch -out "${CERT}/intermediate-${ca_new_expire_ts}.crt" -in "$csr" || exit 2
	rm -rf -- "$ca_dir" "$csr"
fi

if [ -n "$mknew" ]; then
	# Rebuild config module for clients
	echo "Updating client config module..."
	(
		tmpdir="$( mktemp -d '/tmp/bwlp-XXXXXXX' )"
		cp -a "${CERT}/"ca-*.crt "$tmpdir/"
		cd "$tmpdir/" || exit 6
		openssl rehash .
		tar -c -k -f "/opt/openslx/configs/modules/self-signed-ca.tar" \
			--transform 's#^[./][./]*#/opt/openslx/ssl/#' .
		cd /tmp
		rm -rf -- "$tmpdir"
		sudo -u www-data -n php /srv/openslx/www/slx-admin/api.php sysconfig --action rebuild
		echo "."
	)
fi

# Now check the server certificate

declare -a srv_list
srv_list=()
for i in "${PRIV}"/srv-??????????.key; do
	[ -s "$i" ] || continue
	get_ts "$i"
	if (( ts < NOW )) || ! [ -s "${CERT}/srv-${ts}.crt" ]; then
		rm -f -- "$i" "${CERT}/srv-${ts}.crt"
		continue
	fi
	srv_list+=( "$ts" )
done

if [ -n "$mknew" ] || [ "${#srv_list[@]}" = 0 ] \
		|| [ "$(( NOW + srv_min_remain_s ))" -gt "${srv_list[-1]}" ]; then
	# Request ServerCert
	csr="$( mktemp /tmp/bwlp-XXXXXXX.csr )"
	echo "Generating new Server Certificate. Key+CSR..."
	rm -f -- "${CERT}"/srv-*.crt "${PRIV}/srv.key.tmp" "${PRIV}"/srv-*.key
	openssl req -new -nodes -keyout "${PRIV}/srv.key.tmp" -out "$csr" \
		-subj "/C=DE/ST=PewPew/L=HeyHey/O=bwLehrpool/CN=satellite.bwlehrpool" || exit 4
	echo "Signing Server Certificate with intermediate..."
	declare -a in_list
	in_list=()
	for i in "${CERT}"/intermediate-??????????.crt; do
		[ -s "$i" ] || continue
		get_ts "$i"
		if (( ts < NOW )); then
			echo "Expired intermediate $i"
			rm -f -- "$i"
			continue
		fi
		echo "Have intermediate $i"
		in_list+=( "$i" )
	done
	if [ "${#in_list[@]}" = 0 ]; then
		echo "ERROR: Have no intermediate certificate"
		exit 11
	fi
	for in_cert in "${in_list[@]}"; do
		get_ts "$in_cert"
		(( ts < 30 * 86400 + NOW )) && continue # Expiring in a month, ignore
		break # Need only one really
	done
	echo "Signing with $in_cert"
	create_conf
	# Need extfile for SAN, chromium doesn't honor CN anymore
	cat > "${csr}.cnf" <<-END
	basicConstraints = CA:FALSE
	nsCertType = server
	nsComment = "OpenSSL Generated Server Certificate"
	subjectKeyIdentifier = hash
	authorityKeyIdentifier = keyid,issuer:always
	keyUsage = critical, digitalSignature, keyEncipherment
	extendedKeyUsage = serverAuth
	subjectAltName = @alt_names
	[alt_names]
	DNS.1 = satellite.bwlehrpool
	END
	openssl ca -config "$ca_config" -create_serial -policy policy_anything -days "$srv_days" \
		-cert "$in_cert" -keyfile "${PRIV}/intermediate.key" -extfile "${csr}.cnf" \
		-notext -name CA_openslx -batch -out "${CERT}/srv-${srv_new_ts}.crt" -in "$csr" || exit 4
	rm -rf -- "$ca_dir"
	rm -f -- "$csr" "${csr}.cnf"
	mv "${PRIV}/srv.key.tmp" "${PRIV}/srv-${srv_new_ts}.key" || exit 5
	srv_list+=( "$srv_new_ts" )

	# Combine and prepare for lighttpd

	mkdir -p "$LIGHT" || exit 10

	# Combine cert and key, as required by lighttpd
	echo "Writing out lighttpd PEMs..."
	cat "${CERT}/srv-${srv_new_ts}.crt" "${PRIV}/srv-${srv_new_ts}.key" > "${LIGHT}/server.pem" || exit 10
	chmod 0600 "${LIGHT}/server.pem"

	# Create ca-chain
	cat "${in_list[@]}" > "${LIGHT}/ca-chain.pem"

	if [ "$1" = "--restart" ] || [ -t 0 ]; then
		echo "Restarting lighttpd..."
		systemctl restart lighttpd.service
	fi
fi

echo "Done."
exit 0