982 lines
44 KiB
Python
982 lines
44 KiB
Python
# Copyright (c) 2024-2025 Broadcom. All Rights Reserved.
|
|
# The term "Broadcom" refers to Broadcom Inc.
|
|
# and/or its subsidiaries.
|
|
|
|
import logging
|
|
import re
|
|
import OpenSSL
|
|
import glob
|
|
|
|
from enum import Enum
|
|
|
|
from lib import vcdb
|
|
from lib import vecs
|
|
from lib import vmdir
|
|
from lib.certificate_utils import (
|
|
get_x509_certificate, get_certificate_expiry_in_days, get_certificate_extensions,
|
|
get_certificate_fingerprint, build_certification_path, split_certificates_from_pem,
|
|
get_subject_keyid, get_certificate_from_host, build_pem_certificate,
|
|
is_ca_certificate, get_subject_hash, get_authority_keyid, get_subject_and_issuer_dn,
|
|
get_certificate_name
|
|
)
|
|
from lib.console import (
|
|
print_task, print_task_status, print_text_error, print_task_status_error,
|
|
print_task_status_warning, print_header, ColorKey, print_text
|
|
)
|
|
from lib.constants import (VMCA_CERT_FILE_PATH, RBD_CERT_FILE_PATH, READ, WRITE, VMWARE_DEPOT_HOST, VMWARE_DEPOT_CERT_ISSUER)
|
|
from lib.environment import Environment
|
|
from lib.exceptions import MenuExitException
|
|
from lib.host_utils import (
|
|
is_file_exists, get_file_contents, get_vc_version, VcVersion,
|
|
get_ip_address
|
|
)
|
|
from lib.menu import Menu
|
|
from lib.services import is_service_running
|
|
from operation.common import get_vcenter_extensions, get_vcenter_extension_expected_thumbprints
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class CertificateStatus(Enum):
|
|
CERT_STATUS_EXPIRES_SOON = 'One or more certificates are expiring within 30 days'
|
|
CERT_STATUS_MISSING_PNID = 'One or more certificates are missing the PNID >>DETAILS<< from the SAN entry'
|
|
CERT_STATUS_MISSING_SAN = 'One or more certificates do not have any Subject Alternative Name values'
|
|
CERT_STATUS_KEY_USAGE = 'One or more certificates do not have the recommended\nKey Usage values'
|
|
CERT_STATUS_EXPIRED = 'One or more certificates are expired'
|
|
CERT_STATUS_NON_CA = 'One or more certificates are not CA certificates'
|
|
CERT_STATUS_BAD_ALIAS = \
|
|
'One or more entries in the TRUSTED_ROOTS store have an alias that is not the SHA1 thumbprint'
|
|
CERT_STATUS_SHA1_SIGNING = 'One or more certificates are signed using the SHA-1 algorithm'
|
|
CERT_STATUS_MISSING = 'One or more certificates are missing'
|
|
CERT_STATUS_MISSING_VMDIR = \
|
|
'One or more CA certificates are missing from VMware Directory'
|
|
CERT_STATUS_MISMATCH_SERVICE_PRINCIPAL = \
|
|
'One or more Solution User certificates does not match\nthe Service Principal certificate in VMware Directory'
|
|
CERT_STATUS_TOO_MANY_CRLS = 'The number of CRLs in VECS may be preventing some services from starting'
|
|
CERT_STATUS_MISSING_CA = \
|
|
'One or more certificates do not have all of the CA\n' \
|
|
'certificates in its signing chain in VMware Directory'
|
|
CERT_STATUS_EXPIRED_EMBEDDED_CA = \
|
|
'One or more certificates has a CA certificate embedded\n' \
|
|
'in its chain that is expired'
|
|
CERT_STATUS_STORE_MISSING = 'One or more VECS stores are missing'
|
|
CERT_STATUS_STORE_PERMISSIONS = 'One or more VECS stores are missing permissions'
|
|
CERT_STATUS_SERVICE_PRINCIPAL_MISSING = \
|
|
'One or more Service Principal entries are missing\nfrom VMware Directory'
|
|
CERT_STATUS_VMCA_EMPTY_CONFIG = \
|
|
'There are one or more vpxd.certmgmt.certs.cn.* settings with empty values\n' \
|
|
'This can cause issues pushing VMCA-signed certificates to ESXi hosts'
|
|
CERT_STATUS_VMCA_MODE = \
|
|
"The certificate management mode is set to 'thumbprint'\n" \
|
|
"This is not recommended, and should be set to 'vmca' or 'custom'"
|
|
CERT_STATUS_CLIENT_CA_LIST_FILE_MISSING = \
|
|
'The Smart Card issuing CA filter file does not exist at the following location:\n>>DETAILS<<'
|
|
CERT_STATUS_CLIENT_CA_LIST_FILE_EMPTY = \
|
|
'The Smart Card issuing CA filter file at the following location is empty:\n>>DETAILS<<'
|
|
CERT_STATUS_STS_VECS_CONFIG = \
|
|
'The STS server is configured to use a VECS store other than\nthe MACHINE_SSL_CERT store'
|
|
CERT_STATUS_STS_CONNECTION_STRINGS_NUMBER = \
|
|
'There are multiple STS ConnectionStrings values found in VMware Directory'
|
|
CERT_STATUS_STS_CONNECTION_STRINGS_HOSTNAME = \
|
|
'The STS ConnectionStrings value is not set properly for an SSO\ndomain with multiple Domain Controllers'
|
|
CERT_STATUS_UNSUPPORTED_SIGNATURE_ALGORITHM = \
|
|
'One or more certificates is using an unsupported\nsignature algorithm'
|
|
CERT_STATUS_CA_MISSING_SKID = 'One or more CA certificates is missing the Subject Key ID extension'
|
|
CERT_STATUS_ROGUE_CA = \
|
|
'One or more certificates are invalid because it or a signing CA\n' \
|
|
'extends beyond the pathlen restrictions of a parent CA'
|
|
CERT_STATUS_DUPLICATE_CA = \
|
|
'Two or more CA certificates in VMWare Directory or VECS have the\n' \
|
|
'same Subject string, which can cause issues with certificate\n' \
|
|
'validation'
|
|
TRUST_ANCHORS_MISMATCH = "One or more vCenter/PSC nodes have mismatched SSL trust anchors"
|
|
TRUST_ANCHORS_UNKNOWN = \
|
|
'The Machine SSL certificate could not be obtained from\n' \
|
|
'the following nodes to check SSL trust anchors:'
|
|
TRUST_ANCHORS_CHECK_URI_MISMATCH = \
|
|
'One or more vCenter/PSC nodes have mismatched SSL trust anchors and\n' \
|
|
'have Lookup Service registrations using the IP address instead\n' \
|
|
'of the PNID in the endpoint URIs. These can be fixed with the\n' \
|
|
'lsdoctor utility: https://knowledge.broadcom.com/external/article/320837/using-the-lsdoctor-tool.html'
|
|
TRUST_ANCHORS_CHECK_URI_IP = \
|
|
'One or more vCenter/PSC nodes have Lookup Service registrations\n' \
|
|
'using the IP address instead of the PNID in the endpoint URIs.\n' \
|
|
'These can be fixed with the lsdoctor utility:\n' \
|
|
'https://knowledge.broadcom.com/external/article/320837/using-the-lsdoctor-tool.html'
|
|
TRUST_ANCHORS_CHECK_URI_OTHER = \
|
|
'One or more vCenters have no Lookup Service registration endpoints\n' \
|
|
'using the current hostname or IP address. There could be registrations\n' \
|
|
'for these vCenters using a different hostname or IP address.\n' \
|
|
'These can be fixed with the lsdoctor utility: \n' \
|
|
'https://knowledge.broadcom.com/external/article/320837/using-the-lsdoctor-tool.html'
|
|
TRUST_ANCHORS_CHECK_PENDING = \
|
|
'The current Machine SSL certificate is not being served on port\n' \
|
|
'443 due to a pending service restart\n'
|
|
|
|
|
|
def check_certificate_dummy(**_):
|
|
print_text_error('=== Unsupported check certificate operation! ===')
|
|
|
|
|
|
def get_check_options(store, is_solution_user):
|
|
"""
|
|
Get check options based on VECS store name or solution user
|
|
|
|
:param store: VECS store name
|
|
:param is_solution_user: whether the certificate is solution user certificate
|
|
:return: list of check option
|
|
"""
|
|
if store == 'MACHINE_SSL_CERT':
|
|
options = ['CHECK_PNID', 'CHECK_KU', 'CHECK_SAN', 'CHECK_CA_CHAIN', 'CHECK_EMBEDDED_CHAIN', 'CHECK_ROGUE_CA']
|
|
elif store == 'SMS':
|
|
options = []
|
|
else:
|
|
options = ['CHECK_KU', 'CHECK_SAN', 'CHECK_CA_CHAIN', 'CHECK_EMBEDDED_CHAIN', 'CHECK_ROGUE_CA']
|
|
if is_solution_user:
|
|
options.append('CHECK_SERVICE_PRINCIPAL')
|
|
if store in ['wcp', 'wcpsvc']:
|
|
options.remove('CHECK_SAN')
|
|
return options
|
|
|
|
|
|
def check_vecs_certificate(store, alias, is_solution_user=False):
|
|
"""
|
|
Check specific certificate in VECS
|
|
|
|
:param store: VECS store name
|
|
:param alias: certificate alias
|
|
:param is_solution_user: whether the certificate is solution user certificate
|
|
"""
|
|
logger.info("Checking VECS certificate: store {}, alias {}".format(store, alias))
|
|
options = get_check_options(store, is_solution_user)
|
|
logger.info("Check options: {}".format(options))
|
|
aliases = vecs.get_certificate_aliases(store)
|
|
if alias not in aliases:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_MISSING)
|
|
print_task_status_error('NOT FOUND')
|
|
logger.error("Certificate for alias {} was not found in store {}".format(alias, store))
|
|
return
|
|
|
|
pem_cert = vecs.get_certificate(store, alias)
|
|
if not pem_cert:
|
|
print_task_status_error('PROBLEM')
|
|
logger.error("Failed to obtain certificate for alias {} in store {}".format(alias, store))
|
|
return
|
|
|
|
cert = get_x509_certificate(pem_cert)
|
|
days_left = get_certificate_expiry_in_days(cert)
|
|
cert_desc = "Certificate for alias {} in store {}".format(alias, store)
|
|
if days_left < 0:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_EXPIRED)
|
|
print_task_status_warning('EXPIRED')
|
|
logger.warning("{} is expired".format(cert_desc))
|
|
return
|
|
elif days_left < 30:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_EXPIRES_SOON)
|
|
print_task_status_warning("{} DAYS".format(days_left))
|
|
logger.info("{} will expire in {} days".format(cert_desc, days_left))
|
|
return
|
|
|
|
extensions = get_certificate_extensions(cert)
|
|
env = Environment.get_environment()
|
|
if 'CHECK_PNID' in options:
|
|
pnid = env.get_value('PNID')
|
|
san = extensions.get('subjectAltName')
|
|
if san is None or pnid not in san:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_MISSING_PNID, pnid)
|
|
print_task_status_warning('NO PNID')
|
|
logger.warning("{} does not have the PNID {} in the Subject Alternative Name field".format(cert_desc, pnid))
|
|
return
|
|
|
|
if 'CHECK_KU' in options:
|
|
if not check_key_usage(cert, "{}:{}".format(store, alias)):
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_KEY_USAGE)
|
|
print_task_status_warning('KEY USAGE')
|
|
logger.warning("{} does not have the expected Key Usage values".format(cert_desc))
|
|
return
|
|
|
|
if 'CHECK_SAN' in options:
|
|
san = extensions.get('subjectAltName')
|
|
if not san:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_MISSING_SAN)
|
|
print_task_status_warning('NO SAN')
|
|
logger.warning("{} has no values in Subject Alternative Name field".format(cert_desc))
|
|
return
|
|
|
|
if 'CHECK_SERVICE_PRINCIPAL' in options:
|
|
fingerprint1 = get_certificate_fingerprint(cert, 'sha1')
|
|
solution_user_cert = vmdir.get_solution_user_certificate(store)
|
|
fingerprint2 = get_certificate_fingerprint(get_x509_certificate(solution_user_cert), 'sha1')
|
|
if fingerprint1 != fingerprint2:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_MISMATCH_SERVICE_PRINCIPAL)
|
|
print_task_status_warning('MISMATCH')
|
|
logger.warning("{} does not match the certificate for the corresponding Service Principal "
|
|
"in VMware Directory".format(cert_desc))
|
|
return
|
|
|
|
if 'CHECK_CA_CHAIN' in options:
|
|
if check_missing_ca(pem_cert):
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_MISSING_CA)
|
|
print_task_status_warning('MISSING CA')
|
|
logger.warning("{} is missing one of the CA certificates in the certificate chain".format(cert_desc))
|
|
return
|
|
|
|
if 'CHECK_EMBEDDED_CHAIN' in options:
|
|
pem_certs = split_certificates_from_pem(pem_cert)
|
|
pem_certs = pem_certs[1:]
|
|
for pem in pem_certs:
|
|
x509_cert = get_x509_certificate(pem)
|
|
sha1_fingerprint = get_certificate_fingerprint(x509_cert)
|
|
logger.info("Checking embedded CA certificate with SHA1 fingerprint {}".format(sha1_fingerprint))
|
|
days_left = get_certificate_expiry_in_days(x509_cert)
|
|
if days_left < 0:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_EXPIRED_EMBEDDED_CA)
|
|
print_task_status_warning('EMBEDDED CA')
|
|
logger.warning("The embedded CA certificate is expired:\n{}".format(pem))
|
|
return
|
|
|
|
if 'CHECK_ROGUE_CA' in options:
|
|
is_rogue_cert, _ = check_rogue_ca(pem_cert)
|
|
if is_rogue_cert:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_ROGUE_CA)
|
|
print_task_status_warning('ROGUE')
|
|
logger.warning("The certificate is invalid because of a CA that extends beyond a parent CA path length restriction:\n")
|
|
return
|
|
|
|
if not check_signature_algorithm(cert):
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_UNSUPPORTED_SIGNATURE_ALGORITHM)
|
|
print_task_status_warning('ALGORITHM')
|
|
logger.warning("{} is signed with unsupported signature algorithm".format(cert_desc))
|
|
else:
|
|
print_task_status('VALID')
|
|
|
|
|
|
def check_signature_algorithm(x509_cert):
|
|
"""
|
|
Check if the signature algorithm is supported
|
|
|
|
:param x509_cert: X509Certificate object
|
|
:return: True if the signature algorithm is supported
|
|
"""
|
|
unsupported_signature_algs = ['md2WithRSAEncryption', 'md5WithRSAEncryption', 'RSASSA-PSS', 'dsaWithSHA1',
|
|
'ecdsa_with_SHA1', 'sha1WithRSAEncryption']
|
|
sign_alg = x509_cert.get_signature_algorithm().decode('utf-8')
|
|
logger.info("Checking certificate signature algorithm {} against unsupported signature algorithms {}"
|
|
.format(sign_alg, unsupported_signature_algs))
|
|
return False if sign_alg in unsupported_signature_algs else True
|
|
|
|
|
|
def check_key_usage(x509_cert, cert_desc):
|
|
"""
|
|
Check certificate keyUsage extension and validate that all keyUsage are in the supported list
|
|
|
|
:param x509_cert: X509Certificate object
|
|
:param cert_desc: Certificate description (for logging)
|
|
:return: True if all keyUsages are supported
|
|
"""
|
|
supported_kus = ['Digital Signature', 'Key Encipherment', 'Key Agreement', 'Data Encipherment',
|
|
'Non Repudiation']
|
|
logger.info("Checking Key Usage for cert {} among supported values of: {}".format(cert_desc, supported_kus))
|
|
extensions = get_certificate_extensions(x509_cert)
|
|
kus = extensions.get('keyUsage', '').split(', ')
|
|
for ku in kus:
|
|
if ku not in supported_kus:
|
|
logger.warning("Found unsupported Key Usage value: {}".format(ku))
|
|
return False
|
|
return True
|
|
|
|
|
|
def check_certificate_basic(cert):
|
|
"""
|
|
Some basic checks on certificate
|
|
:param cert: X509Certificate
|
|
"""
|
|
x509_cert = get_x509_certificate(cert)
|
|
days_left = get_certificate_expiry_in_days(x509_cert)
|
|
if days_left < 0:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_EXPIRED)
|
|
print_task_status_warning('EXPIRED')
|
|
logger.warning("Certificate is expired")
|
|
return
|
|
elif days_left < 30:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_EXPIRES_SOON)
|
|
print_task_status_warning("{} DAYS".format(days_left))
|
|
logger.warning("Certificate expires in {} days".format(days_left))
|
|
return
|
|
|
|
if not check_signature_algorithm(x509_cert):
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_UNSUPPORTED_SIGNATURE_ALGORITHM)
|
|
print_task_status_warning('ALGORITHM')
|
|
logger.warning("Certificate is signed with unsupported signature algorithm")
|
|
return
|
|
|
|
if is_ca_certificate(x509_cert):
|
|
skid = get_subject_keyid(x509_cert)
|
|
if not skid:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_CA_MISSING_SKID)
|
|
print_task_status_warning('NO SKID')
|
|
return
|
|
|
|
print_task_status('VALID')
|
|
|
|
|
|
def check_file_system_certificate(file):
|
|
"""
|
|
Check certificate file stored on file system
|
|
|
|
:param file: Path of the certificate file
|
|
:return:
|
|
"""
|
|
if not is_file_exists(file):
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_MISSING)
|
|
print_task_status_warning('NOT FOUND')
|
|
logger.error("Certificate at {} could not be found".format(file))
|
|
return
|
|
|
|
logger.info("Checking certificate at {}".format(file))
|
|
pem_cert = get_file_contents(file)
|
|
check_certificate_basic(pem_cert)
|
|
|
|
|
|
def check_certificate_status():
|
|
"""
|
|
Entry point for check certificate status operation
|
|
"""
|
|
env = Environment.get_environment()
|
|
print_task('Checking Machine SSL certificate')
|
|
check_vecs_certificate('MACHINE_SSL_CERT', '__MACHINE_CERT')
|
|
|
|
aliases = vecs.get_certificate_aliases('MACHINE_SSL_CERT')
|
|
if '__MACHINE_CSR' in aliases:
|
|
print_task('Checking Machine SSL CSR')
|
|
check_vecs_certificate('MACHINE_SSL_CERT', '__MACHINE_CSR')
|
|
|
|
print('Checking Solution User certificates:')
|
|
solution_users = env.get_value('SOLUTION_USERS')
|
|
for sol_user in solution_users:
|
|
print_task(" {}".format(sol_user))
|
|
check_vecs_certificate(sol_user, sol_user, is_solution_user=True)
|
|
|
|
vc_version = get_vc_version()
|
|
|
|
print_task('Checking SMS self-signed certificate')
|
|
check_vecs_certificate('SMS', 'sms_self_signed')
|
|
if vc_version >= VcVersion.V8:
|
|
print_task('Checking SMS VMCA-signed certificate')
|
|
check_vecs_certificate('SMS', 'sps-extension')
|
|
|
|
print_task('Checking data-encipherment certificate')
|
|
check_vecs_certificate('data-encipherment', 'data-encipherment')
|
|
|
|
print_task('Checking Authentication Proxy certificate')
|
|
check_file_system_certificate(VMCA_CERT_FILE_PATH)
|
|
|
|
print_task('Checking Auto Deploy CA certificate')
|
|
check_file_system_certificate(RBD_CERT_FILE_PATH)
|
|
|
|
cert_file = '/usr/lib/vmware-vmdir/share/config/vmdircert.pem'
|
|
if is_file_exists(cert_file):
|
|
print_task('Checking VMDir certificate')
|
|
check_file_system_certificate(cert_file)
|
|
|
|
store_list = vecs.get_store_list()
|
|
if 'BACKUP_STORE' in store_list:
|
|
print('Checking BACKUP_STORE entries:')
|
|
for alias in vecs.get_certificate_aliases('BACKUP_STORE'):
|
|
print_task(" {}".format(alias))
|
|
check_vecs_certificate('BACKUP_STORE', alias)
|
|
|
|
if 'BACKUP_STORE_H5C' in store_list:
|
|
print_task('Checking BACKUP_STORE_H5C entries:')
|
|
for alias in vecs.get_certificate_aliases('BACKUP_STORE_H5C'):
|
|
print_task(" {}".format(alias))
|
|
check_vecs_certificate('BACKUP_STORE_H5C', alias)
|
|
|
|
if 'STS_INTERNAL_SSL_CERT' in store_list:
|
|
print_task('Checking legacy Lookup Service certificate')
|
|
check_vecs_certificate('STS_INTERNAL_SSL_CERT', '__MACHINE_CERT')
|
|
|
|
print_task('Checking VMCA certificate')
|
|
check_file_system_certificate(VMCA_CERT_FILE_PATH)
|
|
|
|
|
|
def check_sts_tenant_certificates():
|
|
"""
|
|
Entry point for STS Tenant certificates check
|
|
"""
|
|
certs_map = vmdir.get_sts_tenant_certificates()
|
|
for tenant in certs_map.keys():
|
|
print("Checking {}:".format(tenant))
|
|
for pem_cert in certs_map[tenant]:
|
|
x509_cert = get_x509_certificate(pem_cert)
|
|
check_sts_tenant_certificate(x509_cert, tenant)
|
|
|
|
|
|
def check_sts_tenant_certificate(x509_cert, tenant):
|
|
"""
|
|
Check specific STS tenant certificate
|
|
|
|
:param x509_cert: X509Certificate object
|
|
:param tenant: Tenant name (for output message)
|
|
"""
|
|
is_ca = is_ca_certificate(x509_cert)
|
|
print_task(" {} {} certificate".format(tenant, 'CA' if is_ca else 'signing'))
|
|
|
|
cert_desc = "STS tenant certificate {}".format(tenant)
|
|
days_left = get_certificate_expiry_in_days(x509_cert)
|
|
if days_left < 0:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_EXPIRED)
|
|
print_task_status_warning('EXPIRED')
|
|
logger.warning("{} is expired".format(cert_desc))
|
|
return
|
|
if is_ca:
|
|
skid = get_subject_keyid(x509_cert, remove_colons=True)
|
|
if not vmdir.get_ca_certificate(skid):
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_MISSING_VMDIR)
|
|
print_task_status_warning('MISSING')
|
|
logger.warning("{} is missing from VMDir".format(cert_desc))
|
|
return
|
|
elif not check_key_usage(x509_cert, 'STS Tenant'):
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_KEY_USAGE)
|
|
print_task_status_warning('KEY USAGE')
|
|
return
|
|
if not check_signature_algorithm(x509_cert):
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_UNSUPPORTED_SIGNATURE_ALGORITHM)
|
|
print_task_status_warning('ALGORITHM')
|
|
logger.warning("{}is signed with unsupported signature algorithm".format(cert_desc))
|
|
return
|
|
|
|
if days_left < 30:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_EXPIRES_SOON)
|
|
print_task_status_warning("{} DAYS".format(days_left))
|
|
logger.warning("{} expires in {} days".format(cert_desc, days_left))
|
|
else:
|
|
print_task_status('VALID')
|
|
|
|
|
|
def check_ca_certificates_in_vmdir():
|
|
"""
|
|
Entry point for CA certificates check in VMDir
|
|
"""
|
|
logger.info('Checking CA certificates in VMDir')
|
|
subject_keyids = vmdir.get_all_ca_subject_keyids()
|
|
for subject_keyid in subject_keyids:
|
|
logger.info("Checking certificate with CN(id) {}".format(subject_keyid))
|
|
pem_cert = vmdir.get_ca_certificate(subject_keyid)
|
|
print_task(subject_keyid)
|
|
cert = get_x509_certificate(pem_cert)
|
|
extensions = get_certificate_extensions(cert)
|
|
days_left = get_certificate_expiry_in_days(cert)
|
|
cert_subject_keyid = get_subject_keyid(cert)
|
|
basic_constraints = extensions.get('basicConstraints')
|
|
cert_desc = "Certificate with CN(id) {}".format(subject_keyid)
|
|
is_rogue_ca, _ = check_rogue_ca(pem_cert)
|
|
if days_left < 0:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_EXPIRED)
|
|
print_task_status_warning('EXPIRED')
|
|
logger.warning("{} is expired".format(cert_desc))
|
|
elif basic_constraints is None or 'CA:TRUE' not in basic_constraints:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_NON_CA)
|
|
print_task_status_warning('NON-CA')
|
|
logger.warning("{} is not a CA certificate".format(cert_desc))
|
|
elif is_rogue_ca:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_ROGUE_CA)
|
|
print_task_status_warning('ROGUE')
|
|
logger.warning("{} violates the path length restriction of a parent CA certificate".format(cert_desc))
|
|
elif check_duplicate_ca(pem_cert):
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_DUPLICATE_CA)
|
|
print_task_status_warning('DUPLICATE')
|
|
logger.warning("{} has a duplicate Subject string with one or more CA certificates".format(cert_desc))
|
|
elif not cert_subject_keyid:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_CA_MISSING_SKID)
|
|
print_task_status_warning('NO SKID')
|
|
logger.warning("{} does not have Subject Key Id".format(cert_desc))
|
|
elif days_left < 30:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_EXPIRES_SOON)
|
|
print_task_status_warning("{} DAYS".format(days_left))
|
|
logger.warning("{} expires in {} days".format(cert_desc, days_left))
|
|
else:
|
|
print_task_status('VALID')
|
|
|
|
|
|
def check_ca_certificates_in_vecs():
|
|
"""
|
|
Entry point for CA certificates check in VECS
|
|
"""
|
|
logger.info('Checking CA certificates in VECS')
|
|
pem_certs, aliases = vecs.get_all_ca_certificates()
|
|
for pem_cert, alias in zip(pem_certs, aliases):
|
|
logger.info("Checking certificate with alias {}".format(alias))
|
|
print_task(alias)
|
|
cert = get_x509_certificate(pem_cert)
|
|
basic_constraints = get_certificate_extensions(cert).get('basicConstraints')
|
|
days_left = get_certificate_expiry_in_days(cert)
|
|
cert_desc = "Certificate with alias {}".format(alias)
|
|
is_rogue_ca, _ = check_rogue_ca(pem_cert)
|
|
if days_left < 0:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_EXPIRED)
|
|
print_task_status_warning('EXPIRED')
|
|
logger.warning("{} is expired".format(cert_desc))
|
|
elif basic_constraints is None or 'CA:TRUE' not in basic_constraints:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_NON_CA)
|
|
print_task_status_warning('NON-CA')
|
|
logger.warning("{} is not a CA certificate".format(cert_desc))
|
|
elif is_rogue_ca:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_ROGUE_CA)
|
|
print_task_status_warning('ROGUE')
|
|
logger.warning("{} violates the path length restriction of a parent CA certificate".format(cert_desc))
|
|
elif check_duplicate_ca(pem_cert):
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_DUPLICATE_CA)
|
|
print_task_status_warning('DUPLICATE')
|
|
logger.warning("{} has a duplicate Subject string with one or more CA certificates".format(cert_desc))
|
|
elif alias != get_certificate_fingerprint(cert, remove_colons=True).lower():
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_BAD_ALIAS)
|
|
print_task_status_warning('BAD ALIAS')
|
|
logger.warning("{} is registered using a bad alias".format(cert_desc))
|
|
elif days_left < 30:
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_EXPIRES_SOON)
|
|
print_task_status_warning("{} DAYS".format(days_left))
|
|
logger.warning("{} expires in {} days".format(cert_desc, days_left))
|
|
else:
|
|
print_task_status('VALID')
|
|
|
|
|
|
def check_service_principals():
|
|
"""
|
|
Entry point for service principals check
|
|
"""
|
|
logger.info('Checking service principals in VMware Directory')
|
|
service_principals = vmdir.get_service_principals()
|
|
if not service_principals:
|
|
print_task('Listing SSO Service Principals')
|
|
print_task_status_warning('FAILED')
|
|
logger.error('Could not get list of Service Principal entries from VMware Directory')
|
|
return
|
|
|
|
env = Environment.get_environment()
|
|
machine_id = env.get_value('MACHINE_ID')
|
|
solution_users = env.get_value('SOLUTION_USERS')
|
|
print("Node {}:".format(machine_id))
|
|
for solution_user in solution_users:
|
|
print_task(" {}".format(solution_user))
|
|
if "{}-{}".format(solution_user, machine_id) in service_principals:
|
|
print_task_status('PRESENT')
|
|
else:
|
|
print_task_status_warning('MISSING')
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_SERVICE_PRINCIPAL_MISSING)
|
|
logger.warning("Missing service principal {} in VMware Directory".format(solution_user))
|
|
|
|
|
|
def check_crls():
|
|
"""
|
|
Check Certificate Revocation List in VECS
|
|
"""
|
|
logger.info("Checking the number of CRLS in VECS")
|
|
num_entries = len(vecs.get_certificate_aliases('TRUSTED_ROOT_CRLS'))
|
|
print_task('Number of CRLs in VECS')
|
|
if num_entries < 30:
|
|
print_task_status(num_entries)
|
|
elif num_entries < 100:
|
|
print_task_status_warning(num_entries)
|
|
else:
|
|
print_task_status_error(num_entries)
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_TOO_MANY_CRLS)
|
|
logger.info("Number of CRLs in VECS: {}".format(num_entries))
|
|
|
|
|
|
def get_ids_domain_and_certificates(identity_sources, source_type):
|
|
result = []
|
|
for source in identity_sources:
|
|
if source['type'] == source_type:
|
|
result.append((source['domain_name'], source['certificates']))
|
|
return result
|
|
|
|
|
|
def get_ids_domain_and_certificates_by_domain(identity_sources, source_type, source_domain):
|
|
result = []
|
|
for source in identity_sources:
|
|
if source['type'] == source_type and source['domain_name'] == source_domain:
|
|
result.append((source['domain_name'], source['certificates']))
|
|
return result
|
|
|
|
|
|
def check_identity_source_certificates():
|
|
"""
|
|
Entry point for checking identity source certificates
|
|
"""
|
|
logger.info('Checking identity source certificates')
|
|
|
|
env = Environment.get_environment()
|
|
is_cac_configured = env.get_value('CAC_CONFIGURED')
|
|
|
|
if is_cac_configured:
|
|
check_smart_card_filter_file_certs()
|
|
check_smart_card_vmdir_certs()
|
|
|
|
identity_sources = vmdir.get_identity_sources()
|
|
|
|
# OpenLDAP
|
|
domain_and_certs = get_ids_domain_and_certificates(identity_sources, 'OpenLDAP')
|
|
if domain_and_certs:
|
|
print_header('Checking OpenLDAP LDAPS certificates')
|
|
for domain_name, certificates in domain_and_certs:
|
|
print("Domain: {}".format(domain_name))
|
|
for index, cert in enumerate(certificates, 1):
|
|
print_task(" Certificate {}".format(index))
|
|
check_certificate_basic(cert)
|
|
|
|
# AD over LDAP
|
|
domain_and_certs = get_ids_domain_and_certificates(identity_sources, 'AD over LDAP')
|
|
if domain_and_certs:
|
|
print_header('Checking AD over LDAPS certificates')
|
|
for domain_name, certificates in domain_and_certs:
|
|
print("Domain: {}".format(domain_name))
|
|
for index, cert in enumerate(certificates, 1):
|
|
print_task(" Certificate {}".format(index))
|
|
check_certificate_basic(cert)
|
|
|
|
# ADFS
|
|
domain_and_certs = get_ids_domain_and_certificates(identity_sources, 'ADFS')
|
|
if domain_and_certs:
|
|
print_header('Checking ADFS certificates')
|
|
for _, certificates in domain_and_certs:
|
|
for index, cert in enumerate(certificates, 1):
|
|
print_task("Certificate {}".format(index))
|
|
check_certificate_basic(cert)
|
|
|
|
|
|
def check_smart_card_filter_file_certs():
|
|
print_header('Check Smart Card Issuing CA Filter File')
|
|
print_task('Check CA Filter File')
|
|
|
|
env = Environment.get_environment()
|
|
cac_filter_file = env.get_value('SMART_CARD_FILTER_FILE')
|
|
|
|
if not is_file_exists(cac_filter_file):
|
|
print_task_status_warning('MISSING')
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_CLIENT_CA_LIST_FILE_MISSING, cac_filter_file)
|
|
else:
|
|
pem = get_file_contents(cac_filter_file)
|
|
filter_certs = split_certificates_from_pem(pem)
|
|
|
|
if not filter_certs:
|
|
print_task_status_warning('EMPTY')
|
|
add_certificate_status(CertificateStatus.CERT_STATUS_CLIENT_CA_LIST_FILE_EMPTY, cac_filter_file)
|
|
return
|
|
|
|
print_task_status('OK')
|
|
for index, cert in enumerate(filter_certs, 1):
|
|
print_task("Certificate {}".format(index))
|
|
check_certificate_basic(cert)
|
|
|
|
|
|
def check_smart_card_vmdir_certs():
|
|
print_header('Check VMDir Smart Card Issuing CA Certificates')
|
|
cac_ca_certs = vmdir.get_smart_card_issuing_ca_certs()
|
|
for index, cert in enumerate(cac_ca_certs, 1):
|
|
print_task("Certificate {}".format(index))
|
|
check_certificate_basic(cert)
|
|
|
|
|
|
def check_ssl_trust_anchors():
|
|
"""
|
|
Check if trust anchor certificates are match to server SSL certificate by
|
|
calculating the certificate thumbprints
|
|
|
|
Note: This check seems not correct. The check should be a certificate chain
|
|
validation using the trust anchor, not just leaf certificate thumbprint
|
|
comparison.
|
|
"""
|
|
logger.info("Checking SSL trust anchors")
|
|
env = Environment.get_environment()
|
|
hostname = env.get_value('HOSTNAME')
|
|
sso_domain_nodes = vmdir.get_sso_domain_nodes()
|
|
is_mismatch = False
|
|
is_using_ip_address = False
|
|
is_pending = False
|
|
for node in sso_domain_nodes:
|
|
print_task(node)
|
|
try:
|
|
pem_cert = get_certificate_from_host(node, 443)
|
|
x509_cert = get_x509_certificate(pem_cert)
|
|
node_thumbprint = get_certificate_fingerprint(x509_cert)
|
|
except OpenSSL.crypto.Error:
|
|
add_certificate_status(CertificateStatus.TRUST_ANCHORS_UNKNOWN)
|
|
unknown_nodes = env.get_value('TRUST_ANCHORS_UNKNOWN_NODES')
|
|
if unknown_nodes is None:
|
|
unknown_nodes = []
|
|
env.set_value('TRUST_ANCHORS_UNKNOWN_NODES', unknown_nodes)
|
|
unknown_nodes.append(node)
|
|
print_task_status_warning('UNKNOWN')
|
|
continue
|
|
|
|
ip_address = get_ip_address(node)
|
|
trust_anchors = vmdir.get_node_trust_anchors(node)
|
|
if not trust_anchors:
|
|
is_using_ip_address = True
|
|
trust_anchors = vmdir.get_node_trust_anchors(ip_address)
|
|
|
|
if not trust_anchors:
|
|
add_certificate_status(CertificateStatus.TRUST_ANCHORS_CHECK_URI_OTHER)
|
|
print_task_status_warning('MISSING')
|
|
continue
|
|
|
|
for cert_entry in trust_anchors:
|
|
pem_cert = build_pem_certificate(cert_entry)
|
|
fingerprint = get_certificate_fingerprint(get_x509_certificate(pem_cert))
|
|
logger.info("Checking node thumbprint {} against trust anchor thumbprint {}"
|
|
.format(node_thumbprint, fingerprint))
|
|
if fingerprint != node_thumbprint:
|
|
if node == hostname or node == ip_address:
|
|
machine_ssl_cert = vecs.get_certificate('MACHINE_SSL_CERT', '__MACHINE_CERT')
|
|
x509_machine_ssl_cert = get_x509_certificate(machine_ssl_cert)
|
|
machine_ssl_thumbprint = get_certificate_fingerprint(x509_machine_ssl_cert)
|
|
if fingerprint == machine_ssl_thumbprint:
|
|
is_pending = True
|
|
else:
|
|
is_mismatch = True
|
|
|
|
logger.info("Searching for ghost trust anchors")
|
|
vcs = vmdir.get_registered_vcenters()
|
|
for deployment_id, dn in vcs:
|
|
uris = '\n'.join(vmdir.get_endpoint_registrations(dn))
|
|
pattern = ".*https://{0}/.*|.*https://{0}:.*|.*https://{1}/.*|.*https://{1}:.*".format(node, ip_address)
|
|
if not re.match(pattern, uris):
|
|
continue
|
|
|
|
logger.debug("Found vCenter registration for {}: {}".format(node, deployment_id))
|
|
ghost_vmonapi_dn = get_ghost_vmonapi_dn(deployment_id)
|
|
if not ghost_vmonapi_dn:
|
|
continue
|
|
|
|
logger.debug("cis.vmonapi registration DN: {}".format(ghost_vmonapi_dn))
|
|
trust_anchors = get_ghost_vmonapi_trust_anchors(ghost_vmonapi_dn)
|
|
for cert in trust_anchors:
|
|
pem_cert = build_pem_certificate(cert)
|
|
x509_cert = get_x509_certificate(pem_cert)
|
|
fingerprint = get_certificate_fingerprint(x509_cert)
|
|
logger.info("Checking node thumbprint {} against trust anchor thumbprint {}"
|
|
.format(node_thumbprint, fingerprint))
|
|
if node_thumbprint != fingerprint:
|
|
if node == hostname or node == ip_address:
|
|
machine_ssl_cert = vecs.get_certificate('MACHINE_SSL_CERT', '__MACHINE_CERT')
|
|
x509_machine_ssl_cert = get_x509_certificate(machine_ssl_cert)
|
|
machine_ssl_thumbprint = get_certificate_fingerprint(x509_machine_ssl_cert)
|
|
if fingerprint == machine_ssl_thumbprint:
|
|
is_pending = True
|
|
else:
|
|
is_mismatch = True
|
|
|
|
if is_mismatch:
|
|
add_certificate_status(CertificateStatus.TRUST_ANCHORS_MISMATCH)
|
|
if is_using_ip_address:
|
|
add_certificate_status(CertificateStatus.TRUST_ANCHORS_CHECK_URI_MISMATCH)
|
|
print_task_status_warning('MISMATCH*')
|
|
else:
|
|
print_task_status_warning('MISMATCH')
|
|
elif is_pending:
|
|
print_task_status_warning('PENDING')
|
|
add_certificate_status(CertificateStatus.TRUST_ANCHORS_CHECK_PENDING)
|
|
else:
|
|
if is_using_ip_address:
|
|
add_certificate_status(CertificateStatus.TRUST_ANCHORS_CHECK_URI_OTHER)
|
|
print_task_status_warning('CHECK URI')
|
|
else:
|
|
print_task_status('VALID')
|
|
|
|
|
|
def get_ghost_vmonapi_dn(deployment_id):
|
|
domain_dn = Environment.get_environment().get_value("SSO_DOMAIN_DN")
|
|
search_base = "cn=Sites,cn=Configuration,{}".format(domain_dn)
|
|
search_filter = "(&(vmwLKUPType=cis.vmonapi)(vmwLKUPDeploymentNodeId={}))".format(deployment_id)
|
|
search_attributes = ['dn']
|
|
results = vmdir.perform_ldap_search(search_base, search_filter, search_attributes)
|
|
return results[0]['dn'] if results else ''
|
|
|
|
|
|
def get_ghost_vmonapi_trust_anchors(vmonapi_dn):
|
|
search_filter = '(vmwLKUPURI=http://localhost*)'
|
|
search_attributes = ['vmwLKUPEndpointSslTrust']
|
|
results = vmdir.perform_ldap_search(vmonapi_dn, search_filter, search_attributes)
|
|
trust_anchors = []
|
|
for entry in results:
|
|
trust_anchors.extend(entry['vmwLKUPEndpointSslTrust'])
|
|
return trust_anchors
|
|
|
|
|
|
def check_vcenter_extension_thumbprints():
|
|
"""
|
|
Entry point for certificate check on vcenter extension thumbprints
|
|
"""
|
|
if not is_service_running('vmware-vpostgres'):
|
|
print_text_error('The vPostgres service is stopped!\n'''
|
|
'Please ensure this service is running before updating vCenter extension thumbprints.\n'
|
|
'Hint: Check the number of CRL entries in VECS')
|
|
return None
|
|
|
|
vcenter_extensions = get_vcenter_extensions()
|
|
extension_thumbprints = vcdb.get_extension_thumbprints(vcenter_extensions)
|
|
expected_thumbprints = get_vcenter_extension_expected_thumbprints(vcenter_extensions)
|
|
|
|
check_result = True
|
|
for extension, (thumbprint, db_cert_pem) in extension_thumbprints.items():
|
|
expected_thumbprint, expected_cert_type, expected_cert_pem = expected_thumbprints[extension]
|
|
print_task("{} ({})".format(extension, expected_cert_type))
|
|
logger.info("Comparing {} thumbprint of {} to {}".format(extension, thumbprint, expected_thumbprint))
|
|
if get_vc_version() < VcVersion.V9:
|
|
if thumbprint == expected_thumbprint:
|
|
print_task_status('MATCHES')
|
|
else:
|
|
print_task_status_warning('MISMATCH')
|
|
check_result = False
|
|
else:
|
|
if thumbprint == expected_thumbprint and db_cert_pem == expected_cert_pem:
|
|
print_task_status('MATCHES')
|
|
else:
|
|
print_task_status_warning('MISMATCH')
|
|
check_result = False
|
|
return check_result
|
|
|
|
|
|
def add_certificate_status(cert_status, detail=None):
|
|
env = Environment.get_environment()
|
|
results = env.get_value('CERTIFICATE_CHECK_RESULT')
|
|
if results is None:
|
|
results = []
|
|
env.set_value('CERTIFICATE_CHECK_RESULT', results)
|
|
if cert_status not in [r[0] for r in results]:
|
|
results.append([cert_status, detail])
|
|
|
|
|
|
def reset_certificate_status():
|
|
env = Environment.get_environment()
|
|
env.set_value('CERTIFICATE_CHECK_RESULT', None)
|
|
env.set_value('TRUST_ANCHORS_UNKNOWN_NODES', None)
|
|
|
|
|
|
def show_check_result_summary():
|
|
env = Environment.get_environment()
|
|
results = env.get_value('CERTIFICATE_CHECK_RESULT')
|
|
if not results:
|
|
return
|
|
print_text_error('\n------------------------!!! Attention !!!------------------------')
|
|
for status, detail in results:
|
|
lines = status.value.format_map(env.get_map()).splitlines()
|
|
for idx, line in enumerate(lines):
|
|
print_text_error(' - ' if idx == 0 else ' ', end='')
|
|
if status == CertificateStatus.TRUST_ANCHORS_UNKNOWN:
|
|
for unknown_node in env.get_value('TRUST_ANCHORS_UNKNOWN_NODES'):
|
|
print_text_error(" {}".format(unknown_node))
|
|
print_text_error(line if not detail else line.replace('>>DETAILS<<', detail))
|
|
|
|
|
|
def check_rogue_ca(pem_cert):
|
|
"""
|
|
Check if a certificate is invalid due to a CA extending beyond the
|
|
path length restriction of a parent CA
|
|
:param pem_cert: Base64 certificate hash
|
|
:return cert_is_rogue: True if certificate is invalid due to a rogue CA, False otherwise,
|
|
rogue_ca: name of CA in the signing chain that violates the path length restriction
|
|
of a parent CA
|
|
"""
|
|
logger.info('Entering the function to check for Rogue CA')
|
|
cert_is_rogue = False
|
|
rogue_ca = ''
|
|
subject_keyids = vmdir.get_all_ca_subject_keyids()
|
|
cert_path = build_certification_path(pem_cert, subject_keyids, vmdir.get_ca_certificate)
|
|
ca_certs_in_path = len(cert_path) if cert_path[-1]['is_ca'] is True else len(cert_path)-1
|
|
for index, cert in enumerate(cert_path):
|
|
logger.info("Checking for path length restrictions on certificate {}".format(cert_path[index]['cert_name']))
|
|
if cert['pathlen'] is not False:
|
|
logger.info("Path length restriction found on certificate {}: {}".format(cert_path[index]['cert_name'], cert_path[index]['pathlen']))
|
|
max_allowed_ca = index + int(cert['pathlen']) + 1
|
|
logger.info("Maximum allowed CAs in the signing chain is: {}".format(max_allowed_ca))
|
|
if ca_certs_in_path > max_allowed_ca:
|
|
cert_is_rogue = True
|
|
rogue_ca = cert_path[max_allowed_ca]['cert_name']
|
|
logger.info("Path length violation found on certificate {}".format(cert_path[max_allowed_ca]['cert_name']))
|
|
break
|
|
return cert_is_rogue, rogue_ca
|
|
|
|
|
|
def check_duplicate_ca(pem_cert):
|
|
"""
|
|
Check if there are multiple CA certificates with the same Subject string (but different private key).
|
|
This can cause certificate validation issues.
|
|
:param pem_cert: Base64 certificate hash
|
|
:return True if there are found to be duplicate certificates with the same Subject string, False otherwise
|
|
"""
|
|
subject_hash = get_subject_hash(pem_cert)
|
|
return True if len(glob.glob("/etc/ssl/certs/{}.[0-9]".format(subject_hash))) > 1 else False
|
|
|
|
|
|
def check_missing_ca(pem_cert, provided_ca_chain=False):
|
|
"""
|
|
Check to see if any CA certificates in the signing chain of a cert are missing from VMware Directory
|
|
|
|
:param pem_cert: Base64 certificate hash
|
|
:return: True if there is a CA certificate in the signing chain missing from VMware Directory, False otherwise
|
|
"""
|
|
ca_is_missing = False
|
|
x509_cert = get_x509_certificate(pem_cert)
|
|
if not provided_ca_chain:
|
|
subject_keyids = vmdir.get_all_ca_subject_keyids()
|
|
else:
|
|
subject_keyids = []
|
|
ca_certs = split_certificates_from_pem(get_file_contents(provided_ca_chain))
|
|
for cert in ca_certs:
|
|
ca_x509 = get_x509_certificate(cert)
|
|
subject_keyids.append(get_authority_keyid(ca_x509, True))
|
|
|
|
current_keyid = get_authority_keyid(x509_cert, True)
|
|
while True:
|
|
logger.info("Looking for AKID {} in {}".format(current_keyid, subject_keyids))
|
|
if current_keyid not in subject_keyids:
|
|
logger.info("AKID {} was NOT found in {}".format(current_keyid, subject_keyids))
|
|
ca_is_missing = True
|
|
break
|
|
else:
|
|
ca_x509 = get_x509_certificate(vmdir.get_ca_certificate(current_keyid))
|
|
subject_dn, issuer_dn = get_subject_and_issuer_dn(ca_x509)
|
|
# if we've reached the Root CA cert, we're done
|
|
if subject_dn == issuer_dn:
|
|
break
|
|
current_keyid = get_authority_keyid(ca_x509, True)
|
|
|
|
return ca_is_missing
|
|
|
|
|
|
def check_ssl_interception():
|
|
"""
|
|
Checks to see if SSL interception is occurring between vCenter and one of the online VMware repositories
|
|
"""
|
|
print_task("Checking {}".format(VMWARE_DEPOT_HOST))
|
|
depot_certs = split_certificates_from_pem(get_certificate_from_host('hostupdate.vmware.com'))
|
|
if len(depot_certs) == 0:
|
|
print_task_status_warning('FAILED')
|
|
print_text("{}Unable to obtain a certificate for {}".format(ColorKey.YELLOW, VMWARE_DEPOT_HOST))
|
|
print_text("Ensure the remote repository is accessible.{}".format(ColorKey.NORMAL))
|
|
return
|
|
else:
|
|
depot_issuer_cert_name = get_certificate_name(depot_certs[0], 'issuer')
|
|
logger.info("Certificate for {} is issued by {}".format(VMWARE_DEPOT_HOST, depot_issuer_cert_name))
|
|
if depot_issuer_cert_name == VMWARE_DEPOT_CERT_ISSUER:
|
|
print_task_status('OK')
|
|
print()
|
|
print_text("Issuing CA for {} is {}{}{}".format(VMWARE_DEPOT_HOST, ColorKey.GREEN, depot_issuer_cert_name, ColorKey.NORMAL))
|
|
return
|
|
else:
|
|
print_task_status_warning('WARNING')
|
|
print()
|
|
print_text("Issuing CA for {} is {}{}{}".format(VMWARE_DEPOT_HOST, ColorKey.YELLOW, depot_issuer_cert_name, ColorKey.NORMAL))
|
|
print_text("Expected issuer is {}{}{}".format(ColorKey.GREEN, VMWARE_DEPOT_CERT_ISSUER, ColorKey.NORMAL))
|
|
print()
|
|
print_text("{}SSL interception is likely taking place!{}".format(ColorKey.YELLOW, ColorKey.NORMAL))
|
|
env = Environment.get_environment()
|
|
menu = Menu.load_menu_from_config('config/check_config/ssl_interception/menu_manage_ssl_interception.yaml')
|
|
try:
|
|
menu.run()
|
|
except MenuExitException:
|
|
pass
|