#!/bin/bash #------------------------------ # Script to manage vCenter SSL certificates. # # Author: Vincent Santa Maria [vinny.santa-maria@broadcom.com] #------------------------------ #------------------------------ # for debugging purposes only, uncomment the following line: # export PS4='+[${SECONDS}s][${BASH_SOURCE}:${LINENO}]: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'; set -x; # to debug run: ./vCert 2>vCert-debug.txt #------------------------------ VERSION="4.22.0" set -o pipefail #------------------------------ # Prints help information #------------------------------ function printHelp() { cat << EOF vCert: vCenter Certificate Management Utility Usage: $0 [options] Options: -h | --help Prints this help menu -d | --debug Enables debug-level logging -j | --just-do-it JUST DO IT! Script operations will continue if backup tasks fail -u | --user Specify an SSO administrator account -v | --version Prints script version -w | --password Password for the specified SSO administrator account EOF } #------------------------------ # Parses arguments passed to the script #------------------------------ function parseArguments() { logInfo 'Entering the parseArguments function' logDetails "Arguments: $#" if [ "$#" -ge 1 ]; then logInfo 'There are arguments passed' while [ "$#" -ge 1 ]; do logInfo "Parsing argument '$1'" case "$1" in -h|--help) logInfo 'Printing help menu' stopLoading printHelp exit ;; -d|--debug) logInfo 'Enabling debug-level logging' DEBUG=1 shift 1 ;; -j|--just-do-it) logInfo 'Disabling exit on backup task failure' EXIT_ON_BACKUP_FAILURE=0 shift 1 ;; -u|--user) logInfo "Specified SSO Administrator account: $2" VMDIR_USER_UPN="$2" VMDIR_USER=$(echo $VMDIR_USER_UPN | awk -F'@' '{print $1}') shift 2 ;; -v|--version) logInfo 'Printing script version' stopLoading echo "vCert: version $VERSION" exit ;; -w|--password) logInfo "Specified SSO Administrator password: $(echo $2 | tr '[:print:]' '*')" echo -n "$2" > $STAGE_DIR/.vmdir-user-password chmod 640 $STAGE_DIR/.vmdir-user-password VMDIR_USER_PASSWORD="$2" shift 2 ;; *) logInfo "Invalid argument $1" echo $'\n'"${YELLOW}Invalid argument '$1'" stopLoading printHelp exit ;; esac done fi if [ -n "$VMDIR_USER_UPN" ] && [ -f $STAGE_DIR/.vmdir-user-password ]; then VERIFY_PASSED_CREDENTIALS=1; fi } #------------------------------ # Print loading message #------------------------------ function loading() { i=2 e[0]='.' e[1]='..' e[2]='...' while [ $i -lt 3 ]; do echo -ne "\r\033[KLoading${e[$i]}" if [ $i -eq 2 ]; then i=0 else ((++i)) fi sleep 1 done } loading & LOADING_PID=$! #------------------------------ # Stop loading message #------------------------------ function stopLoading() { kill $LOADING_PID wait $LOADING_PID > /dev/null 2>&1 unset LOADING_PID echo -ne "\r\033[K" } #------------------------------ # Print Disclaimer #------------------------------ function disclaimer() { cat << EOF ${YELLOW} ------------------------!!! Attention !!!------------------------${NORMAL} This script is intended to be used at the direction of Broadcom Global Support. Changes made could render this system inoperable. Please ensure you have a valid VAMI-based backup or offline snaphots of ${YELLOW}${UL}ALL${NORMAL} vCenter/PSC nodes in the SSO domain before continuing. Please refer to the following Knowledge Base article: ${YELLOW}${UL}https://knowledge.broadcom.com/external/article?legacyId=85662${NORMAL} EOF read -p $'\nDo you acknowledge the risks and wish to continue? [y/n]: ' DISCLAIMER_ACKNOWLEDGE_INPUT if [ -n "$DISCLAIMER_ACKNOWLEDGE_INPUT" ] && [[ "$DISCLAIMER_ACKNOWLEDGE_INPUT" =~ ^[Yy] ]]; then logInfo 'User acknowledges the risk assessment disclaimer' return else logInfo 'User does not acknowledge the risk assessment disclaimer' exit fi } #------------------------------ # Print section header #------------------------------ function header() { printf "\n%s\n" "${CYAN}$1" printf "%65s${NORMAL}\n" | tr " " "-" logInfo "Operation: $1" } #------------------------------ # Print task description #------------------------------ function task() { printf "%-52s" "$1" logInfo "Task: $1" } #------------------------------ # Print formatted status message with colored text #------------------------------ function statusMessage() { printf "%13s\n" "${1}" | sed "s/${1}/${!2}&${NORMAL}/" logInfo "Task Status: $1" } #------------------------------ # Print formatted 'errror' message #------------------------------ function errorMessage() { if [ -z $3 ]; then printf "%13s\n\n" "FAILED" | sed "s/FAILED/${RED}&${NORMAL}/"; else printf "\n\n"; fi logError "Task Error: $1" if [ -z $2 ]; then printf "%s\n\n" "${YELLOW}${1}. Exiting...${NORMAL}" exit else case $2 in 'backup') if [ $EXIT_ON_BACKUP_FAILURE == 1 ]; then printf "%s\n\n" "${YELLOW}${1}. Exiting...${NORMAL}" exit fi ;; *) printf "%s\n\n" "${YELLOW}${1}. Exiting...${NORMAL}" exit ;; esac fi } #------------------------------ # Main logging function #------------------------------ function logEntry() { if [ -n "$1" ]; then IN="$1" LOG_LEVEL="$2" else read -r IN LOG_LEVEL='INFO' fi LOG_TIMESTAMP=$(date "+%Y-%m-%dT%H:%M:%S %Z %:z") echo "$LOG_TIMESTAMP $LOG_LEVEL $IN" >> $LOG 2>/dev/null } #------------------------------ # Logging function for log details #------------------------------ function logDetails() { if [ -n "$1" ]; then IN="$1" else read -r IN fi echo "$IN" | sed -e 's/^[.]*/--> &/g' >> $LOG 2>/dev/null } #------------------------------ # Logging function for info-level logging #------------------------------ function logInfo() { if [ -n "$1" ]; then IN="$1" else read -r IN fi logEntry "$IN" 'INFO' } #------------------------------ # Logging function for error-level logging #------------------------------ function logError() { if [ -n "$1" ]; then IN="$1" else read -r IN fi logEntry "$IN" 'ERROR' } #------------------------------ # Logging function for debug-level logging #------------------------------ function logDebug() { if [ "$DEBUG" -gt 0 ]; then if [ -n "$1" ]; then IN="$1" else read -r IN fi logEntry "$IN" 'DEBUG' fi } #------------------------------ # Logging function for debug-level logging #------------------------------ function logDebugDetails() { if [ "$DEBUG" -gt 0 ]; then if [ -n "$1" ]; then IN="$1" else read -r IN fi echo "$IN" | sed -e 's/^[.]*/--> &/g' >> $LOG 2>/dev/null fi } #------------------------------ # Set color variables #------------------------------ function enableColor() { RED=$(tput setaf 1) GREEN=$(tput setaf 2) YELLOW=$(tput setaf 3) CYAN=$(tput setaf 6) UL=$(tput smul) NORMAL=$(tput sgr0) } #------------------------------ # Clear color variables for reports #------------------------------ function disableColor() { RED='' GREEN='' YELLOW='' CYAN='' UL='' NORMAL='' } #------------------------------ # Pre-start operations #------------------------------ function preStartOperations() { if [ ! -d $LOG_DIR ]; then mkdir $LOG_DIR; fi if [ ! -d $STAGE_DIR ]; then mkdir -p $STAGE_DIR; fi if [ ! -d $TRUSTED_CA_DIR ]; then mkdir -p $TRUSTED_CA_DIR; fi if [ ! -d $REQUEST_DIR ]; then mkdir -p $REQUEST_DIR; fi if [ ! -d $BACKUP_DIR ]; then mkdir -p $BACKUP_DIR; fi if [ ! -f $LOG ]; then touch $LOG; fi logInfo "Starting vCert version $VERSION" echo -n "$VMDIR_MACHINE_PASSWORD" > $STAGE_DIR/.machine-account-password chmod 640 $STAGE_DIR/.machine-account-password updateVcSupport setTimestamp parseArguments "$@" enableColor checkServices checkVmdirMachineCredentials checkCAPermissions setSolutionUsers setVECSStores clearCSRInfo checkForVCF stopLoading } #------------------------------ # make sure vCert.log is included in a support bundle #------------------------------ function updateVcSupport() { if [ ! -f /etc/vmware/vm-support/vcert.mfx ]; then logInfo 'Creating vc-support manifest for vCert' cat << EOF > /etc/vmware/vm-support/vcert.mfx % Manifest name: vcert % Manifest group: VirtualAppliance % Manifest default: Enabled # action Options file/command copy IGNORE_MISSING $LOG_DIR/* EOF fi } #------------------------------ # set the TIMESTAMP variable #------------------------------ function setTimestamp() { TIMESTAMP=$(date +%Y%m%d%H%M%S) } #------------------------------ # Cleanup operations #------------------------------ function cleanup() { if [ -n "$LOADING_PID" ]; then stopLoading; fi if [ $CLEANUP -eq 1 ]; then rm -Rf $STAGE_DIR; fi } #------------------------------ # Validate an IP address #------------------------------ function validateIp() { logInfo "Attempting to validate $1 as an IP address" RETURN=1 if [[ $1 =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then OIFS=$IFS IFS='.' ip=($1) IFS=$OIFS [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] RETURN=$? fi if [ $RETURN ]; then logInfo '$1 is a valid IP address' else logInfo '$1 is not a valid IP address' fi return $RETURN } #------------------------------ # Authenticate if needed #------------------------------ function authenticateIfNeeded() { if [ -z "$VMDIR_USER_UPN" ] || [ ! -f $STAGE_DIR/.vmdir-user-password ]; then getSSOCredentials verifySSOCredentials fi if [ $VERIFY_PASSED_CREDENTIALS == 1 ]; then verifySSOCredentials; fi } #------------------------------ # Get SSO administrator credentials #------------------------------ function getSSOCredentials() { unset VMDIR_USER_UPN_INPUT read -t $READ_TIMEOUTS -r -p $'\n'"Please enter a Single Sign-On administrator account [${VMDIR_USER_UPN_DEFAULT}]: " VMDIR_USER_UPN_INPUT if [ $? -le 128 ]; then if [ -z "$VMDIR_USER_UPN_INPUT" ]; then VMDIR_USER_UPN=$VMDIR_USER_UPN_DEFAULT else VMDIR_USER_UPN=$VMDIR_USER_UPN_INPUT fi logInfo "User has chosen the following Single Sign-On account: $VMDIR_USER_UPN" VMDIR_USER=$(echo $VMDIR_USER_UPN | awk -F'@' '{print $1}') USER_PROVIDED_SSO_DOMAIN=$(echo $VMDIR_USER_UPN | awk -F'@' '{print $2}') if [ "$USER_PROVIDED_SSO_DOMAIN" != "$SSO_DOMAIN" ]; then echo '' while [ "$USER_PROVIDED_SSO_DOMAIN" != "$SSO_DOMAIN" ]; do read -r -p "${YELLOW}Invalid domain, please provide an account in the SSO domain ($SSO_DOMAIN):${NORMAL} " VMDIR_USER_UPN_INPUT VMDIR_USER_UPN=$VMDIR_USER_UPN_INPUT VMDIR_USER=$(echo $VMDIR_USER_UPN | awk -F'@' '{print $1}') USER_PROVIDED_SSO_DOMAIN=$(echo $VMDIR_USER_UPN | awk -F'@' '{print $2}') done echo '' fi read -t $READ_TIMEOUTS -r -s -p "Please provide the password for $VMDIR_USER_UPN: " VMDIR_USER_PASSWORD if [ $? -le 128 ]; then echo -n "$VMDIR_USER_PASSWORD" > $STAGE_DIR/.vmdir-user-password chmod 640 $STAGE_DIR/.vmdir-user-password echo '' else errorMessage 'Timeout waiting for password' 'credentials' 'password' fi else errorMessage 'Timeout waiting for SSO Admin account UPN' 'credentials' 'password' fi } #------------------------------ # Verify SSO credentials #------------------------------ function verifySSOCredentials() { VERIFIED=0 ATTEMPT=1 logInfo "Validating credentials for ${VMDIR_USER_UPN}" while [ $ATTEMPT -le 3 ]; do if ! $LDAP_SEARCH -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=Servers,cn=$SSO_SITE,cn=Sites,cn=Configuration,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password "(objectclass=vmwDirServer)" cn 2>/dev/null 1>/dev/null; then logInfo "Invalid credentials for $VMDIR_USER_UPN (attempt $ATTEMPT)" if [ $VERIFY_PASSED_CREDENTIALS == 1 ] && [ $ATTEMPT == 1 ]; then echo "${YELLOW}Unable to validate the provided credentials for $VMDIR_USER_UPN${NORMAL}" getSSOCredentials else read -r -s -p $'\n'"${YELLOW}Invalid credentials, please enter the password for $VMDIR_USER_UPN:${NORMAL} " VMDIR_USER_PASSWORD echo -n "$VMDIR_USER_PASSWORD" > $STAGE_DIR/.vmdir-user-password chmod 640 $STAGE_DIR/.vmdir-user-password fi ((++ATTEMPT)) else VERIFIED=1 logInfo "Credentials verified for $VMDIR_USER_UPN" if [ $ATTEMPT -gt 1 ]; then echo ''; fi break fi done if [ $VERIFIED == 0 ]; then errorMessage "Unable to verify credentials for $VMDIR_USER_UPN" fi } #------------------------------ # Check if vmafdd, vmdird, and reverse proxy are running #------------------------------ function checkServices() { if [[ "$VC_VERSION" =~ ^[78] ]]; then logInfo 'Checking state of the vmware-envoy service' if ! checkService 'envoy'; then logError 'The Envoy service is not running' echo $'\n'"${YELLOW}The Envoy Service is not running!" echo "The script cannot continue. Exiting...${NORMAL}" stopLoading exit fi logInfo 'The vmware-envoy service is running' fi logInfo 'Checking state of the vmware-rhttpproxy service' if ! checkService 'rhttpproxy'; then logError 'The reverse proxy service is not running' echo $'\n'"${YELLOW}The Reverse Proxy Service is not running!" echo "The script cannot continue. Exiting...${NORMAL}" stopLoading exit fi logInfo 'The vmware-rhttpproxy service is running' logInfo 'Checking state of the vmafdd service' if ! checkService 'vmafdd'; then logError 'The vmafdd service is not running' echo $'\n'"${YELLOW}The VMware Authentication Framework Service is not running!" echo "The script cannot continue. Exiting...${NORMAL}" stopLoading exit fi logInfo 'The vmafdd service is running' if [ $NODE_TYPE != 'management' ]; then logInfo 'Checking state of the vmdird service' if ! checkService 'vmdird'; then logError 'The vmdird service is not running' echo $'\n'"${YELLOW}The VMware Directory Service is not running!" echo "The script cannot continue. Exiting...${NORMAL}" stopLoading exit else VMDIR_STATE=$(echo 6 | /usr/lib/vmware-vmdir/bin/vdcadmintool 2>/dev/null | awk -F ' - ' '{print $NF}' | tr -d '\n') fi logInfo "The vmdird service is running and in the following state: $VMDIR_STATE" fi } #------------------------------ # Check if PSC is configured to be behind a load balancer #------------------------------ function checkPSCHA() { if [ $NODE_TYPE = 'infrastructure' ]; then PSC_LB=$(grep proxyName /usr/lib/vmware-sso/vmware-sts/conf/server.xml | sed 's/ /\n/g' | grep proxyName | awk -F'=' '{print $NF}' | tr -d '"') fi } #------------------------------ # Notice for additional steps with PSC HA #------------------------------ function noticePSCHA() { if [ $NODE_TYPE = 'infrastructure' ] && [ -n "$PSC_LB" ]; then cat << EOF ${YELLOW}-------------------------!!! WARNING !!!------------------------- This PSC has been detected to be in an HA configuration. - The new certificate and private key should be installed on all other PSCs configured behind the load balancer. - If the load balancer is configured for SSL termination, it will need the new Machine SSL certificate and private key. - If the load balancer is configured for SSL passthrough, no additional configuration should be necessary.${NORMAL} EOF fi } #------------------------------ # Check if service is running #------------------------------ function checkService() { VMON_SERVICE_LIST=$($VMON_CLI -l) if echo "$VMON_SERVICE_LIST" | grep -q 'Connect error'; then if service-control --status $1 | grep -q -i stopped; then return 1 else return 0 fi else if $VMON_CLI -l | grep -q $1; then if $VMON_CLI -s $1 | grep 'RunState' | grep -q 'STARTED'; then return 0 else return 1 fi else if service-control --status $1 | grep -q -i stopped; then return 1 else return 0 fi fi fi } #------------------------------ # Check if machine account can connect to VMware Directory #------------------------------ function checkVmdirMachineCredentials() { logInfo "Checking credentials for machine account $VMDIR_MACHINE_ACCOUNT_DN" if ! $LDAP_SEARCH -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "ou=Domain Controllers,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password dn > /dev/null 2>&1; then LDAP_ERROR="$?" echo $'\n'"${YELLOW}The machine account $VMDIR_MACHINE_ACCOUNT_DN" echo "was unable to authenticate to the VMware Directory instance at $PSC_LOCATION:$VMDIR_PORT" echo "LDAP error: $LDAP_ERROR" echo "The script cannot continue. Exiting...${NORMAL}" logError "Credentials for machine account $VMDIR_MACHINE_ACCOUNT_DN could not be verified (LDAP error: $LDAP_ERROR)" stopLoading exit fi logInfo "Credentials for machine account $VMDIR_MACHINE_ACCOUNT_DN verified" } #------------------------------ # Check if machine account has proper CA permissions #------------------------------ function checkCAPermissions() { logInfo "Machine account DN: $VMDIR_MACHINE_ACCOUNT_DN" logInfo "PSC location: $PSC_LOCATION" if [ $NODE_TYPE != 'management' ]; then DCADMINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=DCAdmins,cn=BuiltIn,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password member | sed -e 's/^ //g' | tr -d '\n' | sed -e 's/member:/\n&/g') logInfo 'Checking DCAdmins membership:' logDetails "$DCADMINS" if echo "$DCADMINS" | grep -iq "$VMDIR_MACHINE_ACCOUNT_DN"; then CAADMINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=CAAdmins,cn=BuiltIn,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password member | sed -e 's/^ //g' | tr -d '\n' | sed -e 's/member:/\n&/g') if echo "$CAADMINS" | grep -iq "cn=DCAdmins,cn=BuiltIn,$VMDIR_DOMAIN_DN"; then return 0 else echo $'\n'"${YELLOW}The DCAdmins SSO group is not a member of the CAAdmins SSO group!" echo "The script cannot continue. Exiting...${NORMAL}" logError 'The DCAdmins SSO group is not a member of the CAAdmins SSO group' stopLoading exit fi else echo $'\n'"${YELLOW}The machine account is not a member of the DCAdmins SSO group!" echo "The script cannot continue. Exiting...${NORMAL}" logError 'The machine account is not a member of the DCAdmins SSO group' stopLoading exit fi else DCCLIENTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=DCClients,cn=BuiltIn,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password member | sed -e 's/^ //g' | tr -d '\n' | sed -e 's/member:/\n&/g') logInfo 'Checking DCClients membership:' logDetails "$DCCLIENTS" if echo "$DCCLIENTS" | grep -iq "$VMDIR_MACHINE_ACCOUNT_DN"; then CAADMINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=CAAdmins,cn=BuiltIn,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password member | sed -e 's/^ //g' | tr -d '\n' | sed -e 's/member:/\n&/g') if echo "$CAADMINS" | grep -iq "cn=DCClients,cn=BuiltIn,$VMDIR_DOMAIN_DN"; then return 0 else echo $'\n'"${YELLOW}The DCAdmins SSO group is not a member of the CAAdmins SSO group!" echo "The script cannot continue. Exiting...${NORMAL}" logError 'The DCAdmins SSO group is not a member of the CAAdmins SSO group' stopLoading exit fi else echo $'\n'"${YELLOW}The machine account is not a member of the DCClients SSO group!" echo "The script cannot continue. Exiting...${NORMAL}" logError 'The machine account is not a member of the DCAdmins SSO group' stopLoading exit fi fi } #------------------------------ # Set the Solution Users for this node #------------------------------ function setSolutionUsers() { SOLUTION_USERS=('machine' 'vsphere-webclient') if [ $NODE_TYPE != 'infrastructure' ]; then SOLUTION_USERS+=('vpxd' 'vpxd-extension') if [[ "$VC_VERSION" =~ ^[78] ]]; then SOLUTION_USERS+=('hvc' 'wcp') fi fi logInfo "Setting Solution Users: ${SOLUTION_USERS[*]}" } #------------------------------ # Set the VECS Stores and default permissions for this node #------------------------------ function setVECSStores() { VECS_STORES='MACHINE_SSL_CERT TRUSTED_ROOTS TRUSTED_ROOT_CRLS machine vsphere-webclient' declare -gA VECS_STORE_READ_PERMISSIONS=() declare -gA VECS_STORE_WRITE_PERMISSIONS=() if [ $NODE_TYPE == 'infrastructure' ]; then VECS_STORE_READ_PERMISSIONS[MACHINE_SSL_CERT]='' VECS_STORE_READ_PERMISSIONS[TRUSTED_ROOTS]='EVERYONE' VECS_STORE_READ_PERMISSIONS[TRUSTED_ROOT_CRLS]='EVERYONE' VECS_STORE_READ_PERMISSIONS[machine]='cm' VECS_STORE_READ_PERMISSIONS[vsphere-webclient]='vapiEndpoint' else VECS_STORES+=' vpxd vpxd-extension SMS' VECS_STORE_READ_PERMISSIONS[MACHINE_SSL_CERT]='updatemgr vsphere-ui vpxd vpostgres vsphere-client vsm' VECS_STORE_READ_PERMISSIONS[TRUSTED_ROOTS]='EVERYONE vpxd' VECS_STORE_READ_PERMISSIONS[TRUSTED_ROOT_CRLS]='EVERYONE vpxd' VECS_STORE_READ_PERMISSIONS[machine]='vpxd cm' VECS_STORE_READ_PERMISSIONS[vsphere-webclient]='vsphere-ui vpxd perfcharts vapiEndpoint' VECS_STORE_READ_PERMISSIONS[vpxd]='vpxd' VECS_STORE_READ_PERMISSIONS[vpxd-extension]='deploy updatemgr vsphere-ui vpxd vsm imagebuilder content-library eam mbcs' VECS_STORE_READ_PERMISSIONS[SMS]='deploy vpxd' fi case $VC_VERSION in '6.5') if [ $NODE_TYPE != 'infrastructure' ]; then VECS_STORE_READ_PERMISSIONS[SMS]='vpxd' VECS_STORE_READ_PERMISSIONS[vpxd-extension]+=' vsphere-client' fi ;; '6.7') VECS_STORES+=' APPLMGMT_PASSWORD' if [ $NODE_TYPE == 'infrastructure' ]; then VECS_STORE_READ_PERMISSIONS[APPLMGMT_PASSWORD]='' else VECS_STORE_READ_PERMISSIONS[APPLMGMT_PASSWORD]='vpxd' VECS_STORE_READ_PERMISSIONS[data-encipherment]='vpxd' VECS_STORE_READ_PERMISSIONS[vpxd-extension]+=' vsphere-client' fi ;; '7.0') VECS_STORES+=' APPLMGMT_PASSWORD data-encipherment hvc wcp' VECS_STORE_READ_PERMISSIONS[MACHINE_SSL_CERT]='updatemgr vsphere-ui vpxd vpostgres vsm' VECS_STORE_READ_PERMISSIONS[machine]='vpxd vsan-health' VECS_STORE_READ_PERMISSIONS[hvc]='vpxd' if [ $VC_BUILD -ge 19480866 ]; then VECS_STORE_READ_PERMISSIONS[vpxd-extension]='vlcm wcp deploy updatemgr vsphere-ui vpxd vsm vsan-health imagebuilder content-library eam vstatsuser' VECS_STORE_READ_PERMISSIONS[wcp]='wcp vpxd content-library' else VECS_STORE_READ_PERMISSIONS[vpxd-extension]='vlcm deploy updatemgr vsphere-ui vpxd vsm imagebuilder content-library eam vstatsuser' VECS_STORE_READ_PERMISSIONS[wcp]='vpxd content-library' fi if [ $VC_BUILD -ge 20051473 ]; then VECS_STORE_WRITE_PERMISSIONS[TRUSTED_ROOTS]='sps' VECS_STORE_READ_PERMISSIONS[machine]+=' observability' VECS_STORE_WRITE_PERMISSIONS[machine]='infraprofile certauth certmgr' VECS_STORE_READ_PERMISSIONS[vsphere-webclient]+=' analytics' VECS_STORE_WRITE_PERMISSIONS[vsphere-webclient]='infraprofile' VECS_STORE_WRITE_PERMISSIONS[vpxd-extension]='infraprofile sps' VECS_STORE_READ_PERMISSIONS[vpxd-extension]+=' analytics' VECS_STORE_WRITE_PERMISSIONS[SMS]='sps' fi VECS_STORE_READ_PERMISSIONS[APPLMGMT_PASSWORD]='vpxd' VECS_STORE_READ_PERMISSIONS[data-encipherment]='vpxd' ;; '8.0') VECS_STORES+=' APPLMGMT_PASSWORD data-encipherment hvc wcp' VECS_STORE_READ_PERMISSIONS[MACHINE_SSL_CERT]='lighttpd updatemgr vlcm vpostgres vpxd vsan-health vsm vsphere-ui' VECS_STORE_WRITE_PERMISSIONS[MACHINE_SSL_CERT]='rhttpproxy' VECS_STORE_READ_PERMISSIONS[machine]='observability sca statsmon vpxd vsan-health' VECS_STORE_WRITE_PERMISSIONS[machine]='certauth certmgr infraprofile' VECS_STORE_READ_PERMISSIONS[vpxd]='vpxd vsan-health' VECS_STORE_WRITE_PERMISSIONS[vpxd]='' VECS_STORE_READ_PERMISSIONS[vpxd-extension]='analytics content-library deploy eam imagebuilder updatemgr vlcm vpxd vsan-health vsm vsphere-ui vstatsuser wcp' VECS_STORE_WRITE_PERMISSIONS[vpxd-extension]='infraprofile sps' VECS_STORE_READ_PERMISSIONS[vsphere-webclient]='analytics perfcharts vapiEndpoint vpxd vsphere-ui' VECS_STORE_WRITE_PERMISSIONS[vsphere-webclient]='infraprofile' VECS_STORE_READ_PERMISSIONS[hvc]='vpxd' VECS_STORE_WRITE_PERMISSIONS[hvc]='' VECS_STORE_READ_PERMISSIONS[wcp]='content-library wcp' VECS_STORE_WRITE_PERMISSIONS[wcp]='' VECS_STORE_READ_PERMISSIONS[data-encipherment]='vpxd' VECS_STORE_WRITE_PERMISSIONS[data-encipherment]='' VECS_STORE_READ_PERMISSIONS[SMS]='deploy' VECS_STORE_WRITE_PERMISSIONS[SMS]='' case $VC_BUILD in 20519528) VECS_STORE_WRITE_PERMISSIONS[machine]='certauth certmgr infraprofile sts' ;;& 21815093|22617221|23319993|23504390|24305161|24321653) VECS_STORE_READ_PERMISSIONS[MACHINE_SSL_CERT]='updatemgr vlcm vpostgres vpxd vsan-health vsm vsphere-ui' ;;& 22385739|22617221|23319993|24321653) VECS_STORE_READ_PERMISSIONS[vpxd-extension]='analytics content-library eam imagebuilder updatemgr vlcm vpxd vsan-health vsm vsphere-ui vstatsuser wcp' ;;& 22385739|22617221|23319993|23504390|23929136|24022515|24091160|24262322|24305161|24321653|24322831) VECS_STORE_READ_PERMISSIONS[machine]='observability sca vpxd vsan-health' ;;& 23504390) VECS_STORE_READ_PERMISSIONS[vpxd-extension]='analytics eam updatemgr vlcm vpxd vsan-health vsm vsphere-ui' ;;& 23929136) VECS_STORE_READ_PERMISSIONS[vpxd-extension]='analytics content-library eam imagebuilder updatemgr vlcm vpxd vsan-health vsm vsphere-ui vstatsuser wcp' VECS_STORE_READ_PERMISSIONS[wcp]='content-library vpxd wcp' VECS_STORE_READ_PERMISSIONS[SMS]='deploy vpxd' ;;& 24022515|24091160|24262322|24305161|24322831) VECS_STORE_READ_PERMISSIONS[vpxd-extension]='analytics content-library eam imagebuilder sps updatemgr vlcm vpxd vsan-health vsm vsphere-ui vstatsuser wcp' VECS_STORE_WRITE_PERMISSIONS[vpxd-extension]='infraprofile' ;; esac ;; esac } #------------------------------ # Check if vCenter is managed by SDDC Manager #------------------------------ function checkForVCF() { SDDC_MANAGER=$($LDAP_SEARCH -LLL -h $PSC_LOCATION -b "cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password '(objectclass=vmwSTSTenant)' vmwSTSLogonBanner | tr -d '\n' | awk -F'::' '{print $NF}' | tr -d ' ' | base64 -d 2>/dev/null | grep 'SDDC Manager' | awk -F '[()]' '{print $2}' | grep -v '^$') } #------------------------------ # Print warning about VCHA on the main operation menu #------------------------------ function operationMenuSDDCWarning() { cat << EOF ${YELLOW}-------------------------!!! WARNING !!!------------------------- This vCenter is managed by the following SDDC Manager: $SDDC_MANAGER Updating certificates may require adding new CA certificates to the SDDC Manager keystore. See https://knowledge.broadcom.com/external/article/316056/how-to-adddelete-custom-ca-certificates.html for details.$NORMAL EOF } #------------------------------ # Get access token for running API calls to SDDC Manager #------------------------------ function getSDDCAccessToken() { authenticateIfNeeded task 'Get API access token' SDDC_API_ACCESS_TOKEN_RESPONSE=$(curl -i -k -X POST https://$SDDC_MANAGER/v1/tokens -H 'Content-Type: application/json' -H 'Accept: application/json' -d "{'username' : '$VMDIR_USER_UPN', 'password' : '$(cat $STAGE_DIR/.vmdir-user-password)'}" 2>&1) if echo "$SDDC_API_ACCESS_TOKEN_RESPONSE" | grep -q 'accessToken'; then SDDC_API_ACCESS_TOKEN=$(echo "$SDDC_API_ACCESS_TOKEN_RESPONSE" | grep '^{' | jq . | grep accessToken | awk '{print $NF}' | tr -d '",') statusMessage 'OK' 'GREEN' else errorMessage 'Unable to get access token from the SDDC Manager' fi } #------------------------------ # Add new CA certificates to SDDC Manager via API #------------------------------ function publishCACertsSDDCManager() { CA_CERT_STRING=$(cat $1 | awk '{printf "%s\\n", $0}' | sed -e 's/[\\n]*$//g') task 'Publish CA cert for outbound connections' logDebug "CA JSON string: $CA_CERT_STRING" if [ -n "$SDDC_API_ACCESS_TOKEN" ]; then SDDC_API_PUBLISH_CA_OUTBOUND_RESPONSE=$(curl -i -k -X POST https://$SDDC_MANAGER/v1/sddc-manager/trusted-certificates -H "Authorization: Bearer $SDDC_API_ACCESS_TOKEN" -H 'Content-Type: application/json' -H 'Accept: application/json' -d "{'certificate':'$CA_CERT_STRING','certificateUsageType':'TRUSTED_FOR_OUTBOUND'}" 2>&1) if echo "$SDDC_API_PUBLISH_CA_OUTBOUND_RESPONSE" | grep '^HTTP' | grep -q '200' || echo "$SDDC_API_PUBLISH_CA_OUTBOUND_RESPONSE" | grep -q 'CERTIFICATE_CHAIN_EXISTS_IN_TRUST_STORE'; then statusMessage 'OK' 'GREEN' else logInfo 'Publish CA to SDDC Manager response (outbound trust):' logDetails "$SDDC_API_PUBLISH_CA_OUTBOUND_RESPONSE" errorMessage 'Unable to publish CA certificate to SDDC Manager' fi if ([[ "$VC_VERSION" =~ ^[7] ]] && [ $VC_BUILD -ge 17327517 ]) && ([[ "$VC_VERSION" =~ ^[7] ]] && [ $VC_BUILD -le 19234570 ]); then task 'Publish CA cert for inbound connections' SDDC_API_PUBLISH_CA_INBOUND_RESPONSE=$(curl -i -k -X POST https://$SDDC_MANAGER/v1/sddc-manager/trusted-certificates -H "Authorization: Bearer $SDDC_API_ACCESS_TOKEN" -H 'Content-Type: application/json' -H 'Accept: application/json' -d "{'certificate':'$CA_CERT_STRING','certificateUsageType':'TRUSTED_FOR_INBOUND'}" 2>&1) if echo "$SDDC_API_PUBLISH_CA_INBOUND_RESPONSE" | grep '^HTTP' | grep -q '200' || echo "$SDDC_API_PUBLISH_CA_INBOUND_RESPONSE" | grep -q 'CERTIFICATE_CHAIN_EXISTS_IN_TRUST_STORE'; then statusMessage 'OK' 'GREEN' else logInfo 'Publish CA to SDDC Manager response (inbound trust):' logDetails "$SDDC_API_PUBLISH_CA_INBOUND_RESPONSE" errorMessage 'Unable to publish CA certificate to SDDC Manager' fi fi else errorMessage 'No API access token found' fi } #------------------------------ # Add new CA certificates to SDDC Manager via API #------------------------------ function publishMachineSSLCACertsSDDCManager() { rm $STAGE_DIR/sddc-ca-*.crt 2>/dev/null header 'Publishing CA certificates to SDDC Manager' getSDDCAccessToken csplit -s -z -f $STAGE_DIR/sddc-ca- -b %02d.crt $TRUSTED_ROOT_CHAIN '/-----BEGIN CERTIFICATE-----/' '{*}' 2>&1 | logDetails for cert in $STAGE_DIR/sddc-ca-*.crt; do publishCACertsSDDCManager $cert done echo $'\n'"${YELLOW}Services may need to be restarted on the SDDC Manager" echo "by running /opt/vmware/vcf/operationsmanager/scripts/cli/sddcmanager_restart_services.sh${NORMAL}" } #------------------------------ # Print the operation menu #------------------------------ function operationMenu() { UPDATED_MACHINE_SSL=0 UPDATED_TRUST_ANCHORS=0 header "vCenter $VC_VERSION Certificate Management Utility ($VERSION)" logInfo 'Printing Main Menu' cat << EOF 1. Check current certificates status 2. View certificate info 3. Manage certificates 4. Manage SSL trust anchors 5. Check configurations 6. Reset all certificates with VMCA-signed certificates 7. ESXi certificate operations 8. Restart services 9. Generate certificate report EOF if isVCHAConfigured; then echo ' I. vCenter High Availability information'; fi echo ' E. Exit' echo '' if isVCHAConfigured; then operationMenuVCHAWarning fi if [ -n "$SDDC_MANAGER" ]; then operationMenuSDDCWarning fi if [ $NODE_TYPE != 'management' ] && [ "$VMDIR_STATE" != 'Normal' ] && [ "$VMDIR_STATE" != 'Standalone' ]; then echo "${YELLOW}The VMware Directory service is not in NORMAL mode ($VMDIR_STATE)!" echo 'Certificate operations should not be actioned until this service' echo "is running correctly in a NORMAL state.${NORMAL}" exit else if [ "$NODE_TYPE" != 'infrastructure' ] && ! checkService 'vmware-vpostgres'; then echo "${YELLOW}The vPostgres service is stopped!" echo "Many certificate operations require changes to the vCenter database." echo "Please ensure this service is running before replacing any certificates." echo "Hint: Check the number of CRL entries in VECS${NORMAL}" echo '' fi read -p 'Select an option [1]: ' OPERATION if [ -z $OPERATION ]; then OPERATION=1; fi logInfo "User selected option '$OPERATION'" fi } #------------------------------ # Check if VCHA is configured #------------------------------ function isVCHAConfigured() { if grep -q 'HACore' /storage/vmware-vmon/defaultStartProfile; then VMON_SERVICE_PROFILE='--vmon-profile HAActive' return 0 else VMON_SERVICE_PROFILE='--all' return 1 fi } #------------------------------ # Get current VCHA mode #------------------------------ function getVCHAMode() { authenticateIfNeeded VCHA_MODE='UNKNOWN' SESSION_HEADER=$(curl -k -i -u "$VMDIR_USER_UPN:$(cat $STAGE_DIR/.vmdir-user-password)" -X POST -c $STAGE_DIR/session-info.txt https://localhost/rest/com/vmware/cis/session 2>/dev/null) if echo "$SESSION_HEADER" | grep '^HTTP' | grep -q '200'; then if [[ "$VC_VERSION" =~ ^6 ]]; then VCHA_MODE_INFO=$(curl -k -b $STAGE_DIR/session-info.txt https://localhost/rest/vcenter/vcha/cluster/mode 2>/dev/null | python -m json.tool --sort-keys) else VCHA_MODE_INFO=$(curl -k -b $STAGE_DIR/session-info.txt https://localhost/rest/vcenter/vcha/cluster/mode 2>/dev/null | jq .) fi logDebug "VCHA Mode API call: $VCHA_MODE_INFO" VCHA_MODE=$(echo "$VCHA_MODE_INFO" | grep 'mode' | awk '{print $NF}' | tr -d '"') fi logInfo "VCHA Mode: $VCHA_MODE" } #------------------------------ # Print warning about VCHA on the main operation menu #------------------------------ function operationMenuVCHAWarning() { echo "${YELLOW}-------------------------!!! WARNING !!!-------------------------" printf 'vCenter High Availability has been configured,' if service-control --status vmware-vcha | grep -i stopped; then printf " but the\nservice is currently stopped. " else printf " and the\nservice is currently running. " fi printf "\n\n%s\n%s\n\n" "Restarting services may trigger a failover." "For more information, select option 'I' from the menu.${NORMAL}" } #------------------------------ # Print VCHA information #------------------------------ function VCHAInfo() { if [ $NODE_TYPE != 'infrastructure' ]; then getVCHAMode cat << EOF ${YELLOW}The supported methods of replacing SSL certificates with vCenter High Availability configured are: 1. Place VCHA into Maintenance Mode so restarting services does not trigger an automatic failover, or 2. Destroy the VCHA cluster, replace the SSL certificate(s), and re-create the VCHA cluster EOF case $VCHA_MODE in 'MAINTENANCE') cat << EOF The VCHA cluster is in Maintenance Mode, so you should be able to proceed with the certificate replacement.${NORMAL} EOF if checkService 'vpxd'; then read -p 'Place VCHA cluster into Enabled Mode? [n]: ' VCHA_SET_MM_INPUT if [[ "$VCHA_SET_MM_INPUT" =~ ^[Yy] ]]; then header 'vCenter High Availability Mode' task 'Put VCHA cluster into Enabled Mode' VCHA_MM_API=$(curl -k -i -b $STAGE_DIR/session-info.txt -H 'Content-Type: application/json' -X PUT https://localhost/rest/vcenter/vcha/cluster/mode?vmw-task=true -d '{"mode":"ENABLED"}' 2>/dev/null) logDebug "$VCHA_MM_API" if echo "$VCHA_MM_API" | grep '^HTTP' | grep -q '200'; then statusMessage 'OK' 'GREEN' else errorMessage 'Unable to place VCHA cluster into Enabled Mode' fi fi fi ;; 'ENABLED') cat << EOF The VCHA cluster is in Enabled Mode, so restarting services will trigger a failover to the Passive Node. It is recommended to place the VCHA cluster into Maintenance Mode before performing operations on any SSL certificates.${NORMAL} EOF if checkService 'vpxd'; then read -p 'Place VCHA cluster into Maintenance Mode? [n]: ' VCHA_SET_MM_INPUT if [[ "$VCHA_SET_MM_INPUT" =~ ^[Yy] ]]; then header 'vCenter High Availability Mode' task 'Put VCHA cluster into Maintenance Mode' VCHA_MM_API=$(curl -k -i -b $STAGE_DIR/session-info.txt -H 'Content-Type: application/json' -X PUT https://localhost/rest/vcenter/vcha/cluster/mode?vmw-task=true -d '{"mode":"MAINTENANCE"}' 2>/dev/null) logDebug "$VCHA_MM_API" if echo "$VCHA_MM_API" | grep '^HTTP' | grep -q '200'; then statusMessage 'OK' 'GREEN' else errorMessage 'Unable to place VCHA cluster into Maintenance Mode' fi fi fi ;; 'UNKNOWN') cat << EOF The state of the VCHA cluster cannot be determined via the REST API. It is recommended to destroy the VCHA cluster before performing operations on any SSL certificates.${NORMAL} EOF ;; esac else printf "\n%s\n\n" "${YELLOW}Invalid operation${NORMAL}" fi } #------------------------------ # Process the operation selected by user #------------------------------ function processOperationMenu() { setTimestamp if [[ $OPERATION =~ ^[Ee] ]]; then cleanup exit fi if [[ $OPERATION =~ ^[Ii] ]]; then VCHAInfo elif [[ "$OPERATION" =~ ^[0-9]+$ ]]; then echo '' case $OPERATION in 1) checkCerts ;; 2) viewCertificateMenu ;; 3) manageCertificateMenu ;; 4) manageSSLTrustAnchors ;; 5) checkConfigurationMenu ;; 6) resetAllCertificates ;; 7) manageESXiCertificates ;; 8) restartServicesMenu ;; 9) generatevCenterCertificateReport ;; *) echo $'\n'"${YELLOW}Invalid operation${NORMAL}" ;; esac else echo $'\n'"${YELLOW}Invalid operation${NORMAL}" fi operationMenu processOperationMenu } #------------------------------ # Perform quick check of certificates #------------------------------ function checkCerts() { authenticateIfNeeded resetCertStatusChecks exportTrustedCACerts header 'Checking Certificate Status' task 'Checking Machine SSL certificate' checkVECSCert 'MACHINE_SSL_CERT' '__MACHINE_CERT' if checkMachineSSLCSR; then task 'Checking Machine SSL CSR' checkVECSCert 'MACHINE_SSL_CERT' '__MACHINE_CSR' fi echo 'Checking Solution User certificates:' for soluser in "${SOLUTION_USERS[@]}"; do task " $soluser" checkVECSCert "$soluser" "$soluser" done if [ $NODE_TYPE != 'infrastructure' ]; then task 'Checking SMS self-signed certificate' checkVECSCert 'SMS' 'sms_self_signed' if [[ "$VC_VERSION" =~ ^8 ]] && [ $VC_BUILD -ge 22385739 ]; then task 'Checking SMS VMCA-signed certificate' checkVECSCert 'SMS' 'sps-extension' fi if [ "$VC_VERSION" != '6.5' ]; then task 'Checking data-encipherment certificate' checkVECSCert 'data-encipherment' 'data-encipherment' fi task 'Checking Authentication Proxy certificate' checkFilesystemCert '/var/lib/vmware/vmcam/ssl/vmcamcert.pem' task 'Checking Auto Deploy CA certificate' checkFilesystemCert '/etc/vmware-rbd/ssl/rbd-ca.crt' fi if checkVECSStore 'BACKUP_STORE'; then echo 'Checking BACKUP_STORE entries:' for alias in $($VECS_CLI entry list --store BACKUP_STORE | grep Alias | awk '{print $NF}'); do task " $alias" checkVECSCert 'BACKUP_STORE' $alias done fi if checkVECSStore 'BACKUP_STORE_H5C'; then echo 'Checking BACKUP_STORE_H5C entries:' for alias in $($VECS_CLI entry list --store BACKUP_STORE_H5C | grep Alias | awk '{print $NF}'); do task " $alias" checkVECSCert 'BACKUP_STORE_H5C' $alias done fi if checkVECSStore 'STS_INTERNAL_SSL_CERT'; then task 'Checking legacy Lookup Service certificate' checkVECSCert 'STS_INTERNAL_SSL_CERT' '__MACHINE_CERT' fi if [ $NODE_TYPE != 'management' ]; then if [ -f '/usr/lib/vmware-vmdir/share/config/vmdircert.pem' ]; then task 'Checking VMDir certificate' checkFilesystemCert '/usr/lib/vmware-vmdir/share/config/vmdircert.pem' fi task 'Checking VMCA certificate' checkFilesystemCert "$VMCA_CERT" header 'Checking STS Signing Certs & Signing Chains' manageSTSTenantCerts 'Check' fi checkCACertificates if [ "$VC_VERSION" != '6.5' ]; then checkVMCACertsSSO; fi checkSMSVASACerts quickCheckVECSStores quickCheckServicePrincipals checkCRLs manageCACCerts 'Check' manageLDAPSCerts 'Check' manageTanzuSupervisorClusterCerts 'Check' quickCheckSSLTrustAnchors if [ $NODE_TYPE != 'infrastructure' ]; then manageVCExtensionThumbprints 'Checking' checkAutoDeployDB checkVMCADatabaseConfig fi if [ $NODE_TYPE != 'management' ]; then quickCheckSTSConfig fi buildCertificateStatusMessage if [ -n "$CERT_STATUS_MESSAGE" ]; then echo $'\n'"${YELLOW}------------------------!!! Attention !!!------------------------ " echo "$CERT_STATUS_MESSAGE${NORMAL}" fi } #------------------------------ # Resets the certificate status flags #------------------------------ function resetCertStatusChecks() { CERT_STATUS_MESSAGE='' CERT_STATUS_EXPIRES_SOON=0 CERT_STATUS_MISSING_PNID=0 CERT_STATUS_MISSING_SAN=0 CERT_STATUS_KEY_USAGE=0 CERT_STATUS_EXPIRED=0 CERT_STATUS_NON_CA=0 CERT_STATUS_BAD_ALIAS=0 CERT_STATUS_SHA1_SIGNING=0 CERT_STATUS_MISSING=0 CERT_STATUS_MISSING_VMDIR=0 CERT_STATUS_MISMATCH_SERVICE_PRINCIPAL=0 CERT_STATUS_TOO_MANY_CRLS=0 CERT_STATUS_MISSING_CA=0 CERT_STATUS_EXPIRED_EMBEDDED_CA=0 CERT_STATUS_STORE_MISSING=0 CERT_STATUS_STORE_PERMISSIONS=0 CERT_STATUS_SERVICE_PRINCIPAL_MISSING=0 CERT_STATUS_VMCA_EMPTY_CONFIG=0 CERT_STATUS_VMCA_MODE=0 CERT_STATUS_VMCA_UNRETRIEVABLE=0 CERT_STATUS_VMCA_MISSING=0 CERT_STATUS_CLIENT_CA_LIST_FILE_LOCATION=0 CERT_STATUS_CLIENT_CA_LIST_FILE_MISSING=0 CERT_STATUS_STS_VECS_CONFIG=0 CERT_STATUS_STS_CONNECTION_STRINGS=0 CERT_STATUS_UNSUPPORTED_SIGNATURE_ALGORITHM=0 CERT_STATUS_CA_MISSING_SKID=0 CERT_ROGUE_CA=0 } #------------------------------ # Export CA certs from VMDir #------------------------------ function exportTrustedCACerts() { rm $TRUSTED_CA_DIR/* 2>/dev/null for skid in $($DIR_CLI trustedcert list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | awk '{print $NF}'); do $DIR_CLI trustedcert get --id $skid --outcert $TRUSTED_CA_DIR/$skid.crt --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>&1 | logInfo done } #------------------------------ # Checks on certificates in VECS #------------------------------ function checkVECSCert() { KU_LIST='Digital Signature Key Encipherment Key Agreement Data Encipherment Non Repudiation' case $1 in 'MACHINE_SSL_CERT') CHECK_PNID=1 CHECK_KU=1 CHECK_SAN=1 CHECK_SERVICE_PRINCIPAL=0 CHECK_CA_CHAIN=1 CHECK_ROGUE_CA=1 CHECK_EMBEDDED_CHAIN=1 ;; SMS) CHECK_PNID=0 CHECK_KU=0 CHECK_SAN=0 CHECK_SERVICE_PRINCIPAL=0 CHECK_CA_CHAIN=0 CHECK_ROGUE_CA=0 CHECK_EMBEDDED_CHAIN=0 ;; *) CHECK_PNID=0 CHECK_KU=1 CHECK_SAN=1 CHECK_CA_CHAIN=1 CHECK_ROGUE_CA=1 CHECK_EMBEDDED_CHAIN=1 CHECK_SERVICE_PRINCIPAL=0 if [[ " ${SOLUTION_USERS[*]} " =~ " $1 " ]]; then CHECK_SERVICE_PRINCIPAL=1; fi if [ "$1" == 'wcp' ]; then CHECK_SAN=0; fi ;; esac logInfo "Checking VECS for store '$2'" if ! $VECS_CLI entry list --store $1 | grep Alias | grep $2 > /dev/null 2>&1; then CERT_STATUS_MISSING=1 statusMessage 'NOT FOUND' 'RED' logError "Store '$2' not found" return 1 else logInfo "Found Store '$2'" fi TEMP_CERT=$($VECS_CLI entry getcert --store $1 --alias $2 2>/dev/null) if [ -z "$TEMP_CERT" ]; then statusMessage 'PROBLEM' 'RED' logError "No certificate found for alias '$2' in store '$1'" return 1 fi if ! isExpired "$TEMP_CERT" 'hash'; then DAYS_LEFT=$(checkCertExpireSoon "$TEMP_CERT") if [[ $DAYS_LEFT -ge 0 ]]; then CERT_STATUS_EXPIRES_SOON=1 statusMessage "$DAYS_LEFT DAYS" 'YELLOW' logInfo "Certificate for alias '$2' in store '$1' expires in $DAYS_LEFT days" return 0 else if [ $CHECK_PNID = 1 ]; then if ! echo "$TEMP_CERT" | openssl x509 -noout -text 2>&1 | grep -A1 'Subject Alternative Name' | grep -iq "$PNID"; then CERT_STATUS_MISSING_PNID=1 statusMessage 'NO PNID' 'YELLOW' logInfo "Certificate for alias '$2' in store '$1' does not have the PNID ($PNID) in the Subject Alternative Name field" return 0 fi fi if [ $CHECK_KU = 1 ]; then if ! checkCertKeyUsage "$TEMP_CERT" "$1:$2" "$KU_LIST"; then CERT_STATUS_KEY_USAGE=1 statusMessage 'KEY USAGE' 'YELLOW' logInfo "Certificate for alias '$2' in store '$1' does not have the expected Key Usage Values" return 0 fi fi if [ $CHECK_SAN = 1 ]; then if ! echo "$TEMP_CERT" | openssl x509 -noout -text 2>&1 | grep -q 'Subject Alternative Name'; then CERT_STATUS_MISSING_SAN=1 statusMessage 'NO SAN' 'YELLOW' logInfo "Certificate for alias '$2' in store '$1' has no values in the Subject Alternative Name field" return 0 fi fi if [ $CHECK_SERVICE_PRINCIPAL = 1 ]; then if ! checkServicePrincipalCert "$1"; then CERT_STATUS_MISMATCH_SERVICE_PRINCIPAL=1 statusMessage 'MISMATCH' 'YELLOW' logError "The certificate in the VECS store '$1' does not match the certificate for the corresponding Service Principal in VMware Directory" return 0 fi fi if [ $CHECK_CA_CHAIN = 1 ]; then if ! checkCACertsPresent "$TEMP_CERT"; then CERT_STATUS_MISSING_CA=1 statusMessage 'MISSING CA' 'YELLOW' logError "One of the CA certificates in the signing chain for the certificate for alias '$2' in store '$1' is missing" return 0 fi fi if [ $CHECK_ROGUE_CA = 1 ]; then if checkRogueCA "$TEMP_CERT"; then CERT_ROGUE_CA=1 statusMessage 'ROGUE' 'YELLOW' logError "Cert in alias '$2' in store '$1' is invalid due to a CA extending past a parent CA pathlen restrictions" return 0 fi fi if [ $CHECK_EMBEDDED_CHAIN = 1 ]; then if ! checkEmbeddedChain "$TEMP_CERT"; then CERT_STATUS_EXPIRED_EMBEDDED_CA=1 statusMessage 'EMBEDDED CA' 'YELLOW' return 0 fi fi if ! checkCertSignatureAlgorithm "$TEMP_CERT"; then CERT_STATUS_UNSUPPORTED_SIGNATURE_ALGORITHM=1 statusMessage 'ALGORITHM' 'YELLOW' logInfo "Certificate for alias '$2' in store '$1' is signed with an unsupported Signature Algorithm" return 0 fi statusMessage 'VALID' 'GREEN' return 0 fi else CERT_STATUS_EXPIRED=1 statusMessage 'EXPIRED' 'YELLOW' logError "Certificate for alias '$2' in store '$1' is expired" return 1 fi } #------------------------------ # Check for the existence of the __MACHINE_CSR alias in VECS #------------------------------ function checkMachineSSLCSR() { if $VECS_CLI entry list --store 'MACHINE_SSL_CERT' | grep Alias | grep '__MACHINE_CSR' > /dev/null 2>&1; then return 0 else return 1 fi } #------------------------------ # Check Solution User cert in VECS matches Service Principal #------------------------------ function checkServicePrincipalCert() { VECS_THUMBPRINT=$($VECS_CLI entry getcert --store $1 --alias $1 2>&1 | openssl x509 -noout -fingerprint -sha1 2>&1) SERVICE_PRINCIPAL_HASH=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$1-$MACHINE_ID,cn=ServicePrincipals,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password userCertificate 2>&1 | grep '^userCertificate' | awk '{print $NF}') SERVICE_PRINCIPAL_CERT=$(buildCertFromHash "$SERVICE_PRINCIPAL_HASH") SERVICE_PRINCIPAL_THUMBPRINT=$(echo "$SERVICE_PRINCIPAL_CERT" | openssl x509 -noout -fingerprint -sha1 2>&1) logInfo "Checking Service Principal: VECS Thumbprint: $VECS_THUMBPRINT" logInfo "Checking Service Principal: Service Principal Thumbprint: $SERVICE_PRINCIPAL_THUMBPRINT" if [ "$VECS_THUMBPRINT" = "$SERVICE_PRINCIPAL_THUMBPRINT" ]; then return 0 else return 1 fi } #------------------------------ # Check if certificate on the file system has expired #------------------------------ function checkFilesystemCert() { if [ ! -f $1 ]; then CERT_STATUS_MISSING=1 statusMessage 'NOT FOUND' 'RED' logError "Certificate at $1 could not be found" return 1 fi logInfo "Checking certificate at $1" FS_CERT=$(cat $1) checkCert "$FS_CERT" } #------------------------------ # Check if certificate has expired #------------------------------ function checkCert() { logInfo 'Checking the following certificate' logDetails "$1" logDebugDetails "'$(echo "$1" | openssl x509 -noout -text 2>/dev/null)'" if ! isExpired "$1" 'hash'; then DAYS_LEFT=$(checkCertExpireSoon "$1") if [[ $DAYS_LEFT -gt 0 ]]; then CERT_STATUS_EXPIRES_SOON=1 statusMessage "$DAYS_LEFT DAYS" 'YELLOW' logInfo "Certificate expires in $DAYS_LEFT days" return 0 else if ! checkCertSignatureAlgorithm "$1"; then CERT_STATUS_UNSUPPORTED_SIGNATURE_ALGORITHM=1 statusMessage 'ALGORITHM' 'YELLOW' return 1 fi if isCertCA "$1" && ! echo "$1" | openssl x509 -noout -text 2>/dev/null | grep -q 'Subject Key Identifier:'; then if [[ "$VC_VERSION" =~ ^[67] ]] && ! echo "$1" | openssl x509 -noout -subject | grep -q 'Auto Deploy'; then CERT_STATUS_CA_MISSING_SKID=1 statusMessage 'NO SKID' 'YELLOW' return 1 fi fi statusMessage 'VALID' 'GREEN' logInfo 'Certificate is valid' return 0 fi else CERT_STATUS_EXPIRED=1 statusMessage 'EXPIRED' 'YELLOW' logError 'Certificate is expired' return 1 fi } #------------------------------ # Check if a certificate is expired #------------------------------ function isExpired() { if [ "$2" == 'file' ]; then HASH=$(cat "$1") else HASH="$1" fi logInfo 'Checking expiration of the following certificate' logDetails "$HASH" if echo "$HASH" | openssl x509 -noout -checkend 0 > /dev/null 2>&1; then return 1 else return 0 fi } #------------------------------ # Backup certificate and key from filesystem #------------------------------ function backupFilesystemCertKey() { task 'Backing up certificate and private key' if [ -f $1 ]; then cp $1 $BACKUP_DIR/$3-$TIMESTAMP.crt 2>&1 | logDebug || errorMessage "Unable to backup $3 certificate" else statusMessage 'NOT FOUND' 'YELLOW' logError "Certificate not found at $1" fi if [ -f $2 ]; then cp $2 $BACKUP_DIR/$3-$TIMESTAMP.key 2>&1 | logDebug || errorMessage "Unable to backup $3 key" else statusMessage 'NOT FOUND' 'YELLOW' logError "Private key not found at $2" fi statusMessage 'OK' 'GREEN' logInfo "Certificate and key backed up to $BACKUP_DIR/$3-$TIMESTAMP.crt and $BACKUP_DIR/$3-$TIMESTAMP.key" } #------------------------------ # Check if cert has unsupported signature algorithms #------------------------------ function checkCertSignatureAlgorithm() { CERT_HASH=$1 CERT_SIGNATURE_ALGORITHM=$(echo "$CERT_HASH" | openssl x509 -noout -text | grep 'Signature Algorithm' | head -n1 | awk '{print $NF}') logInfo "Checking certificate signature algorithm '$CERT_SIGNATURE_ALGORITHM' against unsupported signature algorithms ($UNSUPPORTED_SIGNATURE_ALGORITHMS)" if echo "$CERT_SIGNATURE_ALGORITHM" | grep -iqE "$UNSUPPORTED_SIGNATURE_ALGORITHMS"; then return 1 else return 0 fi } #------------------------------ # Check if cert has recommended Key Usage #------------------------------ function checkCertKeyUsage() { CERT_HASH=$1 CERT_DESCRIPTION=$2 KU_LIST=$3 UNSUPPORTED_KEY_USAGE=0 if ! echo "$CERT_HASH" | openssl x509 -text -noout 2>/dev/null | grep -q 'X509v3 Key Usage'; then return 0 fi logInfo "Checking Key Usage for cert $CERT_DESCRIPTION among supported values of: $KU_LIST" KEY_USAGE_SEARCH=$(echo "$CERT_HASH" | openssl x509 -text -noout 2>/dev/null | grep -A1 'X509v3 Key Usage' | tail -n1 | sed -e 's/^[[:space:]]*//' -e 's/, /\n/g') logInfo "Key Usages found in cert:" logDetails "$KEY_USAGE_SEARCH" if [ -z "$KEY_USAGE_SEARCH" ]; then return 1; fi IFS=$'\n' for key_usage in $KEY_USAGE_SEARCH; do KEY_USAGE_SEARCH_RESULT=$(echo "$KU_LIST" | grep "$key_usage") if [ -z "$KEY_USAGE_SEARCH_RESULT" ]; then logInfo "Found unsupported Key Usage value: $key_usage" UNSUPPORTED_KEY_USAGE=1 else logInfo "Found supported Key Usage value: $key_usage" fi done IFS=$' \t\n' if [ "$UNSUPPORTED_KEY_USAGE" == 1 ]; then return 1 else return 0 fi } #------------------------------ # Check if cert is expiring within 30 days #------------------------------ function checkCertExpireSoon() { if ! echo "$1" | openssl x509 -noout -checkend 2592000 > /dev/null 2>&1; then CERT_END_DATE=$(echo "$1" | openssl x509 -noout -enddate 2>/dev/null | sed "s/.*=\(.*\)/\1/") CERT_END_EPOCH=$(date -d "$CERT_END_DATE" +%s) NOW_EPOCH=$(date -d now +%s) DAYS_LEFT=$(( (CERT_END_EPOCH - NOW_EPOCH) / 86400)) echo "$DAYS_LEFT" else echo '-1' fi } #------------------------------ # Check VASA Provider certs in SMS store #------------------------------ function checkSMSVASACerts() { if [ $NODE_TYPE != 'infrastructure' ]; then SMS_VASA_ENTRIES=$($VECS_CLI entry list --store SMS | grep Alias | sed -e 's/Alias ://g' -e 's/^[[:space:]]*//g' | grep -vE '^sms_self_signed$|^sps-extension$') if [ -n "$SMS_VASA_ENTRIES" ]; then header 'Checking Additional Entries in SMS Store' for alias in $SMS_VASA_ENTRIES; do task "$alias" checkVECSCert 'SMS' "$alias" done fi fi } #------------------------------ # Quick check of VECS store status and permissions #------------------------------ function quickCheckVECSStores() { header 'Checking VECS Stores' echo 'Checking status and permissions for VECS stores:' logInfo 'Checking status and permissions for VECS stores' MISSING_STORES='' declare -gA MISSING_STORE_READ_PERMISSIONS=() declare -gA MISSING_STORE_WRITE_PERMISSIONS=() for store in $VECS_STORES; do task " $store" if ! checkVECSStore $store; then CERT_STATUS_STORE_MISSING=1 if [ -z "$MISSING_STORES" ]; then MISSING_STORES+="$store"; else MISSING_STORES+=" $store"; fi statusMessage 'MISSING' 'YELLOW' logError "Could not find store '$store' in VECS" else PERMISSIONS_OK=1 STORE_PERMISSIONS=$($VECS_CLI store get-permissions --name $store) STORE_PERMISSIONS_FORMATTED=$'\n'$(echo "$STORE_PERMISSIONS" | head -n2) STORE_PERMISSIONS_FORMATTED+=$'\n'$(echo "$STORE_PERMISSIONS" | tail -n+3 | column -t) logInfo "Permissions for VECS store $store:" logDetails "$STORE_PERMISSIONS_FORMATTED" logInfo "Users with expected read permissions: ${VECS_STORE_READ_PERMISSIONS[$store]}" logInfo "Users with expected write permissions: ${VECS_STORE_WRITE_PERMISSIONS[$store]}" for user in ${VECS_STORE_READ_PERMISSIONS[$store]}; do if ! echo "$STORE_PERMISSIONS" | grep $user | grep -q 'read'; then logInfo "Could not find read permission for user $user in VECS store $store" if [ -z ${MISSING_STORE_READ_PERMISSIONS[$store]} ]; then MISSING_STORE_READ_PERMISSIONS[$store]="$user"; else MISSING_STORE_READ_PERMISSIONS[$store]=" $user"; fi PERMISSIONS_OK=0 else logInfo "Found read permission for user $user in VECS store $store" fi done if [[ "$VC_VERSION" =~ ^[78] ]] && [ $VC_BUILD -ge 20051473 ]; then for user in ${VECS_STORE_WRITE_PERMISSIONS[$store]}; do if ! echo "$STORE_PERMISSIONS" | grep $user | grep -qE 'OWNER|write'; then logInfo "Could not find write permission for user $user in VECS store $store" if [ -z ${MISSING_STORE_WRITE_PERMISSIONS[$store]} ]; then MISSING_STORE_WRITE_PERMISSIONS[$store]="$user"; else MISSING_STORE_WRITE_PERMISSIONS[$store]=" $user"; fi PERMISSIONS_OK=0 else logInfo "Found write permission for user $user in VECS store $store" fi done fi if [ $PERMISSIONS_OK == 1 ]; then statusMessage 'OK' 'GREEN' else CERT_STATUS_STORE_PERMISSIONS=1 statusMessage 'PERMISSIONS' 'YELLOW' fi fi done } #------------------------------ # Check and remediation of VECS store status and permissions #------------------------------ function checkVECSStores() { quickCheckVECSStores unset RECREATE_VECS_STORES_INPUT unset FIX_VECS_STORES_PERMISSIONS if [ -n "$MISSING_STORES" ]; then read -p $'\n'"Some VECS stores are missing, recreate them? [n]: " RECREATE_VECS_STORES_INPUT if [[ $RECREATE_VECS_STORES_INPUT =~ ^[Yy] ]]; then recreateMissingVECSStores; fi fi if [[ ${MISSING_STORE_READ_PERMISSIONS[*]} ]] || [[ ${MISSING_STORE_WRITE_PERMISSIONS[*]} ]]; then read -p $'\n'"Some VECS stores are missing expected permissions, reassign them? [n]: " FIX_VECS_STORES_PERMISSIONS if [[ $FIX_VECS_STORES_PERMISSIONS =~ ^[Yy] ]]; then fixVECSStorePermissions; fi fi } #------------------------------ # Recreate missing VECS store #------------------------------ function recreateMissingVECSStores() { header 'Recreate missing VECS stores' for store in $MISSING_STORES; do task "Recreate store $store" if [ "$store" == "SMS" ]; then vmon-cli -r sps > /dev/null 2>&1 || errorMessage "Unable to create the VECS store SMS by restarting the sps service" else $VECS_CLI store create --name $store > /dev/null 2>&1 || errorMessage "Unable to create VECS store $store" fi statusMessage 'OK' 'GREEN' echo 'Assigning permissions:' for user in ${VECS_STORE_READ_PERMISSIONS[$store]}; do task " Read permmisson for user $user" $VECS_CLI store permission --name $store --user $user --grant read > /dev/null 2>&1 || errorMessage "Unable to assign read permission to user $user on store $store" statusMessage 'OK' 'GREEN' done if [[ "$VC_VERSION" =~ ^[78] ]] && [ $VC_BUILD -ge 20051473 ]; then for user in ${VECS_STORE_WRITE_PERMISSIONS[$store]}; do task " Write permmisson for user $user" $VECS_CLI store permission --name $store --user $user --grant write > /dev/null 2>&1 || errorMessage "Unable to assign write permission to user $user on store $store" statusMessage 'OK' 'GREEN' done fi done } #------------------------------ # Recreate missing VECS store permissions #------------------------------ function fixVECSStorePermissions() { header 'Fix VECS store permissions' logInfo "Stores missing read permissions: ${!MISSING_STORE_READ_PERMISSIONS[*]}" for store in "${!MISSING_STORE_READ_PERMISSIONS[@]}"; do echo "Assign read permissions on store $store:" for user in ${MISSING_STORE_READ_PERMISSIONS[$store]}; do task " Read permission for user $user" $VECS_CLI store permission --name $store --user $user --grant read > /dev/null 2>&1 || errorMessage "Unable to assign read permission to user $user on store $store" statusMessage 'OK' 'GREEN' done done if [[ "$VC_VERSION" =~ ^[78] ]] && [ $VC_BUILD -ge 20051473 ]; then logInfo "Stores missing write permissions: ${!MISSING_STORE_WRITE_PERMISSIONS[*]}" for store in "${!MISSING_STORE_WRITE_PERMISSIONS[@]}"; do echo "Assign write permissions on store $store:" for user in ${MISSING_STORE_WRITE_PERMISSIONS[$store]}; do task " Write permission for user $user" $VECS_CLI store permission --name $store --user $user --grant write > /dev/null 2>&1 || errorMessage "Unable to assign write permission to user $user on store $store" statusMessage 'OK' 'GREEN' done done fi } #------------------------------ # Check if a particular VECS store is present #------------------------------ function checkVECSStore() { if $VECS_CLI store list | grep -q "^$1\$"; then return 0 else return 1 fi } #------------------------------ # Check if a particular entry exists in a VECS store #------------------------------ function checkVECSEntry() { if $VECS_CLI entry list --store "$1" | grep -q "$2\$"; then return 0 else return 1 fi } #------------------------------ # Manage STS Signing certificates #------------------------------ function manageSTSTenantCerts() { case $1 in 'Check') checkSTSTenantCerts checkSTSTrustedCertChains ;; 'View') viewSTSTenantCerts ;; 'Replace') authenticateIfNeeded viewSTSTenantCerts if promptReplaceSTS; then if replaceSSOSTSCert; then promptRestartVMwareServices; fi fi ;; esac } #------------------------------ # Manage Smart Card (CAC) certificates #------------------------------ function manageCACCerts() { case $1 in 'Check') if configuredForCAC; then checkRhttpproxyCACCerts checkVMDirCACCerts fi ;; 'View') if configuredForCAC; then viewRhttpproxyCACCerts viewVMDirCACCerts else echo $'\n'"${YELLOW}This vCenter Server is not configured for Smart Card authentication${NORMAL}" fi ;; 'Manage') if configuredForCAC; then viewRhttpproxyCACCerts viewVMDirCACCerts header 'Manage Smart Card Issuing CA Certificates' cat << EOF 1. Add Smart Card issuing CA certificate(s) to Reverse Proxy filter file 2. Remove Smart Card issuing CA certificate(s) from Reverse Proxy filter file 3. Add Smart Card issuing CA certificate(s) to VMware Directory 4. Remove Smart Card issuing CA certificate(s) from VMware Directory EOF read -p $'\n'"Enter selection [Return to Main Menu]: " MANAGE_CAC_INPUT case $MANAGE_CAC_INPUT in 1) addCACCertsFilterFile ;; 2) removeCACCertsFilterFile ;; 3) updateSSOCACConfig 'add' ;; 4) updateSSOCACConfig 'remove' ;; esac else echo $'\n'"This vCenter Server is not configured for Smart Card authentication" read -t $READ_TIMEOUTS -p $'\nConfigure vCenter Server for Smart Card authentication? [n]: ' CONFIGURE_CAC_INPUT if [ $? -lt 128 ]; then if [[ "$CONFIGURE_CAC_INPUT" =~ ^[Yy] ]]; then configureCACAuthentication; fi else echo '' fi fi ;; esac } #------------------------------ # Get the configured path for the Smart Card Filter file #------------------------------ function getCACFilterFile() { logInfo 'Obtaining Smart Card filter file path' case $VC_VERSION in '7.0') if [ "$VC_BUILD" -ge 20845200 ]; then FILTER_FILE='/usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem' else RHTTPPROXY_CONFIG=$(xmllint --xpath "string(//clientCAListFile/text())" /etc/vmware-rhttpproxy/config.xml) if [ -z "$RHTTPPROXY_CONFIG" ]; then FILTER_FILE='' else if [[ "$RHTTPPROXY_CONFIG" =~ ^/ ]]; then FILTER_FILE="$RHTTPPROXY_CONFIG" else FILTER_FILE="/etc/vmware-rhttpproxy/$RHTTPPROXY_CONFIG" fi fi fi ;; '8.0') FILTER_FILE='/usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem' ;; *) RHTTPPROXY_CONFIG=$(xmllint --xpath "string(//clientCAListFile/text())" /etc/vmware-rhttpproxy/config.xml) if [ -z "$RHTTPPROXY_CONFIG" ]; then FILTER_FILE='' else if [[ "$RHTTPPROXY_CONFIG" =~ ^/ ]]; then FILTER_FILE="$RHTTPPROXY_CONFIG" else FILTER_FILE="/etc/vmware-rhttpproxy/$RHTTPPROXY_CONFIG" fi fi ;; esac logInfo "Smart Card filter file: $FILTER_FILE" echo "$FILTER_FILE" } #------------------------------ # Check Reverse Proxy Smart Card signing CA certificates #------------------------------ function checkRhttpproxyCACCerts() { CAC_FILTER_FILE=$(getCACFilterFile) if [ -n "$CAC_FILTER_FILE" ]; then header 'Check Smart Card Issuing CA Filter File' task 'Check CA Filter File' if [[ "$VC_VERSION" =~ ^7 ]] && [[ $VC_BUILD -ge 20845200 ]]; then if [ "$CAC_FILTER_FILE" == '/usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem' ]; then if [ -s $CAC_FILTER_FILE ]; then statusMessage 'OK' 'GREEN' else statusMessage 'MISSING' 'YELLOW' CERT_STATUS_CLIENT_CA_LIST_FILE_MISSING=1 fi else statusMessage 'PROBLEM' 'YELLOW' CERT_STATUS_CLIENT_CA_LIST_FILE_LOCATION=1 fi else if [ -s $CAC_FILTER_FILE ]; then statusMessage 'OK' 'GREEN' else statusMessage 'MISSING' 'YELLOW' CERT_STATUS_CLIENT_CA_LIST_FILE_MISSING=1 fi fi if [ -s $CAC_FILTER_FILE ]; then rm $STAGE_DIR/rhttpproxy-ca-* 2>&1 | logDebug csplit -s -z -f $STAGE_DIR/rhttpproxy-ca- -b %02d.crt $CAC_FILTER_FILE '/-----BEGIN CERTIFICATE-----/' '{*}' i=1 for cert in $STAGE_DIR/rhttpproxy-ca-*; do [[ -e "$cert" ]] || break task "Certificate $i" checkFilesystemCert "$cert" ((++i)) done rm $STAGE_DIR/rhttpproxy-ca-* 2>&1 | logDebug fi fi } #------------------------------ # View Reverse Proxy Smart Card signing CA certificates #------------------------------ function viewRhttpproxyCACCerts() { REVERSE_PROXY_CAC_CERT_THUMBPRINTS=() CAC_FILTER_FILE=$(getCACFilterFile) header 'Smart Card Issuing CA Filter File' logInfo "Smart Card filter file: $CAC_FILTER_FILE" if [ -n "$CAC_FILTER_FILE" ] && [ -s "$CAC_FILTER_FILE" ]; then rm $STAGE_DIR/rhttpproxy-ca-* 2>&1 | logDebug csplit -s -z -f $STAGE_DIR/rhttpproxy-ca- -b %02d.crt $CAC_FILTER_FILE '/-----BEGIN CERTIFICATE-----/' '{*}' i=1 for cert in $STAGE_DIR/rhttpproxy-ca-*; do [[ -e "$cert" ]] || break TEMP_CERT=$(cat "$cert") CERT_OUTPUT=$(viewBriefCertificateInfo "$TEMP_CERT") REVERSE_PROXY_CAC_CERT_THUMBPRINTS+=($(openssl x509 -noout -fingerprint -sha1 -in $cert 2>>/dev/null | awk -F'=' '{print $NF}')) printf "%2s. %s\n\n" $i "$CERT_OUTPUT" ((++i)) done rm $STAGE_DIR/rhttpproxy-ca-* 2>&1 | logDebug else echo "${YELLOW}No Smart Card CA filter file found or it is empty.$NORMAL" fi } #------------------------------ # Check VMware Directory Smart Card signing CA certificates #------------------------------ function checkVMDirCACCerts() { CAC_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=ClientCertAuthnTrustedCAs,cn=Default,cn=ClientCertificatePolicies,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,${VMDIR_DOMAIN_DN}" -D "cn=${VMDIR_USER},cn=users,${VMDIR_DOMAIN_DN}" -y $STAGE_DIR/.vmdir-user-password '(objectclass=*)' userCertificate 2>/dev/null | grep -v '^dn:' | sed -e 's/userCertificate:: //g') if [ -n "$CAC_CERTS" ]; then header 'Check VMDir Smart Card signing CA certificates' i=1 for hash in $CAC_CERTS; do TEMP_CERT=$(buildCertFromHash "$hash") task "Certificate $i" checkCert "$TEMP_CERT" ((++i)) done fi } #------------------------------ # View VMware Directory Smart Card signing CA certificates #------------------------------ function viewVMDirCACCerts() { CAC_CERT_LIST=() header 'Smart Card Issuing CA Certificates' CAC_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=ClientCertAuthnTrustedCAs,cn=Default,cn=ClientCertificatePolicies,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,${VMDIR_DOMAIN_DN}" -D "cn=${VMDIR_USER},cn=users,${VMDIR_DOMAIN_DN}" -y $STAGE_DIR/.vmdir-user-password '(objectclass=*)' userCertificate 2>/dev/null | grep -v '^dn:' | sed -e 's/userCertificate:: //g') i=1 if [ -n "$CAC_CERTS" ]; then for hash in $CAC_CERTS; do TEMP_CERT=$(buildCertFromHash "$hash") CAC_CERT_LIST+=("$TEMP_CERT") CERT_OUTPUT=$(viewBriefCertificateInfo "$TEMP_CERT") printf "%2s. %s\n\n" $i "$CERT_OUTPUT" ((++i)) done else echo "${YELLOW}No Smart Card issuing CA certificates found in VMware Directory.$NORMAL" fi } #------------------------------ # Add Smart Card (CAC) issuing certificates to reverse proxy filter file #------------------------------ function addCACCertsFilterFile() { read -e -p $'\nEnter path to new Smart Card issuing certificate(s): ' NEW_CAC_CERTS_INPUT while [ ! -f $NEW_CAC_CERTS_INPUT ]; do read -s -p $'\n'"${YELLOW}File not found, enter path to new Smart Card issuing certificate(s):${NORMAL} " NEW_CAC_CERTS_INPUT; done rm $STAGE_DIR/new-cac-cert-* 2>&1 | logDebug csplit -s -z -f $STAGE_DIR/new-cac-cert- -b %02d.crt $NEW_CAC_CERTS_INPUT '/-----BEGIN CERTIFICATE-----/' '{*}' header 'Adding New Smart Card Issuing Certificates' CAC_FILTER_FILE=$(getCACFilterFile) for cert in $STAGE_DIR/new-cac-cert-*; do [[ -e "$cert" ]] || break if ! isExpired "$cert" 'file'; then task "Adding cert $(openssl x509 -noout -hash -in $cert 2>&1) to reverse proxy file" cat $cert >> $CAC_FILTER_FILE statusMessage 'OK' 'GREEN' fi done sed -i '/^$/d' $CAC_FILTER_FILE } #------------------------------ # Remove Smart Card (CAC) issuing certificates from reverse proxy filter file #------------------------------ function removeCACCertsFilterFile() { read -p $'\nEnter number of Smart Card issuing certificate(s) to remove (comma-separated list): ' CAC_CERT_REMOVE_INPUT if [ -n "$CAC_CERT_REMOVE_INPUT" ]; then header 'Removing Smart Card Issuing Certificates' HASHES_TO_REMOVE=() for index in $(echo $CAC_CERT_REMOVE_INPUT | tr -d ' ' | sed 's/,/ /g'); do HASHES_TO_REMOVE+=(" ${REVERSE_PROXY_CAC_CERT_THUMBPRINTS[$((index - 1))]}") done CAC_FILTER_FILE=$(getCACFilterFile) logInfo "Hashes to remove from $CAC_FILTER_FILE: ${HASHES_TO_REMOVE[*]}" if [ -f $CAC_FILTER_FILE ]; then if [ -f $STAGE_DIR/new-cac-certs.pem ]; then rm $STAGE_DIR/new-cac-certs.pem; fi csplit -s -z -f $STAGE_DIR/rhttpproxy-ca- -b %02d.crt $CAC_FILTER_FILE '/-----BEGIN CERTIFICATE-----/' '{*}' for cert in $STAGE_DIR/rhttpproxy-ca-*; do [[ -e "$cert" ]] || break CERT_THUMBPRINT=$(openssl x509 -noout -fingerprint -sha1 -in $cert 2>/dev/null | awk -F'=' '{print $NF}') if [[ ! " ${HASHES_TO_REMOVE[*]} " =~ " $CERT_THUMBPRINT" ]]; then cat $cert >> $STAGE_DIR/new-cac-certs.pem fi done task 'Updating reverse proxy filter file' cp $STAGE_DIR/new-cac-certs.pem $CAC_FILTER_FILE > /dev/null 2>&1 || errorMessage 'Unable to update reverse proxy filter file' statusMessage 'OK' 'GREEN' else errorMessage 'Unable to determine reverse proxy filter file' fi fi rm $STAGE_DIR/rhttpproxy-ca-* 2>&1 | logDebug } #------------------------------ # Configure Smart Card (CAC) authentication #------------------------------ function configureCACAuthentication() { read -e -p $'\nEnter path to Smart Card issuing CA certificate(s): ' CAC_CA_FILE_INPUT while [ ! -f "$CAC_CA_FILE_INPUT" ]; do read -e -p 'File not found, please provide path to the Smart Card issuing CA certificate(s) certificate: ' CAC_CA_FILE_INPUT; done header 'Configure Smart Card authentication' task 'Verify CA certificates' csplit -s -z -f $STAGE_DIR/cac-ca- -b %02d.crt "$CAC_CA_FILE_INPUT" '/-----BEGIN CERTIFICATE-----/' '{*}' for cert in $STAGE_DIR/cac-ca-*; do [[ -e "$cert" ]] || break if isCertCA "$(cat $cert)"; then if ! isExpired "$cert" 'file'; then cat "$cert" >> $STAGE_DIR/cac-certs.pem fi fi done statusMessage 'OK' 'GREEN' if [ ! -s $STAGE_DIR/cac-certs.pem ]; then errorMessage "No valid CA certificates found in $CAC_CA_FILE_INPUT" else task 'Backup reverse proxy config' cp /etc/vmware-rhttpproxy/config.xml /etc/vmware-rhttpproxy/config.xml.backup 2>/dev/null || errorMessage 'Unable to backup /etc/vmware-rhttpproxy/config.xml' statusMessage 'OK' 'GREEN' cp $STAGE_DIR/cac-certs.pem /usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem task 'Configure reverse proxy' CAC_FILTER_FILE=$(grep '' /etc/vmware-rhttpproxy/config.xml | awk -F'>' '{print $2}' | awk -F'<' '{print $1}') sed -i -e "s|$CAC_FILTER_FILE|/usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem|" -e 's|||' -e 's|||' -e '//i true' /etc/vmware-rhttpproxy/config.xml > /dev/null 2>&1 || errorMessage 'Unable to update reverse proxy configuration' statusMessage 'OK' 'GREEN' updateSSOCACConfig 'add' '/usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem' fi } #------------------------------ # Export SSO Smart Card CA certificates #------------------------------ function exportSSOCACCerts() { SSO_CAC_CA_CERTS=$(ldapsearch -LLL -h localhost -b "cn=DefaultClientCertCAStore,cn=ClientCertAuthnTrustedCAs,cn=Default,cn=ClientCertificatePolicies,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password userCertificate 2>/dev/null | sed -e 's/^ //g' | tr -d '\n' | sed -e 's/dn:/\n&/g' -e 's/userCertificate:/\n&/g' | grep '^userCertificate' | awk '{print $NF}') logInfo 'Exporting Smart Card CA certificates from VMware Directory' SSO_CAC_CA_CERT_FILES=() if [ -n "$SSO_CAC_CA_CERTS" ]; then i=$1 for hash in $SSO_CAC_CA_CERTS; do CERT_PRESENT=0 TEMP_CERT=$(buildCertFromHash "$hash") TEMP_CERT_THUMBPRINT=$(echo "$TEMP_CERT" | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}') logInfo "Checking SSO Smart Card CA certificate with thumbprint $TEMP_CERT_THUMBPRINT" for cert in $STAGE_DIR/sso-cac-ca-cert-*.crt; do [[ -e "$cert" ]] || break CURRENT_CERT_THUMBPRINT=$(openssl x509 -noout -fingerprint -sha1 -in $cert 2>/dev/null | awk -F'=' '{print $NF}') logDebug "Checking SSO Smart Card CA certificate thumbprint ($TEMP_CERT_THUMBPRINT) against new certificate thumbprint ($CURRENT_CERT_THUMBPRINT)" if [ "$CURRENT_CERT_THUMBPRINT" = "$TEMP_CERT_THUMBPRINT" ]; then CERT_PRESENT=1; fi done if [ $CERT_PRESENT -eq 0 ]; then echo "$TEMP_CERT" > $STAGE_DIR/sso-cac-ca-cert-$i.crt SSO_CAC_CA_CERT_FILES+=("$STAGE_DIR/sso-cac-ca-cert-$i.crt") logInfo "Saving SSO Smart Card CA certificate to $STAGE_DIR/sso-cac-ca-cert-$i.crt" fi ((++i)) done fi } #------------------------------ # Export SSO LDAPS CA certificates #------------------------------ function exportSSOLDAPSCerts() { logInfo "Exporting LDAPS certificates with Identity Source type: $1" case $1 in 'Microsoft ADFS') LDAPS_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' userCertificate 2>/dev/null | sed -e 's/^ //g' | grep '^userCertificate:' | awk '{print $NF}') ;; *) LDAPS_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$2,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(|(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP))' userCertificate 2>/dev/null | sed -e 's/^ //g' | grep '^userCertificate:' | awk '{print $NF}') ;; esac for hash in $LDAPS_CERTS; do TEMP_CERT=$(buildCertFromHash "$hash") TEMP_CERT_SUBJECT_HASH=$(echo "$TEMP_CERT" | openssl x509 -noout -hash) echo "$TEMP_CERT" > $STAGE_DIR/${3}$TEMP_CERT_SUBJECT_HASH.crt done } #------------------------------ # Update SSO with Smart Card configuration #------------------------------ function updateSSOCACConfig() { rm $STAGE_DIR/sso-cac-ca-cert-* > /dev/null 2>&1 CAC_CA_CERTS=() case $1 in 'add') if [ -n "$2" ]; then CA_FILE=$2 else read -e -p $'\nEnter path to smart card CA certificate file: ' CA_FILE while [ ! -f $CA_FILE ]; do read -e -p "${YELLOW}File not found, enter path to smart card CA certificate file:${NORMAL} " CA_FILE; done fi header 'Adding Smart Card CA certificates to VMware Directory' csplit -z -s -f $STAGE_DIR/sso-cac-ca-cert- -b %01d.crt $CA_FILE '/-----BEGIN CERTIFICATE-----/' '{*}' CAC_CA_CERT_COUNT=$(ls $STAGE_DIR/sso-cac-ca-cert-* | wc -l) exportSSOCACCerts "$CAC_CA_CERT_COUNT" for cert in $STAGE_DIR/sso-cac-ca-cert-*; do [[ -e "$cert" ]] || break CERT_HASH=$(openssl x509 -noout -hash -in $cert 2>&1 | logDebug) task "Adding certificate $CERT_HASH" CAC_CA_CERTS+=("$cert") statusMessage 'OK' 'GREEN' done ;; 'remove') exportSSOCACCerts '0' read -p $'\nEnter the number(s) of the certificate(s) to delete (multiple entries separated by a comma): ' CAC_CAS_TO_REMOVE if [ -n "$CAC_CAS_TO_REMOVE" ]; then logInfo "User has selected the following certificate(s) to delete: $CAC_CAS_TO_REMOVE" header 'Removing Smart Card CA certificates from VMware Directory' for index in $(echo $CAC_CAS_TO_REMOVE | tr -d ' ' | sed 's/,/ /g'); do if [[ $index =~ ^[0-9]+$ ]]; then CERT_TO_REMOVE=${CAC_CERT_LIST[$((index - 1))]} THUMBPRINT_TO_REMOVE=$(echo "$CERT_TO_REMOVE" | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}') SUBJECT_HASH_TO_REMOVE=$(echo "$CERT_TO_REMOVE" | openssl x509 -noout -hash) for cert in $STAGE_DIR/sso-cac-ca-cert-*; do [[ -e "$cert" ]] || break CURRENT_CERT_THUMBPRINT=$(openssl x509 -noout -fingerprint -sha1 -in $cert 2>/dev/null | awk -F'=' '{print $NF}') if [ "$THUMBPRINT_TO_REMOVE" = "$CURRENT_CERT_THUMBPRINT" ]; then task "Removing certificate $SUBJECT_HASH_TO_REMOVE" rm $cert > /dev/null 2>&1 || errorMessage "Unable to remove certificate $SUBJECT_HASH_TO_REMOVE" statusMessage 'OK' 'GREEN' fi done fi done fi for cert in $STAGE_DIR/sso-cac-ca-cert-*; do [[ -e "$cert" ]] || break CAC_CA_CERTS+=("$cert") done ;; esac SSO_CAC_CERTS=$(printf -v joined '%s,' "${CAC_CA_CERTS[@]}"; echo "${joined%,}") logInfo "Updating SSO configuration with '$SSO_CAC_CERTS'" task 'Update SSO configuration' sso-config.sh -set_authn_policy -certAuthn true -cacerts "$SSO_CAC_CERTS" -t "$SSO_DOMAIN" > /dev/null 2>&1 || errorMessage "Unable to configure SSO for Smart Card authentication" statusMessage 'OK' 'GREEN' } #------------------------------ # Manage AD over LDAP certificates #------------------------------ function manageLDAPSCerts() { if configuredForADoverLDAPS; then case $1 in 'Check') checkLDAPSCerts ;; 'View') viewLDAPSCerts read -p $'\nSelect certificate [Return to Main Menu]: ' VIEW_LDAPS_CERT_INPUT if [ -n "$VIEW_LDAPS_CERT_INPUT" ] && [[ $VIEW_LDAPS_CERT_INPUT -le $LDAPS_CERT_COUNTER ]]; then LDAP_CERT_HASH=${LDAPS_CERT_HASHES[$((VIEW_LDAPS_CERT_INPUT - 1))]} TEMP_CERT=$(buildCertFromHash "$LDAP_CERT_HASH") viewCertificateInfo "$TEMP_CERT" 'view-path' fi ;; 'Manage') selectLDAPSDomain viewIdentitySourceLDAPSCerts ;; esac fi } #------------------------------ # List LDAPS domains #------------------------------ function selectLDAPSDomain() { LDAPS_DOMAINS=() SELECTED_LDAPS_DOMAIN='' AD_OVER_LDAPS_DOMAINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' -s one cn 2>/dev/null | sed -e 's/^ //g' | grep '^cn:' | awk '{print $NF}') OPENLDAP_DOMAINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP)' -s one cn 2>/dev/null | sed -e 's/^ //g' | grep '^cn:' | awk '{print $NF}') ADFS_ISSUER=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSExternalIdp)' -s one vmwSTSIssuerName 2>/dev/null | grep '^vmwSTSIssuerName:' | awk '{print $NF}') for domain in $OPENLDAP_DOMAINS; do LDAPS_DOMAINS+=("$domain|OpenLDAP") done for domain in $AD_OVER_LDAPS_DOMAINS; do LDAPS_DOMAINS+=("$domain|AD over LDAPS") done if [[ "$VC_VERSION" =~ ^[78] ]]; then if [ -n "$ADFS_ISSUER" ]; then LDAPS_DOMAINS+=("$ADFS_ISSUER|Microsoft ADFS") fi fi if [ ${#LDAPS_DOMAINS[@]} -eq 0 ]; then echo "${YELLOW}There are no Identity Sources configured that can utilize an LDAPS connection.${NORMAL}" else header 'Select Domain to Manage LDAPS Certificates' i=1 for entry in "${LDAPS_DOMAINS[@]}"; do domain=$(echo "$entry" | awk -F '|' '{print $1}') source_type=$(echo "$entry" | awk -F '|' '{print $2}') printf "%2s. %s (%s)\n" $i $domain "$source_type" ((++i)) done read -p $'\nSelect domain [1]: ' SELECTED_LDAPS_DOMAIN_INPUT if [ -z $SELECTED_LDAPS_DOMAIN_INPUT ]; then SELECTED_LDAPS_DOMAIN_INPUT=1; fi SELECTED_LDAPS_DOMAIN=${LDAPS_DOMAINS[(($SELECTED_LDAPS_DOMAIN_INPUT - 1))]} fi } #------------------------------ # Check LDAPS certificates #------------------------------ function checkLDAPSCerts() { AD_OVER_LDAPS_DOMAINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' -s one cn 2>/dev/null | sed -e 's/^ //g' | grep '^cn:' | awk '{print $NF}') OPENLDAP_DOMAINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP)' -s one cn 2>/dev/null | sed -e 's/^ //g' | grep '^cn:' | awk '{print $NF}') if [ -n "$OPENLDAP_DOMAINS" ]; then header 'Check OpenLDAP LDAPS certificates' for domain in $OPENLDAP_DOMAINS; do echo "Domain: $domain" LDAP_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$domain,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP)' userCertificate 2>/dev/null | sed -e 's/^ //g' | grep '^userCertificate:' | awk '{print $NF}') i=1 for hash in $LDAP_CERTS; do TEMP_CERT=$(buildCertFromHash "$hash") task " Certificate $i" checkCert "$TEMP_CERT" ((++i)) done done fi if [ -n "$AD_OVER_LDAPS_DOMAINS" ]; then header 'Check AD over LDAPS certificates' for domain in $AD_OVER_LDAPS_DOMAINS; do echo "Domain: $domain" LDAP_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$domain,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' userCertificate 2>/dev/null | sed -e 's/^ //g' | grep '^userCertificate:' | awk '{print $NF}') i=1 for hash in $LDAP_CERTS; do TEMP_CERT=$(buildCertFromHash "$hash") task " Certificate $i" checkCert "$TEMP_CERT" ((++i)) done done fi if [[ "$VC_VERSION" =~ ^[78] ]]; then ADFS_LDAPS_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' userCertificate 2>/dev/null | grep '^userCertificate' | awk '{print $NF}') if [ -n "$ADFS_LDAPS_CERTS" ]; then header 'Check ADFS LDAPS certificates' i=1 for hash in $ADFS_LDAPS_CERTS; do TEMP_CERT=$(buildCertFromHash "$hash") task "Certificate $i" checkCert "$TEMP_CERT" ((++i)) done fi fi } #------------------------------ # View Identity Source LDAPS certificates #------------------------------ function viewIdentitySourceLDAPSCerts() { if [ -n "$SELECTED_LDAPS_DOMAIN" ]; then LDAPS_CERT_THUMBPRINT_LIST=() LDAPS_CERT_HASHES=() LDAPS_CERT_COUNTER=1 domain=$(echo "$SELECTED_LDAPS_DOMAIN" | awk -F '|' '{print $1}') source_type=$(echo "$SELECTED_LDAPS_DOMAIN" | awk -F '|' '{print $2}') case $source_type in 'Microsoft ADFS') LDAP_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' userCertificate 2>/dev/null | grep '^userCertificate' | awk -F':' '{print $NF}') header='Manage Certificates for External Provider: Microsoft ADFS' ;; *) LDAP_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$domain,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' userCertificate 2>/dev/null | sed -e 's/^ //g' | grep '^userCertificate:' | awk '{print $NF}') header="Manage Certificates for Identity Provider: Domain $domain" ;; esac header 'Currently Configured Certificatse for LDAPS Connection' viewLDAPSCertInfo "$LDAP_CERTS" "$domain" "$source_type" header "$header" cat << EOF 1. Add LDAP server certificate(s) 2. Remove LDAP server certificate(s) EOF read -p $'\nEnter selection [Return to Main Menu]: ' MANAGE_LDAPS_INPUT case $MANAGE_LDAPS_INPUT in 1) addLDAPSCerts ;; 2) removeLDAPSCerts ;; esac fi } #------------------------------ # View LDAPS certificates #------------------------------ function viewLDAPSCerts() { LDAPS_CERT_THUMBPRINT_LIST=() LDAPS_DOMAINS=() LDAPS_CERT_HASHES=() LDAPS_CERT_COUNTER=1 LDAPS_CERT_DNS=() AD_OVER_LDAPS_DOMAINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' -s one cn 2>/dev/null | sed -e 's/^ //g' | grep '^cn:' | awk '{print $NF}') OPENLDAP_DOMAINS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP)' -s one cn 2>/dev/null | sed -e 's/^ //g' | grep '^cn:' | awk '{print $NF}') ADFS_ISSUER=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSExternalIdp)' -s one vmwSTSIssuerName 2>/dev/null | grep '^vmwSTSIssuerName:' | awk '{print $NF}') header 'LDAPS Certificates' if [ -n "$OPENLDAP_DOMAINS" ]; then for domain in $OPENLDAP_DOMAINS; do LDAPS_DOMAINS+=("$domain|OpenLDAP") LDAP_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$domain,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP)' userCertificate 2>/dev/null | sed -e 's/^ //g' | grep '^userCertificate:' | awk '{print $NF}') viewLDAPSCertInfo "$LDAP_CERTS" "$domain" 'OpenLDAP' done fi if [ -n "$AD_OVER_LDAPS_DOMAINS" ]; then for domain in $AD_OVER_LDAPS_DOMAINS; do LDAPS_DOMAINS+=("$domain|AD over LDAPS") LDAP_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$domain,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' userCertificate 2>/dev/null | sed -e 's/^ //g' | grep '^userCertificate:' | awk '{print $NF}') viewLDAPSCertInfo "$LDAP_CERTS" "$domain" 'AD over LDAPS' done fi if [[ "$VC_VERSION" =~ ^[78] ]]; then ADFS_LDAPS_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' userCertificate 2>/dev/null | grep '^userCertificate' | awk -F':' '{print $NF}') if [ -n "$ADFS_LDAPS_CERTS" ]; then LDAP_CERTS=$ADFS_LDAPS_CERTS LDAPS_DOMAINS+=("$ADFS_ISSUER|Microsoft ADFS") viewLDAPSCertInfo "$LDAP_CERTS" "$ADFS_ISSUER" 'Microsoft ADFS' fi fi } #------------------------------ # View AD over LDAPS certificate info #------------------------------ function viewLDAPSCertInfo() { for hash in $1; do TEMP_CERT=$(buildCertFromHash "$hash") LDAPS_CERT_THUMBPRINT_LIST+=($(echo "$TEMP_CERT" | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}')) LDAPS_CERT_HASHES+=($hash) CERT_OUTPUT=$(viewBriefCertificateInfo "$TEMP_CERT") if [[ "$2" =~ ^http ]]; then CERT_OUTPUT+=$'\n'" External Issuer: $2" else CERT_OUTPUT+=$'\n'" Domain: $2" fi CERT_OUTPUT+=$'\n'" Identity Source Type: $3" printf "%2s. %s\n\n" $LDAPS_CERT_COUNTER "$CERT_OUTPUT" case $3 in 'AD over LDAPS') LDAPS_CERT_DNS+=("cn=$2,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN") ;; *) ADFS_LDAP_PROVIDER_DN=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSIdentityStore)' dn 2>>$LOG | awk '{print $NF}') LDAPS_CERT_DNS+=("$ADFS_LDAP_PROVIDER_DN") ;; esac ((++LDAPS_CERT_COUNTER)) done } #------------------------------ # Add LDAPS certificates #------------------------------ function addLDAPSCerts() { domain=$(echo "$SELECTED_LDAPS_DOMAIN" | awk -F '|' '{print $1}') source_type=$(echo "$SELECTED_LDAPS_DOMAIN" | awk -F '|' '{print $2}') case $source_type in 'Microsoft ADFS') file_prefix='ldaps-adfs-cert-' ;; *) file_prefix="ldaps-$domain-cer-" ;; esac read -e -p $'\nEnter path to new LDAP server certificate(s): ' NEW_LDAPS_CERTS_INPUT while [ ! -f "$NEW_LDAPS_CERTS_INPUT" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to new LDAP server certificate(s):${NORMAL} " NEW_LDAPS_CERTS_INPUT; done if [ -f "$NEW_LDAPS_CERTS_INPUT" ]; then csplit -s -z -f $STAGE_DIR/$file_prefix -b %02d.crt "$NEW_LDAPS_CERTS_INPUT" '/-----BEGIN CERTIFICATE-----/' '{*}' exportSSOLDAPSCerts "$source_type" "$domain" "$file_prefix" header 'Publish new LDAP server ceritifcates' updateLDAPSCerts "$file_prefix" fi } #------------------------------ # Remove LDAPS certificates #------------------------------ function removeLDAPSCerts() { domain=$(echo "$SELECTED_LDAPS_DOMAIN" | awk -F '|' '{print $1}') source_type=$(echo "$SELECTED_LDAPS_DOMAIN" | awk -F '|' '{print $2}') case $source_type in 'Microsoft ADFS') file_prefix='ldaps-adfs-cert-' ;; *) file_prefix="ldaps-$domain-cer-" ;; esac read -p $'\nEnter the number(s) of the LDAP server certificate(s) to delete (multiple entries separated by a comma): ' REMOVE_LDAP_CERTS_INPUT if [ -n "$REMOVE_LDAP_CERTS_INPUT" ]; then logInfo "User has selected the following certificate(s) to delete: $REMOVE_LDAP_CERTS_INPUT" exportSSOLDAPSCerts "$source_type" "$domain" "$file_prefix" for index in $(echo "$REMOVE_LDAP_CERTS_INPUT" | tr -d ' ' | sed 's/,/ /g'); do if [[ $index =~ ^[0-9]+$ ]]; then to_delete_thumbprint=${LDAPS_CERT_THUMBPRINT_LIST[$((index - 1))]} logInfo "Removing LDAPS certificate with thumbprint $to_delete_thumbprint" for cert in $STAGE_DIR/$file_prefix*; do [[ -e "$cert" ]] || break current_thumbprint=$(openssl x509 -noout -fingerprint -sha1 -in $cert 2>/dev/null | awk -F'=' '{print $NF}') if [ "$current_thumbprint" = "$to_delete_thumbprint" ]; then logInfo "Removing $cert" rm $cert 2>&1 | logDebug fi done fi done header 'Publish new LDAP server ceritifcates' updateLDAPSCerts "$file_prefix" fi } #------------------------------ # Remove LDAP certificates #------------------------------ function updateLDAPSCerts() { domain=$(echo "$SELECTED_LDAPS_DOMAIN" | awk -F '|' '{print $1}') source_type=$(echo "$SELECTED_LDAPS_DOMAIN" | awk -F '|' '{print $2}') case $source_type in 'Microsoft ADFS') LDAPS_UPDATE_DN=$($LDAP_SEARCH -LLL -o ldif-wrap=no -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSIdentityStore)' dn | awk '{print $NF}') ;; *) LDAPS_UPDATE_DN="cn=$domain,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" ;; esac echo "dn: $LDAPS_UPDATE_DN" > $STAGE_DIR/$1.ldif echo 'changetype: modify' >> $STAGE_DIR/$1.ldif echo 'replace: userCertificate' >> $STAGE_DIR/$1.ldif for cert in $STAGE_DIR/$1*.crt; do [[ -e "$cert" ]] || break task "Staging certificate $(openssl x509 -noout -hash -in $cert 2>&1 | logDebug)" CERT_BINARY_FILE=$(echo "$cert" | sed -e 's/.crt/.der/') if openssl x509 -inform pem -outform der -in $cert -out $CERT_BINARY_FILE > /dev/null 2>&1; then echo "userCertificate:< file://$CERT_BINARY_FILE" >> $STAGE_DIR/$1.ldif statusMessage 'OK' 'GREEN' else statusMessage 'ERROR' 'YELLOW' fi done task 'Update LDAPS certificates' $LDAP_MODIFY -x -h $PSC_LOCATION -p $VMDIR_PORT -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password -f $STAGE_DIR/$1.ldif > /dev/null 2>&1 || errorMessage 'Unable to update LDAPS server certificates' statusMessage 'OK' 'GREEN' rm $STAGE_DIR/ldaps-$1-cert* 2>&1 | logDebug } #------------------------------ # Manage Tanzu Supervisor Cluster certificates #------------------------------ function manageTanzuSupervisorClusterCerts() { if tanzuSupervisorClustersPresent; then case $1 in 'Check') checkTanzuSupervisorCluseterCerts ;; 'View') viewTanzuSupervisorCluseterCerts ;; 'Manage') ;; esac fi } #------------------------------ # Check certificates in the Tanzu Supervisor Clusters #------------------------------ function checkTanzuSupervisorCluseterCerts() { header 'Checking Tanzu Supervisor Cluster Certificates' IFS=$'\n' for line in $(/usr/lib/vmware-wcp/decryptK8Pwd.py | grep -E '^Cluster: |^IP: |^PWD: '); do if [[ "$line" =~ ^Cluster ]]; then TANZU_CLUSTER_ID=$(echo "$line" | awk '{print $NF}' | awk -F':' '{print $1}' | sed -e 's/domain-c//') TANZU_CLUSTER=$(/opt/vmware/vpostgres/current/bin/psql -d VCDB -U postgres -c "SELECT e.name FROM vpx_entity AS e LEFT JOIN vpx_object_type AS ot ON e.type_id = ot.id WHERE ot.name='CLUSTER_COMPUTE_RESOURCE' AND e.id=$TANZU_CLUSTER_ID" -t | sed -e 's/^[[:space:]]*//g' | grep -v '^$') fi if [[ "$line" =~ ^IP ]]; then TANZU_CLUSTER_IP=$(echo "$line" | awk '{print $NF}') fi if [[ "$line" =~ ^PWD ]]; then TANZU_CLUSTER_PASSWD=$(echo "$line" | awk '{print $NF}') echo "Cluster: $TANZU_CLUSTER" logInfo "Checking cluster: $TANZU_CLUSTER" ssh-keygen -R $TANZU_CLUSTER_IP 2>&1 | logDebug sshpass -p "$TANZU_CLUSTER_PASSWD" ssh -q -o StrictHostKeyChecking=no -t -t root@$TANZU_CLUSTER_IP 'for cert in $(find / -type f \( -name "*.cert" -o -name "*.crt" \) -print 2>/dev/null | egrep -v "ca.crt$|ca-bundle.crt$|kubelet\/pods|var\/lib\/containerd|run\/containerd|bootstrapper"); do printf "%-52s" " $cert"; if openssl x509 -noout -in $cert -checkend 0 > /dev/null 2>&1; then printf "%13s\n" "VALID"; else printf "%13s\n" "EXPIRED"; fi; done' | sed -e "s/VALID/${GREEN}&${NORMAL}/g" -e "s/EXPIRED/${YELLOW}&${NORMAL}/g" fi done IFS=$' \t\n' } #------------------------------ # View info on Tanzu Supervisor Cluster certificates #------------------------------ function viewTanzuSupervisorCluseterCerts() { header 'View Tanzu Supervisor Cluster Certificates' TANZU_CLUSTERS=() TANZU_CLUSTER_IDS=() i=1 echo '' for tanzu_cluster_id in $(/usr/lib/vmware-wcp/decryptK8Pwd.py | grep '^Cluster: ' | awk '{print $NF}' | awk -F':' '{print $1}' | sed -e 's/domain-c//'); do TANZU_CLUSTER=$(/opt/vmware/vpostgres/current/bin/psql -d VCDB -U postgres -c "SELECT e.name FROM vpx_entity AS e LEFT JOIN vpx_object_type AS ot ON e.type_id = ot.id WHERE ot.name='CLUSTER_COMPUTE_RESOURCE' AND e.id=$tanzu_cluster_id" -t | sed -e 's/^[[:space:]]*//g' | grep -v '^$') if [ -n "$TANZU_CLUSTER" ]; then TANZU_CLUSTERS+=("$TANZU_CLUSTER") TANZU_CLUSTER_IDS+=("$tanzu_cluster_id") printf "%2s. %s\n" $i "$TANZU_CLUSTER" fi done if [ -n "${TANZU_CLUSTERS[*]}" ]; then read -p $'\nSelect Supervisor Cluster [Return to Main Menu]: ' TANZU_CLUSTER_INPUT if [ -n "$TANZU_CLUSTER_INPUT" ]; then TANZU_CLUSTER_NAME=${TANZU_CLUSTERS[$((TANZU_CLUSTER_INPUT - 1))]} TANZU_CLUSTER_ID=${TANZU_CLUSTER_IDS[$((TANZU_CLUSTER_INPUT - 1))]} TANZU_CLUSTER_INFO=$(/usr/lib/vmware-wcp/decryptK8Pwd.py | grep -A2 "domain-c$TANZU_CLUSTER_ID") TANZU_CLUSTER_IP=$(echo "$TANZU_CLUSTER_INFO" | awk '/^IP: /{print $NF}') TANZU_CLUSTER_PASSWD=$(echo "$TANZU_CLUSTER_INFO" | awk '/^PWD: /{print $NF}') header "'$TANZU_CLUSTER_NAME' Certificates" ssh-keygen -R $TANZU_CLUSTER_IP 2>&1 | logDebug sshpass -p "$TANZU_CLUSTER_PASSWD" ssh -q -o StrictHostKeyChecking=no -t -t root@$TANZU_CLUSTER_IP 'for cert in $(find / -type f \( -name "*.cert" -o -name "*.crt" \) -print 2>/dev/null | egrep -v "ca.crt$|ca-bundle.crt$|kubelet\/pods|var\/lib\/containerd|run\/containerd|bootstrapper"); do echo "Cert: $cert"; openssl x509 -noout -in $cert -text; echo ''; done' fi fi } #------------------------------ # Check if STS Tenant Credential certificates have expired #------------------------------ function checkSTSTenantCerts() { CA_SKIDS=$($DIR_CLI trustedcert list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | awk '{print $NF}') TENANT_CREDENTIAL_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password '(objectclass=vmwSTSTenantCredential)' userCertificate) IFS=$'\n' for line in $TENANT_CREDENTIAL_CERTS; do if [[ "$line" =~ ^dn: ]]; then TENANT_CN=$(echo "$line" | awk '{print $NF}' | awk -F',' '{print $1}' | awk -F'=' '{print $NF}') echo "Checking $TENANT_CN:" else hash=$(echo "$line" | awk '{print $NF}') TEMP_CERT=$(buildCertFromHash "$hash") if isCertCA "$TEMP_CERT"; then checkSTSTenantCert "$TEMP_CERT" $TENANT_CN 'CA' "$CA_SKIDS" else checkSTSTenantCert "$TEMP_CERT" $TENANT_CN 'signing' fi fi done IFS=$' \t\n' } #------------------------------ # Check if STS Tenant Credential certificates have expired #------------------------------ function checkSTSTrustedCertChains() { CA_SKIDS=$($DIR_CLI trustedcert list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | awk '{print $NF}') TENANT_TRUSTED_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password '(&(objectclass=vmwSTSTenantTrustedCertificateChain)(cn=TrustedCertChain*))' userCertificate) IFS=$'\n' for line in $TENANT_TRUSTED_CERTS; do if [[ "$line" =~ ^dn: ]]; then CHAIN_CN=$(echo "$line" | awk '{print $NF}' | awk -F',' '{print $1}' | awk -F'=' '{print $NF}') echo "Checking $CHAIN_CN:" else hash=$(echo "$line" | awk '{print $NF}') TEMP_CERT=$(buildCertFromHash "$hash") if isCertCA "$TEMP_CERT"; then checkSTSTenantCert "$TEMP_CERT" $CHAIN_CN 'CA' "$CA_SKIDS" else checkSTSTenantCert "$TEMP_CERT" $CHAIN_CN 'signing' fi fi done IFS=$' \t\n' } #------------------------------ # Check if individual STS Signing certificate has expired #------------------------------ function checkSTSTenantCert() { task " $2 $3 certificate" if ! isExpired "$1" 'hash'; then CERT_SKID=$(echo "$1" | openssl x509 -noout -text | grep -A1 'Subject Key Id' | tail -n1 | tr -d ': ') if ! echo "$4" | grep "$CERT_SKID" > /dev/null && [ "$3" == 'CA' ]; then CERT_STATUS_MISSING_VMDIR=1 statusMessage 'MISSING' 'YELLOW' return 0 else DAYS_LEFT=$(checkCertExpireSoon "$1") if [[ $DAYS_LEFT -gt 0 ]]; then CERT_STATUS_EXPIRES_SOON=1 statusMessage "$DAYS_LEFT DAYS" 'YELLOW' return 0 else HAS_KEY_USAGE=$(checkCertKeyUsage "$1" "STS Tenant $2 $3") if [[ $3 == 'signing' && $HAS_KEY_USAGE -gt 0 ]]; then CERT_STATUS_KEY_USAGE=1 statusMessage 'KEY USAGE' 'YELLOW' return 0 fi statusMessage 'VALID' 'GREEN' return 0 fi fi else CERT_STATUS_EXPIRED=1 statusMessage 'EXPIRED' 'YELLOW' return 1 fi } #------------------------------ # View STS Signing certificates #------------------------------ function viewSTSTenantCerts() { header 'View STS signing certificates' TENANT_CREDENTIAL_ENTRIES=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password '(objectclass=vmwSTSTenantCredential)' userCertificate | grep -v '^$') IFS=$'\n' for line in $TENANT_CREDENTIAL_ENTRIES; do if [[ "$line" =~ ^dn ]]; then echo "$line" | awk '{print $2}' | awk -F ',' '{print $1}' | sed 's/cn=//' else hash=$(echo "$line" | awk '{print $NF}') TEMP_CERT=$(buildCertFromHash "$hash") CERT_INFO=$(viewBriefCertificateInfo "$TEMP_CERT") if isCertCA "$TEMP_CERT"; then CERT_OUTPUT=" Certificate Type: CA Certificate"$'\n ' else CERT_OUTPUT=" Certificate Type: Signing Certificate"$'\n ' fi CERT_OUTPUT+=$CERT_INFO echo "$CERT_OUTPUT"$'\n' fi done IFS=$' \t\n' } #------------------------------ # Check CA certificates in VMDir and VECS #------------------------------ function checkCACertificates() { VMDIR_CERTS=() VECS_CERTS=() CERT_STATUS_CA_DUPLICATES=0 exportTrustedCACerts header 'Checking CA certificates in VMDir [by CN(id)]' for cert in $(ls $TRUSTED_CA_DIR/*.crt); do cert_file=$(echo "${cert##*/}") skid=$(echo "${cert_file%%.*}") SUBJECT_HASH=$(openssl x509 -noout -hash -in $cert 2>/dev/null) task "${skid}" CA_CERT=$(cat $cert) if isExpired "$cert" 'file'; then CERT_STATUS_EXPIRED=1 statusMessage 'EXPIRED' 'YELLOW' elif ! isCertCA "$(cat $cert)"; then CERT_STATUS_NON_CA=1 statusMessage 'NON-CA' 'YELLOW' elif [ ! -z $SUBJECT_HASH ] && [[ "$(ls /etc/ssl/certs/$SUBJECT_HASH.[0-9] 2>/dev/null | wc -l)" -gt 1 ]]; then CERT_STATUS_CA_DUPLICATES=1 statusMessage 'DUPLICATE' 'YELLOW' elif ! checkCACertsPresent "$CA_CERT"; then CERT_STATUS_MISSING_CA=1 statusMessage 'MISSING CA' 'YELLOW' elif checkRogueCA "$CA_CERT"; then CERT_ROGUE_CA=1 statusMessage 'ROGUE' 'YELLOW' else DAYS_LEFT=$(checkCertExpireSoon "$CA_CERT") if ! openssl x509 -noout -text -in $cert 2>/dev/null | grep -q 'Subject Key Identifier:'; then CERT_STATUS_CA_MISSING_SKID=1 statusMessage 'NO SKID' 'YELLOW' elif [[ $DAYS_LEFT -gt 0 ]]; then CERT_STATUS_EXPIRES_SOON=1 statusMessage "$DAYS_LEFT DAYS" 'YELLOW' else statusMessage 'VALID' 'GREEN' fi fi done header 'Checking CA certificates in VECS [by Alias]' IFS=$'\n' for alias in $($VECS_CLI entry list --store TRUSTED_ROOTS 2>>$LOG | grep '^Alias' | awk '{print $NF}'); do logInfo "Checking certificate with alias '$alias'" TEMP_VECS_CERT=$($VECS_CLI entry getcert --store TRUSTED_ROOTS --alias "$alias" 2>>$LOG) SUBJECT_HASH=$(echo "$TEMP_VECS_CERT" | openssl x509 -noout -hash 2>/dev/null) task $alias if isExpired "$TEMP_VECS_CERT" 'hash'; then CERT_STATUS_EXPIRED=1 statusMessage 'EXPIRED' 'YELLOW' elif ! isCertCA "$TEMP_VECS_CERT"; then CERT_STATUS_NON_CA=1 statusMessage 'NON-CA' 'YELLOW' elif [ ! -z $SUBJECT_HASH ] && [[ "$(ls /etc/ssl/certs/$SUBJECT_HASH.[0-9] 2>/dev/null | wc -l)" -gt 1 ]]; then CERT_STATUS_CA_DUPLICATES=1 statusMessage 'DUPLICATE' 'YELLOW' elif [ $(echo "$TEMP_VECS_CERT" | openssl x509 -fingerprint -sha1 -noout 2>/dev/null | cut -d '=' -f 2 | tr -d ':' | awk '{print tolower($0)}') != "$alias" ]; then CERT_STATUS_BAD_ALIAS=1 statusMessage 'BAD ALIAS' 'YELLOW' elif ! checkCACertsPresent "$TEMP_VECS_CERT"; then CERT_STATUS_MISSING_CA=1 statusMessage 'MISSING CA' 'YELLOW' elif checkRogueCA "$TEMP_VECS_CERT"; then CERT_ROGUE_CA=1 statusMessage 'ROGUE' 'YELLOW' else DAYS_LEFT=$(checkCertExpireSoon "$TEMP_VECS_CERT") if ! echo "$TEMP_VECS_CERT" | openssl x509 -noout -text 2>/dev/null | grep -q 'Subject Key Identifier:'; then CERT_STATUS_CA_MISSING_SKID=1 statusMessage 'NO SKID' 'YELLOW' elif [[ $DAYS_LEFT -gt 0 ]]; then CERT_STATUS_EXPIRES_SOON=1 statusMessage "$DAYS_LEFT DAYS" 'YELLOW' else statusMessage 'VALID' 'GREEN' fi fi done IFS=$' \t\n' } #------------------------------ # Ensure all VMCA certificates have been published to VMware Directory #------------------------------ function checkVMCACertsSSO() { CERT_STATUS_VMCA_MISSING=0 header "Check for VMCA certificates in SSO" CA_SKIDS=$($DIR_CLI trustedcert list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | awk '{print $NF}') CA_ALIASES=$($VECS_CLI entry list --store TRUSTED_ROOTS | grep 'Alias' | awk '{print $NF}') for dc in $($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -b "ou=Domain Controllers,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password cn | grep '^cn' | awk '{print $NF}'); do task "$dc" dc_vmca_hash=$(curl -k https://$dc/afd/vecs/ca --output /dev/stdout 2>/dev/null | openssl x509 -inform der 2>/dev/null) if [ -z "$dc_vmca_hash" ]; then DC_VMCA_RESPONSE=$(curl -k https://$dc/afd/vecs/ca --output $STAGE_DIR/$dc-vmca.crt -s -w "%{http_code}") statusMessage 'UNRETRIEVABLE' 'YELLOW' logInfo "Response code from https://$dc/afd/vecs/ca: $DC_VMCA_RESPONSE" CERT_STATUS_VMCA_UNRETRIEVABLE=1 else dc_vmca_skid=$(echo "$dc_vmca_hash" | openssl x509 -noout -text | grep -A1 'Subject Key Id' | tail -1 | tr -d ' :') dc_vmca_fingerprint=$(echo "$dc_vmca_hash" | openssl x509 -noout -fingerprint -sha1 | awk -F '=' '{print $NF}' | tr -d ' :' | tr [[:upper:]] [[:lower:]]) if [ -z "$dc_vmca_skid" ]; then if echo "$CA_ALIASES" | grep -q $dc_vmca_fingerprint; then statusMessage 'CHECK VMDIR' 'YELLOW' else statusMessage 'MISSING VECS' 'YELLOW' CERT_STATUS_VMCA_MISSING=1 fi else if echo "$CA_SKIDS" | grep -q $dc_vmca_skid; then if echo "$CA_ALIASES" | grep -q $dc_vmca_fingerprint; then statusMessage 'PRESENT' 'GREEN' else statusMessage 'MISSING VECS' 'YELLOW' CERT_STATUS_VMCA_MISSING=1 fi else statusMessage 'MISSING VMDIR' 'YELLOW' CERT_STATUS_VMCA_MISSING=1 fi fi fi done } #------------------------------ # Publish new signing chain to VMDir #------------------------------ function publishCASigningCertificates() { csplit -s -z -f $STAGE_DIR/signing-ca-new- -b %02d.crt $1 '/-----BEGIN CERTIFICATE-----/' '{*}' VMDIR_CA_SKIDS=$($DIR_CLI trustedcert list --login $VMDIR_USER --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | tr -d '\t' | awk -F':' '{print $2}') for cert in $STAGE_DIR/signing-ca-new-*.crt; do [[ -e "$cert" ]] || break CURRENT_SKID=$(openssl x509 -noout -text -in $cert 2>/dev/null | grep -A1 'Subject Key Id' | tail -n1 | tr -d ' ' | sed 's/keyid://' | tr -d ':') if echo "$VMDIR_CA_SKIDS" | grep -q "$CURRENT_SKID"; then $DIR_CLI trustedcert get --id $CURRENT_SKID --login $VMDIR_USER --password "$(cat $STAGE_DIR/.vmdir-user-password)" --outcert $STAGE_DIR/signing-ca-old-$CURRENT_SKID.crt 2>&1 | logInfo $DIR_CLI trustedcert unpublish --login $VMDIR_USER --password "$(cat $STAGE_DIR/.vmdir-user-password)" --cert $STAGE_DIR/signing-ca-old-$CURRENT_SKID.crt 2>&1 | logInfo fi done $DIR_CLI trustedcert publish --chain --cert $TRUSTED_ROOT_CHAIN --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>&1 | logInfo || errorMessage 'Unable to publish trusted root chain to VMDir' statusMessage 'OK' 'GREEN' rm $STAGE_DIR/signing-ca-new-*.crt $STAGE_DIR/signing-ca-old-*.crt 2>&1 | logDebug } #------------------------------ # Check if certificate is a CA cert #------------------------------ function isCertCA() { if echo "$1" | openssl x509 -noout -text 2>/dev/null | grep -A1 'X509v3 Basic Constraints' | grep -q 'CA:TRUE' && echo "$1" | openssl x509 -noout -text 2>/dev/null | grep -A1 'X509v3 Key Usage' | grep -q 'Certificate Sign'; then return 0 else return 1 fi } #------------------------------ # Quick check if Service Principal entries exist in VMware Directory #------------------------------ function quickCheckServicePrincipals() { header 'Checking Service Principals' EXISTING_SERVICE_PRINCIPALS=$($DIR_CLI service list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>/dev/null) if [ -n "$EXISTING_SERVICE_PRINCIPALS" ]; then echo "Node $MACHINE_ID:" for soluser in "${SOLUTION_USERS[@]}"; do task " $soluser" if echo "$EXISTING_SERVICE_PRINCIPALS" | grep "$soluser-$MACHINE_ID" > /dev/null 2>&1; then statusMessage 'PRESENT' 'GREEN' else CERT_STATUS_SERVICE_PRINCIPAL_MISSING=1 statusMessage 'MISSING' 'YELLOW' fi done else task 'Listing SSO Service Principals' errorMessage 'Could not get list of Service Principal entries from VMware Directory' fi } #------------------------------ # Check if Service Principal entries exist in VMware Directory #------------------------------ function checkServicePrincipals() { task 'Verifying Service Principal entries exist' MISSING_SERVICE_PRINCIPALS='' EXISTING_SERVICE_PRINCIPALS=$($DIR_CLI service list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>&1) if [ -n "$EXISTING_SERVICE_PRINCIPALS" ]; then for soluser in "${SOLUTION_USERS[@]}"; do if ! echo "$EXISTING_SERVICE_PRINCIPALS" | grep "$soluser-$MACHINE_ID" > /dev/null 2>&1; then if [ -z "$MISSING_SERVICE_PRINCIPALS" ]; then MISSING_SERVICE_PRINCIPALS+="$soluser-$MACHINE_ID"; else MISSING_SERVICE_PRINCIPALS+=" $soluser-$MACHINE_ID"; fi fi done if [ -n "$MISSING_SERVICE_PRINCIPALS" ]; then statusMessage 'ERROR' 'RED' echo $'\n'"${YELLOW}------------------------!!! Attention !!!------------------------ " echo 'The following Service Principal entries are missing:' for sp in $MISSING_SERVICE_PRINCIPALS; do echo " - $sp" done echo $'\nPlease refer to KB https://knowledge.broadcom.com/external/article?legacyId=80469' echo 'on using the lsdoctor utility to recreate the missing' echo "Solution User/Service Principal entries.${NORMAL}" exit else statusMessage 'OK' 'GREEN' fi else errorMessage 'Could not get list of Service Principal entries from VMware Directory' fi } #------------------------------ # Builds the expanded message detailng issues with certificates #------------------------------ function buildCertificateStatusMessage() { if [ $CERT_STATUS_EXPIRES_SOON == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates are expiring within 30 days\n'; fi if [ $CERT_STATUS_MISSING_PNID == 1 ]; then CERT_STATUS_MESSAGE+=" - One or more certificates are missing the PNID ($PNID) from the SAN entry"$'\n'; fi if [ $CERT_STATUS_KEY_USAGE == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates do not have the recommended\n' CERT_STATUS_MESSAGE+=$' Key Usage values\n'; fi if [ $CERT_STATUS_CA_DUPLICATES == 1 ]; then CERT_STATUS_MESSAGE+=$' - Two or more CA certificates in VMWare Directory or VECS\n' CERT_STATUS_MESSAGE+=$' have the same Subject string, which can cause issues with\n' CERT_STATUS_MESSAGE+=$' certificate validation\n' fi if [ $CERT_STATUS_EXPIRED == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates are expired\n'; fi if [ $CERT_STATUS_NON_CA == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates are not CA certificates\n'; fi if [ $CERT_STATUS_BAD_ALIAS == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more entries in the TRUSTED_ROOTS store have an alias that is not the SHA1 thumbprint\n'; fi if [ $CERT_STATUS_MISSING_SAN == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates do not have any Subject Alternative Name values\n'; fi if [ $CERT_STATUS_SHA1_SIGNING == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates are signed using the SHA-1 algorithm\n'; fi if [ $CERT_STATUS_MISSING == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates are missing\n'; fi if [ $CERT_ROGUE_CA == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates are invalid because it or a signing CA\n' CERT_STATUS_MESSAGE+=$' extends beyond the pathlen restrictions of a parent CA\n' fi if [ $CERT_STATUS_MISSING_VMDIR == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more CA certificates are missing from\n' CERT_STATUS_MESSAGE+=$' VMware Directory\n' fi if [ $CERT_STATUS_VMCA_UNRETRIEVABLE == 1 ]; then CERT_STATUS_MESSAGE+=$' - Could not retrieve one or more VMCA certificates\n' CERT_STATUS_MESSAGE+=$' from a vCenter/PSC in this SSO domain\n' fi if [ $CERT_STATUS_VMCA_MISSING == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more VMCA certificates are missing from\n' CERT_STATUS_MESSAGE+=$' VMware Directory or VECS\n' fi if [ $CERT_STATUS_MISMATCH_SERVICE_PRINCIPAL == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more Solution User certificates does not match\n' CERT_STATUS_MESSAGE+=$' the Service Principal certificate in VMware Directory\n' fi if [ $CERT_STATUS_MISSING_CA == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates do not have all of the CA\n' CERT_STATUS_MESSAGE+=$' certificates in its signing chain in VMware Directory\n' fi if [ $CERT_STATUS_EXPIRED_EMBEDDED_CA == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates has a CA certificate embedded\n' CERT_STATUS_MESSAGE+=$' in its chain that is expired\n' fi if [ $CERT_STATUS_UNSUPPORTED_SIGNATURE_ALGORITHM == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more certificates are using an unsupported\n' CERT_STATUS_MESSAGE+=$' signature algorithm' fi if [ $CERT_STATUS_STORE_MISSING == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more VECS stores are missing\n'; fi if [ $CERT_STATUS_STORE_PERMISSIONS == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more VECS stores are missing permissions\n'; fi if [ $CERT_STATUS_SERVICE_PRINCIPAL_MISSING == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more Service Principal entries are missing\n' CERT_STATUS_MESSAGE+=$' from VMware Directory\n'; fi if [ $TRUST_ANCHORS_MISMATCH == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more vCenter/PSC nodes have mismatched SSL trust anchors\n' fi if [ $TRUST_ANCHORS_UNKNOWN == 1 ]; then CERT_STATUS_MESSAGE+=$' - The Machine SSL certificate could not be obtained from\n' CERT_STATUS_MESSAGE+=$' one or more nodes to check SSL trust anchors\n' fi if [ $TRUST_ANCHOR_CHECK_SAN == 1 ]; then CERT_STATUS_MESSAGE+=$' - The SSL trust anchors for one or more nodes do not contain\n' CERT_STATUS_MESSAGE+=$' the hostname/IP in the SAN that is used in the registration\n' CERT_STATUS_MESSAGE+=$' endpoint URIs\n' fi if [ $TRUST_ANCHORS_CHECK_URI == 1 ]; then if [ $TRUST_ANCHORS_STATUS_URI_MISMATCH == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more vCenter/PSC nodes have mismatched SSL trust anchors and\n' CERT_STATUS_MESSAGE+=$' have Lookup Service registrations using the IP address instead\n' CERT_STATUS_MESSAGE+=$' of the PNID in the endpoint URIs. These can be fixed with the\n' CERT_STATUS_MESSAGE+=$' lsdoctor utility: https://knowledge.broadcom.com/external/article?legacyId=80469\n' else CERT_STATUS_MESSAGE+=$' - One or more vCenter/PSC nodes have Lookup Service registrations\n' CERT_STATUS_MESSAGE+=$' using the IP address instead of the PNID in the endpoint URIs.\n' CERT_STATUS_MESSAGE+=$' These can be fixed with the lsdoctor utility:\n' CERT_STATUS_MESSAGE+=$' https://knowledge.broadcom.com/external/article?legacyId=80469\n' fi fi if [ $CERT_STATUS_TOO_MANY_CRLS == 1 ]; then CERT_STATUS_MESSAGE+=' - The number of CRLs in VECS may be preventing some services from starting'; fi if [ $CERT_STATUS_VMCA_EMPTY_CONFIG == 1 ]; then CERT_STATUS_MESSAGE+=$' - There are one or more vpxd.certmgmt.certs.cn.* settings with empty values\n' CERT_STATUS_MESSAGE+=$' This can cause issues pushing VMCA-signed certificates to ESXi hosts\n' fi if [ $CERT_STATUS_VMCA_MODE == 1 ]; then CERT_STATUS_MESSAGE+=" - The certificate management mode is set to 'thumbprint'"$'\n' CERT_STATUS_MESSAGE+=" This is not recommended, and should be set to 'vmca' or 'custom'"$'\n' fi if [ $CERT_STATUS_CLIENT_CA_LIST_FILE_LOCATION == 1 ]; then CERT_STATUS_MESSAGE+=" - The Smart Card issuing CA filter file does not reference this file:"$'\n' CERT_STATUS_MESSAGE+=" /usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem" fi if [ $CERT_STATUS_CLIENT_CA_LIST_FILE_MISSING == 1 ]; then CERT_STATUS_MESSAGE+=$' - The Smart Card issuing CA filter file at\n' CERT_STATUS_MESSAGE+=" $CAC_FILTER_FILE"$'\n' CERT_STATUS_MESSAGE+=$' does not exist\n' fi if [ $CERT_STATUS_STS_VECS_CONFIG == 1 ]; then CERT_STATUS_MESSAGE+=$' - The STS server is configured to use a VECS store other than\n' CERT_STATUS_MESSAGE+=$' the MACHINE_SSL_CERT store\n' fi if [ $CERT_STATUS_STS_CONNECTION_STRINGS == 1 ]; then CERT_STATUS_MESSAGE+=$' - The STS ConnectionStrings value is not set properly for an SSO\n' CERT_STATUS_MESSAGE+=$' domain with multiple Domain Controllers\n' fi if [ $CERT_STATUS_CA_MISSING_SKID == 1 ]; then CERT_STATUS_MESSAGE+=$' - One or more CA certificates are missing the Subject Key ID extension' fi if [ $SERVICE_RESTART_PENDING == 1 ]; then CERT_STATUS_MESSAGE+=$' - The current Machine SSL certificate is not being served on port\n' CERT_STATUS_MESSAGE+=$' 443 due to a pending service restart\n' fi } #------------------------------ # Check the number of CRLs in VECS #------------------------------ function checkCRLs() { CERT_STATUS_TOO_MANY_CRLS=0 NUM_CRLS=$($VECS_CLI entry list --store TRUSTED_ROOT_CRLS | head -n1 | awk '{print $NF}') header 'Checking Certificate Revocation Lists' task 'Number of CRLs in VECS' if [ $NUM_CRLS -le 30 ]; then statusMessage "$NUM_CRLS" 'GREEN' elif [ $NUM_CRLS -le 100 ]; then statusMessage "$NUM_CRLS" 'YELLOW' else statusMessage "$NUM_CRLS" 'RED' CERT_STATUS_TOO_MANY_CRLS=1 fi } #------------------------------ # Clear CRLs in VECS #------------------------------ function clearCRLs() { header 'Clear Certificate Revocation Lists in VECS' task 'Backup CRLs' if [ ! -d $BACKUP_DIR/old-CRLs ]; then mkdir $BACKUP_DIR/old-CRLs 2>/dev/null || errorMessage 'Unable to create backup CRL directory'; fi find /etc/ssl/certs -type f -iname '*.r[0-9]' -exec mv {} $BACKUP_DIR/old-CRLs \; || errorMessage "Unable to move CRL files to $BACKUP_DIR/old-CRLs" statusMessage 'OK' 'GREEN' task 'Delete CRLs from VECS (this may take some time)' for alias in $($VECS_CLI entry list --store TRUSTED_ROOT_CRLS | grep Alias | awk '{print $NF}'); do logInfo "Removing CRL $alias from VECS" $VECS_CLI entry delete --store TRUSTED_ROOT_CRLS --alias $alias -y > /dev/null 2>&1 done statusMessage 'OK' 'GREEN' if [ $NODE_TYPE != 'management' ]; then promptRestartVMwareServices 'vmafdd' 'vmdird' 'vmcad' else promptRestartVMwareServices 'vmafdd' fi } #------------------------------ # Clear BACKUP_STORE in VECS #------------------------------ function clearBackupStore() { if checkVECSStore 'BACKUP_STORE'; then header 'Clear BACKUP_STORE' for alias in $($VECS_CLI entry list --store BACKUP_STORE | grep Alias | awk '{print $NF}'); do task "Removing $alias" $VECS_CLI entry delete --store BACKUP_STORE --alias $alias -y > /dev/null 2>&1 || errorMessage "Unable to remove $alias entry from BACKUP_STORE" statusMessage 'OK' 'GREEN' done fi if checkVECSStore 'BACKUP_STORE_H5C'; then header 'Clear BACKUP_STORE_H5C' for alias in $($VECS_CLI entry list --store BACKUP_STORE_H5C | grep Alias | awk '{print $NF}'); do task "Removing $alias" $VECS_CLI entry delete --store BACKUP_STORE_H5C --alias $alias -y > /dev/null 2>&1 || errorMessage "Unable to remove $alias entry from BACKUP_STORE_H5C" statusMessage 'OK' 'GREEN' done fi } #------------------------------ # Clear __MACHINE_CSR in VECS #------------------------------ function clearMachineSSLCSR() { unset DELETE_MACHINE_CSR_INPUT echo $'\n'"${YELLOW}-------------------------!!! WARNING !!!-------------------------" echo "This entry was created using the 'Generate Certificate" echo "Signing Request (CSR)' option from the vSphere Client." echo 'It contains the corresponding private key associated' echo 'with this CSR. DO NOT DELETE if you are still waiting' echo "for this request to be signed by your Certificate Authority!${NORMAL}" read -p $'\nDelete the __MACHINE_CSR entry from VECS? [n]: ' DELETE_MACHINE_CSR_INPUT if [ -z $DELETE_MACHINE_CSR_INPUT ]; then DELETE_MACHINE_CSR_INPUT='n'; fi if [[ $DELETE_MACHINE_CSR_INPUT =~ ^[Yy] ]]; then header 'Delete Machine SSL CSR entry in VECS' task 'Backup private key' $VECS_CLI entry getkey --store MACHINE_SSL_CERT --alias __MACHINE_CSR > $BACKUP_DIR/__MACHINE_CSR.key 2>&1 || errorMessage 'Unable to backup private key in __MACHINE_CSR entry from VECS' statusMessage 'OK' 'GREEN' task 'Delete entry in MACHINE_SSL_CERT store' $VECS_CLI entry delete --store MACHINE_SSL_CERT --alias __MACHINE_CSR -y > /dev/null 2>&1 || errorMessage "Unable to delete entry '__MACHINE_CSR' from VECS" statusMessage 'OK' 'GREEN' fi } #------------------------------ # Perform quick check of SSL trust anchors #------------------------------ function quickCheckSSLTrustAnchors() { header 'Checking SSL Trust Anchors' getSSODomainNodes TRUST_ANCHORS_UNKNOWN=0 TRUST_ANCHORS_MISMATCH=0 TRUST_ANCHOR_CHECK_SAN=0 TRUST_ANCHORS_CHECK_URI=0 SERVICE_RESTART_PENDING=0 for node in "${SSO_NODES[@]}"; do TRUST_ANCHORS_STATUS_MISMATCH=0 TRUST_ANCHORS_STATUS_CHECK_URI=0 TRUST_ANCHORS_STATUS_URI_MISMATCH=0 TRUST_ANCHORS_STATUS_URI_SAN=0 TRUST_ANCHORS_SERVICE_RESTART_PENDING=0 task "$node" NODE_IP=$(dig +short "$node") NODE_LC=$(echo "$node" | awk '{print tolower($0)}') logInfo 'Checking node name against IP' logDetails "Node: $node" logDetails "IP: $NODE_IP" if [[ $node =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then NODE_SAN_CHECK='IP' else NODE_SAN_CHECK='FQDN' fi if [[ "$NODE_LC" == "$PNID_LC" ]] || [[ "$NODE_IP" == "$IP" ]]; then MACHINE_SSL_VECS_THUMBPRINT=$($VECS_CLI entry getcert --store MACHINE_SSL_CERT --alias __MACHINE_CERT | openssl x509 -noout -fingerprint -sha1 | awk -F'=' '{print $NF}') MACHINE_SSL_ENVOY_THUMBPRINT=$(echo | timeout 3 openssl s_client -connect $node:443 2>/dev/null | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}') if [[ "$MACHINE_SSL_VECS_THUMBPRINT" != "$MACHINE_SSL_ENVOY_THUMBPRINT" ]]; then TRUST_ANCHORS_SERVICE_RESTART_PENDING=1 SERVICE_RESTART_PENDING=1 fi NODE_MACHINE_SSL_THUMBPRINT=$MACHINE_SSL_VECS_THUMBPRINT else NODE_MACHINE_SSL_THUMBPRINT=$(echo | timeout 3 openssl s_client -connect $node:443 2>/dev/null | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}') fi NODE_MACHINE_SSL_SAN=$(echo | timeout 3 openssl s_client -connect $node:443 2>/dev/null | openssl x509 -noout -text | grep -A1 'X509v3 Subject Alternative Name' | tail -n1) if [ -n "$NODE_MACHINE_SSL_THUMBPRINT" ]; then TRUST_ANCHOR_SEARCH_FILTER="(&(|(vmwLKUPURI=https://$node:*)(vmwLKUPURI=https://$node/*))(|(objectclass=vmwLKUPServiceEndpoint)(objectclass=vmwLKUPEndpointRegistration)))" NODE_TRUST_ANCHORS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=Sites,cn=Configuration,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password "$TRUST_ANCHOR_SEARCH_FILTER" vmwLKUPEndpointSslTrust vmwLKUPSslTrustAnchor 2>>$LOG | grep -v '^dn:' | awk '{print $NF}' | sort | uniq) if [ -z "$NODE_TRUST_ANCHORS" ]; then TRUST_ANCHOR_SEARCH_FILTER="(&(|(vmwLKUPURI=https://$NODE_IP:*)(vmwLKUPURI=https://$NODE_IP/*))(|(objectclass=vmwLKUPServiceEndpoint)(objectclass=vmwLKUPEndpointRegistration)))" NODE_TRUST_ANCHORS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=Sites,cn=Configuration,$VMDIR_DOMAIN_DN" -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password "$TRUST_ANCHOR_SEARCH_FILTER" vmwLKUPEndpointSslTrust vmwLKUPSslTrustAnchor 2>>$LOG | grep -v '^dn:' | awk '{print $NF}' | sort | uniq) TRUST_ANCHORS_CHECK_URI=1 TRUST_ANCHORS_STATUS_CHECK_URI=1 NODE_SAN_CHECK="IP" fi if [ -z "$NODE_TRUST_ANCHORS" ]; then statusMessage 'MISSING' 'YELLOW' else case $NODE_SAN_CHECK in 'FQDN') if [ "$node" != 'localhost' ] && ! echo "$NODE_MACHINE_SSL_SAN" | grep -iq "DNS:$node"; then logInfo "The node name '$node' is missing as a DNS entry in the SAN'" TRUST_ANCHORS_STATUS_URI_SAN=1 TRUST_ANCHOR_CHECK_SAN=1 fi ;; 'IP') if ! echo "$NODE_MACHINE_SSL_SAN" | grep -iq "IP Address:$NODE_IP"; then logInfo "The node name '$node' is missing as an IP entry in the SAN'" TRUST_ANCHORS_STATUS_URI_SAN=1 TRUST_ANCHOR_CHECK_SAN=1 fi ;; esac for hash_raw in $NODE_TRUST_ANCHORS; do logInfo "Checking following raw certificate hash" logDetails "$hash_raw" if [[ "$hash_raw" =~ ^TUl ]]; then hash=$(echo $hash_raw | base64 --decode | tr -d '\r\n') else hash="$hash_raw" fi TEMP_CERT=$(buildCertFromHash "$hash") ANCHOR_THUMBPRINT=$(echo "$TEMP_CERT" | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}') logInfo "Checking node thumbprint $NODE_MACHINE_SSL_THUMBPRINT against unique trust anchor thumbprint $ANCHOR_THUMBPRINT" if [ "$NODE_MACHINE_SSL_THUMBPRINT" != "$ANCHOR_THUMBPRINT" ]; then TRUST_ANCHORS_STATUS_MISMATCH=1 TRUST_ANCHORS_MISMATCH=1 if [ $TRUST_ANCHORS_CHECK_URI -eq 1 ]; then TRUST_ANCHORS_STATUS_URI_MISMATCH=1; fi fi done logInfo 'Searching for ghost trust anchors' CURRENT_DN='' CURRENT_NODEID='' VCENTERSERVER_REGS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -b "cn=Sites,cn=Configuration,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwLKUPType=vcenterserver)' vmwLKUPDeploymentNodeId | sed 's/, cn/,cn/g') IFS=$'\n' for line in $VCENTERSERVER_REGS; do if [[ $line =~ ^dn: ]]; then CURRENT_DN=${line//dn: /} else CURRENT_NODEID=${line//vmwLKUPDeploymentNodeId: /} VCENTERSERVER_ENDPOINTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -b "$CURRENT_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwLKUPEndpointRegistration)' vmwLKUPURI) logDebug "Searching for vcenterserver registrations tied to Node ID '$CURRENT_NODEID'" if [ -n "$VCENTERSERVER_ENDPOINTS" ]; then if echo "$VCENTERSERVER_ENDPOINTS" | grep -qE "https://$node/|https://$node:|https://$NODE_IP/|https://$NODE_IP:"; then logDebug "Found vcenterserver registration for $NODE_FQDN: '$CURRENT_NODEID'" GHOST_VMONAPI_DN=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -b "cn=Sites,cn=Configuration,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password "(&(vmwLKUPType=cis.vmonapi)(vmwLKUPDeploymentNodeId=$CURRENT_NODEID))" dn | sed -e 's/, cn/,cn/g' -e 's/^dn: //g') if [ -n "$GHOST_VMONAPI_DN" ]; then logDebug "cis.vmonapi registration DN: $GHOST_VMONAPI_DN" GHOST_VMONAPI_ENDPOINT_TRUST_ANCHORS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -b "$GHOST_VMONAPI_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwLKUPURI=http://localhost*)' vmwLKUPEndpointSslTrust | grep '^vmwLKUPEndpointSslTrust' | awk '{print $NF}' | sort | uniq) for hash_raw in $GHOST_VMONAPI_ENDPOINT_TRUST_ANCHORS; do if [[ "$hash_raw" =~ ^TUl ]]; then hash=$(echo $hash_raw | base64 --decode | tr -d '\r\n') else hash="$hash_raw" fi TEMP_CERT=$(buildCertFromHash "$hash") ANCHOR_THUMBPRINT=$(echo "$TEMP_CERT" | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}') logInfo "Checking node thumbprint $NODE_MACHINE_SSL_THUMBPRINT against unique trust anchor thumbprint $ANCHOR_THUMBPRINT" if [ "$NODE_MACHINE_SSL_THUMBPRINT" != "$ANCHOR_THUMBPRINT" ]; then TRUST_ANCHORS_STATUS_MISMATCH=1 TRUST_ANCHORS_MISMATCH=1 fi done fi fi fi fi done if [ $TRUST_ANCHORS_STATUS_MISMATCH -eq 0 ]; then if [ $TRUST_ANCHORS_STATUS_CHECK_URI -eq 1 ]; then if [ $TRUST_ANCHORS_STATUS_URI_SAN -eq 1 ]; then statusMessage 'CHECK SAN' 'YELLOW' else statusMessage 'CHECK URI' 'YELLOW' fi elif [ $TRUST_ANCHORS_SERVICE_RESTART_PENDING -eq 1 ]; then statusMessage 'PENDING' 'YELLOW' else statusMessage 'VALID' 'GREEN' fi else if [ $TRUST_ANCHORS_STATUS_URI_SAN -eq 1 ]; then statusMessage 'CHECK SAN' 'YELLOW' elif [ $TRUST_ANCHORS_CHECK_URI -eq 1 ]; then statusMessage 'MISMATCH*' 'YELLOW' else statusMessage 'MISMATCH' 'YELLOW' fi fi fi else TRUST_ANCHORS_UNKNOWN=1 statusMessage 'UNKNOWN' 'YELLOW' fi done } #------------------------------ # Get the PSC and vCenter nodes in an SSO Domain #------------------------------ function getSSODomainNodes() { SSO_NODES=() PSC_NODES=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "ou=Domain Controllers,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=computer)' cn | grep '^cn:' | awk '{print $NF}') PSC_COUNT=$(echo "$PSC_NODES" | wc -l) VCENTER_NODES=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "ou=Computers,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=computer)' cn | grep '^cn:' | awk '{print $NF}') VCENTER_COUNT=$(echo "$VCENTER_NODES" | wc -l) for psc_node in $PSC_NODES; do if [[ ! "${SSO_NODES[*]}" =~ "$psc_node" ]]; then SSO_NODES+=($psc_node); fi done for vc_node in $VCENTER_NODES; do if [[ ! "${SSO_NODES[*]}" =~ "$vc_node" ]]; then SSO_NODES+=($vc_node); fi done } #------------------------------ # Print menu to view or manage certificates #------------------------------ function printCertificateMenu() { authenticateIfNeeded header "$1 vCenter Certificates" logInfo "Printing the $1 vCenter Certificates Menu" echo ' 1. Machine SSL certificate' echo ' 2. Solution User certificates' echo ' 3. CA certificates in VMware Directory' echo ' 4. CA certificates in VECS' if [ $NODE_TYPE = 'infrastructure' ]; then printf "%s" "$YELLOW"; fi echo ' 5. Authentication Proxy certificate' echo ' 6. Auto Deploy CA certificate' echo ' 7. SMS certificates' if [ "$VC_VERSION" == '6.5' ]; then printf "%s" "$YELLOW"; fi echo ' 8. Data Encipherment certificate' printf "%s" "$NORMAL" if [ $NODE_TYPE = 'infrastructure' ]; then printf "%s" "$YELLOW"; fi echo ' 9. vCenter Extension thumbprints' printf "%s" "$NORMAL" if [ $NODE_TYPE = 'management' ]; then printf "%s" "$YELLOW" else printf "%s" "$NORMAL" fi echo '10. VMware Directory certificate' echo '11. STS signing certificates' echo '12. VMCA certificate' if ! configuredForCAC; then printf "%s" "$YELLOW"; fi echo '13. Smart Card CA certificates' printf "%s" "$NORMAL" if ! configuredForADoverLDAPS; then printf "%s" "$YELLOW"; fi echo '14. LDAPS Identity Source certificates' printf "%s" "$NORMAL" if ! tanzuSupervisorClustersPresent || [ "$1" == 'Manage' ]; then printf "%s" "$YELLOW"; fi echo '15. Tanzu Supervisor Cluster certificates' printf "%s" "$NORMAL" if [ "$1" == 'Manage' ]; then if ! checkVECSStore 'BACKUP_STORE' && ! checkVECSStore 'BACKUP_STORE_H5C'; then printf "%s" "$YELLOW"; fi echo '16. Clear BACKUP_STORE in VECS' printf "%s" "$NORMAL" echo '17. Clear TRUSTED_ROOT_CRLS store in VECS' if ! checkMachineSSLCSR; then printf "%s" "$YELLOW"; fi echo '18. Clear Machine SSL CSR in VECS' printf "%s" "$NORMAL" fi echo '' } #------------------------------ # Check if Smart Card authentication is configured #------------------------------ function configuredForCAC() { if $LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSTenant)' vmwSTSAuthnTypes | grep -q 'vmwSTSAuthnTypes: 4'; then return 0 else return 1 fi } #------------------------------ # Check the an AD over LDAPS Identity Source is configured #------------------------------ function configuredForADoverLDAPS() { if $LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' cn 2>/dev/null | grep cn > /dev/null 2>&1; then logInfo 'vCenter is using AD over LDAPS as an Identity Source' return 0 elif $LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=VCIdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' cn 2>/dev/null | grep cn > /dev/null 2>&1; then logInfo 'vCenter is using ADFS as an Identity Source' return 0 elif $LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP)' cn 2>/dev/null | grep cn > /dev/null 2>&1; then logInfo 'vCenter is using OpenLDAP as an Identity Source' return 0 else logInfo 'vCenter is NOT using AD over LDAPS as an Identity Source' return 1 fi } #------------------------------ # Check if there are any Tanzu Supervisor clusters deployed #------------------------------ function tanzuSupervisorClustersPresent() { if [ -f /usr/lib/vmware-wcp/decryptK8Pwd.py ]; then if /usr/lib/vmware-wcp/decryptK8Pwd.py | grep -E '^Cluster: |^IP: |^PWD: ' > /dev/null 2>&1; then logInfo 'Tanzu Supervisor Clusters detected' return 0 else logInfo 'No Tanzu Supervisor Clusters detected' return 1 fi else return 1 fi } #------------------------------ # Display options to view certificate info #------------------------------ function viewCertificateMenu() { printCertificateMenu 'View' read -p 'Select an option [Return to Main Menu]: ' VIEW_CERT_OPERATION logInfo "User selected option $VIEW_CERT_OPERATION" if [[ "$VIEW_CERT_OPERATION" -ge 0 && "$VIEW_CERT_OPERATION" -le 15 ]]; then processViewCertificate; fi } #------------------------------ # Display options to manage certificates #------------------------------ function manageCertificateMenu() { printCertificateMenu 'Manage' read -t $READ_TIMEOUTS -p 'Select an option [Return to Main Menu]: ' MANAGE_CERT_OPERATION if [ $? -le 128 ]; then logInfo "User selected option '$MANAGE_CERT_OPERATION'" if [[ "$MANAGE_CERT_OPERATION" -ge 1 && "$MANAGE_CERT_OPERATION" -le 18 ]]; then processManageCertificate; fi else echo '' fi } #------------------------------ # Process view certificate selection #------------------------------ function processViewCertificate() { case $VIEW_CERT_OPERATION in 1) viewVECSCertificateInfo 'MACHINE_SSL_CERT' '__MACHINE_CERT' ;; 2) for soluser in "${SOLUTION_USERS[@]}"; do echo $'\n'"Solution User: $soluser" viewVECSCertificateInfo "$soluser" "$soluser" done ;; 3) manageVMDirCACertificates 'View' ;; 4) manageVECSCACertificates 'View' ;; 5) viewFilesystemCertificateInfo '/var/lib/vmware/vmcam/ssl/vmcamcert.pem' ;; 6) viewFilesystemCertificateInfo '/etc/vmware-rbd/ssl/rbd-ca.crt' ;; 7) manageSMSCertificates 'View' ;; 8) viewVECSCertificateInfo 'data-encipherment' 'data-encipherment' ;; 9) manageVCExtensionThumbprints 'View' ;; 10) if [[ "$VC_VERSION" =~ ^[78] ]]; then viewRemoteCertificateInfo 'localhost' '636' else viewFilesystemCertificateInfo '/usr/lib/vmware-vmdir/share/config/vmdircert.pem' fi ;; 11) manageSTSTenantCerts 'View' ;; 12) viewFilesystemCertificateInfo "$VMCA_CERT" ;; 13) manageCACCerts 'View' ;; 14) manageLDAPSCerts 'View' ;; 15) manageTanzuSupervisorClusterCerts 'View' ;; esac } #------------------------------ # Process manage certificate selection #------------------------------ function processManageCertificate() { setTimestamp case $MANAGE_CERT_OPERATION in 1) if promptReplaceMachineSSL; then if replaceMachineSSLCert && [[ $UPDATED_MACHINE_SSL -eq 1 ]]; then if [ $NODE_TYPE != 'infrastructure' ]; then SSLTrustAnchorSelf updateSSLTrustAnchors manageVCExtensionThumbprints 'Update' updateAutoDeployDB fi if [ $NODE_TYPE = 'infrastructure' ]; then if [ -n "$PSC_LB" ]; then NODE_FQDN="$PSC_LB" else SSLTrustAnchorSelf fi updateSSLTrustAnchors fi if [ -n "$SDDC_MANAGER" ]; then publishMachineSSLCACertsSDDCManager fi noticePSCHA promptRestartVMwareServices fi clearCSRInfo fi ;; 2) if promptReplaceSolutionUsers; then if replaceSolutionUserCerts && [ $NODE_TYPE != 'infrastructure' ]; then manageVCExtensionThumbprints 'Update' promptRestartVMwareServices fi clearCSRInfo fi ;; 3) manageVMDirCACertificates 'Manage' ;; 4) manageVECSCACertificates 'Manage' ;; 5) if [ $NODE_TYPE != 'infrastructure' ]; then if promptReplaceAuthProxy; then if replaceAuthProxyCert; then manageVCExtensionThumbprints 'Update' promptRestartVMwareServices 'vmcam' fi clearCSRInfo fi else printf "\n%s\n\n" "${YELLOW}This operation must be done on the vCenter Server.${NORMAL}" fi ;; 6) if [ $NODE_TYPE != 'infrastructure' ]; then if promptReplaceAutoDeployCA; then if replaceAutoDeployCACert; then promptRestartVMwareServices 'vmware-rbd-watchdog'; fi clearCSRInfo fi else printf "\n%s\n\n" "${YELLOW}This operation must be done on the vCenter Server.${NORMAL}" fi ;; 7) manageSMSCertificates 'Manage' ;; 8) replaceDataEnciphermentCertificate ;; 9) if [ $NODE_TYPE != 'infrastructure' ]; then manageVCExtensionThumbprints 'Check' else printf "\n%s\n\n" "${YELLOW}This operation must be done on the vCenter Server.${NORMAL}" fi ;; 10) if [[ "$VC_VERSION" =~ ^[78] ]]; then if [ -f /usr/lib/vmware-vmdir/share/config/vmdircert.pem ]; then removeVMDirCert else printf "\n%s\n\n" "${YELLOW}This operation is not available for vCenter 7.x or later${NORMAL}" fi elif [ $NODE_TYPE != 'management' ]; then if promptReplaceVMDir; then replaceVMDirCert; fi else printf "\n%s\n\n" "${YELLOW}This operation must be done on the Platform Services Controller${NORMAL}" fi ;; 11) if [ $NODE_TYPE != 'management' ]; then manageSTSTenantCerts 'Replace' else printf "\n%s\n\n" "${YELLOW}This operation must be done on the Platform Services Controller${NORMAL}" fi ;; 12) if [ $NODE_TYPE != 'management' ]; then if promptReplaceVMCA; then if replaceVMCACert && [ "$VMCA_REGENERATE_CERTIFICATES" == '1' ]; then resetAllCertificates; fi fi else printf "\n%s\n\n" "${YELLOW}This operation must be done on the Platform Services Controller${NORMAL}" fi ;; 13) manageCACCerts 'Manage' ;; 14) manageLDAPSCerts 'Manage' ;; 15) #manageTanzuSupervisorClusterCerts 'Manage' ;; 16) if checkVECSStore 'BACKUP_STORE' || checkVECSStore 'BACKUP_STORE_H5C'; then clearBackupStore else echo $'\n'"${YELLOW}The BACKUP_STORE does not exist in VECS, nothing to do.$NORMAL" fi ;; 17) clearCRLs ;; 18) clearMachineSSLCSR ;; esac } #------------------------------ # Menu for options generating the certificate report #------------------------------ function viewCertificateReportMenu() { unset CERTIFICATE_REPORT_INPUT header 'Certificate Report Options' cat << EOF 1. Generate vCenter certificate report" 2. Generate ESXi certificate report" 3. Generate vCenter and ESXi certifiate report EOF read -p $'\nEnter report selection [Return to Main Menu]: ' CERTIFICATE_REPORT_INPUT if [ -z $CERTIFICATE_REPORT_INPUT ]; then return 1; fi if [ -f $VC_REPORT ]; then echo '' > $VC_REPORT; fi printf "\n" case $CERTIFICATE_REPORT_INPUT in 1) generatevCenterCertificateReport ;; 2) generateESXiCertificateReport ;; 3) generatevCenterCertificateReport generateESXiCertificateReport ;; esac } #------------------------------ # Generate vCenter certificate report #------------------------------ function generatevCenterCertificateReport() { if [ "$NODE_TYPE" != 'infrastructure' ] && ! checkService 'vmware-vpostgres'; then echo "${YELLOW}The vPostgres service is stopped!" echo "Please ensure this service is running before generating a certificate report." echo "Hint: Check the number of CRL entries in VECS${NORMAL}" return 1 fi authenticateIfNeeded disableColor printf '%0.1s' "="{1..130} | tee $VC_REPORT printf '\n' | tee -a $VC_REPORT echo 'SSL Certificate Report' | tee -a $VC_REPORT echo "vCert $VERSION" | tee -a $VC_REPORT echo "Host: $HOSTNAME" | tee -a $VC_REPORT echo "Date: $(date -u)" | tee -a $VC_REPORT echo "Node Type: $NODE_TYPE" | tee -a $VC_REPORT echo "Build: $VC_BUILD" | tee -a $VC_REPORT echo "Machine ID: $MACHINE_ID" | tee -a $VC_REPORT echo "PNID: $PNID" | tee -a $VC_REPORT if [ $NODE_TYPE != 'infrastructure' ]; then CERT_MGMT_MODE=$($PSQL -d VCDB -U postgres -c "SELECT value FROM vpx_parameter WHERE name='vpxd.certmgmt.mode'" -t | grep -v '^$') echo "Certificate Management Mode: $CERT_MGMT_MODE" | tee -a $VC_REPORT fi printf '%0.1s' "="{1..130} | tee -a $VC_REPORT printf '\n' | tee -a $VC_REPORT VMDIR_CA_SUBJECT_IDS='' VECS_CA_SUBJECT_IDS='' for CNID in $($DIR_CLI trustedcert list --login "$VMDIR_USER_UPN" --password "$VMDIR_USER_PASSWORD" | grep 'CN(id)' | awk '{print $NF}'); do CERT=$($DIR_CLI trustedcert get --id $CNID --login "$VMDIR_USER_UPN" --password "$VMDIR_USER_PASSWORD" --outcert /dev/stdout | grep -v 'Certificate retrieved successfully') VMDIR_CERT_INFO=$(viewCertificateInfo "$CERT") VMDIR_CERT_SERIAL=$(echo "$VMDIR_CERT_INFO" | grep -A1 'Serial Number' | tail -n1 | tr -d ' ' | awk '{print toupper($0)}') VMDIR_CERT_SUBJECT=$(echo "$VMDIR_CERT_INFO" | grep 'Subject: ' | sed 's/Subject: //') VMDIR_CERT_SUBJECT_KEY=$(echo "$VMDIR_CERT_INFO" | grep -A1 'Subject Key Identifier' | tail -n1 | tr -d ' ') VMDIR_CA_SUBJECT_IDS+="serial:$VMDIR_CERT_SERIAL|DirName:$VMDIR_CERT_SUBJECT|keyid:$VMDIR_CERT_SUBJECT_KEY"$'\n' done IFS=$'\n' for alias in $($VECS_CLI entry list --store TRUSTED_ROOTS --text | grep 'Alias' | awk -F"[[:space:]]:[[:space:]]" '{print $NF}'); do CERT=$($VECS_CLI entry getcert --store TRUSTED_ROOTS --alias "$alias") VECS_CERT_INFO=$(viewCertificateInfo "$CERT") VECS_CERT_SERIAL=$(echo "$VECS_CERT_INFO" | grep -A1 'Serial Number' | tail -n1 | tr -d ' ' | awk '{print toupper($0)}') VECS_CERT_SUBJECT=$(echo "$VECS_CERT_INFO" | grep 'Subject: ' | sed 's/Subject: //') VECS_CERT_SUBJECT_KEY=$(echo "$VECS_CERT_INFO" | grep -A1 'Subject Key Identifier' | tail -n1 | tr -d ' ') VECS_CA_SUBJECT_IDS+="serial:$VECS_CERT_SERIAL|DirName:$VECS_CERT_SUBJECT|keyid:$VECS_CERT_SUBJECT_KEY"$'\n' done IFS=$' \t\n' echo 'VECS Certificates' | tee -a $VC_REPORT for store in $($VECS_CLI store list | grep -v 'APPLMGMT_PASSWORD'); do echo " Store: $store" | tee -a $VC_REPORT IFS=$'\n' for alias in $($VECS_CLI entry list --store $store | grep 'Alias' | sed 's/^Alias[[:space:]]*:[[:space:]]*//g'); do echo " Alias: $alias" | tee -a $VC_REPORT VECS_HASH=$($VECS_CLI entry getcert --store $store --alias "$alias" 2>/dev/null) if [[ $? -eq 0 ]]; then if ! echo "$VECS_HASH" | head -n1 | grep -q 'BEGIN CERTIFICATE'; then reportCRLDetails "$VECS_HASH" else case $store-$alias in MACHINE_SSL_CERT-__MACHINE_CERT) EXTRA_INFO='checkCurrentMachineSSLUsage|reportExtentionThumbprintsMachineSSL' ;; vpxd-extension-vpxd-extension) EXTRA_INFO='reportExtentionThumbprintsVpxdExtension' ;; *) EXTRA_INFO='' ;; esac reportCertDetails "$VECS_HASH" "$EXTRA_INFO" fi else echo " |_No certificate found in store" | tee -a $VC_REPORT fi done IFS=$' \t\n' done echo 'VMware Directory Certificates' | tee -a $VC_REPORT echo ' CA Certificates' | tee -a $VC_REPORT for CNID in $($DIR_CLI trustedcert list --login "$VMDIR_USER_UPN" --password "$VMDIR_USER_PASSWORD" | grep 'CN(id)' | awk '{print $NF}'); do echo " CN(id): $CNID" | tee -a $VC_REPORT VMDIR_CA_HASH=$($DIR_CLI trustedcert get --id $CNID --login "$VMDIR_USER_UPN" --password "$VMDIR_USER_PASSWORD" --outcert /dev/stdout | grep -v 'Certificate retrieved successfully') reportCertDetails "$VMDIR_CA_HASH" done echo ' Service Principal (Solution User) Certificates' | tee -a $VC_REPORT IFS=$'\n' for line in $($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=ServicePrincipals,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwServicePrincipal)' userCertificate); do if [[ "$line" =~ ^dn: ]]; then SERVICE_PRINCIPAL=$(echo "$line" | awk -F':' '{print $NF}' | awk -F',' '{print $1}' | awk -F'=' '{print $NF}') echo " Service Principal: $SERVICE_PRINCIPAL" | tee -a $VC_REPORT else SERVICE_PRINCIPAL_CERT_HASH=$(echo "$line" | awk '{print $NF}') TEMP_CERT=$(buildCertFromHash "$SERVICE_PRINCIPAL_CERT_HASH") reportCertDetails "$TEMP_CERT" fi done IFS=$' \t\n' echo ' Single Sign-On Secure Token Service Certificates' | tee -a $VC_REPORT TENANT_COUNT=0 for hash in $($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSTenantCredential)' userCertificate | grep '^userCertificate' | awk '{print $NF}'); do TEMP_CERT=$(buildCertFromHash "$hash") if isCertCA "$TEMP_CERT"; then echo " TenantCredential-$TENANT_COUNT CA Certificate" | tee -a $VC_REPORT else ((++TENANT_COUNT)) echo " TenantCredential-$TENANT_COUNT Signing Certificate" | tee -a $VC_REPORT fi reportCertDetails "$TEMP_CERT" done CHAIN_COUNT=0 for hash in $($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=TrustedCertificateChains,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSTenantTrustedCertificateChain)' userCertificate | grep '^userCertificate' | awk '{print $NF}'); do TEMP_CERT=$(buildCertFromHash "$hash") if isCertCA "$TEMP_CERT"; then echo " TrustedCertChain-$CHAIN_COUNT CA Certificate" | tee -a $VC_REPORT else ((++CHAIN_COUNT)) echo " TrustedCertChain-$CHAIN_COUNT Signing Certificate" | tee -a $VC_REPORT fi reportCertDetails "$TEMP_CERT" done CAC_CAS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=DefaultClientCertCAStore,cn=ClientCertAuthnTrustedCAs,cn=Default,cn=ClientCertificatePolicies,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwSTSTenantTrustedCertificateChain)' userCertificate 2>/dev/null | grep '^userCertificate' | awk '{print $NF}') if [ -n "$CAC_CAS" ]; then CAC_ISSUING_CA_COUNT=1 echo ' Smart Card Issuing CA Certificates' | tee -a $VC_REPORT for hash in $CAC_CAS; do TEMP_CERT=$(buildCertFromHash "$hash") echo " Smart Card Issuing CA $CAC_ISSUING_CA_COUNT" | tee -a $VC_REPORT reportCertDetails "$TEMP_CERT" ((++CAC_ISSUING_CA_COUNT)) done fi AD_LDAPS_CERTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(vmwSTSProviderType=IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING)' userCertificate 2>/dev/null | grep '^userCertificate::' | awk '{print $NF}') if [ -n "$AD_LDAPS_CERTS" ]; then echo ' AD Over LDAPS Domain Controller Certificates' | tee -a $VC_REPORT LDAPS_DC_CERT_COUNT=1 for hash in $AD_LDAPS_CERTS; do echo " Certificate $LDAPS_DC_CERT_COUNT" | tee -a $VC_REPORT reportCertDetails "$(buildCertFromHash $hash)" ((++LDAPS_DC_CERT_COUNT)) done fi echo 'Filesystem Certificates' | tee -a $VC_REPORT if [ "$NODE_TYPE" != 'management' ]; then if [[ "$VC_VERSION" =~ ^6 ]]; then echo ' VMware Directory Certificate' | tee -a $VC_REPORT echo ' Certificate: /usr/lib/vmware-vmdir/share/config/vmdircert.pem' | tee -a $VC_REPORT reportCertDetails "$(cat /usr/lib/vmware-vmdir/share/config/vmdircert.pem)" fi echo ' VMCA Certificate' | tee -a $VC_REPORT echo " Certificate: $VMCA_CERT" | tee -a $VC_REPORT reportCertDetails "$(cat $VMCA_CERT)" fi if [ "$NODE_TYPE" != 'infrastructure' ]; then echo ' Authentication Proxy Certificate' | tee -a $VC_REPORT echo ' Certificate: /var/lib/vmware/vmcam/ssl/vmcamcert.pem' | tee -a $VC_REPORT reportCertDetails "$(cat /var/lib/vmware/vmcam/ssl/vmcamcert.pem)" echo ' Auto Deploy CA Certificate' | tee -a $VC_REPORT echo ' Certificate: /etc/vmware-rbd/ssl/rbd-ca.crt' | tee -a $VC_REPORT reportCertDetails "$(cat /etc/vmware-rbd/ssl/rbd-ca.crt)" fi if grep '' /etc/vmware-rhttpproxy/config.xml | grep -qv '