|
#!/usr/bin/env bash
|
|
|
|
# Define colors uses for status output
|
|
RED=$(tput setaf 1 2> /dev/null)
|
|
GREEN=$(tput setaf 2 2> /dev/null)
|
|
RESET=$(tput sgr0 2> /dev/null)
|
|
|
|
CABUNDLE_MAX_ISSUERS=32
|
|
|
|
# Branding
|
|
BRANDING_FILE=/usr/share/foreman-installer/katello-certs-check-branding
|
|
if [[ -f "${BRANDING_FILE}" ]]; then
|
|
source "${BRANDING_FILE}"
|
|
fi
|
|
PROJECT=${PROJECT:-Katello}
|
|
SERVER_SCENARIO=${SERVER_SCENARIO:-katello}
|
|
INSTALLER=${INSTALLER:-foreman-installer}
|
|
CERTS_TOOL=${CERTS_TOOL:-foreman-proxy-certs-generate}
|
|
SERVER_TARGET=${SERVER_TARGET:-foreman}
|
|
PROXY_TARGET=${PROXY_TARGET:-foreman-proxy}
|
|
PROXY_VAR=${PROXY_VAR:-FOREMAN_PROXY}
|
|
|
|
function usage () {
|
|
cat <<HELP >&2
|
|
Verifies, that custom SSL certificate files are usable
|
|
as part of the ${PROJECT} installation. When passing filenames use absolute paths.
|
|
|
|
usage: $0 -t [${SERVER_TARGET}|${PROXY_TARGET}] -c CERT_FILE -k KEY_FILE -b CA_BUNDLE_FILE
|
|
HELP
|
|
}
|
|
|
|
while getopts "t:c:k:b:" opt; do
|
|
case $opt in
|
|
t)
|
|
TARGET=$(echo $OPTARG|tr '[:upper:]' '[:lower:]')
|
|
;;
|
|
c)
|
|
CERT_FILE="$(readlink -f $OPTARG)"
|
|
;;
|
|
k)
|
|
KEY_FILE="$(readlink -f $OPTARG)"
|
|
;;
|
|
b)
|
|
CA_BUNDLE_FILE="$(readlink -f $OPTARG)"
|
|
;;
|
|
h)
|
|
usage
|
|
exit 0
|
|
;;
|
|
?)
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
EXIT_CODE=0
|
|
|
|
if [ -z "$CERT_FILE" -o -z "$KEY_FILE" -o -z "$CA_BUNDLE_FILE" ]; then
|
|
echo 'One of the required parameters is missing.' >&2
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
function error () {
|
|
echo -e "\n${RED}[FAIL]${RESET}\n"
|
|
CURRENT_EXIT_CODE=$1
|
|
EXIT_CODE=$((EXIT_CODE|CURRENT_EXIT_CODE))
|
|
echo -e $2 >&2
|
|
}
|
|
|
|
function success () {
|
|
echo -e "\n${GREEN}[OK]${RESET}\n"
|
|
}
|
|
|
|
function check-files-exist() {
|
|
if [[ ! -f "$CA_BUNDLE_FILE" ]] || [[ ! -f "$CERT_FILE" ]] || [[ ! -f "$KEY_FILE" ]] ; then
|
|
echo "One of '${CERT_FILE}', '${KEY_FILE}' or '${CA_BUNDLE_FILE}' not found" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
function check-server-cert-encoding () {
|
|
printf 'Checking server certificate encoding: '
|
|
openssl x509 -inform pem -in $CERT_FILE -noout -text &> /dev/null
|
|
if [[ $? == "0" ]]; then
|
|
success
|
|
else
|
|
openssl x509 -inform der -in $CERT_FILE -noout -text &> /dev/null
|
|
if [[ $? == "0" ]]; then
|
|
error 8 "The server certificate is in DER encoding, which is incompatible.\n\n"
|
|
printf "Run the following command to convert the certificate to PEM encoding,\n"
|
|
printf "then test it again.\n"
|
|
printf "# openssl x509 -in %s -outform pem -out %s.pem\n\n" $(basename $CERT_FILE) $(basename $CERT_FILE)
|
|
printf "When you run $(basename $0) again, use file\n"
|
|
printf "%s.pem for the server certificate.\n\n" $(basename $CERT_FILE)
|
|
else
|
|
error 9 "The encoding of the server certificate is unknown."
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function check-expiration () {
|
|
CERT_EXP=$(openssl x509 -noout -enddate -in $CERT_FILE | sed -e 's/notAfter=//' | awk '{$NF="";}1')
|
|
CA_EXP=$(openssl x509 -noout -enddate -in $CA_BUNDLE_FILE | sed -e 's/notAfter=//' | awk '{$NF="";}1')
|
|
DATE_TODAY=$(date -u +%Y%m%d%H%M%S)
|
|
CERT_DATE=$(date -d"${CERT_EXP}" +%Y%m%d%H%M%S)
|
|
CA_DATE=$(date -d"${CA_EXP}" +%Y%m%d%H%M%S)
|
|
printf "Checking expiration of certificate: "
|
|
if [[ $DATE_TODAY -gt $CERT_DATE ]]; then
|
|
error 6 "The certificate \"$CERT_FILE\" has already expired on: $CERT_EXP"
|
|
else
|
|
success
|
|
fi
|
|
printf "Checking expiration of CA bundle: "
|
|
if [[ $DATE_TODAY -gt $CA_DATE ]]; then
|
|
error 7 "The CA bundle \"$CA_BUNDLE_FILE\" has already expired on: $CA_EXP"
|
|
else
|
|
success
|
|
fi
|
|
}
|
|
|
|
function check-cert-ca-flag () {
|
|
printf "Checking if server certificate has CA:TRUE flag "
|
|
openssl x509 -in $CERT_FILE -noout -text | grep -q CA:TRUE
|
|
if [[ $? -ne 0 ]]; then
|
|
success
|
|
else
|
|
error 7 "The server certificate is marked as a CA and can not be used."
|
|
fi
|
|
}
|
|
|
|
function check-passphrase () {
|
|
printf "Checking for private key passphrase: "
|
|
CHECK=$(cat $KEY_FILE | grep ENCRYPTED)
|
|
if [[ $? == "0" ]]; then
|
|
error 2 "The $KEY_FILE contains a passphrase, remove the key's passphrase by doing:
|
|
\nmv $KEY_FILE $KEY_FILE.old
|
|
\nopenssl rsa -in $KEY_FILE.old -out $KEY_FILE"
|
|
exit 1
|
|
else
|
|
success
|
|
fi
|
|
}
|
|
|
|
function check-priv-key () {
|
|
printf "Checking to see if the private key matches the certificate: "
|
|
CERT_MOD=$(openssl x509 -noout -modulus -in $CERT_FILE)
|
|
KEY_MOD=$(openssl rsa -noout -modulus -in $KEY_FILE)
|
|
if [[ "$CERT_MOD" != "$KEY_MOD" ]]; then
|
|
error 2 "The $KEY_FILE does not match the $CERT_FILE"
|
|
else
|
|
success
|
|
fi
|
|
}
|
|
|
|
function check-ca-bundle () {
|
|
printf "Checking CA bundle against the certificate file: "
|
|
ERROR_PATTERN="error [0-9]+ at"
|
|
CHECK=$(openssl verify -CAfile $CA_BUNDLE_FILE -purpose sslserver -verbose $CERT_FILE 2>&1)
|
|
CHECK_STATUS=$?
|
|
|
|
if [[ $CHECK_STATUS != "0" || $CHECK =~ $ERROR_PATTERN ]]; then
|
|
error 4 "The $CA_BUNDLE_FILE does not verify the $CERT_FILE"
|
|
echo -e "${CHECK/OK/}\n"
|
|
else
|
|
success
|
|
fi
|
|
}
|
|
|
|
function check-ca-bundle-size () {
|
|
printf "Checking CA bundle size: "
|
|
CHECK=$(grep -c "^--*BEGIN" $CA_BUNDLE_FILE)
|
|
printf $CHECK
|
|
if [[ $CHECK -lt $CABUNDLE_MAX_ISSUERS ]]; then
|
|
success
|
|
else
|
|
CERRTISSUER=$(openssl x509 -noout -in $CERT_FILE -issuer 2>&1)
|
|
error 10 "The CA bundle counts $CHECK issuers. Please trim your CA bundle and include only the certs relevant to your cert file"
|
|
echo $CERTISSUER
|
|
echo
|
|
fi
|
|
}
|
|
|
|
function check-ca-bundle-trust-rules () {
|
|
printf "Checking if CA bundle has trust rules: "
|
|
CHECK=$(grep 'BEGIN TRUSTED CERTIFICATE' $CA_BUNDLE_FILE| wc -l)
|
|
printf $CHECK
|
|
if [[ $CHECK -lt 1 ]]; then
|
|
success
|
|
else
|
|
error 10 "The CA bundle contains $CHECK certificate(s) with trust rules. This may create problems for older systems to trust the bundle. Please, recreate the bundle using certificates without trust rules"
|
|
echo
|
|
fi
|
|
}
|
|
|
|
function check-cert-san () {
|
|
printf "Checking Subject Alt Name on certificate "
|
|
DNS_LIST=$(openssl x509 -noout -text -in $CERT_FILE | grep DNS:)
|
|
if [[ $? == "0" ]]; then
|
|
success
|
|
printf "Checking if any Subject Alt Name on certificate matches the Subject CN"
|
|
SUBJECT_CN=$(openssl x509 -in $CERT_FILE -noout -subject -nameopt multiline|grep commonName|cut -d '=' -f 2|tr -d ' ')
|
|
for DNS in ${DNS_LIST}
|
|
do
|
|
DNS_VALUE="$( echo ${DNS//DNS:/} | tr -d ',')"
|
|
if [ $SUBJECT_CN == $DNS_VALUE ]
|
|
then
|
|
success
|
|
return
|
|
fi
|
|
done
|
|
error
|
|
echo "The $CERT_FILE does not have a Subject Alt Name matching the Subject CN"
|
|
else
|
|
error
|
|
cat <<Explanation
|
|
$CERT_FILE does not contain a Subject Alt Name. Common Name is deprecated, use Subject Alt Name instead.
|
|
See: https://tools.ietf.org/html/rfc2818#section-3.1
|
|
|
|
Explanation
|
|
fi
|
|
}
|
|
|
|
function check-cert-usage-key-encipherment () {
|
|
printf "Checking Key Usage extension on certificate for Key Encipherment "
|
|
CHECK=$(openssl x509 -noout -text -in $CERT_FILE | grep -A1 'X509v3 Key Usage:' | grep 'Key Encipherment')
|
|
if [[ $? == "0" ]]; then
|
|
success
|
|
else
|
|
error 4 "The $CERT_FILE does not allow for the 'Key Encipherment' key usage."
|
|
fi
|
|
}
|
|
|
|
function check-shortname () {
|
|
printf "Checking for use of shortname as CN"
|
|
|
|
SUBJECT_CN=$(openssl x509 -in $CERT_FILE -noout -subject -nameopt multiline|grep commonName|cut -d '=' -f 2|tr -d ' ')
|
|
if [[ $SUBJECT_CN != *"."* ]]; then
|
|
error 1 "The $(basename $CERT_FILE) is using a shortname for Common Name (CN) and cannot be used with $PROJECT.\n"
|
|
fi
|
|
|
|
DNS_LIST=$(openssl x509 -noout -text -in $CERT_FILE | grep DNS:)
|
|
if [[ $? == "0" ]]; then
|
|
for DNS in ${DNS_LIST}
|
|
do
|
|
DNS_VALUE="$( echo ${DNS//DNS:/} | tr -d ',')"
|
|
|
|
if [[ $DNS_VALUE == *"."* ]]; then
|
|
success
|
|
return
|
|
fi
|
|
done
|
|
error 1 "The $(basename $CERT_FILE) is using only shortnames for Subject Alt Name and cannot be used with $PROJECT.\n"
|
|
fi
|
|
}
|
|
|
|
check-files-exist
|
|
check-server-cert-encoding
|
|
check-expiration
|
|
check-cert-ca-flag
|
|
check-passphrase
|
|
check-priv-key
|
|
check-ca-bundle
|
|
check-ca-bundle-size
|
|
check-ca-bundle-trust-rules
|
|
check-cert-san
|
|
check-cert-usage-key-encipherment
|
|
check-shortname
|
|
|
|
if [[ $EXIT_CODE == "0" ]] && ([[ $TARGET == ${SERVER_TARGET} ]] || [[ -z "$TARGET" ]]) ; then
|
|
echo -e "${GREEN}Validation succeeded${RESET}\n"
|
|
cat <<EOF
|
|
|
|
To install the ${PROJECT} server with the custom certificates, run:
|
|
|
|
${INSTALLER} --scenario ${SERVER_SCENARIO} \\
|
|
--certs-server-cert "$(readlink -f $CERT_FILE)" \\
|
|
--certs-server-key "$(readlink -f $KEY_FILE)" \\
|
|
--certs-server-ca-cert "$(readlink -f $CA_BUNDLE_FILE)"
|
|
|
|
To update the certificates on a currently running ${PROJECT} installation, run:
|
|
|
|
${INSTALLER} --scenario ${SERVER_SCENARIO} \\
|
|
--certs-server-cert "$(readlink -f $CERT_FILE)" \\
|
|
--certs-server-key "$(readlink -f $KEY_FILE)" \\
|
|
--certs-server-ca-cert "$(readlink -f $CA_BUNDLE_FILE)" \\
|
|
--certs-update-server --certs-update-server-ca
|
|
|
|
To use them inside a NEW \$${PROXY_VAR}, rerun this command with -t ${PROXY_TARGET}
|
|
EOF
|
|
elif [[ $EXIT_CODE == "0" ]]; then
|
|
echo -e "${GREEN}Validation succeeded${RESET}\n"
|
|
cat <<EOF
|
|
|
|
To use them inside a NEW \$${PROXY_VAR}, run this command:
|
|
|
|
${CERTS_TOOL} --foreman-proxy-fqdn "\$${PROXY_VAR}" \\
|
|
--certs-tar "~/\$${PROXY_VAR}-certs.tar" \\
|
|
--server-cert "$(readlink -f $CERT_FILE)" \\
|
|
--server-key "$(readlink -f $KEY_FILE)" \\
|
|
--server-ca-cert "$(readlink -f $CA_BUNDLE_FILE)"
|
|
|
|
To use them inside an EXISTING \$${PROXY_VAR}, run this command INSTEAD:
|
|
|
|
${CERTS_TOOL} --foreman-proxy-fqdn "\$${PROXY_VAR}" \\
|
|
--certs-tar "~/\$${PROXY_VAR}-certs.tar" \\
|
|
--server-cert "$(readlink -f $CERT_FILE)" \\
|
|
--server-key "$(readlink -f $KEY_FILE)" \\
|
|
--server-ca-cert "$(readlink -f $CA_BUNDLE_FILE)" \\
|
|
--certs-update-server
|
|
EOF
|
|
else
|
|
exit $EXIT_CODE
|
|
fi
|