setupSSO.sh/setupSSO.sh

391 lines
17 KiB
Bash

#!/bin/bash
# Documentation:
# Reconfigure UCS SSO: https://help.univention.com/t/reconfigure-ucs-single-sign-on/16161
# Renewing SSL Certs : https://help.univention.com/t/renewing-the-ssl-certificates/37
# 2022-06-27 by Ingo Juergensmann
# License: see https://codeberg.org/Windfluechter/setupSSO.sh/src/branch/main/LICENSE
set -e
# echo "§1 = ${1}"
if [ "${1}" = "-v" ]; then
DEBUG="null"
else
DEBUG="stdout"
fi
# On server with SSO active:
DOMAINNAME=$(ucr search domainname | awk '{print $2}')
SSO_FQDN="ucs-sso.${DOMAINNAME}"
PORTAL_FQDN="portal.${DOMAINNAME}"
LE_DOMAIN=$(ucr search letsencrypt/domains | awk '{print $2}')
LE_SSLCERT="/etc/univention/letsencrypt/signed_chain.crt"
LE_SSLKEY="/etc/univention/letsencrypt/domain.key"
# colors according to https://dev.to/ifenna__/adding-colors-to-bash-scripts-48g4
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
GRAY='\033[0;90m' # 37=lightgray, 90=darkgray
NC='\033[0m'
RUNNINGON=$(hostname -f)
if [ "$(ucr get server/role)" = "domaincontroller_master" ]; then
ISMASTER="y"
printf "\n${GREEN}✅ We are running on Primary Domain Node. Very good!${NC}\n\n"
else
ISMASTER="n"
printf "\n${RED}❌ You are running this script not on the Primary Domain Node. Please run it there. Exiting. ${NC}\n\n"
exit 0
fi
# Get a list of servers for each server role (master always one, so skipped)
UCS_PDN_FQDN=$(udm computers/domaincontroller_master list | grep "fqdn:" | awk '{print $2}')
UCS_BDN_FQDN=$(udm computers/domaincontroller_backup list | grep "fqdn:" | awk '{print $2}')
UCS_REP_FQDN=$(udm computers/domaincontroller_slave list | grep "fqdn:" | awk '{print $2}')
# create some random files in /tmp to store the passwords
tmpfile=$(mktemp)
rootpassfile=$(mktemp)
trap "rm -f '${tmpfile}'" EXIT
trap "rm -f '${rootpassfile}'" EXIT
trap "printf '❌ ${RED}Deploying SSO failed because of an error:\n${ERROR}${NC}\n" ERR
createSSOCert () {
# uncomment the next lines when you want to revoke older certs in CA
#if [ "${ISMASTER}" = "y" ];
#for s in $(univention-certificate check -name ${SSO_FQDN} | head -n -1 | awk '{print $6 }' ); do
# echo $s
# univention-certificate revoke -name ${SSO_FQDN} -id ${s}
#done
printf %${COLUMNS}s |tr " " "=" && echo
echo "Checking the self-signed SSL certificate of Identity Provider (IdP) for metadata signing"
echo
echo "When there is no existing SSL cert for the IdP for ${SSO_FQDN} a new cert will be created."
echo "If a cert does exist it will be checked of a validity >1 year and you will be prompted to"
echo "create a new certficate or continue with the current SSL certificate. "
echo "If the cert will expire within a year, the SSL certificate will be renewed. The reason"
echo "for this is the metadata signing: Each time you create a new certificate you need to"
echo "update the metadata on every Service Provider (SP) that uses the SSO IdP."
echo
# does the cert already exists?
if [ -r /etc/univention/ssl/${SSO_FQDN}/cert.pem ]; then
# check if SSO cert will expire within the next 30 days (2592000 secs)
WILLEXPIRE=$(openssl x509 -enddate -noout -in /etc/univention/ssl/${SSO_FQDN}/cert.pem -checkend 2592000 | tail -n 1)
if [ "${WILLEXPIRE}" = "Certificate will expire" ]; then
# when cert will expire within given timeframe, renew the SSL cert by 5 years
univention-certificate renew -name "${SSO_FQDN}" -days 1825
elif [ "${WILLEXPIRE}" = "Certificate will not expire" ]; then
echo "See below for more information about your certificate:"
univention-certificate check -name ${SSO_FQDN}
echo
printf "⚠️ ${YELLOW} Your SSL cert already exists and will expire on $(openssl x509 -enddate -noout -in /etc/univention/ssl/$SSO_FQDN/cert.pem -checkend 2592000 | head -n -1 | cut -d"=" -f2- )${NC}\n"
read -p "Delete the existing SSL cert for ${SSO_FQDN} and create a new one (y) or continue (c) with current cert? " DELSSL
if [ "${DELSSL}" = "y" ]; then
# when opted to create a new SSL cert, do so as told:
for n in cert.pem openssl.cnf private.key req.pem; do
rm -f /etc/univention/ssl/${SSO_FQDN}/${n}
done
rmdir /etc/univention/ssl/${SSO_FQDN}
univention-certificate new -name "${SSO_FQDN}" -days 1825
fi
fi
else
# create a new SSL cert when none exists for SSO_FQDN
univention-certificate new -name "${SSO_FQDN}" -days 1825
fi
}
setupConfig () {
# ask which scenario is applicable
echo "setupSSO.sh supports two scenarios:"
echo "1) SSO and portal on different domains"
echo "2) SSO and portal on the same domain"
echo
read -p "Which scenario is applicable for your setup? (1/2) " SCENARIO
# ask for the SSO/portal domains for the chosen scenario
if [ "${SCENARIO}" = "1" ]; then
read -p "Please enter SSO domain (default: ${SSO_FQDN}): " SSO_FQDN2
read -p "Please enter portal domain (default: ${PORTAL_FQDN}): " PORTAL_FQDN2
SSO_FQDN=${SSO_FQDN2:-$SSO_FQDN}
PORTAL_FQDN=${PORTAL_FQDN2:-$PORTAL_FQDN}
elif [ "${SCENARIO}" = "2" ]; then
read -p "Please enter SSO domain (default: ${SSO_FQDN}): " SSO_FQDN2
SSO_FQDN=${SSO_FQDN2:-$SSO_FQDN}
else
echo "You need to enter 1 or 2 for the scenario. Quitting... "
exit 1
fi
# ask whether all servers do share the same root password and read from user input
echo
read -p "Do all UCS hosts share the same root password (y/n) " COMMONROOTPW
if [ "${COMMONROOTPW}" = "y" ]; then
echo
echo -n 'Please enter your common root password: ' ; read -s ROOTPW
COMMONROOTPASSWD="${ROOTPW}"
fi
# ask for the domain administrator password and read from user input
# store both passwords into the random files (storing for -dcpwd requires *NO* new line in file)
echo
echo -n 'Please enter Domain Administrator password: ' ; read -s DOMAINADMINPASS
echo
echo -n "${DOMAINADMINPASS}" > ${tmpfile}
echo -n "${ROOTPW}" > ${rootpassfile}
echo "Checking ${SSO_FQDN} & ${PORTAL_FQDN} target(s): ${HOST} "
echo
echo "For the Apache2 webserver certificates of the SSO domain ${SSO_FQDN}"
echo "the following LetsEncrypt certifcate will be used. A self-signed SSL"
echo "certificate will be used internally to sign the metadata of SSO."
echo
for HOSTLIST in ${SSO_FQDN} ${PORTAL_FQDN} ; do
HOST_IPLIST=$(host -t A ${HOSTLIST} | awk '{print $NF}')
for HOST_IP in ${HOST_IPLIST}; do
HOST=$(host -t A ${HOST_IP} | awk '{print $NF}' | sed -e 's/\.$//')
if [ "$(echo $HOST | sed -e 's/.*(NXDOMAIN).*/NXDOMAIN/g')" = "NXDOMAIN" ]; then
HOSTLOCAL=$(grep "[[:blank:]]${HOSTLIST}$" /etc/hosts | awk '{print $1}')
HOST=$(host -t A ${HOSTLOCAL} | awk '{print $NF}' | sed -e 's/\.$//')
fi
if [ "${COMMONROOTPW}" = "y" ]; then
ROOTPW="${COMMONROOTPASSWD}"
else
echo "Please enter your root password for ${HOST}: " ; read -s ROOTPW
echo "${ROOTPW}" > $rootpassfile
echo
fi
if [ $(univention-ssh ${rootpassfile} ${HOST} test -f ${LE_SSLCERT} ; echo $?) -eq 1 ]; then
# printf "SSL certificate: ${GREEN}${LE_SSLCERT}${NC}\n"
#else
printf "SSL certificate not readable on ${HOST}: ${RED}${LE_SSLCERT}${NC}\n"
fi
if [ $(univention-ssh ${rootpassfile} ${HOST} test -f ${LE_SSLKEY} ; echo $?) -eq 1 ]; then
# printf "SSL private key: ${GREEN}${LE_SSLKEY}${NC}\n"
#else
printf "SSL private key not readable on ${HOST}: ${RED}${LE_SSLKEY}${NC}\n"
exit
fi
#echo
#echo "This certifcate contains the following domains as Subject Alternate Name (SAN): "
for d in $(univention-ssh ${rootpassfile} ${HOST} "openssl x509 -text -in ${LE_SSLCERT}" | grep -A1 "X509v3 Subject Alternative Name:" | tail -n 1 | sed -e 's/DNS://g' -e 's/,//g') ; do
if [ "${d}" = "${SSO_FQDN}" ]; then
SSO_DOMAINMATCH="t"
SSO_DOMAIN_HOSTS="${HOST} ${SSO_DOMAIN_HOSTS}"
printf "${HOST}\t${GREEN}${d} \t<-- your SSO domain${NC}\n"
elif [ "${d}" = "${PORTAL_FQDN}" ]; then
PORTAL_DOMAINMATCH="t"
PORTAL_DOMAIN_HOSTS="${HOST} ${PORTAL_DOMAIN_HOSTS}"
printf "${HOST}\t${GREEN}${d} \t<-- your portal domain${NC}\n"
else
printf "${HOST}\t${RED}${d}${NC}\n"
fi
done
done
done
echo
SSO_DOMAIN_HOSTS=$(echo ${SSO_DOMAIN_HOSTS} | sort -u )
PORTAL_DOMAIN_HOSTS=$(echo ${PORTAL_DOMAIN_HOSTS} | sort -u )
if [ "${SSO_DOMAINMATCH}" = "t" ]; then
echo "✅ Your SSO domain is included in your LetsEncrypt SSL certificate on ${SSO_DOMAIN_HOSTS}."
fi
if [ "${PORTAL_DOMAINMATCH}" = "t" ]; then
echo "✅ Your portal domain is included in your LetsEncrypt SSL certificate on ${PORTAL_DOMAIN_HOSTS}."
fi
if [ "$SSO_DOMAINMATCH" = "t" -a "${PORTAL_DOMAINMATCH}" = "t" ]; then
echo
else
echo "${SSO_FQDN} not found in your LetsEncrypt SSL certificate!"
echo "Please add ${SSO_FQDN} to your list of domain in LetsEncrypt app"
echo "or set 'letsencrypt/domains' and reconfigure LetsEncrypt app."
echo
exit 1
fi
}
setupSSOServer () {
# create a self-signed SSL cert. This SSL cert will be used in simplesamlphp and will be copied
# to /etc/simplesamlphp/${SSO_FQDN}-idp-certificate.(crt|key) by 91univention-saml.inst join script
createSSOCert
# Iterate across all SSO servers by looking up the SSO_FQDN
#
SSO_HOST_IP=$(host -t A ${SSO_FQDN} | awk '{print $NF}')
#echo "Please enter SSH credentials for root when prompted (i.e. no pubkey auth available)"
for IP in ${SSO_HOST_IP}; do
printf %${COLUMNS}s |tr " " "=" && echo
# If the SSO server is publically reachable we must serve different certs
HOST=$(host ${IP} | awk '{print $NF}' | sed -e 's/\.$//')
if [ "$(echo $HOST | sed -e 's/.*(NXDOMAIN).*/NXDOMAIN/g')" = "NXDOMAIN" ]; then
HOSTLOCAL=$(grep "[[:blank:]]${SSO_HOST_IP}$" /etc/hosts | awk '{print $1}')
HOST=$(host -t A ${HOSTLOCAL} | awk '{print $NF}' | sed -e 's/\.$//')
fi
if [ "${COMMONROOTPW}" = "y" ]; then
ROOTPW="${COMMONROOTPASSWD}"
else
echo "Please enter your root password for ${HOST}: " ; read -s ROOTPW
echo "${ROOTPW}" > $rootpassfile
echo
fi
printf "⚠️ ${YELLOW} Setting registry variables and run 91univention-saml.inst join script on ${HOST}: ${NC}\n"
printf "${GRAY}"
univention-ssh $rootpassfile root@${HOST} "ucr set ucs/server/sso/fqdn=${SSO_FQDN} saml/idp/entityID=https://${SSO_FQDN}/simplesamlphp/saml2/idp/metadata.php ucs/server/sso/autoregistraton=no" || ERROR=$(fc -nl -1)
univention-scp $rootpassfile ${tmpfile} ${HOST}:${tmpfile}.${HOST}
univention-ssh $rootpassfile root@${HOST} "univention-run-join-scripts -dcaccount Administrator -dcpwd ${tmpfile}.${HOST} --force --run-scripts 91univention-saml.inst" || ERROR=$(fc -nl -1)
univention-ssh $rootpassfile ${HOST} "rm ${tmpfile}.${HOST}"
# restart SAML service
printf "⚠️ ${YELLOW} Copy SSO cert and restart univention-saml on ${HOST}: \n${GRAY}\n"
univention-ssh $rootpassfile root@${HOST} "install -o root -g samlcgi -m 0644 /etc/univention/ssl/${SSO_FQDN}/cert.pem /etc/simplesamlphp/${SSO_FQDN}-idp-certificate.crt" || ERROR=$(fc -nl -1)
univention-ssh $rootpassfile root@${HOST} "install -o root -g samlcgi -m 0640 /etc/univention/ssl/${SSO_FQDN}/private.key /etc/simplesamlphp/${SSO_FQDN}-idp-certificate.key" || ERROR=$(fc -nl -1)
univention-ssh $rootpassfile root@${HOST} "ucr set saml/idp/certificate/certificate=/etc/simplesamlphp/${SSO_FQDN}-idp-certificate.crt saml/idp/certificate/privatekey=/etc/simplesamlphp/${SSO_FQDN}-idp-certificate.key"
univention-ssh $rootpassfile root@${HOST} "service univention-saml restart" || ERROR=$(fc -nl -1)
printf "⚠️ ${YELLOW} Configure Apache SSL certs and restart Apache on ${HOST}: ${GRAY}\n"
univention-ssh $rootpassfile root@${HOST} "ucr set saml/apache2/ssl/certificate=${LE_SSLCERT} saml/apache2/ssl/key=${LE_SSLKEY}" || ERROR=$(fc -nl -1)
univention-ssh $rootpassfile root@${HOST} "service apache2 restart" || ERROR=$(fc -nl -1)
printf "${GREEN}✅ successfully succeeded on ${HOST}! ${NC}\n"
done
}
# Configure portal server(s)
setupPortalServers () {
SSO_HOST_IP=$(host -t A ${PORTAL_FQDN} | awk '{print $NF}')
printf %${COLUMNS}s |tr " " "=" && echo
printf "⚠️ ${YELLOW} Portal servers: \n${PORTAL_FQDN} ${NC}\n"
for IP in ${SSO_HOST_IP}; do
printf %${COLUMNS}s |tr " " "=" && echo
# On all other UCS servers that serves the portal
# copy certs to server where portal should run with the fqdn
PORTALHOST=$(host -t A ${IP} | awk '{print $NF}' )
HOST="${PORTALHOST%.*}"
if [ ! -d /etc/univention/ssl/${PORTAL_FQDN} ]; then
univention-certificate new -name ${PORTAL_FQDN}
fi
if [ "$(echo $HOST | sed -e 's/.*(NXDOMAIN).*/NXDOMAIN/g')" = "NXDOMAIN" ]; then
HOSTLOCAL=$(grep "[[:blank:]]${PORTAL_FQDN}$" /etc/hosts | awk '{print $1}')
HOST=$(host -t A ${HOSTLOCAL} | awk '{print $NF}' | sed -e 's/\.$//')
fi
if [ "${COMMONROOTPW}" = "y" ]; then
ROOTPW="${COMMONROOTPASSWD}"
else
echo "Please enter your root password for ${HOST}: " ; read -s ROOTPW
echo "${ROOTPW}" > $rootpassfile
echo
fi
printf %${COLUMNS}s |tr " " "=" && echo
printf "⚠️ ${YELLOW} Setting registry variables and run 92univention-management-console-web-server.inst join script on ${HOST} ${NC}\n"
printf "${GRAY}"
univention-scp $rootpassfile -r "/etc/univention/ssl/${PORTAL_FQDN}/" "${HOST}:/etc/univention/ssl/"
univention-ssh $rootpassfile ${HOST} "ucr set ucs/server/sso/fqdn=\"${SSO_FQDN}\" umc/saml/sp-server=\"${PORTAL_FQDN}\" umc/saml/idp-server=https://${SSO_FQDN}/simplesamlphp/saml2/idp/metadata.php"
univention-scp $rootpassfile ${tmpfile} ${HOST}:${tmpfile}.${HOST}
univention-ssh $rootpassfile ${HOST} "univention-run-join-scripts -dcaccount Administrator -dcpwd ${tmpfile}.${HOST} --force --run-scripts 92univention-management-console-web-server.inst"
univention-ssh $rootpassfile ${HOST} "rm ${tmpfile}.${HOST}"
printf "${GREEN}✅ successfully succeeded on ${HOST}! ${NC}\n"
done
}
# setup all UCS servers
setupAllUCS () {
printf "⚠️ ${YELLOW} Backup & Replica servers:\n${UCS_BDN_FQDN} ${UCS_REP_FQDN}${NC}\n"
for UCSHOST in ${UCS_PDN_FQDN} ${UCS_BDN_FQDN} ${UCS_REP_FQDN}; do
printf %${COLUMNS}s |tr " " "=" && echo
printf "⚠️ ${YELLOW} Running on ${UCSHOST}: ${NC}\n"
#printf %${COLUMNS}s |tr " " "=" && echo
#UCSHOST=$(host ${IP} | awk '{print $NF}' )
#UCSHOST="${UCSHOST%.*}"
# On all other UCS servers (backup & replicas)
if [ "$(echo $UCSHOST | sed -e 's/.*(NXDOMAIN).*/NXDOMAIN/g')" = "NXDOMAIN" ]; then
HOSTLOCAL=$(grep "[[:blank:]]${UCSHOST}$" /etc/hosts | awk '{print $1}')
UCSHOST=$(host -t A ${HOSTLOCAL} | awk '{print $NF}' | sed -e 's/\.$//')
fi
if [ "${COMMONROOTPW}" = "y" ]; then
ROOTPW="${COMMONROOTPW}"
else
echo "Please enter your common root password for ${UCSHOST}: " ; read -s ROOTPW
echo
fi
echo "${ROOTPW}" > $rootpassfile
printf "⚠️ ${YELLOW} Setting registry variables and run 92univention-management-console-web-server.inst join script on ${UCSHOST} ${NC}\n"
printf "${GRAY}"
univention-ssh $rootpassfile ${UCSHOST} "ucr set ucs/server/sso/fqdn=${SSO_FQDN} umc/saml/idp-server=https://${SSO_FQDN}/simplesamlphp/saml2/idp/metadata.php"
univention-scp $rootpassfile ${tmpfile} ${UCSHOST}:${tmpfile}.${UCSHOST}
univention-ssh $rootpassfile ${UCSHOST} "univention-run-join-scripts -dcaccount Administrator -dcpwd ${tmpfile}.${UCSHOST} --force --run-scripts 92univention-management-console-web-server.inst"
univention-ssh $rootpassfile ${UCSHOST} "rm ${tmpfile}.${UCSHOST}"
printf "${GREEN}✅ successfully succeeded on ${UCSHOST}! ${NC}\n"
done
}
# setup Scenario 2 SSO/Portal server
# TODO! needs more testing!
singleSSOPortal () {
# Reconfigure SAML IdP and UMC / Portal
createSSOCert
ucr set ucs/server/sso/autoregistraton=no \
saml/idp/entityID="https://${SSO_FQDN}/simplesamlphp/saml2/idp/metadata.php" \
ucs/server/sso/fqdn=${SSO_FQDN} \
umc/saml/sp-server=${SSO_FQDN} \
ucs/server/sso/virtualhost=false
echo "ServerName ${SSO_FQDN}" >>/etc/apache2/ucs-sites.conf.d/servername.conf
univention-run-join-scripts --force --run-scripts 91univention-saml.inst
ucr set umc/saml/idp-server=https://${FQDN}/simplesamlphp/saml2/idp/metadata.php
univention-run-join-scripts --force --run-scripts 92univention-management-console-web-server.inst
}
# invoke setupConfig subroutine and ask for the config variables needed
setupConfig
case "${SCENARIO}" in
"1")
#echo "in Scenario 1"
#exit 0
setupSSOServer
setupPortalServers
setupAllUCS
;;
"2")
#echo "in Scenario 2"
#exit 0
singleSSOPortal
#singleUCSServers
setupAllUCS
;;
*)
echo ""
;;
esac
echo
printf %${COLUMNS}s |tr " " "=" && echo
printf "${GREEN}✅ Deploying SSO was successful as there were no errors. ${NC}\n\n"
echo
# last step: ask if the IdP cert should be output, because you might need to copy that
# data into your Service Providers config, e.g. Nextcloud...
read -p "Do you want to dump the SSL cert of the SSO Identity Provider (IdP)? (y/n) " ASK
if [ "${ASK}" = "y" ] ; then
#echo "SSO Host: ${SSO_FQDN}"
#SSO_HOST_IP=$(host -t A ${SSO_FQDN} | head -n1 | awk '{print $NF}')
#HOST=$(host ${SSO_HOST_IP} | awk '{print $NF}' | sed -e 's/\.$//')
#if [ "${COMMONROOTPW}" = "y" ]; then
# ROOTPW="${COMMONROOTPW}"
#else
# echo "Please enter your common root password for ${HOST}: " ; read -s ROOTPW
# echo
#fi
#echo "${ROOTPW}" > $rootpassfile
#univention-ssh $rootpassfile ${HOST}
openssl x509 -in /etc/simplesamlphp/${SSO_FQDN}-idp-certificate.crt
fi