summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2023-09-20 15:14:35 +0200
committerSimon Rettberg2023-09-20 15:14:35 +0200
commitdebc7d7160101d4be569aca827e0e4f5791d7c0e (patch)
tree489330495a5c871a157cd0b10d0d148e7b94d09d
parent[SSPS] Update ciphers for lighttpd config (diff)
downloadsetup-scripts-debc7d7160101d4be569aca827e0e4f5791d7c0e.tar.gz
setup-scripts-debc7d7160101d4be569aca827e0e4f5791d7c0e.tar.xz
setup-scripts-debc7d7160101d4be569aca827e0e4f5791d7c0e.zip
[SSPS] Rewrite CA/cert generator and handling
Better sanity checks and fallbacks.
-rwxr-xr-xsatellit_installer/static_files/lighttpd/opt/openslx/slx-cert302
1 files changed, 196 insertions, 106 deletions
diff --git a/satellit_installer/static_files/lighttpd/opt/openslx/slx-cert b/satellit_installer/static_files/lighttpd/opt/openslx/slx-cert
index e25e3d7..3409244 100755
--- a/satellit_installer/static_files/lighttpd/opt/openslx/slx-cert
+++ b/satellit_installer/static_files/lighttpd/opt/openslx/slx-cert
@@ -2,44 +2,47 @@
# OpenSLX SSL Certificate management
-if ! mkdir "/run/openslx-cert-manager"; then
+mkdir -p "/run/openslx"
+if ! mkdir "/run/openslx/cert-manager" 2> /dev/null; then
echo "Already in progress."
exit 1
fi
-trap 'rm -rf -- /run/openslx-cert-manager' EXIT
+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"
+declare -rg PRIVDIR="$BASE/private"
+declare -rg CERTDIR="$BASE/cert"
+declare -rg LIGHTDIR="$BASE/lighttpd"
-mkdir -p "$BASE" "$PRIV" "$CERT"
+mkdir -p "$BASE" "$PRIVDIR" "$CERTDIR"
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
+chmod u+rwx,go+rx-w "$BASE" "$CERTDIR" || exit 1
+chmod u+rwx,go-rwx "$PRIVDIR" || 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.
+# ${PRIVDIR}/ca-TTTTTTTTTT.key
+# ${CERTDIR}/ca-TTTTTTTTTT.crt
+# Where TT is the unix timestamp of "validTo" of that cert.
+#
+# A new CA will be generated when the previous has less than two years
+# of validity left.
+# We deliver a new CA to clients immediately when it was generated, but
+# keep signing server certs with the old one, until the old one only
+# has a year of validity left, then we switch to the next-oldest CA-cert.
+# CA certs are valid for 10 years, server certs for 1 year.
+# This should allow for clients to have two years of uptime without any
+# problems due to cert rollover.
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_s="$(( srv_days * 86400 ))"
+declare -rg srv_min_remain_s="$(( srv_days / 2 * 86400 ))" # half a year
declare -rg srv_new_ts="$(( srv_days * 86400 + NOW ))"
+declare -rg ca_days="$(( 10 * 365 ))" # 10y
+declare -rg ca_min_remain_s="$(( 2 * srv_days * 86400 ))" # 2y, twice that of a server cert
+declare -rg ca_new_expire_ts="$(( ca_days * 86400 + NOW ))"
# TEST
#declare -rg ca_days=1825 # 5y
#declare -rg ca_min_remain_s="$(( 1260 ))" # bit more than 1y
@@ -49,18 +52,27 @@ declare -rg srv_new_ts="$(( srv_days * 86400 + NOW ))"
#declare -rg srv_new_ts="$(( 1230 + NOW ))"
+# Extract timestamp from given filename.
+# Filename format has to be ca-UUUUUUUUU[.-]* or srv-UUUUUUUU[.-]*
+# Where UUUUU is a unix timestamp
get_ts () {
- ts="${1%.*}"
- ts="${ts##*/ca-}"
- ts="${ts##*/srv-}"
- from="${ts%-*}"
+ ts="${1%.*}" # Remove everything from last dot on
+ ts="${ts##*/ca-}" # remove ca- prefix
+ ts="${ts##*/srv-}" # remove srv- prefix
+ from="${ts%-*}" # Remove everything from last dash on
if [ "$from" = "$ts" ]; then
+ # There was no dash, we have a normal timestamp
from=
else
+ # There was a dash - $from is now the start of the range,
+ # $ts is the end of the range
ts="${ts#*-}"
fi
+ # Set the return value
+ (( ts > 1234567890 ))
}
+# Create an openssl config for signing CSRs
create_conf () {
ca_dir="$( mktemp -d /tmp/bwlp-XXXXXXXX )"
[ -z "$ca_dir" ] && exit 1
@@ -86,121 +98,196 @@ create_conf () {
MYCA
}
-latest_ca_file=
+declare -ag cmdargs=( "$@" )
+maybe_restart () {
+ for i in "${cmdargs[@]}"; do
+ [ "$i" = "--restart" ] && exit 13
+ done
+ echo "Restating script, wiping everything except CAs..."
+ rm -f -- /etc/ssl/openslx/*/{srv,int}*
+ exec "$0" "${cmdargs[@]}" --restart
+ exit 14
+}
+
+# Start
+
+# Check if existing CA is still valid for long enough
+# Globbing is sorted so should make sure we check the newest one last
+declare -a ca_list=()
ca_last=
-for i in "${PRIV}"/ca-??????????.key; do
+latest_ca_cert=
+oldest_ca_ts=
+for i in "${PRIVDIR}"/ca-??????????.key; do
[ -s "$i" ] || continue
- get_ts "$i"
- if ! [ -s "${CERT}/ca-${ts}.crt" ] \
- || ! [ -s "${CERT}/intermediate-${ts}.crt" ] \
- || ! [ -s "${PRIV}/intermediate.key" ] \
+ if ! get_ts "$i"; then
+ echo "Invalid ca-key: $i"
+ rm -f -- "$i"
+ continue
+ fi
+ cert="${CERTDIR}/ca-${ts}.crt"
+ if ! [ -s "$cert" ] \
|| (( ts < NOW )); then
# Missing cert, or expired -> delete
- rm -f -- "${CERT}/ca-${ts}.crt" "${PRIV}/ca-${ts}.key" "${CERT}/intermediate-${ts}.crt"
+ rm -f -- "$cert" "$i"
continue
fi
+ # Check if key and cert match
+ if [ "$( openssl x509 -in "$cert" -noout -pubkey )" != "$( openssl pkey -in "$i" -pubout )" ]; then
+ echo "Publickey in cert doesn't match publickey in keypair: $cert + $i"
+ rm -f -- "$cert" "$i"
+ continue
+ fi
+ # All CAs still valid
+ ca_list+=( "$cert" )
+ # Latest CA
ca_last="$ts"
- latest_ca_file="${CERT}/ca-${ts}.crt"
+ latest_ca_cert="$cert"
+ # Oldest one that is still valid for as long as we want to sign a new cert
+ if [ -z "$oldest_ca_ts" ] && (( ts > NOW + srv_s )); then
+ oldest_ca_ts="$ts"
+ fi
done
mknew=
-if [ -z "$ca_last" ] || (( NOW + ca_min_remain_s > ca_last )); then
- # Make new CA
+if [ -z "$ca_last" ] || (( ca_last < NOW + ca_min_remain_s )); then
+ # Make new CA since the newest one we have is about to expire
echo "Creating new CA..."
- openssl req -new -newkey rsa:4096 -x509 -days "$ca_days" -extensions v3_ca \
+ cert="${CERTDIR}/ca-${ca_new_expire_ts}.crt"
+ openssl req -new -newkey rsa:3072 -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
+ -keyout "${PRIVDIR}/ca-${ca_new_expire_ts}.key" -out "$cert" || exit 2
+ ca_list+=( "$cert" )
+ ca_last="${ca_new_expire_ts}"
+ latest_ca_cert="$cert"
+ if [ -z "$oldest_ca_ts" ]; then
+ oldest_ca_ts="$ca_new_expire_ts"
+ fi
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
-
+# Repackage config.tgz module?
if [ -n "$mknew" ] || ! [ -s "/opt/openslx/configs/modules/self-signed-ca.tar" ] \
- || [ "/opt/openslx/configs/modules/self-signed-ca.tar" -ot "$latest_ca_file" ]; then
+ || ! [ -e "/run/openslx/cert-conf-done" ] \
+ || [ "/opt/openslx/configs/modules/self-signed-ca.tar" -ot "$latest_ca_cert" ]; then
# Rebuild config module for clients
echo "Updating client config module..."
(
tmpdir="$( mktemp -d '/tmp/bwlp-XXXXXXX' )"
- cp -a "${CERT}/"ca-*.crt "$tmpdir/"
+ # Copy all current ca-certs to tmpdir
+ cp -a "${CERTDIR}/"ca-*.crt "$tmpdir/"
cd "$tmpdir/" || exit 6
+ # Build hashed symlinks for openssl
openssl rehash .
+ # Put everything in config module and rebuild
tar -c -k -f "/opt/openslx/configs/modules/self-signed-ca.tar" \
--transform 's#^[./][./]*#/opt/openslx/ssl/#' .
cd /tmp || exit 7
rm -rf -- "$tmpdir"
sudo -u www-data -n php /srv/openslx/www/slx-admin/api.php sysconfig --action rebuild
echo "."
+ touch "/run/openslx/cert-conf-done"
)
fi
# Now check the server certificate
+# First, check if we could still use old-style certs with intermediate
+declare -a unt_list=()
+for i in "${CERTDIR}"/intermediate-??????????.crt; do
+ [ -s "$i" ] || continue
+ if ! get_ts "$i"; then
+ echo "Invalid intermediate cert: $i"
+ rm -f -- "$i"
+ continue
+ fi
+ if (( ts < NOW )); then
+ echo "Expired intermediate: $i"
+ rm -f -- "$i"
+ continue
+ fi
+ unt_list+=( "-untrusted" "$i" )
+done
-declare -a srv_list
-srv_list=()
-for i in "${PRIV}"/srv-??????????.key; do
+# Now check existing server certs
+have_srv=
+for i in "${PRIVDIR}"/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"
+ if ! get_ts "$i"; then
+ echo "Invalid srv-key: $i"
+ rm -f -- "$i"
+ continue
+ fi
+ cert="${CERTDIR}/srv-${ts}.crt"
+ if (( ts < NOW + srv_min_remain_s )) || ! [ -s "$cert" ]; then
+ echo "Expired srv cert or key with no cert: $i"
+ rm -f -- "$i" "$cert"
+ continue
+ fi
+ # Keys match?
+ if [ "$( openssl x509 -in "$cert" -noout -pubkey )" != "$( openssl pkey -in "$i" -pubout )" ]; then
+ echo "Publickey in cert doesn't match publickey in keypair: $cert + $i"
+ rm -f -- "$cert" "$i"
continue
fi
- srv_list+=( "$ts" )
+ # Validate chain
+ valid=
+ for ca in "${ca_list[@]}"; do
+ if openssl verify -CAfile "$ca" "${unt_list[@]}" \
+ "$cert" &> /dev/null; then
+ valid=1
+ break
+ fi
+ done
+ if [ -n "$valid" ]; then
+ have_srv=1
+ break
+ fi
+ echo "No valid CA/chain for $i, removing"
+ rm -f -- "$i" "$cert"
done
-if [ -n "$mknew" ] || [ "${#srv_list[@]}" = 0 ] \
- || [ "$(( NOW + srv_min_remain_s ))" -gt "${srv_list[-1]}" ]; then
+# Now still check the current lighttpd config, in case it is out of sync
+# with our generated stuff for whatever reason.
+if [ -n "$have_srv" ] || [ -z "$makenew" ]; then
+ if [ -s "${LIGHTDIR}/ca-chain.pem" ]; then
+ unt_list=( "-untrusted" "${LIGHTDIR}/ca-chain.pem" )
+ else
+ unt_list=()
+ fi
+ valid=
+ for ca in "${ca_list[@]}"; do
+ openssl verify -CAfile "$ca" "${unt_list[@]}" \
+ "${LIGHTDIR}/server.pem" &> /dev/null || continue
+ valid=1
+ break
+ done
+ if [ -z "$valid" ]; then
+ echo "Current lighttpd SSL setup seems invalid, making new one"
+ makenew=1
+ fi
+fi
+
+# Make new one?
+if [ -z "$have_srv" ] || [ -n "$makenew" ]; 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" \
+ rm -f -- "${CERTDIR}"/srv-*.crt "${PRIVDIR}/srv.key.tmp" "${PRIVDIR}"/srv-*.key
+ openssl req -newkey rsa:3072 -nodes -keyout "${PRIVDIR}/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"
+ echo "Signing Server Certificate with CA..."
+ sign_cert="${CERTDIR}/ca-${oldest_ca_ts}.crt"
+ sign_key="${PRIVDIR}/ca-${oldest_ca_ts}.key"
+ if ! [ -s "$sign_cert" ] || ! [ -s "$sign_key" ]; then
+ echo "CA sign/key to sign does not exist!?"
+ maybe_restart
+ fi
+ echo "Signing with $sign_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"
+ nsComment = "bwLehrpool Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
@@ -209,25 +296,28 @@ if [ -n "$mknew" ] || [ "${#srv_list[@]}" = 0 ] \
[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
+ if ! openssl ca -config "$ca_config" -create_serial -policy policy_anything -days "$srv_days" \
+ -cert "$sign_cert" -keyfile "$sign_key" -extfile "${csr}.cnf" \
+ -notext -name CA_openslx -batch -out "${CERTDIR}/srv-${srv_new_ts}.crt" -in "$csr"; then
+ echo "Failed to sign CSR"
+ rm -f -- "$sign_key" "$sign_cert"
+ maybe_restart
+ fi
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" )
+ mv "${PRIVDIR}/srv.key.tmp" "${PRIVDIR}/srv-${srv_new_ts}.key" || exit 5
# Combine and prepare for lighttpd
- mkdir -p "$LIGHT" || exit 10
+ mkdir -p "$LIGHTDIR" || exit 10
- # Combine cert and key, as required by lighttpd
+ # Combine cert and key, as required by (older) 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"
+ cat "${CERTDIR}/srv-${srv_new_ts}.crt" "${PRIVDIR}/srv-${srv_new_ts}.key" > "${LIGHTDIR}/server.pem" || exit 10
+ chmod 0600 "${LIGHTDIR}/server.pem"
- # Create ca-chain
- cat "${in_list[@]}" > "${LIGHT}/ca-chain.pem"
+ # Don't need this anymore
+ rm -f -- "${LIGHTDIR}/ca-chain.pem"
if [ "$1" = "--restart" ] || [ -t 0 ]; then
echo "Restarting lighttpd..."