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