|
|
#!/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
}
latest_ca_file=
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"
latest_ca_file="${CERT}/ca-${ts}.crt"
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" ] || ! [ -s "/opt/openslx/configs/modules/self-signed-ca.tar" ] \
|| [ "/opt/openslx/configs/modules/self-signed-ca.tar" -ot "$latest_ca_file" ]; 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 || exit 7
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
|