Last active
November 18, 2023 20:40
-
-
Save fluggelgleckheimlen/d5c8886ac49f4f66823b80841940bd5c to your computer and use it in GitHub Desktop.
Script to manage vCenter SSL certificates from VMware GSS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
#------------------------------ | |
# Script to manage vCenter SSL certificates. | |
# | |
# Author: Vincent Santa Maria [vsantamaria@vmware.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.13.0" | |
VCERT_PID="$$" | |
trap "exit 1" 10 | |
#------------------------------ | |
# 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 | |
echo -ne "\r\033[K" | |
} | |
#------------------------------ | |
# Print section header | |
#------------------------------ | |
function header() { | |
printf "\n${CYAN}$1\n" | |
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 "${YELLOW}${1}. Exiting...${NORMAL}\n\n" | |
kill -10 $VCERT_PID | |
else | |
case $2 in | |
'backup') | |
if [ $EXIT_ON_BACKUP_FAILURE == 1 ]; then | |
printf "${YELLOW}${1}. Exiting...${NORMAL}\n\n" | |
kill -10 $VCERT_PID | |
fi | |
;; | |
*) | |
printf "${YELLOW}${1}. Exiting...${NORMAL}\n\n" | |
kill -10 $VCERT_PID | |
;; | |
esac | |
fi | |
} | |
#------------------------------ | |
# Main logging function | |
#------------------------------ | |
function logEntry() { | |
if [ -n "$1" ]; then | |
IN="$1" | |
LOG_LEVEL="$2" | |
else | |
read 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 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 IN | |
fi | |
logEntry "$IN" 'INFO' | |
} | |
#------------------------------ | |
# Logging function for error-level logging | |
#------------------------------ | |
function logError() { | |
if [ -n "$1" ]; then | |
IN="$1" | |
else | |
read 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 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 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) | |
NORMAL=$(tput sgr0) | |
} | |
#------------------------------ | |
# Clear color variables for reports | |
#------------------------------ | |
function disableColor() { | |
RED='' | |
GREEN='' | |
YELLOW='' | |
CYAN='' | |
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 $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 [ $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 -p $'\n'"Please enter a Single Sign-On administrator account [${VMDIR_USER_UPN_DEFAULT}]: " VMDIR_USER_UPN_INPUT | |
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 -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 -r -s -p "Please provide the password for $VMDIR_USER_UPN: " VMDIR_USER_PASSWORD | |
echo -n "$VMDIR_USER_PASSWORD" > $STAGE_DIR/.vmdir-user-password | |
chmod 640 $STAGE_DIR/.vmdir-user-password | |
echo '' | |
} | |
#------------------------------ | |
# 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 'vmware-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 'vmware-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 '{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() { | |
if service-control --status $1 | grep -i stopped >/dev/null 2>&1; then | |
return 1 | |
else | |
return 0 | |
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 -i "$VMDIR_MACHINE_ACCOUNT_DN" 2>/dev/null > /dev/null; 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 -i "cn=DCAdmins,cn=BuiltIn,$VMDIR_DOMAIN_DN" 2>/dev/null > /dev/null; 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 -i "$VMDIR_MACHINE_ACCOUNT_DN" 2>/dev/null > /dev/null; 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 -i "cn=DCClients,cn=BuiltIn,$VMDIR_DOMAIN_DN" 2>/dev/null > /dev/null; 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') | |
if [ $NODE_TYPE == 'infrastructure' ]; then | |
VECS_STORES+=' APPLMGMT_PASSWORD' | |
VECS_STORE_READ_PERMISSIONS[APPLMGMT_PASSWORD]='' | |
else | |
VECS_STORES+=' APPLMGMT_PASSWORD' | |
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_STORE_READ_PERMISSIONS[MACHINE_SSL_CERT]='vlcm updatemgr vsphere-ui vpxd vpostgres vsm vsan-health lighttpd' | |
VECS_STORE_WRITE_PERMISSIONS[MACHINE_SSL_CERT]='rhttpproxy' | |
VECS_STORE_WRITE_PERMISSIONS[TRUSTED_ROOTS]='rhttpproxy sps' | |
if [ $VC_BUILD -lt 22385739 ]; then | |
VECS_STORE_READ_PERMISSIONS[machine]='statsmon vsan-health sca vpxd observability' | |
else | |
VECS_STORE_READ_PERMISSIONS[machine]='vsan-health sca vpxd observability' | |
fi | |
VECS_STORE_WRITE_PERMISSIONS[machine]='certauth certmgr infraprofile sts' | |
VECS_STORE_READ_PERMISSIONS[vsphere-webclient]+=' analytics' | |
VECS_STORE_WRITE_PERMISSIONS[vsphere-webclient]='infraprofile' | |
VECS_STORE_READ_PERMISSIONS[vpxd]+=' vsan-health' | |
if [ $VC_BUILD -lt 22385739 ]; then | |
VECS_STORE_READ_PERMISSIONS[vpxd-extension]='analytics content-library deploy eam imagebuilder updatemgr vlcm vpxd vsan-health vsm vsphere-ui vstatsuser wcp' | |
else | |
VECS_STORE_READ_PERMISSIONS[vpxd-extension]='analytics content-library eam imagebuilder updatemgr vlcm vpxd vsan-health vsm vsphere-ui vstatsuser wcp' | |
fi | |
VECS_STORE_WRITE_PERMISSIONS[vpxd-extension]='infraprofile sps' | |
VECS_STORE_READ_PERMISSIONS[hvc]='vpxd' | |
VECS_STORE_READ_PERMISSIONS[wcp]='wcp content-library' | |
VECS_STORE_READ_PERMISSIONS[SMS]='deploy' | |
VECS_STORE_WRITE_PERMISSIONS[SMS]='vpxd' | |
if [ $VC_BUILD -ge 20920323 ]; then | |
VECS_STORE_WRITE_PERMISSIONS[SMS]='' | |
if [ $VC_BUILD -lt 21560480 ]; then | |
VECS_STORE_WRITE_PERMISSIONS[TRUSTED_ROOTS]+=' hvc infraprofile' | |
VECS_STORE_WRITE_PERMISSIONS[machine]+=' sts hvc' | |
VECS_STORE_WRITE_PERMISSIONS[vsphere-webclient]+=' hvc' | |
VECS_STORE_WRITE_PERMISSIONS[vpxd]='hvc' | |
VECS_STORE_WRITE_PERMISSIONS[vpxd-extension]+=' hvc' | |
VECS_STORE_WRITE_PERMISSIONS[hvc]='hvc' | |
VECS_STORE_READ_PERMISSIONS[wcp]+=' vpxd' | |
VECS_STORE_READ_PERMISSIONS[SMS]+=' vpxd' | |
else | |
VECS_STORE_WRITE_PERMISSIONS[TRUSTED_ROOTS]='' | |
VECS_STORE_WRITE_PERMISSIONS[machine]='certauth certmgr infraprofile' | |
fi | |
fi | |
;; | |
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://kb.vmware.com/s/article/78607 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 | logDebug) | |
if echo "$SDDC_API_ACCESS_TOKEN_RESPONSE" | grep '^HTTP' | grep '200' > /dev/null; 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 | logDebug) | |
if echo "$SDDC_API_PUBLISH_CA_OUTBOUND_RESPONSE" | grep '^HTTP' | grep '200' > /dev/null; then | |
statusMessage 'OK' 'GREEN' | |
else | |
logInfo "Publish CA to SDDC Manager response code (outbound trust): $SDDC_API_PUBLISH_CA_INBOUND_RESPONSE" | |
errorMessage 'Unable to publish CA certificate to SDDC Manager' | |
fi | |
if [[ "$VC_VERSION" =~ ^[78] ]] && [ $VC_BUILD -ge 17327517 ]; 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 | logDebug) | |
if echo "$SDDC_API_PUBLISH_CA_INBOUND_RESPONSE" | grep '^HTTP' | grep '200' > /dev/null; then | |
statusMessage 'OK' 'GREEN' | |
else | |
logInfo "Publish CA to SDDC Manager response code (inbound trust): $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 | |
} | |
#------------------------------ | |
# 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 cat /storage/vmware-vmon/defaultStartProfile | grep 'HACore' > /dev/null; 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 '200' > /dev/null; 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\nRestarting services may trigger a failover.\nFor more information, select option 'I' from the menu.\n\n${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 'vmware-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 '200' > /dev/null; 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 'vmware-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 '200' > /dev/null; 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${YELLOW}Invalid operation${NORMAL}\n\n" | |
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) | |
#viewCertificateReportMenu | |
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 | |
header 'Checking Certifcate 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 ]]; 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 '/var/lib/vmware/vmca/root.cer' | |
header 'Checking STS Signing Certs & Signing Chains' | |
manageSTSTenantCerts 'Check' | |
fi | |
checkCACertificates | |
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_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 | |
} | |
#------------------------------ | |
# 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_SHA1=1 | |
CHECK_SERVICE_PRINCIPAL=0 | |
CHECK_CA_CHAIN=1 | |
CHECK_EMBEDDED_CHAIN=1 | |
;; | |
SMS) | |
CHECK_PNID=0 | |
CHECK_KU=0 | |
CHECK_SAN=0 | |
CHECK_SHA1=0 | |
CHECK_SERVICE_PRINCIPAL=0 | |
CHECK_CA_CHAIN=0 | |
CHECK_EMBEDDED_CHAIN=0 | |
;; | |
*) | |
CHECK_PNID=0 | |
CHECK_KU=1 | |
CHECK_SAN=1 | |
CHECK_SHA1=1 | |
CHECK_CA_CHAIN=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 -i "$PNID" > /dev/null; 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 'Subject Alternative Name' > /dev/null; 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_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 | |
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 -iE "$UNSUPPORTED_SIGNATURE_ALGORITHMS" > /dev/null; 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 'X509v3 Key Usage' > /dev/null; 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') | |
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 'read' > /dev/null; 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 -E 'OWNER|write' > /dev/null; 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 "^$1\$" > /dev/null; 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 "$2\$" > /dev/null; 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 | |
} | |
#------------------------------ | |
# Check Reverse Proxy Smart Card signing CA certificates | |
#------------------------------ | |
function checkRhttpproxyCACCerts() { | |
CAC_FILTER_FILE=$(grep clientCAListFile /etc/vmware-rhttpproxy/config.xml | sed -e 's|<clientCAListFile>||g' -e 's|</clientCAListFile>||g' | tr -d ' ' | grep -v '^<!--') | |
if [ -n "$CAC_FILTER_FILE" ]; then | |
header 'Check Reverse Proxy Smart Card signing CA certificates' | |
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 $(ls $STAGE_DIR/rhttpproxy-ca-*); do | |
# task "Certificate $i" | |
# checkFilesystemCert "$cert" | |
# ((++i)) | |
#done | |
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=$(grep clientCAListFile /etc/vmware-rhttpproxy/config.xml | sed -e 's|<clientCAListFile>||g' -e 's|</clientCAListFile>||g' | tr -d ' ' | grep -v '^<!--') | |
if [ -z $CAC_FILTER_FILE ] && [ -f /usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem ] && [ ! -z "$(cat /usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem)" ]; then | |
CAC_FILTER_FILE='/usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem' | |
fi | |
header 'Reverse Proxy CA Certificate 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 certifcate(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 certifcate(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=$(grep '<clientCAListFile>' /etc/vmware-rhttpproxy/config.xml | awk -F'>' '{print $2}' | awk -F'<' '{print $1}') | |
if ! echo $CAC_FILTER_FILE | grep '^/' > /dev/null 2>&1; then CAC_FILTER_FILE="/etc/vmware/rhttpproxy/$CAC_FILTER_FILE"; fi | |
for cert in $(ls $STAGE_DIR/new-cac-cert-*); do | |
if ! isExpired "$cert" 'file'; then | |
task "Adding cert $(openssl x509 -noout -hash -in $cert 2>&1 | logDebug) 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=$(grep clientCAListFile /etc/vmware-rhttpproxy/config.xml | sed -e 's|<clientCAListFile>||g' -e 's|</clientCAListFile>||g' | tr -d ' ') | |
if ! echo "$CAC_FILTER_FILE" | grep '^/' > /dev/null 2>&1; then CAC_FILTER_FILE="/etc/vmware/rhttpproxy/$CAC_FILTER_FILE"; fi | |
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 $(ls $STAGE_DIR/rhttpproxy-ca-*); do | |
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 $(ls $STAGE_DIR/cac-ca-*); do | |
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 '<clientCAListFile>' /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|<!-- <clientCAListFile>|<clientCAListFile>|' -e 's|</clientCAListFile> -->|</clientCAListFile>|' -e 's|<!-- <clientCertificateMaxSize>|<clientCertificateMaxSize>|' -e 's|</clientCertificateMaxSize> -->|</clientCertificateMaxSize>|' -e '/<clientCAListFile>/i <requestClientCertificate>true</requestClientCertificate>' /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 $(ls $STAGE_DIR/sso-cac-ca-cert-*.crt); do | |
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 $(ls $STAGE_DIR/sso-cac-ca-cert-*); do | |
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 | |
header 'Removing Smart Card CA certificates from VMware Directory' | |
for index in $(echo $CAC_CAS_TO_REMOVE | tr -d ' ' | sed 's/,/ /g'); do | |
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 $(ls $STAGE_DIR/sso-cac-ca-cert-*); do | |
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 | |
done | |
fi | |
for cert in $(ls $STAGE_DIR/sso-cac-ca-cert-*); do | |
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 [ ! -z $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 [ ! -z "$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 remove (comma-separated list): ' REMOVE_LDAP_CERTS_INPUT | |
exportSSOLDAPSCerts "$source_type" "$domain" "$file_prefix" | |
for index in $(echo "$REMOVE_LDAP_CERTS_INPUT" | tr -d ' ' | sed 's/,/ /g'); do | |
to_delete_thumbprint=${LDAPS_CERT_THUMBPRINT_LIST[$((index - 1))]} | |
logInfo "Removing LDAPS certificate with thumbprint $to_delete_thumbprint" | |
for cert in $(ls $STAGE_DIR/$file_prefix*); do | |
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 | |
done | |
header 'Publish new LDAP server ceritifcates' | |
updateLDAPSCerts "$file_prefix" | |
} | |
#------------------------------ | |
# 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 $(ls $STAGE_DIR/$1*.crt); do | |
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 echo "$TEMP_CERT" | openssl x509 -text -noout 2>/dev/null | grep 'CA:TRUE' > /dev/null 2>&1; 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 echo "$TEMP_CERT" | openssl x509 -text -noout 2>/dev/null | grep 'CA:TRUE' > /dev/null 2>&1; 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' | |
LDAP_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 | grep '^userCertificate' | awk '{print $NF}') | |
TENANT_COUNT=1 | |
for hash in $LDAP_CERTS; do | |
TEMP_CERT=$(buildCertFromHash "$hash") | |
CERT_INFO=$(viewBriefCertificateInfo "$TEMP_CERT") | |
if echo "$TEMP_CERT" | openssl x509 -noout -text 2>/dev/null | grep 'CA:TRUE' > /dev/null; then | |
CERT_OUTPUT=" Certificate Type: CA Certificate"$'\n ' | |
else | |
CERT_OUTPUT=" Certificate Type: Signing Certificate"$'\n ' | |
fi | |
CERT_OUTPUT+=$CERT_INFO | |
if echo "$TEMP_CERT" | openssl x509 -text -noout 2>/dev/null | grep 'CA:TRUE' > /dev/null 2>&1; then | |
echo $'\n'"$CERT_OUTPUT"$'\n' | |
((++TENANT_COUNT)) | |
else | |
echo "Tenant Credential $TENANT_COUNT" | |
echo "$CERT_OUTPUT" | |
fi | |
done | |
} | |
#------------------------------ | |
# Check CA certificates in VMDir and VECS | |
#------------------------------ | |
function checkCACertificates() { | |
VMDIR_CERTS=() | |
VMDIR_CERT_SKIDS=() | |
VECS_CERTS=() | |
VECS_CERT_ALIASES=() | |
header 'Checking CA certificates in VMDir [by CN(id)]' | |
for skid in $($DIR_CLI trustedcert list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | awk '{print $NF}'); do | |
logInfo "Retrieving certificate with Subject Key ID $skid from VMDir" | |
$DIR_CLI trustedcert get --id $skid --outcert $STAGE_DIR/$skid.crt --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>&1 | logInfo | |
task "${skid}" | |
CA_CERT=$(cat $STAGE_DIR/$skid.crt) | |
if isExpired "$STAGE_DIR/$skid.crt" 'file'; then | |
CERT_STATUS_EXPIRED=1 | |
statusMessage 'EXPIRED' 'YELLOW' | |
elif ! isCertCA "$(cat $STAGE_DIR/$skid.crt)"; then | |
CERT_STATUS_NON_CA=1 | |
statusMessage 'NON-CA' 'YELLOW' | |
else | |
DAYS_LEFT=$(checkCertExpireSoon "$CA_CERT") | |
if ! openssl x509 -noout -text -in $STAGE_DIR/$skid.crt 2>/dev/null | grep 'Subject Key Identifier:' > /dev/null; 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) | |
task $alias | |
if isExpired "$TEMP_VECS_CERT" 'hash'; then | |
CERT_STATUS_EXPIRED=1 | |
statusMessage 'EXPIRED' 'YELLOW' | |
elif ! echo "$TEMP_VECS_CERT" | openssl x509 -text -noout 2>/dev/null | grep 'CA:TRUE' > /dev/null; then | |
CERT_STATUS_NON_CA=1 | |
statusMessage 'NON-CA' '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' | |
else | |
DAYS_LEFT=$(checkCertExpireSoon "$TEMP_VECS_CERT") | |
if [[ $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' | |
} | |
#------------------------------ | |
# 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 $(ls $STAGE_DIR/signing-ca-new-*.crt); do | |
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 "$CURRENT_SKID" > /dev/null; 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 'CA:TRUE' > /dev/null; 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://kb.vmware.com/s/article/80469' | |
echo 'on using the lsdoctor utility to recreate the missing' | |
echo "Solution User/Service Principal entries.${NORMAL}" | |
if [[ "$VC_VERSION" =~ ^[78] ]] && (echo "$MISSING_SERVICE_PRINCIPALS" | grep "wcp-$MACHINE_ID" > /dev/null || echo "$MISSING_SERVICE_PRINCIPALS" | grep "hvc-$MACHINE_ID" > /dev/null); then | |
echo $'\n'"${YELLOW}Note: The hvc and/or wcp Service Principal entries are" | |
echo 'missing, and currently lsdoctor will NOT recreate these' | |
echo 'Service Principal entries. These will need to be created' | |
echo "and assigned to the proper groups manually.${NORMAL}" | |
fi | |
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_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_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_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 is 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+=$' the following nodes to check SSL trust anchors:\n' | |
for unknown_node in "${TRUST_ANCHORS_UNKNOWN_NODES[@]}"; do | |
CERT_STATUS_MESSAGE+=" $unknown_node"$'\n' | |
done | |
fi | |
if [ $TRUST_ANCHORS_CHECK_URI == 1 ]; then | |
if [ $TRUST_ANCHORS_CHECK_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://kb.vmware.com/s/article/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://kb.vmware.com/s/article/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+=$' - Some 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 <clientCAListFile> value must reference the following 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 <clientCAListFile> file at /usr/lib/vmware-sso/vmware-sts/conf/clienttrustCA.pem 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 is missing the Subject Key ID extension' | |
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 | |
restartVMwareServices 'vmafdd' 'vmdird' 'vmcad' | |
else | |
restartVMwareServices 'vmafdd' | |
fi | |
} | |
#------------------------------ | |
# Clear BACKUP_STORE in VECS | |
#------------------------------ | |
function clearBackupStore() { | |
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 | |
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 BACKUP_STORE 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_ANCHORS_CHECK_URI=0 | |
TRUST_ANCHORS_CHECK_URI_MISMATCH=0 | |
TRUST_ANCHORS_UNKNOWN_NODES=() | |
for node in "${SSO_NODES[@]}"; do | |
MISMATCH=0 | |
task "$node" | |
NODE_MACHINE_SSL_THUMBPRINT=$(echo | openssl s_client -connect $node:443 2>/dev/null | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}') | |
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://$IP:*)(vmwLKUPURI=https://$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) | |
if [ -z "$NODE_TRUST_ANCHORS" ]; then | |
statusMessage 'MISSING' 'YELLOW' | |
else | |
TRUST_ANCHORS_CHECK_URI=1 | |
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 | |
MISMATCH=1 | |
fi | |
done | |
if [ $MISMATCH -eq 0 ]; then | |
statusMessage 'CHECK URI' 'YELLOW' | |
else | |
TRUST_ANCHORS_CHECK_URI_MISMATCH=1 | |
statusMessage 'MISMATCH*' 'YELLOW' | |
fi | |
fi | |
else | |
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 | |
MISMATCH=1 | |
TRUST_ANCHORS_MISMATCH=1 | |
fi | |
done | |
if [ $MISMATCH -eq 0 ]; then | |
statusMessage 'VALID' 'GREEN' | |
else | |
statusMessage 'MISMATCH' 'YELLOW' | |
fi | |
fi | |
else | |
TRUST_ANCHORS_UNKNOWN=1 | |
TRUST_ANCHORS_UNKNOWN_NODES+=($node) | |
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 "$YELLOW"; fi | |
echo ' 5. Authentication Proxy certifcate' | |
echo ' 6. Auto Deploy CA certificate' | |
echo ' 7. SMS certificates' | |
if [ "$VC_VERSION" == '6.5' ]; then printf "$YELLOW"; fi | |
echo ' 8. Data Encipherment certificate' | |
printf "$NORMAL" | |
if [ $NODE_TYPE = 'infrastructure' ]; then printf "$YELLOW"; fi | |
echo ' 9. vCenter Extension thumbprints' | |
printf "$NORMAL" | |
if [ $NODE_TYPE = 'management' ]; then | |
printf "$YELLOW" | |
else | |
printf "$NORMAL" | |
fi | |
echo '10. VMware Directory certificate' | |
echo '11. STS signing certificates' | |
echo '12. VMCA certificate' | |
if ! configuredForCAC; then printf "$YELLOW"; fi | |
echo '13. Smart Card CA certificates' | |
printf "$NORMAL" | |
if ! configuredForADoverLDAPS; then printf "$YELLOW"; fi | |
echo '14. LDAPS Identity Source certificates' | |
printf "$NORMAL" | |
if ! tanzuSupervisorClustersPresent || [ "$1" == 'Manage' ]; then printf "$YELLOW"; fi | |
echo '15. Tanzu Supervisor Cluster certificates' | |
printf "$NORMAL" | |
if [ "$1" == 'Manage' ]; then | |
if ! checkVECSStore 'BACKUP_STORE'; then printf "$YELLOW"; fi | |
echo '16. Clear BACKUP_STORE in VECS' | |
printf "$NORMAL" | |
echo '17. Clear TRUSTED_ROOT_CRLS store in VECS' | |
if ! checkMachineSSLCSR; then printf "$YELLOW"; fi | |
echo '18. Clear Machine SSL CSR in VECS' | |
printf "$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 'vmwSTSAuthnTypes: 4' > /dev/null; 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 '/var/lib/vmware/vmca/root.cer' | |
;; | |
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 | |
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${YELLOW}This operation must be done on the vCenter Server.${NORMAL}\n\n" | |
fi | |
;; | |
6) | |
if [ $NODE_TYPE != 'infrastructure' ]; then | |
if promptReplaceAutoDeployCA; then | |
if replaceAutoDeployCACert; then promptRestartVMwareServices 'vmware-rbd-watchdog'; fi | |
clearCSRInfo | |
fi | |
else | |
printf "\n${YELLOW}This operation must be done on the vCenter Server.${NORMAL}\n\n" | |
fi | |
;; | |
7) | |
manageSMSCertificates 'Manage' | |
;; | |
8) | |
replaceDataEnciphermentCertificate | |
;; | |
9) | |
if [ $NODE_TYPE != 'infrastructure' ]; then | |
manageVCExtensionThumbprints 'Check' | |
else | |
printf "\n${YELLOW}This operation must be done on the vCenter Server.${NORMAL}\n\n" | |
fi | |
;; | |
10) | |
if [[ "$VC_VERSION" =~ ^[78] ]]; then | |
if [ -f /usr/lib/vmware-vmdir/share/config/vmdircert.pem ]; then | |
removeVMDirCert | |
else | |
printf "\n${YELLOW}This operation is not available for vCenter 7.x or later${NORMAL}\n\n" | |
fi | |
elif [ $NODE_TYPE != 'management' ]; then | |
if promptReplaceVMDir; then replaceVMDirCert; fi | |
else | |
printf "\n${YELLOW}This operation must be done on the Platform Services Controller${NORMAL}\n\n" | |
fi | |
;; | |
11) | |
if [ $NODE_TYPE != 'management' ]; then | |
manageSTSTenantCerts 'Replace' | |
else | |
printf "\n${YELLOW}This operation must be done on the Platform Services Controller${NORMAL}\n\n" | |
fi | |
;; | |
12) | |
if [ $NODE_TYPE != 'management' ]; then | |
if promptReplaceVMCA; then | |
if replaceVMCACert && [ "$VMCA_REGENERATE_CERTIFICATES" == '1' ]; then resetAllCertificates; fi | |
fi | |
else | |
printf "\n${YELLOW}This operation must be done on the Platform Services Controller${NORMAL}\n\n" | |
fi | |
;; | |
13) | |
manageCACCerts 'Manage' | |
;; | |
14) | |
manageLDAPSCerts 'Manage' | |
;; | |
15) | |
#manageTanzuSupervisorClusterCerts 'Manage' | |
;; | |
16) | |
if checkVECSStore 'BACKUP_STORE'; 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 --text | grep 'Alias' | tr -d '\t' | awk -F':' '{print $NF}'); 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 'BEGIN CERTIFICATE' > /dev/null; then | |
reportCRLDetails "$VECS_HASH" | |
else | |
case $store-$alias in | |
MACHINE_SSL_CERT-__MACHINE_CERT) | |
EXTRA_INFO='checkCurrentMachineSSLUsage' | |
;; | |
vpxd-extension-vpxd-extension) | |
EXTRA_INFO='checkCurrentExtensionThumbprints' | |
;; | |
*) | |
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: /var/lib/vmware/vmca/root.cer' | tee -a $VC_REPORT | |
reportCertDetails "$(cat /var/lib/vmware/vmca/root.cer)" | |
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 '<clientCAListFile>' /etc/vmware-rhttpproxy/config.xml | grep -v '<!--' > /dev/null; then | |
echo ' Smart Card Whitelist Certificates' | tee -a $VC_REPORT | |
CAC_FILTER_FILE=$(grep '<clientCAListFile>' /etc/vmware-rhttpproxy/config.xml | grep -v '<!--' | awk -F'>' '{print $2}' | awk -F'<' '{print $1}') | |
csplit -s -z -f $STAGE_DIR/cac_whitelist_ca- -b %02d.crt $CAC_FILTER_FILE '/-----BEGIN CERTIFICATE-----/' '{*}' | |
WHITELIST_CERT_COUNT=1 | |
for cert in $(ls $STAGE_DIR/cac_whitelist_ca-*); do | |
echo " Certificate $WHITELIST_CERT_COUNT: $CAC_FILTER_FILE" | tee -a $VC_REPORT | |
reportCertDetails "$(cat $cert)" | |
((++WHITELIST_CERT_COUNT)) | |
done | |
fi | |
if tanzuSupervisorClustersPresent; then | |
echo 'Tanzu Supervisor Cluster Certificates' | tee -a $VC_REPORT | |
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" | tee -a $VC_REPORT | |
ssh-keygen -R $TANZU_CLUSTER_IP 2>&1 | logDebug | |
TANZU_CLUSTER_CERTIFICATES=$(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 "Certificate: $cert"; hash=$(openssl x509 -in $cert | grep -v '^-----' | tr -d "\n"); echo "Hash: $hash"; done') | |
for line2 in $TANZU_CLUSTER_CERTIFICATES; do | |
if [[ "$line2" =~ ^Certificate ]]; then | |
echo " $line2" | |
else | |
TANZU_CERT_HASH=$(echo "$line2" | awk '{print $NF}') | |
TEMP_CERT=$(buildCertFromHash "$TANZU_CERT_HASH") | |
reportCertDetails "$TEMP_CERT" | |
fi | |
done | |
fi | |
done | |
IFS=$' \t\n' | |
fi | |
echo 'Lookup Service Registration Trust Anchors' | tee -a $VC_REPORT | |
getSSLTrustAnchorHashes | |
for hash in "${CERT_HASHES[@]}"; do | |
echo " Endpoint Certificate $CERT_COUNT" | tee -a $VC_REPORT | |
TEMP_CERT=$(buildCertFromHash "$hash") | |
double_encoded_hash=$(echo "$hash" | tr -d '\n' | sed -e 's/.\{76\}/&\r\n/g' | xargs -0 printf "%s\r\n" | base64 -w 0) | |
USED_BY_SERVICE_IDS=$(getSSLTrustAnchorServiceIds "$hash" "$double_encoded_hash") | |
NUM_USED_BY_SERVICE_IDS=$(echo "$USED_BY_SERVICE_IDS" | grep -v '^$' | wc -l) | |
USED_BY_ENDPOINTS=$(getSSLTrustAnchorEndpoints "$hash" "$double_encoded_hash") | |
NUM_USED_BY_ENDPOINTS=$(echo "$USED_BY_ENDPOINTS" | grep -v '^$' | wc -l) | |
((++CERT_COUNT)) | |
reportTrustAnchorDetails "$TEMP_CERT" "$USED_BY_SERVICE_IDS" "$USED_BY_ENDPOINTS" | |
done | |
enableColor | |
echo $'\n'"${YELLOW}Certificate report is available at ${VC_REPORT}${NORMAL}"$'\n' | |
} | |
#------------------------------ | |
# Generate ESXi certificate report | |
#------------------------------ | |
#function generateESXiCertificateReport() { | |
# | |
#} | |
#------------------------------ | |
# CRL information for report | |
#------------------------------ | |
function reportCRLDetails() { | |
REPORT_CRL=$1 | |
REPORT_CRL_INFO=$(viewCRLInfo "$REPORT_CRL") | |
REPORT_CRL_ISSUER=$(echo "$REPORT_CRL_INFO" | grep 'Issuer:' | awk -F'Issuer: ' '{print $NF}') | |
REPORT_CRL_LAST_UPDATE=$(echo "$REPORT_CRL" | openssl crl -noout -lastupdate 2>/dev/null | sed 's/lastUpdate=//') | |
REPORT_CRL_NEXT_UPDATE=$(echo "$REPORT_CRL" | openssl crl -noout -nextupdate 2>/dev/null | sed 's/nextUpdate=//') | |
REPORT_CRL_SIGNATURE_ALGORITHM=$(echo "$REPORT_CRL_INFO" | grep 'Signature Algorithm' | head -n1 | awk '{print $NF}') | |
REPORT_CRL_AUTH_KEYS=$(echo "$REPORT_CRL_INFO" | grep 'Authority Key Identifier' -A3 | grep -E 'keyid:|DirName:|issuer:' | tr -d ' ') | |
echo " Issuer: $REPORT_CRL_ISSUER" | tee -a $VC_REPORT | |
echo " Last Update: $REPORT_CRL_LAST_UPDATE" | tee -a $VC_REPORT | |
echo " Next Update: $REPORT_CRL_NEXT_UPDATE" | tee -a $VC_REPORT | |
echo " Signature Algorithm: $REPORT_CRL_SIGNATURE_ALGORITHM" | tee -a $VC_REPORT | |
} | |
#------------------------------ | |
# Certificate information for report | |
#------------------------------ | |
function reportCertDetails() { | |
ISSUER_FOUND_VMDIR=0 | |
ISSUER_FOUND_VECS=0 | |
REPORT_CERT=${1} | |
if isCertCA "$REPORT_CERT"; then REPORT_CERT_IS_CA='Yes'; else REPORT_CERT_IS_CA='No'; fi | |
REPORT_CERT_INFO=$(viewCertificateInfo "$REPORT_CERT") | |
REPORT_CERT_SUBJECT=$(echo "$REPORT_CERT_INFO" | grep 'Subject:' | awk -F'Subject: ' '{print $NF}') | |
REPORT_CERT_ISSUER=$(echo "$REPORT_CERT_INFO" | grep 'Issuer:' | awk -F'Issuer: ' '{print $NF}') | |
REPORT_CERT_VALID_START=$(echo "$REPORT_CERT" | openssl x509 -noout -startdate 2>/dev/null | sed 's/notBefore=//') | |
REPORT_CERT_VALID_END=$(echo "$REPORT_CERT" | openssl x509 -noout -enddate 2>/dev/null | sed 's/notAfter=//') | |
REPORT_CERT_FINGERPRINT=$(echo "$REPORT_CERT" | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $2}') | |
REPORT_CERT_SIGNATURE_ALGORITHM=$(echo "$REPORT_CERT_INFO" | grep 'Signature Algorithm' | head -n1 | awk '{print $NF}') | |
REPORT_CERT_SUBJECT_KEY=$(echo "$REPORT_CERT_INFO" | grep 'Subject Key Identifier:' -A1 | tail -n1 | tr -d ' ') | |
REPORT_CERT_AUTH_KEYS=$(echo "$REPORT_CERT_INFO" | grep 'Authority Key Identifier' -A3 | grep -E 'keyid:|DirName:|issuer:' | tr -d ' ') | |
REPORT_CERT_KEY_USAGE=$(echo "$REPORT_CERT_INFO" | grep 'X509v3 Key Usage' -A1 | tail -n1 | sed -e 's/^ *//g' -e 's/, /\n/g' | grep -v '^$') | |
REPORT_CERT_KEY_EXT_USAGE=$(echo "$REPORT_CERT_INFO" | grep 'X509v3 Extended Key Usage' -A1 | tail -n1 | sed -e 's/^ *//g' -e 's/, /\n/g' | grep -v '^$') | |
REPORT_CERT_SAN=$(echo "$REPORT_CERT_INFO" | grep 'X509v3 Subject Alternative Name' -A1 | tail -n1 | sed -e 's/^ *//g' -e 's/, /\n/g' | grep -v '^$' | sort) | |
echo " Issuer: $REPORT_CERT_ISSUER" | tee -a $VC_REPORT | |
echo " Subject: $REPORT_CERT_SUBJECT" | tee -a $VC_REPORT | |
echo " Not Before: $REPORT_CERT_VALID_START" | tee -a $VC_REPORT | |
echo " Not After : $REPORT_CERT_VALID_END" | tee -a $VC_REPORT | |
echo " SHA1 Fingerprint : $REPORT_CERT_FINGERPRINT" | tee -a $VC_REPORT | |
echo " Signature Algorithm: $REPORT_CERT_SIGNATURE_ALGORITHM" | tee -a $VC_REPORT | |
echo " Subject Key Identifier: $REPORT_CERT_SUBJECT_KEY" | tee -a $VC_REPORT | |
if [ -n "$REPORT_CERT_AUTH_KEYS" ]; then | |
echo ' Authority Key Identifier:' | tee -a $VC_REPORT | |
IFS=$'\n' | |
for auth_key in $(echo "$REPORT_CERT_AUTH_KEYS"); do | |
echo " |_$auth_key" | tee -a $VC_REPORT | |
if echo "$VMDIR_CA_SUBJECT_IDS" | grep "$auth_key" > /dev/null; then ISSUER_FOUND_VMDIR=1; fi | |
if echo "$VECS_CA_SUBJECT_IDS" | grep "$auth_key" > /dev/null; then ISSUER_FOUND_VECS=1; fi | |
done | |
IFS=$' \t\n' | |
fi | |
if [[ $ISSUER_FOUND_VMDIR -eq 0 && $ISSUER_FOUND_VECS -eq 0 ]]; then | |
if [[ "$REPORT_CERT_SUBJECT" == "$REPORT_CERT_ISSUER" ]]; then | |
REPORT_CERT_ISSUER_FOUND='No (Self-Signed)' | |
else | |
REPORT_CERT_ISSUER_FOUND='No' | |
fi | |
elif [[ $ISSUER_FOUND_VMDIR -eq 1 && $ISSUER_FOUND_VECS -eq 0 ]]; then | |
REPORT_CERT_ISSUER_FOUND='Yes, in VMware Directory' | |
elif [[ $ISSUER_FOUND_VMDIR -eq 0 && $ISSUER_FOUND_VECS -eq 1 ]]; then | |
REPORT_CERT_ISSUER_FOUND='Yes, in VECS' | |
else | |
REPORT_CERT_ISSUER_FOUND='Yes, in both' | |
fi | |
echo ' Key Usage:' | tee -a $VC_REPORT | |
if [ -n "$REPORT_CERT_KEY_USAGE" ]; then | |
IFS=$'\n' | |
for key_usage in $(echo "$REPORT_CERT_KEY_USAGE"); do | |
echo " |_$key_usage" | tee -a $VC_REPORT | |
done | |
IFS=$' \t\n' | |
fi | |
echo ' Extended Key Usage:' | tee -a $VC_REPORT | |
if [ -n "$REPORT_CERT_KEY_EXT_USAGE" ]; then | |
IFS=$'\n' | |
for ext_key_usage in $(echo "$REPORT_CERT_KEY_EXT_USAGE"); do | |
echo " |_$ext_key_usage" | tee -a $VC_REPORT | |
done | |
IFS=$' \t\n' | |
fi | |
echo ' Subject Alternative Name entries:' | tee -a $VC_REPORT | |
if [ -n "$REPORT_CERT_SAN" ]; then | |
IFS=$'\n' | |
for san in $(echo "$REPORT_CERT_SAN"); do | |
echo " |_$san" | tee -a $VC_REPORT | |
done | |
IFS=$' \t\n' | |
fi | |
echo ' Other Information:' | tee -a $VC_REPORT | |
echo " |_Is a Certificate Authority: $REPORT_CERT_IS_CA" | tee -a $VC_REPORT | |
echo " |_Issuing CA in VMware Directory/VECS: $REPORT_CERT_ISSUER_FOUND" | tee -a $VC_REPORT | |
if [ -n "$2" ]; then | |
CUSTOM_INFO=$(echo "$2" | tr '|' '\n') | |
IFS=$'\n' | |
for custom_call in $CUSTOM_INFO; do | |
FUNCTION_STRING=$(echo "$custom_call" | tr ':' ' ') | |
eval $FUNCTION_STRING | |
done | |
IFS=$' \t\n' | |
fi | |
} | |
#------------------------------ | |
# Trust Anchor information for report | |
#------------------------------ | |
function reportTrustAnchorDetails() { | |
TRUST_ANCHOR_CERT="$1" | |
SERVICE_IDS="$2" | |
ENDPOINTS="$3" | |
TRUST_ANCHOR_INFO=$(viewCertificateInfo "$TRUST_ANCHOR_CERT") | |
TRUST_ANCHOR_SUBJECT=$(echo "$TRUST_ANCHOR_INFO" | grep 'Subject:' | awk -F'Subject: ' '{print $NF}') | |
TRUST_ANCHOR_ISSUER=$(echo "$TRUST_ANCHOR_INFO" | grep 'Issuer:' | awk -F'Issuer: ' '{print $NF}') | |
TRUST_ANCHOR_VALID_START=$(echo "$TRUST_ANCHOR_CERT" | openssl x509 -noout -startdate 2>/dev/null | sed 's/notBefore=//') | |
TRUST_ANCHOR_VALID_END=$(echo "$TRUST_ANCHOR_CERT" | openssl x509 -noout -enddate 2>/dev/null | sed 's/notAfter=//') | |
TRUST_ANCHOR_FINGERPRINT=$(echo "$TRUST_ANCHOR_CERT" | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $2}') | |
echo " Issuer: $TRUST_ANCHOR_ISSUER" | tee -a $VC_REPORT | |
echo " Subject: $TRUST_ANCHOR_SUBJECT" | tee -a $VC_REPORT | |
echo " Not Before: $TRUST_ANCHOR_VALID_START" | tee -a $VC_REPORT | |
echo " Not After : $TRUST_ANCHOR_VALID_END" | tee -a $VC_REPORT | |
echo " SHA1 Fingerprint: $TRUST_ANCHOR_FINGERPRINT" | tee -a $VC_REPORT | |
echo ' Service IDs:' | tee -a $VC_REPORT | |
for service in $SERVICE_IDS; do | |
echo " |_$service" | tee -a $VC_REPORT | |
done | |
echo ' Endpoints:' | tee -a $VC_REPORT | |
for endpoint in $ENDPOINTS; do | |
echo " |_$endpoint" | tee -a $VC_REPORT | |
done | |
return 0 | |
} | |
#------------------------------ | |
# Extra information regarding the Machine SSL certificate | |
#------------------------------ | |
function checkCurrentMachineSSLUsage() { | |
RHTTPPROXY_CERT_FINGERPRINT=$(echo | openssl s_client -connect localhost:443 2>/dev/null | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}') | |
VPXD_CERT_FINGERPRINT=$(echo | openssl s_client -connect localhost:8089 2>/dev/null | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}') | |
echo " |_Current certificate used by the reverse proxy: $RHTTPPROXY_CERT_FINGERPRINT" | tee -a $VC_REPORT | |
echo " |_Current certificate used by vCenter (vpxd) : $VPXD_CERT_FINGERPRINT" | tee -a $VC_REPORT | |
} | |
#------------------------------ | |
# Extra information regarding the vpxd-extension certificate | |
#------------------------------ | |
function checkCurrentExtensionThumbprints() { | |
EAM_EXT_FINGERPRINT=$($PSQL -d VCDB -U postgres -c "SELECT thumbprint FROM vpx_ext WHERE ext_id = 'com.vmware.vim.eam'" -t | grep -v '^$' | tr -d ' ') | |
RBD_EXT_FINGERPRINT=$($PSQL -d VCDB -U postgres -c "SELECT thumbprint FROM vpx_ext WHERE ext_id = 'com.vmware.rbd'" -t | grep -v '^$' | tr -d ' ') | |
VUM_EXT_FINGERPRINT=$($PSQL -d VCDB -U postgres -c "SELECT thumbprint FROM vpx_ext WHERE ext_id = 'com.vmware.vcIntegrity'" -t | grep -v '^$' | tr -d ' ') | |
VLCM_CLIENT_EXT_FINGERPRINT=$($PSQL -d VCDB -U postgres -c "SELECT thumbprint FROM vpx_ext WHERE ext_id = 'com.vmware.vlcm.client'" -t | grep -v '^$' | tr -d ' ') | |
IMAGE_BUILDER_EXT_FINGERPRINT=$($PSQL -d VCDB -U postgres -c "SELECT thumbprint FROM vpx_ext WHERE ext_id = 'com.vmware.imagebuilder'" -t | grep -v '^$' | tr -d ' ') | |
echo ' |_Thumbprints in VCDB for extensions that should use the vpxd-extension certificate' | tee -a ${REPORT} | |
echo " |_com.vmware.vim.eam : $EAM_EXT_FINGERPRINT" | tee -a $VC_REPORT | |
echo " |_com.vmware.vcIntegrity : $VUM_EXT_FINGERPRINT" | tee -a $VC_REPORT | |
if [ -n "$RBD_EXT_FINGERPRINT" ]; then | |
echo " |_com.vmware.rbd : $RBD_EXT_FINGERPRINT" | tee -a $VC_REPORT | |
fi | |
if [ -n "$IMAGE_BUILDER_EXT_FINGERPRINT" ]; then | |
echo " |_com.vmware.imagebuilder: $IMAGE_BUILDER_EXT_FINGERPRINT" | tee -a $VC_REPORT | |
fi | |
if [[ "$VC_VERSION" =~ ^8 ]]; then | |
echo " |_com.vmware.vlcm.client : $VLCM_CLIENT_EXT_FINGERPRINT" | tee -a $VC_REPORT | |
fi | |
} | |
#------------------------------ | |
# View VECS certificate info | |
#------------------------------ | |
function viewVECSCertificateInfo() { | |
logInfo "Viewing certificate in VECS store $1 with alias $2" | |
CERT=$($VECS_CLI entry getcert --store $1 --alias $2 2>/dev/null) | |
if [ -n "$CERT" ]; then | |
logDetails "$CERT" | |
viewCertificateInfo "$CERT" 'view-path' | |
else | |
logInfo "Unable to view the certificate in VECS store $1 with alias $2" | |
echo $'\n'"${YELLOW}Unable to view the $1 certificate.${NORMAL}" | |
fi | |
} | |
#------------------------------ | |
# View certificate info from a file | |
#------------------------------ | |
function viewFilesystemCertificateInfo() { | |
logInfo "Viewing certificate at $1" | |
CERT=$(cat $1 2>/dev/null) | |
if [ -n "$CERT" ]; then | |
logDetails "$CERT" | |
viewCertificateInfo "$CERT" 'view-path' | |
else | |
logInfo "Unable to view the certificate at $1" | |
echo $'\n'"${YELLOW}Unable to view certificate at $1.${NORMAL}" | |
fi | |
} | |
#------------------------------ | |
# View certificate info from a remote service | |
#------------------------------ | |
function viewRemoteCertificateInfo() { | |
logInfo "Viewing certificate at $1:$2" | |
CERT=$(echo | openssl s_client -connect $1:$2 2>/dev/null | openssl x509 2>/dev/null) | |
if [ -n "$CERT" ]; then | |
logDetails "$CERT" | |
viewCertificateInfo "$CERT" 'view-path' | |
else | |
logInfo "Unable to view certificate at $1:$2" | |
echo $'\n'"${YELLOW}Unable to view certificate from $1:$2.${NORMAL}" | |
fi | |
} | |
#------------------------------ | |
# Manage VMDir CA certificates | |
#------------------------------ | |
function manageVMDirCACertificates() { | |
authenticateIfNeeded | |
header 'CA Certificates in VMware Directory' | |
listVMDirCACertificates | |
case $1 in | |
'View') | |
read -p $'\nSelect certificate [Return to Main Menu]: ' VIEW_VMDIR_CA_CERT_INPUT | |
if [ -n "$VIEW_VMDIR_CA_CERT_INPUT" ]; then | |
viewVMDirCACertificate "$VIEW_VMDIR_CA_CERT_INPUT" | |
fi | |
;; | |
'Manage') | |
header 'Manage Certificates in VMware Directory' | |
echo ' 1. Publish CA certificate(s) to VMware Directory' | |
echo ' 2. Remove CA certificate(s) from VMware Directory' | |
if [ -n "$SDDC_MANAGER" ]; then echo ' 3. Publish CA certificate(s) to SDDC Manager'; fi | |
read -p $'\nSelect an option [Return to Main Menu]: ' MANAGE_VMDIR_CA_CERT_INPUT | |
case $MANAGE_VMDIR_CA_CERT_INPUT in | |
1) | |
publishCACertsVMDir | |
;; | |
2) | |
removeCACertsVMDir | |
;; | |
3) | |
if [ -n "$SDDC_MANAGER" ]; then | |
publishVMDirCACertSDDCManager | |
else | |
echo $'\n'"${YELLOW}Invalid option.${NORMAL}" | |
fi | |
;; | |
esac | |
;; | |
esac | |
} | |
#------------------------------ | |
# Add new STS signing certificate | |
#------------------------------ | |
function replaceSSOSTSCert() { | |
if [ $STS_REPLACE == 'VMCA-SIGNED' ]; then | |
STS_CERT=$STAGE_DIR/sso-sts.crt | |
STS_PUBKEY=$STAGE_DIR/sso-sts.pub | |
STS_KEY=$STAGE_DIR/sso-sts.key | |
header 'Replace SSO STS Signing Certificate' | |
generateCertoolConfig 'sts' 'ssoserverSign' | |
task 'Regenerate STS signing certificate' | |
regenerateVMCASignedCertificate 'sso-sts' | |
statusMessage 'OK' 'GREEN' | |
TRUSTED_ROOT_CHAIN=$VMCA_CERT | |
else | |
unset STS_CA_SIGNED_OPTION_INPUT | |
echo $'\n1. Generate Certificate Signing Request and Private Key' | |
echo '2. Generate Certificate Signing Request and Private Key' | |
echo ' from custom OpenSSL configuration file' | |
echo '3. Import CA-signed Certificate and Key' | |
read -p $'\nSelect an option [Return to Main Menu]: ' STS_CA_SIGNED_OPTION_INPUT | |
if [ -z $STS_CA_SIGNED_OPTION_INPUT ]; then return 1; fi | |
if [ "$STS_CA_SIGNED_OPTION_INPUT" == '3' ]; then | |
logInfo 'User has chosen to import a CA-signed STS Signing certificate and key' | |
read -e -p $'\n'"Provide path to the CA-signed ${CYAN}Machine SSL${NORMAL} certificate: " STS_CERT_INPUT | |
while [ ! -f "$STS_CERT_INPUT" ]; do read -e -p "${YELLOW}File not found, please provide path to the STS Signing certificate:${NORMAL} " STS_CERT_INPUT; done | |
getCorrectCertFormat "$STS_CERT_INPUT" 'STS_CERT' | |
STS_CERT_MODULUS_HASH=$(openssl x509 -noout -modulus -in $STS_CERT 2>/dev/null | md5sum | awk '{print $1}') | |
getPrivateKey "$STS_CERT_MODULUS_HASH" "STS" 'STS Signing' | |
getCAChain "$STS_CERT" | |
header 'Certificate Verification' | |
task 'Verifying certificate and key' | |
logDebug "Using STS Signing cert: $STS_CERT" | |
logDebug "Using Private Key: $STS_KEY" | |
logDebug "Using trusted root chain: $TRUSTED_ROOT_CHAIN" | |
verifyCertAndKey "$STS_CERT" "$STS_KEY" | |
statusMessage 'OK' 'GREEN' | |
task 'Verifying root chain' | |
verifyRootChain $STS_CERT $TRUSTED_ROOT_CHAIN || errorMessage 'Certificate Authority chain is not complete' | |
statusMessage 'OK' 'GREEN' | |
header 'Replace SSO STS Signing Certificate' | |
task 'Publish CA signing certificates' | |
publishCASigningCertificates $TRUSTED_ROOT_CHAIN | |
else | |
unset STS_CN_INPUT | |
STS_CSR=$REQUEST_DIR/sso-sts-$TIMESTAMP.csr | |
STS_KEY=$REQUEST_DIR/sso-sts-$TIMESTAMP.key | |
if [ "$STS_CA_SIGNED_OPTION_INPUT" == '1' ]; then | |
STS_CFG=$REQUEST_DIR/sso-sts.cfg | |
logInfo 'User has chosen to generate the STS Signing private key and CSR' | |
if [ -z "$CSR_COUNTRY" ]; then getCSRInfo; fi | |
read -p "Enter a value for the ${CYAN}CommonName${NORMAL} of the certificate [ssoserver]: " STS_CN_INPUT | |
if [ -z "$STS_CN_INPUT" ]; then STS_CN_INPUT='ssoserver'; fi | |
checkPSCHA | |
defineSANEntries 'sso-sts' 'STS Signing' | |
generateOpensslConfig $STS_CN_INPUT $STS_CFG 'sso-sts' | |
else | |
logInfo 'User has chosen to generate the STS Signing private key and CSR from a custom OpenSSL configuration file' | |
read -e -p $'\nEnter path to custom OpenSSL configuration file: ' STS_CFG | |
while [ ! -f "$STS_CFG" ]; do read -e -p "${YELLOW}File not found, enter path to custom OpenSSL configuration file:${NORMAL} " STS_CFG; done | |
fi | |
generateCSR $STS_CSR $STS_KEY "$STS_CFG" || errorMessage "Unable to generate Certificate Signing Request and Private Key" | |
printf "\nCertificate Signing Request generated at ${CYAN}${STS_CSR}${NORMAL}" | |
printf "\nPrivate Key generated at ${CYAN}${STS_KEY}${NORMAL}\n" | |
return 0 | |
fi | |
fi | |
task 'Backup and delete tenant credentials' | |
TENANT_CREDS=$($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)' dn | sed 's/^dn: //g') | |
i=1 | |
for credential in $TENANT_CREDS; do | |
logInfo "Backing up credential $credential" | |
$LDAP_SEARCH -h $PSC_LOCATION -p $VMDIR_PORT -D "cn=${VMDIR_USER},cn=users,${VMDIR_DOMAIN_DN}" -y $STAGE_DIR/.vmdir-user-password -b "$credential" 2>/dev/null > $BACKUP_DIR/TenantCredential-$i.ldif | |
logInfo "Deleting credential $credential" | |
$LDAP_DELETE -h $PSC_LOCATION -p $VMDIR_PORT -D "cn=${VMDIR_USER},cn=users,${VMDIR_DOMAIN_DN}" -y $STAGE_DIR/.vmdir-user-password "$credential" 2>&1 | logDebug | |
((i++)) | |
done | |
statusMessage 'OK' 'GREEN' | |
task 'Backup and delete trusted cert chains' | |
TRUSTED_CHAINS=$($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=vmwSTSTenantTrustedCertificateChain)' dn | sed 's/^dn: //g') | |
i=1 | |
for chain in $TRUSTED_CHAINS; do | |
logInfo "Backing up chain $chain" | |
$LDAP_SEARCH -h $PSC_LOCATION -p $VMDIR_PORT -D "cn=${VMDIR_USER},cn=users,${VMDIR_DOMAIN_DN}" -y $STAGE_DIR/.vmdir-user-password -b "$chain" 2>/dev/null > $BACKUP_DIR/TrustedCertChain-$i.ldif | |
logInfo "Deleting chain $chain" | |
$LDAP_DELETE -h $PSC_LOCATION -p $VMDIR_PORT -D "cn=${VMDIR_USER},cn=users,${VMDIR_DOMAIN_DN}" -y $STAGE_DIR/.vmdir-user-password "$chain" 2>&1 | logDebug | |
((i++)) | |
done | |
statusMessage 'OK' 'GREEN' | |
task 'Add new STS signing certifcate to VMDir' | |
openssl x509 -outform der -in $STS_CERT -out $STAGE_DIR/sso-sts.der 2>/dev/null || errorMessage 'Unable to create binary SSO STS certificate' | |
openssl pkcs8 -topk8 -inform pem -outform der -in $STS_KEY -out $STAGE_DIR/sso-sts.key.der -nocrypt 2>/dev/null || errorMessage 'Unable to create binary SSO STS private key' | |
STS_CA_CERTS=$(csplit -z -f $STAGE_DIR/sts-ca-cert- -b %02d.crt $TRUSTED_ROOT_CHAIN '/-----BEGIN CERTIFICATE-----/' '{*}' | wc -l) | |
i=0 | |
until [ $i -eq $STS_CA_CERTS ]; do | |
openssl x509 -outform der -in $STAGE_DIR/sts-ca-cert-0$i.crt -out $STAGE_DIR/sts-ca-cert-0$i.der 2>&1 | logDebug | |
((i++)) | |
done | |
echo "dn: cn=TenantCredential-1,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" > $STAGE_DIR/sso-sts.ldif | |
echo 'changetype: add' >> $STAGE_DIR/sso-sts.ldif | |
echo 'objectClass: vmwSTSTenantCredential' >> $STAGE_DIR/sso-sts.ldif | |
echo 'objectClass: top' >> $STAGE_DIR/sso-sts.ldif | |
echo 'cn: TenantCredential-1' >> $STAGE_DIR/sso-sts.ldif | |
echo "userCertificate:< file://$STAGE_DIR/sso-sts.der" >> $STAGE_DIR/sso-sts.ldif | |
i=0 | |
until [ $i -eq $STS_CA_CERTS ]; do | |
echo "userCertificate:< file:$STAGE_DIR/sts-ca-cert-0${i}.der" >> $STAGE_DIR/sso-sts.ldif | |
((i++)) | |
done | |
echo "vmwSTSPrivateKey:< file://$STAGE_DIR/sso-sts.key.der" >> $STAGE_DIR/sso-sts.ldif | |
echo '' >> $STAGE_DIR/sso-sts.ldif | |
echo "dn: cn=TrustedCertChain-1,cn=TrustedCertificateChains,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN" >> $STAGE_DIR/sso-sts.ldif | |
echo 'changetype: add' >> $STAGE_DIR/sso-sts.ldif | |
echo 'objectClass: vmwSTSTenantTrustedCertificateChain' >> $STAGE_DIR/sso-sts.ldif | |
echo 'objectClass: top' >> $STAGE_DIR/sso-sts.ldif | |
echo 'cn: TrustedCertChain-1' >> $STAGE_DIR/sso-sts.ldif | |
echo "userCertificate:< file://$STAGE_DIR/sso-sts.der" >> $STAGE_DIR/sso-sts.ldif | |
i=0 | |
until [ $i -eq $STS_CA_CERTS ]; do | |
echo "userCertificate:< file:$STAGE_DIR/sts-ca-cert-0$i.der" >> $STAGE_DIR/sso-sts.ldif | |
((i++)) | |
done | |
$LDAP_MODIFY -v -h $PSC_LOCATION -p $VMDIR_PORT -D "$VMDIR_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.machine-account-password -f $STAGE_DIR/sso-sts.ldif 2>&1 | logDebug | |
statusMessage 'OK' 'GREEN' | |
} | |
#------------------------------ | |
# Remove certificates from VMDir | |
#------------------------------ | |
function removeCACertsVMDir() { | |
read -p $'\nEnter the number(s) of the certificate(s) to delete (multiple entries separated by a comma): ' DELETE_VMDIR_CA_LIST | |
if [ -n "$DELETE_VMDIR_CA_LIST" ]; then | |
header 'Removing CA certificates from VMware Directory' | |
for index in $(echo "$DELETE_VMDIR_CA_LIST" | tr -d ' ' | sed 's/,/ /g'); do | |
skid=${VMDIR_CA_CERT_SKIDS[$((index - 1))]} | |
task "Backup $skid" | |
if $DIR_CLI trustedcert get --id $skid --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" --outcert $BACKUP_DIR/$skid.crt 2>&1 | logInfo; then | |
statusMessage 'OK' 'GREEN' | |
else | |
errorMessage "Unable to backup certificate with Subject Key ID $skid" 'backup' | |
fi | |
task "Remove $skid" | |
$DIR_CLI trustedcert unpublish --cert $BACKUP_DIR/$skid.crt --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>&1 | logInfo | |
if [ $? -eq 0 ]; then | |
statusMessage 'OK' 'GREEN' | |
else | |
statusMessage 'FAILED' 'YELLOW' | |
task "Remove $skid directly" | |
$LDAP_DELETE -h $PSC_LOCATION -p $VMDIR_PORT -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password "cn=$skid,cn=Certificate-Authorities,cn=Configuration,$VMDIR_DOMAIN_DN" 2>&1 | logDebug || errorMessage "Unable to delete certificate with Subject Key ID $skid from VMware Directory" | |
statusMessage 'OK' 'GREEN' | |
fi | |
done | |
task 'Refreshing CA certificates to VECS' | |
$VECS_CLI force-refresh 2>/dev/null || errorMessage 'Error refreshing CA certificates to VECS' | |
statusMessage 'OK' 'GREEN' | |
fi | |
} | |
#------------------------------ | |
# Publish CA certificate(s) from VMDir to SDDC Manager | |
#------------------------------ | |
function publishVMDirCACertSDDCManager() { | |
read -p $'\n'"Enter the number(s) of the certificate(s) to publish to $SDDC_MANAGER (multiple entries separated by a comma): " PUBLISH_VMDIR_CA_SDDC_LIST | |
if [ -n "$PUBLISH_VMDIR_CA_SDDC_LIST" ]; then | |
header 'Publishing CA certificates to SDDC Manager' | |
getSDDCAccessToken | |
for index in $(echo "$PUBLISH_VMDIR_CA_SDDC_LIST" | tr -d ' ' | sed 's/,/ /g'); do | |
skid=${VMDIR_CA_CERT_SKIDS[$((index - 1))]} | |
task 'Export cert from VMware Directory' | |
$DIR_CLI trustedcert get --id $skid --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" --outcert $STAGE_DIR/sddc_$skid.crt 2>&1 | logInfo || errorMessage "Unable to backup certificate with Subject Key ID $skid" 'backup' | |
statusMessage 'OK' 'GREEN' | |
publishCACertsSDDCManager "$STAGE_DIR/sddc_$skid.crt" | |
done | |
echo $'\n'"${YELLOW}Services will need to be restarted on the SDDC Manager" | |
echo "by running /opt/vmware/vcf/operationsmanager/scripts/cli/sddcmanager_restart_services.sh${NORMAL}" | |
fi | |
} | |
#------------------------------ | |
# View VECS CA certificates | |
#---------------------------- | |
function manageVECSCACertificates() { | |
header 'CA Certificates in TRUSTED_ROOTS store in VECS' | |
listVECSCACertificates | |
case $1 in | |
'View') | |
read -p $'\nSelect certificate [Return to Main Menu]: ' VIEW_VECS_CA_CERT_INPUT | |
if [ -n "$VIEW_VECS_CA_CERT_INPUT" ]; then | |
viewVECSCACertificate "$VIEW_VECS_CA_CERT_INPUT" | |
fi | |
;; | |
'Manage') | |
header 'Manage Certificates in VECS' | |
echo ' 1. Remove CA certificate(s) from VMware Directory' | |
if [ -n "$SDDC_MANAGER" ]; then echo ' 2. Publish CA certificate(s) to SDDC Manager'; fi | |
read -p $'\nSelect an option [Return to Main Menu]: ' MANAGE_VECS_CA_CERT_INPUT | |
case $MANAGE_VECS_CA_CERT_INPUT in | |
1) | |
removeCACertsVECS | |
;; | |
2) | |
if [ -n "$SDDC_MANAGER" ]; then | |
publishVECSCACertSDDCManager | |
else | |
echo $'\n'"${YELLOW}Invalid option.${NORMAL}" | |
fi | |
;; | |
esac | |
;; | |
esac | |
} | |
#------------------------------ | |
# Remove CA certificates from VECS | |
#------------------------------ | |
function removeCACertsVECS() { | |
echo $'\n'"${CYAN}To add CA certificates to VECS, publish them to VMware Directory.${NORMAL}" | |
read -p $'\nEnter the number(s) of the certificate(s) to delete (multiple entries separated by a comma): ' DELETE_VECS_CA_LIST | |
if [ -n "$DELETE_VECS_CA_LIST" ]; then | |
header 'Removing CA certificates from VECS' | |
for index in $(echo "$DELETE_VECS_CA_LIST" | tr -d ' ' | sed 's/,/ /g'); do | |
alias=${VECS_CA_CERT_ALIASES[$((index - 1))]} | |
task "Backup $alias" | |
BACKUP_ALIAS_FILENAME=$(echo "$alias" | tr '/' '_') | |
if $VECS_CLI entry getcert --store TRUSTED_ROOTS --alias $alias > $BACKUP_DIR/$BACKUP_ALIAS_FILENAME.crt 2>/dev/null; then | |
statusMessage 'OK' 'GREEN' | |
else | |
errorMessage "Unable to backup certificate with $alias" 'backup' | |
fi | |
task "Remove $alias" | |
$VECS_CLI entry delete --store TRUSTED_ROOTS --alias $alias -y 2>/dev/null|| errorMessage "Unable to delete certificate with Alias $alias" | |
statusMessage 'OK' 'GREEN' | |
done | |
fi | |
} | |
#------------------------------ | |
# Publish CA certificate(s) from VECS to SDDC Manager | |
#------------------------------ | |
function publishVECSCACertSDDCManager() { | |
read -p $'\n'"Enter the number(s) of the certificate(s) to publish to $SDDC_MANAGER (multiple entries separated by a comma): " PUBLISH_VECS_CA_SDDC_LIST | |
if [ -n "$PUBLISH_VECS_CA_SDDC_LIST" ]; then | |
header 'Publishing CA certificates to SDDC Manager' | |
getSDDCAccessToken | |
for index in $(echo "$PUBLISH_VECS_CA_SDDC_LIST" | tr -d ' ' | sed 's/,/ /g'); do | |
alias=${VECS_CA_CERT_ALIASES[$((index - 1))]} | |
task "Export $alias" | |
$VECS_CLI entry getcert --store TRUSTED_ROOTS --alias $alias > $STAGE_DIR/sddc_$alias.crt 2>/dev/null || errorMessage "Unable to backup certificate with alias $alias" 'backup' | |
statusMessage 'OK' 'GREEN' | |
publishCACertsSDDCManager "$STAGE_DIR/sddc_$alias.crt" | |
done | |
echo $'\n'"${YELLOW}Services will need to be restarted on the SDDC Manager" | |
echo "by running /opt/vmware/vcf/operationsmanager/scripts/cli/sddcmanager_restart_services.sh${NORMAL}" | |
fi | |
} | |
#------------------------------ | |
# List CA certificates in VMware Directory | |
#------------------------------ | |
function listVMDirCACertificates() { | |
VMDIR_CERTS=() | |
VMDIR_CA_CERT_SKIDS=() | |
logInfo 'Listing certificate information for CA certificates in VMware Directory' | |
for skid in $($DIR_CLI trustedcert list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | tr -d '\t' | awk -F':' '{print $2}'); do | |
CA_CERT=$($DIR_CLI trustedcert get --id $skid --outcert $STAGE_DIR/$skid.crt --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" --outcert /dev/stdout | grep -v 'Certificate retrieved successfully') | |
VMDIR_CA_CERT_INFO=$(viewBriefCertificateInfo "$CA_CERT" '' "$skid") | |
VMDIR_CERTS+=("$VMDIR_CA_CERT_INFO") | |
VMDIR_CA_CERT_SKIDS+=($skid) | |
done | |
i=0 | |
while [ $i -lt "${#VMDIR_CERTS[@]}" ]; do | |
n=$((i+1)) | |
printf "%2s. %s\n\n" $n "${VMDIR_CERTS[$i]}" | |
logDetails "${VMDIR_CERTS[$i]}" | |
logDetails ' ' | |
((++i)) | |
done | |
} | |
#------------------------------ | |
# Publish CA certificate(s) to VMware Directory | |
#------------------------------ | |
function publishCACertsVMDir() { | |
read -e -p $'\nEnter path to CA certificate (or chain): ' CA_CERTS_TO_PUBLISH | |
i=0 | |
NON_CA_FOUND=0 | |
CA_PUBLISHED=0 | |
UPDATED_EMBEDDED=0 | |
while [ ! -f $CA_CERTS_TO_PUBLISH ]; do | |
if [ $i -lt 3 ]; then | |
read -e -p 'File not found. Enter path to CA certificate (or chain): ' CA_CERTS_TO_PUBLISH | |
else | |
errorMessage "CA Certificate file not found at $CA_CERTS_TO_PUBLISH" | |
fi | |
done | |
getCorrectCertFormat "$CA_CERTS_TO_PUBLISH" 'CA_CERTS_TO_PUBLISH' | |
logInfo "Provided new CA certificates to publish to VMware Directory:" | |
logDetails "$(cat $CA_CERTS_TO_PUBLISH)" | |
header 'Publish CA Certificate(s)' | |
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}') | |
csplit -s -z -f $STAGE_DIR/ca-to-publish- -b %02d.crt $CA_CERTS_TO_PUBLISH '/-----BEGIN CERTIFICATE-----/' '{*}' | |
for cert in $(ls $STAGE_DIR/ca-to-publish-*); do | |
if isCertCA "$(cat $cert)"; then | |
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 "$CURRENT_SKID" > /dev/null; then | |
logInfo "Found CA certificate with Subject Key ID $CURRENT_SKID, unpublishing" | |
$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 | |
logInfo "Publishing CA certificate with Subject Key ID $CURRENT_SKID" | |
if $DIR_CLI trustedcert publish --cert $cert --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>&1 | logInfo; then | |
checkUpdateEmbeddedCAChain "$cert" | |
((++CA_PUBLISHED)) | |
fi | |
else | |
((++NON_CA_FOUND)) | |
fi | |
done | |
echo "Published ${GREEN}${CA_PUBLISHED}${NORMAL} certificates to VMware Directory" | |
statusMessage 'OK' 'GREEN' | |
if [ $NON_CA_FOUND -gt 0 ]; then | |
echo $'\n'"Found ${YELLOW}${NON_CA_FOUND}${NORMAL} non-CA certificates in the provided file." | |
echo 'These certificates were not published to VMware Directory.' | |
fi | |
if [ $CA_PUBLISHED -gt 0 ]; then | |
task 'Refreshing CA certificates to VECS' | |
$VECS_CLI force-refresh 2>/dev/null || errorMessage 'Unable to perform a force-refresh of CA certificates to VECS' | |
statusMessage 'OK' 'GREEN' | |
fi | |
if [ $UPDATED_EMBEDDED -gt 0 ]; then | |
echo $'\n'"${YELLOW}Certificate(s) with an embedded CA chain have been updated.${NORMAL}" | |
promptRestartVMwareServices | |
fi | |
rm $STAGE_DIR/ca-to-publish-* | |
} | |
#------------------------------ | |
# Check if we need to update CA cert in embedded chain | |
#------------------------------ | |
function checkUpdateEmbeddedCAChain() { | |
CURRENT_SKID=$(cat "$1" | openssl x509 -noout -text 2>/dev/null | grep -A1 'Subject Key Id' | tail -n1) | |
MACHINE_SSL_CERT=$($VECS_CLI entry getcert --store MACHINE_SSL_CERT --alias __MACHINE_CERT) | |
if [ $(echo "$MACHINE_SSL_CERT" | grep 'BEGIN CERTIFICATE' | wc -l) -gt 1 ]; then | |
if echo "$MACHINE_SSL_CERT" | openssl crl2pkcs7 -nocrl -certfile /dev/stdin 2>/dev/null | openssl pkcs7 -print_certs -noout -text 2>/dev/null | grep "$CURRENT_SKID" > /dev/null; then | |
updateEmbeddedCACertVECS "$CURRENT_SKID" "$1" 'MACHINE_SSL_CERT' '__MACHINE_CERT' | |
fi | |
fi | |
for soluser in "${SOLUTION_USERS[@]}"; do | |
SOLUTION_USER_CERT=$($VECS_CLI entry getcert --store $soluser --alias $soluser) | |
if [ $(echo "$SOLUTION_USER_CERT" | grep 'BEGIN CERTIFICATE' | wc -l) -gt 1 ]; then | |
if echo "$SOLUTION_USER_CERT" | openssl crl2pkcs7 -nocrl -certfile /dev/stdin 2>/dev/null | openssl pkcs7 -print_certs -noout -text 2>/dev/null | grep "$CURRENT_SKID" > /dev/null; then | |
updateEmbeddedCACertVECS "$CURRENT_SKID" "$1" "$soluser" "$soluser" | |
fi | |
fi | |
done | |
AUTH_PROXY_CERT='/var/lib/vmware/vmcam/ssl/rui.crt' | |
if [ $(cat "$AUTH_PROXY_CERT" | grep 'BEGIN CERTIFICATE' | wc -l) -gt 1 ]; then | |
if cat "$AUTH_PROXY_CERT" | openssl crl2pkcs7 -nocrl -certfile /dev/stdin 2>/dev/null | openssl pkcs7 -print_certs -noout -text 2>/dev/null | grep "$CURRENT_SKID" > /dev/null; then | |
updateEmbeddedCACertFile "$CURRENT_SKID" "$1" "$AUTH_PROXY_CERT" 'Auth Proxy Cert' | |
fi | |
fi | |
AUTO_DEPLOY_CA_CERT='/etc/vmware-rbd/ssl/rbd-ca.crt' | |
if [ $(cat "$AUTO_DEPLOY_CA_CERT" | grep 'BEGIN CERTIFICATE' | wc -l) -gt 1 ]; then | |
if cat "$AUTH_PROXY_CERT" | openssl crl2pkcs7 -nocrl -certfile /dev/stdin 2>/dev/null | openssl pkcs7 -print_certs -noout -text 2>/dev/null | grep "$CURRENT_SKID" > /dev/null; then | |
updateEmbeddedCACertFile "$CURRENT_SKID" "$1" "$AUTO_DEPLOY_CA_CERT" 'Auto Deploy CA Cert' | |
fi | |
fi | |
if [ $(cat "$VMCA_CERT" | grep 'BEGIN CERTIFICATE' | wc -l) -gt 1 ]; then | |
if cat "$VMCA_CERT" | openssl crl2pkcs7 -nocrl -certfile /dev/stdin 2>/dev/null | openssl pkcs7 -print_certs -noout -text 2>/dev/null | grep "$CURRENT_SKID" > /dev/null; then | |
updateEmbeddedCACertFile "$CURRENT_SKID" "$1" "$VMCA_CERT" 'VMCA Cert' | |
fi | |
fi | |
if [[ "$VC_VERSION" =~ ^6 ]]; then | |
VMDIR_CERT='/usr/lib/vmware-vmdir/share/config/vmdircert.pem' | |
if [ $(cat "$VMDIR_CERT" | grep 'BEGIN CERTIFICATE' | wc -l) -gt 1 ]; then | |
if cat "$VMDIR_CERT" | openssl crl2pkcs7 -nocrl -certfile /dev/stdin 2>/dev/null | openssl pkcs7 -print_certs -noout -text 2>/dev/null | grep "$CURRENT_SKID" > /dev/null; then | |
updateEmbeddedCACertFile "$CURRENT_SKID" "$1" "$VMDIR_CERT" 'VMDir Cert' | |
fi | |
fi | |
fi | |
} | |
#------------------------------ | |
# Update CA cert in embedded chain in VECS | |
#------------------------------ | |
function updateEmbeddedCACertVECS() { | |
NEW_CA_SERIAL=$(openssl x509 -noout -serial -in $2 2>/dev/null) | |
NEW_CA_SUBJECT=$(openssl x509 -noout -subject -in $2 2>/dev/null) | |
NEEDS_UPDATING=0 | |
logInfo "Splitting certs from alias $4 in store $3" | |
$VECS_CLI entry getcert --store "$3" --alias "$4" | csplit -s -z -f $STAGE/update-embedded-ca- -b %02d.crt /dev/stdin '/-----BEGIN CERTIFICATE-----/' '{*}' | |
for cert in $(ls -d $STAGE/update-embedded-ca-*.crt); do | |
CURRENT_CERT_SERIAL=$(openssl x509 -noout -serial -in $cert 2>/dev/null) | |
if openssl x509 -noout -text -in $cert 2>/dev/null | grep -A1 'Subject Key Id' | tail -n1 | grep "$1" > /dev/null; then | |
if [ "$NEW_CA_SERIAL" != "$CURRENT_CERT_SERIAL" ]; then | |
NEEDS_UPDATING=1 | |
logInfo "Checking new CA cert ($2) against embedded CA file ($cert)" | |
logInfo "Updating CA cert $NEW_CA_SUBJECT in alias $4, store $3" | |
logInfo "Checking $NEW_CA_SUBJECT checksum ($NEW_CA_SERIAL) against embedded CA checksum ($CURRENT_CERT_SERIAL)" | |
fi | |
cat $2 >> $STAGE/update-embedded-cert.pem | |
else | |
cat $cert >> $STAGE/update-embedded-cert.pem | |
fi | |
done | |
if [ $NEEDS_UPDATING -gt 0 ]; then | |
task 'Updating embedded CA cert in VECS' | |
$VECS_CLI entry getkey --store "$3" --alias "$4" > $STAGE/update-embedded-key.key 2>/dev/null || errorMessage "Unable to export key from store $3, alias $4" | |
$VECS_CLI entry delete --store "$3" --alias "$4" -y 2>/dev/null || errorMessage "Unable to delete alias $4 from store $3" | |
$VECS_CLI entry create --store "$3" --alias "$4" --cert $STAGE/update-embedded-cert.pem --key $STAGE/update-embedded-key.key 2>/dev/null || errorMessage "Unable to create alias $4 in store $3" | |
statusMessage 'OK' 'GREEN' | |
UPDATED_EMBEDDED=1 | |
fi | |
rm $STAGE/update-embedded-* 2>&1 | logDebug | |
} | |
#------------------------------ | |
# Update CA cert in embedded chain in a file | |
#------------------------------ | |
function updateEmbeddedCACertFile() { | |
NEW_CA_SERIAL=$(openssl x509 -noout -serial -in $2 2>/dev/null) | |
NEW_CA_SUBJECT=$(openssl x509 -noout -subject -in $2 2>/dev/null) | |
NEEDS_UPDATING=0 | |
csplit -s -z -f $STAGE/update-embedded-ca- -b %02d.crt $3 '/-----BEGIN CERTIFICATE-----/' '{*}' | |
for cert in $(ls -d $STAGE/update-embedded-ca-*.crt); do | |
if openssl x509 -noout -text -in $cert 2>/dev/null | grep -A1 'Subject Key Id' | tail -n1 | grep "$1" > /dev/null; then | |
CURRENT_CERT_SERIAL=$(openssl x509 -noout -serial -in $cert 2>/dev/null) | |
if [ "$NEW_CA_SERIAL" != "$CURRENT_CERT_SERIAL" ]; then | |
NEEDS_UPDATING=1 | |
fi | |
cat $2 >> $STAGE/update-embedded-cert.pem | |
logInfo "Updating CA cert $NEW_CA_SUBJECT in $3" | |
else | |
cat $cert >> $STAGE/update-embedded-cert.pem | |
fi | |
done | |
if [ $NEEDS_UPDATING -gt 0 ]; then | |
task "Updating embedded CA cert in $4" | |
cp $3 $BACKUP_DIR/$(basename $3) 2>/dev/null || errorMessage "Unable to backup $3" | |
cp $STAGE/update-embedded-cert.pem $3 2>/dev/null || errorMessage "Unable to copy new certificate to $3" | |
statusMessage 'OK' 'GREEN' | |
UPDATED_EMBEDDED=1 | |
fi | |
rm $STAGE/update-embedded-* | |
} | |
#------------------------------ | |
# List CA certificates in VECS | |
#------------------------------ | |
function listVECSCACertificates() { | |
VECS_CERTS=() | |
VECS_CA_CERT_ALIASES=() | |
for alias in $($VECS_CLI entry list --store TRUSTED_ROOTS | grep '^Alias' | awk -F"[[:space:]]:[[:space:]]" '{print $NF}'); do | |
CA_CERT=$($VECS_CLI entry getcert --store TRUSTED_ROOTS --alias $alias 2>/dev/null) | |
VECS_CA_CERT_INFO=$(viewBriefCertificateInfo "$CA_CERT" "$alias") | |
VECS_CERTS+=("$VECS_CA_CERT_INFO") | |
VECS_CA_CERT_ALIASES+=($alias) | |
done | |
i=0 | |
while [ $i -lt "${#VECS_CERTS[@]}" ]; do | |
n=$((i+1)) | |
printf "%2s. %s\n\n" $n "${VECS_CERTS[$i]}" | |
((++i)) | |
done | |
} | |
#------------------------------ | |
# View VMDir CA certificate info | |
#------------------------------ | |
function viewVMDirCACertificate() { | |
skid=${VMDIR_CA_CERT_SKIDS[$(($1- 1))]} | |
CERT=$($DIR_CLI trustedcert get --id $skid --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" --outcert /dev/stdout | grep -v 'Certificate retrieved successfully') | |
if [ -n "$CERT" ]; then | |
viewCertificateInfo "$CERT" 'view-path' | |
else | |
echo $'\n'"${YELLOW}Unable to view the CA certificate with Subject Key ID $skid.${NORMAL}" | |
fi | |
} | |
#------------------------------ | |
# View VECS CA certificate info | |
#------------------------------ | |
function viewVECSCACertificate() { | |
alias=${VECS_CA_CERT_ALIASES[$(($1 - 1))]} | |
viewVECSCertificateInfo 'TRUSTED_ROOTS' "$alias" | |
} | |
#------------------------------ | |
# Build certificate from hash | |
#------------------------------ | |
function buildCertFromHash() { | |
if [[ "$1" =~ ^MII ]]; then | |
hash=$(echo "$1" | fold -c64) | |
elif [[ "$1" =~ ^LS0 ]]; then | |
echo "$hash" | base64 -d | |
return 0 | |
else | |
hash=$(echo $1 | base64 -d | tr -d '\r\n') | |
fi | |
TEMP_CERT=$'-----BEGIN CERTIFICATE-----\n' | |
TEMP_CERT+=$hash | |
TEMP_CERT+=$'\n-----END CERTIFICATE-----' | |
echo "$TEMP_CERT" | |
} | |
#------------------------------ | |
# View certificate info | |
#------------------------------ | |
function viewCertificateInfo() { | |
header 'Certificate Information' | |
CERT_INFO=$(echo "$1" | openssl x509 -text -noout -fingerprint -sha1 2>/dev/null) | |
logDetails "$CERT_INFO" | |
echo "$CERT_INFO" | |
if [ -n "$2" ] && [ "$2" == 'view-path' ]; then printCertificationPath "$1"; fi | |
} | |
#------------------------------ | |
# Print certification path info | |
#------------------------------ | |
function printCertificationPath() { | |
header 'Certificaton Path' | |
CERT_IDS=() | |
VMDIR_CA_SKIDS=$($DIR_CLI trustedcert list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | tr -d '\t' | awk -F':' '{print $2}' 2>&1) | |
for skid in $($DIR_CLI trustedcert list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" | grep '^CN' | tr -d '\t' | awk -F':' '{print $2}' 2>&1); do | |
logDebug "Exporting CA certificate with Subject Key ID $skid" | |
$DIR_CLI trustedcert get --id $skid --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" --outcert $STAGE_DIR/vmdir-ca-$skid.crt 2>&1 | logDebug | |
done | |
CHAIN_COMPLETE=1 | |
CURRENT_SUBJECT=$(echo "$1" | openssl x509 -noout -text 2>/dev/null | grep 'Subject:' | sed -e 's/[[:space:]]*//' -e 's/Subject: //') | |
CURRENT_ISSUER=$(echo "$1" | openssl x509 -noout -text 2>/dev/null | grep 'Issuer:' | sed -e 's/[[:space:]]*//' -e 's/Issuer: //') | |
CURRENT_CERT_ID=$(certificationPathCertIdentifier "$CURRENT_SUBJECT") | |
NEXT_CERT_ID=$(certificationPathCertIdentifier "$CURRENT_ISSUER") | |
CURRENT_AKID=$(echo "$1" | openssl x509 -noout -text 2>/dev/null | grep -A1 'Authority Key Id' | tail -n1 | tr -d ' ' | sed 's/keyid://' | tr -d ':') | |
CERT_IDS+=("$CURRENT_CERT_ID") | |
if [ "$CURRENT_SUBJECT" != "$CURRENT_ISSUER" ]; then | |
CERT_IDS+=("$NEXT_CERT_ID") | |
CHAIN_COMPLETE=0 | |
fi | |
while true; do | |
CURRENT_CA_CERT="$STAGE_DIR/vmdir-ca-$CURRENT_AKID.crt" | |
if [ -f $CURRENT_CA_CERT ]; then | |
CURRENT_SUBJECT=$(openssl x509 -noout -text -in $CURRENT_CA_CERT 2>/dev/null | grep 'Subject:' | sed -e 's/[[:space:]]*//' -e 's/Subject: //') | |
CURRENT_ISSUER=$(openssl x509 -noout -text -in $CURRENT_CA_CERT 2>/dev/null | grep 'Issuer:' | sed -e 's/[[:space:]]*//' -e 's/Issuer: //') | |
if [ "$CURRENT_SUBJECT" == "$CURRENT_ISSUER" ]; then | |
CHAIN_COMPLETE=1 | |
break | |
else | |
CURRENT_AKID=$(openssl x509 -noout -text -in $CURRENT_CA_CERT 2>/dev/null | grep -A1 'Authority Key Id' | tail -n1 | tr -d ' ' | sed 's/keyid://' | tr -d ':') | |
fi | |
NEXT_CERT_ID=$(certificationPathCertIdentifier "$CURRENT_ISSUER") | |
CERT_IDS+=("$NEXT_CERT_ID") | |
else | |
break | |
fi | |
done | |
i=$((${#CERT_IDS[@]} - 1)) | |
p=0 | |
CERTIFICATION_PATH='' | |
while [ $i -ge 0 ]; do | |
if [ $p -gt 0 ]; then | |
b=$((p-1)) | |
s=$(( (p*2)+(b*2) )) | |
for (( c=1; c<=$s; c++)); do CERTIFICATION_PATH+=' '; done | |
CERTIFICATION_PATH+=$'|_' | |
fi | |
if [ $p -eq 0 ] && [ $CHAIN_COMPLETE -eq 0 ]; then | |
CERTIFICATION_PATH+="[ ${RED}!${NORMAL} ] ${CERT_IDS[$i]}"$'\n' | |
else | |
CERTIFICATION_PATH+="[ ${GREEN}+${NORMAL} ] ${CERT_IDS[$i]}"$'\n' | |
fi | |
((--i)) | |
((++p)) | |
done | |
echo "$CERTIFICATION_PATH" | |
logDetails "$CERTIFICATION_PATH" | |
/usr/bin/rm $STAGE_DIR/vmdir-ca-*.crt 2>&1 | logDebug | |
} | |
#------------------------------ | |
# Print certification path info for missing CAs | |
#------------------------------ | |
function printCertificationPathMissingCA() { | |
logInfo 'Printing Certification Path of Missing CA certificates' | |
CERT_IDS=() | |
CERT_SUBJECTS=() | |
CURRENT_SUBJECT=$(openssl x509 -noout -text -in "$1" 2>/dev/null | grep 'Subject:' | sed -e 's/[[:space:]]*//' -e 's/Subject: //') | |
CURRENT_ISSUER=$(openssl x509 -noout -text -in "$1" 2>/dev/null | grep 'Issuer:' | sed -e 's/[[:space:]]*//' -e 's/Issuer: //') | |
CURRENT_AKID=$(openssl x509 -noout -text -in "$1" 2>/dev/null | grep -A1 'Authority Key Id' | tail -n1 | tr -d ' ' | sed 's/keyid://' | tr -d ':') | |
CURRENT_CERT_ID=$(certificationPathCertIdentifier "$CURRENT_SUBJECT") | |
csplit -s -z -f $STAGE_DIR/validate-root-chain-tmp- -b %02d.crt $2 '/-----BEGIN CERTIFICATE-----/' '{*}' 2>&1 | logDebug | |
for ca in $(ls $STAGE_DIR/validate-root-chain-tmp-*); do | |
CURRENT_SKID=$(openssl x509 -noout -text -in $ca | grep -A1 'Subject Key Id' | tail -n1 | tr -d ': ') | |
mv $ca $STAGE_DIR/validate-root-chain-ca-$CURRENT_SKID.crt | |
done | |
CERT_IDS+=("$CURRENT_CERT_ID") | |
CERT_SUBJECTS+=("$CURRENT_SUBJECT") | |
while true; do | |
CURRENT_CA_CERT="$STAGE_DIR/validate-root-chain-ca-$CURRENT_AKID.crt" | |
if [ -f "$CURRENT_CA_CERT" ]; then | |
CURRENT_SUBJECT=$(openssl x509 -noout -text -in $CURRENT_CA_CERT 2>/dev/null | grep 'Subject:' | sed -e 's/[[:space:]]*//' -e 's/Subject: //') | |
CURRENT_ISSUER=$(openssl x509 -noout -text -in $CURRENT_CA_CERT 2>/dev/null | grep 'Issuer:' | sed -e 's/[[:space:]]*//' -e 's/Issuer: //') | |
CURRENT_AKID=$(openssl x509 -noout -text -in $CURRENT_CA_CERT 2>/dev/null | grep -A1 'Authority Key Id' | tail -n1 | tr -d ' ' | sed 's/keyid://' | tr -d ':') | |
CURRENT_CERT_ID=$(certificationPathCertIdentifier "$CURRENT_SUBJECT") | |
CERT_IDS+=("$CURRENT_CERT_ID") | |
CERT_SUBJECTS+=("$CURRENT_SUBJECT") | |
else | |
CURRENT_CERT_ID=$(certificationPathCertIdentifier "$CURRENT_ISSUER") | |
CERT_IDS+=("$CURRENT_CERT_ID") | |
CERT_SUBJECTS+=("$CURRENT_ISSUER") | |
break | |
fi | |
done | |
i=$((${#CERT_IDS[@]} - 1)) | |
last_index=$i | |
p=0 | |
CERTIFICATION_PATH='' | |
while [ $i -ge 0 ]; do | |
if [ $p -gt 0 ]; then | |
b=$((p-1)) | |
s=$(( (p*2)+(b*2) )) | |
for (( c=1; c<=$s; c++)); do CERTIFICATION_PATH+=' '; done | |
CERTIFICATION_PATH+=$'|_' | |
fi | |
if [ $p -eq 0 ]; then | |
CERTIFICATION_PATH+="[ ! ] ${CERT_IDS[$i]}" | |
else | |
CERTIFICATION_PATH+="[ + ] ${CERT_IDS[$i]}" | |
fi | |
((--i)) | |
((++p)) | |
done | |
CERTIFICATION_PATH+=$'\nPlease ensure that the following certificate (and its issuers, if any) are included in the signing CA chain:' | |
CERTIFICATION_PATH+=" Subject: ${CERT_SUBJECTS[$last_index]}" | |
echo "$CERTIFICATION_PATH" | |
logDetails "$CERTIFICATION_PATH" | |
/usr/bin/rm $STAGE_DIR/validate-root-chain-ca-* 2>&1 | logDebug | |
} | |
#------------------------------ | |
# Get Certificate Identifier from Subject string | |
#------------------------------ | |
function certificationPathCertIdentifier() { | |
CERT_CN=$(echo "$1" | sed 's/, /\n/g' | grep 'CN=' | awk -F'=' '{print $NF}') | |
if [ -z "$CERT_CN" ]; then | |
CERT_LAST_OU=$(echo "$1" | sed 's/, /\n/g' | grep 'OU=' | tail -n1 | awk -F'=' '{print $NF}') | |
if [ -z "$CERT_LAST_OU" ]; then | |
CERT_LAST_O=$(echo "$1" | sed 's/, /\n/g' | grep 'O=' | tail -n1 | awk -F'=' '{print $NF}') | |
if [ -z "$CERT_LAST_O" ]; then | |
echo "<unknown>" | |
else | |
echo "$CERT_LAST_O" | |
fi | |
else | |
echo "$CERT_LAST_OU" | |
fi | |
else | |
echo "$CERT_CN" | |
fi | |
} | |
#------------------------------ | |
# View brief certificate info | |
#------------------------------ | |
function viewBriefCertificateInfo() { | |
CERT_SUBJECT=$(echo "$1" | openssl x509 -noout -subject 2>/dev/null | sed 's/subject= //') | |
CERT_ISSUER=$(echo "$1" | openssl x509 -noout -issuer 2>/dev/null | sed 's/issuer= //') | |
CERT_ENDDATE=$(echo "$1" | openssl x509 -noout -enddate 2>/dev/null | awk -F'=' '{print $NF}') | |
CERT_SKID=$(echo "$1" | openssl x509 -noout -text 2>/dev/null | grep -A1 'Subject Key' | tail -n1 | tr -d '[:space:]') | |
if [ -z "$CERT_SKID" ] && [ -n "$3" ]; then | |
CERT_SKID="$3 (computed)" | |
fi | |
if ! echo "$1" | openssl x509 -noout -subject -serial 2>/dev/null > /dev/null; then | |
CERT_INFO="${YELLOW}Invalid format, certificate cannot be parsed!${NORMAL}" | |
CERT_INFO+=$'\n'" Subject Key ID: $CERT_SKID" | |
else | |
if isCertCA "$1"; then | |
CERT_CA='Yes' | |
else | |
CERT_CA='No' | |
fi | |
if [ -n "$2" ]; then | |
CERT_INFO="Alias: $2" | |
CERT_INFO+=$'\n'" Subject: $CERT_SUBJECT" | |
else | |
CERT_INFO="Subject: $CERT_SUBJECT" | |
fi | |
CERT_INFO+=$'\n'" Issuer: $CERT_ISSUER" | |
CERT_INFO+=$'\n'" End Date: $CERT_ENDDATE" | |
CERT_INFO+=$'\n'" Subject Key ID: $CERT_SKID" | |
CERT_INFO+=$'\n'" Is CA Cert: $CERT_CA" | |
fi | |
echo "$CERT_INFO" | |
} | |
#------------------------------ | |
# View CRL info | |
#------------------------------ | |
function viewCRLInfo() { | |
echo "$1" | openssl crl -text -noout 2>&1 | logDebug | |
} | |
#------------------------------ | |
# View vCenter Extension thumbprints | |
#------------------------------ | |
function manageVCExtensionThumbprints() { | |
header "$1 vCenter Extension Thumbprints" | |
if [ "$NODE_TYPE" != 'infrastructure' ] && ! checkService 'vmware-vpostgres'; then | |
echo "${YELLOW}The vPostgres service is stopped!" | |
echo "Please ensure this service is running before updating vCenter extension thumbprints." | |
echo "Hint: Check the number of CRL entries in VECS${NORMAL}" | |
return 1 | |
fi | |
VC_EXT_THUMBPRINT_MISMATCH=0 | |
VPXD_EXT_THUMBPRINT=$($VECS_CLI entry getcert --store vpxd-extension --alias vpxd-extension 2>/dev/null | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}') | |
MACHINE_SSL_THUMBPRINT=$($VECS_CLI entry getcert --store MACHINE_SSL_CERT --alias __MACHINE_CERT 2>/dev/null | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}') | |
VMCAM_THUMBPRINT=$(openssl x509 -noout -fingerprint -sha1 -in /var/lib/vmware/vmcam/ssl/vmcamcert.pem 2>/dev/null | awk -F'=' '{print $NF}') | |
VCENTER_EXTENSIONS="'com.vmware.vsan.health','com.vmware.vcIntegrity','com.vmware.rbd','com.vmware.imagebuilder','com.vmware.vmcam','com.vmware.vim.eam'" | |
if [[ "$VC_VERSION" =~ ^8 ]]; then | |
if [ $VC_BUILD -ge 22385739 ]; then | |
VCENTER_EXTENSIONS="'com.vmware.vsan.health','com.vmware.vcIntegrity','com.vmware.imagebuilder','com.vmware.vmcam','com.vmware.vim.eam'" | |
fi | |
VCENTER_EXTENSIONS+=",'com.vmware.vlcm.client'" | |
fi | |
IFS=$'\n' | |
for record in $($PSQL -d VCDB -U postgres -c "SELECT ext_id,thumbprint FROM vpx_ext WHERE ext_id IN ($VCENTER_EXTENSIONS) ORDER BY ext_id" -t); do | |
extension=$(echo $record | awk -F'|' '{print $1}' | tr -d ' ') | |
thumbprint=$(echo $record | awk -F'|' '{print $NF}' | tr -d ' ') | |
case $extension in | |
'com.vmware.vmcam') | |
COMPARE_TO=$VMCAM_THUMBPRINT | |
EXPECTED_CERT='Authentication Proxy' | |
;; | |
'com.vmware.vsan.health') | |
COMPARE_TO=$MACHINE_SSL_THUMBPRINT | |
EXPECTED_CERT='Machine SSL' | |
;; | |
*) | |
COMPARE_TO=$VPXD_EXT_THUMBPRINT | |
EXPECTED_CERT='vpxd-extension' | |
;; | |
esac | |
case $1 in | |
'Checking'|'Check') | |
task "$extension ($EXPECTED_CERT)" | |
logInfo "Comparing $extension thumbprint of '$thumbprint' to '$COMPARE_TO'" | |
if [ "$thumbprint" = "$COMPARE_TO" ]; then | |
statusMessage 'MATCHES' 'GREEN' | |
else | |
VC_EXT_THUMBPRINT_MISMATCH=1 | |
statusMessage 'MISMATCH' 'YELLOW' | |
fi | |
;; | |
'View') | |
echo "$extension ($EXPECTED_CERT)" | |
echo " $thumbprint" | |
;; | |
'Update') | |
task "$extension ($EXPECTED_CERT)" | |
logInfo "Comparing $extension thumbprint of '$thumbprint' to '$COMPARE_TO'" | |
if [ "$thumbprint" = "$COMPARE_TO" ]; then | |
statusMessage 'MATCHES' 'GREEN' | |
else | |
$PSQL -d VCDB -U postgres -c "UPDATE vpx_ext SET thumbprint = '$COMPARE_TO' WHERE ext_id = '$extension'" > /dev/null 2>&1 || errorMessage "Unable to update $extension extension thumbprint in VCDB" | |
statusMessage 'UPDATED' 'GREEN' | |
fi | |
;; | |
esac | |
done | |
IFS=$' \t\n' | |
if [ "$1" == 'Check' ] && [ "$VC_EXT_THUMBPRINT_MISMATCH" == '1' ]; then | |
unset UPDATE_THUMBPRINTS_INPUT | |
echo $'\n'"${YELLOW}------------------------!!! Attention !!!------------------------" | |
echo "Mismatched thumbprints detected.${NORMAL}" | |
read -p $'\nUpdate extension thumbprints? [n]: ' UPDATE_THUMBPRINTS_INPUT | |
if [ -z $UPDATE_THUMBPRINTS_INPUT ]; then UPDATE_THUMBPRINTS_INPUT='n'; fi | |
if [[ "$UPDATE_THUMBPRINTS_INPUT" =~ ^[Yy] ]]; then manageVCExtensionThumbprints 'Update'; fi | |
fi | |
} | |
#------------------------------ | |
# View VMCA configuration options in VCDB | |
#------------------------------ | |
function checkVMCADatabaseConfig() { | |
VMCA_CONFIG_OUTPUT='' | |
header 'Checking VMCA Configurations in VCDB' | |
if [ "$NODE_TYPE" != 'infrastructure' ] && ! checkService 'vmware-vpostgres'; then | |
task 'Connect to vPostgres database' | |
statusMessage 'ERROR' 'YELLOW' | |
else | |
VMCA_CONFIGS=$($PSQL -d VCDB -U postgres -c "SELECT name,value FROM vpx_parameter WHERE name='vpxd.certmgmt.mode' OR name LIKE 'vpxd.certmgmt.certs.cn.%'" -t) | |
logDetails "$VMCA_CONFIGS" | |
IFS=$'\n' | |
for line in $VMCA_CONFIGS; do | |
config=$(echo $line | awk -F'|' '{print $1}' | sed -e 's/^[[:space:]]*//' | sed -e 's/[[:space:]]*$//') | |
value=$(echo $line | awk -F'|' '{print $NF}' | sed -e 's/^[[:space:]]*//') | |
if [ -z $value ]; then | |
value="${YELLOW}EMPTY${NORMAL}" | |
CERT_STATUS_VMCA_EMPTY_CONFIG=1 | |
else | |
if [ "$config" = 'vpxd.certmgmt.mode' ] && [ "$value" = 'thumbprint' ]; then | |
value="${YELLOW}'${value}'${NORMAL}" | |
CERT_STATUS_VMCA_MODE=1 | |
else | |
value="${GREEN}'${value}'${NORMAL}" | |
fi | |
fi | |
VMCA_CONFIG_OUTPUT+=$'\n'"$config: $value" | |
done | |
echo "$VMCA_CONFIG_OUTPUT" | column -t -s ':' | |
IFS=$' \t\n' | |
fi | |
} | |
#------------------------------ | |
# Generate configuration file for certool utility | |
#------------------------------ | |
function generateCertoolConfig() { | |
if [[ " ${SOLUTION_USERS[*]} " =~ " $1 " ]]; then | |
task "$1" | |
else | |
task 'Generate certool configuration' | |
fi | |
case $1 in | |
'auth proxy') | |
echo "Country = $CSR_COUNTRY" > $STAGE_DIR/auth-proxy.cfg | |
echo "Organization = $CSR_ORG" >> $STAGE_DIR/auth-proxy.cfg | |
echo "OrgUnit = $CSR_ORG_UNIT" >> $STAGE_DIR/auth-proxy.cfg | |
echo "Name = $HOSTNAME" >> $STAGE_DIR/auth-proxy.cfg | |
echo "Hostname = $HOSTNAME" >> $STAGE_DIR/auth-proxy.cfg | |
;; | |
'vmdir') | |
echo "Country = $CSR_COUNTRY" > $STAGE_DIR/vmdir.cfg | |
echo "Name = $HOSTNAME" >> $STAGE_DIR/vmdir.cfg | |
echo "Hostname = $HOSTNAME" >> $STAGE_DIR/vmdir.cfg | |
;; | |
'sts') | |
echo 'Name = ssoserverSign' > $STAGE_DIR/sso-sts.cfg | |
echo "Hostname = $HOSTNAME" >> $STAGE_DIR/sso-sts.cfg | |
;; | |
*) | |
echo "Country = $CSR_COUNTRY" > $STAGE_DIR/$1.cfg | |
echo "Name = $2" >> $STAGE_DIR/$1.cfg | |
echo "Organization = $CSR_ORG" >> $STAGE_DIR/$1.cfg | |
echo "OrgUnit = $CSR_ORG_UNIT" >> $STAGE_DIR/$1.cfg | |
echo "State = $CSR_STATE" >> $STAGE_DIR/$1.cfg | |
echo "Locality = $CSR_LOCALITY" >> $STAGE_DIR/$1.cfg | |
if [ "$2" == $IP ]; then | |
echo "IPAddress = $2" >> $STAGE_DIR/$1.cfg | |
elif [ -n "$CSR_IP" ]; then | |
echo "IPAddress = $CSR_IP" >> $STAGE_DIR/$1.cfg | |
fi | |
if [ -n "$CSR_EMAIL" ]; then echo "Email = $CSR_EMAIL" >> $STAGE_DIR/$1.cfg; fi | |
printf "Hostname = $HOSTNAME" >> $STAGE_DIR/$1.cfg | |
if [ "$HOSTNAME_LC" != "$PNID_LC" ] && [ "$IP" != "$PNID" ]; then | |
printf ",$PNID" >> $STAGE_DIR/$1.cfg | |
fi | |
if [ -n "$CSR_ADDITIONAL_DNS" ]; then | |
CSR_ADDITIONAL_DNS=$(echo "$CSR_ADDITIONAL_DNS" | tr -d ' ') | |
printf ",$CSR_ADDITIONAL_DNS" >> $STAGE_DIR/$1.cfg | |
fi | |
;; | |
esac | |
statusMessage 'OK' 'GREEN' | |
} | |
#------------------------------ | |
# Generate a VMCA-signed certificate | |
#------------------------------ | |
function regenerateVMCASignedCertificate() { | |
$CERTOOL --genkey --privkey=$STAGE_DIR/$1.key --pubkey=$STAGE_DIR/$1.pub --server=$PSC_LOCATION > /dev/null 2>&1 || errorMessage "Unable to genereate new keys for $1" | |
$CERTOOL --gencert --privkey=$STAGE_DIR/$1.key --cert=$STAGE_DIR/$1.crt --config=$STAGE_DIR/$1.cfg --server=$PSC_LOCATION > /dev/null 2>&1 || errorMessage "Unable to generate self-signed certificate for $1" | |
} | |
#------------------------------ | |
# Generate a certificate Signing Request | |
#------------------------------ | |
function generateCSR() { | |
openssl req -new -newkey rsa:2048 -nodes -out $1 -keyout $2 -config $3 > /dev/null 2>&1 || errorMessage "Unable to generate Certificate Signing Request and Private Key" | |
return 0 | |
} | |
#------------------------------ | |
# Reset Certificate Signing Request fields | |
#------------------------------ | |
function clearCSRInfo() { | |
logInfo 'Clearing CSR fields' | |
CSR_COUNTRY='' | |
CSR_ORG='' | |
CSR_ORG_UNIT='' | |
CSR_STATE='' | |
CSR_LOCALITY='' | |
CSR_IP='' | |
CSR_EMAIL='' | |
CSR_ADDITIONAL_DNS='' | |
} | |
#------------------------------ | |
# Collect information for a Certificate Signing Request | |
#------------------------------ | |
function getCSRInfo() { | |
unset CSR_COUNTRY_INPUT | |
unset CSR_ORG_INPUT | |
unset CSR_ORG_UNIT_INPUT | |
unset CSR_STATE_INPUT | |
unset CSR_LOCALITY_INPUT | |
unset CSR_IP_INPUT | |
unset CSR_EMAIL_INPUT | |
unset CSR_SAN_INPUT | |
if [ -z "$1" ]; then | |
header 'Certificate Signing Request Information' | |
else | |
header "Certificate Signing Request Information [$1]" | |
fi | |
read -p "Enter the country code [$CSR_COUNTRY_DEFAULT]: " CSR_COUNTRY_INPUT | |
if [ -z "$CSR_COUNTRY_INPUT" ]; then | |
CSR_COUNTRY=$CSR_COUNTRY_DEFAULT | |
else | |
while [ "${#CSR_COUNTRY_INPUT}" != 2 ]; do read -p "Please enter the two-character country code [$CSR_COUNTRY_DEFAULT]: " CSR_COUNTRY_INPUT; done | |
CSR_COUNTRY=$CSR_COUNTRY_INPUT | |
CSR_COUNTRY_DEFAULT=$CSR_COUNTRY_INPUT | |
fi | |
if [ -n "$1" ] && [ "$1" == 'VMCA' ]; then | |
CSR_ORG_DEFAULT_OPTION="$VMDIR_FQDN" | |
else | |
CSR_ORG_DEFAULT_OPTION="$CSR_ORG_DEFAULT" | |
fi | |
read -p "Enter the Organization name [$CSR_ORG_DEFAULT_OPTION]: " CSR_ORG_INPUT | |
if [ -z "$CSR_ORG_INPUT" ]; then | |
CSR_ORG="$CSR_ORG_DEFAULT_OPTION" | |
else | |
CSR_ORG="$CSR_ORG_INPUT" | |
CSR_ORG_DEFAULT="$CSR_ORG_INPUT" | |
fi | |
read -p "Enter the Organizational Unit name [$CSR_ORG_UNIT_DEFAULT]: " CSR_ORG_UNIT_INPUT | |
if [ -z "$CSR_ORG_UNIT_INPUT" ]; then | |
CSR_ORG_UNIT="$CSR_ORG_UNIT_DEFAULT" | |
else | |
CSR_ORG_UNIT="$CSR_ORG_UNIT_INPUT" | |
CSR_ORG_UNIT_DEFAULT="$CSR_ORG_UNIT_INPUT" | |
fi | |
read -p "Enter the state [$CSR_STATE_DEFAULT]: " CSR_STATE_INPUT | |
if [ -z "$CSR_STATE_INPUT" ]; then | |
CSR_STATE="$CSR_STATE_DEFAULT" | |
else | |
CSR_STATE="$CSR_STATE_INPUT" | |
CSR_STATE_DEFAULT="$CSR_STATE_INPUT" | |
fi | |
read -p "Enter the locality (city) name [$CSR_LOCALITY_DEFAULT]: " CSR_LOCALITY_INPUT | |
if [ -z "$CSR_LOCALITY_INPUT" ]; then | |
CSR_LOCALITY="$CSR_LOCALITY_DEFAULT" | |
else | |
CSR_LOCALITY="$CSR_LOCALITY_INPUT" | |
CSR_LOCALITY_DEFAULT="$CSR_LOCALITY_INPUT" | |
fi | |
read -p 'Enter the IP address (optional): ' CSR_IP_INPUT | |
if [ -n "$CSR_IP_INPUT" ]; then | |
while ! validateIp $CSR_IP_INPUT; do echo "${YELLOW}Invalid IP address, enter valid IP address: " CSR_IP_INPUT; done | |
CSR_IP=$CSR_IP_INPUT | |
fi | |
read -p 'Enter an email address (optional): ' CSR_EMAIL_INPUT | |
if [ -n "$CSR_EMAIL_INPUT" ]; then CSR_EMAIL=$CSR_EMAIL_INPUT; fi | |
if [ -n "$1" ]; then | |
read -p 'Enter any additional hostnames for SAN entries (comma separated value): ' CSR_SAN_INPUT | |
if [ -n "$CSR_SAN_INPUT" ]; then CSR_ADDITIONAL_DNS=$CSR_SAN_INPUT; fi | |
fi | |
} | |
#------------------------------ | |
# Define the SAN entries for creating an openssl configuration file | |
#------------------------------ | |
function defineSANEntries() { | |
unset ADDITIONAL_SAN_ITEMS | |
unset INCLUDE_SHORTNAME_INPUT | |
if [ "$1" == 'ESXi' ]; then | |
DEFAULT_SANS=("$3") | |
if ! echo "$3" | awk -F '.' '{print $1}' | grep '^[0-9]' > /dev/null; then | |
ESXi_SHORT=$(echo "$3" | awk -F '.' '{print $1}') | |
read -p "Include host short name (${CYAN}${ESXi_SHORT}${NORMAL}) as a Subject Alternative Name entry? [n]: " INCLUDE_SHORTNAME_INPUT | |
SHORTNAME_TO_INCLUDE=$ESXi_SHORT | |
fi | |
else | |
DEFAULT_SANS=("$HOSTNAME") | |
read -p "Include host short name (${CYAN}${HOSTNAME_SHORT}${NORMAL}) as a Subject Alternative Name entry? [n]: " INCLUDE_SHORTNAME_INPUT | |
SHORTNAME_TO_INCLUDE=$HOSTNAME_SHORT | |
fi | |
if [[ "$INCLUDE_SHORTNAME_INPUT" =~ ^[Yy] ]]; then | |
DEFAULT_SANS+=("$SHORTNAME_TO_INCLUDE") | |
fi | |
if [ "$HOSTNAME_LC" != "$PNID_LC" ]; then | |
DEFAULT_SANS+=("$PNID") | |
fi | |
echo $'\n'"The following items will be added as Subject Alternative Name entries on the '$2' Certificate Signing Request:" | |
echo "$CYAN" | |
for san in "${DEFAULT_SANS[@]}"; do | |
echo "$san" | |
done | |
if [ -n "$CSR_IP" ]; then echo "$CSR_IP"; fi | |
if [ -n "$CSR_EMAIL" ]; then echo "$CSR_EMAIL"; fi | |
echo -n "$NORMAL" | |
if [ "$1" == "machine-ssl" ] && [ $NODE_TYPE = 'infrastructure' ] && [ -n "$PSC_LB" ]; then | |
cat << EOF | |
${YELLOW}-------------------------!!! WARNING !!!-------------------------" | |
This PSC is detected to be in an HA configuration. | |
Make sure to add the hostnames of the additional PSCs | |
as Subject Alternative Name entries. | |
EOF | |
echo $'\n' | |
fi | |
read -p $'\nIf you want any additional items added as Subject Alternative Name entries, enter them as a comma-separated list (optional): ' ADDITIONAL_SAN_ITEMS | |
} | |
#------------------------------ | |
# Generate a configuration file to be used with the openssl commands | |
#------------------------------ | |
function generateOpensslConfig() { | |
echo '[ req ]' > $2 | |
echo 'prompt = no' >> $2 | |
echo 'default_bits = 2048' >> $2 | |
echo 'distinguished_name = req_distinguished_name' >> $2 | |
echo 'req_extensions = v3_req' >> $2 | |
echo '' >> $2 | |
echo '[ req_distinguished_name ]' >> $2 | |
echo "C = $CSR_COUNTRY" >> $2 | |
echo "ST = $CSR_STATE" >> $2 | |
echo "L = $CSR_LOCALITY" >> $2 | |
echo "O = $CSR_ORG" >> $2 | |
echo "OU = $CSR_ORG_UNIT" >> $2 | |
echo "CN = $1" >> $2 | |
echo '' >> $2 | |
echo '[ v3_req ]' >> $2 | |
printf 'subjectAltName = ' >> $2 | |
echo -n "DNS:${DEFAULT_SANS[@]}" | sed 's/ /, DNS:/g' >> $2 | |
for item in $(echo "$ADDITIONAL_SAN_ITEMS" | tr -d ' ' | sed -e 's/,/\n/g'); do | |
if [[ $item =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then | |
printf ", IP:$item" >> $2 | |
else | |
printf ", DNS:$item" >> $2 | |
fi | |
done | |
if [ -n "$CSR_IP" ]; then printf ", IP:$CSR_IP" >> $2; fi | |
if [ -n "$CSR_EMAIL" ]; then printf ", email:$CSR_EMAIL" >> $2; fi | |
} | |
#------------------------------ | |
# Check if certificate is in DER (binary) format | |
#------------------------------ | |
function isCertKeyDER() { | |
if [ $(file $1 | awk -F':' '{print $NF}' | tr -d ' ') == 'data' ]; then | |
logInfo "Provided certificate/keystore $1 is a binary file" | |
return 0 | |
else | |
logInfo "Provided certificate/keystore $1 is NOT a binary file" | |
return 1 | |
fi | |
} | |
#------------------------------ | |
# Check if certificate is in the correct format (PEM Base64), and convert if necessary | |
#------------------------------ | |
function getCorrectCertFormat() { | |
if isCertKeyDER $1; then | |
if openssl x509 -noout -text -inform der -in $1 > /dev/null 2>&1; then | |
openssl x509 -inform der -in $1 -outform pem -out $1-converted.pem > /dev/null 2>&1 | |
logInfo "Converting DER certificate to PEM format: $1-converted.pem" | |
declare -g "$2"="$1-converted.pem" | |
return 0 | |
fi | |
if openssl pkcs7 -print_certs -inform der -in $1 > /dev/null 2>&1; then | |
openssl pkcs7 -print_certs -inform der -in $1 2>/dev/null | grep -vE '^subject|^issuer|^$' > $1-converted.pem | |
logInfo "Converting DER PKCS#7 certificate to PEM mulit-cert format: $1-converted.pem" | |
declare -g "$2"="$1-converted.pem" | |
return 0 | |
fi | |
if openssl pkcs12 -nokeys -info -in $1 -passin pass: 2>&1 | grep 'BEGIN CERTIFICATE' > /dev/null; then | |
openssl pkcs12 -nokeys -info -in $1 -passin pass: 2>/dev/null | sed -n '/^-----BEGIN CERTIFICATE-----/,/^-----END CERTIFICATE-----/p' > $1-converted.pem | |
openssl pkcs12 -nocerts -nodes -info -in $1 -passin pass: 2>/dev/null | sed -n '/^-----BEGIN PRIVATE KEY-----/,/^-----END PRIVATE KEY-----/p' > $REQUEST_DIR/pkcs12-extracted-key.key | |
if [ -s $-converted.pem ]; then logInfo "Extracting certificates from PKCS#12 keystore to $1-converted.pem"; fi | |
if [ -s $REQUEST_DIR/pkcs12-extracted-key.key ]; then logInfo "Extracting private key from PKCS#12 keystore to $REQUEST_DIR/pkcs12-extracted-key.key"; fi | |
declare -g "$2"="$1-converted.pem" | |
return 0 | |
elif openssl pkcs12 -nokeys -info -in $1 -passin pass:Antidisestablishmentarianism123581321 2>&1 | grep 'invalid password' > /dev/null 2>&1; then | |
read -r -s -p $'\n'"Enter password for PKCS#12 keystore: " PKCS12_KEYSTORE_PASSWORD | |
PKCS12_KEYSTORE_PASSWORD_VALIDATED=0 | |
i=0 | |
while [ $i -lt 3 ]; do | |
if [ -z "$PKCS12_KEYSTORE_PASSWORD" ]; then | |
read -r -s -p $'\n'"${YELLOW}Enter password for PKCS#12 keystore:${NORMAL} " PKCS12_KEYSTORE_PASSWORD | |
elif openssl pkcs12 -nokeys -info -in $1 -passin pass:$PKCS12_KEYSTORE_PASSWORD 2>&1 | grep 'invalid password' > /dev/null 2>&1; then | |
read -r -s -p $'\n'"${YELLOW}Invalid password, enter password for PKCS#12 keystore:${NORMAL} " PKCS12_KEYSTORE_PASSWORD | |
else | |
PKCS12_KEYSTORE_PASSWORD_VALIDATED=1 | |
break | |
fi | |
((++i)) | |
done | |
if [ $PKCS12_KEYSTORE_PASSWORD_VALIDATED -eq 0 ]; then errorMessage 'Unable to validate keystore password' '-' '-'; fi | |
openssl pkcs12 -nokeys -info -in $1 -passin pass:$PKCS12_KEYSTORE_PASSWORD 2>/dev/null | sed -n '/^-----BEGIN CERTIFICATE-----/,/^-----END CERTIFICATE-----/p' > $1-converted.pem | |
openssl pkcs12 -nocerts -nodes -info -in $1 -passin pass:$PKCS12_KEYSTORE_PASSWORD 2>/dev/null | sed -n '/^-----BEGIN PRIVATE KEY-----/,/^-----END PRIVATE KEY-----/p' > $REQUEST_DIR/pkcs12-extracted-key.key | |
if [ -s $-converted.pem ]; then logInfo "Extracting certificates from PKCS#12 keystore to $1-converted.pem"; fi | |
if [ -s $REQUEST_DIR/pkcs12-extracted-key.key ]; then logInfo "Extracting private key from PKCS#12 keystore to $REQUEST_DIR/pkcs12-extracted-key.key"; fi | |
declare -g "$2"="$1-converted.pem" | |
echo '' | |
return 0 | |
fi | |
else | |
if openssl x509 -noout -text -in $1 > /dev/null 2>&1; then | |
logInfo "No conversion necessary for $1" | |
declare -g "$2"="$1" | |
return 0 | |
fi | |
if openssl pkcs7 -print_certs -in $1 > /dev/null 2>&1; then | |
openssl pkcs7 -print_certs -in $1 2>/dev/null | grep -vE '^subject|^issuer|^$' > $1-converted.pem | |
logInfo "Converting PKCS#7 certificate to PEM multi-cert format: $1-converted.pem" | |
declare -g "$2"="$1-converted.pem" | |
return 0 | |
fi | |
fi | |
logInfo "Unknown certificate format for $1" | |
declare -g "$2"="Unknown format" | |
return 0 | |
} | |
#------------------------------ | |
# Check if private key is in the correct format (PEM Base64), and convert if necessary | |
#------------------------------ | |
function getCorrectKeyFormat() { | |
unset key_path | |
unset PKCS12_KEYSTORE_PASSWORD | |
if isCertKeyDER $1; then | |
logInfo "Converting private key from DER to PEM format: $1-converted.key" | |
if openssl rsa -inform der -in $1 > $1-converted.key > /dev/null 2>&1; then | |
key_path="$1-converted.key" | |
elif openssl pkcs12 -nokeys -info -in $1 -passin pass:VMware123! 2>&1 | grep 'invalid password' > /dev/null 2>&1; then | |
read -r -s -p $'\n'"Enter password for PKCS#12 keystore: " PKCS12_KEYSTORE_PASSWORD | |
while [ -z "$PKCS12_KEYSTORE_PASSWORD" ]; do read -r -s -p $'\n'"${YELLOW}Enter password for PKCS#12 keystore:${NORMAL} " PKCS12_KEYSTORE_PASSWORD; done | |
openssl pkcs12 -nocerts -nodes -info -in $1 -passin pass:$PKCS12_KEYSTORE_PASSWORD 2>/dev/null | sed -n '/^----------BEGIN PRIVATE KEY----------/,/^-----END PRIVATE KEY-----/p' > $REQUEST_DIR/pkcs12-extracted-key.key | |
if [ -s $REQUEST_DIR/pkcs12-extracted-key.key ]; then | |
key_path="$REQUEST_DIR/pkcs12-extracted-key.key" | |
else | |
errorMessage "Unable to export private key from PKCS#12 keystore" '-' '-' | |
fi | |
else | |
errorMessage "Unable to convert private key $1 to PEM format" '-' '-' | |
fi | |
else | |
if grep 'ENCRYPTED' $1 > /dev/null 2>&1; then | |
i=1 | |
read -s -p $'\n'"Private key is encrypted, enter pass phrase to decrypt: " PRIVATE_KEY_PASSPHRASE | |
while [ -z "$PRIVATE_KEY_PASSPHRASE" ]; do | |
if [ $i -gt 3 ]; then errorMessage "No pass phrase provided" '-' '-'; fi | |
read -s -p $'\n'"${YELLOW}Private key is encrypted, enter pass phrase to decrypt:${NORMAL} " PRIVATE_KEY_PASSPHRASE | |
((++i)) | |
done | |
logInfo "Decrypting private key: $1" | |
if openssl rsa -in $1 -out $1-decrypted.key -passin pass:$PRIVATE_KEY_PASSPHRASE > /dev/null 2>&1; then | |
key_path="$1-decrypted.key" | |
echo '' | |
else | |
openssl rsa -in $1 -out $1-decrypted.key -passin pass:$PRIVATE_KEY_PASSPHRASE > $STAGE_DIR/private-key-decrypt-failure.stdout 2>&1 | |
if grep 'disabled for fips' $STAGE_DIR/private-key-decrypt-failure.stdout > /dev/null; then | |
errorMessage "Private key encrypted with cipher not supported by FIPS, cannot decrypt key" '-' '-' | |
else | |
errorMessage "Unable to decrypt private key $1" '-' '-' | |
fi | |
fi | |
elif grep 'BEGIN RSA PRIVATE KEY' $1 > /dev/null 2>&1; then | |
logInfo "Converting private key $1 from PKCS#1 to PKCS#8" | |
if openssl rsa -in $1 -out $1-converted.key > /dev/null 2>&1; then | |
key_path="$1-converted.key" | |
else | |
errorMessage "Unable to convert private key $1 from PKCS#1 to PKCS#8" '-' '-' | |
fi | |
else | |
logInfo "No conversion necessary for $1" | |
key_path="$1" | |
fi | |
fi | |
declare -g CURRENT_PRIVATE_KEY_PATH="$key_path" | |
} | |
#------------------------------ | |
# Check if certificate contains complete CA signing chain | |
#------------------------------ | |
function checkEmbeddedCAChain() { | |
if [ "$(grep 'BEGIN CERTIFICATE' $1 | wc -l)" -gt 1 ]; then | |
CHAIN_START=$(grep -n -m2 'BEGIN CERTIFICATE' $1 | tail -n1 | cut -d':' -f1) | |
tail -n+$CHAIN_START $1 > $STAGE_DIR/embedded-root-chain.pem | |
echo "$STAGE_DIR/embedded-root-chain.pem" | |
fi | |
} | |
#------------------------------ | |
# Get CA chain from certificate file, or by prompt | |
#------------------------------ | |
function getCAChain() { | |
TRUSTED_ROOT_CHAIN=$(checkEmbeddedCAChain "$1") | |
if [ -z "$TRUSTED_ROOT_CHAIN" ]; then | |
read -e -p 'Provide path to the Certificate Authority chain: ' TRUSTED_ROOT_CHAIN_INPUT | |
while [ ! -f "$TRUSTED_ROOT_CHAIN_INPUT" ]; do read -e -p $'\n'"${YELLOW}File not found, please provide path to the CA-signed Certificate Authority chain:${NORMAL} " TRUSTED_ROOT_CHAIN_INPUT; done | |
getCorrectCertFormat "$TRUSTED_ROOT_CHAIN_INPUT" 'TRUSTED_ROOT_CHAIN' | |
while true; do | |
if ! verifyRootChain "$1" "$TRUSTED_ROOT_CHAIN"; then | |
MISSING_CA_OUTPUT=$(printCertificationPathMissingCA "$1" "$TRUSTED_ROOT_CHAIN") | |
cat << EOF | |
$YELLOW | |
The provided certificate signing chain is not complete! | |
$MISSING_CA_OUTPUT | |
$NORMAL | |
EOF | |
read -e -p 'Provide path to the Certificate Authority chain: ' TRUSTED_ROOT_CHAIN_INPUT | |
while [ ! -f "$TRUSTED_ROOT_CHAIN_INPUT" ]; do read -e -p $'\n'"${YELLOW}File not found, please provide path to the CA-signed Certificate Authority chain:${NORMAL} " TRUSTED_ROOT_CHAIN_INPUT; done | |
getCorrectCertFormat "$TRUSTED_ROOT_CHAIN_INPUT" 'TRUSTED_ROOT_CHAIN' | |
else | |
break | |
fi | |
done | |
fi | |
} | |
#------------------------------ | |
# Get location of private key for CA-signed certs | |
#------------------------------ | |
function getPrivateKey() { | |
logInfo "Looking for private key for $2" | |
dynamic_key="$2_KEY" | |
KEY_FOUND=0 | |
if [ -s $REQUEST_DIR/pkcs12-extracted-key.key ]; then | |
CURRENT_KEY_MODULUS_HASH=$(openssl rsa -noout -modulus -in $REQUEST_DIR/pkcs12-extracted-key.key 2>logInfo | md5sum | awk '{print $1}') | |
logInfo "Checking modulus of $REQUEST_DIR/pkcs12-extracted-key.key ($CURRENT_KEY_MODULUS_HASH) against '$1'" | |
if [ "$1" == "$CURRENT_KEY_MODULUS_HASH" ]; then | |
KEY_FOUND=1; | |
getCorrectKeyFormat "$REQUEST_DIR/pkcs12-extracted-key.key" | |
declare -g "$dynamic_key"="$CURRENT_PRIVATE_KEY_PATH" | |
logInfo "Found private key at $CURRENT_PRIVATE_KEY_PATH" | |
fi | |
fi | |
if [ $KEY_FOUND -eq 0 ]; then | |
for key in $(find $TOP_DIR -wholename '*/requests/*.key'); do | |
CURRENT_KEY_MODULUS_HASH=$(openssl rsa -noout -modulus -in $key 2>logInfo | md5sum | awk '{print $1}') | |
logInfo "Checking modulus of $key ($CURRENT_KEY_MODULUS_HASH) against '$1'" | |
if [ "$1" == "$CURRENT_KEY_MODULUS_HASH" ]; then | |
KEY_FOUND=1; | |
getCorrectKeyFormat "$key" | |
declare -g "$dynamic_key"="$CURRENT_PRIVATE_KEY_PATH" | |
logInfo "Found private key at $CURRENT_PRIVATE_KEY_PATH" | |
fi | |
done | |
fi | |
if [ $KEY_FOUND -eq 0 ]; then | |
if $VECS_CLI entry list --store MACHINE_SSL_CERT | grep '__MACHINE_CSR' > /dev/null; then | |
CURRENT_KEY_MODULUS_HASH=$($VECS_CLI entry getkey --store MACHINE_SSL_CERT --alias __MACHINE_CSR | openssl rsa -noout -modulus 2>/dev/null | md5sum | awk '{print $1}') | |
logInfo "Checking modulus of key in __MACHINE_CSR alias ($CURRENT_KEY_MODULUS_HASH) against '$1'" | |
if [ "$1" == "$CURRENT_KEY_MODULUS_HASH" ]; then | |
KEY_FOUND=1; | |
$VECS_CLI entry getkey --store MACHINE_SSL_CERT --alias __MACHINE_CSR > $STAGE_DIR/vmca_issued_key.key | |
getCorrectKeyFormat "$STAGE_DIR/vmca_issued_key.key" | |
declare -g "$dynamic_key"="$CURRENT_PRIVATE_KEY_PATH" | |
logInfo 'Found private key in the __MACHINE_CSR entry in VECS' | |
fi | |
fi | |
fi | |
if [ $KEY_FOUND -eq 0 ]; then | |
unset CURRENT_PRIVATE_KEY_PATH | |
read -e -p "Provide path to the ${CYAN}$3${NORMAL} private key: " PRIVATE_KEY_INPUT | |
while [ ! -f "$PRIVATE_KEY_INPUT" ]; do read -e -p $'\n'"${YELLOW}File not found, please provide path to the $3 private key:${NORMAL} " PRIVATE_KEY_INPUT; done | |
getCorrectKeyFormat "$PRIVATE_KEY_INPUT" | |
declare -g "$dynamic_key"="$CURRENT_PRIVATE_KEY_PATH" | |
logInfo "Customer provided key: $CURRENT_PRIVATE_KEY_PATH" | |
fi | |
} | |
#------------------------------ | |
# Verify a certificate, private key | |
#------------------------------ | |
function verifyCertAndKey() { | |
if [ ! -f $1 ]; then errorMessage "Could not locate certificate $1"; fi | |
if [ ! -f $2 ]; then errorMessage "Could not locate private key $2"; fi | |
CERT_HASH=$(openssl x509 -noout -modulus -in $1 2>/dev/null | md5sum) | |
KEY_HASH=$(openssl rsa -noout -modulus -in $2 2>/dev/null| md5sum) | |
logInfo "Modulus of $1: $CERT_HASH" | |
logInfo "Modulus of $2: $KEY_HASH" | |
if [ "$CERT_HASH" != "$KEY_HASH" ]; then errorMessage "The private key $2 does not correspond to the certificate $1"; fi | |
} | |
#------------------------------ | |
# Verifies root chain by subject/issuer strings | |
#------------------------------ | |
function verifyRootChain() { | |
unset EXPIRED_CA_CERTS | |
rm $STAGE_DIR/root-chain-cert-*.crt 2>/dev/null | |
csplit -s -z -f $STAGE_DIR/root-chain-cert- -b %02d.crt $2 '/-----BEGIN CERTIFICATE-----/' '{*}' 2>&1 | logDebug | |
logInfo "Contents of trusted root chain $2" | |
openssl crl2pkcs7 -nocrl -certfile $2 2>/dev/null | openssl pkcs7 -print_certs -noout 2>&1 | logInfo | |
ISSUER_TO_CHECK=$(openssl x509 -noout -issuer -in $1 2>/dev/null | sed -e 's/issuer= //') | |
FOUND_ROOT=0 | |
NUM_CA_CERTS=$(ls $STAGE_DIR/root-chain-cert-* | wc -l) | |
i=0 | |
while [ $i -lt $NUM_CA_CERTS ]; do | |
logInfo "Looking for issuing CA '$ISSUER_TO_CHECK'" | |
for cert in $(ls $STAGE_DIR/root-chain-cert-*); do | |
CURRENT_SUBJECT=$(openssl x509 -noout -subject -in $cert 2>/dev/null | sed -e 's/subject= //') | |
if [ "$ISSUER_TO_CHECK" == "$CURRENT_SUBJECT" ]; then | |
logInfo "Found issuing CA '$ISSUER_TO_CHECK' in $cert" | |
if isExpired "$cert" 'file'; then | |
logInfo "CA Certificate $CURRENT_SUBJECT is expired!" | |
EXPIRED_CA_CERTS+=$'\n\t'"$CURRENT_SUBJECT" | |
fi | |
ISSUER_TO_CHECK=$(openssl x509 -noout -issuer -in $cert 2>/dev/null | sed -e 's/issuer= //') | |
if [ "$ISSUER_TO_CHECK" == "$CURRENT_SUBJECT" ]; then FOUND_ROOT=1; fi | |
break | |
fi | |
done | |
((++i)) | |
done | |
if [ -n "$EXPIRED_CA_CERTS" ]; then | |
ERROR_MESSAGE='The following provided CA certificates are expired:' | |
ERROR_MESSAGE+=$'\n'$(echo "$EXPIRED_CA_CERTS" | sort | uniq | grep -vE '^$') | |
ERROR_MESSAGE+=$'\n\nInstallation of the certificates cannot continue' | |
errorMessage "$ERROR_MESSAGE" 2 | |
fi | |
if [ $FOUND_ROOT == 0 ]; then | |
return 1 | |
else | |
return 0 | |
fi | |
} | |
#------------------------------ | |
# Verifies CA certificates in signing chain are present in VMware Directory | |
#------------------------------ | |
function checkCACertsPresent() { | |
CHAIN_COMPLETE=0 | |
CHECK=1 | |
CURRENT_CERT=$1 | |
while [ $CHECK -gt 0 ]; do | |
CURRENT_SUBJECT=$(echo "$CURRENT_CERT" | openssl x509 -noout -subject 2>/dev/null | sed -e 's/subject= //') | |
CURRENT_ISSUER=$(echo "$CURRENT_CERT" | openssl x509 -noout -issuer 2>/dev/null | sed -e 's/issuer= //') | |
if [ "$CURRENT_SUBJECT" != "$CURRENT_ISSUER" ]; then | |
CURRENT_AUTH_KEY_ID=$(echo "$CURRENT_CERT" | openssl x509 -noout -text 2>/dev/null | grep -A1 'Authority Key Id' | grep 'keyid' | sed 's/keyid://' | tr -d ': ') | |
if $DIR_CLI trustedcert list --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>/dev/null | grep 'CN(id)' | grep "$CURRENT_AUTH_KEY_ID" > /dev/null; then | |
CURRENT_CERT=$($DIR_CLI trustedcert get --id $CURRENT_AUTH_KEY_ID --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" --outcert /dev/stdout 2>/dev/null) | |
else | |
CHECK=0 | |
fi | |
else | |
CHAIN_COMPLETE=1 | |
CHECK=0 | |
fi | |
done | |
if [ $CHAIN_COMPLETE == 1 ]; then | |
return 0 | |
else | |
return 1 | |
fi | |
} | |
#------------------------------ | |
# Check for expired CA certificates in certificate chain | |
#------------------------------ | |
function checkEmbeddedChain() { | |
NUM_CERTS=$(echo "$1" | grep 'BEGIN CERTIFICATE' | wc -l) | |
if [ $NUM_CERTS -gt 1 ]; then | |
echo "$1" > $STAGE_DIR/embedded-chain-check-cert.pem | |
csplit -z -s -f $STAGE_DIR/embedded-chain-check- -b %02d.crt $STAGE_DIR/embedded-chain-check-cert.pem '/-----BEGIN CERTIFICATE-----/' '{*}' | |
for cert in $(ls $STAGE_DIR/embedded-chain-check-*.crt); do | |
logInfo "Running check on embedded CA cert against $cert" | |
if isExpired "$cert" 'file'; then | |
logInfo "The CA cert $cert is expired" | |
rm $STAGE_DIR/embedded-chain-check-* 2>&1 | logDebug | |
return 1 | |
fi | |
done | |
rm $STAGE_DIR/embedded-chain-check-* 2>&1 | logDebug | |
return 0 | |
else | |
return 0 | |
fi | |
} | |
#------------------------------ | |
# Prompt options for replacing VMCA certificate | |
#------------------------------ | |
function promptReplaceVMCA() { | |
VMCA_REPLACE='SELF-SIGNED' | |
unset VMCA_REPLACE_INPUT | |
VMCA_REGENERATE_CERTIFICATES=1 | |
header 'Select VMCA Certificate Replacement Method' | |
echo '1. Replace VMCA certificate with a self-signed certificate' | |
echo '2. Replace VMCA certificate with a self-signed certificate' | |
echo ' and regenerate all certificates' | |
echo '3. Replace VMCA certificate with a CA-signed certificate' | |
read -p $'\nSelect an option [Return to Main Menu]: ' VMCA_REPLACE_INPUT | |
if [ -z "$VMCA_REPLACE_INPUT" ]; then return 1; fi | |
if [ "$VMCA_REPLACE_INPUT" == '1' ]; then VMCA_REGENERATE_CERTIFICATES=0; fi | |
if [ "$VMCA_REPLACE_INPUT" == '3' ]; then VMCA_REPLACE='CA-SIGNED'; fi | |
logInfo "User selected to replace VMCA certificate with a $VMCA_REPLACE certificate" | |
if [ "$VMCA_REGENERATE_CERTIFICATES" == '1' ]; then | |
logInfo 'Certificates will be regenerated and signed by the VMCA' | |
else | |
logInfo 'Certificates will NOT be regenerated' | |
fi | |
return 0 | |
} | |
#------------------------------ | |
# Replace all certificates with VMCA-signed certs | |
#------------------------------ | |
function resetAllCertificates() { | |
STS_REPLACE='VMCA-SIGNED' | |
MACHINE_SSL_REPLACE='VMCA-SIGNED' | |
SOLUTION_USER_REPLACE='VMCA-SIGNED' | |
AUTH_PROXY_REPLACE='VMCA-SIGNED' | |
AUTO_DEPLOY_CA_REPLACE='SELF-SIGNED' | |
authenticateIfNeeded | |
getCSRInfo | |
case $NODE_TYPE in | |
embedded|infrastructure) | |
replaceMachineSSLCert | |
replaceSolutionUserCerts | |
if [ $NODE_TYPE = 'embedded' ]; then | |
replaceDataEnciphermentCertificate 'replace-only' | |
replaceAuthProxyCert | |
replaceAutoDeployCACert | |
if [[ "$VC_VERSION" =~ ^8 ]]; then replaceSMSCertificate 'VMCA-signed' 'sps-extesion' 'no-restart'; fi | |
manageVCExtensionThumbprints 'Update' | |
updateAutoDeployDB | |
fi | |
if [[ "$VC_VERSION" =~ ^6 ]]; then replaceVMDirCert; fi | |
replaceSSOSTSCert | |
SSLTrustAnchorSelf | |
updateSSLTrustAnchors | |
clearCSRInfo | |
promptRestartVMwareServices | |
;; | |
management) | |
replaceMachineSSLCert | |
replaceSolutionUserCerts | |
replaceDataEnciphermentCertificate 'replace-only' | |
replaceAuthProxyCert | |
replaceAutoDeployCACert | |
if [[ "$VC_VERSION" =~ ^8 ]]; then replaceSMSCertificate 'VMCA-signed' 'sps-extesion' 'no-restart'; fi | |
manageVCExtensionThumbprints 'Update' | |
updateAutoDeployDB | |
SSLTrustAnchorSelf | |
updateSSLTrustAnchors | |
clearCSRInfo | |
promptRestartVMwareServices | |
;; | |
esac | |
} | |
#------------------------------ | |
# Prompt options for replacing STS Signing certificate | |
#------------------------------ | |
function promptReplaceSTS() { | |
STS_REPLACE='VMCA-SIGNED' | |
header 'Select STS Signing Certificate Replacement Method' | |
echo '1. Replace STS Signing certificate with a VMCA-signed certificate' | |
echo '2. Replace STS Signing certificate with a CA-signed certificate' | |
read -p $'\nSelect an option [Return to Main Menu]: ' STS_REPLACE_INPUT | |
if [ -z "$STS_REPLACE_INPUT" ]; then return 1; fi | |
if [ "$STS_REPLACE_INPUT" == '2' ]; then STS_REPLACE='CA-SIGNED'; fi | |
logInfo "User selected to replace STS Signing certificate with a $STS_REPLACE certificate" | |
return 0 | |
} | |
#------------------------------ | |
# Prompt options for replacing Machine SSL certificate | |
#------------------------------ | |
function promptReplaceMachineSSL() { | |
MACHINE_SSL_REPLACE='VMCA-SIGNED' | |
unset MACHINE_SSL_REPLACE_INPUT | |
header 'Select Machine SSL Certificate Replacement Method' | |
echo ' 1. Replace Machine SSL certificate with a VMCA-signed certificate' | |
echo ' 2. Replace Machine SSL certificate with a CA-signed certificate' | |
read -p $'\nSelect an option [Return to Main Menu]: ' MACHINE_SSL_REPLACE_INPUT | |
if [ -z "$MACHINE_SSL_REPLACE_INPUT" ]; then | |
return 1 | |
else | |
if [ "$MACHINE_SSL_REPLACE_INPUT" == '2' ]; then MACHINE_SSL_REPLACE='CA-SIGNED'; fi | |
logInfo "User selected to replace Machine SSL certificate with a $MACHINE_SSL_REPLACE certificate" | |
return 0 | |
fi | |
} | |
#------------------------------ | |
# Prompt options for replacing Solution User certificates | |
#------------------------------ | |
function promptReplaceSolutionUsers() { | |
SOLUTION_USER_REPLACE='VMCA-SIGNED' | |
unset SOLUTION_USER_REPLACE_INPUT | |
header 'Select Solution User Certificate Replacement Method' | |
echo ' 1. Replace Solution User certificates with VMCA-signed certificates' | |
echo ' 2. Replace Solution User certificates with CA-signed certificates (not recommended)' | |
read -p $'\nSelect an option [Return to Main Menu]: ' SOLUTION_USER_REPLACE_INPUT | |
if [ -z "$SOLUTION_USER_REPLACE_INPUT" ]; then return 1; fi | |
if [ "$SOLUTION_USER_REPLACE_INPUT" == '2' ]; then | |
unset SOLUTION_USER_REPLACE_INPUT | |
cat << EOF | |
${YELLOW} | |
-------------------------!!! STOP !!!---------------------------- | |
Solution User certificates are used by services internally to | |
authenticate into vCenter and the SSO instance. | |
These certificates are not presented externally (as of 7.0 and later) | |
and it is VMware's recommendation that they be VMCA-signed | |
certificates. | |
The replacement options, again, are: | |
1. Replace Solution User certificates with VMCA-signed certificates | |
2. Replace Solution User certificates with CA-signed certificates (not recommended)${NORMAL} | |
EOF | |
read -p $'\n'"${YELLOW}Please confirm your selection [Return to Main Menu]:${NORMAL} " SOLUTION_USER_REPLACE_INPUT | |
if [ -z "$SOLUTION_USER_REPLACE_INPUT" ]; then return 1; fi | |
fi | |
if [ "$SOLUTION_USER_REPLACE_INPUT" == '2' ]; then | |
SOLUTION_USER_REPLACE='CA-SIGNED' | |
fi | |
logInfo "User selected to replace Solution User certificates with $SOLUTION_USER_REPLACE certificates" | |
return 0 | |
} | |
#------------------------------ | |
# Prompt options for replacing Authentication Proxy certificate | |
#------------------------------ | |
function promptReplaceAuthProxy() { | |
AUTH_PROXY_REPLACE='VMCA-SIGNED' | |
unset AUTH_PROXY_REPLACE_INPUT | |
header 'Select Authentication Proxy Certificate Replacement Method' | |
echo '1. Replace Authentication Proxy certificate with VMCA-signed certificate' | |
echo '2. Replace Authentication Proxy certificate with CA-signed certificate' | |
read -p $'\nSelect an option [Return to Main Menu]: ' AUTH_PROXY_REPLACE_INPUT | |
if [ -z "$AUTH_PROXY_REPLACE_INPUT" ]; then return 1; fi | |
if [ "$AUTH_PROXY_REPLACE_INPUT" == '2' ]; then AUTH_PROXY_REPLACE='CA-SIGNED'; fi | |
logInfo "User selected to replace Authentication Proxy certifcate with a $AUTH_PROXY_REPLACE certificate" | |
return 0 | |
} | |
#------------------------------ | |
# Prompt options for replacing Auto Deploy CA certificate | |
#------------------------------ | |
function promptReplaceAutoDeployCA() { | |
AUTO_DEPLOY_CA_REPLACE='SELF-SIGNED' | |
unset AUTO_DEPLOY_CA_REPLACE_INPUT | |
header 'Select Auto Deploy CA Certificate Replacement Method' | |
echo '1. Replace Auto Deploy CA certificate with a self-signed certificate' | |
echo '2. Replace Auto Deploy CA certificate with a CA-signed certificate' | |
read -p $'\nSelect an option [Return to Main Menu]: ' AUTO_DEPLOY_CA_REPLACE_INPUT | |
if [ -z "$AUTO_DEPLOY_CA_REPLACE_INPUT" ]; then return 1; fi | |
if [ "$AUTO_DEPLOY_CA_REPLACE_INPUT" == '2' ]; then AUTO_DEPLOY_CA_REPLACE='CA-SIGNED'; fi | |
logInfo "User selected to replace Auto Deploy CA certificate with a $AUTO_DEPLOY_CA_REPLACE certificate" | |
return 0 | |
} | |
#------------------------------ | |
# Prompt options for replacing VMDir certificate | |
#------------------------------ | |
function promptReplaceVMDir() { | |
VMDIR_REPLACE='VMCA-SIGNED' | |
unset VMDIR_REPLACE_INPUT | |
header 'Select VMDir Certificate Replacement Method' | |
echo '1. Replace VMware Directory Service certificate with a VMCA-signed certificate' | |
echo '2. Replace VMware Directory Service certificate with a CA-signed certificate' | |
read -p $'\nSelect an option [Return to Main Menu]: ' VMDIR_REPLACE_INPUT | |
if [ -z "$VMDIR_REPLACE_INPUT" ]; then return 1; fi | |
if [ "$VMDIR_REPLACE_INPUT" == '2' ]; then VMDIR_REPLACE='CA-SIGNED'; fi | |
logInfo "User selected to replace VMDir certificate with a $VMDIR_REPLACE certificate" | |
return 0 | |
} | |
#------------------------------ | |
# Remove legacy VMDir certificate | |
#------------------------------ | |
function removeVMDirCert() { | |
if [ -f /usr/lib/vmware-vmdir/share/config/vmdircert.pem ]; then | |
header 'Manage VMware Directory Certificate' | |
echo $'\n'"The certificate at ${CYAN}/usr/lib/vmware-vmdir/share/config/vmdircert.pem${NORMAL}" | |
echo 'is no longer used in vCenter 7.0 and later.' | |
read -p $'\nRemove this certificate and private key [n]: ' REMOVE_LEGACY_VMDIR_CERT_INPUT | |
if [ -z $REMOVE_LEGACY_VMDIR_CERT_INPUT ]; then REMOVE_LEGACY_VMDIR_CERT_INPUT='n'; fi | |
if [[ "$REMOVE_LEGACY_VMDIR_CERT_INPUT" =~ ^[Yy] ]]; then | |
header 'Remove Legacy VMware Directory Certificate' | |
backupFilesystemCertKey '/usr/lib/vmware-vmdir/share/config/vmdircert.pem' '/usr/lib/vmware-vmdir/share/config/vmdirkey.pem' 'vmdir' | |
task 'Remove legacy VMDir certificate' | |
rm /usr/lib/vmware-vmdir/share/config/vmdircert.pem 2>>$LOG || errorMessage 'Unable to remove legacy VMDir certificate' | |
statusMessage 'OK' 'GREEN' | |
if [ -f /usr/lib/vmware-vmdir/share/config/vmdirkey.pem ]; then | |
task 'Remove legacy VMDir private key' | |
rm /usr/lib/vmware-vmdir/share/config/vmdirkey.pem 2>>$LOG || errorMessage 'Unable to remove legacy VMDir private key' | |
statusMessage 'OK' 'GREEN' | |
fi | |
fi | |
fi | |
} | |
#------------------------------ | |
# Replace the VMCA certificate | |
#------------------------------ | |
function replaceVMCACert() { | |
if [ $VMCA_REPLACE == 'SELF-SIGNED' ]; then | |
unset VMCA_CN_INPUT | |
NEW_VMCA_CERT=$STAGE_DIR/vmca.crt | |
NEW_VMCA_KEY=$STAGE_DIR/vmca.key | |
if [ -z "$CSR_COUNTRY" ]; then getCSRInfo 'VMCA'; fi | |
read -p $'\n'"Enter a value for the ${CYAN}CommonName${NORMAL} of the certificate [$VMCA_CN_DEFAULT]: " VMCA_CN_INPUT | |
if [ -z "$VMCA_CN_INPUT" ]; then VMCA_CN_INPUT="$VMCA_CN_DEFAULT"; fi | |
header 'Replace VMCA Certificate' | |
generateCertoolConfig 'vmca' "$VMCA_CN_INPUT" | |
task 'Generate VMCA certificate' | |
$CERTOOL --genselfcacert --outcert=$NEW_VMCA_CERT --outprivkey=$NEW_VMCA_KEY --config=$STAGE_DIR/vmca.cfg > /dev/null 2>&1 || errorMessage 'Unable to generate new VMCA certificate' | |
statusMessage 'OK' 'GREEN' | |
else | |
unset VMCA_CA_SIGNED_OPTION_INPUT | |
header 'Replace VMCA Certificate' | |
echo $'\n1. Generate Certificate Signing Request and Private Key' | |
echo '2. Generate Certificate Signing Request and Private Key' | |
echo ' from custom OpenSSL configuration file' | |
echo '3. Import CA-signed Certificate and Key' | |
read -p $'\nSelect an option [Return to Main Menu]: ' VMCA_CA_SIGNED_OPTION_INPUT | |
if [ -z $VMCA_CA_SIGNED_OPTION_INPUT ]; then return 1; fi | |
if [ "$VMCA_CA_SIGNED_OPTION_INPUT" == '3' ]; then | |
unset VMCA_CERT_INPUT | |
read -e -p "Provide path to the CA-signed ${CYAN}VMCA${NORMAL} certificate: " VMCA_CERT_INPUT | |
while [ ! -f "$VMCA_CERT_INPUT" ]; do read -e -p $'\n'"${YELLOW}File not found, please provide path to the CA-signed VMCA certificate:${NORMAL} " VMCA_CERT_INPUT; done | |
getCorrectCertFormat "$VMCA_CERT_INPUT" 'NEW_VMCA_CERT' | |
NEW_VMCA_CERT_MODULUS_HASH=$(openssl x509 -noout -modulus -in $NEW_VMCA_CERT 2>/dev/null | md5sum | awk '{print $1}') | |
logInfo "Provided new VMCA certificate:" | |
logDetails "$(cat $NEW_VMCA_CERT)" | |
logInfo "New VMCA certificate details:" | |
logDetails "$(openssl x509 -noout -text -fingerprint -sha1 -in $NEW_VMCA_CERT)" | |
getPrivateKey "$NEW_VMCA_CERT_MODULUS_HASH" "NEW_VMCA" 'VMCA' | |
getCAChain "$NEW_VMCA_CERT" | |
header 'Certificate Verification' | |
task 'Verifying certificate and key: ' | |
verifyCertAndKey $NEW_VMCA_CERT $NEW_VMCA_KEY | |
statusMessage 'OK' 'GREEN' | |
task 'Verifying CA certificate: ' | |
isCertCA "$(cat $NEW_VMCA_CERT)" || errorMessage "The provided certificate $NEW_VMCA_CERT is not a CA certificate." | |
openssl x509 -in $NEW_VMCA_CERT | cat /dev/stdin $TRUSTED_ROOT_CHAIN > $STAGE_DIR/vmca-complete-chain.pem | |
NEW_VMCA_CERT="$STAGE_DIR/vmca-complete-chain.pem" | |
statusMessage 'OK' 'GREEN' | |
else | |
unset VMCA_CN_INPUT | |
NEW_VMCA_CSR=$REQUEST_DIR/vmca-$TIMESTAMP.csr | |
NEW_VMCA_KEY=$REQUEST_DIR/vmca-$TIMESTAMP.key | |
if [ "$VMCA_CA_SIGNED_OPTION_INPUT" == '1' ]; then | |
NEW_VMCA_CFG=$REQUEST_DIR/vmca.cfg | |
if [ -z "$CSR_COUNTRY" ]; then getCSRInfo 'VMCA'; fi | |
read -p $'\n'"Enter a value for the ${CYAN}CommonName${NORMAL} of the certificate [CA]: " VMCA_CN_INPUT | |
if [ -z "$VMCA_CN_INPUT" ]; then VMCA_CN_INPUT='CA'; fi | |
defineSANEntries 'vmca' 'VMCA' | |
generateOpensslConfig "$VMCA_CN_INPUT" $NEW_VMCA_CFG 'vmca' | |
else | |
logInfo 'User has chosen to generate the VMCA private key and CSR from a custom OpenSSL configuration file' | |
read -e -p $'\nEnter path to custom OpenSSL configuration file: ' NEW_VMCA_CFG | |
while [ ! -f "$NEW_VMCA_CFG" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to custom OpenSSL configuration file:${NORMAL} " NEW_VMCA_CFG; done | |
logDetails "$(cat $NEW_VMCA_CFG)" | |
fi | |
generateCSR $NEW_VMCA_CSR $NEW_VMCA_KEY "$NEW_VMCA_CFG" || errorMessage "Unable to generate Certificate Signing Request and Private Key" | |
printf "\nCertificate Signing Request generated at ${CYAN}${NEW_VMCA_CSR}${NORMAL}" | |
printf "\nPrivate Key generated at ${CYAN}${NEW_VMCA_KEY}${NORMAL}\n" | |
return 1 | |
fi | |
fi | |
backupFilesystemCertKey '/var/lib/vmware/vmca/root.cer' '/var/lib/vmware/vmca/privatekey.pem' 'VMCA' | |
task 'Reconfigure VMCA' | |
$CERTOOL --rootca --cert=$NEW_VMCA_CERT --privkey=$NEW_VMCA_KEY > /dev/null 2>&1 || errorMessage 'Unable to reconfigure the VMCA with the new certificate' | |
statusMessage 'OK' 'GREEN' | |
if [ $VMCA_REPLACE == 'CA-SIGNED' ]; then | |
task 'Publish CA certificates to VMDir' | |
publishCASigningCertificates $TRUSTED_ROOT_CHAIN | |
fi | |
if [ -f /etc/vmware-sso/keys/ssoserverRoot.crt ]; then | |
task 'Update VMCA certificate on filesystem' | |
mv /etc/vmware-sso/keys/ssoserverRoot.crt /etc/vmware-sso/keys/ssoserverRoot.crt.old > /dev/null 2>&1 || errorMessage 'Unable to backup old SSO server root certificate' | |
cp $VMCA_CERT /etc/vmware-sso/keys/ssoserverRoot.crt > /dev/null 2>&1 || errorMessage 'Unable to update SSO server root certificate' | |
statusMessage 'OK' 'GREEN' | |
fi | |
return 0 | |
} | |
#------------------------------ | |
# Replace the Machine SSL certificate | |
#------------------------------ | |
function replaceMachineSSLCert() { | |
if [ "$NODE_TYPE" != 'infrastructure' ] && ! checkService 'vmware-vpostgres'; then | |
echo "${YELLOW}The vPostgres service is stopped!" | |
echo "Please ensure this service is running before replacing the Machine SSL certificate." | |
echo "Hint: Check the number of CRL entries in VECS${NORMAL}" | |
return 1 | |
fi | |
if [ $MACHINE_SSL_REPLACE == 'VMCA-SIGNED' ]; then | |
MACHINE_SSL_CERT=$STAGE_DIR/machine-ssl.crt | |
MACHINE_SSL_PUBKEY=$STAGE_DIR/machine-ssl.pub | |
MACHINE_SSL_KEY=$STAGE_DIR/machine-ssl.key | |
if [ -z "$CSR_COUNTRY" ]; then getCSRInfo 'Machine SSL'; fi | |
checkPSCHA | |
if [ -n "$PSC_LB" ]; then | |
unset PSC_LB_ADDITIONAL_HOSTS_INPUT | |
cat << EOF | |
${YELLOW}-------------------------!!! WARNING !!!-------------------------" | |
This PSC is detected to be in an HA configuration!${NORMAL}" | |
The Load Balancer address is detected to be: ${CYAN}${PSC_LB}${NORMAL}" | |
This hostname will be added to the Subject Altnernative Name field. | |
EOF | |
read -p $'\n'"Please add the hostnames of the additional PSCs behind the load balancer (comma-separated list): " PSC_LB_ADDITIONAL_HOSTS_INPUT | |
if [ -z "$PSC_LB_ADDITIONAL_HOSTS_INPUT" ]; then | |
CSR_ADDITIONAL_DNS="$PSC_LB" | |
else | |
CSR_ADDITIONAL_DNS="$PSC_LB,$PSC_LB_ADDITIONAL_HOSTS_INPUT" | |
fi | |
fi | |
header 'Replace Machine SSL Certificate' | |
generateCertoolConfig 'machine-ssl' $PNID | |
task 'Regenerate Machine SSL certificate' | |
regenerateVMCASignedCertificate 'machine-ssl' | |
statusMessage 'OK' 'GREEN' | |
else | |
unset MACHINE_SSL_CA_SIGNED_OPTION_INPUT | |
echo $'\n1. Generate Certificate Signing Request and Private Key' | |
echo '2. Generate Certificate Signing Request and Private Key' | |
echo ' from custom OpenSSL configuration file' | |
echo '3. Import CA-signed Certificate and Key' | |
read -p $'\nSelect an option [Return to Main Menu]: ' MACHINE_SSL_CA_SIGNED_OPTION_INPUT | |
if [ -z $MACHINE_SSL_CA_SIGNED_OPTION_INPUT ]; then return 1; fi | |
if [ "$MACHINE_SSL_CA_SIGNED_OPTION_INPUT" == '3' ]; then | |
logInfo 'User has chosen to import a CA-signed Machine SSL certificate and key' | |
read -e -p $'\n'"Provide path to the CA-signed ${CYAN}Machine SSL${NORMAL} certificate: " MACHINE_SSL_CERT_INPUT | |
while [ ! -f "$MACHINE_SSL_CERT_INPUT" ]; do read -e -p $'\n'"${YELLOW}File not found, please provide path to the Machine SSL certificate:${NORMAL} " MACHINE_SSL_CERT_INPUT; done | |
getCorrectCertFormat "$MACHINE_SSL_CERT_INPUT" 'MACHINE_SSL_CERT' | |
MACHINE_SSL_CERT_MODULUS_HASH=$(openssl x509 -noout -modulus -in $MACHINE_SSL_CERT 2>/dev/null | md5sum | awk '{print $1}') | |
if ! checkCertSignatureAlgorithm "$(cat $MACHINE_SSL_CERT)"; then | |
errorMessage 'Certificate is using an unsupported signature algorithm' 'machine-ssl' 'no-task' | |
fi | |
logInfo "Provided new Machine SSL certificate:" | |
logDetails "$(cat $MACHINE_SSL_CERT)" | |
logInfo "New Machine SSL certificate details:" | |
logDetails "$(openssl x509 -noout -text -fingerprint -sha1 -in $MACHINE_SSL_CERT)" | |
getPrivateKey "$MACHINE_SSL_CERT_MODULUS_HASH" "MACHINE_SSL" 'Machine SSL' | |
getCAChain "$MACHINE_SSL_CERT" | |
header 'Certificate Verification' | |
task 'Verifying certificate and key' | |
logInfo "Using Machine SSL cert: $MACHINE_SSL_CERT" | |
logInfo "Using Private Key: $MACHINE_SSL_KEY" | |
logInfo "Using trusted root chain: $TRUSTED_ROOT_CHAIN" | |
verifyCertAndKey "$MACHINE_SSL_CERT" "$MACHINE_SSL_KEY" | |
statusMessage 'OK' 'GREEN' | |
task 'Verifying root chain' | |
verifyRootChain $MACHINE_SSL_CERT $TRUSTED_ROOT_CHAIN || errorMessage 'Certificate Authority chain is not complete' | |
statusMessage 'OK' 'GREEN' | |
task 'Verify PNID included in SAN' | |
cat "$MACHINE_SSL_CERT" | openssl x509 -noout -text 2>/dev/null | grep -A1 'Subject Alternative Name' | grep -i "$PNID" > /dev/null || errorMessage 'The Primary Network Identifier (PNID) is not included in the Subject Alternative Name field' | |
statusMessage 'OK' 'GREEN' | |
header 'Replace Machine SSL Certificate' | |
task 'Publish CA signing certificates' | |
publishCASigningCertificates $TRUSTED_ROOT_CHAIN | |
else | |
unset MACHINE_SSL_CN_INPUT | |
MACHINE_SSL_CSR=$REQUEST_DIR/machine-ssl-$TIMESTAMP.csr | |
MACHINE_SSL_KEY=$REQUEST_DIR/machine-ssl-$TIMESTAMP.key | |
if [ "$MACHINE_SSL_CA_SIGNED_OPTION_INPUT" == '1' ]; then | |
MACHINE_SSL_CFG=$REQUEST_DIR/machine-ssl.cfg | |
logInfo 'User has chosen to generate the Machine SSL private key and CSR' | |
if [ -z "$CSR_COUNTRY" ]; then getCSRInfo 'Machine SSL'; fi | |
read -p "Enter a value for the ${CYAN}CommonName${NORMAL} of the certificate [$HOSTNAME]: " MACHINE_SSL_CN_INPUT | |
if [ -z "$MACHINE_SSL_CN_INPUT" ]; then MACHINE_SSL_CN_INPUT=$HOSTNAME; fi | |
checkPSCHA | |
defineSANEntries 'machine-ssl' 'Machine SSL' | |
generateOpensslConfig $MACHINE_SSL_CN_INPUT $MACHINE_SSL_CFG 'machine-ssl' | |
else | |
logInfo 'User has chosen to generate the Machine SSL private key and CSR from a custom OpenSSL configuration file' | |
read -e -p $'\nEnter path to custom OpenSSL configuration file: ' MACHINE_SSL_CFG | |
while [ ! -f "$MACHINE_SSL_CFG" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to custom OpenSSL configuration file:${NORMAL} " MACHINE_SSL_CFG; done | |
logDetails "$(cat $MACHINE_SSL_CFG)" | |
fi | |
generateCSR $MACHINE_SSL_CSR $MACHINE_SSL_KEY "$MACHINE_SSL_CFG" || errorMessage "Unable to generate Certificate Signing Request and Private Key" | |
printf "\nCertificate Signing Request generated at ${CYAN}${MACHINE_SSL_CSR}${NORMAL}" | |
printf "\nPrivate Key generated at ${CYAN}${MACHINE_SSL_KEY}${NORMAL}\n" | |
return 1 | |
fi | |
fi | |
backupVECSCertKey 'machine-ssl' | |
updateVECS 'machine-ssl' | |
if checkVECSStore 'STS_INTERNAL_SSL_CERT'; then | |
updateVECS 'legacy-lookup-service' 'machine-ssl' | |
fi | |
UPDATED_MACHINE_SSL=1 | |
return 0 | |
} | |
#------------------------------ | |
# Replace Solution User certificates | |
#------------------------------ | |
function replaceSolutionUserCerts() { | |
if [ "$NODE_TYPE" != 'infrastructure' ] && ! checkService 'vmware-vpostgres'; then | |
echo "${YELLOW}The vPostgres service is stopped!" | |
echo "Please ensure this service is running before replacing the Solution User certificates." | |
echo "Hint: Check the number of CRL entries in VECS${NORMAL}" | |
return 1 | |
fi | |
if [ $SOLUTION_USER_REPLACE == 'VMCA-SIGNED' ]; then | |
for soluser in "${SOLUTION_USERS[@]}"; do | |
soluser_fix=$(echo $soluser | sed 's/-/_/g') | |
dynamic_cert="${soluser_fix^^}_CERT" | |
dynamic_key="${soluser_fix^^}_KEY" | |
dynamic_pubkey="${soluser_fix^^}_PUBKEY" | |
declare "$dynamic_cert"=$STAGE_DIR/$soluser.crt | |
declare "$dynamic_key"=$STAGE_DIR/$soluser.key | |
declare "$dynamic_pubkey"=$STAGE_DIR/$soluser.pub | |
done | |
header 'Replace Solution User Certificates' | |
checkServicePrincipals | |
echo 'Generate new certificates and keys:' | |
for soluser in "${SOLUTION_USERS[@]}"; do | |
task " $soluser" | |
soluser_fix=$(echo $soluser | sed 's/-/_/g') | |
dynamic_cert="${soluser_fix^^}_CERT" | |
dynamic_key="${soluser_fix^^}_KEY" | |
dynamic_pubkey="${soluser_fix^^}_PUBKEY" | |
$CERTOOL --genkey --privkey=${!dynamic_key} --pubkey=${!dynamic_pubkey} > /dev/null 2>&1 || errorMessage "Unable to generate a key pair for $soluser" | |
if [ $soluser == 'wcp' ]; then | |
$CERTOOL --gencert --server=$PSC_LOCATION --Name=$soluser --genCIScert --dataencipherment --privkey=${!dynamic_key} --cert=${!dynamic_cert} --config=/dev/null --Country="$CSR_COUNTRY_DEFAULT" --State="$CSR_STATE_DEFAULT" --Locality="$CSR_LOCALITY_DEFAULT" --Organization="$CSR_ORG_DEFAULT" --OrgUnit="mID-$MACHINE_ID" > /dev/null 2>&1 || errorMessage "Unable to generate a VMCA-signed cert for $soluser" | |
else | |
$CERTOOL --gencert --server=$PSC_LOCATION --Name=$soluser --genCIScert --privkey=${!dynamic_key} --cert=${!dynamic_cert} --config=/dev/null --Country="$CSR_COUNTRY_DEFAULT" --State="$CSR_STATE_DEFAULT" --Locality="$CSR_LOCALITY_DEFAULT" --Organization="$CSR_ORG_DEFAULT" --OrgUnit="mID-$MACHINE_ID" --FQDN=$PNID > /dev/null 2>&1 || errorMessage "Unable to generate a VMCA-signed cert for $soluser" | |
fi | |
statusMessage 'OK' 'GREEN' | |
done | |
else | |
unset SOLUTION_USERS_CA_SIGNED_OPTION_INPUT | |
echo $'\n1. Generate Certificate Signing Requests and Private Keys' | |
echo '2. Generate Certificate Signing Request and Private Keys' | |
echo ' from custom OpenSSL configuration files' | |
echo '3. Import CA-signed Certificates and Keys' | |
read -p $'\nSelect an option [Return to Main Menu]: ' SOLUTION_USERS_CA_SIGNED_OPTION_INPUT | |
if [ -z $SOLUTION_USERS_CA_SIGNED_OPTION_INPUT ]; then return 1; fi | |
if [ "$SOLUTION_USERS_CA_SIGNED_OPTION_INPUT" == '3' ]; then | |
logInfo 'User has chosen to import a CA-signed Solution User certificates and keys' | |
for soluser in "${SOLUTION_USERS[@]}"; do | |
unset SOLUTION_USER_CERT_INPUT | |
unset SOLUTION_USER_KEY_INPUT | |
soluser_fix=$(echo $soluser | sed 's/-/_/g') | |
dynamic_cert="${soluser_fix^^}_CERT" | |
dynamic_key="${soluser_fix^^}_KEY" | |
dynamic_cert_modulus="${soluser_fix^^}_MODULUS_HASH" | |
read -e -p $'\n'"Provide path to the CA-signed ${CYAN}${soluser}${NORMAL} certificate: " SOLUTION_USER_CERT_INPUT | |
while [ ! -f "$SOLUTION_USER_CERT_INPUT" ]; do read -e -p $'\n'"${YELLOW}File not found, please provide path to the ${soluser} certificate:${NORMAL} " SOLUTION_USER_CERT_INPUT; done | |
getCorrectCertFormat "$SOLUTION_USER_CERT_INPUT" "$dynamic_cert" | |
if ! checkCertSignatureAlgorithm "$(cat ${!dynamic_cert})"; then | |
errorMessage 'Certificate is using an unsupported signature algorithm' 'soluser' 'no-task' | |
fi | |
declare "$dynamic_cert_modulus"=$(openssl x509 -noout -modulus -in ${!dynamic_cert} 2>/dev/null | md5sum | awk '{print $1}') | |
logInfo "Provided new $soluser certificate:" | |
logDetails "$(cat ${!dynamic_cert})" | |
logInfo "New $soluser certificate details:" | |
logDetails "$(openssl x509 -noout -text -fingerprint -sha1 -in ${!dynamic_cert})" | |
getPrivateKey "${!dynamic_cert_modulus}" "${soluser_fix^^}" "$soluser" | |
done | |
echo '' | |
getCAChain "$MACHINE_CERT" | |
header 'Replace Solution User Certificates' | |
checkServicePrincipals | |
echo 'Verify certificates and keys:' | |
for soluser in "${SOLUTION_USERS[@]}"; do | |
soluser_fix=$(echo $soluser | sed 's/-/_/g') | |
dynamic_cert="${soluser_fix^^}_CERT" | |
dynamic_key="${soluser_fix^^}_KEY" | |
task " $soluser" | |
verifyCertAndKey "${!dynamic_cert}" "${!dynamic_key}" | |
statusMessage 'OK' 'GREEN' | |
done | |
task 'Verifying root chain' | |
verifyRootChain $MACHINE_CERT $TRUSTED_ROOT_CHAIN || errorMessage 'Certificate Authority chain is not complete' | |
statusMessage 'OK' 'GREEN' | |
task 'Publish CA signing certificates' | |
publishCASigningCertificates $TRUSTED_ROOT_CHAIN | |
else | |
for soluser in "${SOLUTION_USERS[@]}"; do | |
soluser_fix=$(echo $soluser | sed 's/-/_/g') | |
dynamic_csr="${soluser_fix^^}_CSR" | |
dynamic_key="${soluser_fix^^}_KEY" | |
declare "$dynamic_csr"=$REQUEST_DIR/$soluser-$TIMESTAMP.csr | |
declare "$dynamic_key"=$REQUEST_DIR/$soluser-$TIMESTAMP.key | |
if [ "$SOLUTION_USERS_CA_SIGNED_OPTION_INPUT" == '1' ]; then | |
dynamic_cfg="${soluser_fix^^}_CFG" | |
declare "$dynamic_cfg"=$REQUEST_DIR/$soluser-$TIMESTAMP.cfg | |
fi | |
done | |
if [ "$SOLUTION_USERS_CA_SIGNED_OPTION_INPUT" == '1' ] && [ -z "$CSR_COUNTRY" ]; then getCSRInfo 'Solution Users'; fi | |
defineSANEntries 'soluser' 'Solution User' | |
for soluser in "${SOLUTION_USERS[@]}"; do | |
soluser_fix=$(echo $soluser | sed 's/-/_/g') | |
dynamic_csr="${soluser_fix^^}_CSR" | |
dynamic_key="${soluser_fix^^}_KEY" | |
dynamic_cfg="${soluser_fix^^}_CFG" | |
if [ "$SOLUTION_USERS_CA_SIGNED_OPTION_INPUT" == '1' ]; then | |
generateOpensslConfig "$soluser-$MACHINE_ID" "${!dynamic_cfg}" "$soluser" | |
else | |
logInfo "User has chosen to generate the $soluser private key and CSR from a custom OpenSSL configuration file" | |
read -e -p $'\n'"Enter path to custom OpenSSL configuration file for ${CYAN}${soluser}${NORMAL}: " SOLUTION_USER_CFG | |
while [ ! -f "$SOLUTION_USER_CFG" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to custom OpenSSL configuration file for ${soluser}:${NORMAL} " SOLUTION_USER_CFG; done | |
declare "$dynamic_cfg"="$SOLUTION_USER_CFG" | |
logDetails "$(cat ${!dynamic_cfg})" | |
fi | |
generateCSR "${!dynamic_csr}" "${!dynamic_key}" "${!dynamic_cfg}" || errorMessage "Unable to generate Certificate Signing Request and Private Key for $soluser" | |
done | |
echo $'\nCertificate Signing Requests generated at:' | |
for soluser in "${SOLUTION_USERS[@]}"; do | |
soluser_fix=$(echo $soluser | sed 's/-/_/g') | |
dynamic_csr="${soluser_fix^^}_CSR" | |
echo "${CYAN}${!dynamic_csr}${NORMAL}" | |
done | |
echo $'\nPrivate Keys generated at:' | |
for soluser in "${SOLUTION_USERS[@]}"; do | |
soluser_fix=$(echo $soluser | sed 's/-/_/g') | |
dynamic_key="${soluser_fix^^}_KEY" | |
echo "${CYAN}${!dynamic_key}${NORMAL}" | |
done | |
return 1 | |
fi | |
fi | |
echo $'\nBackup certificate and private key:' | |
for soluser in "${SOLUTION_USERS[@]}"; do | |
backupVECSCertKey "$soluser" | |
done | |
echo $'\nUpdating certificates and keys in VECS:' | |
for soluser in "${SOLUTION_USERS[@]}"; do | |
updateVECS "$soluser" | |
done | |
echo $'\nUpdating solution user certificates in VMware Directory:' | |
for soluser in "${SOLUTION_USERS[@]}"; do | |
soluser_fix=$(echo $soluser | sed 's/-/_/g') | |
dynamic_cert="${soluser_fix^^}_CERT" | |
replaceServicePrincipalCert "$soluser" "${!dynamic_cert}" | |
done | |
} | |
#------------------------------ | |
# Replace a Solution User certificate in VMDir | |
#------------------------------ | |
function replaceServicePrincipalCert() { | |
task " $1" | |
$DIR_CLI service update --name $1-$MACHINE_ID --cert $2 --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" > /dev/null 2>&1 || errorMessage "Unable to update $1-$MACHINE_ID solution user certificate in VMDir" | |
statusMessage 'OK' 'GREEN' | |
} | |
#------------------------------ | |
# Replace the Authentication Proxy certificate | |
#------------------------------ | |
function replaceAuthProxyCert() { | |
header 'Replace Authentication Proxy Certificate' | |
if [ $AUTH_PROXY_REPLACE = 'VMCA-SIGNED' ]; then | |
generateCertoolConfig 'auth proxy' | |
task 'Regenerate Authentication Proxy certificate' | |
regenerateVMCASignedCertificate 'auth-proxy' | |
statusMessage 'OK' 'GREEN' | |
AUTH_PROXY_CERT=$STAGE_DIR/auth-proxy.crt | |
AUTH_PROXY_KEY=$STAGE_DIR/auth-proxy.key | |
else | |
unset AUTH_PROXY_CA_SIGNED_OPTION_INPUT | |
echo $'\n1. Generate Certificate Signing Request and Private Key' | |
echo '2. Generate Certificate Signing Request and Private Key' | |
echo ' from custom OpenSSL configuration file' | |
echo '3. Import CA-signed Certificate and Key' | |
read -p $'\nSelect an option [Return to Main Menu]: ' AUTH_PROXY_CA_SIGNED_OPTION_INPUT | |
if [ -z $AUTH_PROXY_CA_SIGNED_OPTION_INPUT ]; then return 1; fi | |
if [ "$AUTH_PROXY_CA_SIGNED_OPTION_INPUT" == '3' ]; then | |
read -e -p $'\n'"Provide path to CA-signed ${CYAN}Authentication Proxy${NORMAL} certificate: " AUTH_PROXY_CERT_INPUT | |
while [ ! -f "$AUTH_PROXY_CERT_INPUT" ]; do read -e -p "${YELLOW}File not found, please provide path to the Authentication Proxy certificate:${NORMAL} " AUTH_PROXY_CERT_INPUT; done | |
getCorrectCertFormat "$AUTH_PROXY_CERT_INPUT" 'AUTH_PROXY_CERT' | |
if ! checkCertSignatureAlgorithm "$(cat $AUTH_PROXY_CERT)"; then | |
errorMessage 'Certificate is using an unsupported signature algorithm' 'auth-proxy' 'no-task' | |
fi | |
AUTH_PROXY_CERT_MODULUS_HASH=$(openssl x509 -noout -modulus -in $AUTH_PROXY_CERT 2>/dev/null | md5sum | awk '{print $1}') | |
logInfo "Provided new Authentication Proxy certificate:" | |
logDetails "$(cat $AUTH_PROXY_CERT)" | |
logInfo "New Authentication Proxy certificate details:" | |
logDetails "$(openssl x509 -noout -text -fingerprint -sha1 -in $AUTH_PROXY_CERT)" | |
getPrivateKey "$AUTH_PROXY_CERT_MODULUS_HASH" "AUTH_PROXY" 'Authentication Proxy' | |
getCAChain "$AUTH_PROXY_CERT" | |
task 'Verifying certificates and keys: ' | |
verifyCertAndKey $AUTH_PROXY_CERT $AUTH_PROXY_KEY | |
else | |
AUTH_PROXY_CSR=$REQUEST_DIR/auth-proxy-$TIMESTAMP.csr | |
AUTH_PROXY_KEY=$REQUEST_DIR/auth-proxy-$TIMESTAMP.key | |
if [ "$AUTH_PROXY_CA_SIGNED_OPTION_INPUT" == '1' ]; then | |
AUTH_PROXY_CFG=$REQUEST_DIR/auth-proxy.cfg | |
if [ -z "$CSR_COUNTRY" ]; then getCSRInfo; fi | |
defineSANEntries 'auth-proxy' 'Authentication Proxy' | |
generateOpensslConfig $HOSTNAME $AUTH_PROXY_CFG 'Authentication Proxy' | |
else | |
logInfo 'User has chosen to generate the Authentication Proxy private key and CSR from a custom OpenSSL configuration file' | |
read -e -p $'\nEnter path to custom OpenSSL configuration file: ' AUTH_PROXY_CFG | |
while [ ! -f "$AUTH_PROXY_CFG" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to custom OpenSSL configuration file:${NORMAL} " AUTH_PROXY_CFG; done | |
logDetails "$(cat $AUTH_PROXY_CFG)" | |
fi | |
generateCSR $AUTH_PROXY_CSR $AUTH_PROXY_KEY "$AUTH_PROXY_CFG" || errorMessage "Unable to generate Certificate Signing Request and Private Key" | |
printf "\nCertificate Signing Request generated at ${CYAN}${AUTH_PROXY_CSR}${NORMAL}" | |
printf "\nPrivate Key generated at ${CYAN}${AUTH_PROXY_KEY}${NORMAL}\n" | |
return 1 | |
fi | |
fi | |
if [ $AUTH_PROXY_REPLACE != 'VMCA-SIGNED' ]; then | |
task 'Publish CA signing certificates' | |
publishCASigningCertificates $TRUSTED_ROOT_CHAIN | |
fi | |
if [ -f /var/lib/vmware/vmcam/ssl/rui.crt ] && [ -f /var/lib/vmware/vmcam/ssl/rui.key ]; then backupFilesystemCertKey '/var/lib/vmware/vmcam/ssl/rui.crt' '/var/lib/vmware/vmcam/ssl/rui.crt' 'auth-proxy'; fi | |
task 'Replace certificate on filesystem' | |
mv /var/lib/vmware/vmcam/ssl/vmcamcert.pem /var/lib/vmware/vmcam/ssl/vmcamcert.pem.old > /dev/null 2>&1 || errorMessage 'Unable to backup Authentication Proxy PEM file' | |
cp $AUTH_PROXY_CERT /var/lib/vmware/vmcam/ssl/rui.crt > /dev/null 2>&1 || errorMessage 'Unable to update Authentication Proxy certificate' | |
cp $AUTH_PROXY_KEY /var/lib/vmware/vmcam/ssl/rui.key > /dev/null 2>&1 || errorMessage 'Unable to update Authentication Proxy private key' | |
cat /var/lib/vmware/vmcam/ssl/rui.key <(echo) /var/lib/vmware/vmcam/ssl/rui.crt > /var/lib/vmware/vmcam/ssl/vmcamcert.pem 2>&1 || errorMessage 'Unable to update Authentication Proxy PEM file' | |
chmod 600 /var/lib/vmware/vmcam/ssl/* | |
statusMessage 'OK' 'GREEN' | |
return 0 | |
} | |
#------------------------------ | |
# Replace the Auto Deploy CA certificate | |
#------------------------------ | |
function replaceAutoDeployCACert() { | |
header 'Replace Auto Deploy CA Certificate' | |
if [ $AUTO_DEPLOY_CA_REPLACE == 'SELF-SIGNED' ]; then | |
task 'Regenerate Auto Deploy CA certificate' | |
AUTO_DEPLOY_CA_CONFIG=$(cat << EOF | |
[ req ] | |
default_bits = 2048 | |
distinguished_name = req_distinguished_name | |
encrypt_key = no | |
prompt = no | |
string_mask = nombstr | |
x509_extensions = x509 | |
[ req_distinguished_name ] | |
O = VMware Auto Deploy | |
[ x509 ] | |
basicConstraints = CA:true, pathlen:0 | |
keyUsage = keyCertSign | |
EOF | |
) | |
openssl req -new -x509 -config <(echo -n "$AUTO_DEPLOY_CA_CONFIG") -days 4200 -sha256 -keyout $STAGE_DIR/auto-deploy-ca.key -out $STAGE_DIR/auto-deploy-ca.crt > /dev/null 2>&1 || errorMessage 'Unable to generate new Auto Deploy CA certificate and private key. See log for details.' | |
#openssl req -new -newkey rsa:2048 -nodes -keyout $STAGE_DIR/auto-deploy-ca.key -x509 -out $STAGE_DIR/auto-deploy-ca.crt -subj '/O=VMware Auto Deploy' -days 3650 > /dev/null 2>&1 || errorMessage 'Unable to generate new Auto Deploy CA certificate and private key. See log for details.' | |
statusMessage 'OK' 'GREEN' | |
AUTO_DEPLOY_CA_CERT=$STAGE_DIR/auto-deploy-ca.crt | |
AUTO_DEPLOY_CA_KEY=$STAGE_DIR/auto-deploy-ca.key | |
else | |
unset AUTO_DEPLOY_CA_CA_SIGNED_OPTION_INPUT | |
echo $'\n1. Generate Certificate Signing Request and Private Key' | |
echo '2. Generate Certificate Signing Request and Private Key' | |
echo ' from custom OpenSSL configuration file' | |
echo '3. Import CA-signed Certificate and Key' | |
read -p $'\nChoose option [Return to Main Menu]: ' AUTO_DEPLOY_CA_CA_SIGNED_OPTION_INPUT | |
if [ -z $AUTO_DEPLOY_CA_CA_SIGNED_OPTION_INPUT ]; then return 1; fi | |
if [ "$AUTO_DEPLOY_CA_CA_SIGNED_OPTION_INPUT" == '3' ]; then | |
logInfo 'User has chosen to import a CA-signed Auto Deploy certificate and key' | |
read -e -p $'\n'"Provide path to CA-signed ${CYAN}Auto Deploy CA${NORMAL} certificate: " AUTO_DEPLOY_CA_CERT_INPUT | |
while [ ! -f "$AUTO_DEPLOY_CA_CERT_INPUT" ]; do read -e -p "${YELLOW}File not found, please provide path to the Auto Deploy CA certificate:${NORMAL} " AUTO_DEPLOY_CA_CERT_INPUT; done | |
getCorrectCertFormat "$AUTO_DEPLOY_CA_CERT_INPUT" 'AUTO_DEPLOY_CA_CERT' | |
if ! checkCertSignatureAlgorithm "$(cat $AUTO_DEPLOY_CA_CERT)"; then | |
errorMessage 'Certificate is using an unsupported signature algorithm' 'auto-deploy-ca' 'no-task' | |
fi | |
AUTO_DEPLOY_CA_CERT_MODULUS_HASH=$(openssl x509 -noout -modulus -in $AUTO_DEPLOY_CA_CERT 2>/dev/null | md5sum | awk '{print $1}') | |
logInfo "Provided new Auto Deploy CA certificate:" | |
logDetails "$(cat $AUTO_DEPLOY_CA_CERT)" | |
logInfo "New Auto Deploy CA certificate details:" | |
logDetails "$(openssl x509 -noout -text -fingerprint -sha1 -in $AUTO_DEPLOY_CA_CERT)" | |
getPrivateKey "$AUTO_DEPLOY_CA_CERT_MODULUS_HASH" "AUTO_DEPLOY_CA" 'Auto Deploy CA' | |
getCAChain "$AUTO_DEPLOY_CERT" | |
task 'Verifying certificates and keys' | |
verifyCertAndKey $AUTO_DEPLOY_CA_CERT $AUTO_DEPLOY_CA_KEY | |
task 'Verifying CA certificate' | |
isCertCA "$(cat $AUTO_DEPLOY_CA_CERT)" || errorMessage "The provided certificate ${AUTO_DEPLOY_CA_CERT} is not a CA certificate." | |
statusMessage 'OK' 'GREEN' | |
else | |
AUTO_DEPLOY_CA_CSR=$REQUEST_DIR/auto-deploy-ca-$TIMESTAMP.csr | |
AUTO_DEPLOY_CA_KEY=$REQUEST_DIR/auto-deploy-ca-$TIMESTAMP.key | |
if [ "$AUTO_DEPLOY_CA_CA_SIGNED_OPTION_INPUT" == '1' ]; then | |
AUTO_DEPLOY_CA_CFG=$REQUEST_DIR/auto-deploy-ca.cfg | |
if [ -z "$CSR_COUNTRY" ]; then getCSRInfo; fi | |
defineSANEntries 'rbd' 'Auto Deploy CA' | |
generateOpensslConfig $HOSTNAME $AUTO_DEPLOY_CA_CFG 'Auto Deploy' | |
else | |
logInfo 'User has chosen to generate the Auto Deploy CA private key and CSR from a custom OpenSSL configuration file' | |
read -e -p $'\nEnter path to custom OpenSSL configuration file: ' AUTO_DEPLOY_CA_CFG | |
while [ ! -f "$AUTO_DEPLOY_CA_CFG" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to custom OpenSSL configuration file:${NORMAL} " AUTO_DEPLOY_CA_CFG; done | |
logDetails "$(cat $AUTO_DEPLOY_CA_CFG)" | |
fi | |
generateCSR $AUTO_DEPLOY_CA_CSR $AUTO_DEPLOY_CA_KEY "$AUTO_DEPLOY_CA_CFG" || errorMessage "Unable to generate Certificate Signing Request and Private Key" | |
printf "\n\nCertificate Signing Request generated at ${CYAN}${AUTO_DEPLOY_CA_CFG}${NORMAL}" | |
printf "\nPrivate Key generated at ${CYAN}${AUTO_DEPLOY_CA_KEY}${NORMAL}\n" | |
return 1 | |
fi | |
fi | |
if [ $AUTO_DEPLOY_CA_REPLACE != 'SELF-SIGNED' ]; then | |
task 'Publish CA signing certificates' | |
publishCASigningCertificates $TRUSTED_ROOT_CHAIN | |
fi | |
backupFilesystemCertKey '/etc/vmware-rbd/ssl/rbd-ca.crt' '/etc/vmware-rbd/ssl/rbd-ca.key' 'auto-deploy-ca' | |
task 'Replace certificate on filesystem' | |
cp $AUTO_DEPLOY_CA_CERT /etc/vmware-rbd/ssl/rbd-ca.crt > /dev/null 2>&1 || errorMessage 'Unable to update Auto Deploy CA certificate' | |
cp $AUTO_DEPLOY_CA_KEY /etc/vmware-rbd/ssl/rbd-ca.key > /dev/null 2>&1 || errorMessage 'Unable to update Auto Deploy CA private key' | |
statusMessage 'OK' 'GREEN' | |
return 0 | |
} | |
#------------------------------ | |
# Manage SMS certificates | |
#------------------------------ | |
function manageSMSCertificates() { | |
case $1 in | |
'View') | |
listSMSCertificates 'View' | |
read -p $'\nSelect certificate [Return to Main Menu]: ' VIEW_SMS_CERT_INPUT | |
if [ -n "$VIEW_SMS_CERT_INPUT" ]; then | |
SMS_CERT=${SMS_CERT_HASHES[$((VIEW_SMS_CERT_INPUT - 1))]} | |
viewCertificateInfo "$SMS_CERT" 'view-path' | |
fi | |
;; | |
'Manage') | |
listSMSCertificates 'Manage' | |
header 'Manage SMS Certificates' | |
echo ' 1. Replace SMS self-signed certificate' | |
echo ' 2. Replace SMS VMCA-signed certificate' | |
echo ' 3. Add VASA Provider certificate' | |
echo ' 4. Remove VASA Provider certificate' | |
read -p $'\nEnter selection [Return to Main Menu]: ' MANAGE_SMS_INPUT | |
if [ -n "$MANAGE_SMS_INPUT" ]; then | |
case "$MANAGE_SMS_INPUT" in | |
1) | |
replaceSMSCertificate 'self-signed' 'sms_self_signed' | |
;; | |
2) | |
replaceSMSCertificate 'VMCA-signed' 'sps-extension' | |
;; | |
3) | |
addSMSVASACertificate | |
;; | |
4) | |
removeSMSVASACertificate | |
;; | |
esac | |
fi | |
;; | |
esac | |
} | |
#------------------------------ | |
# Replace SMS certificate | |
#------------------------------ | |
function replaceSMSCertificate() { | |
header "Replace SMS $1 certifificate" | |
task "Remove current SMS $1 certificate" | |
$VECS_CLI entry delete --store SMS --alias $2 -y > /dev/null 2>&1 || errorMessage 'Unable to delete current SMS certificate' | |
statusMessage 'OK' 'GREEN' | |
if [ -z $3 ]; then restartVMwareServices 'vmware-sps'; fi | |
} | |
#------------------------------ | |
# List SMS certificates | |
#------------------------------ | |
function listSMSCertificates() { | |
header "$1 Certificates in the SMS VECS Store" | |
SMS_CERTS=() | |
SMS_CERT_HASHES=() | |
SMS_ALIASES=() | |
for alias in $($VECS_CLI entry list --store SMS | grep Alias | sed -e 's/Alias[[:space:]]:[[:space:]]//g'); do | |
SMS_CERT=$($VECS_CLI entry getcert --store SMS --alias "$alias") | |
SMS_CERT_INFO=$(viewBriefCertificateInfo "$SMS_CERT") | |
SMS_CERT_HASHES+=("$SMS_CERT") | |
SMS_CERTS+=("$SMS_CERT_INFO") | |
SMS_ALIASES+=("$alias") | |
done | |
i=0 | |
while [ $i -lt "${#SMS_CERTS[@]}" ]; do | |
n=$((i+1)) | |
printf "%2s. %s\n %s\n\n" $n "Alias: ${SMS_ALIASES[$i]}" "${SMS_CERTS[$i]}" | |
((++i)) | |
done | |
} | |
#------------------------------ | |
# Add new VASA provider cert to SMS store | |
#------------------------------ | |
function addSMSVASACertificate() { | |
read -e -p $'\n\nEnter path to new VASA provider certificate: ' NEW_VASA_INPUT | |
while [ ! -f "$NEW_VASA_INPUT" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to new VASA provider certificate:${NORMAL} " NEW_VASA_INPUT; done | |
read -p 'Enter alias (usually URL for VASA provider): ' NEW_VASA_ALIAS_INPUT | |
header 'Add New VASA Provider Certificate' | |
task 'Add entry to SMS store in VECS' | |
$VECS_CLI entry create --store SMS --cert "$NEW_VASA_INPUT" --alias "$NEW_VASA_ALIAS_INPUT" > /dev/null 2>&1 || errorMessage 'Unable to add VASA provider certificate to SMS store' | |
statusMessage 'OK' 'GREEN' | |
} | |
#------------------------------ | |
# Remove VASA provider cert from SMS store | |
#------------------------------ | |
function removeSMSVASACertificate() { | |
read -p $'\nEnter the number(s) of the VASA provider certificate(s) to remove (comma-separated list): ' REMOVE_VASA_INPUT | |
if [ -n "$REMOVE_VASA_INPUT" ]; then | |
header 'Remove VASA Provider Certificates' | |
for index in $(echo "$REMOVE_VASA_INPUT" | tr -d ' ' | sed 's/,/ /g'); do | |
vasa_alias=${SMS_ALIASES[$((index - 1))]} | |
task "$vasa_alias" | |
$VECS_CLI entry delete --store SMS --alias "$vasa_alias" -y > /dev/null 2>&1 || errorMessage "Unable to delete alias '$vasa_alias' from SMS store in VECS" | |
statusMessage 'OK' 'GREEN' | |
done | |
fi | |
} | |
#------------------------------ | |
# Replace data-encipherment certificate | |
#------------------------------ | |
function replaceDataEnciphermentCertificate() { | |
if [ "$VC_VERSION" == '6.5' ]; then return 0; fi | |
header 'Replace Data Encipherment certificate' | |
if checkVECSEntry 'data-encipherment' 'data-encipherment'; then | |
$VECS_CLI entry getkey --store data-encipherment --alias data-encipherment 2>/dev/null > $STAGE_DIR/data-encipherment.key | |
else | |
$CERTOOL --genkey --privkey=$STAGE_DIR/data-encipherment.key --pubkey=$STAGE_DIR/data-encipherment.pub | |
fi | |
task 'Generate new Data Enciphermenet certificate' | |
$CERTOOL --server=$PSC_LOCATION --genCIScert --privkey=$STAGE_DIR/data-encipherment.key --dataencipherment --cert=$STAGE_DIR/data-encipherment.crt --Name=data-encipherment --FQDN=$HOSTNAME_LC > /dev/null 2>&1 || errorMessage 'Unable to generate new Data Encipherment certificate' | |
statusMessage 'OK' 'GREEN' | |
updateVECS 'data-encipherment' | |
if [ -z $1 ]; then promptRestartVMwareServices 'vmware-vpxd'; fi | |
} | |
#------------------------------ | |
# Update Machine SSL thumbprint in Auto Deploy DB | |
#------------------------------ | |
function updateAutoDeployDB() { | |
RBD_STARTUP=$($VMON_CLI -s rbd | grep Starttype | awk '{print $NF}') | |
if [ "$RBD_STARTUP" = "AUTOMATIC" ]; then | |
header "Updating Auto Deploy Database" | |
MACHINE_SSL_THUMB=$($VECS_CLI entry getcert --store MACHINE_SSL_CERT --alias __MACHINE_CERT | openssl x509 -noout -fingerprint -sha1 2>/dev/null | cut -d'=' -f2) | |
if checkService 'vmware-rbd-watchdog'; then | |
task "Stopping Auto Deploy Service" | |
service-control --stop vmware-rbd-watchdog > /dev/null 2>&1 || errorMessage "Unable to stop Auto Deploy service" | |
statusMessage "OK" "GREEN" | |
fi | |
task "Updating Machine SSL thumbprint" | |
sqlite3 /var/lib/rbd/db "update vc_servers set thumbprint = '$MACHINE_SSL_THUMB'" || errorMessage "Unable to update Auto Deploy database" | |
statusMessage "OK" "GREEN" | |
task "Starting Auto Deploy Service" | |
service-control --start vmware-rbd-watchdog > /dev/null 2>&1 || errorMessage "Unable to start Auto Deploy service" | |
statusMessage "OK" "GREEN" | |
fi | |
} | |
#------------------------------ | |
# Checks Machine SSL thumbprint in Auto Deploy DB | |
#------------------------------ | |
function checkAutoDeployDB() { | |
RBD_STARTUP=$($VMON_CLI -s rbd | grep Starttype | awk '{print $NF}') | |
if [ "$RBD_STARTUP" = "AUTOMATIC" ]; then | |
header "Checking Auto Deploy Database" | |
RBD_VC_THUMBPRINT=$(sqlite3 /var/lib/rbd/db "select thumbprint from vc_servers where addr = '$HOSTNAME' collate nocase") | |
MACHINE_SSL_THUMB=$($VECS_CLI entry getcert --store MACHINE_SSL_CERT --alias __MACHINE_CERT | openssl x509 -noout -fingerprint -sha1 2>/dev/null | cut -d'=' -f2) | |
task "Machine SSL thumbprint" | |
if [ -z "$RBD_VC_THUMBPRINT" ]; then | |
statusMessage "BLANK" "GREEN" | |
elif [ "$RBD_VC_THUMBPRINT" != "$MACHINE_SSL_THUMB" ]; then | |
statusMessage "MISMATCH" "YELLOW" | |
if [ -n "$1" ]; then | |
read -p $'\nThe Machine SSL thumbprint is mismatched, update thumbprint? [n]: ' UPDATE_RBD_DB_INPUT | |
if [[ "$UPDATE_RBD_DB_INPUT" =~ ^[Yy] ]]; then updateAutoDeployDB; fi | |
fi | |
else | |
statusMessage "MATCHES" "GREEN" | |
fi | |
elif [ -n "$1" ]; then | |
header "Checking Auto Deploy Database" | |
echo "${YELLOW}The Auto Deploy service is not set to run automatically${NORMAL}" | |
fi | |
} | |
#------------------------------ | |
# Replace the VMDir certificate | |
#------------------------------ | |
function replaceVMDirCert() { | |
header 'Replace VMware Directory Service Certificate' | |
if [ $VMDIR_REPLACE == 'VMCA-SIGNED' ]; then | |
generateCertoolConfig 'vmdir' | |
task 'Regenerate VMware Directory certificate' | |
regenerateVMCASignedCertificate 'vmdir' | |
statusMessage 'OK' 'GREEN' | |
VMDIR_CERT=$STAGE_DIR/vmdir.crt | |
VMDIR_KEY=$STAGE_DIR/vmdir.key | |
else | |
echo $'\n1. Generate Certificate Signing Request and Private Key' | |
echo '2. Generate Certificate Signing Request and Private Key' | |
echo ' from custom OpenSSL configuration file' | |
echo '3. Import CA-signed Certificate and Key' | |
read -p $'\nChoose option [Return to Main Menu]: ' VMDIR_CA_SIGNED_OPTION | |
if [ -z "$VMDIR_CA_SIGNED_OPTION" ]; then return 1; fi | |
if [ "${VMDIR_CA_SIGNED_OPTION}" == '3' ]; then | |
logInfo 'User has chosen to import a CA-signed VMware Directory certificate and key' | |
read -e -p $'\n'"Provide path to CA-signed ${CYAN}VMware Directory Service${NORMAL} certificate: " VMDIR_CERT_INPUT | |
while [ ! -f "$VMDIR_CERT_INPUT" ]; do read -e -p "${YELLOW}File not found, please provide path to the VMware Directory Service certificate:${NORMAL} " VMDIR_CERT_INPUT; done | |
getCorrectCertFormat "$VMDIR_CERT_INPUT" 'VMDIR_CERT' | |
if ! checkCertSignatureAlgorithm "$(cat $VMDIR_CERT)"; then | |
errorMessage 'Certificate is using an unsupported signature algorithm' 'vmdir' 'no-task' | |
fi | |
VMDIR_CERT_MODULUS_HASH=$(openssl x509 -noout -modulus -in $VMDIR_CERT 2>/dev/null | md5sum | awk '{print $1}') | |
getPrivateKey "$VMDIR_CERT_MODULUS_HASH" 'VMDIR' 'VMware Directory Service' | |
getCAChain "$VMDIR_CERT" | |
task 'Verifying certificates and keys: ' | |
verifyCertAndKey $VMDIR_CERT $VMDIR_KEY | |
else | |
VMDIR_CSR=$REQUEST_DIR/vmdir-$TIMESTAMP.csr | |
VMDIR_KEY=$REQUEST_DIR/vmdir-$TIMESTAMP.key | |
if [ "${VMDIR_CA_SIGNED_OPTION}" == '1' ]; then | |
VMDIR_CFG=$REQUEST_DIR/vmdir.cfg | |
if [ -z "$CSR_COUNTRY" ]; then getCSRInfo; fi | |
defineSANEntries 'vmdir' 'VMware Directory' | |
generateOpensslConfig $HOSTNAME $VMDIR_CFG 'vmdir' | |
else | |
logInfo 'User has chosen to generate the VMware Directory private key and CSR from a custom OpenSSL configuration file' | |
read -e -p $'\nEnter path to custom OpenSSL configuration file: ' VMDIR_CFG | |
while [ ! -f "$VMDIR_CFG" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to custom OpenSSL configuration file:${NORMAL} " VMDIR_CFG; done | |
fi | |
task "Generating Certificate Signing Request" | |
generateCSR $VMDIR_CSR $VMDIR_KEY "$VMDIR_CFG" || errorMessage "Unable to generate Certificate Signing Request and Private Key" | |
statusMessage "OK" "GREEN" | |
printf "\n\nCertificate Signing Request generated at ${CYAN}${VMDIR_CSR}${NORMAL}" | |
printf "\nPrivate Key generated at ${CYAN}${VMDIR_KEY}${NORMAL}\n" | |
return 1 | |
fi | |
fi | |
if [ $VMDIR_REPLACE != 'VMCA-SIGNED' ]; then | |
task 'Publish CA signing certificates' | |
publishCASigningCertificates $TRUSTED_ROOT_CHAIN | |
fi | |
backupFilesystemCertKey '/usr/lib/vmware-vmdir/share/config/vmdircert.pem' '/usr/lib/vmware-vmdir/share/config/vmdirkey.pem' 'VMDir' | |
task 'Replace certificate on filesystem' | |
cp $VMDIR_CERT /usr/lib/vmware-vmdir/share/config/vmdircert.pem > /dev/null 2>&1 || errorMessage 'Unable to update VMware Directory Services certificate' | |
cp $VMDIR_KEY /usr/lib/vmware-vmdir/share/config/vmdirkey.pem > /dev/null 2>&1 || errorMessage 'Unable to update VMware Directory Services private key' | |
statusMessage 'OK' 'GREEN' | |
} | |
#------------------------------ | |
# Backup certificate and key from VECS | |
#------------------------------ | |
function backupVECSCertKey() { | |
case $1 in | |
machine-ssl) | |
VECS_STORE='MACHINE_SSL_CERT' | |
VECS_ALIAS='__MACHINE_CERT' | |
;; | |
*) | |
VECS_STORE=$1 | |
VECS_ALIAS=$1 | |
;; | |
esac | |
if [ "$1" == 'machine-ssl' ]; then | |
task 'Backing up certificate and private key' | |
else | |
task " $1" | |
fi | |
if $VECS_CLI entry list --store $VECS_STORE | grep $VECS_ALIAS > /dev/null; then | |
$VECS_CLI entry getcert --store $VECS_STORE --alias $VECS_ALIAS > $BACKUP_DIR/$1-$TIMESTAMP.crt 2>/dev/null || errorMessage "Unable to backup $1 certificate" 'backup' | |
$VECS_CLI entry getkey --store $VECS_STORE --alias $VECS_ALIAS > $BACKUP_DIR/$1-$TIMESTAMP.key 2>/dev/null || errorMessage "Unable to backup $1 private key" 'backup' | |
if [ -f $BACKUP_DIR/$1-$TIMESTAMP.crt ] && [ -f $BACKUP_DIR/$1-$TIMESTAMP.key ]; then statusMessage 'OK' 'GREEN'; fi | |
else | |
statusMessage 'NOT FOUND' 'YELLOW' | |
fi | |
} | |
#------------------------------ | |
# Replace certificate in VECS | |
#------------------------------ | |
function updateVECS() { | |
case $1 in | |
machine-ssl) | |
VECS_STORE='MACHINE_SSL_CERT' | |
VECS_ALIAS='__MACHINE_CERT' | |
VECS_CERT_FILE=$MACHINE_SSL_CERT | |
VECS_KEY_FILE=$MACHINE_SSL_KEY | |
;; | |
legacy-lookup-service) | |
VECS_STORE='STS_INTERNAL_SSL_CERT' | |
VECS_ALIAS='__MACHINE_CERT' | |
VECS_CERT_FILE=$MACHINE_SSL_CERT | |
VECS_KEY_FILE=$MACHINE_SSL_KEY | |
;; | |
data-encipherment) | |
VECS_STORE="$1" | |
VECS_ALIAS="$1" | |
VECS_CERT_FILE=$STAGE_DIR/$1.crt | |
VECS_KEY_FILE=$STAGE_DIR/$1.key | |
;; | |
machine) | |
VECS_STORE=$1 | |
VECS_ALIAS=$1 | |
VECS_CERT_FILE=$MACHINE_CERT | |
VECS_KEY_FILE=$MACHINE_KEY | |
;; | |
vpxd) | |
VECS_STORE=$1 | |
VECS_ALIAS=$1 | |
VECS_CERT_FILE=$VPXD_CERT | |
VECS_KEY_FILE=$VPXD_KEY | |
;; | |
vpxd-extension) | |
VECS_STORE=$1 | |
VECS_ALIAS=$1 | |
VECS_CERT_FILE=$VPXD_EXTENSION_CERT | |
VECS_KEY_FILE=$VPXD_EXTENSION_KEY | |
;; | |
vsphere-webclient) | |
VECS_STORE=$1 | |
VECS_ALIAS=$1 | |
VECS_CERT_FILE=$VSPHERE_WEBCLIENT_CERT | |
VECS_KEY_FILE=$VSPHERE_WEBCLIENT_KEY | |
;; | |
wcp) | |
VECS_STORE=$1 | |
VECS_ALIAS=$1 | |
VECS_CERT_FILE=$WCP_CERT | |
VECS_KEY_FILE=$WCP_KEY | |
;; | |
hvc) | |
VECS_STORE=$1 | |
VECS_ALIAS=$1 | |
VECS_CERT_FILE=$HVC_CERT | |
VECS_KEY_FILE=$HVC_KEY | |
;; | |
esac | |
if [ "$1" == 'machine-ssl' ] || [ "$1" == 'data-encipherment' ]; then | |
task "Updating ${VECS_STORE} certificate" | |
else | |
task " $1" | |
fi | |
if $VECS_CLI entry list --store $VECS_STORE | grep 'Alias :' | grep "$VECS_ALIAS" > /dev/null 2>&1; then | |
$VECS_CLI entry delete --store $VECS_STORE --alias $VECS_ALIAS -y > /dev/null 2>&1 || errorMessage "Unable to delete entry $VECS_ALIAS in the VECS store $VECS_STORE" | |
fi | |
$VECS_CLI entry create --store $VECS_STORE --alias $VECS_ALIAS --cert $VECS_CERT_FILE --key $VECS_KEY_FILE > /dev/null 2>&1 || errorMessage "Unable to create entry $VECS_ALIAS in VECS store $VECS_STORE" | |
statusMessage 'OK' 'GREEN' | |
} | |
#------------------------------ | |
# Replace certificate in VECS | |
#------------------------------ | |
function manageSSLTrustAnchors() { | |
unset TRUST_ANCHORS_INPUT | |
authenticateIfNeeded | |
header 'Manage SSL Trust Anchors' | |
echo ' 1. Check SSL Trust Anchors' | |
echo ' 2. Update SSL Trust Anchors' | |
read -p $'\nSelect action [Return to Main Menu]: ' TRUST_ANCHORS_INPUT | |
case $TRUST_ANCHORS_INPUT in | |
1) | |
checkSSLTrustAnchors | |
;; | |
2) | |
SSLTrustAnchorsSelectNode | |
updateSSLTrustAnchors | |
promptRestartVMwareServices | |
;; | |
esac | |
} | |
#------------------------------ | |
# List all certificates used as SSL trust anchors | |
#------------------------------ | |
function checkSSLTrustAnchors() { | |
TP_ALGORITHM='sha1' | |
TP_REGEX_ITER='19' | |
OUTPUT_OPTIONS='' | |
header 'Check SSL Trust Anchors' | |
cat << EOF | |
Additional output options: | |
1. None | |
2. Show associated Service IDs | |
3. Show associated endpoint URIs | |
4. Show both associated Service IDs and endpoint URIs | |
5. Show the SHA256 fingerprint of the certificates | |
EOF | |
read -p $'\nPlease select additional information options [1]: ' CHECK_TRUST_ANCHOR_OUTPUT_OPTIONS | |
case $CHECK_TRUST_ANCHOR_OUTPUT_OPTIONS in | |
2) | |
OUTPUT_OPTIONS='service-ids' | |
;; | |
3) | |
OUTPUT_OPTIONS='endpoints' | |
;; | |
4) | |
OUTPUT_OPTIONS='service-ids endpoints' | |
;; | |
5) | |
TP_ALGORITHM='sha256' | |
TP_REGEX_ITER='31' | |
;; | |
esac | |
printSSLTrustAnchorInfo "$TP_ALGORITHM" "$TP_REGEX_ITER" "$OUTPUT_OPTIONS" | |
getSSODomainNodes | |
echo '' | |
for node in "${SSO_NODES[@]}"; do | |
echo "${CYAN}-----Machine SSL Certificate-----${NORMAL}" | |
echo "${CYAN}${node}${NORMAL}" | |
CURRENT_MACHINE_SSL_CERT_INFO=$(echo | openssl s_client -connect $node:443 2>/dev/null | openssl x509 -text -noout -fingerprint -$TP_ALGORITHM 2>/dev/null | grep -E 'Issuer:|Subject:|Validity|Not Before:|Not After :|Fingerprint' | sed -e 's/SHA[0-9]* Fingerprint/\t&/g' -e "s/Subject:/${GREEN}&${NORMAL}/g" -e "s/[[:xdigit:]]\{2\}\(:[[:xdigit:]]\{2\}\)\{${TP_REGEX_ITER}\}/${YELLOW}&${NORMAL}/g") | |
if [ -n "$CURRENT_MACHINE_SSL_CERT_INFO" ]; then | |
echo 'Certificate Info:' | |
if ! isExpired "$(echo | openssl s_client -connect $node:443 2>/dev/null)" 'hash'; then | |
echo "$CURRENT_MACHINE_SSL_CERT_INFO" | |
else | |
echo "$CURRENT_MACHINE_SSL_CERT_INFO" | sed -e "s/Not Before/${RED}&/" | |
fi | |
else | |
echo "${YELLOW}Unable to get certificate from $node on port 443" | |
echo "Please make sure the server is up and the reverse proxy service is running.$NORMAL" | |
fi | |
echo "${CYAN}---------------------------------${NORMAL}" | |
done | |
} | |
#------------------------------ | |
# Print SSL trust anchors information | |
#------------------------------ | |
function printSSLTrustAnchorInfo() { | |
CERT_COUNT=1 | |
TP_ALGORITHM="$1" | |
TP_REGEX_ITER="$2" | |
OUTPUT_OPTIONS="$3" | |
echo -n '' > $STAGE_DIR/trust-anchors.raw | |
getSSLTrustAnchorHashes | |
printf "\n" | |
for hash in "${CERT_HASHES[@]}"; do | |
echo "${CYAN}-----Endpoint Certificate ${CERT_COUNT}-----${NORMAL}" | |
TEMP_CERT=$(buildCertFromHash "$hash") | |
double_encoded_hash=$(echo "$hash" | tr -d '\n' | sed -e 's/.\{76\}/&\r\n/g' | xargs -0 printf "%s\r\n" | base64 -w 0) | |
if ! isExpired "$TEMP_CERT" 'hash'; then | |
DATE_COLOR='NORMAL' | |
else | |
DATE_COLOR='RED' | |
fi | |
echo "$TEMP_CERT" | openssl x509 -text -noout -fingerprint -$TP_ALGORITHM 2>/dev/null | grep -E 'Issuer:|Subject:|Validity|Not Before:|Not After :|Fingerprint' | sed -e "s/Not Before/${!DATE_COLOR}&/" -e 's/SHA[0-9]* Fingerprint/\t&/g' -e "s/Subject:/${GREEN}&${NORMAL}/g" -e "s/[[:xdigit:]]\{2\}\(:[[:xdigit:]]\{2\}\)\{${TP_REGEX_ITER}\}/${YELLOW}&${NORMAL}/g" | |
if echo "$OUTPUT_OPTIONS" | grep 'service-ids' > /dev/null; then | |
USED_BY_SERVICE_IDS=$(getSSLTrustAnchorServiceIds "$hash" "$double_encoded_hash") | |
NUM_USED_BY_SERVICE_IDS=$(echo "$USED_BY_SERVICE_IDS" | grep -v '^$' | wc -l) | |
echo "Used by $NUM_USED_BY_SERVICE_IDS service registrations:" | |
for service in $USED_BY_SERVICE_IDS; do | |
echo $'\t'"$service" | |
done | |
fi | |
if echo "$OUTPUT_OPTIONS" | grep 'endpoints' > /dev/null; then | |
USED_BY_ENDPOINTS=$(getSSLTrustAnchorEndpoints "$hash" "$double_encoded_hash") | |
NUM_USED_BY_ENDPOINTS=$(echo "$USED_BY_ENDPOINTS" | grep -v '^$' | wc -l) | |
echo "Used by $NUM_USED_BY_ENDPOINTS endpoints:" | |
for endpoint in $USED_BY_ENDPOINTS; do | |
echo $'\t'"$endpoint" | |
done | |
fi | |
echo "${CYAN}--------------------------------${NORMAL}" | |
((++CERT_COUNT)) | |
done | |
} | |
#------------------------------ | |
# Get certificate hashes of unique SSL Trust Anchor | |
#------------------------------ | |
function getSSLTrustAnchorHashes() { | |
CERT_HASHES=() | |
LDAP_SEARCH_RESULTS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=Sites,cn=Configuration,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(|(objectclass=vmwLKUPEndpointRegistration)(objectclass=vmwLKUPServiceEndpoint))' vmwLKUPEndpointSslTrust vmwLKUPSslTrustAnchor vmwLKUPURI) | |
IFS=$'\n' | |
dn='' | |
attrs='' | |
while read line; do | |
if [ -n "$line" ]; then | |
if [[ "$line" =~ ^dn: ]]; then | |
dn="$line" | |
else | |
if [ -z "$attrs" ]; then | |
attrs+="$line" | |
else | |
attrs+=$'\n'"$line" | |
fi | |
fi | |
else | |
echo "$dn" >> $STAGE_DIR/trust-anchors.raw | |
echo "$attrs" | sort >> $STAGE_DIR/trust-anchors.raw | |
dn='' | |
attrs='' | |
fi | |
done <<<"$LDAP_SEARCH_RESULTS" | |
IFS=$' \t\n' | |
TRUST_ANCHORS=$(cat $STAGE_DIR/trust-anchors.raw | grep -vE '^dn:|^vmwLKUPURI' | awk -F': ' '{print $NF}' | sort | uniq) | |
for cert in $TRUST_ANCHORS; do | |
if [[ "$cert" =~ ^TUl ]]; then | |
CURRENT_CERT=$(echo $cert | base64 --decode | tr -d '\r\n') | |
else | |
CURRENT_CERT=($cert) | |
fi | |
if [[ ! "${CERT_HASHES[@]}" =~ "$CURRENT_CERT" ]]; then | |
CERT_HASHES+=($CURRENT_CERT) | |
fi | |
done | |
} | |
#------------------------------ | |
# Get the service IDs using a unique SSL Trust Anchor | |
#------------------------------ | |
function getSSLTrustAnchorServiceIds() { | |
USED_BY_SERVICE_IDS=$(cat $STAGE_DIR/trust-anchors.raw | grep -B1 $1 | grep '^dn:' | awk -F',' '{print $2}' | sed -e 's/cn=//g' | sort | uniq) | |
USED_BY_SERVICE_IDS+=$'\n'$(cat $STAGE_DIR/trust-anchors.raw | grep -B1 $2 | grep '^dn:' | awk -F',' '{print $2}' | sed -e 's/cn=//g' | sort | uniq | xargs -0 printf "\n%s") | |
echo "$USED_BY_SERVICE_IDS" | |
} | |
#------------------------------ | |
# Get the endpoint URIs using a unique SSL Trust Anchor | |
#------------------------------ | |
function getSSLTrustAnchorEndpoints() { | |
USED_BY_ENDPOINTS=$(cat $STAGE_DIR/trust-anchors.raw | grep -A1 $1 | grep '^vmwLKUPURI' | sed -e 's/vmwLKUPURI: //g' | sort | uniq) | |
USED_BY_ENDPOINTS+=$'\n'$(cat $STAGE_DIR/trust-anchors.raw | grep -A1 $2 | grep '^vmwLKUPURI' | sed -e 's/vmwLKUPURI: //g' | sort | uniq) | |
echo "$USED_BY_ENDPOINTS" | |
} | |
#------------------------------ | |
# 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 | |
} | |
#------------------------------ | |
# Select which node to update SSL trust anchors | |
#------------------------------ | |
function SSLTrustAnchorsSelectNode() { | |
getSSODomainNodes | |
NODE_COUNTER=1 | |
NODE_DEFAULT=1 | |
PSC_VIP_COUNTER=0 | |
printf "\nNodes in SSO domain '$SSO_DOMAIN'\n" | |
for node in "${SSO_NODES[@]}"; do | |
echo " $NODE_COUNTER. $node" | |
if [ $HOSTNAME = $node ]; then NODE_DEFAULT=$NODE_COUNTER; fi | |
((++NODE_COUNTER)) | |
done | |
if [[ $VCENTER_COUNT -gt 0 && $PSC_COUNT -gt 1 ]]; then | |
echo " $NODE_COUNTER. FQDN of PSC Load Balancer" | |
PSC_VIP_COUNTER=$NODE_COUNTER | |
fi | |
echo ' C. Custom hostname or IP address' | |
read -p $'\n'"Select node to update [${NODE_DEFAULT}]: " NODE_SELECT | |
if [ -z $NODE_SELECT ]; then | |
NODE_FQDN=${SSO_NODES[$((NODE_DEFAULT - 1))]} | |
NODE_SSL_FQDN=$NODE_FQDN | |
else | |
if [[ $PSC_VIP_COUNTER -gt 0 && "${NODE_SELECT}" == "$PSC_VIP_COUNTER" ]]; then | |
read -p 'Enter the FQDN of the PSC Load Balancer: ' PSC_LB_FQDN | |
while [ -z $PSC_LB_FQDN ]; do | |
read -p 'Enter the FQDN of the PSC Load Balancer: ' PSC_LB_FQDN | |
done | |
NODE_FQDN=$PSC_LB_FQDN | |
NODE_SSL_FQDN=$NODE_FQDN | |
elif [[ "$NODE_SELECT" =~ ^[Cc] ]]; then | |
echo $'\n'"${YELLOW}Note: This operation is used when the endpoint URIs refer to a hostname or IP address" | |
echo "other than the target vCenter/PSC hostname or IP address. These situations are very uncommon." | |
echo "Only use this option at the direction of VMware Global Support.${NORMAL}" | |
read -p $'\nEnter hostname or IP address of registration endpoint URIs to update: ' CUSTOM_NODE_SELECT | |
while [ -z "$CUSTOM_NODE_SELECT" ]; do read -p $'\n'"${YELLOW}Enter hostname or IP address of registration endpoint URIs to update:${NORMAL} " CUSTOM_NODE_SELECT; done | |
NODE_FQDN="$CUSTOM_NODE_SELECT" | |
read -p $'\n'"Enter the hostname or IP address of the node serving the SSL certificate to update [$NODE_FQDN]: " NODE_SSL_FQDN_INPUT | |
if [ -z $NODE_SSL_FQDN_INPUT ]; then | |
NODE_SSL_FQDN=$NODE_FQDN | |
else | |
NODE_SSL_FQDN=$NODE_SSL_FQDN_INPUT | |
fi | |
else | |
NODE_FQDN=${SSO_NODES[$((NODE_SELECT - 1))]} | |
NODE_SSL_FQDN=$NODE_FQDN | |
fi | |
fi | |
logInfo "User has selected '$NODE_FQDN'" | |
logInfo "SSL certificate to update will be obtained from $NODE_SSL_FQDN:443" | |
echo | openssl s_client -connect $NODE_SSL_FQDN:443 2>/dev/null | openssl x509 > $STAGE_DIR/trust-anchor-machine-ssl.crt 2>/dev/null | |
} | |
#------------------------------ | |
# Setup environment to update SSL trust anchors for the current node | |
#------------------------------ | |
function SSLTrustAnchorSelf() { | |
openssl x509 -in $MACHINE_SSL_CERT > $STAGE_DIR/trust-anchor-machine-ssl.crt 2>/dev/null | |
NODE_FQDN="$PNID" | |
} | |
#------------------------------ | |
# Update the SSL trust anchors | |
#------------------------------ | |
function updateSSLTrustAnchors() { | |
TOTAL_SERVICES_UPDATED=0 | |
header "Update SSL Trust Anchors ($NODE_FQDN)" | |
find $STAGE_DIR -type f -iname 'ls-service-reg-*.ldif' -exec rm {} \; | |
if [ "$VMDIR_FQDN" != "$PNID" ]; then | |
read -r -s -p $'\n'"Enter the root password for $PSC_LOCATION: " SSHPASS | |
PSC_INFO=$(sshpass -p "$SSHPASS" ssh -q -o StrictHostKeyChecking=no -t -t root@$PSC_LOCATION "/opt/likewise/bin/lwregshell list_values '[HKEY_THIS_MACHINE\services\vmdir]' | grep -E 'dcAccountPassword|dcAccountDN'" | grep 'dcAccount') | |
logInfo "PSC info is: $PSC_INFO" | |
if [ -z "$PSC_INFO" ]; then | |
echo $'\n\n'"${YELLOW}Unable to get machine account password for $PSC_LOCATION." | |
echo $'\n'"This is usually because the default shell on the PSC is /bin/appliancesh instead of /bin/bash" | |
echo $'\n'"Please change the default shell on $PSC_LOCATION," | |
echo "or run this script on $PSC_LOCATION to update the SSL trust anchors.${NORMAL}" | |
return 1 | |
fi | |
UPDATE_MACHINE_PASSWORD=$(echo "$PSC_INFO" | grep 'dcAccountPassword' | awk -F" " '{print $NF}' | awk '{print substr($0,2,length($0)-3)}' | sed -e 's/\\"/"/g' -e 's/\\\\/\\/g') | |
UPDATE_MACHINE_ACCOUNT_DN=$(echo "$PSC_INFO" | grep 'dcAccountDN' | awk -F" " '{print $NF}' | awk '{print substr($0,2,length($0)-3)}') | |
printf "\n\n" | |
else | |
UPDATE_MACHINE_ACCOUNT_DN=$VMDIR_MACHINE_ACCOUNT_DN | |
UPDATE_MACHINE_PASSWORD=$VMDIR_MACHINE_PASSWORD | |
fi | |
echo -n "$UPDATE_MACHINE_PASSWORD" > $STAGE_DIR/.update-machine-account-password | |
chmod 640 $STAGE_DIR/.update-machine-account-password | |
cat $STAGE_DIR/trust-anchor-machine-ssl.crt | grep -vE '^-----' | tr -d '\n' > $STAGE_DIR/trust-anchor-machine-ssl.hash | |
openssl x509 -outform der -in $STAGE_DIR/trust-anchor-machine-ssl.crt -out $STAGE_DIR/trust-anchor-machine-ssl.der 2>/dev/null | |
SERVICE_REGISTRATION_DNS=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=Sites,cn=Configuration,$VMDIR_DOMAIN_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password "(&(|(vmwLKUPURI=https://$NODE_FQDN:*)(vmwLKUPURI=https://$NODE_FQDN/*))(|(objectclass=vmwLKUPServiceEndpoint)(objectclass=vmwLKUPEndpointRegistration)))" vmwLKUPEndpointSslTrust vmwLKUPSslTrustAnchor | grep '^dn:' | sed -r 's/cn=Endpoint[0-9]+,//g' | sed -e 's/dn: //g' -e 's/, cn=/,cn=/g' | sort | uniq) | |
logInfo 'Service Registration DNs to update:' | |
logDetails "'$SERVICE_REGISTRATION_DNS'" | |
SSO_ALL_SITES=$($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 -s one '(objectclass=*)' cn | grep '^cn:' | awk -F': ' '{print $NF}') | |
for svc_dn in $SERVICE_REGISTRATION_DNS; do | |
LEGACY_REGISTRATION=0 | |
for site in $SSO_ALL_SITES; do | |
SVC_LOWER=$(echo "$svc_dn" | awk -F',' '{print $1}' | awk -F'=' '{print $2}' | tr '[:upper:]' '[:lower:]') | |
SITE_LOWER=$(echo "$site" | tr '[:upper:]' '[:lower:]') | |
if [[ $SVC_LOWER =~ ^$SITE_LOWER: ]]; then LEGACY_REGISTRATION=1; fi | |
done | |
logInfo "Updating service $svc_dn" | |
if [ $LEGACY_REGISTRATION = 1 ]; then | |
updateLegacySSLTrustAnchorTargeted $svc_dn | |
else | |
updateSSLTrustAnchorTargeted $svc_dn | |
fi | |
done | |
echo "Updated $TOTAL_SERVICES_UPDATED service(s)" | |
UPDATED_TRUST_ANCHORS=1 | |
return 0 | |
} | |
#------------------------------ | |
# Update a legacy SSL trust anchor | |
#------------------------------ | |
function updateLegacySSLTrustAnchorTargeted() { | |
SERVICE_DN=$1 | |
SERVICE_ID=$(echo "$SERVICE_DN" | awk -F',' '{print $1}' | awk -F'=' '{print $2}') | |
ENDPOINT_INFO=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "$SERVICE_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(|(objectclass=vmwLKUPServiceEndpoint)(objectclass=vmwLKUPEndpointRegistration))' vmwLKUPEndpointSslTrust vmwLKUPSslTrustAnchor 2>/dev/null | sed -e 's/, cn=/,cn=/g' -e '/^$/d') | |
IFS=$'\n' | |
for line in $ENDPOINT_INFO; do | |
if [[ $line =~ ^dn: ]]; then | |
CURRENT_DN=$line | |
elif [[ $line =~ ^vmwLKUP ]]; then | |
echo "$CURRENT_DN" >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif | |
echo 'changetype: modify' >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif | |
if echo $line | grep 'vmwLKUPSslTrustAnchor' > /dev/null; then | |
echo 'replace: vmwLKUPSslTrustAnchor' >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif | |
echo "vmwLKUPSslTrustAnchor:< file://$STAGE_DIR/trust-anchor-machine-ssl.der" >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif | |
else | |
echo 'replace: vmwLKUPEndpointSslTrust' >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif | |
echo "vmwLKUPEndpointSslTrust:< file://$STAGE_DIR/trust-anchor-machine-ssl.hash" >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif | |
fi | |
echo '' >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif | |
fi | |
done | |
IFS=$' \t\n' | |
if [ -f $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif ]; then | |
logInfo "Updating legacy Lookup Service entry: $1" | |
logDebug "Contents of LDIF file $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif" | |
logDebugDetails "'$(cat $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif)'" | |
echo "Updating service: ${SERVICE_ID}" | |
if ! $LDAP_MODIFY -v -h $PSC_LOCATION -p $VMDIR_PORT -D "${UPDATE_MACHINE_ACCOUNT_DN}" -y $STAGE_DIR/.update-machine-account-password -f $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif > /dev/null 2>&1; then | |
echo 'Error updating service: please check logs for details' | |
logError "Error updating service $1, ldapmodify return code: $?" | |
else | |
logInfo "Service $1 successfully updated" | |
((++TOTAL_SERVICES_UPDATED)) | |
fi | |
fi | |
} | |
#------------------------------ | |
# Update an SSL trust anchor | |
#------------------------------ | |
function updateSSLTrustAnchorTargeted() { | |
SERVICE_DN=$1 | |
SERVICE_ID=$(echo "$SERVICE_DN" | awk -F',' '{print $1}' | awk -F'=' '{print $2}') | |
ENDPOINT_INFO=$($LDAP_SEARCH -o ldif-wrap=no -LLL -h $PSC_LOCATION -p $VMDIR_PORT -b "$SERVICE_DN" -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password '(objectclass=vmwLKUPEndpointRegistration)' vmwLKUPEndpointSslTrust 2>/dev/null | sed -e 's/, cn=/,cn=/g' -e '/^$/d') | |
IFS=$'\n' | |
for line in $ENDPOINT_INFO; do | |
if [[ $line =~ ^dn: ]]; then | |
CURRENT_DN=$line | |
elif [[ $line =~ ^vmwLKUPEndpointSslTrust: ]]; then | |
echo "$CURRENT_DN" >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif | |
echo 'changetype: modify' >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif | |
echo 'replace: vmwLKUPEndpointSslTrust' >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif | |
echo "vmwLKUPEndpointSslTrust:< file://$STAGE_DIR/trust-anchor-machine-ssl.hash" >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif | |
echo '' >> $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif | |
fi | |
done | |
IFS=$' \t\n' | |
if [ -f $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif ]; then | |
logInfo "Updating Lookup Service entry: $1" | |
logDebug "Contents of LDIF file $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif" | |
logDebugDetails "'$(cat $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif)'" | |
echo "Updating service: $SERVICE_ID" | |
if ! $LDAP_MODIFY -v -h $PSC_LOCATION -p $VMDIR_PORT -D "$UPDATE_MACHINE_ACCOUNT_DN" -y $STAGE_DIR/.update-machine-account-password -f $STAGE_DIR/ls-service-reg-$SERVICE_ID.ldif > /dev/null 2>&1; then | |
echo 'Error updating service: please check logs for details' | |
logError "Error updating service $1, ldapmodify return code: $?" | |
else | |
logInfo "Service $1 successfully updated" | |
((++TOTAL_SERVICES_UPDATED)) | |
fi | |
fi | |
} | |
#------------------------------ | |
# Print configuration check menu | |
#------------------------------ | |
function checkConfigurationMenu() { | |
unset CONFIGURATION_CHECK_INPUT | |
header "Configuration Check Menu" | |
cat << EOF | |
1. Check for SSL Interception | |
2. Check STS server certificate configuration | |
3. Check SSO Smart Card configuration options | |
4. Check VECS store status and permissions | |
5. Check Auto Deploy configuration | |
EOF | |
read -p $'\nSelect an option [Return to Main Menu]: ' CONFIGURATION_CHECK_INPUT | |
case $CONFIGURATION_CHECK_INPUT in | |
1) | |
checkSSLInterception | |
;; | |
2) | |
if [ $NODE_TYPE != 'management' ]; then | |
checkSTSConfig | |
else | |
echo $'\n'"${YELLOW}This operation must be done on the Platform Services Controller${NORMAL}"$'\n' | |
fi | |
;; | |
3) | |
checkSmartCardOptions | |
;; | |
4) | |
checkVECSStores | |
;; | |
5) | |
if [ $NODE_TYPE != 'infrastructure' ]; then checkAutoDeployDB 'check'; fi | |
;; | |
esac | |
} | |
#------------------------------ | |
# Check if SSL Interception is in play | |
#------------------------------ | |
function checkSSLInterception() { | |
header 'Checking for SSL Interception' | |
task 'Checking hostupdate.vmware.com' | |
HOSTUPDATE_ISSUER=$(echo | openssl s_client -connect hostupdate.vmware.com:443 2>/dev/null | openssl x509 -noout -issuer 2>/dev/null | awk -F'/' '{for(i=1; i<=NF;i++) if($i ~ /^CN/) {print $i}}' | sed 's/CN=//') | |
if [ -n "$HOSTUPDATE_ISSUER" ]; then | |
statusMessage 'OK' 'GREEN' | |
if [ "$HOSTUPDATE_ISSUER" != "$HOSTUPDATE_ISSUER_EXPECTED" ]; then | |
unset DOWNLOAD_PROXY_CA_CERTS_INPUT | |
echo $'\n'"Issuing CA for hostupdate.vmware.com is ${YELLOW}${HOSTUPDATE_ISSUER}${NORMAL}" | |
echo "The expected issuer is ${GREEN}${HOSTUPDATE_ISSUER_EXPECTED}${NORMAL}" | |
echo $'\n'"${YELLOW}SSL Interception is likely taking place.${NORMAL}" | |
header "SSL Interception Certificate Management" | |
echo " 1. Download and import CA certificates from the proxy" | |
echo " 2. Import CA certificate(s) from a file" | |
read -p $'\nSelect an option [Return to Main Menu]: ' PROXY_CA_CERTS_INPUT | |
case $PROXY_CA_CERTS_INPUT in | |
1) | |
downloadProxyCACerts | |
;; | |
2) | |
provideProxyCACerts | |
;; | |
esac | |
else | |
echo $'\n'"Issuing CA for hostupdate.vmware.com is ${GREEN}${HOSTUPDATE_ISSUER}${NORMAL}"$'\n' | |
fi | |
else | |
statusMessage 'ERROR' 'YELLOW' | |
echo $'\n'"${YELLOW}Could not identify the issuer of the certificate for hostupdate.vmware.com" | |
echo "Check your network connection and try again.${NORMAL}"$'\n' | |
fi | |
} | |
#------------------------------ | |
# Download CA certs from proxy used for SSL Interception | |
#------------------------------ | |
function downloadProxyCACerts() { | |
authenticateIfNeeded | |
if [ -f $STAGE_DIR/proxy-ca-chain.pem ]; then rm $STAGE_DIR/proxy-ca-chain.pem; fi | |
header "Processing Proxy CA certificates" | |
task 'Downloadng certificate chain from the proxy' | |
echo | openssl s_client -connect hostupdate.vmware.com:443 2>/dev/null -showcerts | sed -n '/^-----BEGIN CERTIFICATE-----/,/^-----END CERTIFICATE-----/p' | csplit -z -f $STAGE_DIR/proxy-cert- -b%02d.crt /dev/stdin '/-----BEGIN CERTIFICATE-----/' '{*}' 2>&1 | logDebug | |
if [ "$(ls -l $STAGE_DIR/proxy-cert* 2>/dev/null)" != '' ]; then | |
statusMessage 'OK' 'GREEN' | |
for cert in $(ls $STAGE_DIR/proxy-cert-* 2>/dev/null); do | |
if isCertCA "$(cat $cert)"; then cat $cert >> $STAGE_DIR/proxy-ca-chain.pem; fi | |
done | |
if [ -f $STAGE_DIR/proxy-ca-chain.pem ]; then | |
importProxyCACerts | |
NUM_PROXY_CA_CERTS=$(ls -l $STAGE_DIR/proxy-cert-* | wc -l) | |
CERT_FILE_INDEX=$(printf "%02d" $((NUM_PROXY_CA_CERTS-1))) | |
LAST_PROXY_CA_SUBJECT=$(openssl x509 -noout -subject -in $STAGE_DIR/proxy-cert-$CERT_FILE_INDEX.crt 2>/dev/null | sed -e 's/subject= //') | |
LAST_PROXY_CA_ISSUER=$(openssl x509 -noout -issuer -in $STAGE_DIR/proxy-cert-$CERT_FILE_INDEX.crt 2>/dev/null | sed -e 's/issuer= //') | |
if [ "$LAST_PROXY_CA_SUBJECT" != "$LAST_PROXY_CA_ISSUER" ]; then | |
echo $'\n'"${YELLOW}There proxy does not provide the Root CA certificate in the chain." | |
echo "Please aquire this certificate and import it.${NORMAL}"$'\n' | |
fi | |
else | |
echo $'\n'"${YELLOW}There proxy does not appear to provide any of the CA certificates." | |
echo "Please aquire these certificates and import them.${NORMAL}"$'\n' | |
fi | |
fi | |
} | |
#------------------------------ | |
# Manually provide CA certs that signed proxy certificate used for SSL Interception | |
#------------------------------ | |
function provideProxyCACerts() { | |
authenticateIfNeeded | |
if [ -f $STAGE_DIR/proxy-ca-chain.pem ]; then rm $STAGE_DIR/proxy-ca-chain.pem; fi | |
read -e -p $'\n'"Enter path to CA certificate (or chain): " PROXY_CA_CERT_INPUT | |
while [ ! -f $PROXY_CA_CERT_INPUT ]; do read -e -p $'\n'"${YELLOW}Enter path to CA certificate (or chain):${NORMAL} " PROXY_CA_CERT_INPUT; done | |
getCorrectCertFormat "$PROXY_CA_CERT_INPUT" 'PROXY_CA_CERT_FILE' | |
csplit -s -z -f $STAGE_DIR/proxy-cert- -b%02d.crt $PROXY_CA_CERT_FILE '/-----BEGIN CERTIFICATE-----/' '{*}' | |
if [ "$(ls -l $STAGE_DIR/proxy-cert* 2>/dev/null)" != '' ]; then | |
header "Processing Proxy CA certificates" | |
task "Checking CA file" | |
for cert in $(ls $STAGE_DIR/proxy-cert-* 2>/dev/null); do | |
if isCertCA "$(cat $cert)"; then cat $cert >> $STAGE_DIR/proxy-ca-chain.pem; fi | |
done | |
if [ -f $STAGE_DIR/proxy-ca-chain.pem ]; then | |
statusMessage "OK" "GREEN" | |
importProxyCACerts | |
else | |
errorMessage "No CA certificates found in $PROXY_CA_CERT_INPUT" | |
fi | |
fi | |
} | |
function importProxyCACerts() { | |
if [ -f $STAGE_DIR/proxy-ca-chain.pem ]; then | |
task 'Publishing certificates to VMware Directory' | |
$DIR_CLI trustedcert publish --chain --cert $STAGE_DIR/proxy-ca-chain.pem --login $VMDIR_USER_UPN --password "$(cat $STAGE_DIR/.vmdir-user-password)" 2>&1 | logInfo || errorMessage 'Unable to publish proxy CA certificates to VMware Directory' | |
statusMessage 'OK' 'GREEN' | |
task 'Refreshing CA certificates to VECS' | |
$VECS_CLI force-refresh || errorMessage 'Unable to refresh CA certificates in VECS' | |
statusMessage 'OK' 'GREEN' | |
if [[ "$VC_VERSION" =~ ^[78] ]] && [[ $VC_BUILD -ge 17327517 ]]; then | |
if [ ! -f /usr/lib/python3.7/site-packages/certifi/cacert.pem.backup ]; then | |
task "Backup Python CA store" | |
cp /usr/lib/python3.7/site-packages/certifi/cacert.pem /usr/lib/python3.7/site-packages/certifi/cacert.pem.backup 2>/dev/null || errorMessage "Could not backup the Python CA store at /usr/lib/python3.7/site-packages/certifi/cacert.pem" | |
statusMessage "OK" "GREEN" | |
fi | |
task 'Adding certificates to python CA store' | |
cat $STAGE_DIR/proxy-ca-chain.pem >> /usr/lib/python3.7/site-packages/certifi/cacert.pem | |
statusMessage 'OK' 'GREEN' | |
NUM_PROXY_CERTS_PEM=$(grep 'END CERTIFICATE' $STAGE_DIR/proxy-ca-chain.pem | wc -l) | |
NUM_PROXY_CERTS_KEYSTORE=$(keytool -list -keystore /usr/java/jre-vmware/lib/security/cacerts -storepass changeit 2>/dev/null | grep sslproxyca | wc -l) | |
if [ ! -f /usr/java/jre-vmware/lib/security/cacerts.backup ]; then | |
task "Backup Java keystore" | |
cp /usr/java/jre-vmware/lib/security/cacerts /usr/java/jre-vmware/lib/security/cacerts.backup 2>/dev/null || errorMessage "Could not backup the Java keystore at /usr/java/jre-vmware/lib/security/cacerts" | |
statusMessage "OK" "GREEN" | |
fi | |
for N in $(seq 0 $(($NUM_PROXY_CERTS_PEM - 1))); do | |
SSL_PROXY_CA_ALIAS="sslproxyca$(($NUM_PROXY_CERTS_KEYSTORE + $N))" | |
task "Import $SSL_PROXY_CA_ALIAS to Java keystore" | |
cat $STAGE_DIR/proxy-ca-chain.pem | awk "n==$N { print }; /END CERTIFICATE/ { n++ }" | keytool -noprompt -import -file /dev/stdin -trustcacerts -alias "$SSL_PROXY_CA_ALIAS" -storepass changeit -keystore /usr/java/jre-vmware/lib/security/cacerts 2>&1 | logDebug | |
statusMessage "OK" "GREEN" | |
done | |
fi | |
fi | |
} | |
#------------------------------ | |
# Quick check of the STS server configuration | |
#------------------------------ | |
function quickCheckSTSConfig() { | |
header 'Checking STS Server Configuration' | |
task 'Check STS VECS store configuration' | |
if quickCheckSTSCertConfig; then | |
statusMessage 'OK' 'GREEN' | |
else | |
statusMessage 'LEGACY' 'YELLOW' | |
CERT_STATUS_STS_VECS_CONFIG=1 | |
fi | |
task 'Check STS ConnectionStrings configuration' | |
if quickCheckSTSConnectionStrings; then | |
statusMessage 'OK' 'GREEN' | |
else | |
statusMessage 'MISCONFIG' 'YELLOW' | |
CERT_STATUS_STS_CONNECTION_STRINGS=1 | |
fi | |
} | |
#------------------------------ | |
# Quick check of the STS server VECS configuration | |
#------------------------------ | |
function quickCheckSTSCertConfig() { | |
STS_LOCALHOST_CONNECTOR_STORE=$(grep 'bio-ssl-localhost.https.port' /usr/lib/vmware-sso/vmware-sts/conf/server.xml | grep 'store=' | awk '{for(i=1;i<=NF;i++) if($i ~ /^store/) {print $i}}' | tr -d '>' | awk -F'=' '{print $NF}' | tr -d '"') | |
STS_LOCALHOST_CERTIFICATE_STORE=$(grep -A2 'bio-ssl-localhost.https.port' /usr/lib/vmware-sso/vmware-sts/conf/server.xml | grep 'certificateKeystoreFile=' | awk '{for(i=1;i<=NF;i++) if($i ~ /^certificateKeystoreFile/) {print $i}}' | tr -d '>' | awk -F'=' '{print $NF}' | tr -d '"') | |
STS_CLIENTAUTH_CONNECTOR_STORE=$(grep 'bio-ssl-clientauth.https.port' /usr/lib/vmware-sso/vmware-sts/conf/server.xml | grep 'store=' | awk '{for(i=1;i<=NF;i++) if($i ~ /^store/) {print $i}}' | tr -d '>' | awk -F'=' '{print $NF}' | tr -d '"') | |
STS_CLIENTAUTH_CERTIFICATE_STORE=$(grep -A2 'bio-ssl-clientauth.https.port' /usr/lib/vmware-sso/vmware-sts/conf/server.xml | grep 'certificateKeystoreFile=' | awk '{for(i=1;i<=NF;i++) if($i ~ /^certificateKeystoreFile/) {print $i}}' | tr -d '>' | awk -F'=' '{print $NF}' | tr -d '"') | |
if [[ "$VC_VERSION" =~ ^8 ]]; then | |
if [ "$STS_LOCALHOST_CONNECTOR_STORE" != 'STS_INTERNAL_SSL_CERT' ] && [ "$STS_LOCALHOST_CERTIFICATE_STORE" != 'STS_INTERNAL_SSL_CERT' ] && [ "$STS_CLIENTAUTH_CONNECTOR_STORE" != 'STS_INTERNAL_SSL_CERT' ] && [ "$STS_CLIENTAUTH_CERTIFICATE_STORE" != 'STS_INTERNAL_SSL_CERT' ]; then | |
return 0 | |
else | |
return 1 | |
fi | |
else | |
if [ "$STS_LOCALHOST_CONNECTOR_STORE" != 'STS_INTERNAL_SSL_CERT' ] && [ "$STS_LOCALHOST_CERTIFICATE_STORE" != 'STS_INTERNAL_SSL_CERT' ]; then | |
return 0 | |
else | |
return 1 | |
fi | |
fi | |
} | |
#------------------------------ | |
# Check STS Connection String for ELM configurations | |
#------------------------------ | |
function quickCheckSTSConnectionStrings() { | |
STS_CONNECTION_STRINGS=$($LDAP_SEARCH -LLL -o ldif-wrap=no -h $PSC_LOCATION -p $VMDIR_PORT -b "cn=$SSO_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 vmwSTSConnectionStrings | grep vmwSTSConnectionStrings | awk '{print $2}') | |
NUM_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:' | wc -l) | |
logInfo "vmwSTSConnectionStrings value: $STS_CONNECTION_STRINGS" | |
if [ "$STS_CONNECTION_STRINGS" == "ldap://localhost:$VMDIR_PORT" ]; then | |
return 0 | |
else | |
if [ "$NUM_PSC_NODES" -gt 1 ]; then | |
return 1 | |
else | |
return 0 | |
fi | |
fi | |
} | |
#------------------------------ | |
# Check of the STS server configuration | |
#------------------------------ | |
function checkSTSConfig() { | |
authenticateIfNeeded | |
checkSTSCertConfig | |
checkSTSConnectionStrings | |
} | |
#------------------------------ | |
# Check configuration of the STS server | |
#------------------------------ | |
function checkSTSCertConfig() { | |
VECS_STORES_TO_REPLACE='STS_INTERNAL_SSL_CERT' | |
UPDATE_STS_CONFIG=0 | |
header 'Checking STS server configuration' | |
task 'Checking VECS store configuration' | |
#STS_LOCALHOST_CONNECTOR_STORE=$(grep 'bio-ssl-localhost.https.port' /usr/lib/vmware-sso/vmware-sts/conf/server.xml | grep 'store=' | awk '{for(i=1;i<=NF;i++) if($i ~ /^store/) {print $i}}' | tr -d '>' | awk -F'=' '{print $NF}' | tr -d '"') | |
#STS_LOCALHOST_CERTIFICATE_STORE=$(grep -A2 'bio-ssl-localhost.https.port' /usr/lib/vmware-sso/vmware-sts/conf/server.xml | grep 'certificateKeystoreFile=' | awk '{for(i=1;i<=NF;i++) if($i ~ /^certificateKeystoreFile/) {print $i}}' | tr -d '>' | awk -F'=' '{print $NF}' | tr -d '"') | |
#STS_CLIENTAUTH_CONNECTOR_STORE=$(grep 'bio-ssl-clientauth.https.port' /usr/lib/vmware-sso/vmware-sts/conf/server.xml | grep 'store=' | awk '{for(i=1;i<=NF;i++) if($i ~ /^store/) {print $i}}' | tr -d '>' | awk -F'=' '{print $NF}' | tr -d '"') | |
#STS_CLIENTAUTH_CERTIFICATE_STORE=$(grep -A2 'bio-ssl-clientauth.https.port' /usr/lib/vmware-sso/vmware-sts/conf/server.xml | grep 'certificateKeystoreFile=' | awk '{for(i=1;i<=NF;i++) if($i ~ /^certificateKeystoreFile/) {print $i}}' | tr -d '>' | awk -F'=' '{print $NF}' | tr -d '"') | |
STS_LOCALHOST_CONNECTOR_STORE=$(xmllint --xpath 'string(//Server/Service/Connector[@port="${bio-ssl-localhost.https.port}"]/@store)' /usr/lib/vmware-sso/vmware-sts/conf/server.xml) | |
STS_LOCALHOST_CERTIFICATE_STORE=$(xmllint --xpath 'string(//Server/Service/Connector[@port="${bio-ssl-localhost.https.port}"]/SSLHostConfig/Certificate/@certificateKeystoreFile)' /usr/lib/vmware-sso/vmware-sts/conf/server.xml) | |
STS_CLIENTAUTH_CONNECTOR_STORE=$(xmllint --xpath 'string(//Server/Service/Connector[@port="${bio-ssl-clientauth.https.port}"]/@store)' /usr/lib/vmware-sso/vmware-sts/conf/server.xml) | |
STS_CLIENTAUTH_CERTIFICATE_STORE=$(xmllint --xpath 'string(//Server/Service/Connector[@port="${bio-ssl-clientauth.https.port}"]/SSLHostConfig/Certificate/@certificateKeystoreFile)' /usr/lib/vmware-sso/vmware-sts/conf/server.xml) | |
statusMessage 'OK' 'GREEN' | |
if [[ "$VC_VERSION" =~ ^8 ]] || ([[ "$VC_VERSION" =~ ^7 ]] && [ $VC_BUILD -ge 20845200 ]); then | |
if [ "$STS_LOCALHOST_CONNECTOR_STORE" == 'MACHINE_SSL_CERT' ] && [ "$STS_LOCALHOST_CERTIFICATE_STORE" == 'MACHINE_SSL_CERT' ] && [ "$STS_CLIENTAUTH_CONNECTOR_STORE" == 'MACHINE_SSL_CERT' ] && [ "$STS_CLIENTAUTH_CERTIFICATE_STORE" == 'MACHINE_SSL_CERT' ]; then | |
echo $'\n'"The STS server is using the ${GREEN}MACHINE_SSL_CERT${NORMAL} VECS store."$'\n' | |
else | |
UPDATE_STS_CONFIG=1 | |
if [ "$STS_LOCALHOST_CONNECTOR_STORE" != 'STS_INTERNAL_SSL_CERT' ]; then VECS_STORES_TO_REPLACE+=" $STS_LOCALHOST_CONNECTOR_STORE"; fi | |
if [ "$STS_LOCALHOST_CERTIFICATE_STORE" != 'STS_INTERNAL_SSL_CERT' ]; then VECS_STORES_TO_REPLACE+=" $STS_LOCALHOST_CERTIFICATE_STORE"; fi | |
if [ "$STS_CLIENTAUTH_CONNECTOR_STORE" != 'STS_INTERNAL_SSL_CERT' ]; then VECS_STORES_TO_REPLACE+=" $STS_CLIENTAUTH_CONNECTOR_STORE"; fi | |
if [ "$STS_CLIENTAUTH_CERTIFICATE_STORE" != 'STS_INTERNAL_SSL_CERT' ]; then VECS_STORES_TO_REPLACE+=" $STS_CLIENTAUTH_CERTIFICATE_STORE"; fi | |
echo $'\nThe STS server is using the following VECS stores:' | |
echo "Server > Service > Connector (localhost port): ${YELLOW}${STS_LOCALHOST_CONNECTOR_STORE}${NORMAL}" | |
echo "Server > Service > Connector (localhost port) > SSLHostConfig > Certificate: ${YELLOW}${STS_LOCALHOST_CERTIFICATE_STORE}${NORMAL}" | |
echo "Server > Service > Connector (client auth port): ${YELLOW}${STS_CLIENTAUTH_CONNECTOR_STORE}${NORMAL}" | |
echo "Server > Service > Connector (client auth port) > SSLHostConfig > Certificate: ${YELLOW}${STS_CLIENTAUTH_CERTIFICATE_STORE}${NORMAL}"$'\n' | |
fi | |
else | |
if [ "$STS_LOCALHOST_CONNECTOR_STORE" == 'MACHINE_SSL_CERT' ] && [ "$STS_LOCALHOST_CERTIFICATE_STORE" == 'MACHINE_SSL_CERT' ]; then | |
echo $'\n'"The STS server is using the ${GREEN}MACHINE_SSL_CERT${NORMAL} VECS store."$'\n' | |
else | |
UPDATE_STS_CONFIG=1 | |
if [ "$STS_LOCALHOST_CONNECTOR_STORE" != 'STS_INTERNAL_SSL_CERT' ]; then VECS_STORES_TO_REPLACE+=" $STS_LOCALHOST_CONNECTOR_STORE"; fi | |
if [ "$STS_LOCALHOST_CERTIFICATE_STORE" != 'STS_INTERNAL_SSL_CERT' ]; then VECS_STORES_TO_REPLACE+=" $STS_LOCALHOST_CERTIFICATE_STORE"; fi | |
echo $'\nThe STS server is using the following VECS stores:' | |
echo "Server > Service > Connector (localhost port): ${YELLOW}${STS_LOCALHOST_CONNECTOR_STORE}${NORMAL}" | |
echo "Server > Service > Connector (localhost port) > SSLHostConfig > Certificate: ${YELLOW}${STS_LOCALHOST_CERTIFICATE_STORE}${NORMAL}"$'\n' | |
fi | |
fi | |
if [ $UPDATE_STS_CONFIG -eq 1 ]; then | |
read -p $'\n'"Update STS server configuration to use the ${GREEN}MACHINE_SSL_CERT${NORMAL} store? [n]: " UPDATE_STS_CONFIG_INPUT | |
if [ -z $UPDATE_STS_CONFIG_INPUT ]; then UPDATE_STS_CONFIG_INPUT='n'; fi | |
if [[ $UPDATE_STS_CONFIG_INPUT =~ ^[Yy] ]]; then | |
logInfo 'User has selected to update the STS server configuration' | |
header 'Updating STS server configuration' | |
task 'Backing up configuration' | |
cp /usr/lib/vmware-sso/vmware-sts/conf/server.xml /usr/lib/vmware-sso/vmware-sts/conf/server.xml.backup 2>/dev/null || errorMessage 'Unable to backup /usr/lib/vmware-sso/vmware-sts/conf/server.xml' | |
statusMessage 'OK' 'GREEN' | |
task 'Backing up certificate' | |
$VECS_CLI entry getcert --store STS_INTERNAL_SSL_CERT --alias __MACHINE_CERT > $BACKUP_DIR/STS_INTERNAL_SSL_CERT.crt || errorMessage 'Unable to backup STS_INTERNAL_SSL_CERT certificate' | |
statusMessage 'OK' 'GREEN' | |
task 'Backing up private key' | |
$VECS_CLI entry getkey --store STS_INTERNAL_SSL_CERT --alias __MACHINE_CERT > $BACKUP_DIR/STS_INTERNAL_SSL_CERT.key || errorMessage 'Unable to backup STS_INTERNAL_SSL_CERT private key' | |
statusMessage 'OK' 'GREEN' | |
task 'Changing STS server configuration' | |
for store in $VECS_STORES_TO_REPLACE; do | |
sed -i "s/$store/MACHINE_SSL_CERT/g" /usr/lib/vmware-sso/vmware-sts/conf/server.xml || errorMessage 'Unable to update STS server configuration' | |
done | |
statusMessage 'OK' 'GREEN' | |
if $VECS_CLI store list | grep 'STS_INTERNAL_SSL_CERT' > /dev/null; then | |
task 'Remove legacy STS VECS store' | |
$VECS_CLI store delete --name STS_INTERNAL_SSL_CERT -y > /dev/null|| errorMessage 'Unable to delete VECS store STS_INTERNAL_SSL_CERT' | |
statusMessage 'OK' 'GREEN' | |
fi | |
task 'Stopping STS service' | |
service-control --stop vmware-stsd > /dev/null 2>&1 || errorMessage 'Unable to stop the STS service' | |
statusMessage 'OK' 'GREEN' | |
task 'Starting STS service' | |
service-control --start vmware-stsd > /dev/null 2>&1 || errorMessage 'Unable to start the STS service' | |
statusMessage 'OK' 'GREEN' | |
fi | |
else | |
if $VECS_CLI store list | grep 'STS_INTERNAL_SSL_CERT' > /dev/null; then | |
read -p 'Legacy STS SSL certificate store found. Remove store from VECS [n]: ' REMOVE_STS_STORE_INPUT | |
if [ -z $REMOVE_STS_STORE_INPUT ]; then REMOVE_STS_STORE_INPUT='n'; fi | |
if [[ "$REMOVE_STS_STORE_INPUT" =~ ^[Yy] ]]; then | |
echo '' | |
task 'Remove legacy STS VECS store' | |
$VECS_CLI store delete --name STS_INTERNAL_SSL_CERT -y > /dev/null|| errorMessage 'Unable to delete VECS store STS_INTERNAL_SSL_CERT' | |
statusMessage 'OK' 'GREEN' | |
else | |
echo '' | |
fi | |
fi | |
fi | |
} | |
function checkSTSConnectionStrings() { | |
task 'Checking STS ConnectionStrings' | |
if quickCheckSTSConnectionStrings; then | |
statusMessage 'OK' 'GREEN' | |
else | |
statusMessage 'MISCONFIG' 'YELLOW' | |
read -p $'\n'"Update STS ConnectionStrings value to ${GREEN}ldap://localhost:${VMDIR_PORT}${NORMAL}? [n]: " UPDATE_STS_CONNECTIONSTRINGS | |
if [ -z $UPDATE_STS_CONNECTIONSTRINGS ]; then UPDATE_STS_CONNECTIONSTRINGS='n'; fi | |
if [[ $UPDATE_STS_CONNECTIONSTRINGS =~ ^[Yy] ]]; then | |
header 'Update STS ConnectionStrings' | |
task 'Change ConnectionStrings value' | |
$LDAP_MODIFY -h $PSC_LOCATION -p $VMDIR_PORT -D "cn=$VMDIR_USER,cn=users,$VMDIR_DOMAIN_DN" -y $STAGE_DIR/.vmdir-user-password 2>&1 >> $LOG <<EOF | |
dn: cn=$SSO_DOMAIN,cn=IdentityProviders,cn=$SSO_DOMAIN,cn=Tenants,cn=IdentityManager,cn=Services,$VMDIR_DOMAIN_DN | |
changetype: modify | |
replace: vmwSTSConnectionStrings | |
vmwSTSConnectionStrings: ldap://localhost:$VMDIR_PORT | |
EOF | |
if [ $? -eq 0 ]; then | |
statusMessage 'OK' 'GREEN' | |
else | |
errorMessage 'Unable to update the STS ConnectionStrings value. See log for details' | |
fi | |
fi | |
fi | |
} | |
#------------------------------ | |
# Check Smart Card configuration options | |
#------------------------------ | |
function checkSmartCardOptions() { | |
header 'Smart Card SSO options' | |
echo -n 'Gathering authn SSO options...' | |
SC_SSO_CONFIG=$(sso-config.sh -get_authn_policy -t $SSO_DOMAIN 2>/dev/null) | |
if [ -n "$SC_SSO_CONFIG" ]; then | |
echo -ne "\r" | |
SC_SSO_USE_CRL=$(echo "$SC_SSO_CONFIG" | grep useCertCRL | awk '{print $NF}') | |
SC_SSO_CRL_URL=$(echo "$SC_SSO_CONFIG" | grep CRLUrl | awk '{print $NF}') | |
SC_SSO_CRL_FAILOVER=$(echo "$SC_SSO_CONFIG" | grep useCRLAsFailOver | awk '{print $NF}') | |
SC_SSO_USE_OCSP=$(echo "$SC_SSO_CONFIG" | grep useOCSP | awk '{print $NF}') | |
task 'Use CRL in certificate' | |
if [ "$SC_SSO_USE_CRL" == 'false' ]; then | |
statusMessage 'FALSE' 'YELLOW' | |
else | |
statusMessage 'TRUE' 'GREEN' | |
fi | |
task 'CRL override URL' | |
if [ "$SC_SSO_CRL_URL" == 'UndefinedConfig' ]; then | |
statusMessage 'NONE' 'YELLOW' | |
else | |
statusMessage "$SC_SSO_CRL_URL" 'GREEN' | |
fi | |
task 'Use CRL as failover' | |
if [ "$SC_SSO_CRL_FAILOVER" == 'false' ]; then | |
statusMessage 'FALSE' 'YELLOW' | |
else | |
statusMessage 'TRUE' 'GREEN' | |
fi | |
task 'Use OCSP' | |
if [ "$SC_SSO_USE_OCSP" == 'false' ]; then | |
statusMessage 'FALSE' 'YELLOW' | |
else | |
statusMessage 'TRUE' 'GREEN' | |
fi | |
else | |
echo -ne "\r" | |
echo "${YELLOW}Unable to obtain SSO Authn policy options.${NORMAL}" | |
fi | |
} | |
#------------------------------ | |
# Manage ESXi certificates | |
#------------------------------ | |
function manageESXiCertificates() { | |
authenticateIfNeeded | |
unset ESXI_MANAGE_INPUT | |
header 'Manage ESXi Certificates' | |
echo ' 1. Check ESXi/vCenter certificate trust' | |
echo ' 2. Check ESXi certificate against vCenter database' | |
echo ' 3. Replace ESXi certificate' | |
read -p $'\nSelect an option [Return to Main Menu]: ' ESXI_MANAGE_INPUT | |
case $ESXI_MANAGE_INPUT in | |
1) | |
checkESXivCenterTrustMenu | |
;; | |
2) | |
checkESXiAgainstVCDBMenu | |
;; | |
3) | |
replaceESXiCertificate | |
;; | |
esac | |
} | |
#------------------------------ | |
# Check certificates trust of ESXi hosts and vCenter | |
#------------------------------ | |
function checkESXivCenterTrustMenu() { | |
NUM_HOSTS=$($PSQL -d VCDB -U postgres -c "SELECT COUNT(id) FROM vpx_host WHERE enabled=1" -t | tr -d ' \n') | |
echo $'\n'"There are ${GREEN}${NUM_HOSTS}${NORMAL} hosts connected to vCenter."$'\n' | |
cat << EOF | |
1. Perform check on all hosts (requires uniform root password on all hosts) | |
2. Perform check on all hosts in a cluster (requires uniform root password on all hosts) | |
3. Perform check on single host | |
EOF | |
read -p $'\nSelect an option [Return to Main Menu]: ' ESXI_TRUST_INPUT | |
CERT_MGMT_MODE=$($PSQL -d VCDB -U postgres -c "SELECT value FROM vpx_parameter WHERE name='vpxd.certmgmt.mode'" -t | grep -v '^$' | tr -d ' ') | |
case $ESXI_TRUST_INPUT in | |
1) | |
checkAllESXivCenterTrust "$CERT_MGMT_MODE" | |
;; | |
2) | |
checkClusteredESXivCenterTrust "$CERT_MGMT_MODE" | |
;; | |
3) | |
checkSingleESXivCenterTrust "$CERT_MGMT_MODE" | |
;; | |
esac | |
} | |
#------------------------------ | |
# Check ESXi certificate against vCenter database | |
#------------------------------ | |
function checkESXiAgainstVCDBMenu() { | |
NUM_HOSTS=$($PSQL -d VCDB -U postgres -c "SELECT COUNT(id) FROM vpx_host WHERE enabled=1" -t | tr -d ' \n') | |
echo $'\n'"There are ${GREEN}${NUM_HOSTS}${NORMAL} hosts connected to vCenter."$'\n' | |
cat << EOF | |
1. Perform check on all hosts | |
2. Perform check on all hosts in a cluster | |
3. Perform check on single host | |
EOF | |
read -p $'\nSelect an option [Return to Main Menu]: ' ESXI_CERT_VCDB_INPUT | |
CERT_MGMT_MODE=$($PSQL -d VCDB -U postgres -c "SELECT value FROM vpx_parameter WHERE name='vpxd.certmgmt.mode'" -t | grep -v '^$' | tr -d ' ') | |
case $ESXI_CERT_VCDB_INPUT in | |
1) | |
checkAllESXiAgainstVCDB "$CERT_MGMT_MODE" | |
;; | |
2) | |
checkClusteredESXiAgainstVCDB "$CERT_MGMT_MODE" | |
;; | |
3) | |
checkSingleESXiAgainstVCDB "$CERT_MGMT_MODE" | |
;; | |
esac | |
} | |
#------------------------------ | |
# List clusters in vCenter inventory | |
#------------------------------ | |
function listVCClusters() { | |
CLUSTER_IDS=() | |
CLUSTER_NAMES=() | |
unset CHECK_CLUSTER_INPUT | |
CLUSTERS=$($PSQL -d VCDB -U postgres -c "SELECT ent.id,ent.name FROM vpx_entity AS ent LEFT JOIN vpx_object_type AS obj ON ent.type_id = obj.id WHERE obj.name = 'CLUSTER_COMPUTE_RESOURCE'" -t) | |
echo $'\nCompute clusters:' | |
i=1 | |
logInfo 'List of clusters:' | |
logDetails $CLUSTERS | |
IFS=$'\n' | |
for cluster in $CLUSTERS; do | |
id=$(echo "$cluster" | awk -F'|' '{print $1}' | sed -e 's/[[:space:]]*//') | |
name=$(echo "$cluster" | awk -F'|' '{print $2}' | sed -e 's/[[:space:]]*//') | |
"$name" | |
CLUSTER_IDS+=($id) | |
CLUSTER_NAMES+=($name) | |
((++i)) | |
done | |
IFS=$' \t\n' | |
read -p $'\n\nSelect cluster: ' CHECK_CLUSTER_INPUT | |
} | |
#------------------------------ | |
# Check certificates of all ESXi hosts | |
#------------------------------ | |
function checkAllESXivCenterTrust() { | |
HOSTS=$($PSQL -d VCDB -U postgres -c "SELECT id, dns_name, ip_address FROM vpx_host WHERE enabled=1 ORDER BY dns_name ASC, ip_address ASC" -t) | |
checkESXivCenterTrust "$1" "$HOSTS" | |
} | |
#------------------------------ | |
# Check certificates of ESXi hosts in a cluster | |
#------------------------------ | |
function checkClusteredESXivCenterTrust() { | |
listVCClusters | |
while [ -z $CHECK_CLUSTER_INPUT ]; do read -p $'\n'"${YELLOW}Select cluster:${NORMAL} " CHECK_CLUSTER_INPUT; done | |
HOSTS=$($PSQL -d VCDB -U postgres -c "SELECT id, dns_name, ip_address FROM vpx_host WHERE id IN (SELECT e.id FROM vpx_entity AS e LEFT JOIN vpx_object_type AS obj ON e.type_id=obj.id WHERE obj.name='HOST' AND e.parent_id=${CLUSTER_IDS[$((CHECK_CLUSTER_INPUT-1))]}) AND enabled=1 ORDER BY dns_name ASC, ip_address ASC" -t) | |
checkESXivCenterTrust "$1" "$HOSTS" | |
} | |
#------------------------------ | |
# Check certificate trust status between vCenter and ESXi hosts | |
#------------------------------ | |
function checkESXivCenterTrust() { | |
NEED_TO_RESTART_SPS=0 | |
ENTRIES_UPDATED=0 | |
ENTRIES_CREATED=0 | |
HOSTS="$2" | |
read -r -s -p $'\nEnter root password for all ESXi hosts: ' ESXI_PASSWORD_INPUT | |
while [ -z $ESXI_PASSWORD_INPUT ]; do read -r -s -p $'\n'"${YELLOW}Enter root password for all ESXi hosts:${NORMAL} " ESXI_PASSWORD_INPUT; done | |
echo $'\n\n'"Certificate Management Mode: ${GREEN}$1${NORMAL}" | |
IFS=$'\n' | |
for host_info in $HOSTS; do | |
id=$(echo "$host_info" | awk -F'|' '{print $1}' | tr -d ' ') | |
name=$(echo "$host_info" | awk -F'|' '{print $2}' | tr -d ' ') | |
ip=$(echo "$host_info" | awk -F'|' '{print $3}' | tr -d ' ') | |
if [ -n "$name" ]; then | |
viewESXiCertificateTrust "$name" "$1" "$ESXI_PASSWORD_INPUT" | |
else | |
viewESXiCertificateTrust "$ip" "$1" "$ESXI_PASSWORD_INPUT" | |
fi | |
done | |
IFS=$' \t\n' | |
if [ "$1" == 'thumbprint' ]; then | |
echo $'\n'"Entries updated: $ENTRIES_UPDATED" | |
echo "Entries created: $ENTRIES_CREATED" | |
if [ $NEED_TO_RESTART_SPS -gt 0 ]; then | |
echo $'\nRestarting the SPS service...' | |
restartVMwareServices 'vmware-sps' | |
else | |
echo $'\nDone!' | |
fi | |
fi | |
} | |
#------------------------------ | |
# Check certificates of a single ESXi host | |
#------------------------------ | |
function checkSingleESXivCenterTrust() { | |
header 'ESXi Certificate Trust Check' | |
unset ESXI_NAME_INPUT | |
read -p 'Enter FQDN or IP of the ESXi host: ' ESXI_NAME_INPUT | |
while [ -z $ESXI_NAME_INPUT ]; do read -p 'Enter FQDN or IP of the ESXi host: ' ESXI_NAME_INPUT; done | |
ESXI_VALID_NAME=0 | |
while [ $ESXI_VALID_NAME -eq 0 ]; do | |
if [[ $ESXI_NAME_INPUT =~ ^[a-zA-Z] ]]; then | |
logDetails "Verifying name resolution of $ESXI_NAME_INTPUT" | |
if ! nslookup $ESXI_NAME_INPUT > /dev/null 2>&1; then | |
read -p $'\n'"${YELLOW}Unable to resolve '$ESXI_NAME_INPUT', enter FQDN or IP of the ESXi host:${NORMAL} " ESXI_NAME_INPUT | |
else | |
ESXI_VALID_NAME=1 | |
fi | |
else | |
if ! validateIp $ESXI_NAME_INPUT; then | |
read -p $'\n'"${YELLOW}Invalid IP address, enter FQDN or IP of the ESXi host:${NORMAL} " ESXI_NAME_INPUT | |
else | |
ESXI_VALID_NAME=1 | |
fi | |
fi | |
done | |
read -r -s -p $'\n'"Enter root password for $ESXI_NAME_INPUT: " ESXI_PASSWORD_INPUT | |
while [ -z $ESXI_PASSWORD_INPUT ]; do read -r -s -p $'\n'"${YELLOW}Enter root password for $ESXI_NAME_INPUT:${NORMAL} " ESXI_PASSWORD_INPUT; done | |
echo $'\n\n'"Certificate Management Mode: ${GREEN}$1${NORMAL}" | |
viewESXiCertificateTrust "$ESXI_NAME_INPUT" "$1" "$ESXI_PASSWORD_INPUT" | |
} | |
#------------------------------ | |
# View trust information between vCenter and a host | |
#------------------------------ | |
function viewESXiCertificateTrust() { | |
echo $'\n'"Host: ${CYAN}$1${NORMAL}" | |
HOST_HASH=$(timeout 3 openssl s_client -connect $1:443 2>/dev/null | openssl x509 2>/dev/null) | |
if [ -n "$HOST_HASH" ]; then | |
HOST_CERT_INFO=$(viewCertificateInfo "$HOST_HASH") | |
HOST_CERT_ISSUER=$(echo "$HOST_CERT_INFO" | grep 'Issuer:' | awk -F'Issuer: ' '{print $NF}') | |
HOST_CERT_SUBJECT=$(echo "$HOST_CERT_INFO" | grep 'Subject:' | awk -F'Subject: ' '{print $NF}') | |
HOST_CERT_VALID_START=$(echo "$HOST_HASH" | openssl x509 -noout -startdate 2>/dev/null | sed 's/notBefore=//') | |
HOST_CERT_VALID_END=$(echo "$HOST_HASH" | openssl x509 -noout -enddate 2>/dev/null | sed 's/notAfter=//') | |
HOST_CERT_FINGERPRINT=$(echo "$HOST_HASH" | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}') | |
HOST_CERT_ALGORITHM=$(echo "$HOST_CERT_INFO" | grep 'Signature Algorithm' | head -n1 | awk '{print $NF}') | |
HOST_CERT_SAN=$(echo "$HOST_CERT_INFO" | grep 'X509v3 Subject Alternative Name' -A1 | tail -n1 | sed -e 's/^ *//g' -e 's/, /\n/g' | grep -v '^$' | sort) | |
echo " Issuer: $HOST_CERT_ISSUER" | |
echo " Subject: $HOST_CERT_SUBJECT" | |
if isExpired "$HOST_HASH" 'hash'; then echo "${RED}"; fi | |
echo " Not Before: $HOST_CERT_VALID_START" | |
echo " Not After : $HOST_CERT_VALID_END" | |
echo " ${NORMAL}SHA1 Fingerprint : $HOST_CERT_FINGERPRINT" | |
echo " Signature Algorithm: $HOST_CERT_ALGORITHM" | |
echo ' Subject Alternative Name entries:' | |
if [ -n "$HOST_CERT_SAN" ]; then | |
IFS=$'\n' | |
for san in $(echo "$HOST_CERT_SAN"); do | |
echo " |_$san" | |
done | |
IFS=$' \t\n' | |
fi | |
echo $'\n Certificate Trusts:' | |
if [ "$2" = 'thumbprint' ]; then | |
CURRENT_HOST_SMS_THUMBPRINT=$($VECS_CLI entry getcert --store SMS --alias "https://$1:9080/version.xml" 2>/dev/null | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F'=' '{print $NF}') | |
if [ -n "$CURRENT_HOST_SMS_THUMBPRINT" ]; then | |
echo ' Host IOFILTER provider found in VECS, checking certificate...' | |
if [[ "$CURRENT_SMS_THUMBPRINT" != "$HOST_THUMBPRINT" ]]; then | |
echo ' Mismatch found, re-creating entry...' | |
if $VECS_CLI entry delete --store SMS --alias "https://$1:9080/version.xml" -y > /dev/null; then | |
if echo | openssl s_client -connect $1:443 2>/dev/null | openssl x509 > $STAGE_DIR/$1.crt 2>/dev/null; then | |
if $VECS_CLI entry create --store SMS --alias "https://$1:9080/version.xml" --cert $STAGE_DIR/$1.crt > /dev/null; then | |
echo ' IOFILTER provider certificate updated!' | |
((++ENTRIES_UPDATED)) | |
NEED_TO_RESTART_SPS=1 | |
else | |
echo " ${YELLOW}Unable to re-create the IOFILTER provider certificate in VECS!${NORMAL}" | |
fi | |
else | |
echo " ${YELLOW}Unable to obtain host's SSL certificate on port 443!${NORMAL}" | |
fi | |
else | |
echo " ${YELLOW}Unable to delete the IOFILTER provider certificate from VECS!${NORMAL}" | |
fi | |
else | |
echo " ${GREEN}Certificate matches, no need to update.${NORMAL}" | |
fi | |
else | |
echo ' Host IOFILTER provider certificate not in VECS. Creating entry...' | |
if echo | openssl s_client -connect $1:443 2>/dev/null | openssl x509 > $STAGE_DIR/$1.crt 2>/dev/null; then | |
if $VECS_CLI entry create --store SMS --alias "https://$1:9080/version.xml" --cert $STAGE_DIR/$1.crt > /dev/null; then | |
echo ' IOFILTER provider certificate created!' | |
((++ENTRIES_CREATED)) | |
NEED_TO_RESTART_SPS=1 | |
else | |
echo " ${YELLOW}Unable to re-create the IOFILTER provider certificate in VECS!${NORMAL}" | |
fi | |
else | |
echo " ${YELLOW}Unable to obtain host's SSL certificate on port 443!${NORMAL}" | |
fi | |
fi | |
else | |
HOST_RHTTPPROXY_CERT=$(echo | openssl s_client -connect $1:443 2>/dev/null | openssl x509 2>/dev/null) | |
HOST_IOFILTERVP_CERT=$(echo | openssl s_client -connect $1:9080 2>/dev/null | openssl x509 2>/dev/null) | |
VCENTER_MACHINE_SSL_CERT=$($VECS_CLI entry getcert --store MACHINE_SSL_CERT --alias __MACHINE_CERT) | |
SPS_CERT=$($VECS_CLI entry getcert --store SMS --alias sms_self_signed) | |
echo -n ' Reverse Proxy cert (port 443): ' | |
SEARCH_CERTS="$(find /etc/vmware-vpx/docRoot/certs/ -type f | grep -v '\.r')" | |
if checkForCACerts "$HOST_RHTTPPROXY_CERT"; then | |
echo "${GREEN}Trusted by vCenter${NORMAL}" | |
else | |
echo "${YELLOW}Not trusted by vCenter${NORMAL}" | |
fi | |
echo -n ' IOFilter VASA provider cert (port 9080): ' | |
if [ -n "$HOST_IOFILTERVP_CERT" ]; then | |
if checkForCACerts "$HOST_IOFILTERVP_CERT"; then | |
echo "${GREEN}Trusted by vCenter${NORMAL}" | |
else | |
echo "${YELLOW}Not trusted by vCenter${NORMAL}" | |
fi | |
else | |
echo "${YELLOW}unknown${NORMAL}" | |
fi | |
ESXI_CASTORE_HTTP_CODE=$(curl -k -X GET -u "root:$3" https://$1/host/castore -o $STAGE_DIR/$1-castore.pem -s -w "%{http_code}\n") | |
logInfo "Getting castore.pem file from host $1" | |
logInfo "Response from host $1 - HTTP status code: $ESXI_CASTORE_HTTP_CODE" | |
if [ "$ESXI_CASTORE_HTTP_CODE" == '200' ]; then | |
sed -i '/^$/d' $STAGE_DIR/$1-castore.pem | |
csplit -s -z -f $STAGE_DIR/$1-ca- -b %02d.crt $STAGE_DIR/$1-castore.pem '/-----BEGIN CERTIFICATE-----/' '{*}' | |
SEARCH_CERTS=$(ls $STAGE_DIR/$1-ca-*.crt 2>/dev/null) | |
if [ -n "$SEARCH_CERTS" ]; then | |
echo -n ' vCenter Machine SSL cert: ' | |
if checkForCACerts "$VCENTER_MACHINE_SSL_CERT"; then | |
echo "${GREEN}Trusted by host${NORMAL}" | |
else | |
echo "${YELLOW}Not trusted by host${NORMAL}" | |
fi | |
echo -n ' SPS service connection: ' | |
if checkForCACerts "$SPS_CERT"; then | |
echo "${GREEN}Trusted by host${NORMAL}" | |
else | |
echo "${YELLOW}Not trusted by host (maybe)${NORMAL}" | |
fi | |
else | |
echo " vCenter Machine SSL cert: ${YELLOW}No CA certs in /etc/vmware/ssl/castore.pem${NORMAL}" | |
echo " SPS service connection: ${YELLOW}No CA certs in /etc/vmware/ssl/castore.pem${NORMAL}" | |
fi | |
elif [ "$ESXI_CASTORE_HTTP_CODE" == '401' ]; then | |
echo " vCenter Machine SSL cert: ${YELLOW}unknown (possible bad ESXi root password)${NORMAL}" | |
echo " SPS service connection: ${YELLOW}unknown (possible bad ESXi root password)${NORMAL}" | |
else | |
echo " vCenter Machine SSL cert: ${YELLOW}unknown${NORMAL}" | |
echo " SPS service connection: ${YELLOW}unknown${NORMAL}" | |
fi | |
fi | |
else | |
echo $'\n'"${YELLOW}Unable to obtain SSL certificate from host $1.${NORMAL}" | |
fi | |
} | |
#------------------------------ | |
# Check the SSL certificate of all ESXi hosts against VCDB | |
#------------------------------ | |
function checkAllESXiAgainstVCDB() { | |
HOSTS=$($PSQL -d VCDB -U postgres -c "SELECT id, dns_name, ip_address FROM vpx_host WHERE enabled=1 ORDER BY dns_name ASC, ip_address ASC" -t) | |
checkESXiAgainstVCDB "$1" "$HOSTS" | |
} | |
#------------------------------ | |
# Check the SSL certificate of ESXi hosts in a cluster against VCDB | |
#------------------------------ | |
function checkClusteredESXiAgainstVCDB() { | |
listVCClusters | |
while [ -z $CHECK_CLUSTER_INPUT ]; do read -p $'\n'"${YELLOW}Select cluster:${NORMAL} " CHECK_CLUSTER_INPUT; done | |
HOSTS=$($PSQL -d VCDB -U postgres -c "SELECT id, dns_name, ip_address FROM vpx_host WHERE id IN (SELECT e.id FROM vpx_entity AS e LEFT JOIN vpx_object_type AS obj ON e.type_id=obj.id WHERE obj.name='HOST' AND e.parent_id=${CLUSTER_IDS[$((CHECK_CLUSTER_INPUT-1))]}) AND enabled=1 ORDER BY dns_name ASC, ip_address ASC" -t) | |
checkESXiAgainstVCDB "$1" "$HOSTS" | |
} | |
#------------------------------ | |
# Check the SSL certificate of a single ESXi host against VCDB | |
#------------------------------ | |
function checkSingleESXiAgainstVCDB() { | |
unset ESXI_NAME_INPUT | |
read -p 'Enter FQDN or IP of the ESXi host: ' ESXI_NAME_INPUT | |
while [ -z $ESXI_NAME_INPUT ]; do read -p 'Enter FQDN or IP of the ESXi host: ' ESXI_NAME_INPUT; done | |
ESXI_VALID_NAME=0 | |
while [ $ESXI_VALID_NAME -eq 0 ]; do | |
if [[ $ESXI_NAME_INPUT =~ ^[a-zA-Z] ]]; then | |
logDetails "Verifying name resolution of $ESXI_NAME_INTPUT" | |
if ! nslookup $ESXI_NAME_INPUT > /dev/null 2>&1; then | |
read -p $'\n'"${YELLOW}Unable to resolve '$ESXI_NAME_INPUT', enter FQDN or IP of the ESXi host:${NORMAL} " ESXI_NAME_INPUT | |
else | |
HOSTS="|$ESXI_NAME_INPUT|" | |
ESXI_VALID_NAME=1 | |
fi | |
else | |
if ! validateIp $ESXI_NAME_INPUT; then | |
read -p $'\n'"${YELLOW}Invalid IP address, enter FQDN or IP of the ESXi host:${NORMAL} " ESXI_NAME_INPUT | |
else | |
HOSTS="||$ESXI_NAME_INPUT" | |
ESXI_VALID_NAME=1 | |
fi | |
fi | |
done | |
checkESXiAgainstVCDB "$1" "$HOSTS" | |
} | |
#------------------------------ | |
# Output information on SSL certificate of a host compared to VCDB | |
#------------------------------ | |
function checkESXiAgainstVCDB() { | |
header 'Checking ESXi SSL Thumbprints Against vCenter Database' | |
if [ "$1" == 'thumbprint' ]; then | |
echo "${YELLOW}VMCA Certificate Management Mode is set to 'thumbprint'${NORMAL}" | |
else | |
IFS=$'\n' | |
for host_info in $HOSTS; do | |
id=$(echo "$host_info" | awk -F'|' '{print $1}' | tr -d ' ') | |
name=$(echo "$host_info" | awk -F'|' '{print $2}' | tr -d ' ') | |
ip=$(echo "$host_info" | awk -F'|' '{print $3}' | tr -d ' ') | |
if [ -n "$name" ]; then | |
viewESXiAgainstVCDB "$name" | |
else | |
viewESXiAgainstVCDB "$ip" | |
fi | |
done | |
IFS=$' \t\n' | |
fi | |
} | |
#------------------------------ | |
# Output information on SSL certificate of a host compared to VCDB | |
#------------------------------ | |
function viewESXiAgainstVCDB() { | |
echo $'\n'"${CYAN}$1${NORMAL}" | |
THUMBPRINT_INFO_VCDB=$($PSQL -U postgres -d VCDB -c "SELECT expected_ssl_thumbprint,host_ssl_thumbprint FROM vpx_host WHERE dns_name = '$1' OR ip_address = '$1'" -t) | |
EXPECTED_SSL_THUMBPRINT=$(echo "$THUMBPRINT_INFO_VCDB" | awk -F '|' '{print $1}' | tr -d ' ') | |
HOST_SSL_THUMBPRINT=$(echo "$THUMBPRINT_INFO_VCDB" | awk -F '|' '{print $2}' | tr -d ' ') | |
ACTUAL_SSL_THUMBPRINT=$(echo | openssl s_client -connect $1:443 2>/dev/null | openssl x509 -noout -fingerprint -sha1 2>/dev/null | awk -F '=' '{print $NF}') | |
echo " Expected Thumbprint (VCDB) : $EXPECTED_SSL_THUMBPRINT" | |
echo " Host Thumbprint (VCDB) : $HOST_SSL_THUMBPRINT" | |
if [ -z "$ACTUAL_SSL_THUMBPRINT" ]; then | |
echo " Actual Thumbprint (openssl) : ${YELLOW}Cannot connect to host${NORMAL}" | |
echo " Status: ${YELLOW}UNKNOWN${NORMAL}" | |
else | |
echo " Actual Thumbprint (openssl) : $ACTUAL_SSL_THUMBPRINT" | |
if [ "$EXPECTED_SSL_THUMBPRINT" != "$ACTUAL_SSL_THUMBPRINT" ] && [ "$HOST_SSL_THUMBPRINT" != "$ACTUAL_SSL_THUMBPRINT" ] && [ "$EXPECTED_SSL_THUMBPRINT" != "$HOST_SSL_THUMBPRINT" ]; then | |
echo " Status: ${YELLOW}MISMATCH${NORMAL}" | |
else | |
echo " Status: ${GREEN}MATCH${NORMAL}" | |
fi | |
fi | |
} | |
#------------------------------ | |
# Check for signing certificates of a leaf certificate | |
#------------------------------ | |
function checkForCACerts() { | |
SEARCH=0 | |
FOUND_ROOT=0 | |
FOUND_ANY=0 | |
SEARCH_ISSUER=$(echo "$1" | openssl x509 -noout -text 2>/dev/null | grep 'Issuer:' | sed -e 's/Issuer: //' -e 's/[[:space:]]*//') | |
SEARCH_AUTH_KEY_ID=$(echo "$1" | openssl x509 -noout -text 2>/dev/null | grep -A1 'Authority Key Id' | grep 'keyid:' | sed -e 's/keyid://' | tr -d ' ') | |
while [ $SEARCH -eq 0 ]; do | |
IFS=$'\n' | |
for cert in $SEARCH_CERTS; do | |
SEARCH_CURRENT_SUBJECT=$(openssl x509 -noout -text -in $cert 2>/dev/null | grep 'Subject:' | sed -e 's/Subject: //' -e 's/[[:space:]]*//') | |
SEARCH_CURRENT_ISSUER=$(openssl x509 -noout -text -in $cert 2>/dev/null | grep 'Issuer:' | sed -e 's/Issuer: //' -e 's/[[:space:]]*//') | |
SEARCH_SKID=$(openssl x509 -noout -text -in $cert 2>/dev/null | grep -A1 'Subject Key Id' | tail -n1 | tr -d ' ') | |
SEARCH_CURRENT_AUTH_KEY_ID=$(openssl x509 -noout -text -in $cert 2>/dev/null | grep -A1 'Authority Key Id' | grep 'keyid:' | sed -e 's/keyid://' -e 's/[[:space:]]*//') | |
if [ "$SEARCH_ISSUER" = "$SEARCH_CURRENT_SUBJECT" ] && [ "$SEARCH_AUTH_KEY_ID" = "$SEARCH_SKID" ]; then | |
if [ "$SEARCH_CURRENT_SUBJECT" = "$SEARCH_CURRENT_ISSUER" ]; then | |
SEARCH=1 | |
FOUND_ROOT=1 | |
break 2 | |
else | |
SEARCH_ISSUER=$SEARCH_CURRENT_ISSUER | |
SEARCH_AUTH_KEY_ID=$SEARCH_CURRENT_AUTH_KEY_ID | |
FOUND_ANY=1 | |
fi | |
fi | |
done | |
IFS=$' \t\n' | |
if [ $FOUND_ANY -eq 0 ]; then | |
SEARCH=1 | |
else | |
FOUND_ANY=0 | |
fi | |
done | |
if [ $FOUND_ROOT -eq 0 ]; then | |
return 1 | |
else | |
return 0 | |
fi | |
} | |
#------------------------------ | |
# Replace SSL certificate, private key, and CA store on ESXi host | |
#------------------------------ | |
function replaceESXiCertificate() { | |
unset ESXI_CA_SIGNED_OPTION_INPUT | |
header 'ESXi Certificate Options' | |
echo $'\n1. Generate Certificate Signing Request and Private Key' | |
echo '2. Generate Certificate Signing Request and Private Key' | |
echo ' from custom OpenSSL configuration file' | |
echo '3. Import CA-signed certificate and key' | |
read -p $'\nSelect an option [Return to Main Menu]: ' ESXI_CA_SIGNED_OPTION_INPUT | |
if [ -z $ESXI_CA_SIGNED_OPTION_INPUT ]; then return 1; fi | |
if [ "$ESXI_CA_SIGNED_OPTION_INPUT" == '3' ]; then | |
authenticateIfNeeded | |
logInfo 'User has chosen to import a CA-signed ESXi SSL certificate and key' | |
read -p $'\nEnter FQDN or IP of the ESXi host: ' ESXI_NAME_INPUT | |
ESXI_VALID_NAME=0 | |
while [ $ESXI_VALID_NAME -eq 0 ]; do | |
if [[ $ESXI_NAME_INPUT =~ ^[a-zA-Z] ]]; then | |
if ! nslookup $ESXI_NAME_INPUT > /dev/null 2>&1; then | |
read -p $'\n'"${YELLOW}Unable to resolve '$ESXI_NAME_INPUT'${NORMAL}', enter FQDN or IP of the ESXi host:${NORMAL} " ESXI_NAME_INPUT | |
else | |
ESXI_VALID_NAME=1 | |
fi | |
else | |
if ! validateIp $ESXI_NAME_INPUT; then | |
read -p $'\n'"${YELLOW}Invalid IP address, enter FQDN or IP of the ESXi host:${NORMAL} " ESXI_NAME_INPUT | |
else | |
ESXI_VALID_NAME=1 | |
fi | |
fi | |
done | |
read -r -s -p $'\n'"Enter root password for $ESXI_NAME_INPUT: " ESXI_PASSWORD_INPUT | |
while [ -z $ESXI_PASSWORD_INPUT ]; do read -r -s -p $'\n'"${YELLOW}Enter root password for $ESXI_NAME_INPUT:${NORMAL} " ESXI_PASSWORD_INPUT; done | |
read -e -p $'\n\nEnter path to new ESXi certificate: ' ESXI_NEW_CERT_INPUT | |
while [ ! -f $ESXI_NEW_CERT_INPUT ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to new ESXi certificate:${NORMAL} " ESXI_NEW_CERT_INPUT; done | |
getCorrectCertFormat "$ESXI_NEW_CERT_INPUT" 'NEW_ESXI_CERT' | |
NEW_ESXI_CERT_MODULUS=$(openssl x509 -noout -modulus -in $NEW_ESXI_CERT 2>/dev/null | md5sum | awk '{print $1}') | |
getPrivateKey "$NEW_ESXI_CERT_MODULUS" "NEW_ESXI" "ESXi host" | |
verifyCertAndKey "$NEW_ESXI_CERT" "$NEW_ESXI_KEY" | |
getCAChain "$NEW_ESXI_CERT" | |
header 'Replace ESXi Certificate' | |
task 'Publish CA signing certificates' | |
$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' | |
$VECS_CLI force-refresh > /dev/null 2>&1 || errorMessage 'Unable to perform a force-refresh of CA certificates in VECS' | |
statusMessage 'OK' 'GREEN' | |
SEARCH_CERTS="$(find /etc/vmware-vpx/docRoot/certs/ -type f | grep -v '\.r')" | |
if [ -f $STAGE_DIR/$ESXI_NAME_INPUT-castore.pem ]; then echo '' > $STAGE_DIR/$ESXI_NAME_INPUT-castore.pem; fi | |
for cert in "$SEARCH_CERTS"; do | |
if isCertCA "$(cat $cert)"; then cat $cert >> $STAGE_DIR/$ESXI_NAME_INPUT-castore.pem; fi | |
done | |
$VECS_CLI entry getcert --store SMS --alias sms_self_signed >> $STAGE_DIR/$ESXI_NAME_INPUT-castore.pem | |
task 'Replace ESXi certificate' | |
logInfo "Replacing ESXi certificate with $NEW_ESXI_CERT" | |
logInfo "Replacing ESXi private key with $CURRENT_PRIVATE_KEY_PATH | |
" | |
ESXI_CERT_REPLACEMENT_HTTP_CODE=$(curl -k -X PUT -u "root:$ESXI_PASSWORD_INPUT" https://$ESXI_NAME_INPUT/host/ssl_cert --data-binary @$NEW_ESXI_CERT -s -w "%{http_code}\n") | |
if [[ "$ESXI_CERT_REPLACEMENT_HTTP_CODE" =~ ^20 ]]; then | |
statusMessage 'OK' 'GREEN' | |
else | |
errorMessage "Unable to replace certificate, HTTP return code: $ESXI_CERT_REPLACEMENT_HTTP_CODE" | |
fi | |
task 'Replace ESXi private key' | |
ESXI_KEY_REPLACEMENT_HTTP_CODE=$(curl -k -X PUT -u "root:$ESXI_PASSWORD_INPUT" https://$ESXI_NAME_INPUT/host/ssl_key --data-binary @$CURRENT_PRIVATE_KEY_PATH -s -w "%{http_code}\n") | |
if [[ "$ESXI_KEY_REPLACEMENT_HTTP_CODE" =~ ^20 ]]; then | |
statusMessage 'OK' 'GREEN' | |
else | |
errorMessage "Unable to replace private key, HTTP return code: $ESXI_KEY_REPLACEMENT_HTTP_CODE" | |
fi | |
task 'Replace castore.pem' | |
ESXI_CASTORE_REPLACEMENT_HTTP_CODE=$(curl -k -X PUT -u "root:$ESXI_PASSWORD_INPUT" https://$ESXI_NAME_INPUT/host/castore --data-binary @$STAGE_DIR/$ESXI_NAME_INPUT-castore.pem -s -w "%{http_code}\n") | |
if [[ "$ESXI_CASTORE_REPLACEMENT_HTTP_CODE" =~ ^20 ]]; then | |
statusMessage 'OK' 'GREEN' | |
else | |
errorMessage "Unable to replace castore.pem, HTTP return code: $ESXI_CASTORE_REPLACEMENT_HTTP_CODE" | |
fi | |
cat << EOF | |
${YELLOW}Additional steps are necessary to complete this process: | |
1. Run the following command on the ESXi host to save | |
the new certificate and key to the bootbank: | |
/bin/auto-backup.sh | |
2. Either reboot the ESXi host, or restart the | |
Management Agents (rhttpproxy, hostd, vpxa, etc.) | |
3. Disconnect and Re-connect the host in vCenter to | |
update certificate information in the vCenter database${NORMAL} | |
EOF | |
else | |
clearCSRInfo | |
read -p $'\n'"Enter a value for the ${CYAN}CommonName${NORMAL} of the certificate: " ESXI_CN_INPUT | |
while [ -z $ESXI_CN_INPUT ]; do read -p $'\n'"${YELLOW}Enter a value for the CommonName of the certificate:${NORMAL} " ESXI_CN_INPUT; done | |
ESXI_CSR=$REQUEST_DIR/$ESXI_CN_INPUT-$TIMESTAMP.csr | |
ESXI_KEY=$REQUEST_DIR/$ESXI_CN_INPUT-$TIMESTAMP.key | |
if [ "$ESXI_CA_SIGNED_OPTION_INPUT" == '1' ]; then | |
logInfo 'User has chosen to generate the ESXi SSL private key and CSR' | |
ESXI_CFG=$REQUEST_DIR/$ESXI_CN_INPUT.cfg | |
if [ -z "$CSR_COUNTRY" ]; then getCSRInfo; fi | |
defineSANEntries 'ESXi' 'ESXi' "$ESXI_CN_INPUT" | |
generateOpensslConfig $ESXI_CN_INPUT $ESXI_CFG "ESXi" | |
else | |
logInfo 'User has chosen to generate the ESXi SSL private key and CSR from a custom OpenSSL configuration file' | |
read -e -p $'\nEnter path to custom OpenSSL configuration file: ' ESXI_CFG | |
while [ ! -f "$ESXI_CFG" ]; do read -e -p $'\n'"${YELLOW}File not found, enter path to custom OpenSSL configuration file:${NORMAL} " ESXI_CFG; done | |
fi | |
header "Replace ESXi Certificate" | |
task "Generating Certificate Signing Request" | |
generateCSR $ESXI_CSR $ESXI_KEY $ESXI_CFG || errorMessage "Unable to generate Certificate Signing Request and Private Key" | |
statusMessage "OK" "GREEN" | |
printf "\nCertificate Signing Request generated at ${CYAN}${ESXI_CSR}${NORMAL}" | |
printf "\nPrivate Key generated at ${CYAN}${ESXI_KEY}${NORMAL}\n" | |
fi | |
} | |
#------------------------------ | |
# Prompt to restart VMware services | |
#------------------------------ | |
function promptRestartVMwareServices() { | |
if [ -z $1 ]; then | |
read -p $'\nRestart VMware services [no]: ' RESTART_SERVICES_INPUT | |
else | |
read -p $'\n'"Restart service $1 [no]: " RESTART_SERVICES_INPUT | |
fi | |
if [[ "$RESTART_SERVICES_INPUT" =~ ^[yY] ]]; then | |
if [ -z $1 ]; then | |
restartVMwareServices | |
else | |
restartVMwareServices $1 | |
fi | |
operationMenu | |
processOperationMenu | |
elif [ -n "$1" ]; then | |
operationMenu | |
processOperationMenu | |
fi | |
} | |
#------------------------------ | |
# Menu to restart VMware services | |
#------------------------------ | |
function restartServicesMenu() { | |
header 'Restart VMware Services' | |
echo ' 1. Restart all VMware services' | |
echo ' 2. Restart specific VMware service' | |
read -p $'\nSelect an option [Return to Main Menu]: ' RESTART_SERVICES_INPUT | |
case $RESTART_SERVICES_INPUT in | |
1) | |
restartVMwareServices | |
;; | |
2) | |
read -p $'\nEnter VMware service to restart: ' RESTART_SERVICE_INPUT | |
while [ -z "$RESTART_SERVICE_INPUT" ]; do read -p $'\n'"${YELLOW}Enter VMware service to restart:${NORMAL} " RESTART_SERVICE_INPUT; done | |
if echo "$VMWARE_SERVICES" | grep "^$RESTART_SERVICE_INPUT$" > /dev/null; then | |
restartVMwareServices "$RESTART_SERVICE_INPUT" | |
else | |
echo $'\n'"${YELLOW}Unknown service '$RESTART_SERVICE_INPUT'${NORMAL}" | |
fi | |
;; | |
esac | |
} | |
#------------------------------ | |
# Restart all VMware services | |
#------------------------------ | |
function restartVMwareServices() { | |
header 'Restarting Services' | |
if [ $# -eq 0 ]; then | |
task 'Stopping VMware services' | |
service-control --stop $VMON_SERVICE_PROFILE > /dev/null 2>&1 || errorMessage 'Unable to stop all VMware services, check log for details' | |
statusMessage 'OK' 'GREEN' | |
task 'Starting VMware services' | |
service-control --start $VMON_SERVICE_PROFILE > /dev/null 2>&1 || errorMessage 'Unable to start all VMware services, check log for details' | |
statusMessage 'OK' 'GREEN' | |
if [[ "$VC_VERSION" =~ ^6 ]]; then | |
task 'Restarting VAMI service' | |
systemctl restart vami-lighttp | |
if [ $(systemctl status vami-lighttp | grep 'Active:' | awk '{print $3}') == '(running)' ]; then | |
statusMessage 'OK' 'GREEN' | |
else | |
statusMessage 'ERROR' 'YELLOW' | |
fi | |
fi | |
if [[ $UPDATED_MACHINE_SSL -eq 1 || $UPDATED_TRUST_ANCHORS -eq 1 ]] && [ "$NODE_TYPE" != 'embedded' ]; then | |
printf "\n\n${YELLOW}Please restart services on all other vCenter/PSC nodes in this environment.${NORMAL}\n\n" | |
fi | |
else | |
while [ $# -gt 0 ]; do | |
task "Stopping $1" | |
service-control --stop $1 > /dev/null 2>&1 || errorMessage "Unable to stop service $1, check log for details" | |
statusMessage 'OK' 'GREEN' | |
task "Starting $1" | |
service-control --start $1 > /dev/null 2>&1 || errorMessage "Unable to start service $1, check log for details" | |
statusMessage 'OK' 'GREEN' | |
shift | |
done | |
fi | |
} | |
# commands | |
VECS_CLI='/usr/lib/vmware-vmafd/bin/vecs-cli' | |
DIR_CLI='/usr/lib/vmware-vmafd/bin/dir-cli' | |
VMAFD_CLI='/usr/lib/vmware-vmafd/bin/vmafd-cli' | |
if [ -f '/usr/sbin/vmon-cli' ]; then | |
VMON_CLI='/usr/sbin/vmon-cli' | |
else | |
VMON_CLI='/usr/lib/vmware-vmon/vmon-cli' | |
fi | |
CERTOOL='/usr/lib/vmware-vmca/bin/certool' | |
LDAP_DELETE='/usr/bin/ldapdelete' | |
LDAP_SEARCH='/usr/bin/ldapsearch' | |
LDAP_MODIFY='/usr/bin/ldapmodify' | |
PSQL='/opt/vmware/vpostgres/current/bin/psql' | |
# variables | |
VC_VERSION=$(grep 'CLOUDVM_VERSION:' /etc/vmware/.buildInfo | awk -F ':' '{print $NF}' | awk -F'.' '{print $1"."$2}') | |
VC_BUILD=$(grep '"build":' /etc/applmgmt/appliance/update.conf | awk -F ':' '{print $NF}' | tr -d '", ') | |
LOG_DIR='/var/log/vmware/vCert' | |
LOG="$LOG_DIR/vCert.log" | |
DEBUG=0 | |
CLEANUP=1 | |
EXIT_ON_BACKUP_FAILURE=1 | |
READ_TIMEOUTS=120 | |
VERIFY_PASSED_CREDENTIALS=0 | |
TOP_DIR='/root/vCert-master' | |
WORK_DIR="$TOP_DIR/$(date +%Y%m%d)" | |
STAGE_DIR="$WORK_DIR/stage" | |
REQUEST_DIR="$WORK_DIR/requests" | |
BACKUP_DIR="$WORK_DIR/backup" | |
VC_REPORT="$LOG_DIR/vcenter-certificate-report.txt" | |
#ESXi_REPORT="$LOG_DIR/esxi-certificate-report.txt" | |
VMCA_CERT='/var/lib/vmware/vmca/root.cer' | |
VMON_SERVICE_PROFILE='--all' | |
VMWARE_SERVICES=$(service-control --list | awk '{print $1}' | sort) | |
NODE_TYPE=$(cat /etc/vmware/deployment.node.type) | |
HOSTNAME=$(hostname -f) | |
HOSTNAME_LC=$(echo $HOSTNAME | awk '{print tolower($0)}') | |
HOSTNAME_SHORT=$(hostname -s) | |
IP=$(ip a | grep -A 2 eth0 | grep inet | awk '{print $2}' | awk -F'/' '{print $1}') | |
PNID=$($VMAFD_CLI get-pnid --server-name localhost) | |
PNID_LC=$(echo $PNID | awk '{print tolower($0)}') | |
MACHINE_ID=$($VMAFD_CLI get-machine-id --server-name localhost) | |
SSO_DOMAIN=$($VMAFD_CLI get-domain-name --server-name localhost) | |
SSO_SITE=$($VMAFD_CLI get-site-name --server-name localhost) | |
VMDIR_FQDN=$($VMAFD_CLI get-ls-location --server-name localhost | sed -e 's/:443//g' | awk -F'/' '{print $3}') | |
VMDIR_PORT='389' | |
VMDIR_DOMAIN_DN="dc=$(echo $SSO_DOMAIN | sed 's/\./,dc=/g')" | |
VMDIR_MACHINE_PASSWORD=$(/opt/likewise/bin/lwregshell list_values '[HKEY_THIS_MACHINE\services\vmdir]' | grep dcAccountPassword | awk -F' ' '{print $NF}' | awk '{print substr($0,2,length($0)-2)}' | sed -e 's/\\"/"/g' -e 's/\\\\/\\/g') | |
VMDIR_MACHINE_ACCOUNT_DN=$(/opt/likewise/bin/lwregshell list_values '[HKEY_THIS_MACHINE\services\vmdir]' | grep '"dcAccountDN"' | awk -F' ' '{print $NF}' | awk '{print substr($0,2,length($0)-2)}') | |
VMDIR_USER_UPN_DEFAULT="administrator@$SSO_DOMAIN" | |
VMDIR_USER='' | |
VMDIR_USER_UPN='' | |
VMDIR_USER_PASSWORD='' | |
if [ $NODE_TYPE != 'management' ]; then | |
PSC_LOCATION='localhost' | |
else | |
PSC_LOCATION=$VMDIR_FQDN | |
fi | |
HOSTUPDATE_ISSUER_EXPECTED='DigiCert TLS RSA SHA256 2020 CA1' | |
UNSUPPORTED_SIGNATURE_ALGORITHMS='md2WithRSAEncryption|md5WithRSAEncryption|RSASSA-PSS|dsaWithSHA1|ecdsa_with_SHA1|sha1WithRSAEncryption' | |
CERT_HASHES=() | |
TRUSTED_ROOT_CHAIN='' | |
VMCA_REPLACE='SELF-SIGNED' | |
STS_REPLACE='VMCA-SIGNED' | |
MACHINE_SSL_REPLACE='VMCA-SIGNED' | |
SOLUTION_USER_REPLACE='VMCA-SIGNED' | |
VMDIR_REPLACE='VMCA-SIGNED' | |
AUTH_PROXY_REPLACE='VMCA-SIGNED' | |
AUTO_DEPLOY_CA_REPLACE='SELF-SIGNED' | |
# CSR defaults and variables | |
VMCA_CN_DEFAULT='CA' | |
CSR_COUNTRY_DEFAULT='US' | |
CSR_ORG_DEFAULT='VMware' | |
CSR_ORG_UNIT_DEFAULT='VMware Engineering' | |
CSR_STATE_DEFAULT='California' | |
CSR_LOCALITY_DEFAULT='Palo Alto' | |
# script workflow | |
preStartOperations $@ | |
operationMenu | |
processOperationMenu |
Syntax error in 5628
Syntax error in 5628
Спасибо, надеюсь эту штуку выложат в паблик рано или поздно.
Недавно обновлял сертификат на вЦентре, очень помог скрипт, стандартный скрипт не очень удобен! Кстати сертификат на два года получился, это запрос так сформировал или центр сертификации (шаблон) на такой срок ?--Отправлено из мобильной Яндекс Почты18.11.2023, 20:07, "fluggelgleckheimlen" ***@***.***>***@***.*** commented on this gist.Syntax error in 5628Спасибо, надеюсь эту штуку выложат в паблик рано или поздно.—Reply to this email directly, view it on GitHub or unsubscribe.You are receiving this email because you commented on the thread.Triage notifications on the go with GitHub Mobile for iOS or Android.
По-моему это хардкод в VMCA.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Originally found here:
https://virtham.us/posts/f/vcert