From 4f39d384e22e3ca07d4340d8bd5239e758e89958 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 23 Jun 2026 11:27:54 +0800 Subject: [PATCH] add vmware-tools --- fixcerts_3_2.py | 2433 +++++++++++++++++ .../check_cert/op_check_00-reset_results.yaml | 7 + .../op_check_01-certificate_status.yaml | 7 + .../op_check_02-sts_tenant_certs.yaml | 7 + .../op_check_03-ca_certs_in_vmdir.yaml | 7 + .../op_check_04-ca_certs_in_vecs.yaml | 7 + .../op_check_05-vecs_store_permissions.yaml | 7 + .../op_check_06-service_principals.yaml | 7 + .../config/check_cert/op_check_07-crls.yaml | 7 + .../op_check_08-identity-source.yaml | 7 + .../op_check_09-ssl_trust_anchors.yaml | 7 + .../op_check_10-vc_ext_thumbprints.yaml | 7 + .../check_cert/op_check_11-vmca_vcdb.yaml | 7 + .../check_cert/op_check_12-sts_config.yaml | 7 + ...op_check_99-show_check_result_summary.yaml | 7 + .../op_check_01-ssl_interception.yaml | 7 + .../config/check_config/op_check_02-sts.yaml | 9 + .../op_check_04-vecs_store_permissions.yaml | 10 + .../menu_manage_ssl_interception.yaml | 13 + .../op_01_download_certs_proxy.yaml | 11 + .../op_02_provide_proxy_ca_certs.yaml | 11 + .../sts_config/op_check_sts_config.yaml | 7 + .../op_esxi_action_01-manage_cert.yaml | 7 + ...tion_02-check_cert_against_vCenter_db.yaml | 7 + .../op_esxi_action_03-replace_cert.yaml | 7 + .../op_genreport_01-default-report.yaml | 37 + vCert-6.1.1-20260401/config/logging.yaml | 30 + .../custom_ca_signed/op_01_generate-csr.yaml | 12 + .../op_02_generate-csr-openssl-config.yaml | 12 + .../op_03_import-certificate.yaml | 11 + .../menu_custom-ca-signed-cert.yaml | 13 + .../machine_ssl/op_vmca-signed-cert.yaml | 11 + .../manage_cert/menu_machine_ssl_cert.yaml | 17 + .../config/manage_cert/menu_sms_certs.yaml | 11 + .../manage_cert/menu_solution_user_cert.yaml | 17 + .../config/manage_cert/menu_vmca_cert.yaml | 20 + .../manage_cert/op_ca_certs_in_vecs.yaml | 11 + .../manage_cert/op_ca_certs_in_vmdir.yaml | 11 + .../op_clear_expired_backup_store.yaml | 7 + .../manage_cert/op_clear_machine_ssl_csr.yaml | 7 + .../op_clear_trusted_root_crls.yaml | 6 + .../manage_cert/op_data-encipherment.yaml | 11 + .../manage_cert/op_ldaps_identity_source.yaml | 12 + .../op_manage-vc-ext-thumbprints.yaml | 11 + .../config/manage_cert/op_smart_card.yaml | 12 + .../manage_cert/op_sts_signing_certs.yaml | 16 + .../manage_cert/sms/menu_manage_sms.yaml | 13 + .../sms/op_01-renew_sms_self_signed.yaml | 11 + .../sms/op_02-renew_sps_vmca_signed.yaml | 11 + .../custom_ca_signed/op_01_generate-csr.yaml | 10 + .../op_02_generate-csr-openssl-config.yaml | 10 + .../op_03_import-certificate.yaml | 11 + .../soluser/menu_custom-ca-signed-cert.yaml | 13 + .../soluser/op_vmca-signed-cert.yaml | 11 + .../custom_ca_signed/op_01_generate-csr.yaml | 12 + .../op_02_generate-csr-openssl-config.yaml | 12 + .../op_03_import-certificate.yaml | 11 + .../menu_custom-ca-signed-cert.yaml | 13 + .../sts_signing/menu_sts-signing-certs.yaml | 18 + .../sts_signing/op_vmca-signed-cert.yaml | 11 + .../menu_check_trust_anchors.yaml | 21 + .../trust_anchors/op_check_none.yaml | 12 + .../trust_anchors/op_check_with_ep_uri.yaml | 15 + .../op_check_with_fingerprint.yaml | 14 + .../op_check_with_service_id.yaml | 15 + .../op_check_with_service_id_and_uri.yaml | 16 + .../op_update_trust_anchors.yaml | 11 + .../custom_ca_signed/op_01_generate-csr.yaml | 14 + .../op_02_generate-csr-openssl-config.yaml | 12 + .../op_03_import-certificate.yaml | 11 + ...mport-certificate-and-reset-all-certs.yaml | 11 + .../vmca/menu_custom-ca-signed-vmca-cert.yaml | 13 + .../op_replace-vmca-cert-and-reset-all.yaml | 11 + .../vmca/op_replace-vmca-cert.yaml | 12 + .../config/menu_check_cert_status.yaml | 8 + .../config/menu_check_config.yaml | 13 + .../config/menu_esxi_cert.yaml | 13 + vCert-6.1.1-20260401/config/menu_main.yaml | 28 + .../config/menu_manage_cert.yaml | 39 + .../config/menu_manage_trust_anchors.yaml | 17 + .../config/menu_restart_service.yaml | 13 + .../config/menu_view_cert.yaml | 13 + .../config/op_check_cert.yaml | 9 + .../config/op_check_machine_ssl.yaml | 6 + .../config/op_generate_report.yaml | 17 + .../config/op_prevalidate_credential.yaml | 7 + .../config/op_reset_cert_vmca.yaml | 11 + ...op_restart_01-restart_all_vm_services.yaml | 7 + ..._restart_02-restart_specific_services.yaml | 7 + .../vecs_permissions/vcsa-7.0.0-15952599.json | 1 + .../vecs_permissions/vcsa-7.0.0-16189207.json | 1 + .../vecs_permissions/vcsa-7.0.0-16386335.json | 1 + .../vecs_permissions/vcsa-7.0.0-16620013.json | 1 + .../vecs_permissions/vcsa-7.0.0-16749670.json | 1 + .../vecs_permissions/vcsa-7.0.1-16858589.json | 1 + .../vecs_permissions/vcsa-7.0.1-17005016.json | 1 + .../vecs_permissions/vcsa-7.0.1-17327586.json | 1 + .../vecs_permissions/vcsa-7.0.1-17491160.json | 1 + .../vecs_permissions/vcsa-7.0.2-17694817.json | 1 + .../vecs_permissions/vcsa-7.0.2-17920168.json | 1 + .../vecs_permissions/vcsa-7.0.2-17958471.json | 1 + .../vecs_permissions/vcsa-7.0.2-18356314.json | 1 + .../vecs_permissions/vcsa-7.0.2-18455184.json | 1 + .../vecs_permissions/vcsa-7.0.3-18700403.json | 1 + .../vecs_permissions/vcsa-7.0.3-18778458.json | 1 + .../vecs_permissions/vcsa-7.0.3-19234570.json | 1 + .../vecs_permissions/vcsa-7.0.3-19480866.json | 1 + .../vecs_permissions/vcsa-7.0.3-19717403.json | 1 + .../vecs_permissions/vcsa-7.0.3-20051473.json | 1 + .../vecs_permissions/vcsa-7.0.3-20150588.json | 1 + .../vecs_permissions/vcsa-7.0.3-20395099.json | 1 + .../vecs_permissions/vcsa-7.0.3-20845200.json | 1 + .../vecs_permissions/vcsa-7.0.3-20990077.json | 1 + .../vecs_permissions/vcsa-7.0.3-21290409.json | 1 + .../vecs_permissions/vcsa-7.0.3-21477706.json | 1 + .../vecs_permissions/vcsa-7.0.3-21784236.json | 1 + .../vecs_permissions/vcsa-7.0.3-21958406.json | 1 + .../vecs_permissions/vcsa-7.0.3-22357613.json | 1 + .../vecs_permissions/vcsa-7.0.3-22837322.json | 1 + .../vecs_permissions/vcsa-7.0.3-23788036.json | 1 + .../vecs_permissions/vcsa-7.0.3-24026615.json | 1 + .../vecs_permissions/vcsa-7.0.3-24201990.json | 1 + .../vecs_permissions/vcsa-7.0.3-24322018.json | 1 + .../vecs_permissions/vcsa-7.0.3-24614210.json | 1 + .../vecs_permissions/vcsa-7.0.3-24730281.json | 1 + .../vecs_permissions/vcsa-7.0.3-24927011.json | 1 + .../vecs_permissions/vcsa-8.0.0-20519528.json | 1 + .../vecs_permissions/vcsa-8.0.0-20920323.json | 1 + .../vecs_permissions/vcsa-8.0.0-21216066.json | 1 + .../vecs_permissions/vcsa-8.0.0-21457384.json | 1 + .../vecs_permissions/vcsa-8.0.1-21560480.json | 1 + .../vecs_permissions/vcsa-8.0.1-21815093.json | 1 + .../vecs_permissions/vcsa-8.0.1-21860503.json | 1 + .../vecs_permissions/vcsa-8.0.1-22088981.json | 1 + .../vecs_permissions/vcsa-8.0.1-22368047.json | 1 + .../vecs_permissions/vcsa-8.0.1-24005165.json | 1 + .../vecs_permissions/vcsa-8.0.2-22385739.json | 1 + .../vecs_permissions/vcsa-8.0.2-22617221.json | 1 + .../vecs_permissions/vcsa-8.0.2-23319993.json | 1 + .../vecs_permissions/vcsa-8.0.2-23504390.json | 1 + .../vecs_permissions/vcsa-8.0.2-23929136.json | 1 + .../vecs_permissions/vcsa-8.0.2-24321653.json | 1 + .../vecs_permissions/vcsa-8.0.3-24022515.json | 1 + .../vecs_permissions/vcsa-8.0.3-24091160.json | 1 + .../vecs_permissions/vcsa-8.0.3-24262322.json | 1 + .../vecs_permissions/vcsa-8.0.3-24305161.json | 1 + .../vecs_permissions/vcsa-8.0.3-24322831.json | 1 + .../vecs_permissions/vcsa-8.0.3-24674346.json | 1 + .../vecs_permissions/vcsa-8.0.3-24853646.json | 1 + .../vecs_permissions/vcsa-8.0.3-25092719.json | 1 + .../vecs_permissions/vcsa-8.0.3-25197330.json | 1 + .../vecs_permissions/vcsa-9.0.0-24755230.json | 1 + .../vecs_permissions/vcsa-9.0.1-24957454.json | 1 + .../vecs_permissions/vcsa-9.0.2-25148086.json | 1 + .../view_cert/op_view_01-machine_ssl.yaml | 7 + .../view_cert/op_view_02-solution_user.yaml | 7 + .../config/view_cert/op_view_03-ca_vmdir.yaml | 11 + .../config/view_cert/op_view_04-ca_vecs.yaml | 7 + .../config/view_cert/op_view_07-sms.yaml | 7 + .../op_view_08-data_encipherment.yaml | 7 + .../op_view_09-vc_ext_thumbprint.yaml | 7 + .../config/view_cert/op_view_11-sts.yaml | 7 + .../config/view_cert/op_view_12-vmca.yaml | 6 + .../view_cert/op_view_13-smart_card.yaml | 7 + .../op_view_14-ldaps_identity_source.yaml | 12 + vCert-6.1.1-20260401/lib/.gitkeep | 0 vCert-6.1.1-20260401/lib/__init__.py | 0 vCert-6.1.1-20260401/lib/certificate_utils.py | 688 +++++ vCert-6.1.1-20260401/lib/command_runner.py | 178 ++ vCert-6.1.1-20260401/lib/console.py | 155 ++ vCert-6.1.1-20260401/lib/constants.py | 42 + vCert-6.1.1-20260401/lib/environment.py | 82 + vCert-6.1.1-20260401/lib/exceptions.py | 39 + vCert-6.1.1-20260401/lib/execution_replay.py | 240 ++ vCert-6.1.1-20260401/lib/host_utils.py | 502 ++++ vCert-6.1.1-20260401/lib/input.py | 97 + vCert-6.1.1-20260401/lib/java_utils.py | 60 + vCert-6.1.1-20260401/lib/ldap_utils.py | 266 ++ vCert-6.1.1-20260401/lib/menu.py | 438 +++ vCert-6.1.1-20260401/lib/operation.py | 230 ++ vCert-6.1.1-20260401/lib/services.py | 119 + vCert-6.1.1-20260401/lib/text_utils.py | 274 ++ vCert-6.1.1-20260401/lib/vcdb.py | 155 ++ vCert-6.1.1-20260401/lib/vecs.py | 305 +++ vCert-6.1.1-20260401/lib/vmdir.py | 607 ++++ vCert-6.1.1-20260401/operation/.gitkeep | 0 vCert-6.1.1-20260401/operation/__init__.py | 6 + .../operation/check_certificate.py | 981 +++++++ .../operation/check_configuration.py | 234 ++ vCert-6.1.1-20260401/operation/common.py | 206 ++ .../operation/esxi_certificate_operations.py | 884 ++++++ .../operation/generate_report.py | 432 +++ .../operation/manage_certificate.py | 2287 ++++++++++++++++ .../operation/manage_configuration.py | 129 + .../operation/reset_certificate.py | 290 ++ .../operation/restart_service.py | 87 + .../operation/view_certificate.py | 332 +++ vCert-6.1.1-20260401/vCert.py | 197 ++ 198 files changed, 14170 insertions(+) create mode 100644 fixcerts_3_2.py create mode 100644 vCert-6.1.1-20260401/config/check_cert/op_check_00-reset_results.yaml create mode 100644 vCert-6.1.1-20260401/config/check_cert/op_check_01-certificate_status.yaml create mode 100644 vCert-6.1.1-20260401/config/check_cert/op_check_02-sts_tenant_certs.yaml create mode 100644 vCert-6.1.1-20260401/config/check_cert/op_check_03-ca_certs_in_vmdir.yaml create mode 100644 vCert-6.1.1-20260401/config/check_cert/op_check_04-ca_certs_in_vecs.yaml create mode 100644 vCert-6.1.1-20260401/config/check_cert/op_check_05-vecs_store_permissions.yaml create mode 100644 vCert-6.1.1-20260401/config/check_cert/op_check_06-service_principals.yaml create mode 100644 vCert-6.1.1-20260401/config/check_cert/op_check_07-crls.yaml create mode 100644 vCert-6.1.1-20260401/config/check_cert/op_check_08-identity-source.yaml create mode 100644 vCert-6.1.1-20260401/config/check_cert/op_check_09-ssl_trust_anchors.yaml create mode 100644 vCert-6.1.1-20260401/config/check_cert/op_check_10-vc_ext_thumbprints.yaml create mode 100644 vCert-6.1.1-20260401/config/check_cert/op_check_11-vmca_vcdb.yaml create mode 100644 vCert-6.1.1-20260401/config/check_cert/op_check_12-sts_config.yaml create mode 100644 vCert-6.1.1-20260401/config/check_cert/op_check_99-show_check_result_summary.yaml create mode 100644 vCert-6.1.1-20260401/config/check_config/op_check_01-ssl_interception.yaml create mode 100644 vCert-6.1.1-20260401/config/check_config/op_check_02-sts.yaml create mode 100644 vCert-6.1.1-20260401/config/check_config/op_check_04-vecs_store_permissions.yaml create mode 100644 vCert-6.1.1-20260401/config/check_config/ssl_interception/menu_manage_ssl_interception.yaml create mode 100644 vCert-6.1.1-20260401/config/check_config/ssl_interception/op_01_download_certs_proxy.yaml create mode 100644 vCert-6.1.1-20260401/config/check_config/ssl_interception/op_02_provide_proxy_ca_certs.yaml create mode 100644 vCert-6.1.1-20260401/config/check_config/sts_config/op_check_sts_config.yaml create mode 100644 vCert-6.1.1-20260401/config/esxi_cert_actions/op_esxi_action_01-manage_cert.yaml create mode 100644 vCert-6.1.1-20260401/config/esxi_cert_actions/op_esxi_action_02-check_cert_against_vCenter_db.yaml create mode 100644 vCert-6.1.1-20260401/config/esxi_cert_actions/op_esxi_action_03-replace_cert.yaml create mode 100644 vCert-6.1.1-20260401/config/generate_report/op_genreport_01-default-report.yaml create mode 100644 vCert-6.1.1-20260401/config/logging.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/machine_ssl/custom_ca_signed/op_01_generate-csr.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/machine_ssl/custom_ca_signed/op_02_generate-csr-openssl-config.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/machine_ssl/custom_ca_signed/op_03_import-certificate.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/machine_ssl/menu_custom-ca-signed-cert.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/machine_ssl/op_vmca-signed-cert.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/menu_machine_ssl_cert.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/menu_sms_certs.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/menu_solution_user_cert.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/menu_vmca_cert.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/op_ca_certs_in_vecs.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/op_ca_certs_in_vmdir.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/op_clear_expired_backup_store.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/op_clear_machine_ssl_csr.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/op_clear_trusted_root_crls.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/op_data-encipherment.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/op_ldaps_identity_source.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/op_manage-vc-ext-thumbprints.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/op_smart_card.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/op_sts_signing_certs.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/sms/menu_manage_sms.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/sms/op_01-renew_sms_self_signed.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/sms/op_02-renew_sps_vmca_signed.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/soluser/custom_ca_signed/op_01_generate-csr.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/soluser/custom_ca_signed/op_02_generate-csr-openssl-config.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/soluser/custom_ca_signed/op_03_import-certificate.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/soluser/menu_custom-ca-signed-cert.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/soluser/op_vmca-signed-cert.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/sts_signing/custom_ca_signed/op_01_generate-csr.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/sts_signing/custom_ca_signed/op_02_generate-csr-openssl-config.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/sts_signing/custom_ca_signed/op_03_import-certificate.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/sts_signing/menu_custom-ca-signed-cert.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/sts_signing/menu_sts-signing-certs.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/sts_signing/op_vmca-signed-cert.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/trust_anchors/menu_check_trust_anchors.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_none.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_with_ep_uri.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_with_fingerprint.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_with_service_id.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_with_service_id_and_uri.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_update_trust_anchors.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/vmca/custom_ca_signed/op_01_generate-csr.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/vmca/custom_ca_signed/op_02_generate-csr-openssl-config.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/vmca/custom_ca_signed/op_03_import-certificate.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/vmca/custom_ca_signed/op_04_import-certificate-and-reset-all-certs.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/vmca/menu_custom-ca-signed-vmca-cert.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/vmca/op_replace-vmca-cert-and-reset-all.yaml create mode 100644 vCert-6.1.1-20260401/config/manage_cert/vmca/op_replace-vmca-cert.yaml create mode 100644 vCert-6.1.1-20260401/config/menu_check_cert_status.yaml create mode 100644 vCert-6.1.1-20260401/config/menu_check_config.yaml create mode 100644 vCert-6.1.1-20260401/config/menu_esxi_cert.yaml create mode 100644 vCert-6.1.1-20260401/config/menu_main.yaml create mode 100644 vCert-6.1.1-20260401/config/menu_manage_cert.yaml create mode 100644 vCert-6.1.1-20260401/config/menu_manage_trust_anchors.yaml create mode 100644 vCert-6.1.1-20260401/config/menu_restart_service.yaml create mode 100644 vCert-6.1.1-20260401/config/menu_view_cert.yaml create mode 100644 vCert-6.1.1-20260401/config/op_check_cert.yaml create mode 100644 vCert-6.1.1-20260401/config/op_check_machine_ssl.yaml create mode 100644 vCert-6.1.1-20260401/config/op_generate_report.yaml create mode 100644 vCert-6.1.1-20260401/config/op_prevalidate_credential.yaml create mode 100644 vCert-6.1.1-20260401/config/op_reset_cert_vmca.yaml create mode 100644 vCert-6.1.1-20260401/config/restart_service/op_restart_01-restart_all_vm_services.yaml create mode 100644 vCert-6.1.1-20260401/config/restart_service/op_restart_02-restart_specific_services.yaml create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-15952599.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-16189207.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-16386335.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-16620013.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-16749670.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.1-16858589.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.1-17005016.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.1-17327586.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.1-17491160.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-17694817.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-17920168.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-17958471.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-18356314.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-18455184.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-18700403.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-18778458.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-19234570.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-19480866.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-19717403.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20051473.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20150588.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20395099.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20845200.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20990077.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-21290409.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-21477706.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-21784236.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-21958406.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-22357613.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-22837322.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-23788036.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24026615.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24201990.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24322018.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24614210.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24730281.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24927011.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.0-20519528.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.0-20920323.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.0-21216066.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.0-21457384.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-21560480.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-21815093.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-21860503.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-22088981.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-22368047.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-24005165.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-22385739.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-22617221.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-23319993.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-23504390.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-23929136.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-24321653.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24022515.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24091160.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24262322.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24305161.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24322831.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24674346.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24853646.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-25092719.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-25197330.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-9.0.0-24755230.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-9.0.1-24957454.json create mode 100644 vCert-6.1.1-20260401/config/vecs_permissions/vcsa-9.0.2-25148086.json create mode 100644 vCert-6.1.1-20260401/config/view_cert/op_view_01-machine_ssl.yaml create mode 100644 vCert-6.1.1-20260401/config/view_cert/op_view_02-solution_user.yaml create mode 100644 vCert-6.1.1-20260401/config/view_cert/op_view_03-ca_vmdir.yaml create mode 100644 vCert-6.1.1-20260401/config/view_cert/op_view_04-ca_vecs.yaml create mode 100644 vCert-6.1.1-20260401/config/view_cert/op_view_07-sms.yaml create mode 100644 vCert-6.1.1-20260401/config/view_cert/op_view_08-data_encipherment.yaml create mode 100644 vCert-6.1.1-20260401/config/view_cert/op_view_09-vc_ext_thumbprint.yaml create mode 100644 vCert-6.1.1-20260401/config/view_cert/op_view_11-sts.yaml create mode 100644 vCert-6.1.1-20260401/config/view_cert/op_view_12-vmca.yaml create mode 100644 vCert-6.1.1-20260401/config/view_cert/op_view_13-smart_card.yaml create mode 100644 vCert-6.1.1-20260401/config/view_cert/op_view_14-ldaps_identity_source.yaml create mode 100644 vCert-6.1.1-20260401/lib/.gitkeep create mode 100644 vCert-6.1.1-20260401/lib/__init__.py create mode 100644 vCert-6.1.1-20260401/lib/certificate_utils.py create mode 100644 vCert-6.1.1-20260401/lib/command_runner.py create mode 100644 vCert-6.1.1-20260401/lib/console.py create mode 100644 vCert-6.1.1-20260401/lib/constants.py create mode 100644 vCert-6.1.1-20260401/lib/environment.py create mode 100644 vCert-6.1.1-20260401/lib/exceptions.py create mode 100644 vCert-6.1.1-20260401/lib/execution_replay.py create mode 100644 vCert-6.1.1-20260401/lib/host_utils.py create mode 100644 vCert-6.1.1-20260401/lib/input.py create mode 100644 vCert-6.1.1-20260401/lib/java_utils.py create mode 100644 vCert-6.1.1-20260401/lib/ldap_utils.py create mode 100644 vCert-6.1.1-20260401/lib/menu.py create mode 100644 vCert-6.1.1-20260401/lib/operation.py create mode 100644 vCert-6.1.1-20260401/lib/services.py create mode 100644 vCert-6.1.1-20260401/lib/text_utils.py create mode 100644 vCert-6.1.1-20260401/lib/vcdb.py create mode 100644 vCert-6.1.1-20260401/lib/vecs.py create mode 100644 vCert-6.1.1-20260401/lib/vmdir.py create mode 100644 vCert-6.1.1-20260401/operation/.gitkeep create mode 100644 vCert-6.1.1-20260401/operation/__init__.py create mode 100644 vCert-6.1.1-20260401/operation/check_certificate.py create mode 100644 vCert-6.1.1-20260401/operation/check_configuration.py create mode 100644 vCert-6.1.1-20260401/operation/common.py create mode 100644 vCert-6.1.1-20260401/operation/esxi_certificate_operations.py create mode 100644 vCert-6.1.1-20260401/operation/generate_report.py create mode 100644 vCert-6.1.1-20260401/operation/manage_certificate.py create mode 100644 vCert-6.1.1-20260401/operation/manage_configuration.py create mode 100644 vCert-6.1.1-20260401/operation/reset_certificate.py create mode 100644 vCert-6.1.1-20260401/operation/restart_service.py create mode 100644 vCert-6.1.1-20260401/operation/view_certificate.py create mode 100644 vCert-6.1.1-20260401/vCert.py diff --git a/fixcerts_3_2.py b/fixcerts_3_2.py new file mode 100644 index 0000000..aaca52e --- /dev/null +++ b/fixcerts_3_2.py @@ -0,0 +1,2433 @@ +#!/usr/bin/python +# Run this from the PSC/VC to replace the Certificates using VMCA +# +# Main purpose of this script is to quickly replace the Expired Certificates on vCenter Server with minimal manual intervention. +# +# This script is capable of performing following certificate replacement tasks on VCSA based on the CLI arguments passed +# 1: Reset all Certificates on vCenter Server (including VMCA Root, Machine SSL, STS, Solution Users, Data-enciphement, SMS and Lookupservice certificate) +# 2: Replace Secure Token Signing (STS) Certificate +# 3: Replace Machine SSL Certificate +# 4: Replace all Solution User Certificates +# 5: Replace data-encipherment Certificate +# 6: Replace Expired SMS Certificate +# +# This script also has the following advanced functionalities: +# 1. Customize the Certificate Key Size (2048/3072/4096) based on environment specific security rules +# 2. Execute the replacement Silently without requesting for any user input, might help to schedule the operation +# 3. Customize the Certificate Validity (Between 1 day and 10 years) +# 4. Add additional FQDNs in Machine SSL Certificate +# 5. Remove Non-CA Certificates from TRUSTED_ROOTS store +# 6. Update thumbprint of vpxd extensions (eam, rbd imagebuilder) +# +# Prerequisite for executing the script +# 1: Offline snapshots of VCs/PSCs in same vSphere Domain, this is required for the VC rollback in case required +# 2: SSO Admin Password + +VERSION = "3.2-2024-12-11" + +import sys +import os +import getpass +import tempfile +import time +import re +import logging +import subprocess +import argparse +import textwrap as _textwrap +import traceback +import shutil +from prettytable import PrettyTable +from logging.handlers import RotatingFileHandler +start_time = time.time() + +from OpenSSL import crypto +sys.path.append(os.environ['VMWARE_PYTHON_PATH']) +sys.path.append('/usr/lib/vmware-vmafd/lib64') +from datetime import datetime +import vmafd +from cis.utils import * +from cis.defaults import * +try: + from cis.tools import get_install_parameter + from cis.exceptions import InstallParameterException +except ImportError: + class InstallParameterException(Exception): + """Imitates missing InstallParameterException class""" + +""" +Constants class stores the multiple variables which are needed for Certificate Replacement +Such as Certool path, VMCA Root certificate location, SSO Admin Username, VECS store names etc +""" +class constants: + vmca_root_path = "/var/lib/vmware/vmca/root.cer" + vmca_key_path = "/var/lib/vmware/vmca/privatekey.pem" + _CERT_TOOL = "/usr/lib/vmware-vmca/bin/certool" + _OPENSSL = "/bin/openssl" + _LDAPSEARCH="/usr/bin/ldapsearch" + _LDAPDELETE="/usr/bin/ldapdelete" + _LDAPMODIFY="/usr/bin/ldapmodify" + _VECS_CLI = "/usr/lib/vmware-vmafd/bin/vecs-cli" + _VMAFD_CLI = "/usr/lib/vmware-vmafd/bin/vmafd-cli" + _DIR_CLI = "/usr/lib/vmware-vmafd/bin/dir-cli" + _VMON_CLI = 'vmon-cli' + _VMAFD_Service = "vmafdd" + _VMCAD_Service = "vmcad" + _VMDIRD_Service = "vmdird" + _vPostgres_Service = "vpostgres" + if sys.version_info[0] < 3: + inputfunction = raw_input + else: + inputfunction = input + isLinux = os.name == 'posix' + _SERVICE_CTL = 'service-control' + store_names = ["machine", "vpxd", "vpxd-extension", "vsphere-webclient", "wcp", "hvc"] + extensions = ["com.vmware.vim.eam", "com.vmware.rbd", "com.vmware.imagebuilder"] + result_directory = tempfile.mkdtemp(prefix="fixcerts-") + _CERT_TOOL_CFG = result_directory + "/certool_default.cfg" + logfile_name = "fixcerts.log" + mincertvalidity = 365 + silent_execution = False + cert_replaced = False + auto_service_restart = False + additional_san = False + use_openssl_functions = False + MAX_CERT_VALIDITY = 3651 + DEFAULT_STS_VALIDITY = 3651 + DEFAULT_KEY_SIZE = "2048" + DEFAULT_VALIDITY = 730 + custom_validity = False + BACKUP_STORE = "BACKUP_STORE" + force_encipherment_cert = False + VCHA_CFG_FILE_PATH = '/etc/vmware-vcha/vcha.cfg' + replace_only_sms_roots = False + expired_vmca = expired_machinessl = expired_sts = expired_solutionusers = expired_dataencipherment = expired_lookupservice = expired_trustedroots = expired_sms = False + replace_only_lookupservice = False + remove_only_trusted_roots = False + services_start_flag = False + wait_time = "" + remove_nonca_trusted_roots = False + update_only_vpxd_extensions = False + extension_type = [] + +class environment: + ssopassword="" + ldapssopassword="" + DOMAIN = "" + DOMAINCN = "" + DCNAME = "" + PNID = "" + SITENAME = "" + hostname_type = "" + deployment_type = "" + Machine_ID = "" + additional_fqdns = "" + +class LineWrapRawTextHelpFormatter(argparse.RawDescriptionHelpFormatter): + """ + Utility to properly display help text with proper line wrapping. + """ + def _split_lines(self, text, width): + text = self._whitespace_matcher.sub(' ', text).strip() + return _textwrap.wrap(text, width) + +""" +VecsCli Class handles the functions needed for managing VECS Stores, which are handled by the vecs-cli utility. +Such as list the certificate in specific VECS store (eg. vpxd), export certificate and key from stores, +Deletion and creation of certificates in stores. +""" +class VecsCli(object): + def __init__(self): + self.vecs_cli = constants._VECS_CLI + + def list_stores(self): + cmd = [self.vecs_cli, + 'store', + 'list' + ] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def create_store(self,store_name): + cmd = [self.vecs_cli, + 'store', + 'create', + '--name', store_name + ] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def list_certs(self,store): + cmd = [self.vecs_cli, 'entry', 'list', '--store', store] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def list_certs_text(self,store): + cmd = [self.vecs_cli, 'entry', 'list', '--store', store, '--text'] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def get_cert_text(self, store, alias): + cmd = [self.vecs_cli, + 'entry', 'getcert', '--store', store, + '--alias', alias, '--text'] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def get_cert_tofile(self, store, alias,filename): + cmd = [self.vecs_cli, + 'entry', 'getcert', '--store', store, + '--alias', alias, '--output',filename] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def get_key_tofile(self, store, alias,filename): + cmd = [self.vecs_cli, + 'entry', 'getkey', '--store', store, + '--alias', alias, '--output',filename] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def delete_cert(self, store, alias): + cmd = [self.vecs_cli, + 'entry', 'delete', '--store', + store, '--alias', alias,'-y'] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def create_cert(self, store, alias, cert_name, key_name): + cmd = [self.vecs_cli, + 'entry', 'create', '--store', store, + '--alias', alias, '--cert', cert_name, + '--key', key_name] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + +""" +DirCli Class handles the functions handled by the dir-cli utility to interact wit VMDIR database. +These operations include, updating solution user certificate (service update), listing solution users (service list), +and, reading the state of VMDIR DB (such as Standalone, Read Only, Normal etc.) +""" +class DirCli(object): + def __init__(self): + self.dir_cli = constants._DIR_CLI + + def service_update(self, cert_name, user, machineid, username, password): + cmd = [self.dir_cli, + 'service', 'update', '--cert', cert_name, + '--name', user + '-' + machineid, + '--login', username, '--password', password] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def service_list(self, username, password): + cmd = [self.dir_cli, + 'service', 'list', + '--login', username, '--password', password] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def trustedcert_unpublish(self, certfile_path, username, password): + cmd = [self.dir_cli, + 'trustedcert', 'unpublish', '--cert', certfile_path, + '--login', username, '--password', password] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def vmdir_state_get(self, username, password): + cmd = [self.dir_cli, + 'state', 'get', + '--login', username, '--password', password] + (code, result, err) = execute_cmd(cmd, False, None) + if code == 0: + return code, result, err + else: + cmd = "echo 6 | /usr/lib/vmware-vmdir/bin/vdcadmintool" + (code, result, err) = execute_cmd(cmd, True, None) + return code, result, err + +""" +Certool Class handles certain functions managed by the 'certool' utility +These operations include, generating new private key, generating certificate using configuration file, +and, generating certificate using specific values for field such as Country, State, Locality etc. +""" +class Certool(object): + def __init__(self): + self.certool = constants._CERT_TOOL + self.certool_cfg = constants._CERT_TOOL_CFG + + def gen_key(self, privkey_name, pubkey_name, dcname): + cmd = [self.certool, + '--genkey', '--privkey=' + privkey_name, + '--pubkey=' + pubkey_name, + '--server=' + dcname] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def gen_cert_from_cfg(self,cert_file, privkey_name, cfg_file_name,dcname): + # Not used as parameters are passed in-line with gen_cert function + cmd = [self.certool, + '--gencert', '--privkey=' + privkey_name, + '--cert=' + cert_file, + '--server=' + dcname, + '--config', cfg_file_name] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def add_additional_fqdn(self, hostname_string): + for additional_fqdn in environment.additional_fqdns: + if additional_fqdn.lower() != environment.PNID.lower(): + hostname_string += "," + hostname_string += additional_fqdn + return hostname_string + + def gen_cis_cert(self, cis_cert_cn, cert_file, privkey_name, dcname): + cmd = [self.certool, + '--genCIScert', + '--privkey=' + privkey_name, + '--cert=' + cert_file, + '--server=' + dcname, + '--Name=' + cis_cert_cn, + '--config', self.certool_cfg] + if environment.hostname_type == 'fqdn': + cmd.append('--Hostname=' + environment.PNID) + elif environment.hostname_type in ['ipv4','ipv6']: + cmd.append('--IPAddress=' + environment.PNID) + if cis_cert_cn == 'data-encipherment': + cmd.append('--dataencipherment') + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def gen_cert(self, cert_file, privkey_name, dcname, CN, C, O, OU, ST, L): + cmd = [self.certool, + '--gencert', '--privkey=' + privkey_name, + '--cert=' + cert_file, + '--server=' + dcname, + '--Name', CN, + '--Country', C, + '--Organization=' + O, + '--OrgUnit=' + OU, + '--State=' + ST, + '--Locality=' + L, + '--config', self.certool_cfg] + if environment.hostname_type == 'fqdn': + hostnames = environment.PNID + if environment.additional_fqdns and "MACHINE_SSL_CERT" in cert_file: + hostnames = self.add_additional_fqdn(hostnames) + cmd.append('--Hostname=' + hostnames) + else: + cmd.append('--Hostname=' + environment.PNID) + elif environment.hostname_type in ['ipv4','ipv6']: + cmd.append('--IPAddress=' + environment.PNID) + if environment.additional_fqdns and "MACHINE_SSL_CERT" in cert_file : + hostnames="" + hostnames = self.add_additional_fqdn(hostnames) + hostnames.lstrip(",") + cmd.append('--Hostname=' + hostnames) + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + +""" +Class for openssl commands to convert Certificate/Key to DER format +Also, it helps to identify sha1 fingerprint of Certificate +""" +class OpensslCli(object): + def __init__(self): + self.openssl_cli = constants._OPENSSL + + def convert_cert_der(self, cert_path, cert_out_path): + cmd = [self.openssl_cli, + 'x509', '-outform', 'der', + '-in', cert_path, + '-out', cert_out_path] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def convert_key_der(self, key_path, key_out_path): + cmd = [self.openssl_cli, + 'pkcs8', '-topk8', '-inform', 'pem', + '-outform', 'der', + '-in', key_path, + '-out', key_out_path, + '-nocrypt'] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def get_fingerprint(self,cert): + cmd = [self.openssl_cli, + 'x509', '-noout', '-fingerprint', + '-sha1','-inform', 'pem', + '-in', cert] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def gen_key(self, csr_path, key_path, cfg_path, key_size): + cmd = [self.openssl_cli, + 'req', '-new', '-nodes', '-out', csr_path, + '-newkey', 'rsa:'+key_size, + '-keyout', key_path, + '-config', cfg_path] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + + def gen_cert(self, validity_days, csr_path, cert_path, root_ca_path, root_key_path, cfg_path): + cmd = [self.openssl_cli, + 'x509', '-req', '-days', str(validity_days), + '-in', csr_path, + '-out', cert_path, + '-CA', root_ca_path, + '-CAkey', root_key_path, + '-extensions', 'v3_req', + '-CAcreateserial', + '-extfile', cfg_path] + (code, result, err) = execute_cmd(cmd, False, None) + return code, result, err + +""" +CertCfg Class initializes the values needed for creating a certificate, such as Country, State, Locality, Organization, OrgUnit etc. +__init__ -> Initialize the default CFG file which are available in certool.cfg +read_cert_fields -> Input functions to read the value if Default field values needs to be changed +initialize_cert_fields -> This function reads the value of existing Machine SSL Certificates and initializes the variable with that values +create_cert_cfg -> Create Configuration file with the Customized fields +""" +class CertCfg(object): + def __init__(self): + self.Country = self.Organization = self.OrgUnit = self.Locality = self.State = self.sanhostname = self.sanip = None + defaultcfg = """ + # + # Template file for a CSR request + # + + # Country is needed and has to be 2 characters + Country = US + Name = CA + Organization = VMware + OrgUnit = VMware Engineering + State = California + Locality = Palo Alto + #IPAddress = 127.0.0.1 + #Email = email@acme.com + #Hostname = server.acme.com + """ + if not os.path.exists(constants.result_directory): + os.makedirs(constants.result_directory) + + with open(constants._CERT_TOOL_CFG, "w") as text_file: + text_file.write(defaultcfg) + text_file.close() + + def read_cert_fields(self): + self.Country = constants.inputfunction("\nPlease enter value for Country (should be Two letter): ") + while not len(self.Country.strip()) == 2: + print("Enter valid 2 letter country code, example 'US'") + self.Country = constants.inputfunction("\nPlease enter value for Country (should be Two letter): ") + Organization = constants.inputfunction("\nPlease enter value for Organization (Default - " + self.Organization + "): ") + if Organization: + self.Organization = Organization + OrgUnit = constants.inputfunction("\nPlease enter value for OrgUnit (Default - " + self.OrgUnit + "): ") + if OrgUnit: + self.OrgUnit = OrgUnit + State = constants.inputfunction("\nPlease enter value for State (Default - " + self.State + "): ") + if State: + self.State = State + Locality = constants.inputfunction("\nPlease enter value for Locality (Default - " + self.Locality + "): ") + if Locality: + self.Locality = Locality + + def initialize_cert_fields(self): + (code, result, err) = vecs_ops.get_cert_tofile("MACHINE_SSL_CERT", "__MACHINE_CERT", constants.result_directory+"/old_machine_ssl.crt") + old_machine_ssl = get_x509_from_file(constants.result_directory + "/old_machine_ssl.crt") + if old_machine_ssl.get_subject().C: + self.Country = old_machine_ssl.get_subject().C + else: + self.Country = "US" + if old_machine_ssl.get_subject().O: + self.Organization = old_machine_ssl.get_subject().O + else: + self.Organization = "VMware" + if old_machine_ssl.get_subject().OU: + self.OrgUnit = old_machine_ssl.get_subject().OU + else: + self.OrgUnit = "VMware Engineering" + if old_machine_ssl.get_subject().ST: + self.State = old_machine_ssl.get_subject().ST + else: + self.State = "California" + if old_machine_ssl.get_subject().L: + self.Locality = old_machine_ssl.get_subject().L + else: + self.Locality = "Palo Alto" + if environment.hostname_type == 'fqdn': + self.sanhostname = environment.PNID + elif environment.hostname_type == 'ipv4': + self.sanip = environment.PNID + elif environment.hostname_type == 'ipv6': + self.sanip = environment.PNID + print("\nFollowing are the Certificate Fields based on existing Machine SSL Certificate :") + print("Country\t\t: %s\nOrganization\t: %s\nOrgUnit\t\t: %s\nState\t\t: %s\nLocality\t: %s" %(self.Country,self.Organization,self.OrgUnit,self.State,self.Locality)) + if (constants.silent_execution): + logging.info("This is a Silent execution, so proceeding with the default Certificate Fields fo Country, State, Locality etc.") + else: + configchange = constants.inputfunction("\nDo you want to proceed with the default values mentioned above ? please enter YES/NO [[Yes/yes/YES/Y/y] or [No/no/NO/N/n]] ? ") + if configchange.lower() in ['n','N','No','NO','no','nO']: + logging.info("Reading the Certificate fields based on User Input") + self.read_cert_fields() + else: + logging.info("Proceeding with the default values based based on User Input") + + def create_cert_cfg(self,cfg_file_name): + cfg_String=""" + Country = {0} + Name = {1} + Organization = {2} + OrgUnit = {3} + Locality = {4} + State = {5} + """ + if environment.hostname_type == 'fqdn': + cert_san_field = ("Hostname = {0}".format(environment.PNID)) + elif environment.hostname_type == 'ipv4': + cert_san_field = ("IPAddress = {0}".format(environment.PNID)) + elif environment.hostname_type == 'ipv6': + cert_san_field = ("IPAddress = {0}".format(environment.PNID)) + cert_cfg_string = cfg_String.format(self.Country, "VMCA", self.Organization, self.OrgUnit, self.State, self.Locality) + cert_cfg_string = cert_cfg_string + cert_san_field + with open(cfg_file_name, "w") as text_file: + text_file.write(cert_cfg_string) + text_file.close() + return True + + def create_cert_cfg_openssl(self,openssl_cfg_file_path,CN,org_unit=None): + cfg_String="""[ req ] +distinguished_name = req_distinguished_name +encrypt_key = no +prompt = no +string_mask = nombstr +x509_extensions = v3_req +req_extensions = v3_req +[ v3_req ] +basicConstraints = CA:false +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectKeyIdentifier=hash +subjectAltName = DNS:{0} +[ req_distinguished_name ] +countryName = {1} +stateOrProvinceName = {2} +localityName = {3} +0.organizationName = {4} +organizationalUnitName = {5} +commonName = {6}""" + if not org_unit: + org_unit = self.OrgUnit + if environment.hostname_type in ['ipv4','ipv6']: + cfg_String = cfg_String.replace("DNS:","IP:") + if environment.additional_fqdns and "MACHINE_SSL_CERT" in openssl_cfg_file_path: + hostname_string = environment.PNID + for additional_fqdn in environment.additional_fqdns: + if additional_fqdn.lower() != environment.PNID.lower(): + hostname_string += ", DNS:" + hostname_string += additional_fqdn + else: + hostname_string = environment.PNID + cert_cfg_string = cfg_String.format(hostname_string, self.Country, self.State, self.Locality, self.Organization, org_unit,CN) + with open(openssl_cfg_file_path, "w") as text_file: + text_file.write(cert_cfg_string) + text_file.close() + return True + + def add_authKey_in_cfg(self,openssl_cfg_file_path): + string_to_search = 'authorityKeyIdentifier=keyid,issuer' + string_to_replace = 'subjectKeyIdentifier=hash' + new_string = 'subjectKeyIdentifier=hash' + "\n" + 'authorityKeyIdentifier=keyid,issuer' + file_handle = open(openssl_cfg_file_path, 'r') + file_contents = file_handle.read() + file_handle.close() + if string_to_search not in file_contents: + file_contents = (re.sub(string_to_replace, new_string, file_contents)) + file_handle = open(openssl_cfg_file_path, 'w') + file_handle.write(file_contents) + file_handle.close() + return True + +""" +Class for vmafd client operationsert. +__init__ -> Tries to start VMAFD service and initialize vsphere domain variables using vmafd client +get_machine_id -> Reads the machine id of vCenter Server using vmafd-cli command +""" +class vmafdClient(object): + def __init__(self): + self.run_commands([constants._SERVICE_CTL, '--start',constants._VMAFD_Service], quiet=True) + + self.client = vmafd.client('localhost') + + try: + self.client.GetStatus() + except RuntimeError as e: + logging.error("VMAFD Service failed to Start, this Service needs to be in Running state to proceed with Certificate Replacement. Please manually start 'vmafd' service and retry the script") + print("VMAFD Service failed to Start, this Service needs to be in Running state to proceed with Certificate Replacement. Please manually start 'vmafd' service and retry the script") + print("......Failed\n") + + environment.DOMAIN = self.client.GetDomainName() + environment.DOMAINCN = ("dc=" + environment.DOMAIN).replace(".",",dc=") + environment.DCNAME = self.client.GetDCName() + environment.PNID = self.client.GetPNID() + environment.SITENAME = self.client.GetSiteName() + environment.Machine_ID = self.get_machine_id() + + def get_machine_id(self): + vmafd_cli_path = "/usr/lib/vmware-vmafd/bin/vmafd-cli" + code, stdout, stderr = self.run_commands([vmafd_cli_path, 'get-machine-id','--server-name', 'localhost'], quiet=True) #.strip() + if code == 0: + return stdout.strip() + else: + if 'Could not connect to VMware Directory Service' in stderr: + try: + host_id = get_install_parameter('sca.hostid', quiet=True) + return host_id + except Exception as e: + print(color_red("Unable to get Machine ID of vCenter Server, please make sure VMAFD and VMDIRD services are in Running state to proceed with Certificate Replacement. Please manually start 'vmafdd' and 'vmdird' service and retry the script")) + print(color_red("......Failed\n")) + sys.exit(1) + else: + print(color_red("Unable to get Machine ID of vCenter Server, please make sure VMAFD and VMDIRD services are in Running state to proceed with Certificate Replacement. Please manually start 'vmafdd' and 'vmdird' service and retry the script")) + print(color_red("......Failed\n")) + sys.exit(1) + + def run_commands(self, cmd, quiet=False, failuremsg=""): + ret, stdout, stderr = run_command(cmd, quiet=quiet) + return(ret, stdout, stderr) + +""" +Initializing logging +by default creating a log file with name fixcerts.log which is defined in constants class +It creates the log file in the same directory where the script is stored +""" +def setup_logging(): + logging.basicConfig(handlers=[ + RotatingFileHandler( + constants.logfile_name, + maxBytes=5120000, + backupCount=1 + ) + ], + level=logging.INFO, + format='%(asctime)s %(levelname)s %(message)s' + ) + logging.warning('***** Starting Execution of fixcerts script *****') + +""" +Color functions mentioned below are used to change text result to Green and Red +Specifically used to print Success and Failure during each operation +""" +def parse_arguments(): + fixcerts_examples = '''Examples: + + #To replace ONLY EXPIRED Certificates, please use below option. + python fixcerts.py replace --certType expired_only + python fixcerts.py replace --certType expired_only --serviceRestart True (To restart all the services automatically post certificate replacement) + python fixcerts.py replace --certType expired_only --additionalSAN fqdn1,fqdn2 (if multiple hostnames are required in SAN, provide comma separated values for multiple FQDNs) + python fixcerts.py replace --certType expired_only --silent True --password "" --serviceRestart True|False (for silent replacement without any user inputs) + python fixcerts.py replace --certType expired_only --validityDays (To Customize the certificate validity, by default VMCA signs certificate with 2 year validity) + python fixcerts.py replace --certType expired_only --keySize <2048/3072/4096> (To Customize the Key Length, by default VMCA signs certificate with 2048 as key size) + + #To replace all the Certificates on vCenter Server such as VMCA Root, Machine SSL, STS, Solution Users etc.. + python fixcerts.py replace --certType all + python fixcerts.py replace --certType all --serviceRestart True (To restart all the services automatically post certificate replacement) + python fixcerts.py replace --certType all --additionalSAN fqdn1,fqdn2 (if multiple hostnames are required in SAN, provide comma separated values for multiple FQDNs) + python fixcerts.py replace --certType all --silent True --password "" --serviceRestart True|False (for silent replacement without any user inputs) + python fixcerts.py replace --certType all --validityDays (To Customize the certificate validity, by default VMCA signs certificate with 2 year validity) + python fixcerts.py replace --certType all --keySize <2048/3072/4096> (To Customize the Key Length, by default VMCA signs certificate with 2048 as key size) + + #To replace VMCA Root Certificate and all other Certificates + python fixcerts.py replace --certType root + python fixcerts.py replace --certType root --serviceRestart True (To restart all the services automatically post certificate replacement) + python fixcerts.py replace --certType root --additionalSAN fqdn1,fqdn2 (if multiple hostnames are required in SAN, provide comma separated values for multiple FQDNs) + python fixcerts.py replace --certType root --silent True --password "" --serviceRestart True|False (for silent replacement without any user inputs) + python fixcerts.py replace --certType root --validityDays (To Customize the certificate validity, by default VMCA signs certificate with 2 year validity) + python fixcerts.py replace --certType root --keySize <2048/3072/4096> (To Customize the Key Length for leaf certificates (eg. machine ssl cert), by default VMCA signs certificate with 2048 as key size) + + #To replace only MACHINE_SSL_CERT Certificate + python fixcerts.py replace --certType machinessl + python fixcerts.py replace --certType machinessl --serviceRestart True (To restart all the services automatically post certificate replacement) + python fixcerts.py replace --certType machinessl --additionalSAN fqdn1,fqdn2 (if multiple hostnames are required in SAN, provide comma separated values for multiple FQDNs) + python fixcerts.py replace --certType machinessl --silent True --password "" --serviceRestart True|False (for silent replacement without any user inputs) + python fixcerts.py replace --certType machinessl --validityDays (To Customize the certificate validity, by default VMCA signs certificate with 2 year validity) + python fixcerts.py replace --certType machinessl --keySize <2048/3072/4096> (To Customize the Key Length, by default VMCA signs certificate with 2048 as key size) + + #To replace only STS (Signing Certificate) Certificate, this is stored in VMDIR Database + python fixcerts.py replace --certType sts + python fixcerts.py replace --certType sts --serviceRestart True (To restart all the services automatically post certificate replacement) + python fixcerts.py replace --certType sts --silent True --password "" --serviceRestart True|False (for silent replacement without any user inputs) + python fixcerts.py replace --certType sts --validityDays (To Customize the certificate validity, by default VMCA signs certificate with 2 year validity) + python fixcerts.py replace --certType sts --keySize <2048/3072/4096> (To Customize the Key Length, by default VMCA signs certificate with 2048 as key size) + + #To replace only Solution User Certificates such as vpxd, vpxd-extension, machine etc.. + python fixcerts.py replace --certType solutionusers + python fixcerts.py replace --certType solutionusers --serviceRestart True (To restart all the services automatically post certificate replacement) + python fixcerts.py replace --certType solutionusers --silent True --password "" --serviceRestart True|False (for silent replacement without any user inputs) + python fixcerts.py replace --certType solutionusers --validityDays (To Customize the certificate validity, by default VMCA signs certificate with 2 year validity) + python fixcerts.py replace --certType solutionusers --keySize <2048/3072/4096> (To Customize the Key Length, by default VMCA signs certificate with 2048 as key size) + + #To replace only data-encipherment Certificate if the store is available and cert is expired + python fixcerts.py replace --certType data-encipherment + python fixcerts.py replace --certType data-encipherment --serviceRestart True (To restart all the services automatically post certificate replacement) + python fixcerts.py replace --certType data-encipherment --silent True --password "" --serviceRestart True|False (for silent replacement without any user inputs) + python fixcerts.py replace --certType data-encipherment --force_encipherment_replace True|False (by default script will replace the data-enciphement cert only if it is expired, use the force switch if you want to override) + + #To replace only LookupService Certificate if a separate STS_INTERNAL_SSL_CERT store is available + python fixcerts.py replace --certType lookupservice + python fixcerts.py replace --certType lookupservice --serviceRestart True (To restart all the services automatically post certificate replacement) + python fixcerts.py replace --certType lookupservice --additionalSAN fqdn1,fqdn2 (if multiple hostnames are required in SAN, provide comma separated values for multiple FQDNs) + python fixcerts.py replace --certType lookupservice --silent True --password "" --serviceRestart True|False (for silent replacement without any user inputs) + python fixcerts.py replace --certType lookupservice --validityDays (To Customize the certificate validity, by default VMCA signs certificate with 2 year validity) + python fixcerts.py replace --certType lookupservice --keySize <2048/3072/4096> (To Customize the Key Length, by default VMCA signs certificate with 2048 as key size) + + #To replace expired Certificates from SMS store + python fixcerts.py replace --certType sms + python fixcerts.py replace --certType sms --serviceRestart True (To restart all the services automatically post certificate replacement) + + #To remove expired Certificates from TRUSTED_ROOTS store, if any + python fixcerts.py remove --storeType trusted_roots --certType expired + python fixcerts.py remove --storeType trusted_roots --certType expired --serviceRestart True (To restart all the services automatically after the operation) + + #To remove Non-CA Certificates from TRUSTED_ROOTS store, if any + python fixcerts.py remove --storeType trusted_roots --certType non-ca + python fixcerts.py remove --storeType trusted_roots --certType non-ca --serviceRestart True (To restart all the services automatically after the operation) + + #To update certificate for vpxd extensions such as eam, rbd and imagebuilder + python fixcerts.py update --ExtensionType all + python fixcerts.py update --ExtensionType eam + python fixcerts.py update --ExtensionType eam --serviceRestart True (To restart all the services automatically after the operation) + + #For help + python fixcerts.py --help''' + + commands = argparse.ArgumentParser(description='vCenter Certificate Replacement Tool %s' % VERSION, formatter_class= + LineWrapRawTextHelpFormatter, + epilog=fixcerts_examples) + + commands.add_argument("replace", help="Replace the Certificates on vCenter Server, add the type argument for the Certificate to replace", nargs='?') + commands.add_argument("--certType", help="Which Certificate to replace, can be any of these values [machinessl | lookupservice | solutionusers | root | all]") + commands.add_argument("--serviceRestart", help="This argument is used to restart the services automatically, accepts True or False") + commands.add_argument("--additionalSAN", nargs='+', help="This argument is used to add multiple Hostnames in Subject Alternative Names of Certificate") + commands.add_argument("--silent", help="This can be used to replace the certificates silently, accepts values True or False, CAUTION: It will not ask for any user Input, will proceed with all default values. Also, services needs to be restarted manually post Certificate replacement") + commands.add_argument("--password", help="SSO Administrator password, this argument is used in conjunction with --silent argument") + commands.add_argument("--validityDays", help="Certificate validity in days and maximum value is 10 years (3650), by default VMCA signs certificates for 2 years. This Argument will work ONLY on Embedded or Exteral PSC Nodes") + commands.add_argument("--keySize", type=int, help="This specifies the Certificate Key Size, accepts values 2048/3072 & 4096 and by default VMCA uses 2048 as key size. This Argument will work ONLY on Embedded or Exteral PSC Nodes") + commands.add_argument("--force_encipherment_replace", help="By default script will replace the data-enciphement cert if it is expired, use the force switch if you want to override") + commands.add_argument("--storeType", help="To Remove expired Certificates from TRUSTED_ROOTS store, 'replace' all will by default perform this action as well from Trusted Roots store") + commands.add_argument("--debug", help="Enable Debug logging", action="store_true") + commands.add_argument("remove", help="Remove Certificates from TRUSTED_ROOTS on vCenter Servers on vCenter Server, add the storeType for the store name",nargs='?') + commands.add_argument("update", help="To update the Thumbprint for VPXD Extensions",nargs='?') + commands.add_argument("--ExtensionType", help="To update thumbprint of vpxd-extensions, such as eam, rbd and imagebuilder") + return commands + +def color_green(input_string): + OKGREEN = '\033[92m' + ENDC = '\033[0m' + new_string = OKGREEN + input_string + ENDC + return new_string + +def color_red(input_string): + OKRED = '\033[91m' + ENDC = '\033[0m' + new_string = OKRED + input_string + ENDC + return new_string + +def color_cyan(input_string): + OKRED = '\033[96m' + ENDC = '\033[0m' + new_string = OKRED + input_string + ENDC + return new_string + +""" +This function utilizes subprocess library in python to execute an external program +As certificate replacement involves executing multiple utilities, +And, execute command function is utilized by almost all the commands in the script +""" +def execute_cmd(cmd, shellvalue=True, stdin=None, quiet=False): + p = None + log_cmd = str(cmd) + if environment.ssopassword in log_cmd: + log_cmd = log_cmd.replace(environment.ssopassword,"*******") + logging.info("Running command :- " + log_cmd) + elif environment.ldapssopassword in log_cmd: + log_cmd = log_cmd.replace(environment.ldapssopassword,"*******") + logging.info("Running command :- " + log_cmd) + else: + logging.info("Running command :- " + log_cmd) + p = subprocess.Popen(cmd, shell=shellvalue, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (stdout, stderr) = p.communicate() + if (p.returncode != 0): + logging.error("Return Code - %s" %p.returncode) + logging.error("STDOUT - %s" %stdout) + logging.error("STDERR - %s" %stderr) + else: + logging.debug("Return Code - %s" %p.returncode) + logging.debug("STDOUT - %s" %stdout) + logging.debug("STDERR - %s" %stderr) + logging.info("Done running command") + return p.returncode, stdout, stderr + +""" +Check unsupported scenarios like VCHA Configured vCenter Server +""" +def unsupported_scenario(): + if os.path.exists(constants.VCHA_CFG_FILE_PATH): + logging.info("Found VCHA Config files, script does not support execution on VCHA nodes") + print(color_red("\nFound VCHA Config file %s, this script does not support execution on VCHA nodes.\n" %constants.VCHA_CFG_FILE_PATH)) + return True + else: + return False + +""" +Verifies the need to restart the services +""" +def check_service_restart(): + #Messaging after successful replacement of Certificate + services_stop_flag = False + if (not constants.auto_service_restart) and (constants.cert_replaced) and (not constants.silent_execution): + restart_services_flag = constants.inputfunction("\nServices needs to be restarted after cert replacement, please enter Yes to restart the services [[Yes/Y/y]] ? ") + if restart_services_flag.lower() in ['y','Y','Yes','YES','yes','yES','yeS']: + constants.auto_service_restart = True + logging.info("Enabling Auto Service Restart based on user input") + if (constants.cert_replaced) and constants.auto_service_restart: + print("\nStopping All Services.") + print("...Waiting for Status") + (status, result, err) = restart_all_services("stop") + if status: + print(color_green("......Success\n")) + services_stop_flag = True + else: + print(color_red("......Failed\n")) + logging.error("Failed to Stop All Services %s - %s" % (result,err)) + + if (services_stop_flag): + print("\nStarting All Services.") + print("...Waiting for Status") + (status, result, err) = restart_all_services("start") + if status: + print(color_green("......Success\n")) + constants.services_start_flag = True + else: + print(color_red("......Failed\n")) + logging.error("Failed to Start All Services %s - %s" % (result,err)) + +""" +This function helps to restart all services +""" +def restart_all_services(action): + if action in ['stop','Stop','STOP']: + service_action = " --stop" + elif action in ['start','Start','START']: + service_action = " --start" + cmd = constants._SERVICE_CTL + service_action + ' --all ' + try: + (code, result, err) = execute_cmd(cmd, True, None) + if code != 0: + return (False,result,err) + else: + return (True,result,err) + except Exception as e: + msg = 'Error while performing all services stop/start operation : {0}'.format(e) + logging.error(msg) + return (False,result,err) + +""" +This function helps to check startup type of a service +It accepts service name as argument +""" +def check_startup_type(service_name): + cmd = constants._VMON_CLI + ' --status ' + service_name + "| grep -i Starttype" + try: + (code, service_start_type, err) = execute_cmd(cmd, True, None) + if code == 0: + if (service_start_type): + service_start_type = service_start_type.decode('utf-8') + if "AUTOMATIC" in service_start_type: + service_start_type = "AUTOMATIC" + elif "MANUAL" in service_start_type: + service_start_type = "MANUAL" + elif "DISABLE" in service_start_type: + service_start_type = "DISABLED" + else: + service_start_type = "UNKNOWN" + else: + service_start_type = "UNKNOWN" + except Exception as e: + msg = 'Error while fetching statup type of service : {0}'.format(e) + logging.error(msg) + return service_start_type + +""" +This function helps to check running state of a service +It accepts service name as argument +""" +def check_service_runstate(service_name): + cmd = constants._VMON_CLI + ' --status ' + service_name + "| grep -i RunState" + try: + (code, service_runstate, err) = execute_cmd(cmd, True, None) + if code == 0: + if (service_runstate): + service_runstate = service_runstate.decode('utf-8') + if "STARTED" in service_runstate: + service_runstate = "RUNNING" + elif "STOPPED" in service_runstate: + service_runstate = "STOPPED" + else: + service_runstate = "UNKNOWN" + else: + service_runstate = "UNKNOWN" + else: + service_runstate = "UNKNOWN" + except Exception as e: + msg = 'Error while fetching running state of service : {0}'.format(e) + logging.error(msg) + return service_runstate + +""" +This function helps to stop an individual service +It accepts service name as argument +""" +def stop_service(service_name): + cmd = constants._SERVICE_CTL + ' --stop ' + service_name + try: + (code, result, err) = execute_cmd(cmd, True, None) + if code != 0: + return False + else: + return True + except Exception as e: + msg = 'Error while stopping service : {0}'.format(e) + logging.error(msg) + return False + +""" +This function helps to start an individual service +It accepts service name as argument +""" +def start_service(service_name): + cmd = constants._SERVICE_CTL + ' --start ' + service_name + try: + (code, result, err) = execute_cmd(cmd, True, None) + if code != 0: + return False + else: + return True + except Exception as e: + msg = 'Error while starting service : {0}'.format(e) + logging.error(msg) + return False + +""" +This is utilized by the pre-check function to ensure essential services for the cert replacement are in running state +Following services needs to be in running state, this function tries to start these services +vmafdd (Authentication Framework) +vmcad (Certificate Service) +vmdird (Directory Service) +""" +def verify_required_services(): + try: + logging.info("Starting the required Services for Certificate Replacement Operation (vmcad, vmdird & vpostgres)") + if not environment.deployment_type == "management": + if not (start_service(constants._VMCAD_Service)): + return False + if not (start_service(constants._VMDIRD_Service)): + return False + if(environment.deployment_type == "management" or environment.deployment_type == "embedded"): + if not (start_service(constants._vPostgres_Service)): + return False + return True + except Exception as e: + logging.error('Failed to start services required for Certificate Replacement Operation') + logging.error(e) + return False + +""" +This function verifies the SSO Admin password entered by the Customer +It uses "dir-cli service list" command degined in dircli class to verify the password +""" +def verify_sso_pwd(): + logging.info("Verifying SSO Password") + (code, result, err) = dircli_ops.service_list("Administrator",environment.ssopassword) + if code !=0: + return False + else: + return True + +""" +This function reads the of VMDIR state +It uses "dir-cli state get " defined in dircli class +""" +def check_vmdir_state(): + logging.info("Verifying VMDIR State") + (code, result, err) = dircli_ops.vmdir_state_get("Administrator",environment.ssopassword) + if (code == 0): + logging.info("VMDIR State - %s" %result.decode('utf-8')) + return result.decode('utf-8') + else: + return(err) + +""" +Before starting the actual certificate replacement operation, a pre-check is required to make sure essential services are initialized +This pre-check function calls other functions to check the state: +- Verify the running service +- Check sso password +- Check vmdir state + if vmdir state is not Normal or Standalone, script terminates +""" +def precheck(): + print("\nDoing Pre-Check before starting the actual certificate replacement") + print("...Waiting for Status") + logging.info("Doing Pre-Check before starting the actual certificate replacement") + if not verify_required_services(): + print(color_red("These Services needs to be initialized before starting the Certificate replacement : vmcad, vmdird and vpostgres\nScript tried to start these services but some failure occured. Please start above services manually and retry the script")) + print(color_red("......Failed\n")) + exit(1) + + if not (verify_sso_pwd()): + print(color_red("Pre-Check Fails to execute \'dir-cli service list\' command, probably SSO Password you provided is wrong. Please retry the script with right SSO Admin Password")) + print(color_red("......Failed\n")) + exit(1) + + vmdir_state = check_vmdir_state() + if ("Normal" in vmdir_state) or ("Standalone" in vmdir_state): + print(color_green("......Success\n")) + else: + logging.error("VMDIR not in Normal or Standalone state to proceed with Certificate Replacement - %s" %vmdir_state) + print(color_red("VMDIRD State needs to be in Normal or Standalone to continue with Certificate Replacement, you may check the status using \'dir-cli state get or vdcadmintool\' command")) + print(color_red("......Failed\n")) + exit(1) + +""" +Reads the deployment type and hostname type from install defaults +Deployment type means Embedded/External/Management +Hostname type can be fqdn, ipv4 or ipv6 +""" +def get_deployment_parameters(): + try: + environment.hostname_type = get_install_parameter('system.hostname.type', quiet=True) + if environment.hostname_type in ['ipv4','ipv6']: + if (not re.match('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$', environment.PNID)): + environment.hostname_type = "fqdn" + try: + environment.deployment_type = get_install_parameter('deployment.node.type', quiet=True).lower().strip() + except (InstallParameterException, NameError): + try: + file = os.path.join(os.environ['VMWARE_CFG_DIR'], 'deployment.node.type') + with open(file, 'r') as file_descriptor: + environment.deployment_type = file_descriptor.read().lower().strip() + except Exception as e: + msg = 'Error while fetching install parameter deployment.node.type : {0}'.format(e) + logging.error(msg) + return False + except Exception as e: + msg = 'Error while fetching install parameter system.hostname.type : {0}'.format(e) + logging.error(msg) + return False + return True + +""" +Reads the vecs stores, it utilizes the functions in vecscli class +""" +def get_vecs_stores(): + try: + (code, result, err) = vecs_ops.list_stores() + if code != 0: + logging.info("Failed to list the VECS Stores - Error - %s" %(err.decode('utf-8'))) + return False + except Exception as e: + msg = 'Error while reading VECS Stores : {0}'.format(e) + logging.error(msg) + return False + return result.decode('utf-8') + +""" +This function is used to read the certificate in x509 format and returns the cert object +Cert object helps to read various paramenters of the cert, such as Subject, Validity etc. +""" +def get_x509_from_file(file_name): + try: + with open(file_name, 'r') as cert_file: + try: + cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert_file.read()) + return cert + except crypto.Error as e: + logging.error("Invalid PEM encoded certificate file passed as input") + raise e + except Exception as e: + logging.error("Certificate file " + file_name + " could not be found") + raise e + +""" +Set value of DEFAULT_STS_VALIDITY based on root certificate validity +And, based on the script argument +""" +def adjust_default_sts_cert_validity(): + (vmca_validity,vmca_valid_days) = check_certificate_validity(constants.vmca_root_path) + if (vmca_validity): + if constants.DEFAULT_STS_VALIDITY > vmca_valid_days.days: + constants.DEFAULT_STS_VALIDITY = vmca_valid_days.days + +""" +Set value of DEFAULT_VALIDITY based on root certificate validity +And, based on the script argument +""" +def adjust_default_cert_validity(): + (vmca_validity,vmca_valid_days) = check_certificate_validity(constants.vmca_root_path) + if (vmca_validity): + if constants.DEFAULT_VALIDITY > vmca_valid_days.days: + constants.DEFAULT_VALIDITY = vmca_valid_days.days + +""" +Check validity of Certificate +""" +def check_certificate_validity(cert_path): + logging.info("Checking Certificate Validity of - %s" %cert_path) + x509_cert = get_x509_from_file(cert_path) + certvalidity = datetime.strptime(x509_cert.get_notAfter().decode('ascii'), '%Y%m%d%H%M%SZ') + todaydate = datetime.utcnow() + diff = certvalidity - todaydate + if certvalidity: + logging.info("Certificate Validity - %s" %certvalidity) + return(certvalidity, diff) + else: + logging.warning("Couldn't identify the Certificate Validity") + return (False, False) + +""" +Check the Certificate is expired or not +""" +def is_cert_expired(certificate_path): + logging.info("Checking Expired Status of the Certificate - %s" %certificate_path) + (cert_validity, cert_valid_days) = check_certificate_validity(certificate_path) + if cert_validity: + if cert_valid_days.days < 0: + logging.warning("Cert %s is Expired" %certificate_path) + return True + else: + logging.info("Cert %s is Not Expired" %certificate_path) + return False + +""" +Function to replace the VMCA Root Certificate +It will replace the root certificate only if its validity is less than 2 years +If the validity is less than 2 years, it asks for confirmation (user input) before proceeding with the replacement +It is not mandatory to replace VMCA root certificate, user can decide it based on the validity +""" +def replace_root_certificate(): + print("Replacing Root Certificate.\n...Waiting for Status") + logging.info("Replacing Root Certificate") + if not (certcfg_ops.create_cert_cfg(constants.result_directory+"/vmca.cfg")): + logging.error("Creating configuration file %s/vmca.cfg failed!" %constants.result_directory) + print(color_red("Creating configuration file %s/vmca.cfg failed!" %constants.result_directory)) + sys.ext() + cmd = constants._CERT_TOOL + ' --genselfcacert --outprivkey ' + constants.result_directory + '/vmcacert.key --outcert ' + constants.result_directory + '/vmcacert.crt --config ' + constants.result_directory + '/vmca.cfg ' + (code, result, err) = execute_cmd(cmd, True, None) + if code == 0: + logging.info('Successfully Created New Root Certificate') + else: + logging.error('Generating new VMCA root certificate failed! - ' + err) + print(color_red("......Failed\n")) + raise Exception("Generating new VMCA root certificate failed! - %s" %err) + + cmd = constants._CERT_TOOL + ' --rootca --cert ' + constants.result_directory + '/vmcacert.crt --privkey ' + constants.result_directory + '/vmcacert.key ' + (code, result, err) = execute_cmd(cmd, True, None) + if code == 0: + logging.info('Successfully replaced VMCA Root Certificate') + constants.cert_replaced = True + if (constants.custom_validity): + adjust_default_cert_validity() + print(color_green("......Success\n")) + else: + logging.error('Replacing VMCA root certificate failed! - ' + err) + print(color_red("......Failed\n")) + raise Exception("Replacing VMCA root certificate failed! - %s" %err) + +""" +Check validity of existing STS Certificate +""" +def check_sts_certificate(): + logging.info("Checking STS Certificate Validity") + cmd = constants._LDAPSEARCH + ' -o ldif-wrap=no -h localhost -p 389 -b "cn={0},cn=Tenants,cn=IdentityManager,cn=Services,{1}" -D "cn=administrator,cn=users,{1}" -w "{2}" "(objectclass=vmwSTSTenantCredential)" dn | grep -i "dn: cn=TenantCredential" '.format( + environment.DOMAIN, environment.DOMAINCN, environment.ldapssopassword) + (code, result, err) = execute_cmd(cmd, True, None) + if result: + tenantcredentials = (result.decode('utf-8').strip().split('\n')) + tenantcredentials_sorted = sorted(tenantcredentials, key=lambda s: int(re.search(r'\d+', s).group())) + tenantcredentials_active = tenantcredentials_sorted[-1].replace("dn: ","") + cmd = constants._LDAPSEARCH + ' -o ldif-wrap=no -h localhost -p 389 -b "{0}" -D "cn=administrator,cn=users,{1}" -w "{2}" "(objectclass=vmwSTSTenantCredential)" | grep "userCertificate::" '.format( + tenantcredentials_active, environment.DOMAINCN, environment.ldapssopassword) + (code, tenantcred_result, err) = execute_cmd(cmd, True, None) + if tenantcred_result: + tenantcerts = (tenantcred_result.decode('utf-8').strip().split('\n')) + sts_cert = tenantcerts[0].replace("userCertificate:: ","") + sts_cert = re.sub("(.{64})", "\\1\n", sts_cert, 0, re.DOTALL) + sts_cert = "-----BEGIN CERTIFICATE-----\n" + sts_cert + "\n-----END CERTIFICATE-----" + sts_cert = re.sub(r'\n\s*\n','\n',sts_cert,re.MULTILINE) + sts_cert_obj = crypto.load_certificate(crypto.FILETYPE_PEM, sts_cert) + certvalidity = datetime.strptime(sts_cert_obj.get_notAfter().decode('ascii'), '%Y%m%d%H%M%SZ') + todaydate = datetime.utcnow() + diff = certvalidity - todaydate + logging.info("STS Certificate Validity - %s" %certvalidity) + return(certvalidity, diff) + else: + logging.warning("STS Certificate Search Result was Empty") + return (False,False) + else: + logging.warning("STS Certificate Search Result was Empty") + return (False,False) + +""" +It helps to replace the Secure Token Signing Certificate, this certificate is same for all the vCenter Servers in the Linked mode +This function is a python version of the fixsts.sh Shell script attached in KB https://kb.vmware.com/s/article/76719 +""" +def replace_sts_certificate(): + logging.info("Replacing STS Certificate") + stsresult = "" + print("Replacing STS Certificate.\n...Waiting for Status") + adjust_default_sts_cert_validity() + certcfg_ops.create_cert_cfg_openssl(constants.result_directory+"/sts_openssl.cfg", "STS") + (code, result, err) = openssl_ops.gen_key(constants.result_directory+"/sts.csr",constants.result_directory+"/sts.key",constants.result_directory+"/sts_openssl.cfg",constants.DEFAULT_KEY_SIZE) + if code == 0: + logging.info('Successfully Created Private Key for New STS Certificate') + else: + stsresult = "Failed" + logging.error('Failed to Create Private Key for New STS Certificate - %s' % err) + raise Exception("Failed to Create Private Key for New STS Certificate - %s" %err) + + certcfg_ops.add_authKey_in_cfg(constants.result_directory+"/sts_openssl.cfg") + (code, result, err) = openssl_ops.gen_cert(constants.DEFAULT_STS_VALIDITY, constants.result_directory+"/sts.csr", constants.result_directory+"/sts.cer", constants.vmca_root_path, constants.vmca_key_path, constants.result_directory+"/sts_openssl.cfg") + if code == 0: + logging.info('Successfully Created New STS Certificate') + else: + stsresult = "Failed" + logging.error('Failed to Create New STS Certificate - %s'% err) + raise Exception("Failed to Create New STS Certificate - %s" %err) + + cert = "" + No_of_Certs = 1 + for line in open(constants.vmca_root_path, 'r'): + cert += line + if '-----END CERTIFICATE-----' in line: + with open(constants.result_directory+"/root0%s.cer" % No_of_Certs, 'w') as rootca: + rootca.write(cert) + cert = "" + No_of_Certs += 1 + + (code, result, err) = openssl_ops.convert_cert_der(constants.result_directory+"/sts.cer",constants.result_directory+"/sts.der") + if code == 0: + logging.info('Successfully Converted STS Cert to DER Format') + else: + stsresult = "Failed" + logging.error('Failed to Convert New STS Certificate to DER format - %s'% err) + raise Exception("Failed to Convert New STS Certificate to DER format - %s" %err) + + (code, result, err) = openssl_ops.convert_key_der(constants.result_directory+"/sts.key",constants.result_directory+"/sts.key.der") + if code == 0: + logging.info("Successfully Converted STS Key to DER Format") + else: + stsresult = "Failed" + logging.error('Failed to Convert Private Key of New STS Certificate to DER format - %s'% err) + raise Exception("Failed to Convert Private Key of New STS Certificate to DER format - %s" %err) + + No_of_Certs = No_of_Certs - 1 + tenantcredentials = "" + trustedcertchains = "" + + i = 1 + while i <= No_of_Certs: + root_cer_path = constants.result_directory+"/root0{0}.cer".format(i) + root_der_path = constants.result_directory+"/vmca0{0}.der".format(i) + (code, result, err) = openssl_ops.convert_cert_der(root_cer_path, root_der_path) + if code == 0: + logging.info("Successfully Converted Root-%s to DER Format" %i) + i = i + 1 + + cmd = constants._LDAPSEARCH + ' -o ldif-wrap=no -h localhost -p 389 -b "cn={0},cn=Tenants,cn=IdentityManager,cn=Services,{1}" -D "cn=administrator,cn=users,{1}" -w "{2}" "(objectclass=vmwSTSTenantCredential)" | grep -i "dn:" '.format( + environment.DOMAIN, environment.DOMAINCN, environment.ldapssopassword) + (code, result, err) = execute_cmd(cmd, True, None) + if result: + tenantcredentials = (result.decode('utf-8').split('\n')) + else: + logging.warning("STS Certificate Search Result was Empty") + + cmd = constants._LDAPSEARCH + ' -o ldif-wrap=no -h localhost -p 389 -b "cn={0},cn=Tenants,cn=IdentityManager,cn=Services,{1}" -D "cn=administrator,cn=users,{1}" -w "{2}" "(objectclass=vmwSTSTenantTrustedCertificateChain)" | grep -i "dn:" '.format( + environment.DOMAIN, environment.DOMAINCN, environment.ldapssopassword) + (code, result, err) = execute_cmd(cmd, True, None) + if result: + trustedcertchains = (result.decode('utf-8').split('\n')) + else: + logging.warning("STS Certificate Trusted Chain Search Result was Empty") + if tenantcredentials: + for tenantcredential in tenantcredentials: + tenantcredentialdn = tenantcredential.replace('dn: ','') + filename = tenantcredentialdn.split(',')[0].replace('cn=', '') + cmd = constants._LDAPSEARCH + ' -h localhost -D "cn=administrator,cn=users,{0}" -w "{1}" -b "{2}" > {3}/{4}.ldif '.format( + environment.DOMAINCN, environment.ldapssopassword, tenantcredentialdn, constants.result_directory,filename) + (code, result, err) = execute_cmd(cmd, True, None) + if code == 0: + logging.info('Successfully Exported STS Certificate-%s' % tenantcredentialdn) + cmd = constants._LDAPDELETE + ' -h localhost -D "cn=administrator,cn=users,{0}" -w "{1}" "{2}" '.format( + environment.DOMAINCN, environment.ldapssopassword, tenantcredentialdn) + (code, result, err) = execute_cmd(cmd, True, None) + if code == 0: + logging.info('Successfully Deleted STS Certificate-%s' % tenantcredentialdn) + else: + logging.error('Failed to Delete STS Certificate from VMDIR - %s' % err) + raise Exception("Failed to Delete STS Certificate from VMDIR - %s" %err) + + if trustedcertchains: + for trustedcertchain in trustedcertchains: + chaindn = trustedcertchain.replace('dn: ', '') + filename = chaindn.split(',')[0].replace('cn=','') + cmd = constants._LDAPSEARCH + ' -h localhost -D "cn=administrator,cn=users,{0}" -w "{1}" -b "{2}" > {3}/{4}.ldif '.format( + environment.DOMAINCN, environment.ldapssopassword, chaindn,constants.result_directory,filename) + (code, result, err) = execute_cmd(cmd, True, None) + if code == 0: + logging.info('Successfully Exported STS Certificate-%s' % chaindn) + cmd = constants._LDAPDELETE + ' -h localhost -D "cn=administrator,cn=users,{0}" -w "{1}" "{2}" '.format( + environment.DOMAINCN, environment.ldapssopassword, chaindn) + (code, result, err) = execute_cmd(cmd, True, None) + if code == 0: + logging.info('Successfully Deleted STS Certificate-%s' % chaindn) + else: + logging.error('Failed to Delete STS Certificate Chain from VMDIR - %s' % err) + raise Exception("Failed to Delete STS Certificate Chain from VMDIR - %s" %err) + + with open(constants.result_directory+"/sso-sts.ldif", "w") as text_file: + text_file.write("dn: cn=TenantCredential-1,cn={0},cn=Tenants,cn=IdentityManager,cn=Services,{1}".format(environment.DOMAIN,environment.DOMAINCN)) + text_file.write("\nchangetype: add") + text_file.write("\nobjectClass: vmwSTSTenantCredential") + text_file.write("\nobjectClass: top") + text_file.write("\ncn: TenantCredential-1") + text_file.write("\nuserCertificate:< file:{0}/sts.der".format(constants.result_directory)) + i = 1 + while i <= No_of_Certs: + text_file.write("\nuserCertificate:< file:{0}/vmca0{1}.der".format(constants.result_directory,i)) + i = i + 1 + text_file.write("\nvmwSTSPrivateKey:< file:{0}/sts.key.der".format(constants.result_directory)) + text_file.write("\n") + text_file.write("\ndn: cn=TrustedCertChain-1,cn=TrustedCertificateChains,cn={0},cn=Tenants,cn=IdentityManager,cn=Services,{1}".format(environment.DOMAIN,environment.DOMAINCN)) + text_file.write("\nchangetype: add") + text_file.write("\nobjectClass: vmwSTSTenantTrustedCertificateChain") + text_file.write("\nobjectClass: top") + text_file.write("\ncn: TrustedCertChain-1") + text_file.write("\nuserCertificate:< file:{0}/sts.der".format(constants.result_directory)) + i = 1 + while i <= No_of_Certs: + text_file.write("\nuserCertificate:< file:{0}/vmca0{1}.der".format(constants.result_directory,i)) + i = i + 1 + text_file.write("\n") + text_file.close() + try: + cmd = constants._LDAPMODIFY + ' -x -h localhost -p 389 -D "cn=administrator,cn=users,{0}" -w "{1}" -f {2}/sso-sts.ldif '.format(environment.DOMAINCN,environment.ldapssopassword,constants.result_directory) + (code, result, err) = execute_cmd(cmd, True, None) + if code == 0 and stsresult == "": + logging.info('Successfully replaced STS Certificate') + constants.cert_replaced = True + print(color_green("......Success\n")) + else: + logging.error('Failed to Replace STS Certificate - Result - %s' % result) + logging.error('Failed to Replace STS Certificate - Error - %s' % err) + print(color_red("Failed to Replace STS Certificate, you may follow KB https://kb.vmware.com/s/article/76719 to replace STS Certificate using Shell Script")) + print(color_red("......Failed\n")) + raise Exception("Failed to Replace STS Certificate - %s" %err) + except Exception as e: + logging.error("Failed to replace STS Certificate Certificate %s" % e) + print("..Failed to replace STS Certificate %s" % e) + print(color_red("......Failed\n")) + traceback.print_exc() + sys.exit() + +""" +Function to replace the Machine SSL Certificate, means certificate in MACHINE_SSL_CERT store with alias __MACHINE_CERT +It takes the backup of existing certificate in BACKUP_STORE before proceeding with the replacement +This function calls the sub function update_trust_anchors to update the SSL Trust of each endpoint in SSO Lookupservice +""" +def replace_machine_ssl_certificate(): + logging.info("Replacing Machine SSL Cert") + print("Replacing Machine SSL Cert.\n...Waiting for Status") + (code, result, err) = vecs_ops.get_cert_tofile("MACHINE_SSL_CERT", "__MACHINE_CERT", constants.result_directory+"/old_machine_ssl.crt") + if code != 0: + logging.error("Reading the current Machine SSL Certificate Failed with error - %s" % err) + try: + (code, result, err) = vecs_ops.get_cert_tofile("MACHINE_SSL_CERT", "__MACHINE_CERT", constants.result_directory+"/MACHINE_SSL_CERT_bkp.crt") + (code, result, err) = vecs_ops.get_key_tofile("MACHINE_SSL_CERT", "__MACHINE_CERT", constants.result_directory+"/MACHINE_SSL_CERT_bkp.priv") + (code, result, err) = vecs_ops.delete_cert("BACKUP_STORE", "bkp___MACHINE_CERT") + (code, result, err) = vecs_ops.create_cert("BACKUP_STORE", "bkp___MACHINE_CERT", constants.result_directory+"/MACHINE_SSL_CERT_bkp.crt", constants.result_directory+"/MACHINE_SSL_CERT_bkp.priv") + + if (constants.use_openssl_functions): + certcfg_ops.create_cert_cfg_openssl(constants.result_directory+"/MACHINE_SSL_CERT_openssl.cfg", environment.PNID) + (code, result, err) = openssl_ops.gen_key(constants.result_directory+"/MACHINE_SSL_CERT.csr",constants.result_directory+"/MACHINE_SSL_CERT.priv",constants.result_directory+"/MACHINE_SSL_CERT_openssl.cfg",constants.DEFAULT_KEY_SIZE) + certcfg_ops.add_authKey_in_cfg(constants.result_directory+"/MACHINE_SSL_CERT_openssl.cfg") + (code, result, err) = openssl_ops.gen_cert(constants.DEFAULT_VALIDITY, constants.result_directory+"/MACHINE_SSL_CERT.csr", constants.result_directory+"/MACHINE_SSL_CERT.crt", constants.vmca_root_path, constants.vmca_key_path, constants.result_directory+"/MACHINE_SSL_CERT_openssl.cfg") + else: + (code, result, err) = certool_ops.gen_key(constants.result_directory+"/MACHINE_SSL_CERT.priv",constants.result_directory+"/MACHINE_SSL_CERT.pub", environment.DCNAME) + (code, result, err) = certool_ops.gen_cert(constants.result_directory+"/MACHINE_SSL_CERT.crt", constants.result_directory+"/MACHINE_SSL_CERT.priv", environment.DCNAME,environment.PNID,certcfg_ops.Country,certcfg_ops.Organization,certcfg_ops.OrgUnit,certcfg_ops.State,certcfg_ops.Locality) + + (code, result, err) = vecs_ops.delete_cert("MACHINE_SSL_CERT", "__MACHINE_CERT") + (code, result, err) = vecs_ops.create_cert("MACHINE_SSL_CERT", "__MACHINE_CERT", constants.result_directory+"/MACHINE_SSL_CERT.crt", constants.result_directory+"/MACHINE_SSL_CERT.priv") + if code == 0: + print(color_green("......Success\n")) + constants.cert_replaced = True + else: + print(color_red("......Failed\n")) + logging.error("Failed to replace Machine SSL Certificate %s" % err) + raise Exception("Failed to replace Machine SSL Certificate - %s" %err) + update_trust_anchors() + logging.info("Successfully replaced Machine SSL Certificate") + except Exception as e: + logging.error("Failed to replace Machine SSL Certificate %s" % e) + print("..Failed to replace Machine SSL Certificate %s" % e) + print(color_red("......Failed\n")) + traceback.print_exc() + sys.exit() + +""" +Sub function to update the Trust Anchors of each endpoint in SSO Lookupservice, this function will be initiated by update_trust_anchors +This function uses ldapsearch and ldapmodify commands to update each endpoint which belongs to the VC where script is executed +""" +def update_endpoints(RegistrationClass,SSLTrustClass): + logging.info("Updating Trust Anchors" + RegistrationClass) + logging.info("Listing Services") + new_machine_ssl = get_x509_from_file(constants.result_directory+"/MACHINE_SSL_CERT.crt") + cert = crypto.dump_certificate(crypto.FILETYPE_PEM, new_machine_ssl).decode('utf-8').replace("\n","").replace("-----BEGIN CERTIFICATE-----","").replace("-----END CERTIFICATE-----","") + cmd = '/usr/bin/ldapsearch -o ldif-wrap=no -h {0} -p 389 -s sub -b "cn=ServiceRegistrations,' \ + 'cn=LookupService,cn={1},cn=Sites,cn=Configuration,{2}" -D "cn=administrator,' \ + 'cn=users,{2}" -w "{3}" "(objectclass={4})" '.format(environment.DCNAME,environment.SITENAME,environment.DOMAINCN,environment.ldapssopassword,RegistrationClass) + (code, result, err) = execute_cmd(cmd, True, None) + if code != 0: + logging.error("Listing the Endpoints failed to update the Trust Anchor, script cannot continue further - Error - %s" % err) + return False + endpointresults = (result.decode('utf-8').split('\n')) + nodeendpoints = [] + i = 0 + while i < len(endpointresults): + if endpointresults[i].startswith('dn: cn=Endpoint'): + servicedn = endpointresults[i].split(',',1)[1] + for j in range(i + 1, len(endpointresults)): + i = i + 1 + if endpointresults[j].startswith('dn: cn=Endpoint') and servicedn not in nodeendpoints: + break + elif endpointresults[j].startswith('vmwLKUPURI') and environment.PNID.lower() in endpointresults[j].lower(): + if servicedn not in nodeendpoints: + nodeendpoints.append(servicedn) + break + else: + continue + else: + i = i + 1 + continue + for nodeendpoint in nodeendpoints: + logging.info(nodeendpoint) + cmd = '/usr/bin/ldapsearch -o ldif-wrap=no -h {0} -p 389 -s sub -b "{1}" -D "cn=administrator,' \ + 'cn=users,{2}" -w "{3}" "(objectclass={4})" '.format(environment.DCNAME,nodeendpoint,environment.DOMAINCN,environment.ldapssopassword,RegistrationClass) + (code, result, err) = execute_cmd(cmd, True, None) + if code != 0: + logging.error("Listing the Endpoints failed to update the Trust Anchor, script cannot continue further - Error - %s" % err) + return False + endpoints = (result.decode('utf-8').split('\n')) + for endpoint in endpoints: + if endpoint.startswith('dn: cn=Endpoint'): + with open(constants.result_directory+"/servicereg.ldif", "w") as text_file: + text_file.write("{0}".format(endpoint)) + text_file.write("\nchangetype: modify") + text_file.write("\nreplace: {0}".format(SSLTrustClass)) + if SSLTrustClass == "vmwLKUPSslTrustAnchor": + text_file.write("\n{0}:< file:{1}".format(SSLTrustClass, constants.result_directory+"/MACHINE_SSL_CERT.der")) + else: + text_file.write("\n{0}: {1}".format(SSLTrustClass, cert)) + cmd = constants._LDAPMODIFY + ' -x -h {0} -p 389 -D "cn=administrator,cn=users,{1}" -w "{2}" -f {3}/servicereg.ldif '.format(environment.DCNAME,environment.DOMAINCN, environment.ldapssopassword,constants.result_directory) + (code, result, err) = execute_cmd(cmd, True, None) + if code != 0: + logging.error("Listing the Endpoints failed to update the Trust Anchor, script cannot continue further - Error - %s" % err) + return False + return True + +""" +Function to update the Trust Anchors in SSO Lookupservice during Machine SSL Certificate Replacement +It utilizes function update_endpoints to connect to VMDIR DB and read the values of each endpoint +""" +def update_trust_anchors(): + logging.info("Updating Trust Anchors") + print("Updating SSL Trust of Services with new Machine SSL Certificate.\n...Waiting for Status") + (code, result, err) = openssl_ops.convert_cert_der(constants.result_directory+"/MACHINE_SSL_CERT.crt",constants.result_directory+"/MACHINE_SSL_CERT.der") + if code == 0: + logging.info('Successfully Converted Machine SSL Certificate to DER Format for Trust Anchor Update') + else: + logging.error("Error while converting Machine SSL Certificate to DER format for Trust Anchor Update - %s" %err) + print(color_red("......Failed\n")) + raise Exception("Error while converting Machine SSL Certificate to DER format for Trust Anchor Update - %s" %err) + if not (update_endpoints("vmwLKUPEndpointRegistration","vmwLKUPEndpointSslTrust")): + logging.error("Error while updating SSL Trust Anchors in LookupService") + print("...Error while updating SSL Trust Anchors in LookupService") + print(color_red("......Failed\n")) + raise Exception("Error while updating SSL Trust Anchors in LookupService") + else: + logging.info("Successfully updated Trust Anchor of endpoints in Lookup Service Registrations") + #Update Legacy SSO Endpoints - sso:sts, sso:groupcheck and sso:admin + if not (update_endpoints("vmwLKUPServiceEndpoint","vmwLKUPSslTrustAnchor")): + logging.error("Error while updating SSL Trust Anchors in LookupService for SSO Legacy Endpoints") + print("...Error while updating SSL Trust Anchors in LookupService for SSL Legacy Endpoints") + print(color_red("......Failed\n")) + raise Exception("Error while updating SSL Trust Anchors in LookupService for SSL Legacy Endpoints") + else: + print(color_green("......Success\n")) + logging.info("Successfully updated Trust Anchor of Legacy endpoints in Lookup Service Registrations") + +""" +Backup solution user certificate to the BACKUP_STORE +""" +def backup_solution_certs(user): + logging.info("Taking backup of %s Solution User Certificate" %user) + bkp_key_name = constants.result_directory + "/" + user + "_bkp.priv" + bkp_cert_name = constants.result_directory + "/" + user + "_bkp.crt" + bkp_alias = "bkp_" + user + + (code, result, err) = vecs_ops.get_cert_tofile(user, user, bkp_cert_name) + (code, result, err) = vecs_ops.get_key_tofile(user, user, bkp_key_name) + (code, result, err) = vecs_ops.delete_cert("BACKUP_STORE", bkp_alias) + (code, result, err) = vecs_ops.create_cert("BACKUP_STORE", bkp_alias, bkp_cert_name, bkp_key_name) + if code != 0: + logging.error("Taking Backup of Solution User Failed with error - %s" %err) + else: + logging.info("Successfully took backup of %s Solution User Certificate" %user) + +""" +Sub function to create new solution user certificate and replace the existing one +Uses function from vecscli, dircli and certool classes to perform the required tasks +""" +def replace_solution_certs_sub(user): + logging.info("Replacing %s Solution User Certificate" %user) + print("Replacing %s Solution User Certificate.\n...Waiting for Status" % user) + user_key_name = constants.result_directory+ "/" + user + ".priv" + user_pub_name = constants.result_directory+ "/" + user + ".pub" + user_cert_name = constants.result_directory+ "/" + user + ".crt" + user_csr_name = constants.result_directory+ "/" + user + ".csr" + user_openssl_cfg_path = constants.result_directory+ "/" + user + "_openssl.cfg" + + try: + machineid = environment.Machine_ID + sol_ou_name = "mID-" + machineid + if (constants.use_openssl_functions): + certcfg_ops.create_cert_cfg_openssl(user_openssl_cfg_path, user,sol_ou_name) + (code, result, err) = openssl_ops.gen_key(user_csr_name,user_key_name,user_openssl_cfg_path,constants.DEFAULT_KEY_SIZE) + certcfg_ops.add_authKey_in_cfg(user_openssl_cfg_path) + (code, result, err) = openssl_ops.gen_cert(constants.DEFAULT_VALIDITY, user_csr_name, user_cert_name, constants.vmca_root_path, constants.vmca_key_path, user_openssl_cfg_path) + else: + (code, result, err) = certool_ops.gen_key(user_key_name,user_pub_name, environment.DCNAME) + (code, result, err) = certool_ops.gen_cis_cert(user, user_cert_name, user_key_name, environment.DCNAME) + (code, result, err) = dircli_ops.service_update(user_cert_name, user, machineid, "Administrator@"+environment.DOMAIN, environment.ssopassword) + if (code != 0): + raise Exception("Failed to update the certificate of solution user in VMDIR - %s" %err) + (code, result, err) = vecs_ops.delete_cert(user, user) + (code, result, err) = vecs_ops.list_certs(user) + logging.info(result) + (code, result, err) = vecs_ops.create_cert(user, user, user_cert_name, user_key_name) + if code == 0: + logging.info(result) + print(color_green("......Success\n")) + constants.cert_replaced = True + else: + print(color_red("......Failed")) + logging.info("Failed to update Solution User Certificate - %s - Error %s" %(user,err)) + print(color_red("Failed to update Solution User Certificate - %s - Error %s" %(user,err))) + raise Exception("Failed to update Solution User Certificate - %s" %err) + except Exception as e: + logging.error("Failed to replace Solution User Certificate %s" % e) + print("..Failed to replace Solution User Certificate %s" % e) + print(color_red("......Failed\n")) + traceback.print_exc() + sys.exit() + +""" +This function initiates various other sub functions for the Solution User certificate replacement +It calls backup_solution_certs to take backup of Solution user Certificates +And, calls replace_solution_certs_sub to create and replace solution user certificate +If the store name is vpxd-extension: + it also calls the function update_extension_in_vc_database to update the Certificate in VCDB +""" +def replace_solution_user_certificate(stores): + logging.info("Replacing Solution Users Certs") + for store_name in constants.store_names: + if store_name in stores: + backup_solution_certs(store_name) + replace_solution_certs_sub(store_name) + if store_name == "vpxd-extension": + extension_cert_path = constants.result_directory+"/vpxd-extension.crt" + update_extension_in_vc_database(extension_cert_path) + logging.info("Successfully replaced Solution User Certificates") + +""" +Update VPXD extensions independently +""" +def update_vpxd_extensions(store="vpxd-extension"): + updated_vcdb = False + extensions_auto_service_restart = False + logging.info("Updating certificate for extensions registered in vpxd") + extension_cert_path = constants.result_directory+"/vpxd-extension-cert.crt" + vecs_ops.get_cert_tofile("vpxd-extension", "vpxd-extension", extension_cert_path) + updated_vcdb = update_extension_in_vc_database(extension_cert_path) + if (updated_vcdb): + print("Restarting the Services %s" %constants.extension_type) + logging.info("Restarting the Services %s" %constants.extension_type) + for extension in constants.extension_type: + service_running_status = check_service_runstate(extension) + if service_running_status == "RUNNING": + print("...Restarting %s Service" % extension) + stop_service(extension) + start_service(extension) + elif service_running_status in ["STOPPED"]: + print("...Skipping service restart of %s as service is in STOPPED state" % extension) + else: + print("...Please restart %s service manually using 'service-control' utility" %extension) + logging.info("Successfully updated certificate for VPXD extensions") + else: + print("Skipping Service restart as the script did not update thumbprint for any extensions ") + logging.warning("Skipping Service restart as script did not update thumbprint for any extensions ") + +""" +VPXD extensions rbd,eam and impagebuilder needs to be updated in VCDB while replacing vpxd-extension certificate +These services uses vpxd-extension certificate to connect to VPXD. +In this function, vpxd-extension Thumbprint is updated in VPX_EXT table, +DB update method is used because Certificate expiration, VPXD will not be not be running and utilizing updateExtensionCertInVC.py script will fail +""" +def update_extension_in_vc_database(certificate_path): + logging.info("Updating Extensions in VPXD") + print("Updating Thumbprint of Extensions in VPXD.\n...Waiting for Status") + (code, result, err) = openssl_ops.get_fingerprint(certificate_path) + if code != 0: + logging.info("Failed to read thumbprint of vpxd-extension certificate, however script will try to continue - %s\n Error - %s" %(cmd, err.decode('utf-8'))) + else: + thumbprint = (result.decode('utf-8').split("=")[1].replace('\n','')) + logging.info("vpxd-extension certificate thumbprint is " + thumbprint) + status = existing_tumbprint = "" + db_update = False + if thumbprint: + for extension in constants.extensions: + cmd = '/opt/vmware/vpostgres/current/bin/psql -d VCDB -U postgres -t -c "SELECT THUMBPRINT FROM VPX_EXT WHERE EXT_ID=\'{0}\'" '.format(extension) + (code, result, err) = execute_cmd(cmd, True, None) + if code==0: + existing_tumbprint = result.decode("UTF-8").strip() + if thumbprint in existing_tumbprint: + status = "Success" + print("....Skipping thumprint update for extension %s as VPXD already have the correct thumbprint" %extension) + logging.warning("Skipping thumprint update for extension %s as the VPXD have the correct thumbprint" %extension) + else: + cmd = '/opt/vmware/vpostgres/current/bin/psql -d VCDB -U postgres -c "UPDATE VPX_EXT SET THUMBPRINT=\'{0}\' WHERE EXT_ID=\'{1}\'" '.format(thumbprint,extension) + logging.info("Updating %s in VPXD" %extension) + print("....Updating thumbprint of %s" %extension) + logging.info(cmd) + (code, result, err) = execute_cmd(cmd, True, None) + if code==0: + status = "Success" + db_update = True + else: + status = "Failed" + if status == "Success": + print(color_green("......Success\n")) + logging.info("Successfully updated Certificate Thumbprint in VPXD Database") + else: + print(color_red("......Failed")) + logging.warning("Failed to update the extension rbd,eam and impagebuilder in VPXD, however script will continue, you may manually update the thumbprint using KB https://kb.vmware.com/s/article/57379") + print(color_red("Failed to update the extension rbd,eam and impagebuilder in VPXD, however script will continue, you may manually update the thumbprint using KB https://kb.vmware.com/s/article/57379")) + return db_update + +""" +Certificate in data-encipherment store is used to encrypt the Administrator password used in Guest Customization Templates. +This function will renew the data-encipherment certificate using the same Private Key +""" +def replace_data_encipherment_certificate(stores): + logging.info("Replacing data-enciphement Certificate") + if 'data-encipherment' in stores: + print("Replacing data-enciphement Certificate.\n...Waiting for Status") + (code, result, err) = vecs_ops.get_cert_tofile("data-encipherment", "data-encipherment", "/var/tmp/data-encipherment_bkp.crt") + (code, result, err) = vecs_ops.get_key_tofile("data-encipherment", "data-encipherment", "/var/tmp/data-encipherment_bkp.priv") + if code == 0: + logging.info("Checking data-encipherment Certificate Validity") + (encipherment_cert_validity, encipherment_cert_validity_days) = check_certificate_validity("/var/tmp/data-encipherment_bkp.crt") + if (encipherment_cert_validity and encipherment_cert_validity_days.days < 0) or constants.force_encipherment_cert: + logging.info("Proceeding with data-encipherment Certificate as it is expired or force replace is True") + (code, result, err) = vecs_ops.delete_cert("data-encipherment", "data-encipherment") + (code, result, err) = certool_ops.gen_cis_cert('data-encipherment', "/var/tmp/data-encipherment.crt", "/var/tmp/data-encipherment_bkp.priv", environment.DCNAME) + if code == 0: + print(color_green("......Success\n")) + constants.cert_replaced = True + logging.info("Successfully replaced data-encipherment Certificate") + else: + logging.error("Failed to replace data-encipherment certificate using the same private key") + (code, result, err) = vecs_ops.create_cert("data-encipherment", "data-encipherment", "/var/tmp/data-encipherment_bkp.crt", "/var/tmp/data-encipherment_bkp.priv") + print(color_red("Failed to replace data-encipherment certificate, script will continue and you may follow KB https://kb.vmware.com/s/article/88548 to replace this certificate if it is expired.")) + print(color_red("......Failed\n")) + else: + logging.info("Script did not attempt data-encipherment certificate as it is not expired") + print(color_green("......Script did not attempt data-encipherment certificate replacement as it is not expired (valid till-%s), please refer https://kb.vmware.com/s/article/88548.\n......It is not recommended to replace this Certificate if it is not expired, you may force it using argument (--force_encipherment_replace True) if it is really required" %encipherment_cert_validity)) + print(color_green("......Skipped")) + else: + logging.error("Failed to export data-encipherment certificate or private key") + print(color_red("Failed to replace data-encipherment certificate, script will continue and you may follow KB https://kb.vmware.com/s/article/88548 to replace this certificate if it is expired.")) + print(color_red("......Failed\n")) + else: + logging.info("This vCenter Server does not have data-enciphement VECS store, hence not replacing the Certificate. This store is available from vCenter Server 6.7 onwards") + +""" +In upgraded environments, there will be an additional store named STS_INTERNAL_SSL_CERT to store the Certificate for LookupService (port 7444) +Currently certificate-manager utility does not handle this store. +This function will replace the Cert and Key in STS_INTERNAL_SSL_CERT with the certificate and key stored in MACHINE_SSL_CERT +""" +def replace_lookupservice_certificate(): + logging.info("Replacing LookupService Certificate in STS_INTERNAL_SSL_CERT") + print("Replacing LookupService SSL Cert.\n...Waiting for Status") + (code, result, err) = vecs_ops.delete_cert("STS_INTERNAL_SSL_CERT", "__MACHINE_CERT") + if code != 0: + logging.error("Failed to delete the entries from VECS store STS_INTERNAL_SSL_CERT, however script will try to continue") + print(color_red("Failed to delete the entries from VECS store STS_INTERNAL_SSL_CERT, however script will try to continue")) + (code, result, err) = vecs_ops.get_cert_tofile("MACHINE_SSL_CERT", "__MACHINE_CERT", constants.result_directory+"/MACHINE_SSL_CERT.crt") + if code != 0: + logging.error("Reading the Machine SSL Certificate failed with error - %s" % err) + raise Exception("Failed to read the Machine SSL Certificate - %s" %err) + (code, result, err) = vecs_ops.get_key_tofile("MACHINE_SSL_CERT", "__MACHINE_CERT", constants.result_directory+"/MACHINE_SSL_CERT.priv") + if code != 0: + logging.error("Reading the Machine SSL private key failed with error - %s" % err) + raise Exception("Failed to read the Machine SSL private key - %s" %err) + (code, result, err) = vecs_ops.create_cert("STS_INTERNAL_SSL_CERT", "__MACHINE_CERT", constants.result_directory+"/MACHINE_SSL_CERT.crt", constants.result_directory+"/MACHINE_SSL_CERT.priv") + if code == 0: + print(color_green("......Success\n")) + constants.cert_replaced = True + logging.info("Successfully replaced LookupService Certificate") + else: + logging.error("Failed to create new certificate entry in store STS_INTERNAL_SSL_CERT, terminating the script") + print(color_red("Failed to create new certificate entry in store STS_INTERNAL_SSL_CERT, script is terminating")) + print(color_red("......Failed\n")) + raise Exception("Failed to update LookupService Certificate - %s" %err) + +""" +This function will replace the SMS self signed certificate only if it is expired +""" +def replace_sms_certificate(stores): + logging.info("Replacing SMS Certificate if expired") + print("\nReplacing SMS Certificate.\n...Waiting for Status") + found_expired_sms_cert = False + error_verifying_sms_cert = False + sms_cert_removal_status = False + if 'SMS' in stores: + alias = 'sms_self_signed' + (code, result, err) = vecs_ops.list_certs("SMS") + if code == 0 and alias in result.decode('utf-8'): + cert_file = constants.result_directory + "/" + alias + ".crt" + backup_filename = '/var/tmp' + "/" + alias + ".crt" + (code, result, err) = vecs_ops.get_cert_tofile('SMS', alias, cert_file) + if code == 0: + if (is_cert_expired(cert_file)): + found_expired_sms_cert = True + logging.info("Removing expired SMS Certificate") + shutil.copy(cert_file, backup_filename) + (code, result, err) = vecs_ops.delete_cert('SMS', alias) + if code == 0: + logging.info("Backup of expired SMS certificate saved to %s" %backup_filename) + print("....Backup of expired SMS certificate saved to %s" %backup_filename) + sms_cert_removal_status = True + else: + sms_cert_removal_status = False + else: + error_verifying_sms_cert = True + else: + error_verifying_sms_cert = True + + if not found_expired_sms_cert: + logging.info("NO Expired Certs in SMS store") + print(color_green("....NO Expired Certs in SMS store")) + print(color_green("......Skipped\n")) + elif(sms_cert_removal_status): + print(color_green("......Success\n")) + constants.cert_replaced = True + logging.info("Successfully removed expired SMS certificate") + elif(error_verifying_sms_cert): + logging.error("Failed to verify SMS Certificate, you may follow KB https://kb.vmware.com/s/article/2146011 to remove it manually") + print(color_red("Failed to verify SMS Certificate, you may follow KB https://kb.vmware.com/s/article/2146011 to remove it manually")) + print(color_red("......Failed\n")) + else: + logging.error("Failed to verify SMS Certificate, you may follow KB https://kb.vmware.com/s/article/2146011 to remove it manually") + print(color_red("Failed to verify SMS Certificate, you may follow KB https://kb.vmware.com/s/article/2146011 to remove it manually")) + print(color_red("......Failed\n")) + sys.exit() + +""" +This function will remove all the expired certificates from TRUSTED_ROOTS VECS store. +It will not perform any replace operation, just will unpublish expired Root Certificates +""" +def remove_expired_certs_from_trusted_roots(): + logging.info("Removing Expired Certificates from TRUSTED_ROOTS store") + print("\nRemoving Expired Certificates from TRUSTED_ROOTS store.\n...Waiting for Status") + error_verifying_trusted_roots = False + trusted_removal_status = False + found_expired_root_cert = False + (code, result, err) = vecs_ops.list_certs("TRUSTED_ROOTS") + if code == 0 and (result): + trusted_certs = result.decode('utf-8') + root_aliases = [] + if ('Alias' in trusted_certs): + trusted_root_entries = trusted_certs.split('\n') + for value in trusted_root_entries: + if 'Alias :' in value: + root_aliases.append(value.replace('Alias :\t','')) + for alias in root_aliases: + cert_file = constants.result_directory + "/" + alias + ".crt" + backup_filename = '/var/tmp' + "/" + alias + ".crt" + (code, result, err) = vecs_ops.get_cert_tofile('TRUSTED_ROOTS', alias, cert_file) + if code == 0: + if (is_cert_expired(cert_file)): + found_expired_root_cert = True + logging.info("Un-publishing Expired Root Certificate with Alias %s" %alias) + shutil.copy(cert_file, backup_filename) + (code, result, err) = dircli_ops.trustedcert_unpublish(cert_file, "Administrator", environment.ssopassword) + if code == 0: + logging.info("Backup of expired root certificate saved to %s" %backup_filename) + print("....Backup of expired root certificate saved to %s" %backup_filename) + trusted_removal_status = True + else: + trusted_removal_status = False + else: + error_verifying_trusted_roots = True + else: + error_verifying_trusted_roots = True + + if not found_expired_root_cert: + logging.info("NO Expired Certs in Trusted_Roots store") + print(color_green("....NO Expired Certs in Trusted_Roots store")) + print(color_green("......Skipped\n")) + elif(trusted_removal_status): + print(color_green("......Success\n")) + constants.cert_replaced = True + logging.info("Successfully removed expired root certificates") + elif(error_verifying_trusted_roots): + logging.error("Failed to verify some Root Certificates, you may follow KB https://kb.vmware.com/s/article/2146011 to remove it manually") + print(color_red("Failed to verify some Root Certificates, you may follow KB https://kb.vmware.com/s/article/2146011 to remove it manually")) + print(color_red("......Failed\n")) + else: + logging.error("Failed to remove expired Root Certificates from TRUSTED_ROOTS store, you may follow KB https://kb.vmware.com/s/article/2146011 to remove it manually") + print(color_red("Failed to remove expired Root Certificates from TRUSTED_ROOTS store, you may follow KB https://kb.vmware.com/s/article/2146011 to remove it manually")) + print(color_red("......Failed\n")) + +""" +Check for Non-CA Certificate +""" +def is_ca_cert(certificate_path): + logging.info("Checking CA Status of the Certificate - %s" %certificate_path) + x509_cert = get_x509_from_file(certificate_path) + ca_cert = False + count = 0 + try: + while (count < x509_cert.get_extension_count()): + if ( ("CA:TRUE" in str(x509_cert.get_extension(count))) or ("Certificate Sign" in str(x509_cert.get_extension(count))) ) : + ca_cert = True + break + count+=1 + if ca_cert: + logging.info("Cert %s is CA Certificate" %certificate_path) + return("ca") + else: + logging.info("Cert %s is Non CA Certificate" %certificate_path) + return("non-ca") + except Exception as e: + msg = 'Error while fetching certificate extensions : {0}'.format(e) + logging.error(msg) + return("Unknown") + +""" +This function will remove all Non-CA certificates from TRUSTED_ROOTS VECS store. +""" +def remove_non_ca_certs_from_trusted_roots(): + logging.info("Removing Non-CA Certificates from TRUSTED_ROOTS store") + print("\nRemoving Non-CA Certificates from TRUSTED_ROOTS store.\n...Waiting for Status") + error_verifying_trusted_roots = False + non_ca_removal_status = False + found_non_ca_cert = False + (code, result, err) = vecs_ops.list_certs("TRUSTED_ROOTS") + if code == 0 and (result): + trusted_certs = result.decode('utf-8') + root_aliases = [] + if ('Alias' in trusted_certs): + trusted_root_entries = trusted_certs.split('\n') + for value in trusted_root_entries: + if 'Alias :' in value: + root_aliases.append(value.replace('Alias :\t','')) + for alias in root_aliases: + cert_file = constants.result_directory + "/" + alias + ".crt" + backup_filename = '/var/tmp' + "/" + alias + ".crt" + (code, result, err) = vecs_ops.get_cert_tofile('TRUSTED_ROOTS', alias, cert_file) + if code == 0: + non_ca_cert = is_ca_cert(cert_file) + if (non_ca_cert == "non-ca"): + found_non_ca_cert = True + logging.info("Un-publishing Non CA Certificate with Alias %s" %alias) + shutil.copy(cert_file, backup_filename) + (code, result, err) = dircli_ops.trustedcert_unpublish(cert_file, "Administrator", environment.ssopassword) + if code == 0: + logging.info("Backup of Non CA Certificate saved to %s" %backup_filename) + print("....Backup of Non CA Certificate saved to %s" %backup_filename) + non_ca_removal_status = True + else: + non_ca_removal_status = False + elif (non_ca_cert == "Unknown"): + error_verifying_trusted_roots = True + else: + error_verifying_trusted_roots = True + else: + error_verifying_trusted_roots = True + + if not found_non_ca_cert: + logging.info("Non-CA Certs doesn't exist in Trusted_Roots store") + print(color_green("....Non-CA Certs doesn't exist in Trusted_Roots store")) + print(color_green("......Skipped\n")) + elif(non_ca_removal_status): + print(color_green("......Success\n")) + constants.cert_replaced = True + logging.info("Successfully removed Non-CA certificates") + elif(error_verifying_trusted_roots): + logging.error("Failed to verify some Root Certificates, you may follow KB https://kb.vmware.com/s/article/2146011 to remove it manually") + print(color_red("Failed to verify some Root Certificates, you may follow KB https://kb.vmware.com/s/article/2146011 to remove it manually")) + print(color_red("......Failed\n")) + else: + logging.error("Failed to remove Non-CA Certificates from TRUSTED_ROOTS store, you may follow KB https://kb.vmware.com/s/article/2146011 to remove it manually") + print(color_red("Failed to remove Non-CA Certificates from TRUSTED_ROOTS store, you may follow KB https://kb.vmware.com/s/article/2146011 to remove it manually")) + print(color_red("......Failed\n")) + +""" +Read the Certificate Validity +""" +def get_cert_details(store, alias, cert_file): + (code, result, err) = vecs_ops.get_cert_tofile(store, alias, cert_file) + if code == 0: + (cert_validity, cert_valid_days) = check_certificate_validity(cert_file) + cert_expired = is_cert_expired(cert_file) + return (cert_validity, cert_expired) + else: + return(False,False) + + +""" +Verify the validity of all the existing Certs +""" +def read_all_certs(): + from prettytable import PrettyTable + Y = "\033[0;33;40m" + C = "\033[0;36;40m" + N = "\033[0m" + R = "\033[0;31;40m" #RED + G = "\033[0;32;40m" # GREEN + constants.expired_vmca = constants.expired_sts = constants.expired_machinessl = constants.expired_solutionusers = constants.expired_lookupservice = constants.expired_dataencipherment = constants.expired_sms = constants.expired_trustedroots = False + cert_details = PrettyTable(['CertificateType', 'Validity(UTC)']) + root_cert_details = PrettyTable(['TRUSTED_ROOTS_Alias', 'Validity(UTC)', 'Type']) + store_list = get_vecs_stores() + if not store_list: + logging.info("Failed to list the VECS Stores to show the Certificate Validity, script will Continue...") + else: + store_list = store_list.split('\n') + for store in store_list: + if store in ["MACHINE_SSL_CERT"]: + cert_file = constants.result_directory + "/current_machine_ssl" + ".crt" + (cert_validity, cert_expired) = get_cert_details(store,"__MACHINE_CERT",cert_file) + if (cert_validity): + if (cert_expired): + constants.expired_machinessl = True + cert_details.add_row([C + "MACHINE_SSL_CERT" + N, R + cert_validity.strftime('%b %d %H:%M:%S %Y') + N]) + else: + cert_details.add_row([C + "MACHINE_SSL_CERT" + N, G + cert_validity.strftime('%b %d %H:%M:%S %Y') + N]) + elif store in constants.store_names: + cert_file = constants.result_directory + "/current_" + store + ".crt" + (cert_validity, cert_expired) = get_cert_details(store,store,cert_file) + if (cert_validity): + if (cert_expired): + constants.expired_solutionusers = True + cert_details.add_row([C + store + N, R + cert_validity.strftime('%b %d %H:%M:%S %Y') + N]) + else: + cert_details.add_row([C + store + N, G + cert_validity.strftime('%b %d %H:%M:%S %Y') + N]) + elif store in ["data-encipherment"]: + cert_file = constants.result_directory + "/current_" + store + ".crt" + (cert_validity, cert_expired) = get_cert_details(store,store,cert_file) + if (cert_validity): + if (cert_expired): + constants.expired_dataencipherment = True + cert_details.add_row([C + store + N, R + cert_validity.strftime('%b %d %H:%M:%S %Y') + N]) + else: + cert_details.add_row([C + store + N, G + cert_validity.strftime('%b %d %H:%M:%S %Y') + N]) + elif store in ["STS_INTERNAL_SSL_CERT"]: + cert_file = constants.result_directory + "/current_" + store + ".crt" + (cert_validity, cert_expired) = get_cert_details(store,"__MACHINE_CERT",cert_file) + if (cert_validity): + if (cert_expired): + constants.expired_lookupservice = True + cert_details.add_row([C + store + N, R + cert_validity.strftime('%b %d %H:%M:%S %Y') + N]) + else: + cert_details.add_row([C + store + N, G + cert_validity.strftime('%b %d %H:%M:%S %Y') + N]) + elif store in ["SMS"]: + cert_file = constants.result_directory + "/current_" + store + ".crt" + (cert_validity, cert_expired) = get_cert_details(store,"sms_self_signed",cert_file) + if (cert_validity): + if (cert_expired): + constants.expired_sms = True + cert_details.add_row([C + store + N, R + cert_validity.strftime('%b %d %H:%M:%S %Y') + N]) + else: + cert_details.add_row([C + store + N, G + cert_validity.strftime('%b %d %H:%M:%S %Y') + N]) + elif store in ["TRUSTED_ROOTS"]: + (code, result, err) = vecs_ops.list_certs_text(store) + for line in result.decode('utf-8').split('\n'): + if re.search("Alias :", line): + alias = line.replace("Alias :","").strip() + cert_file = constants.result_directory + "/current__root" + alias + ".crt" + (cert_validity, cert_expired) = get_cert_details(store,alias,cert_file) + ca_type = is_ca_cert(cert_file) + if ca_type == "ca": + ca_type = "CA" + elif (ca_type == "non-ca"): + ca_type = "Non-CA" + else: + ca_type = "Error Fetching Cert Details" + if (cert_validity): + if (cert_expired): + #cert_details.add_row([C + "TRUSTED_ROOTS-Alias-" + alias + N, R + cert_validity.strftime('%b %d %H:%M:%S %Y') + N]) + constants.expired_trustedroots = True + root_cert_details.add_row([C + alias + N, R + cert_validity.strftime('%b %d %H:%M:%S %Y') + N, C + ca_type + N]) + else: + #cert_details.add_row([C + "TRUSTED_ROOTS-Alias-" + alias + N, G + cert_validity.strftime('%b %d %H:%M:%S %Y') + N]) + root_cert_details.add_row([C + alias + N, G + cert_validity.strftime('%b %d %H:%M:%S %Y') + N, C + ca_type + N]) + (sts_validity,sts_valid_days) = check_sts_certificate() + if (sts_validity): + if sts_valid_days.days > 0: + cert_details.add_row([C + "Signing Cert (STS)" + N, G + sts_validity.strftime('%b %d %H:%M:%S %Y') + N]) + else: + constants.expired_sts = True + cert_details.add_row([C + "Signing Cert (STS)" + N, R + sts_validity.strftime('%b %d %H:%M:%S %Y') + N]) + else: + cert_details.add_row([C + "Signing Cert (STS)" + N, R + "Error Fetching Cert Details" + N]) + if (os.path.exists(constants.vmca_root_path)): + (vmca_validity,vmca_valid_days) = check_certificate_validity(constants.vmca_root_path) + if vmca_valid_days.days < 0: + constants.expired_vmca = True + return (cert_details, root_cert_details) + + +""" +Master function to reset all certificates on vCenter Server (root/sts/machine ssl/lookupservice and solution user certificates) +This will call various sub functions to replace the certificates +""" +def replace_certificates(args,argparser): + #Start time used to calculate the execution time + start_time = time.time() + + display_warning_message = True + + #Check unsupported replacement + if unsupported_scenario(): + logging.info("This script does not support Certificate replacement on this node") + sys.exit() + + #Reading SSO Password + if (constants.silent_execution): + logging.info("This is a Silent execution") + environment.ssopassword = args.password + else: + environment.ssopassword = getpass.getpass(prompt='Please enter the password for administrator@%s to proceed further : ' % environment.DOMAIN) + + #set ldappassword to escapte $ symbol + environment.ldapssopassword = environment.ssopassword.replace("$","\\$") + environment.ldapssopassword = environment.ldapssopassword.replace("`","\\`") + environment.ldapssopassword = environment.ldapssopassword.replace('"','\\"') + + (certs_validity, root_certificates_validity) = read_all_certs() + + #Initialize Flags to identify the type of Certificates to replace based on CLI arguments + REPLACE_ROOT = REPLACE_MACHINESSL = REPLACE_STS = REPLACE_SOLUTIONUSERS = REPLACE_DATAENCIPHERMENT = REPLACE_LOOKUPSERVICE = REMOVE_TRUSTEDROOTS = REPLACE_SMS = REMOVE_NON_CA_TRUSTEDROOTS = UPDATE_VPXD_EXTENSIONS = False + if args.certType == 'machinessl': + REPLACE_MACHINESSL = True + elif args.certType == 'sts': + REPLACE_STS = True + elif args.certType == 'lookupservice': + constants.replace_only_lookupservice = True + REPLACE_LOOKUPSERVICE = True + elif args.certType == 'solutionusers': + REPLACE_SOLUTIONUSERS = True + elif args.certType == 'data-encipherment': + REPLACE_DATAENCIPHERMENT = True + elif args.certType == 'sms': + REPLACE_SMS = True + constants.replace_only_sms_roots = True + elif args.storeType in ['trusted_roots', 'trustedroots', 'TRUSTED_ROOTS', 'Trusted_Roots', 'Trusted_roots', 'TRUSTEDROOTS'] and args.certType.lower() in ['expired', 'expired_only', 'expiredonly']: + REMOVE_TRUSTEDROOTS = True + constants.remove_only_trusted_roots = True + elif args.storeType in ['trusted_roots', 'trustedroots', 'TRUSTED_ROOTS', 'Trusted_Roots', 'Trusted_roots', 'TRUSTEDROOTS'] and args.certType.lower() in ['nonca', 'non-ca']: + REMOVE_NON_CA_TRUSTEDROOTS = True + constants.remove_nonca_trusted_roots = True + elif args.ExtensionType: + if args.ExtensionType.lower() in ['all', 'eam', 'rbd','imagebuilder']: + UPDATE_VPXD_EXTENSIONS = True + constants.update_only_vpxd_extensions = True + if args.ExtensionType.lower() == "eam": + constants.extension_type = ["eam"] + constants.extensions = ["com.vmware.vim.eam"] + elif args.ExtensionType.lower() == "rbd": + constants.extension_type = ["rbd"] + constants.extensions = ["com.vmware.rbd"] + elif args.ExtensionType.lower() == "imagebuilder": + constants.extension_type = ["imagebuilder"] + constants.extensions = ["com.vmware.imagebuilder"] + else: + constants.extension_type = ['eam', 'rbd' , 'imagebuilder'] + elif args.certType == 'root': + REPLACE_ROOT = True + REPLACE_MACHINESSL = True + REPLACE_SOLUTIONUSERS = True + REPLACE_LOOKUPSERVICE = True + REMOVE_TRUSTEDROOTS = True + elif args.certType == 'all': + REPLACE_ROOT = True + REPLACE_MACHINESSL = True + REPLACE_STS = True + REPLACE_SOLUTIONUSERS = True + REPLACE_LOOKUPSERVICE = True + REPLACE_DATAENCIPHERMENT = True + REMOVE_TRUSTEDROOTS = True + REPLACE_SMS = True + elif args.certType == 'expired_only': + if (constants.expired_vmca or constants.expired_sts or constants.expired_machinessl or constants.expired_solutionusers or constants.expired_lookupservice or constants.expired_dataencipherment or constants.expired_sms or constants.expired_trustedroots): + REPLACE_ROOT = constants.expired_vmca + REPLACE_STS = constants.expired_sts + REPLACE_MACHINESSL = constants.expired_machinessl + REPLACE_SOLUTIONUSERS = constants.expired_solutionusers + REPLACE_LOOKUPSERVICE = constants.expired_lookupservice + REPLACE_DATAENCIPHERMENT = constants.expired_dataencipherment + REPLACE_SMS = constants.expired_sms + REMOVE_TRUSTEDROOTS = constants.expired_trustedroots + else: + print("\nValidity of Certificates:") + if (constants.remove_only_trusted_roots or constants.remove_nonca_trusted_roots): + print(root_certificates_validity) + else: + print(certs_validity) + print(root_certificates_validity) + logging.warning("There are NO EXPIRED CERTIFICATES on this vCenter Server, hence DID NOT replace any Certificates.\nIf you still want to replace the certificates, use any other arguments such as --certType all") + print("\nThere are NO EXPIRED CERTIFICATES on this vCenter Server, hence DID NOT replace any Certificates.\nIf you still want to replace the certificates, use any other arguments such as --certType all") + sys.exit(1) + else: + print("Please execute the script with valid arguments\n") + argparser.print_help() + sys.exit(1) + + if (constants.remove_nonca_trusted_roots) or (constants.remove_only_trusted_roots): + display_warning_message = False + elif constants.update_only_vpxd_extensions: + cert_replace_message = ( + "\nThis script will update the certificate thumbprint for VPXD extensions :\n" + "\t1. Services {0} needs to be restarted after updating the thumbprint\n" + ).format(constants.extension_type) + else: + cert_replace_message = ( + "\nThis script will replace the certificates on vCenter Server, please read below important points :\n" + "\t1. Services needs to be restarted for certificate replacement, you may do it manually or let the script do it\n" + "\t2. Services on partner VCs in Linked Mode also needs to be restarted after replacing STS (Secure Token Signing) certificate, as VCs in ELM uses same STS Certificate\n" + "\t Note: Point 2 is Not Applicable for vCenter Server 8.0, as service restart is not required for STS Certificate Replacement on 8.0.\n" + "\t3. Please make sure you have taken OFFLINE SNAPSHOT of all the VCs in the Linked Mode before continuing with the Certificate replacement\n" + ) + + #Displaying the Cert validity before replacement + if not (constants.update_only_vpxd_extensions): + if (constants.remove_only_trusted_roots or constants.remove_nonca_trusted_roots): + print(root_certificates_validity) + else: + print(certs_validity) + print(root_certificates_validity) + + #Initial messaging on the console + if (display_warning_message): + print(cert_replace_message) + if not (constants.silent_execution): + Customeragreement = constants.inputfunction("\nPlease read above points and enter YES to proceed further [[Yes/yes/YES/Y/y]] ? ") + if Customeragreement.lower() not in ['y','Y','Yes','YES','yes','yES','yeS']: + print(color_red("\nScript Terminated based on user selection")) + logging.error("Script Terminated based on user selection") + sys.exit() + else: + logging.info("Entered YES to proceed with operation") + + #Read Deployment Type and FQDN Type + print("\nReading Hostname Type & Deployment Type.\n...Waiting for Status") + if (get_deployment_parameters()): + print(color_green("......Success\n")) + else: + logging.error("Unable to read the Host Name Type and/or Deployment Type vCenter Server, script is terminating") + print(color_red("Unable to read the Host Name and/or Deployment Type of vCenter Server.\nPlease check availability of files \'system.hostname.type\' & \'deployment.node.type\' under \'/etc/vmware/install-defaults\' folder and retry.")) + print(color_red("......Failed\n")) + sys.exit() + + #Customized openssl functions to create the certificate with user specified Validity and Key Size will not work on Management nodes (VC pointing to external PSC), hence ignoring the values + if environment.deployment_type == "management" and (args.validityDays or args.keySize): + logging.warning("You have passed validityDays or keySize arguments to the script, these arguments will be ignored for Management Nodes (VC pointing to External PSC)") + print(color_red("You have passed validityDays or keySize arguments to the script, these arguments will be ignored for Management Nodes (VC pointing to External PSC)")) + constants.use_openssl_functions = False + + #Performs pre-check to start the required services for certificate replacement, also verifies the SSO Admin password + precheck() + + #Initialize the Certificate fields such as Country, Organlization etc + if not (constants.remove_only_trusted_roots or constants.remove_nonca_trusted_roots or constants.update_only_vpxd_extensions): + certcfg_ops.initialize_cert_fields() + + #User input for replacing STS Certificate if the Certificate is valid more than the mincertvalidity defined in the script(it's defined as 365 days) + sts_replace_flag = False + if REPLACE_STS: + if not (constants.silent_execution): + (sts_validity,sts_valid_days) = check_sts_certificate() + if (sts_validity): + if (sts_valid_days.days > constants.mincertvalidity): + sts_user_input = constants.inputfunction("STS (Token Signing) Certificate is Valid for more than 1 YEAR (Till - " + sts_validity.strftime('%Y-%m-%d-%H:%M:%SZ') + "). Do you really want to replace STS Certificate [Y/N] ? ") + if sts_user_input.lower() in ['y','Y','Yes','YES','yes','yES','yeS']: + logging.info("Script will replace STS Certificate based on User Input") + sts_replace_flag = True + else: + logging.info("Not replacing STS Certificate based on User Input") + else: + sts_replace_flag = True + else: + sts_replace_flag = True + else: + logging.info("This is a silent execution, so forcefully replacing STS Certificate without verifying the existing certificate validity") + sts_replace_flag = True + #User input for replacing VMCA Root Certificate if the Certificate is valid more than the mincertvalidity defined in the script(it's defined as 365 days) + vmca_replace_flag = False + if REPLACE_ROOT: + if not (constants.silent_execution): + (vmca_validity,vmca_valid_days) = check_certificate_validity(constants.vmca_root_path) + if vmca_valid_days.days > constants.mincertvalidity: + rootinput = constants.inputfunction("\nVMCA Root Certificate is valid for more than 1 YEAR (Till - " + vmca_validity.strftime('%Y-%m-%d-%H:%M:%SZ') + "), Do you really want to replace Root Certificate [Y/N] ? ") + if rootinput.lower() in ['y','Y','Yes','YES','yes','yES','yeS']: + logging.info("Script will replace VMCA Root Certificate based on User Input") + vmca_replace_flag = True + else: + logging.info("Not replacing VMCA Root Certificate based on User Input") + else: + logging.info("VMCA Certificate is already expired") + vmca_replace_flag = True + else: + logging.info("This is a silent execution, so forcefully replacing Root Certificate without verifying the existing certificate validity") + vmca_replace_flag = True + + if not (vmca_replace_flag): + if not (constants.silent_execution): + if is_cert_expired(constants.vmca_root_path): + rootinput = constants.inputfunction("VMCA Root Certificate is already Expired, VMCA Root and all other Certificates needs to be replaced, Do you want to replace VMCA Root and all other Certificates [Y/N] ? ") + if rootinput.lower() in ['y','Y','Yes','YES','yes','yES','yeS']: + logging.info("Script will replace VMCA Root Certificate as VMCA Root Certificate is already expired") + vmca_replace_flag = True + else: + logging.info("Script is exiting based on user input, please execute the script again with 'root' or 'all' argument") + sys.exit(1) + else: + logging.info("This is a silent execution, so forcefully replacing Root Certificate without user input as VMCA Root is already expired") + vmca_replace_flag = True + + if (constants.custom_validity): + if not (vmca_replace_flag): + adjust_default_cert_validity() + + #List the VECS stores and terminate if the list is empty/None + vecs_stores = get_vecs_stores() + if not vecs_stores: + print(color_red("Failed to list the VECS Stores, script is terminating")) + sys.exit() + elif constants.BACKUP_STORE not in vecs_stores: + vecs_ops.create_store(constants.BACKUP_STORE) + + #Call the specific Certificate replacement function based on the value of Command Line Argument --type, such as machinessl, sts, lookupservice etc.. + if environment.deployment_type == "management": + if (REPLACE_MACHINESSL): + replace_machine_ssl_certificate() + if (REPLACE_SOLUTIONUSERS): + replace_solution_user_certificate(vecs_stores) + if (REPLACE_DATAENCIPHERMENT): + replace_data_encipherment_certificate(vecs_stores) + if (REPLACE_SMS): + replace_sms_certificate(vecs_stores) + if (UPDATE_VPXD_EXTENSIONS): + update_vpxd_extensions() + elif environment.deployment_type in ["embedded","infrastructure"]: + if vmca_replace_flag: + replace_root_certificate() + if sts_replace_flag: + replace_sts_certificate() + if (REPLACE_MACHINESSL): + replace_machine_ssl_certificate() + if (REPLACE_LOOKUPSERVICE): + if "STS_INTERNAL_SSL_CERT" in vecs_stores: + replace_lookupservice_certificate() + elif (constants.replace_only_lookupservice): + print("\nThis vCenter Server DOES NOT have separate STS_INTERNAL_SSL_CERT store for LookupService, hence not taking any action.\nScript is terminating.") + logging.warning("This vCenter Server DOES NOT have separate STS_INTERNAL_SSL_CERT store for LookupService, hence not taking any action\nScript is terminating.") + sys.exit() + else: + logging.warning("LookupService Store STS_INTERNAL_SSL_CERT DOES NOT EXIST on this vCenter Server") + if (REPLACE_SOLUTIONUSERS): + replace_solution_user_certificate(vecs_stores) + if (REPLACE_DATAENCIPHERMENT): + replace_data_encipherment_certificate(vecs_stores) + if (REPLACE_SMS) and environment.deployment_type in ["embedded"]: + replace_sms_certificate(vecs_stores) + if (UPDATE_VPXD_EXTENSIONS): + update_vpxd_extensions() + if (REMOVE_TRUSTEDROOTS): + remove_expired_certs_from_trusted_roots() + if(REMOVE_NON_CA_TRUSTEDROOTS): + remove_non_ca_certs_from_trusted_roots() + else: + logging.error("Deployment Type %s does not match with Management/Embedded/Infrastructure" % environment.deployment_type) + print(color_red("Deployment Type %s does not match with Management/Embedded/Infrastructure" % environment.deployment_type)) + sys.exit() + + #Check user input to restart the services if any Certificate is replaced + check_service_restart() + + if (constants.auto_service_restart): + print("\nValidity of Certificates post replacement:") + (certs_validity, root_certificates_validity) = read_all_certs() + print(certs_validity) + print(root_certificates_validity) + if (constants.services_start_flag): + print("\nSuccessfully Completed the Certificate Replacement -> Total Execution Time ## %s seconds ##" % round((time.time() - start_time))) + logging.info("\nSuccessfully Completed the Certificate Replacement -> Total Execution Time ## %s seconds ##" % round((time.time() - start_time))) + else: + print("\Completed the Certificate Replacement, however there were failures while restarting Services -> Total Execution Time ## %s seconds ##" % round((time.time() - start_time))) + logging.info("\nCompleted the Certificate Replacement, however there were failures while restarting Services -> Total Execution Time ## %s seconds ##" % round((time.time() - start_time))) + logging.error("Script tried to restart the services, howerver there were failures during the restart operation, please restart all the Services manually using command \'service-control --stop --all && service-control --start --all\' for the changes to take effect") + print("\nScript tried to restart the services, howerver there were failures during the restart operation, please restart all the Services manually using command \'service-control --stop --all && service-control --start --all\' for the changes to take effect") + elif (not constants.cert_replaced) and constants.remove_only_trusted_roots: + print("\nSkipped the removal of Certificates from Trusted Roots store as there are no expired Certificates in the Roots Store -> Total Execution Time ## %s seconds ##" % round((time.time() - start_time))) + logging.info("\nSkipped the removal of Certificates from Trusted Roots store as there are no expired Certificates in the Roots Store -> Total Execution Time ## %s seconds ##" % round((time.time() - start_time))) + elif (constants.update_only_vpxd_extensions): + print("\nUpdated the Thumbprint of VPXD Extensions -> Total Execution Time ## %s seconds ##" % round((time.time() - start_time))) + logging.info("\nUpdated the Thumbprint of VPXD Extensions -> Total Execution Time ## %s seconds ##" % round((time.time() - start_time))) + elif (not constants.cert_replaced) and constants.remove_nonca_trusted_roots: + print("\nSkipped the removal of Certificates from Trusted Roots store as it does not have any Non-CA Certificates -> Total Execution Time ## %s seconds ##" % round((time.time() - start_time))) + logging.info("\nSkipped the removal of Certificates from Trusted Roots store as it does not have any Non-CA Certificates -> Total Execution Time ## %s seconds ##" % round((time.time() - start_time))) + elif ((constants.replace_only_sms_roots) and (not constants.cert_replaced ) ): + print("\nSkipped the replacement of SMS Certificate as the the Cert is not Expired -> Total Execution Time ## %s seconds ##" % round((time.time() - start_time))) + logging.info("\nkipped the replacement of SMS Certificate as the the Cert is not Expired -> Total Execution Time ## %s seconds ##" % round((time.time() - start_time))) + else: + if not (constants.update_only_vpxd_extensions): + (certs_validity, root_certificates_validity) = read_all_certs() + if (constants.remove_only_trusted_roots or constants.remove_nonca_trusted_roots or constants.update_only_vpxd_extensions): + print("\nCertificate status post removal from TRUSTED_ROOTS:") + print(root_certificates_validity) + else: + print("\nValidity of Certificates post replacement:") + print(certs_validity) + print(root_certificates_validity) + print("\nSuccessfully Completed the Certificate Replacement -> Total Execution Time ## %s seconds ##" % round((time.time() - start_time))) + logging.info("\nSuccessfully Completed the Certificate Replacement -> Total Execution Time ## %s seconds ##" % round((time.time() - start_time))) + logging.info("Please restart all the Services using command \'service-control --stop --all && service-control --start --all\' for the changes to take effect") + print("\nPlease restart all the Services using below command for the changes to take effect.") + print(color_cyan("\tservice-control --stop --all && service-control --start --all")) + + #Remove the temporary directory created for certificate replacement + shutil.rmtree(constants.result_directory,ignore_errors=True) + +""" +Initializing the various classes required for the Certificate Replacement Operation +These variables will be used to call the functions in those classes +""" +vecs_ops = VecsCli() +dircli_ops = DirCli() +certool_ops = Certool() +certcfg_ops = CertCfg() +openssl_ops = OpensslCli() +vmafdclient_ops = vmafdClient() + +""" +Main function which initializes logging, verifies the arguments +And, calls the replace_certificates function for the certificate replacement operations +""" +def main(): + #setup_logging() + argparser = parse_arguments() + + if len(sys.argv) == 1: + argparser.print_help() + sys.exit(1) + + args = argparser.parse_args() + + setup_logging() + if args.certType not in ['expired_only', 'machinessl', 'sts', 'lookupservice', 'solutionusers', 'root', 'all', 'data-encipherment', 'sms']: + if args.storeType not in ['trusted_roots', 'trustedroots', 'TRUSTED_ROOTS', 'Trusted_Roots', 'Trusted_roots', 'TRUSTEDROOTS']: + if args.ExtensionType not in ['all', 'ALL', 'All', 'EAM', 'Eam', 'eam', 'RBD', 'Rbd', 'rbd', 'IMAGEBUILDER', 'Imagebuilder', 'imagebuilder']: + argparser.print_help() + sys.exit(1) + + if args.force_encipherment_replace: + if args.force_encipherment_replace in ['true', 'TRUE', 'True']: + constants.force_encipherment_cert = True + elif args.force_encipherment_replace in ['false', 'FALSE', 'False']: + constants.force_encipherment_cert = False + else: + argparser.print_help() + sys.exit(1) + + if (args.serviceRestart): + if args.serviceRestart in ['true', 'TRUE', 'True']: + constants.auto_service_restart = True + elif args.serviceRestart in ['false', 'FALSE', 'False']: + constants.auto_service_restart = False + else: + argparser.print_help() + sys.exit(1) + + if (args.additionalSAN): + constants.additional_san = True + environment.additional_fqdns = ' '.join([str(elem) for elem in args.additionalSAN]) + environment.additional_fqdns = ((environment.additional_fqdns).replace(" ","")).split(",") + + if (args.validityDays): + try: + if int(args.validityDays) in range(1,constants.MAX_CERT_VALIDITY): + constants.use_openssl_functions = True + constants.DEFAULT_VALIDITY = int(args.validityDays) + constants.DEFAULT_STS_VALIDITY = int(args.validityDays) + constants.custom_validity = True + else: + print("validityDays will accept number of days as values between 2 to 3650\n") + argparser.print_help() + sys.exit(1) + except ValueError: + print("validityDays will accept number of days as values between 2 to 3650\n") + argparser.print_help() + sys.exit(1) + + if (args.keySize): + if args.keySize in range(2048, 5120, 1024): + if args.keySize != "2048": + constants.DEFAULT_KEY_SIZE = str(args.keySize) + constants.use_openssl_functions = True + else: + print("keySize will not accept any values other than 2048/3072/4096") + print("Script is Terminating") + sys.exit(1) + + if (args.silent): + if args.silent in ['true', 'TRUE', 'True'] and (args.password) and (args.serviceRestart): + constants.silent_execution = True + sys.stdout = open(os.devnull, 'w') + elif args.silent in ['false', 'FALSE', 'False']: + constants.silent_replace = False + else: + argparser.print_help() + sys.exit(1) + + if (args.debug): + logging.info("Enabling Debug logging") + logging.basicConfig(level=logging.DEBUG) + + replace_certificates(args,argparser) + +if __name__ == '__main__': + exit(main()) + diff --git a/vCert-6.1.1-20260401/config/check_cert/op_check_00-reset_results.yaml b/vCert-6.1.1-20260401/config/check_cert/op_check_00-reset_results.yaml new file mode 100644 index 0000000..defe1b9 --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_cert/op_check_00-reset_results.yaml @@ -0,0 +1,7 @@ +type: operation +description: +title: +label: +entry_point: + module: operation.check_certificate + method: reset_certificate_status diff --git a/vCert-6.1.1-20260401/config/check_cert/op_check_01-certificate_status.yaml b/vCert-6.1.1-20260401/config/check_cert/op_check_01-certificate_status.yaml new file mode 100644 index 0000000..ccfce9b --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_cert/op_check_01-certificate_status.yaml @@ -0,0 +1,7 @@ +type: operation +description: +title: "Checking Certificate Status" +label: +entry_point: + module: operation.check_certificate + method: check_certificate_status diff --git a/vCert-6.1.1-20260401/config/check_cert/op_check_02-sts_tenant_certs.yaml b/vCert-6.1.1-20260401/config/check_cert/op_check_02-sts_tenant_certs.yaml new file mode 100644 index 0000000..c82214f --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_cert/op_check_02-sts_tenant_certs.yaml @@ -0,0 +1,7 @@ +type: operation +description: "Check machine SSL certificate" +title: "Checking STS Signing Certs & Signing Chains" +label: +entry_point: + module: operation.check_certificate + method: check_sts_tenant_certificates diff --git a/vCert-6.1.1-20260401/config/check_cert/op_check_03-ca_certs_in_vmdir.yaml b/vCert-6.1.1-20260401/config/check_cert/op_check_03-ca_certs_in_vmdir.yaml new file mode 100644 index 0000000..8640d6e --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_cert/op_check_03-ca_certs_in_vmdir.yaml @@ -0,0 +1,7 @@ +type: operation +description: +title: "Checking CA certificates in VMDir [by CN(id)]" +label: +entry_point: + module: operation.check_certificate + method: check_ca_certificates_in_vmdir diff --git a/vCert-6.1.1-20260401/config/check_cert/op_check_04-ca_certs_in_vecs.yaml b/vCert-6.1.1-20260401/config/check_cert/op_check_04-ca_certs_in_vecs.yaml new file mode 100644 index 0000000..61fa540 --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_cert/op_check_04-ca_certs_in_vecs.yaml @@ -0,0 +1,7 @@ +type: operation +description: +title: "Checking CA certificates in VECS [by Alias]" +label: +entry_point: + module: operation.check_certificate + method: check_ca_certificates_in_vecs diff --git a/vCert-6.1.1-20260401/config/check_cert/op_check_05-vecs_store_permissions.yaml b/vCert-6.1.1-20260401/config/check_cert/op_check_05-vecs_store_permissions.yaml new file mode 100644 index 0000000..6e53253 --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_cert/op_check_05-vecs_store_permissions.yaml @@ -0,0 +1,7 @@ +type: operation +description: +title: "Checking VECS Stores" +label: +entry_point: + module: operation.check_configuration + method: check_vecs_store_permissions diff --git a/vCert-6.1.1-20260401/config/check_cert/op_check_06-service_principals.yaml b/vCert-6.1.1-20260401/config/check_cert/op_check_06-service_principals.yaml new file mode 100644 index 0000000..e2f6125 --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_cert/op_check_06-service_principals.yaml @@ -0,0 +1,7 @@ +type: operation +description: +title: "Checking Service Principals" +label: +entry_point: + module: operation.check_certificate + method: check_service_principals diff --git a/vCert-6.1.1-20260401/config/check_cert/op_check_07-crls.yaml b/vCert-6.1.1-20260401/config/check_cert/op_check_07-crls.yaml new file mode 100644 index 0000000..8fe65b6 --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_cert/op_check_07-crls.yaml @@ -0,0 +1,7 @@ +type: operation +description: +title: "Checking Certificate Revocation Lists" +label: +entry_point: + module: operation.check_certificate + method: check_crls diff --git a/vCert-6.1.1-20260401/config/check_cert/op_check_08-identity-source.yaml b/vCert-6.1.1-20260401/config/check_cert/op_check_08-identity-source.yaml new file mode 100644 index 0000000..c9e330a --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_cert/op_check_08-identity-source.yaml @@ -0,0 +1,7 @@ +type: operation +description: +title: +label: +entry_point: + module: operation.check_certificate + method: check_identity_source_certificates diff --git a/vCert-6.1.1-20260401/config/check_cert/op_check_09-ssl_trust_anchors.yaml b/vCert-6.1.1-20260401/config/check_cert/op_check_09-ssl_trust_anchors.yaml new file mode 100644 index 0000000..383c3a8 --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_cert/op_check_09-ssl_trust_anchors.yaml @@ -0,0 +1,7 @@ +type: operation +description: +title: "Checking SSL Trust Anchors" +label: +entry_point: + module: operation.check_certificate + method: check_ssl_trust_anchors diff --git a/vCert-6.1.1-20260401/config/check_cert/op_check_10-vc_ext_thumbprints.yaml b/vCert-6.1.1-20260401/config/check_cert/op_check_10-vc_ext_thumbprints.yaml new file mode 100644 index 0000000..302ec1b --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_cert/op_check_10-vc_ext_thumbprints.yaml @@ -0,0 +1,7 @@ +type: operation +description: +title: "Checking vCenter Extension Thumbprints" +label: +entry_point: + module: operation.check_certificate + method: check_vcenter_extension_thumbprints diff --git a/vCert-6.1.1-20260401/config/check_cert/op_check_11-vmca_vcdb.yaml b/vCert-6.1.1-20260401/config/check_cert/op_check_11-vmca_vcdb.yaml new file mode 100644 index 0000000..e547e2e --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_cert/op_check_11-vmca_vcdb.yaml @@ -0,0 +1,7 @@ +type: operation +description: +title: 'Checking VMCA Configurations in VCDB' +label: +entry_point: + module: operation.check_configuration + method: check_vmca_configuration_vcdb \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/check_cert/op_check_12-sts_config.yaml b/vCert-6.1.1-20260401/config/check_cert/op_check_12-sts_config.yaml new file mode 100644 index 0000000..4a4b1fe --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_cert/op_check_12-sts_config.yaml @@ -0,0 +1,7 @@ +type: operation +description: +title: +label: +entry_point: + module: operation.check_configuration + method: check_sts_configuration \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/check_cert/op_check_99-show_check_result_summary.yaml b/vCert-6.1.1-20260401/config/check_cert/op_check_99-show_check_result_summary.yaml new file mode 100644 index 0000000..cd34c0c --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_cert/op_check_99-show_check_result_summary.yaml @@ -0,0 +1,7 @@ +type: operation +description: +title: +label: +entry_point: + module: operation.check_certificate + method: show_check_result_summary diff --git a/vCert-6.1.1-20260401/config/check_config/op_check_01-ssl_interception.yaml b/vCert-6.1.1-20260401/config/check_config/op_check_01-ssl_interception.yaml new file mode 100644 index 0000000..feeec91 --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_config/op_check_01-ssl_interception.yaml @@ -0,0 +1,7 @@ +type: operation +description: +title: "Checking for SSL Interception" +label: "Check for SSL Interception" +entry_point: + module: operation.check_certificate + method: check_ssl_interception \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/check_config/op_check_02-sts.yaml b/vCert-6.1.1-20260401/config/check_config/op_check_02-sts.yaml new file mode 100644 index 0000000..3b99616 --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_config/op_check_02-sts.yaml @@ -0,0 +1,9 @@ +type: operation-group +description: +title: +label: 'Check STS server certificate and configuration' +operations: + - type: single + config: 'config/op_prevalidate_credential.yaml' + - type: single + config: 'config/check_config/sts_config/op_check_sts_config.yaml' \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/check_config/op_check_04-vecs_store_permissions.yaml b/vCert-6.1.1-20260401/config/check_config/op_check_04-vecs_store_permissions.yaml new file mode 100644 index 0000000..f7908a2 --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_config/op_check_04-vecs_store_permissions.yaml @@ -0,0 +1,10 @@ +type: operation +description: +title: "Checking VECS Stores" +label: "Check VECS store status and permissions" +entry_point: + module: operation.check_configuration + method: check_vecs_store_permissions +arguments: + - name: check_only + value: false \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/check_config/ssl_interception/menu_manage_ssl_interception.yaml b/vCert-6.1.1-20260401/config/check_config/ssl_interception/menu_manage_ssl_interception.yaml new file mode 100644 index 0000000..112ad5a --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_config/ssl_interception/menu_manage_ssl_interception.yaml @@ -0,0 +1,13 @@ +type: menu +description: +title: "SSL Interception Certificate Management" +label: "SSL Interception Certificate Management" +items: + - type: multiple + config: "config/check_config/ssl_interception/op_*.yaml" + - type: navigation:return + label: "Return to previous menu" + key: "R" + default: True + hidden: True + use_label_as_key: True \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/check_config/ssl_interception/op_01_download_certs_proxy.yaml b/vCert-6.1.1-20260401/config/check_config/ssl_interception/op_01_download_certs_proxy.yaml new file mode 100644 index 0000000..48dba3c --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_config/ssl_interception/op_01_download_certs_proxy.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: +title: +label: "Download and import CA certificates from the proxy" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "download_proxy_ca_certificates" \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/check_config/ssl_interception/op_02_provide_proxy_ca_certs.yaml b/vCert-6.1.1-20260401/config/check_config/ssl_interception/op_02_provide_proxy_ca_certs.yaml new file mode 100644 index 0000000..99ffb98 --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_config/ssl_interception/op_02_provide_proxy_ca_certs.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: +title: +label: "Import CA certificate(s) from a file" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "import_proxy_ca_certificates" \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/check_config/sts_config/op_check_sts_config.yaml b/vCert-6.1.1-20260401/config/check_config/sts_config/op_check_sts_config.yaml new file mode 100644 index 0000000..f4ed7f6 --- /dev/null +++ b/vCert-6.1.1-20260401/config/check_config/sts_config/op_check_sts_config.yaml @@ -0,0 +1,7 @@ +type: operation +description: +title: +label: +entry_point: + module: operation.manage_configuration + method: manage_sts_configuration \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/esxi_cert_actions/op_esxi_action_01-manage_cert.yaml b/vCert-6.1.1-20260401/config/esxi_cert_actions/op_esxi_action_01-manage_cert.yaml new file mode 100644 index 0000000..e5d86c8 --- /dev/null +++ b/vCert-6.1.1-20260401/config/esxi_cert_actions/op_esxi_action_01-manage_cert.yaml @@ -0,0 +1,7 @@ +type: operation +description: "Manage ESXi Certificates" +title: "" +label: "Check ESXi/vCenter certificate trust" +entry_point: + module: operation.esxi_certificate_operations + method: check_esxi_vcenter_trust diff --git a/vCert-6.1.1-20260401/config/esxi_cert_actions/op_esxi_action_02-check_cert_against_vCenter_db.yaml b/vCert-6.1.1-20260401/config/esxi_cert_actions/op_esxi_action_02-check_cert_against_vCenter_db.yaml new file mode 100644 index 0000000..813f76c --- /dev/null +++ b/vCert-6.1.1-20260401/config/esxi_cert_actions/op_esxi_action_02-check_cert_against_vCenter_db.yaml @@ -0,0 +1,7 @@ +type: operation +description: "Check ESXi certificate against vCenter database" +title: "" +label: "Check ESXi certificate against vCenter database" +entry_point: + module: operation.esxi_certificate_operations + method: check_esxi_certificate_against_vcdb diff --git a/vCert-6.1.1-20260401/config/esxi_cert_actions/op_esxi_action_03-replace_cert.yaml b/vCert-6.1.1-20260401/config/esxi_cert_actions/op_esxi_action_03-replace_cert.yaml new file mode 100644 index 0000000..202a2a5 --- /dev/null +++ b/vCert-6.1.1-20260401/config/esxi_cert_actions/op_esxi_action_03-replace_cert.yaml @@ -0,0 +1,7 @@ +type: operation +description: "Replace ESXi certificate" +title: "" +label: "Replace ESXi certificate" +entry_point: + module: operation.esxi_certificate_operations + method: replace_esxi_certificate \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/generate_report/op_genreport_01-default-report.yaml b/vCert-6.1.1-20260401/config/generate_report/op_genreport_01-default-report.yaml new file mode 100644 index 0000000..c83e1a2 --- /dev/null +++ b/vCert-6.1.1-20260401/config/generate_report/op_genreport_01-default-report.yaml @@ -0,0 +1,37 @@ +type: operation-group +description: +title: +label: +operations: + - type: single + entry_point: + module: operation.generate_report + method: generate_report_ca_certs_in_vecs + - type: single + entry_point: + module: operation.generate_report + method: generate_report_ca_certs_in_vmdir + - type: single + entry_point: + module: operation.generate_report + method: generate_report_solution_user_certs_in_vmdir + - type: single + entry_point: + module: operation.generate_report + method: generate_report_sts_tenant_certs + - type: single + entry_point: + module: operation.generate_report + method: generate_report_ca_certs_sca + - type: single + entry_point: + module: operation.generate_report + method: generate_report_ca_certs_ad_ldaps + - type: single + entry_point: + module: operation.generate_report + method: generate_report_certs_in_filesystem + - type: single + entry_point: + module: operation.generate_report + method: generate_report_ssl_trust_anchors diff --git a/vCert-6.1.1-20260401/config/logging.yaml b/vCert-6.1.1-20260401/config/logging.yaml new file mode 100644 index 0000000..fd8032c --- /dev/null +++ b/vCert-6.1.1-20260401/config/logging.yaml @@ -0,0 +1,30 @@ +version: 1 + +disable_existing_loggers: false + +formatters: + file_format: + format: '%(asctime)s - [%(name)s - %(funcName)s] - %(levelname)s - %(message)s' + datefmt: '%Y-%m-%dT%H:%M:%S' + console_format: + format: '%(asctime)s - %(levelname)s - %(message)s' + datefmt: '%Y-%m-%dT%H:%M:%S' + +handlers: + console: + class: logging.StreamHandler + level: CRITICAL + formatter: console_format + stream: ext://sys.stdout + file: + class: logging.handlers.RotatingFileHandler + level: DEBUG + formatter: file_format + filename: /var/log/vmware/vCert/vCert.log + # 20MB file size + maxBytes: 20000000 + backupCount: 5 + +root: + level: DEBUG + handlers: [console, file] diff --git a/vCert-6.1.1-20260401/config/manage_cert/machine_ssl/custom_ca_signed/op_01_generate-csr.yaml b/vCert-6.1.1-20260401/config/manage_cert/machine_ssl/custom_ca_signed/op_01_generate-csr.yaml new file mode 100644 index 0000000..2b9d573 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/machine_ssl/custom_ca_signed/op_01_generate-csr.yaml @@ -0,0 +1,12 @@ +type: operation +description: +title: +label: "Generate Certificate Signing Request and Private Key" +entry_point: + module: operation.manage_certificate + method: generate_csr_and_private_key +arguments: + - name: cert_usage + value: "machine-ssl" + - name: custom_openssl_config + value: false diff --git a/vCert-6.1.1-20260401/config/manage_cert/machine_ssl/custom_ca_signed/op_02_generate-csr-openssl-config.yaml b/vCert-6.1.1-20260401/config/manage_cert/machine_ssl/custom_ca_signed/op_02_generate-csr-openssl-config.yaml new file mode 100644 index 0000000..d5b3b18 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/machine_ssl/custom_ca_signed/op_02_generate-csr-openssl-config.yaml @@ -0,0 +1,12 @@ +type: operation +description: +title: +label: "Generate Certificate Signing Request and Private Key from custom OpenSSL configuration file" +entry_point: + module: operation.manage_certificate + method: generate_csr_and_private_key +arguments: + - name: cert_usage + value: "machine-ssl" + - name: custom_openssl_config + value: true diff --git a/vCert-6.1.1-20260401/config/manage_cert/machine_ssl/custom_ca_signed/op_03_import-certificate.yaml b/vCert-6.1.1-20260401/config/manage_cert/machine_ssl/custom_ca_signed/op_03_import-certificate.yaml new file mode 100644 index 0000000..cb169fa --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/machine_ssl/custom_ca_signed/op_03_import-certificate.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: +title: +label: "Import CA-signed Certificate and Key" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "import_custom_ca_signed_machine_ssl_certificate" diff --git a/vCert-6.1.1-20260401/config/manage_cert/machine_ssl/menu_custom-ca-signed-cert.yaml b/vCert-6.1.1-20260401/config/manage_cert/machine_ssl/menu_custom-ca-signed-cert.yaml new file mode 100644 index 0000000..a768e1b --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/machine_ssl/menu_custom-ca-signed-cert.yaml @@ -0,0 +1,13 @@ +type: menu +description: +title: "Replace Machine SSL certificate with a custom CA-signed certificate" +label: "Replace Machine SSL certificate with a custom CA-signed certificate" +items: + - type: multiple + config: "config/manage_cert/machine_ssl/custom_ca_signed/op_*.yaml" + - type: navigation:return + label: "Return to previous menu" + key: "R" + default: True + hidden: True + use_label_as_key: True diff --git a/vCert-6.1.1-20260401/config/manage_cert/machine_ssl/op_vmca-signed-cert.yaml b/vCert-6.1.1-20260401/config/manage_cert/machine_ssl/op_vmca-signed-cert.yaml new file mode 100644 index 0000000..abc7c46 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/machine_ssl/op_vmca-signed-cert.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: +title: +label: "Replace Machine SSL certificate with a VMCA-signed certificate" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "manage_machine_ssl_certificate_with_vmca_signed" diff --git a/vCert-6.1.1-20260401/config/manage_cert/menu_machine_ssl_cert.yaml b/vCert-6.1.1-20260401/config/manage_cert/menu_machine_ssl_cert.yaml new file mode 100644 index 0000000..921aa91 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/menu_machine_ssl_cert.yaml @@ -0,0 +1,17 @@ +type: menu +description: "Manage Machine SSL certificate" +title: "Select Machine SSL Certificate Replacement Method" +label: "Machine SSL certificate" +items: + - type: single + config: "config/manage_cert/machine_ssl/op_vmca-signed-cert.yaml" + - type: single + config: "config/manage_cert/machine_ssl/menu_custom-ca-signed-cert.yaml" + - type: navigation:return + label: "Return to previous menu" + key: "R" + default: true + hidden: true + +input: + text: "Select an option [Return to the previous menu]: " \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/menu_sms_certs.yaml b/vCert-6.1.1-20260401/config/manage_cert/menu_sms_certs.yaml new file mode 100644 index 0000000..d6b2f0b --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/menu_sms_certs.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: "Manage CA certificate in the SMS VECS Store" +title: +label: "SMS certificates" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "manage_sms_certificates" \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/menu_solution_user_cert.yaml b/vCert-6.1.1-20260401/config/manage_cert/menu_solution_user_cert.yaml new file mode 100644 index 0000000..f71f2d7 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/menu_solution_user_cert.yaml @@ -0,0 +1,17 @@ +type: menu +description: "Manage Solution User certificates" +title: "Select Solution User Certificate Replacement Method" +label: "Solution User certificates" +items: + - type: single + config: "config/manage_cert/soluser/op_vmca-signed-cert.yaml" + - type: single + config: "config/manage_cert/soluser/menu_custom-ca-signed-cert.yaml" + - type: navigation:return + label: "Return to previous menu" + key: "R" + default: true + hidden: true + +input: + text: "Select an option [Return to the previous menu]: " \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/menu_vmca_cert.yaml b/vCert-6.1.1-20260401/config/manage_cert/menu_vmca_cert.yaml new file mode 100644 index 0000000..bc32228 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/menu_vmca_cert.yaml @@ -0,0 +1,20 @@ +type: menu +description: 'Manage VMCA certificate' +title: 'Select VMCA Certificate Replacement Method' +label: 'VMCA certificate' +items: + - type: single + config: 'config/manage_cert/vmca/op_replace-vmca-cert.yaml' + - type: single + config: 'config/manage_cert/vmca/op_replace-vmca-cert-and-reset-all.yaml' + - type: single + config: 'config/manage_cert/vmca/menu_custom-ca-signed-vmca-cert.yaml' + - type: navigation:return + label: 'Return to previous menu' + key: 'R' + default: true + hidden: true + +input: + text: 'Select an option [Return to the previous menu]: ' + \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/op_ca_certs_in_vecs.yaml b/vCert-6.1.1-20260401/config/manage_cert/op_ca_certs_in_vecs.yaml new file mode 100644 index 0000000..a302f78 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/op_ca_certs_in_vecs.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: "Manage CA certificate in VECS" +title: +label: "CA certificates in VECS Directory" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "manage_ca_certificates_in_vecs" diff --git a/vCert-6.1.1-20260401/config/manage_cert/op_ca_certs_in_vmdir.yaml b/vCert-6.1.1-20260401/config/manage_cert/op_ca_certs_in_vmdir.yaml new file mode 100644 index 0000000..c37600e --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/op_ca_certs_in_vmdir.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: "Manage CA certificate in VMware Directory" +title: +label: "CA certificates in VMware Directory" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "manage_ca_certificates_in_vmdir" diff --git a/vCert-6.1.1-20260401/config/manage_cert/op_clear_expired_backup_store.yaml b/vCert-6.1.1-20260401/config/manage_cert/op_clear_expired_backup_store.yaml new file mode 100644 index 0000000..b913645 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/op_clear_expired_backup_store.yaml @@ -0,0 +1,7 @@ +type: operation +title: 'Clear Expired Certificates in BACKUP_STORE in VECS' +label: 'Clear expired certificates in BACKUP_STORE in VECS' +condition: 'HAS_BACKUP_STORE' +entry_point: + module: operation.manage_certificate + method: manage_expired_backup_store \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/op_clear_machine_ssl_csr.yaml b/vCert-6.1.1-20260401/config/manage_cert/op_clear_machine_ssl_csr.yaml new file mode 100644 index 0000000..1f4307b --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/op_clear_machine_ssl_csr.yaml @@ -0,0 +1,7 @@ +type: operation +title: +label: 'Clear Machine SSL CSR in VECS' +condition: 'HAS_MACHINE_SSL_CSR' +entry_point: + module: operation.manage_certificate + method: manage_machine_ssl_csr \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/op_clear_trusted_root_crls.yaml b/vCert-6.1.1-20260401/config/manage_cert/op_clear_trusted_root_crls.yaml new file mode 100644 index 0000000..9ca1eca --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/op_clear_trusted_root_crls.yaml @@ -0,0 +1,6 @@ +type: operation +title: 'Clear TRUSTED_ROOT_CRLS store in VECS' +label: 'Clear TRUSTED_ROOT_CRLS store in VECS' +entry_point: + module: operation.manage_certificate + method: clear_trusted_root_crls \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/op_data-encipherment.yaml b/vCert-6.1.1-20260401/config/manage_cert/op_data-encipherment.yaml new file mode 100644 index 0000000..f17028e --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/op_data-encipherment.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: "Manage Data Encipherment certificate" +title: +label: "Data Encipherment certificate" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "manage_data_encipherment_certificate" \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/op_ldaps_identity_source.yaml b/vCert-6.1.1-20260401/config/manage_cert/op_ldaps_identity_source.yaml new file mode 100644 index 0000000..15ad32c --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/op_ldaps_identity_source.yaml @@ -0,0 +1,12 @@ +type: operation-group +description: 'Manage LDAPS Identity Source certificates' +title: +label: 'LDAPS Identity Source certificates' +condition: 'HAS_LDAPS_IDENTITY_SOURCE' +operations: + - type: single + config: 'config/op_prevalidate_credential.yaml' + - type: single + entry_point: + module: 'operation.manage_certificate' + method: 'manage_ldaps_identity_source_certificates' \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/op_manage-vc-ext-thumbprints.yaml b/vCert-6.1.1-20260401/config/manage_cert/op_manage-vc-ext-thumbprints.yaml new file mode 100644 index 0000000..122ef69 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/op_manage-vc-ext-thumbprints.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: +title: +label: "vCenter Extension thumbprints" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "manage_vcenter_extension_thumbprints" diff --git a/vCert-6.1.1-20260401/config/manage_cert/op_smart_card.yaml b/vCert-6.1.1-20260401/config/manage_cert/op_smart_card.yaml new file mode 100644 index 0000000..0d4ef93 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/op_smart_card.yaml @@ -0,0 +1,12 @@ +type: operation-group +description: 'Manage Smart Card CA certificates' +title: +label: 'Smart Card CA certificates' +condition: 'CAC_CONFIGURED' +operations: + - type: single + config: 'config/op_prevalidate_credential.yaml' + - type: single + entry_point: + module: 'operation.manage_certificate' + method: 'manage_smart_card_certificates' \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/op_sts_signing_certs.yaml b/vCert-6.1.1-20260401/config/manage_cert/op_sts_signing_certs.yaml new file mode 100644 index 0000000..8b98be8 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/op_sts_signing_certs.yaml @@ -0,0 +1,16 @@ +type: operation-group +description: "Manage STS signing certificates" +title: +label: "STS signing certificates" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + config: "config/view_cert/op_view_11-sts.yaml" + - type: single + entry_point: + module: "lib.menu" + method: "Menu.run_menu_from_config" + arguments: + - name: "config" + value: "config/manage_cert/sts_signing/menu_sts-signing-certs.yaml" diff --git a/vCert-6.1.1-20260401/config/manage_cert/sms/menu_manage_sms.yaml b/vCert-6.1.1-20260401/config/manage_cert/sms/menu_manage_sms.yaml new file mode 100644 index 0000000..bb3be31 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/sms/menu_manage_sms.yaml @@ -0,0 +1,13 @@ +type: menu +description: +title: "Manage SMS Certificates" +label: +items: + - type: multiple + config: "config/manage_cert/sms/op_*.yaml" + - type: navigation:return + label: "Return to previous menu" + key: "R" + default: True + hidden: True + use_label_as_key: True \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/sms/op_01-renew_sms_self_signed.yaml b/vCert-6.1.1-20260401/config/manage_cert/sms/op_01-renew_sms_self_signed.yaml new file mode 100644 index 0000000..9684ee0 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/sms/op_01-renew_sms_self_signed.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: +title: "Replace SMS self-signed certificate" +label: "Replace SMS self-signed certificate" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "manage_sms_certificate_with_self_signed" \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/sms/op_02-renew_sps_vmca_signed.yaml b/vCert-6.1.1-20260401/config/manage_cert/sms/op_02-renew_sps_vmca_signed.yaml new file mode 100644 index 0000000..ad4be86 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/sms/op_02-renew_sps_vmca_signed.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: +title: +label: "Replace SPS-extension certificate" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "manage_sps_certificate_with_vmca_signed" \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/soluser/custom_ca_signed/op_01_generate-csr.yaml b/vCert-6.1.1-20260401/config/manage_cert/soluser/custom_ca_signed/op_01_generate-csr.yaml new file mode 100644 index 0000000..4532bdf --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/soluser/custom_ca_signed/op_01_generate-csr.yaml @@ -0,0 +1,10 @@ +type: operation +description: +title: +label: "Generate Certificate Signing Request and Private Key" +entry_point: + module: operation.reset_certificate + method: generate_csr_and_private_key_of_soluser +arguments: + - name: custom_openssl_config + value: false diff --git a/vCert-6.1.1-20260401/config/manage_cert/soluser/custom_ca_signed/op_02_generate-csr-openssl-config.yaml b/vCert-6.1.1-20260401/config/manage_cert/soluser/custom_ca_signed/op_02_generate-csr-openssl-config.yaml new file mode 100644 index 0000000..e2adee9 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/soluser/custom_ca_signed/op_02_generate-csr-openssl-config.yaml @@ -0,0 +1,10 @@ +type: operation +description: +title: +label: "Generate Certificate Signing Request and Private Key from custom OpenSSL configuration file" +entry_point: + module: operation.reset_certificate + method: generate_csr_and_private_key_of_soluser +arguments: + - name: custom_openssl_config + value: true diff --git a/vCert-6.1.1-20260401/config/manage_cert/soluser/custom_ca_signed/op_03_import-certificate.yaml b/vCert-6.1.1-20260401/config/manage_cert/soluser/custom_ca_signed/op_03_import-certificate.yaml new file mode 100644 index 0000000..29edd57 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/soluser/custom_ca_signed/op_03_import-certificate.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: +title: +label: "Import CA-signed Certificate and Key" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: operation.manage_certificate + method: manage_solution_user_certificate_with_custom_ca_signed diff --git a/vCert-6.1.1-20260401/config/manage_cert/soluser/menu_custom-ca-signed-cert.yaml b/vCert-6.1.1-20260401/config/manage_cert/soluser/menu_custom-ca-signed-cert.yaml new file mode 100644 index 0000000..9c50ece --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/soluser/menu_custom-ca-signed-cert.yaml @@ -0,0 +1,13 @@ +type: menu +description: +title: "Replace Solution User certificates with CA-signed certificates (not recommended)" +label: "Replace Solution User certificates with CA-signed certificates (not recommended)" +items: + - type: multiple + config: "config/manage_cert/soluser/custom_ca_signed/op_*.yaml" + - type: navigation:return + label: "Return to previous menu" + key: "R" + default: True + hidden: True + use_label_as_key: True diff --git a/vCert-6.1.1-20260401/config/manage_cert/soluser/op_vmca-signed-cert.yaml b/vCert-6.1.1-20260401/config/manage_cert/soluser/op_vmca-signed-cert.yaml new file mode 100644 index 0000000..055328a --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/soluser/op_vmca-signed-cert.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: +title: +label: "Replace Solution User certificate with a VMCA-signed certificate" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.reset_certificate" + method: "manage_solution_user_certificate_with_vmca_signed" diff --git a/vCert-6.1.1-20260401/config/manage_cert/sts_signing/custom_ca_signed/op_01_generate-csr.yaml b/vCert-6.1.1-20260401/config/manage_cert/sts_signing/custom_ca_signed/op_01_generate-csr.yaml new file mode 100644 index 0000000..c8890a1 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/sts_signing/custom_ca_signed/op_01_generate-csr.yaml @@ -0,0 +1,12 @@ +type: operation +description: +title: +label: "Generate Certificate Signing Request and Private Key" +entry_point: + module: operation.manage_certificate + method: generate_csr_and_private_key +arguments: + - name: cert_usage + value: "sso-sts" + - name: custom_openssl_config + value: false diff --git a/vCert-6.1.1-20260401/config/manage_cert/sts_signing/custom_ca_signed/op_02_generate-csr-openssl-config.yaml b/vCert-6.1.1-20260401/config/manage_cert/sts_signing/custom_ca_signed/op_02_generate-csr-openssl-config.yaml new file mode 100644 index 0000000..21585c1 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/sts_signing/custom_ca_signed/op_02_generate-csr-openssl-config.yaml @@ -0,0 +1,12 @@ +type: operation +description: +title: +label: "Generate Certificate Signing Request and Private Key from custom OpenSSL configuration file" +entry_point: + module: operation.manage_certificate + method: generate_csr_and_private_key +arguments: + - name: cert_usage + value: "sso-sts" + - name: custom_openssl_config + value: true diff --git a/vCert-6.1.1-20260401/config/manage_cert/sts_signing/custom_ca_signed/op_03_import-certificate.yaml b/vCert-6.1.1-20260401/config/manage_cert/sts_signing/custom_ca_signed/op_03_import-certificate.yaml new file mode 100644 index 0000000..92f24d4 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/sts_signing/custom_ca_signed/op_03_import-certificate.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: +title: +label: "Import CA-signed Certificate and Key" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "import_custom_ca_signed_sts_signing_certificate" diff --git a/vCert-6.1.1-20260401/config/manage_cert/sts_signing/menu_custom-ca-signed-cert.yaml b/vCert-6.1.1-20260401/config/manage_cert/sts_signing/menu_custom-ca-signed-cert.yaml new file mode 100644 index 0000000..606c5d3 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/sts_signing/menu_custom-ca-signed-cert.yaml @@ -0,0 +1,13 @@ +type: menu +description: +title: "Replace STS Signing certificate with a custom CA-signed certificate" +label: "Replace STS Signing certificate with a custom CA-signed certificate" +items: + - type: multiple + config: "config/manage_cert/sts_signing/custom_ca_signed/op_*.yaml" + - type: navigation:return + label: "Return to previous menu" + key: "R" + default: True + hidden: True + use_label_as_key: True diff --git a/vCert-6.1.1-20260401/config/manage_cert/sts_signing/menu_sts-signing-certs.yaml b/vCert-6.1.1-20260401/config/manage_cert/sts_signing/menu_sts-signing-certs.yaml new file mode 100644 index 0000000..d6789fa --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/sts_signing/menu_sts-signing-certs.yaml @@ -0,0 +1,18 @@ +type: menu +description: "Manage STS signing certificates" +title: "Select STS Signing Certificate Replacement Method" +label: "STS signing certificates" +items: + - type: single + config: "config/manage_cert/sts_signing/op_vmca-signed-cert.yaml" + - type: single + config: "config/manage_cert/sts_signing/menu_custom-ca-signed-cert.yaml" + - type: navigation:return + label: "Return to previous menu" + key: "R" + default: True + hidden: True + use_label_as_key: True + +input: + text: "Select an option [Return to the previous menu]: " diff --git a/vCert-6.1.1-20260401/config/manage_cert/sts_signing/op_vmca-signed-cert.yaml b/vCert-6.1.1-20260401/config/manage_cert/sts_signing/op_vmca-signed-cert.yaml new file mode 100644 index 0000000..9f4b6b2 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/sts_signing/op_vmca-signed-cert.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: +title: +label: "Replace STS Signing certificate with a VMCA-signed certificate" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "manage_sts_signing_certificate_with_vmca_signed" diff --git a/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/menu_check_trust_anchors.yaml b/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/menu_check_trust_anchors.yaml new file mode 100644 index 0000000..c11f3f1 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/menu_check_trust_anchors.yaml @@ -0,0 +1,21 @@ +type: menu +description: "Check SSL Trust Anchors" +title: "Check SSL Trust Anchors" +sub_title: "Additional output options:" +label: "Check SSL Trust Anchors" +run_once: True +items: + - type: single + config: "config/manage_cert/trust_anchors/op_check_none.yaml" + default: True + - type: single + config: "config/manage_cert/trust_anchors/op_check_with_service_id.yaml" + - type: single + config: "config/manage_cert/trust_anchors/op_check_with_ep_uri.yaml" + - type: single + config: "config/manage_cert/trust_anchors/op_check_with_service_id_and_uri.yaml" + - type: single + config: "config/manage_cert/trust_anchors/op_check_with_fingerprint.yaml" + +input: + text: "Please select additional information options [{CURRENT_MENU[__DEFAULT__]}]: " \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_none.yaml b/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_none.yaml new file mode 100644 index 0000000..e4fd74c --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_none.yaml @@ -0,0 +1,12 @@ +type: operation-group +description: "Check SSL trust anchors" +title: +label: "None" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "check_ssl_trust_anchors" + diff --git a/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_with_ep_uri.yaml b/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_with_ep_uri.yaml new file mode 100644 index 0000000..ef58851 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_with_ep_uri.yaml @@ -0,0 +1,15 @@ +type: operation-group +description: "Check SSL trust anchors" +title: +label: "Show associated endpoint URIs" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "check_ssl_trust_anchors" + arguments: + - name: "show_endpoint_uris" + value: true + diff --git a/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_with_fingerprint.yaml b/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_with_fingerprint.yaml new file mode 100644 index 0000000..9ff57f5 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_with_fingerprint.yaml @@ -0,0 +1,14 @@ +type: operation-group +description: "Check SSL trust anchors" +title: +label: "Show the SHA256 fingerprint of the certificates" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "check_ssl_trust_anchors" + arguments: + - name: "use_sha256_fingerprint" + value: true diff --git a/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_with_service_id.yaml b/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_with_service_id.yaml new file mode 100644 index 0000000..5cc99a7 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_with_service_id.yaml @@ -0,0 +1,15 @@ +type: operation-group +description: "Check SSL trust anchors" +title: +label: "Show associated Service IDs" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "check_ssl_trust_anchors" + arguments: + - name: "show_service_id" + value: true + diff --git a/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_with_service_id_and_uri.yaml b/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_with_service_id_and_uri.yaml new file mode 100644 index 0000000..43a317c --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_check_with_service_id_and_uri.yaml @@ -0,0 +1,16 @@ +type: operation-group +description: "Check SSL trust anchors" +title: +label: "Show both associated Service IDs and endpoint URIs" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "check_ssl_trust_anchors" + arguments: + - name: "show_service_id" + value: true + - name: "show_endpoint_uris" + value: true diff --git a/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_update_trust_anchors.yaml b/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_update_trust_anchors.yaml new file mode 100644 index 0000000..fac8e45 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/trust_anchors/op_update_trust_anchors.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: "Update SSL trust anchors" +title: +label: "Update SSL Trust Anchors" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.manage_certificate" + method: "manage_ssl_trust_anchors" diff --git a/vCert-6.1.1-20260401/config/manage_cert/vmca/custom_ca_signed/op_01_generate-csr.yaml b/vCert-6.1.1-20260401/config/manage_cert/vmca/custom_ca_signed/op_01_generate-csr.yaml new file mode 100644 index 0000000..e3d3b53 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/vmca/custom_ca_signed/op_01_generate-csr.yaml @@ -0,0 +1,14 @@ +type: operation +description: +title: +label: "Generate Certificate Signing Request and private key" +entry_point: + module: operation.manage_certificate + method: generate_csr_and_private_key +arguments: + - name: cert_usage + value: "vmca" + - name: custom_openssl_config + value: false + - name: is_CA + value: true diff --git a/vCert-6.1.1-20260401/config/manage_cert/vmca/custom_ca_signed/op_02_generate-csr-openssl-config.yaml b/vCert-6.1.1-20260401/config/manage_cert/vmca/custom_ca_signed/op_02_generate-csr-openssl-config.yaml new file mode 100644 index 0000000..343eee9 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/vmca/custom_ca_signed/op_02_generate-csr-openssl-config.yaml @@ -0,0 +1,12 @@ +type: operation +description: +title: +label: "Generate Certificate Signing Request and private key \n from custom OpenSSL configuration file" +entry_point: + module: operation.manage_certificate + method: generate_csr_and_private_key +arguments: + - name: cert_usage + value: "vmca" + - name: custom_openssl_config + value: true \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/vmca/custom_ca_signed/op_03_import-certificate.yaml b/vCert-6.1.1-20260401/config/manage_cert/vmca/custom_ca_signed/op_03_import-certificate.yaml new file mode 100644 index 0000000..5e10212 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/vmca/custom_ca_signed/op_03_import-certificate.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: +title: +label: "Import CA-signed certificate and private key" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.reset_certificate" + method: "import_custom_ca_signed_vmca_certificate" diff --git a/vCert-6.1.1-20260401/config/manage_cert/vmca/custom_ca_signed/op_04_import-certificate-and-reset-all-certs.yaml b/vCert-6.1.1-20260401/config/manage_cert/vmca/custom_ca_signed/op_04_import-certificate-and-reset-all-certs.yaml new file mode 100644 index 0000000..6e73f9e --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/vmca/custom_ca_signed/op_04_import-certificate-and-reset-all-certs.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: +title: +label: "Import CA-signed certificate and private key \n and regenerate all certificates" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.reset_certificate" + method: "import_custom_ca_signed_vmca_certificate_and_reset_all" \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/vmca/menu_custom-ca-signed-vmca-cert.yaml b/vCert-6.1.1-20260401/config/manage_cert/vmca/menu_custom-ca-signed-vmca-cert.yaml new file mode 100644 index 0000000..5ade125 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/vmca/menu_custom-ca-signed-vmca-cert.yaml @@ -0,0 +1,13 @@ +type: menu +description: +title: "Replace VMCA certificate with a CA-signed certificate" +label: "Replace VMCA certificate with a CA-signed certificate" +items: + - type: multiple + config: "config/manage_cert/vmca/custom_ca_signed/op_*.yaml" + - type: navigation:return + label: "Return to previous menu" + key: "R" + default: true + hidden: true + use_label_as_key: True \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/manage_cert/vmca/op_replace-vmca-cert-and-reset-all.yaml b/vCert-6.1.1-20260401/config/manage_cert/vmca/op_replace-vmca-cert-and-reset-all.yaml new file mode 100644 index 0000000..75f53a6 --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/vmca/op_replace-vmca-cert-and-reset-all.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: +title: +label: "Replace VMCA certificate with a self-signed certificate \n and regenerate all certificates" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.reset_certificate" + method: "replace_vmca_cert_and_regenerate_all" diff --git a/vCert-6.1.1-20260401/config/manage_cert/vmca/op_replace-vmca-cert.yaml b/vCert-6.1.1-20260401/config/manage_cert/vmca/op_replace-vmca-cert.yaml new file mode 100644 index 0000000..dff80fc --- /dev/null +++ b/vCert-6.1.1-20260401/config/manage_cert/vmca/op_replace-vmca-cert.yaml @@ -0,0 +1,12 @@ +type: operation-group +description: +title: +label: "Replace VMCA certificate with a self-signed certificate" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.reset_certificate" + method: "replace_vmca_cert" + diff --git a/vCert-6.1.1-20260401/config/menu_check_cert_status.yaml b/vCert-6.1.1-20260401/config/menu_check_cert_status.yaml new file mode 100644 index 0000000..b2aeb55 --- /dev/null +++ b/vCert-6.1.1-20260401/config/menu_check_cert_status.yaml @@ -0,0 +1,8 @@ +type: operation-group +description: "Operation for checking certificates status" +title: "Check current certificates status" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: multiple + config: "config/check_cert/op_*.yaml" diff --git a/vCert-6.1.1-20260401/config/menu_check_config.yaml b/vCert-6.1.1-20260401/config/menu_check_config.yaml new file mode 100644 index 0000000..74b6fa4 --- /dev/null +++ b/vCert-6.1.1-20260401/config/menu_check_config.yaml @@ -0,0 +1,13 @@ +type: menu +description: "Check configuration" +title: "Configuration Check Menu" +label: "Check configurations" +items: + - type: multiple + config: "config/check_config/*.yaml" + - type: navigation:return + label: "Return to main menu" + key: "R" + default: True + use_label_as_key: True + hidden: True diff --git a/vCert-6.1.1-20260401/config/menu_esxi_cert.yaml b/vCert-6.1.1-20260401/config/menu_esxi_cert.yaml new file mode 100644 index 0000000..bd3808a --- /dev/null +++ b/vCert-6.1.1-20260401/config/menu_esxi_cert.yaml @@ -0,0 +1,13 @@ +type: menu +description: +title: "Manage ESXi Certificates" +label: "ESXi certificate operations" +items: + - type: multiple + config: "config/esxi_cert_actions/op_esxi_action*.yaml" + - type: navigation:return + label: "Return to main menu" + key: "R" + default: True + hidden: true + use_label_as_key: True \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/menu_main.yaml b/vCert-6.1.1-20260401/config/menu_main.yaml new file mode 100644 index 0000000..b903b18 --- /dev/null +++ b/vCert-6.1.1-20260401/config/menu_main.yaml @@ -0,0 +1,28 @@ +type: menu +description: "vCert main menu" +title: "{VCERT_DESC}" +items: + - type: single + config: "config/op_check_cert.yaml" + default: True + - type: single + config: "config/menu_view_cert.yaml" + - type: single + config: "config/menu_manage_cert.yaml" + - type: single + config: "config/menu_manage_trust_anchors.yaml" + - type: single + config: "config/menu_check_config.yaml" + - type: single + config: "config/op_reset_cert_vmca.yaml" + - type: single + config: "config/menu_esxi_cert.yaml" + - type: single + config: "config/menu_restart_service.yaml" + - type: single + config: "config/op_generate_report.yaml" + - type: navigation:exit + label: "Exit" + key: "E" +input: + text: "Select an option [{CURRENT_MENU[__DEFAULT__]}]: " diff --git a/vCert-6.1.1-20260401/config/menu_manage_cert.yaml b/vCert-6.1.1-20260401/config/menu_manage_cert.yaml new file mode 100644 index 0000000..c423535 --- /dev/null +++ b/vCert-6.1.1-20260401/config/menu_manage_cert.yaml @@ -0,0 +1,39 @@ +type: menu +description: 'Manage certificate' +title: 'Manage vCenter Certificates' +label: 'Manage certificates' +items: + - type: single + config: 'config/manage_cert/menu_machine_ssl_cert.yaml' + - type: single + config: 'config/manage_cert/menu_solution_user_cert.yaml' + - type: single + config: 'config/manage_cert/op_ca_certs_in_vmdir.yaml' + - type: single + config: 'config/manage_cert/op_ca_certs_in_vecs.yaml' + - type: single + config: 'config/manage_cert/menu_sms_certs.yaml' + - type: single + config: 'config/manage_cert/op_data-encipherment.yaml' + - type: single + config: 'config/manage_cert/op_manage-vc-ext-thumbprints.yaml' + - type: single + config: 'config/manage_cert/op_sts_signing_certs.yaml' + - type: single + config: 'config/manage_cert/menu_vmca_cert.yaml' + - type: single + config: 'config/manage_cert/op_smart_card.yaml' + - type: single + config: 'config/manage_cert/op_ldaps_identity_source.yaml' + - type: single + config: 'config/manage_cert/op_clear_expired_backup_store.yaml' + - type: single + config: 'config/manage_cert/op_clear_trusted_root_crls.yaml' + - type: single + config: 'config/manage_cert/op_clear_machine_ssl_csr.yaml' + - type: navigation:return + label: 'Return to main menu' + key: 'R' + default: True + use_label_as_key: True + hidden: True diff --git a/vCert-6.1.1-20260401/config/menu_manage_trust_anchors.yaml b/vCert-6.1.1-20260401/config/menu_manage_trust_anchors.yaml new file mode 100644 index 0000000..2085ae6 --- /dev/null +++ b/vCert-6.1.1-20260401/config/menu_manage_trust_anchors.yaml @@ -0,0 +1,17 @@ +type: menu +description: "Manage SSL Trust Anchors" +title: "Manage SSL Trust Anchors" +label: "Manage SSL trust anchors" +items: + - type: single + config: "config/manage_cert/trust_anchors/menu_check_trust_anchors.yaml" + - type: single + config: "config/manage_cert/trust_anchors/op_update_trust_anchors.yaml" + - type: navigation:return + label: "Return to previous menu" + key: "R" + default: true + hidden: true + +input: + text: "Select an option [Return to the previous menu]: " diff --git a/vCert-6.1.1-20260401/config/menu_restart_service.yaml b/vCert-6.1.1-20260401/config/menu_restart_service.yaml new file mode 100644 index 0000000..43db94b --- /dev/null +++ b/vCert-6.1.1-20260401/config/menu_restart_service.yaml @@ -0,0 +1,13 @@ +type: menu +description: "Operation for restarting services" +title: "Restart VMware Services" +label: "Restart services" +items: + - type: multiple + config: "config/restart_service/op_restart_*.yaml" + - type: navigation:return + label: "Return to main menu" + key: "R" + default: True + use_label_as_key: True + hidden: True diff --git a/vCert-6.1.1-20260401/config/menu_view_cert.yaml b/vCert-6.1.1-20260401/config/menu_view_cert.yaml new file mode 100644 index 0000000..401755a --- /dev/null +++ b/vCert-6.1.1-20260401/config/menu_view_cert.yaml @@ -0,0 +1,13 @@ +type: menu +description: "View certificate info" +title: "View vCenter Certificates" +label: "View certificate info" +items: + - type: multiple + config: "config/view_cert/op_view_*.yaml" + - type: navigation:return + label: "Return to main menu" + key: "R" + default: True + use_label_as_key: True + hidden: True diff --git a/vCert-6.1.1-20260401/config/op_check_cert.yaml b/vCert-6.1.1-20260401/config/op_check_cert.yaml new file mode 100644 index 0000000..aa3b7f4 --- /dev/null +++ b/vCert-6.1.1-20260401/config/op_check_cert.yaml @@ -0,0 +1,9 @@ +type: operation-group +description: +label: "Check current certificate status" +title: +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: multiple + config: "config/check_cert/op_*.yaml" diff --git a/vCert-6.1.1-20260401/config/op_check_machine_ssl.yaml b/vCert-6.1.1-20260401/config/op_check_machine_ssl.yaml new file mode 100644 index 0000000..aceeef9 --- /dev/null +++ b/vCert-6.1.1-20260401/config/op_check_machine_ssl.yaml @@ -0,0 +1,6 @@ +type: operation +title: +label: Check Machine SSL certificate +entry_point: + module: operation.checks_certificates + method: check_machine_ssl_certificate diff --git a/vCert-6.1.1-20260401/config/op_generate_report.yaml b/vCert-6.1.1-20260401/config/op_generate_report.yaml new file mode 100644 index 0000000..aa00b73 --- /dev/null +++ b/vCert-6.1.1-20260401/config/op_generate_report.yaml @@ -0,0 +1,17 @@ +type: operation-group +description: +title: +label: "Generate certificate report" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: operation.generate_report + method: generate_header + - type: multiple + config: config/generate_report/op_*.yaml + - type: single + entry_point: + module: operation.generate_report + method: finalize_report diff --git a/vCert-6.1.1-20260401/config/op_prevalidate_credential.yaml b/vCert-6.1.1-20260401/config/op_prevalidate_credential.yaml new file mode 100644 index 0000000..355b246 --- /dev/null +++ b/vCert-6.1.1-20260401/config/op_prevalidate_credential.yaml @@ -0,0 +1,7 @@ +type: operation +description: +label: +title: +entry_point: + module: "operation.common" + method: "prevalidate_credential" diff --git a/vCert-6.1.1-20260401/config/op_reset_cert_vmca.yaml b/vCert-6.1.1-20260401/config/op_reset_cert_vmca.yaml new file mode 100644 index 0000000..7cf5caa --- /dev/null +++ b/vCert-6.1.1-20260401/config/op_reset_cert_vmca.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: +label: "Reset all certificates with VMCA-signed certificates" +title: +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: "operation.reset_certificate" + method: "reset_all_certificates" diff --git a/vCert-6.1.1-20260401/config/restart_service/op_restart_01-restart_all_vm_services.yaml b/vCert-6.1.1-20260401/config/restart_service/op_restart_01-restart_all_vm_services.yaml new file mode 100644 index 0000000..e4f9b9d --- /dev/null +++ b/vCert-6.1.1-20260401/config/restart_service/op_restart_01-restart_all_vm_services.yaml @@ -0,0 +1,7 @@ +type: operation +description: "Restart all VMware services" +title: +label: "Restart all VMware services" +entry_point: + module: operation.restart_service + method: restart_vmware_services \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/restart_service/op_restart_02-restart_specific_services.yaml b/vCert-6.1.1-20260401/config/restart_service/op_restart_02-restart_specific_services.yaml new file mode 100644 index 0000000..816c414 --- /dev/null +++ b/vCert-6.1.1-20260401/config/restart_service/op_restart_02-restart_specific_services.yaml @@ -0,0 +1,7 @@ +type: operation +description: "Restart specific VMware service" +title: +label: "Restart specific VMware service" +entry_point: + module: operation.restart_service + method: restart_specific_vmware_service \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-15952599.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-15952599.json new file mode 100644 index 0000000..6c1154b --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-15952599.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-16189207.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-16189207.json new file mode 100644 index 0000000..6c1154b --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-16189207.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-16386335.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-16386335.json new file mode 100644 index 0000000..6c1154b --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-16386335.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-16620013.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-16620013.json new file mode 100644 index 0000000..6c1154b --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-16620013.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-16749670.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-16749670.json new file mode 100644 index 0000000..6c1154b --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.0-16749670.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.1-16858589.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.1-16858589.json new file mode 100644 index 0000000..6c1154b --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.1-16858589.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.1-17005016.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.1-17005016.json new file mode 100644 index 0000000..6c1154b --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.1-17005016.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.1-17327586.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.1-17327586.json new file mode 100644 index 0000000..6c1154b --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.1-17327586.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.1-17491160.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.1-17491160.json new file mode 100644 index 0000000..6c1154b --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.1-17491160.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-17694817.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-17694817.json new file mode 100644 index 0000000..a78e64a --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-17694817.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-17920168.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-17920168.json new file mode 100644 index 0000000..a78e64a --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-17920168.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-17958471.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-17958471.json new file mode 100644 index 0000000..a78e64a --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-17958471.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-18356314.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-18356314.json new file mode 100644 index 0000000..a78e64a --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-18356314.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-18455184.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-18455184.json new file mode 100644 index 0000000..a78e64a --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.2-18455184.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-18700403.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-18700403.json new file mode 100644 index 0000000..a78e64a --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-18700403.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-18778458.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-18778458.json new file mode 100644 index 0000000..a78e64a --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-18778458.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-19234570.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-19234570.json new file mode 100644 index 0000000..a78e64a --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-19234570.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-19480866.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-19480866.json new file mode 100644 index 0000000..4cb8f4d --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-19480866.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-19717403.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-19717403.json new file mode 100644 index 0000000..4cb8f4d --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-19717403.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": []}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "vsphere-ui", "vpxd"], "write": []}, "SMS": {"owner": "root", "read": ["deploy", "vpxd"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20051473.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20051473.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20051473.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20150588.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20150588.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20150588.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20395099.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20395099.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20395099.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20845200.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20845200.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20845200.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20990077.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20990077.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-20990077.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-21290409.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-21290409.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-21290409.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-21477706.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-21477706.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-21477706.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-21784236.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-21784236.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-21784236.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-21958406.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-21958406.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-21958406.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-22357613.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-22357613.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-22357613.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-22837322.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-22837322.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-22837322.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-23788036.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-23788036.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-23788036.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24026615.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24026615.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24026615.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24201990.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24201990.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24201990.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24322018.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24322018.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24322018.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24614210.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24614210.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24614210.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24730281.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24730281.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24730281.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24927011.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24927011.json new file mode 100644 index 0000000..38cff25 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-7.0.3-24927011.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": []}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "vpxd", "observability"], "write": ["infraprofile", "certmgr", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.0-20519528.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.0-20519528.json new file mode 100644 index 0000000..8975f23 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.0-20519528.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["infraprofile", "hvc", "sps", "rhttpproxy"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "statsmon", "sca"], "write": ["hvc", "sts", "infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["hvc", "infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": ["hvc"]}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "deploy", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "vstatsuser"], "write": ["hvc", "infraprofile", "sps"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": ["vpxd"]}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": [], "write": ["hvc", "vpxd"]}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.0-20920323.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.0-20920323.json new file mode 100644 index 0000000..9527aad --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.0-20920323.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["hvc", "infraprofile", "sps", "rhttpproxy"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "statsmon", "sca"], "write": ["hvc", "sts", "infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["hvc", "infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": ["hvc"]}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "deploy", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "vstatsuser"], "write": ["hvc", "infraprofile", "sps"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": ["vpxd"]}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": [], "write": ["hvc", "vpxd"]}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.0-21216066.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.0-21216066.json new file mode 100644 index 0000000..f527697 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.0-21216066.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["hvc", "infraprofile", "sps", "rhttpproxy"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "statsmon", "sca"], "write": ["hvc", "infraprofile", "certauth", "certmgr", "sts"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["hvc", "infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": ["hvc"]}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "deploy", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "vstatsuser"], "write": ["hvc", "infraprofile", "sps"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": ["vpxd"]}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": [], "write": ["hvc", "vpxd"]}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.0-21457384.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.0-21457384.json new file mode 100644 index 0000000..3276c82 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.0-21457384.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["infraprofile", "hvc", "sps", "rhttpproxy"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "statsmon", "sca"], "write": ["hvc", "infraprofile", "certauth", "certmgr", "sts"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["hvc", "infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": ["hvc"]}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "deploy", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "vstatsuser"], "write": ["hvc", "infraprofile", "sps"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": ["vpxd"]}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": [], "write": ["hvc", "vpxd"]}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-21560480.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-21560480.json new file mode 100644 index 0000000..08fe49c --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-21560480.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["sps", "rhttpproxy"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "statsmon", "sca"], "write": ["infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "deploy", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "vstatsuser"], "write": ["infraprofile", "sps"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": ["vpxd"]}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-21815093.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-21815093.json new file mode 100644 index 0000000..564aadc --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-21815093.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["hvc", "infraprofile", "sps", "rhttpproxy"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "statsmon", "sca"], "write": ["hvc", "infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["hvc", "infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": ["hvc"]}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "deploy", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "vstatsuser"], "write": ["hvc", "infraprofile", "sps"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": ["vpxd"]}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": [], "write": ["hvc", "vpxd"]}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-21860503.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-21860503.json new file mode 100644 index 0000000..235ea8c --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-21860503.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["infraprofile", "hvc", "sps", "rhttpproxy"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "statsmon", "sca"], "write": ["hvc", "infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["hvc", "infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": ["hvc"]}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "deploy", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "vstatsuser"], "write": ["hvc", "infraprofile", "sps"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": ["vpxd"]}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": [], "write": ["hvc", "vpxd"]}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-22088981.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-22088981.json new file mode 100644 index 0000000..564aadc --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-22088981.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["hvc", "infraprofile", "sps", "rhttpproxy"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "statsmon", "sca"], "write": ["hvc", "infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["hvc", "infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": ["hvc"]}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "deploy", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "vstatsuser"], "write": ["hvc", "infraprofile", "sps"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": ["vpxd"]}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": [], "write": ["hvc", "vpxd"]}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-22368047.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-22368047.json new file mode 100644 index 0000000..235ea8c --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-22368047.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["infraprofile", "hvc", "sps", "rhttpproxy"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "statsmon", "sca"], "write": ["hvc", "infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["hvc", "infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": ["hvc"]}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "deploy", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "vstatsuser"], "write": ["hvc", "infraprofile", "sps"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": ["vpxd"]}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": [], "write": ["hvc", "vpxd"]}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-24005165.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-24005165.json new file mode 100644 index 0000000..2713dfb --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.1-24005165.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["lighttpd", "vsm", "vsan-health", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["statsmon", "vsan-health", "sca", "vpxd", "observability"], "write": ["certmgr", "infraprofile", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "deploy", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["sps", "infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-22385739.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-22385739.json new file mode 100644 index 0000000..3a4ad3d --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-22385739.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["hvc", "infraprofile", "sps", "rhttpproxy"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "sca"], "write": ["hvc", "infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["hvc", "infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": ["hvc"]}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "vstatsuser"], "write": ["hvc", "infraprofile", "sps"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": ["vpxd"]}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": [], "write": ["hvc", "vpxd"]}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-22617221.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-22617221.json new file mode 100644 index 0000000..d169ee7 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-22617221.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "deploy", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["hvc", "infraprofile", "sps", "rhttpproxy"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "sca"], "write": ["hvc", "infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["hvc", "infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": ["hvc"]}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "vstatsuser"], "write": ["hvc", "infraprofile", "sps"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": ["vpxd"]}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": [], "write": ["hvc", "vpxd"]}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-23319993.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-23319993.json new file mode 100644 index 0000000..1ca4d7f --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-23319993.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["infraprofile", "hvc", "sps", "rhttpproxy"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "sca"], "write": ["hvc", "infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["hvc", "infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": ["hvc"]}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "vstatsuser"], "write": ["hvc", "infraprofile", "sps"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": ["vpxd"]}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": [], "write": ["hvc", "vpxd"]}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-23504390.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-23504390.json new file mode 100644 index 0000000..ce3f619 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-23504390.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["sps", "rhttpproxy"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "sca"], "write": ["infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "vstatsuser"], "write": ["infraprofile", "sps"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": ["vpxd"]}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-23929136.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-23929136.json new file mode 100644 index 0000000..4b76e4c --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-23929136.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["sps", "rhttpproxy"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "sca"], "write": ["infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "vstatsuser"], "write": ["infraprofile", "sps"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": ["vpxd"]}, "APPLMGMT_PASSWORD": {"owner": "root", "read": ["vpxd"], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "vpxd", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-24321653.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-24321653.json new file mode 100644 index 0000000..67af9fc --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.2-24321653.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["lighttpd", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "sca"], "write": ["infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "vstatsuser"], "write": ["infraprofile", "sps"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24022515.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24022515.json new file mode 100644 index 0000000..a338019 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24022515.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "sca"], "write": ["infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "sps", "vstatsuser"], "write": ["infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24091160.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24091160.json new file mode 100644 index 0000000..52e353e --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24091160.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["rhttpproxy", "sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "sca"], "write": ["infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "sps", "vstatsuser"], "write": ["infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24262322.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24262322.json new file mode 100644 index 0000000..a338019 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24262322.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "sca"], "write": ["infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "sps", "vstatsuser"], "write": ["infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24305161.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24305161.json new file mode 100644 index 0000000..c7d4362 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24305161.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["deploy", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "sca"], "write": ["infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "sps", "vstatsuser"], "write": ["infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24322831.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24322831.json new file mode 100644 index 0000000..a338019 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24322831.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres", "vsm", "vsan-health", "lighttpd"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["EVERYONE", "vpxd"], "write": []}, "machine": {"owner": "root", "read": ["vpxd", "vsan-health", "observability", "sca"], "write": ["infraprofile", "certauth", "certmgr"]}, "vsphere-webclient": {"owner": "root", "read": ["vsphere-ui", "vpxd", "perfcharts", "analytics", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vpxd", "vsan-health"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["vlcm", "wcp", "updatemgr", "vsphere-ui", "vpxd", "analytics", "vsm", "vsan-health", "imagebuilder", "content-library", "eam", "sps", "vstatsuser"], "write": ["infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24674346.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24674346.json new file mode 100644 index 0000000..4e50e17 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24674346.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["lighttpd", "vsm", "vsan-health", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "sca", "vpxd", "observability"], "write": ["certmgr", "infraprofile", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "vsm", "vsan-health", "vlcm", "updatemgr", "sps", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24853646.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24853646.json new file mode 100644 index 0000000..4e50e17 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-24853646.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["lighttpd", "vsm", "vsan-health", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "sca", "vpxd", "observability"], "write": ["certmgr", "infraprofile", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "vsm", "vsan-health", "vlcm", "updatemgr", "sps", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-25092719.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-25092719.json new file mode 100644 index 0000000..4e50e17 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-25092719.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["lighttpd", "vsm", "vsan-health", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "sca", "vpxd", "observability"], "write": ["certmgr", "infraprofile", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "vsm", "vsan-health", "vlcm", "updatemgr", "sps", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-25197330.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-25197330.json new file mode 100644 index 0000000..c40d6e1 --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-8.0.3-25197330.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["vsm", "vsan-health", "vlcm", "updatemgr", "vsphere-ui", "vpxd", "vpostgres"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "sca", "vpxd", "observability"], "write": ["certmgr", "infraprofile", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "analytics", "vsphere-ui", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "vsm", "vsan-health", "vlcm", "updatemgr", "sps", "eam", "analytics", "vsphere-ui", "vpxd"], "write": ["infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcp": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-9.0.0-24755230.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-9.0.0-24755230.json new file mode 100644 index 0000000..fb4acaf --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-9.0.0-24755230.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["lighttpd", "vsphere-ui", "vsm", "vsan-health", "vlcm", "updatemgr", "vpxd", "vpostgres"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "sca", "observability", "vpxd"], "write": ["certmgr", "infraprofile", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "analytics", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "vsphere-ui", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vpxd"], "write": ["infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcpsvc": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-9.0.1-24957454.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-9.0.1-24957454.json new file mode 100644 index 0000000..fb4acaf --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-9.0.1-24957454.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["lighttpd", "vsphere-ui", "vsm", "vsan-health", "vlcm", "updatemgr", "vpxd", "vpostgres"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "sca", "observability", "vpxd"], "write": ["certmgr", "infraprofile", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "analytics", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "vsphere-ui", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vpxd"], "write": ["infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcpsvc": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-9.0.2-25148086.json b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-9.0.2-25148086.json new file mode 100644 index 0000000..fb4acaf --- /dev/null +++ b/vCert-6.1.1-20260401/config/vecs_permissions/vcsa-9.0.2-25148086.json @@ -0,0 +1 @@ +{"MACHINE_SSL_CERT": {"owner": "root", "read": ["lighttpd", "vsphere-ui", "vsm", "vsan-health", "vlcm", "updatemgr", "vpxd", "vpostgres"], "write": ["rhttpproxy"]}, "TRUSTED_ROOTS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": ["sps"]}, "TRUSTED_ROOT_CRLS": {"owner": "root", "read": ["vpxd", "EVERYONE"], "write": []}, "machine": {"owner": "root", "read": ["vsan-health", "sca", "observability", "vpxd"], "write": ["certmgr", "infraprofile", "certauth"]}, "vsphere-webclient": {"owner": "root", "read": ["perfcharts", "vsphere-ui", "analytics", "vpxd", "vapiEndpoint"], "write": ["infraprofile"]}, "vpxd": {"owner": "root", "read": ["vsan-health", "vpxd"], "write": []}, "vpxd-extension": {"owner": "root", "read": ["imagebuilder", "content-library", "wcp", "vstatsuser", "vsphere-ui", "vsm", "vsan-health", "vlcm", "updatemgr", "eam", "analytics", "vpxd"], "write": ["infraprofile"]}, "SMS": {"owner": "sps", "read": ["deploy"], "write": []}, "APPLMGMT_PASSWORD": {"owner": "root", "read": [], "write": []}, "data-encipherment": {"owner": "root", "read": ["vpxd"], "write": []}, "hvc": {"owner": "root", "read": ["vpxd"], "write": []}, "wcpsvc": {"owner": "root", "read": ["wcp", "content-library"], "write": []}} \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/view_cert/op_view_01-machine_ssl.yaml b/vCert-6.1.1-20260401/config/view_cert/op_view_01-machine_ssl.yaml new file mode 100644 index 0000000..f709558 --- /dev/null +++ b/vCert-6.1.1-20260401/config/view_cert/op_view_01-machine_ssl.yaml @@ -0,0 +1,7 @@ +type: operation +description: "View machine SSL certificate" +title: +label: "Machine SSL certificate" +entry_point: + module: operation.view_certificate + method: view_machine_ssl_certificate diff --git a/vCert-6.1.1-20260401/config/view_cert/op_view_02-solution_user.yaml b/vCert-6.1.1-20260401/config/view_cert/op_view_02-solution_user.yaml new file mode 100644 index 0000000..9456d23 --- /dev/null +++ b/vCert-6.1.1-20260401/config/view_cert/op_view_02-solution_user.yaml @@ -0,0 +1,7 @@ +type: operation +description: "View solution user certificates" +title: +label: "Solution User certificates" +entry_point: + module: operation.view_certificate + method: view_solution_user_certificate diff --git a/vCert-6.1.1-20260401/config/view_cert/op_view_03-ca_vmdir.yaml b/vCert-6.1.1-20260401/config/view_cert/op_view_03-ca_vmdir.yaml new file mode 100644 index 0000000..13f7583 --- /dev/null +++ b/vCert-6.1.1-20260401/config/view_cert/op_view_03-ca_vmdir.yaml @@ -0,0 +1,11 @@ +type: operation-group +description: "View CA certificates in VMware Directory" +title: "View CA certificates in VMware Directory" +label: "CA certificates in VMware Directory" +operations: + - type: single + config: "config/op_prevalidate_credential.yaml" + - type: single + entry_point: + module: operation.view_certificate + method: view_ca_certificates_in_vmdir diff --git a/vCert-6.1.1-20260401/config/view_cert/op_view_04-ca_vecs.yaml b/vCert-6.1.1-20260401/config/view_cert/op_view_04-ca_vecs.yaml new file mode 100644 index 0000000..27edf1c --- /dev/null +++ b/vCert-6.1.1-20260401/config/view_cert/op_view_04-ca_vecs.yaml @@ -0,0 +1,7 @@ +type: operation +description: "View CA certificates in VECS" +title: "CA Certificates in TRUSTED_ROOTS store in VECS" +label: "CA certificates in VECS" +entry_point: + module: operation.view_certificate + method: view_ca_certificates_in_vecs diff --git a/vCert-6.1.1-20260401/config/view_cert/op_view_07-sms.yaml b/vCert-6.1.1-20260401/config/view_cert/op_view_07-sms.yaml new file mode 100644 index 0000000..6af861e --- /dev/null +++ b/vCert-6.1.1-20260401/config/view_cert/op_view_07-sms.yaml @@ -0,0 +1,7 @@ +type: operation +description: "View SMS certificates" +title: "View SMS certificates" +label: "SMS certificates" +entry_point: + module: operation.view_certificate + method: view_sms_certificates_in_vecs \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/view_cert/op_view_08-data_encipherment.yaml b/vCert-6.1.1-20260401/config/view_cert/op_view_08-data_encipherment.yaml new file mode 100644 index 0000000..5f8ee61 --- /dev/null +++ b/vCert-6.1.1-20260401/config/view_cert/op_view_08-data_encipherment.yaml @@ -0,0 +1,7 @@ +type: operation +description: "View data encipherment certificate" +title: +label: "Data Encipherment certificate" +entry_point: + module: operation.view_certificate + method: view_data_encipherment_certificate \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/view_cert/op_view_09-vc_ext_thumbprint.yaml b/vCert-6.1.1-20260401/config/view_cert/op_view_09-vc_ext_thumbprint.yaml new file mode 100644 index 0000000..28012a0 --- /dev/null +++ b/vCert-6.1.1-20260401/config/view_cert/op_view_09-vc_ext_thumbprint.yaml @@ -0,0 +1,7 @@ +type: operation +description: +title: "View vCenter Extension Thumbprints" +label: "vCenter Extension thumbprints" +entry_point: + module: operation.view_certificate + method: view_vcenter_extension_thumbprints diff --git a/vCert-6.1.1-20260401/config/view_cert/op_view_11-sts.yaml b/vCert-6.1.1-20260401/config/view_cert/op_view_11-sts.yaml new file mode 100644 index 0000000..7c45a84 --- /dev/null +++ b/vCert-6.1.1-20260401/config/view_cert/op_view_11-sts.yaml @@ -0,0 +1,7 @@ +type: operation +description: "View STS signing certificates" +title: "View STS signing certificates" +label: "STS signing certificates" +entry_point: + module: operation.view_certificate + method: view_sts_signing_certificates diff --git a/vCert-6.1.1-20260401/config/view_cert/op_view_12-vmca.yaml b/vCert-6.1.1-20260401/config/view_cert/op_view_12-vmca.yaml new file mode 100644 index 0000000..866e160 --- /dev/null +++ b/vCert-6.1.1-20260401/config/view_cert/op_view_12-vmca.yaml @@ -0,0 +1,6 @@ +type: operation +description: "View VMCA certificate" +label: "VMCA certificate" +entry_point: + module: operation.view_certificate + method: view_VMCA_Certificate diff --git a/vCert-6.1.1-20260401/config/view_cert/op_view_13-smart_card.yaml b/vCert-6.1.1-20260401/config/view_cert/op_view_13-smart_card.yaml new file mode 100644 index 0000000..bc3b601 --- /dev/null +++ b/vCert-6.1.1-20260401/config/view_cert/op_view_13-smart_card.yaml @@ -0,0 +1,7 @@ +type: operation +description: "View Smart Card certificates" +label: "Smart Card CA certificates" +condition: 'CAC_CONFIGURED' +entry_point: + module: operation.view_certificate + method: view_smart_card_certificates \ No newline at end of file diff --git a/vCert-6.1.1-20260401/config/view_cert/op_view_14-ldaps_identity_source.yaml b/vCert-6.1.1-20260401/config/view_cert/op_view_14-ldaps_identity_source.yaml new file mode 100644 index 0000000..6b5ecda --- /dev/null +++ b/vCert-6.1.1-20260401/config/view_cert/op_view_14-ldaps_identity_source.yaml @@ -0,0 +1,12 @@ +type: operation-group +description: "LDAPS Certificates" +title: "LDAPS Certificates" +label: "LDAPS Identity Source certificates" +condition: 'HAS_LDAPS_IDENTITY_SOURCE' +operations: + - type: single + config: 'config/op_prevalidate_credential.yaml' + - type: single + entry_point: + module: operation.view_certificate + method: view_ldaps_identity_source_certificates \ No newline at end of file diff --git a/vCert-6.1.1-20260401/lib/.gitkeep b/vCert-6.1.1-20260401/lib/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vCert-6.1.1-20260401/lib/__init__.py b/vCert-6.1.1-20260401/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vCert-6.1.1-20260401/lib/certificate_utils.py b/vCert-6.1.1-20260401/lib/certificate_utils.py new file mode 100644 index 0000000..8cf9a57 --- /dev/null +++ b/vCert-6.1.1-20260401/lib/certificate_utils.py @@ -0,0 +1,688 @@ +# Copyright (c) 2024-2025 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import base64 +import subprocess +import OpenSSL +import re + +from datetime import datetime + +from lib.exceptions import CommandExecutionError +from lib.command_runner import CommandRunner +from lib.console import print_text_error +from lib.input import MenuInput +from lib.host_utils import get_file_contents, get_file_type +from lib.text_utils import TextFilter + + +OPENSSL_CLI = '/usr/bin/openssl' +CERTOOL_CLI = '/usr/lib/vmware-vmca/bin/certool' + + +def get_command_output_with_input(args, input_text): + """ + Get command output with the specified input + + :param args: Command and its arguments + :param input_text: input text to be supplied to stdin + :return: command output from stdout + """ + cmd = CommandRunner(*args, command_input=input_text, expected_return_code=0) + _, stdout, _ = cmd.run() + return stdout + + +def get_certificate_info(pem_text): + """ + Get certificate information from openssl command + This is a wrapper for 'openssl x509 -text -fingerprint -noout' command + + :param pem_text: Certificate in PEM format + :return: Certificate info generated by openssl command + """ + args = [OPENSSL_CLI, 'x509', '-noout', '-text', '-fingerprint'] + return get_command_output_with_input(args, pem_text).strip() + + +def get_subject_hash(pem_cert): + """ + Get the Subject hash for a certificate + This is a wrapper for the 'openssl x509 -noout -subject_hash' command + + :param pem_cert: Base64 certificate hash + :return: Subject hash returned by openssl + """ + args = [OPENSSL_CLI, 'x509', '-noout', '-subject_hash'] + return get_command_output_with_input(args, pem_cert).strip() + + +def get_formatted_dn(x509_name, use_slash_sep=False): + """ + Get formatted distinguished name for subject and issuer + + The original vCert depends on 'openssl x509 -subject' format, but the output format + varies between openssl versions. + - openssl 1.0 (VC 6.x): + subject= /CN=CA/DC=vsphere/DC=local/C=US/ST=California/O=sc2-10-184-104-251.eng.vmware.com/OU=VMware Engineering + - openssl 3.0 (VC 7.x, 8.x): + subject=CN = CA, DC = vsphere, DC = local, C = US, ST = California, O = sc2-10-184-104-251.eng.vmware.com, OU = VMware Engineering + - openssl 3.2: + subject=CN=CA, DC=vsphere, DC=local, C=US, ST=California, O=sc2-10-184-104-251.eng.vmware.com, OU=VMware Engineering + Unless requested, we will use the last one since it's the most widely used format. + + :param x509_name: OpenSSL.crypto.X509Name object + :param use_slash_sep: Use slash separator + :return: formatted distinguished name + """ + comps = [] + comp_map = dict() + for comp in x509_name.get_components(): + comp_name = str(comp[0], 'utf-8') + comp_value = str(comp[1], 'utf-8') + comp_map[comp_name] = comp_value + comps.append("{}={}".format(comp_name, comp_value)) + if use_slash_sep: + return "/{}".format('/'.join(comps)) + else: + return ', '.join(comps) + + +def get_subject_and_issuer_dn(x509_cert): + """ + Get Subject DN and Issuer DN from X509 certificate in a formatted DN + + :param x509_cert: X509Certificate object + :return: tuple (formatted subject DN, formatted issuer DN) + """ + return get_formatted_dn(x509_cert.get_subject()), get_formatted_dn(x509_cert.get_issuer()) + + +def get_certificate_extensions(x509_cert): + """ + Get X509 certificate extensions as a dict object + + :param x509_cert: X509 certificate + :return: X509 certificate extensions + """ + extensions = dict() + for idx in range(x509_cert.get_extension_count()): + ext = x509_cert.get_extension(idx) + ext_short_name = ext.get_short_name().decode('utf-8') + try: + extensions[ext_short_name] = str(ext) + except OpenSSL.crypto.Error: + pass + + return extensions + + +def get_subject_keyid(x509_cert, remove_colons=False, allow_alternate=False): + """ + Get Subject Key Identifier from X509v3 extension + If the keyid is not available and {allow_alternate} is True, the keyid + will be generated using serial/subject_dn pair. + + :param x509_cert: X509 certificate + :param remove_colons: Remove colon separators + :param allow_alternate: Allow alternate keyid value using serial/subject_dn + :return: subject keyid + """ + extensions = get_certificate_extensions(x509_cert) + skid = extensions.get('subjectKeyIdentifier') + if skid: + if remove_colons: + skid = skid.replace(':', '') + elif allow_alternate: + serial = get_serial_number(x509_cert) + subject_dn = get_formatted_dn(x509_cert.get_subject(), use_slash_sep=True) + skid = "DirName:{},serial:{}".format(subject_dn, serial) + else: + skid = '' + return skid + + +def get_authority_keyid(x509_cert, remove_colons=False, allow_alternate=False): + """ + Get Authority Key Identifier from X509v3 extension + If the keyid is not available and {allow_alternate} is True, the keyid + will be generated using serial/DirName pair, if available + + :param x509_cert: X509 certificate + :param remove_colons: Remove colon separators + :param allow_alternate: Allow alternate keyid value using serial/subject_dn + :return: authority keyid + """ + akid = '' + extensions = get_certificate_extensions(x509_cert) + akids = extensions.get('authorityKeyIdentifier') + if akids: + akid = TextFilter(akids).start_with('keyid:').get_text() + if not akid and len(akids.splitlines()) == 1 and len(akids.split(':')) >= 7: + akid = akids + if akid: + akid = akid.replace('keyid:', '') + if remove_colons: + akid = akid.replace(':', '') + elif akids and allow_alternate: + dir_name = TextFilter(akids).start_with('DirName:').get_text() + serial = TextFilter(akids).start_with('serial:').get_text() + if dir_name and serial: + akid = "{},{}".format(dir_name, serial) + return akid + + +def get_serial_number(x509_cert): + """ + Get certificate serial number in colon separated capitalized text, + e.g. 88:EC:FC:3B:FB:3F:53:52 + :param x509_cert: X509 certificate + :return: certificate serial number + """ + serial = "{:X}".format(x509_cert.get_serial_number()) + if len(serial) % 2 != 0: + serial = "0{}".format(serial) + parts = [serial[idx:idx+2] for idx in range(0, len(serial), 2)] + return ":".join(parts) + + +def get_certificate_start_date(x509_cert): + """ + Get certificate start date + + :param x509_cert: X509 certificate object + :return: Certificate's start date as a datetime object + """ + input_format = '%Y%m%d%H%M%SZ' + return datetime.strptime(x509_cert.get_notBefore().decode('utf-8'), input_format) + + +def get_certificate_end_date(x509_cert): + """ + Get certificate end date + + :param x509_cert: X509 certificate object + :return: Certificate's end date as a datetime object + """ + input_format = '%Y%m%d%H%M%SZ' + return datetime.strptime(x509_cert.get_notAfter().decode('utf-8'), input_format) + + +def get_certificate_fingerprint(x509_cert, algorithm=None, remove_colons=False): + """ + Get certificate fingerprint + + :param x509_cert: X509 certificate object + :param algorithm: fingerprint algorithm. If it's not specified, use sha1 + :param remove_colons: remove colon delimiters + :return: + """ + if algorithm is None: + algorithm = 'sha1' + fingerprint = x509_cert.digest(algorithm).decode('utf-8') + return fingerprint.replace(':', '') if remove_colons else fingerprint + + +def get_x509_certificate(pem_cert): + """ + Load X509Certificate from PEM text + + :param pem_cert: Certificate in PEM format + :return: X509Certificate object + """ + return OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, pem_cert) + + +def get_certificate_expiry_in_days(x509_cert): + """ + Return certificate expiry in days + + :param x509_cert: X509 certificate object + :return: certificate's expiry in days + """ + return (get_certificate_end_date(x509_cert) - datetime.utcnow()).days + + +def is_x509_expired(x509_cert): + """ + Checks if x509 object is expired + """ + days_left = get_certificate_expiry_in_days(x509_cert) + return True if days_left < 0 else False + + +def is_cert_expired(pem_cert): + """ + Checks if Base64 certificate is expired + """ + x509_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, pem_cert) + return is_x509_expired(x509_cert) + + +def is_cert_file_expired(cert_file): + """ + Checks if PEM certificate file is expired + """ + pem_cert = get_file_contents(cert_file) + return is_cert_expired(pem_cert) + +def get_subject_alternative_names(x509_cert): + """ + Get SAN entries in the certificate + :param x509_cert: X590 certificate object + :return: list of SAN entries (including the prefixes) + """ + extensions = get_certificate_extensions(x509_cert) + return extensions.get('subjectAltName') + + +def get_cert_info_for_path_building(pem_cert): + """ + Obtain the basic certificate information for certificate path + building. The information includes: + - subject keyId + - authority keyId + - certificate name (common name) + - whether the certificate is self-signed + - whether the certificate is trusted + - whether the certificate is a CA + - pathlen of the certificate basic constraints + (or False if basic constraints are not present) + + :param pem_cert: Certificate in PEM format + :return: dict object contains the required information + """ + x509_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, pem_cert) + subject_dn, issuer_dn = get_subject_and_issuer_dn(x509_cert) + skid = get_subject_keyid(x509_cert, remove_colons=True, allow_alternate=True) + akid = get_authority_keyid(x509_cert, remove_colons=True, allow_alternate=True) + basic_constraints = get_certificate_extensions(x509_cert).get('basicConstraints') + if basic_constraints is not None: + pathlen_search = re.search('pathlen:[0-9]+', basic_constraints) + pathlen = pathlen_search.group().split(':')[1] if pathlen_search is not None else False + else: + pathlen = False + if skid and akid: + is_selfsigned = skid == akid + else: + is_selfsigned = subject_dn == issuer_dn + + cert_name = get_certificate_name(pem_cert) + + return { + "subject_keyid": skid, + "auth_keyid": akid, + "cert_name": cert_name, + "is_selfsigned": is_selfsigned, + "is_trusted": False, + "is_ca": is_ca_certificate(x509_cert), + "pathlen": pathlen, + } + + +def get_certificate_fetcher_from_list(pem_certs): + """ + Method to compose certificate fetcher closure method from certificate list + in PEM text. The fetcher method will be used by build_certification_path + method. + + :param pem_certs: List of certificate in PEM format + :return: subject keyId list, fetcher method + """ + subject_keyids = [] + cert_map = dict() + for pem_cert in pem_certs: + cert = get_x509_certificate(pem_cert) + subject_keyid = get_subject_keyid(cert, remove_colons=True, allow_alternate=True) + if subject_keyid: + cert_map[subject_keyid] = pem_cert + subject_keyids.append(subject_keyid) + + def fetch_certificate_closure(skid): + return cert_map.get(skid) + + return subject_keyids, fetch_certificate_closure + + +def build_certification_path(pem_cert, trusted_subject_keyids, certificate_fetcher): + """ + Build certification path using a strict method of subject keyId and authority keyId + matching. + + :param pem_cert: Certificate in PEM format. It may contain intermediate CA certificate + :param trusted_subject_keyids: list of subject keyId of trusted root CA certificates + :param certificate_fetcher: method to fetch certificate from subject keyId + :return: list of certificate info, from leaf certificate to root CA certificate + """ + cert_map = dict() + machine_certs = split_certificates_from_pem(pem_cert) + cur_cert = None + for idx, machine_cert in enumerate(machine_certs): + cert_info = get_cert_info_for_path_building(machine_cert) + subject_keyid = cert_info['subject_keyid'] + cert_info['is_trusted'] = subject_keyid in trusted_subject_keyids + if idx == 0: + cur_cert = cert_info + else: + cert_map[subject_keyid] = cert_info + + cert_path = [cur_cert] + while not cur_cert['is_selfsigned'] and cur_cert['auth_keyid'] is not None: + auth_keyid = cur_cert['auth_keyid'] + cert_info = cert_map.get(auth_keyid) + if not cert_info: + next_pem_cert = certificate_fetcher(auth_keyid) + if not next_pem_cert: + break + cert_info = get_cert_info_for_path_building(next_pem_cert) + if cert_info['subject_keyid'] != auth_keyid: + break + cert_info['is_trusted'] = True + cert_map[cert_info["subject_keyid"]] = cert_info + cert_path.insert(0, cert_info) + cur_cert = cert_info + + return cert_path + + +def split_certificates_from_pem(pem): + """ + Get list of individual certificates from PEM text + + :param pem: Certificates in PEM format + :return: list of certificates in PEM format + """ + return TextFilter(pem).match_block('-----BEGIN CERTIFICATE-----', + '-----END CERTIFICATE-----', concatenate=True).get_lines() + + +def get_certificate_from_host(hostname, port=443): + """ + Get server TLS certificate from host via SSL handshake + :param hostname: hostname to connect + :param port: port + :return: Server certificate in PEM format + """ + remote_host = "{}:{}".format(hostname, port) + output = CommandRunner(OPENSSL_CLI, 's_client', '-connect', remote_host, '-showcerts', + command_input=subprocess.DEVNULL).run_and_get_output() + pem_cert = TextFilter(output).match_block('^-----BEGIN CERTIFICATE-----$', + '^-----END CERTIFICATE-----$', + concatenate=True).get_text() + return pem_cert + + +def build_pem_certificate(cert_text): + """ + Build PEM certificate based on certificate text input + This is required since certificates are often stored in DB + using a single line text. + + :param cert_text: certificate text to be processed + :return: return certificate in a proper PEM format + """ + if type(cert_text) is bytes: + if cert_text.startswith(b'-----BEGIN CERTIFICATE-----'): + pem_cert = cert_text.replace(b'\\n', b'\n').decode('utf-8').strip() + elif cert_text.startswith(b'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t'): + pem_cert = base64.decodebytes(cert_text).decode('utf-8').strip() + else: + pem_cert = '\n'.join(['-----BEGIN CERTIFICATE-----', + base64.encodebytes(cert_text).decode('utf-8').strip(), + '-----END CERTIFICATE-----']) + else: + if cert_text.startswith('-----BEGIN CERTIFICATE-----'): + pem_cert = cert_text.replace('\\n', '\n') + else: + pem_cert = '\n'.join(['-----BEGIN CERTIFICATE-----', cert_text.strip(), + '-----END CERTIFICATE-----']) + + # reformat to ensure that each line less than or equal to 72 characters + lines = pem_cert.splitlines() + contents = ''.join(lines[1:-1]) + result = [lines[0]] + for line in contents.splitlines(): + while len(line) > 72: + result.append(line[:72]) + line = line[72:] + if line: + result.append(line) + result.append(lines[-1]) + return '\n'.join(result) + + +def is_ca_certificate(x509_cert): + """ + Check if certificate is a CA certificate + :param x509_cert: certificate + :return: True of a CA certificate, otherwise False + """ + basic_constraints = get_certificate_extensions(x509_cert).get('basicConstraints') + key_usage = get_certificate_extensions(x509_cert).get('keyUsage') + is_ca = basic_constraints is not None and 'CA:TRUE' in basic_constraints + can_sign_certs = key_usage is not None and 'Certificate Sign' in key_usage + return is_ca and can_sign_certs + + +def is_self_signed_certificate(x509_cert): + """ + Check if the certificate is a self-signed certificate + :param x509_cert: X509 certificate + :return: True if self-signed, otherwise False + """ + subject_kid = get_subject_keyid(x509_cert, allow_alternate=True) + auth_keyid = get_authority_keyid(x509_cert, allow_alternate=True) + if subject_kid and auth_keyid: + return subject_kid == auth_keyid + else: + subject_dn, issuer_dn = get_subject_and_issuer_dn(x509_cert) + return subject_dn == issuer_dn + + +def generate_vmca_signed_certificate(config, output_dir, filename): + """ + Generate VMCA signed certificate using certool utility + In the {output_dir} directory, the following will be created: + - {filename}.key : the private key + - {filename}.pub : the public key + - {filename}.crt : the certificate file + + :param config: certool config file + :param output_dir: The output directory + :param filename: The filename for the output (without extension) + """ + privkey_arg = "--privkey={}/{}.key".format(output_dir, filename) + pubkey_arg = "--pubkey={}/{}.pub".format(output_dir, filename) + CommandRunner(CERTOOL_CLI, '--genkey', privkey_arg, pubkey_arg, expected_return_code=0).run() + + config_arg = "--config={}".format(config) + cert_arg = "--cert={}/{}.crt".format(output_dir, filename) + CommandRunner(CERTOOL_CLI, privkey_arg, '--gencert', cert_arg, config_arg, expected_return_code=0).run() + + +def detect_and_convert_to_pem(input_cert, password=None, allow_input=False): + """ + Detect certificate file format and convert it to PEM certificate as necessary + :param input_cert: The certificate file + :param password: In case of PKCS#12, the store password + :param allow_input: In case of PKCS#12, allow the method to ask for password + :return: tuple of ( certificate in PEM, private key in PEM of None ) + """ + file_type = get_file_type(input_cert) + if 'PEM' in file_type or 'ASCII' in file_type: + contents = get_file_contents(input_cert) + cert_pem, key_pem = get_certificate_and_private_key(contents) + if cert_pem or key_pem: + return cert_pem, key_pem + + # need conversion, try PKCS#12 format + if is_pkcs12_file(input_cert): + result = convert_pkcs12_to_pem(input_cert) + if password is not None: + result = convert_pkcs12_to_pem(input_cert, password) + if result is None: + result = convert_pkcs12_to_pem(input_cert, 'Antidisestablishmentarianism123581321', + allow_input=allow_input) + if result is None: + raise CommandExecutionError('Unable to validate keystore password') + return result + + # DER format + cert_pem = CommandRunner(OPENSSL_CLI, 'x509', '-inform', 'der', '-in', input_cert, + '-outform', 'pem').run_and_get_output().strip() + key_pem = CommandRunner(OPENSSL_CLI, 'rsa', '-inform', 'der', '-in', input_cert, + '-outform', 'pem').run_and_get_output().strip() + output = "{}\n{}".format(cert_pem, key_pem) if cert_pem or key_pem else '' + + if not output: + # PKCS#7, PEM + output = CommandRunner(OPENSSL_CLI, 'pkcs7', '-print_certs', '-inform', 'pem', '-in', + input_cert, '-outform', 'pem').run_and_get_output().strip() + if not output: + # PKCS#7, DER + output = CommandRunner(OPENSSL_CLI, 'pkcs7', '-print_certs', '-inform', 'der', '-in', + input_cert, '-outform', 'pem').run_and_get_output().strip() + if output: + return get_certificate_and_private_key(output) + else: + raise CommandExecutionError('Unsupported file format') + + +def is_pkcs12_file(filename): + ret_code, _, stderr = CommandRunner(OPENSSL_CLI, 'pkcs12', '-noout', '-in', filename, + '-passin', 'pass:').run() + if ret_code != 0 and 'asn1 error' in stderr: + return False + else: + return True + + +def convert_pkcs12_to_pem(cert_file, password=None, allow_input=False): + """ + Convert PKCS#12 format to PEM + :param cert_file: + :param password: + :param allow_input: + :return: + """ + password_arg = "pass:{}".format(password if password is not None else '') + ret_code, stdout, stderr = CommandRunner(OPENSSL_CLI, 'pkcs12', '-nokeys', '-info', + '-in', cert_file, '-passin', password_arg, '-nomacver').run() + if ret_code != 0 and allow_input and 'pkcs12 cipherfinal error' in stderr: + retry = 0 + password = '' + while retry < 3 and not password: + password = MenuInput('Enter password for PKCS#12 keystore: ', + case_insensitive=False, masked=True).get_input() + password_arg = "pass:{}".format(password) + ret_code, stdout, _ = CommandRunner(OPENSSL_CLI, 'pkcs12', '-nokeys', '-info', + '-in', cert_file, '-passin', password_arg, '-nomacver').run() + if ret_code != 0: + print_text_error('Invalid password.') + password = '' + retry += 1 + + if ret_code == 0: + cert_pem, _ = get_certificate_and_private_key(stdout) + ret_code, stdout, _ = CommandRunner(OPENSSL_CLI, 'pkcs12', '-nocerts', '-nodes', + '-info', '-in', cert_file, '-passin', password_arg, '-nomacver').run() + _, key_pem = get_certificate_and_private_key(stdout) + return cert_pem, key_pem + + return None + + +def get_certificate_and_private_key(text): + """ + Separate certificate and private key parts from PEM text + :param text: PEM text contains certificate and private key + """ + cert_pem = TextFilter(text)\ + .match_block('^-----BEGIN CERTIFICATE-----', + '^-----END CERTIFICATE-----').get_text() + key_pem = TextFilter(text) \ + .match_block('^-----BEGIN .*PRIVATE KEY-----', + '^-----END .*PRIVATE KEY-----') \ + .get_text() + + return cert_pem, key_pem + + +def get_key_modulus_from_pem_text(cert_or_key_text): + """ + Get the key modulus from certificate or private key text (PEM) + :param cert_or_key_text: certificate or key file + :return: The key modulus + """ + if TextFilter(cert_or_key_text).match('^-----BEGIN CERTIFICATE-----').get_lines(): + modulus = CommandRunner(OPENSSL_CLI, 'x509', '-noout', '-modulus', + command_input=cert_or_key_text).run_and_get_output() + + elif TextFilter(cert_or_key_text).match('^-----BEGIN .*PRIVATE KEY-----').get_text(): + modulus = CommandRunner(OPENSSL_CLI, 'rsa', '-noout', '-modulus', + command_input=cert_or_key_text).run_and_get_output() + else: + return None + + return TextFilter(modulus).cut('=', [1]).get_text().strip() + + +def generate_csr(openssl_config, csr_file, key_file): + """ + Generate CSR file using config {openssl_config} + :param openssl_config: OpenSSL config file + :param csr_file: The output CSR file + :param key_file: The output key file + """ + command = CommandRunner(OPENSSL_CLI, 'req', '-new', '-newkey', 'rsa:3072', '-nodes', '-config', + openssl_config, '-out', csr_file, '-keyout', key_file, expected_return_code=0) + command.run() + + +def load_pem_certificate_file_in_der(cert_file, leaf_only=False): + """ + Load PEM certificates and return them in DER format + """ + certs_pem = split_certificates_from_pem(get_file_contents(cert_file)) + certs_der = [] + for cert_pem in certs_pem: + cert_x509 = get_x509_certificate(cert_pem) + certs_der.append(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, cert_x509)) + if leaf_only: + return certs_der[:1] + return certs_der + + +def load_pem_key_file_in_pkcs8_der(key_file): + """ + Load PEM key file and return them in PKCS#8 DER format + """ + key_der = CommandRunner(OPENSSL_CLI, 'pkcs8', '-topk8', '-inform', 'pem', '-outform', + 'der', '-in', key_file, '-out', '/dev/stdout', '-nocrypt', + command_input=subprocess.DEVNULL, binary_output=True).run_and_get_output() + return key_der + + +def get_certificate_name(pem_cert, target_dn='subject') -> str: + """ + Get the name of the entity in an x509 certificate, taken from the + Subject attributes CommonName, OrgUnit, and Organization (in that order of preference) + :param pem_cert: Base64 hash of a certificate + :return: string of the computed certificate name + """ + x509_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, pem_cert) + # get certificate name, typically will return the common name + comps = dict() + dn = x509_cert.get_subject() if target_dn == 'subject' else x509_cert.get_issuer() + for comp in dn.get_components(): + comp_name = str(comp[0], 'utf-8') + comp_value = str(comp[1], 'utf-8') + comps[comp_name] = comp_value + + cert_name = '' + for key in ['CN', 'OU', 'O']: + if comps.get(key): + cert_name = comps[key] + break + return cert_name diff --git a/vCert-6.1.1-20260401/lib/command_runner.py b/vCert-6.1.1-20260401/lib/command_runner.py new file mode 100644 index 0000000..2925bf6 --- /dev/null +++ b/vCert-6.1.1-20260401/lib/command_runner.py @@ -0,0 +1,178 @@ +# Copyright (c) 2024 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import subprocess + +from lib.environment import Environment +from lib.exceptions import CommandExecutionError, CommandExecutionTimeout +from lib.execution_replay import ReplayContext + + +class CommandRunner(object): + """ + A utility class to execute external command and obtain the output + This class also provide a mechanism for rerouting the execution on + remote machine via SSH session, capturing the execution result and + replaying it back for development and testing purpose. + """ + + def __init__(self, *command_args, **options): + """ + CommandRunner constructor + + :param command_args: the command and its arguments + :param options: Supports the following optional parameters: + - expected_return_code: if specified, CommandRunner will check the command + return value and will raise CommandExecutionError it doesn't match + - command_input: Text to be supplied to stdin when executing the command + - binary_output: indicating that the command is expected to return binary output + """ + self.command_args = command_args + self.options = options + self.timeout = None + self.replay_context = None + + if options.get('expected_return_code') is not None: + self.expected_return_code = options['expected_return_code'] + else: + self.expected_return_code = None + if options.get('command_input') is not None: + self.command_input = options['command_input'] + else: + self.command_input = None + if options.get('binary_output') is not None: + self.binary_output = options['binary_output'] is True + else: + self.binary_output = False + if options.get('timeout') is not None: + self.timeout = options['timeout'] + + self.remote_hostname = None + self.remote_username = None + self.is_remote = False + self.setup_remote_exec() + + + def setup_remote_exec(self): + """ + Setup the remote execution, capture/replay mechanism if the required + keys are set in the environment + """ + env = Environment.get_environment() + if env.get_value('VCERT_REMOTE_EXEC'): + self.set_remote(env.get_value('VCERT_REMOTE_HOSTNAME'), + env.get_value('VCERT_REMOTE_USERNAME')) + if env.get_value('VCERT_REMOTE_EXEC_REPLAY') is True \ + or env.get_value('VCERT_REMOTE_EXEC_CAPTURE') is True: + self.replay_context = ReplayContext.get_replay_context() + + def set_remote(self, hostname, username='root'): + self.is_remote = True + self.remote_hostname = hostname + self.remote_username = username if not username else 'root' + + + def set_input(self, command_input): + self.command_input = command_input + + + def run(self): + """ + Run the command, redirect to run_remote if remote execution is set + """ + if self.is_remote: + return self.run_remote(self.command_args, self.command_input, self.timeout, + self.expected_return_code, self.binary_output) + else: + return self.run_local(self.command_args, self.command_input, self.timeout, + self.expected_return_code, self.binary_output) + + def run_and_get_output(self): + """ + Run the command and get the standard output only + """ + _, stdout, _ = self.run() + return stdout + + + @staticmethod + def run_local(command_args, command_input, timeout, expected_return_code, binary_output) -> (int, str, str): + """ + Run the command locally + + :param command_args: external command and the arguments + :param command_input: Text to be supplied as stdin + :param timeout: Timeout value for waiting the external command to + return. It will raise CommandExecutionTimeout when this happen + :param expected_return_code: The expected return code. If it's + specified and it doesn't match to the actual return code, + CommandExecutionError will be raised + :param binary_output: need to handle binary output instead of text + :return: a tuple of (return code, stdout output, stderr output) + """ + try: + if binary_output: + ret = subprocess.run(command_args, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, timeout=timeout) + elif command_input == subprocess.DEVNULL: + ret = subprocess.run(command_args, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, universal_newlines=True, timeout=timeout) + else: + ret = subprocess.run(command_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + input=command_input, universal_newlines=True, timeout=timeout) + if expected_return_code is not None: + if ret.returncode != expected_return_code: + raise CommandExecutionError("External command '{}' returned {}, error message: {}".format( + " ".join(command_args), ret.returncode, ret.stderr)) + return ret.returncode, ret.stdout, ret.stderr + except subprocess.TimeoutExpired: + raise CommandExecutionTimeout("External command '{}' timed out".format(" ".join(command_args))) + + def run_remote(self, command_args, command_input, timeout, expected_return_code, binary_output): + """ + Run the command on remote machine, or perform capture/replay when + set in the environment variables + + It will append the required ssh command arguments 'ssh', '-l', '', '' + The capture and replay mechanism will use the remote setting for storing + and retrieve the command result. + + Refer to run_local for the parameter description + """ + env = Environment.get_environment() + remote_hostname = env.get_value('VCERT_REMOTE_HOSTNAME') + remote_username = env.get_value('VCERT_REMOTE_USERNAME') + local_hostname = env.get_value('LOCAL_HOSTNAME') + + if self.replay_context and self.replay_context.is_replaying: + result = self.replay_context.get_execution_result('command', command_args, command_input) + if result is not None: + if expected_return_code is not None: + return_code, _, _ = result + if return_code != expected_return_code: + raise CommandExecutionError("External command '{}' returned {}".format( + " ".join(command_args), return_code)) + return result + + if local_hostname and remote_hostname.lower() == local_hostname.lower(): + # run locally + final_args = command_args + else: + final_args = ['ssh', '-l', remote_username, remote_hostname] + final_args.extend(command_args) + CommandRunner.add_quotation_escape(final_args) + return_code, stdout, stderr = CommandRunner.run_local(final_args, command_input, timeout, + None, binary_output) + if self.replay_context and self.replay_context.is_capturing: + self.replay_context.store_result('command', command_args, command_input, return_code, stdout, stderr) + if expected_return_code is not None and expected_return_code != return_code: + raise CommandExecutionError("External command '{}' returned {}".format( + " ".join(command_args), return_code)) + return return_code, stdout, stderr + + @staticmethod + def add_quotation_escape(args): + for index, arg in enumerate(args): + if ' ' in arg or '"' in arg or '\'' in arg or '\\' in arg: + args[index] = "\"{}\"".format(arg.replace('"', '\\"')) diff --git a/vCert-6.1.1-20260401/lib/console.py b/vCert-6.1.1-20260401/lib/console.py new file mode 100644 index 0000000..ad4516b --- /dev/null +++ b/vCert-6.1.1-20260401/lib/console.py @@ -0,0 +1,155 @@ +# Copyright (c) 2024 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import sys +import tempfile + +from lib.environment import Environment, DefaultDict +from lib.text_utils import translate_text + + +class ColorKey(object): + """ + Class for holding color mapping to its terminal escape sequence + """ + RED = '{COLORS[RED]}' + GREEN = '{COLORS[GREEN]}' + BLUE = '{COLORS[BLUE]}' + YELLOW = '{COLORS[YELLOW]}' + CYAN = '{COLORS[CYAN]}' + LIGHT_BLUE = '{COLORS[LIGHT_BLUE]}' + UNDER_LINE = '{COLORS[UNDER_LINE]}' + NORMAL = '{COLORS[NORMAL]}' + + +def init_env_colormap(): + """ + Initialize color map in the environment + """ + env = Environment.get_environment() + color_disabled = env.get_value('COLORS_DISABLED') is True + if color_disabled: + color_map = DefaultDict() + else: + color_map = { + 'RED': '\033[31m', + 'GREEN': '\033[32m', + 'YELLOW': '\033[33m', + 'BLUE': '\033[34m', + 'CYAN': '\033[36m', + 'LIGHT_BLUE': '\033[94m', + 'UNDER_LINE': '\033[04m', + 'NORMAL': '\033[00m' + } + env.set_value('COLORS', color_map) + + +def set_text_color(color): + """ + Set the color. The color will affect any normal print() calls following this method call + :param color: ColorKey object + """ + print_text(color, end='') + + +def print_text(*text, max_width=0, sep='', end='\n'): + """ + Print text with additional preprocessing based + + :param text: Text to be print. The text will be translated first + :param max_width: if > 0, new line will be added automatically if each line + :param sep: separator between text + :param end: add end text after the last of line + """ + text = translate_text(*text, sep=sep) + if max_width > 0: + text_len = len(text) + idx = 0 + while idx < text_len: + if idx + max_width < text_len: + print(text[idx:max_width]) + else: + print(text[idx:], end=end) + else: + print(text, end=end) + sys.stdout.flush() + + +def print_text_warning(text, end='\n'): + """ + Print warning text with yellow color. + + :param text: Text to be print + :param end: end characters at the end of text + """ + print_text(ColorKey.YELLOW, text, ColorKey.NORMAL, sep='', end=end) + + +def print_text_error(text, end='\n'): + """ + Print error text. By default it's identical to print_text_warning, + printing text with yellow color. + """ + print_text_warning(text, end=end) + + +def print_header(text): + """ + Print header text + """ + print() + print_text("{}{}".format(ColorKey.CYAN, text)) + print_text("{}{}".format('-' * 65, ColorKey.NORMAL)) + + +def print_task(text): + """ + Print formatted text for task name + """ + print("{:<52}".format(text), end='', flush=True) + + +def print_task_status(status_text, color=ColorKey.GREEN): + """ + Print formatted text for task status + """ + set_text_color(color) + print("{:>13}".format(status_text), end='') + set_text_color(ColorKey.NORMAL) + print(flush=True) + + +def print_task_status_warning(result): + """ + Print warning task status (color: yellow) + """ + print_task_status(result, ColorKey.YELLOW) + + +def print_task_status_error(result): + """ + Print error task status (color: red) + """ + print_task_status(result, ColorKey.RED) + + +def capture_method_output(method, *args, **kwargs): + """ + Invoke the method and capture the stdout output produced by this method + + :param method: method to be invoked + :return: captured stdout output + """ + stdout = sys.stdout + try: + with tempfile.NamedTemporaryFile('w+') as file: + sys.stdout = file + method(*args, **kwargs) + file.seek(0) + return file.read() + finally: + sys.stdout = stdout + + +init_env_colormap() diff --git a/vCert-6.1.1-20260401/lib/constants.py b/vCert-6.1.1-20260401/lib/constants.py new file mode 100644 index 0000000..f1d992e --- /dev/null +++ b/vCert-6.1.1-20260401/lib/constants.py @@ -0,0 +1,42 @@ +# Copyright (c) 2024-2026 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +VCERT_PROGRAM = 'vCert.py' +VCERT_NAME = 'VCF/VVF Certificate Management Utility' +VCERT_VERSION = '6.1.1' +VCERT_DESC = "{} (version {})".format(VCERT_NAME, VCERT_VERSION) + +TOP_DIR = '/storage/vCert' +LOG_DIR = '/var/log/vmware/vCert' +REPORT_FILE_PATH = "{}/vcenter-certificate-report.txt".format(LOG_DIR) +VMCA_CERT_FILE_PATH = '/var/lib/vmware/vmca/root.cer' +VMCA_KEY_FILE_PATH = '/var/lib/vmware/vmca/privatekey.pem' +VMCA_SSO_FILE_PATH = '/etc/vmware-sso/keys/ssoserverRoot.crt' +VMCAM_CERT_FILE_PATH = '/var/lib/vmware/vmcam/ssl/vmcamcert.pem' +RBD_CERT_FILE_PATH = '/etc/vmware-rbd/ssl/rbd-ca.crt' +AUTH_PROXY_CERT_FILE_PATH = '/var/lib/vmware/vmcam/ssl/rui.crt' +RHTTPPROXY_CONFIG_FILE_PATH = '/etc/vmware-rhttpproxy/config.xml' + +# For VC prior to version 9.0 the STS server config file was an XML file. +# Beginning with version 9.0 the format was converted to a Java property file. +STS_SERVER_CONFIG_FILE_PATH = '/usr/lib/vmware-sso/vmware-sts/conf/server.xml' +STS_SERVER_CONFIG_PROPERTY_FILE_PATH = '/usr/lib/vmware-sso/vmware-sts/conf/catalina.properties' + +LOCALHOST = 'localhost' +READ = 'read' +WRITE = 'write' +SPACE = " " +WARNING_TEXT = """ +{COLORS[YELLOW]}------------------------!!! Attention !!!------------------------{COLORS[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 snapshots of {COLORS[UNDER_LINE]}{COLORS[YELLOW]}ALL{COLORS[NORMAL]} vCenter/PSC nodes in the SSO domain +before continuing. Please refer to the following Knowledge Base article: +{COLORS[UNDER_LINE]}{COLORS[YELLOW]}https://knowledge.broadcom.com/external/article?legacyId=85662{COLORS[NORMAL]} + +Do you acknowledge the risks and wish to continue? [y/n]: """ +VMWARE_DEPOT_HOST = 'dl.broadcom.com' +VMWARE_DEPOT_CERT_ISSUER = 'DigiCert TLS RSA SHA256 2020 CA1' diff --git a/vCert-6.1.1-20260401/lib/environment.py b/vCert-6.1.1-20260401/lib/environment.py new file mode 100644 index 0000000..e6b57fc --- /dev/null +++ b/vCert-6.1.1-20260401/lib/environment.py @@ -0,0 +1,82 @@ +# Copyright (c) 2024 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import yaml +from pathlib import Path + + +class DefaultDict(dict): + + def __missing__(self, key): + return '' + + +class Environment(object): + + shared_environment = None + + def __init__(self): + self.cache = DefaultDict() + self.load_default() + self.restricted_keys = dict() + + def get_value(self, key): + value = self.cache.get(key) + if value is None and key in self.restricted_keys.keys(): + for k, v in self.restricted_keys[key](): + self.cache[k] = v + if k == key: + value = v + return value + + def set_value(self, key, value): + if key in self.restricted_keys.keys(): + raise KeyError('Cannot update environment using restricted key') + self.cache[key] = value + + def invalidate_value(self, key): + self.cache[key] = None + + def get_map(self): + return self.cache + + def add_restricted_key(self, key, populate_method): + """ + Set {key} as restricted variable. The existing value is invalidated. + + :param key: environment key + :param populate_method: method to be called to populate the values + """ + if key in self.restricted_keys: + raise KeyError('Duplicating restricted key') + self.restricted_keys[key] = populate_method + self.cache[key] = None + + def load_from_file(self, env_file): + """ + Load environment variables from config file + :param env_file: config file + """ + self.cache = DefaultDict() + self.restricted_keys = dict() + with open(env_file, 'r') as file: + config = yaml.safe_load(file) + env_map = config['environments'] + for key in env_map.keys(): + self.set_value(key, env_map[key]) + + def load_default(self): + """ + Load environment variables from default config file 'config/env.yaml' + """ + base_dir = str(Path(__file__).resolve().parent.parent) + default_env_file = Path(base_dir, 'config/env.yaml') + if Path.exists(default_env_file): + self.load_from_file(str(default_env_file)) + + @staticmethod + def get_environment(): + if Environment.shared_environment is None: + Environment.shared_environment = Environment() + return Environment.shared_environment diff --git a/vCert-6.1.1-20260401/lib/exceptions.py b/vCert-6.1.1-20260401/lib/exceptions.py new file mode 100644 index 0000000..7e09dbd --- /dev/null +++ b/vCert-6.1.1-20260401/lib/exceptions.py @@ -0,0 +1,39 @@ +# Copyright (c) 2024 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +class BaseScriptException(Exception): + def __init__(self, error_msg): + super().__init__(error_msg) + + +class LdapException(BaseScriptException): + """ + Base Exception class for all the Exception in ldap functionality + """ + def __init__(self, error_code, description): + self.error_msg = "LDAP exception error code {} ({})".format(error_code, description) + super().__init__(self.error_msg) + + def __str__(self): + return self.error_msg + + +class MenuExitException(BaseScriptException): + pass + + +class CommandExecutionError(BaseScriptException): + pass + + +class CommandExecutionTimeout(BaseScriptException): + pass + + +class ReplayEntryNotFound(BaseScriptException): + pass + + +class OperationFailed(BaseScriptException): + pass diff --git a/vCert-6.1.1-20260401/lib/execution_replay.py b/vCert-6.1.1-20260401/lib/execution_replay.py new file mode 100644 index 0000000..3ef0d37 --- /dev/null +++ b/vCert-6.1.1-20260401/lib/execution_replay.py @@ -0,0 +1,240 @@ +# Copyright (c) 2024 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import base64 +import json +import glob +import os +import subprocess +from collections.abc import Mapping +from datetime import datetime + +from lib.environment import Environment +from lib.exceptions import ReplayEntryNotFound, CommandExecutionError + +replay_context = None + + +class ReplayEntry(object): + """ + Class for representing execution result entry + """ + + def __init__(self, category, command_args, input_string, tag): + """ + ReplayEntry constructor + + :param category: category of result (e.g. 'command', 'ldap') + :param command_args: command and the parameters + :param input_string: Input text to be supplied as stdin + """ + self.category = category + self.command_args = command_args + self.input_string = input_string if input_string is not None else '' + self.used_count = 0 + self.key = self.generate_key(category, command_args, input_string, tag) + self.results = [] + + def add_result(self, timestamp, return_code, stdout, stderr): + """ + Add command result entry + + :param timestamp: the timestamp of command execution + :param return_code: command's return code + :param stdout: The command output from stdout + :param stderr: The command output from stderr + """ + # always add result in sorted + added = False + value = (timestamp, return_code, stdout, stderr) + for index, (ts, _, _, _) in enumerate(self.results): + if ts > timestamp: + self.results.insert(index, value) + added = True + break + if not added: + self.results.append(value) + + @staticmethod + def generate_key(category, command_args, input_string, tag): + """ + Generate key based on category, command_args, and input_string. + The key is not guaranteed to be unique but should be good enough for + debugging and testing purpose. This key will be used to retrieve + the previous command result for replay + + :param category: the category, 'command', 'ldap' + :param command_args: command arguments + :param input_string: input string + :param tag: additional tag to differentiate the same commands in a different condition + :return: generated key + """ + for idx, arg in enumerate(command_args): + if not isinstance(arg, str): + command_args[idx] = str(arg) + if input_string == subprocess.DEVNULL: + input_string = '__/dev/null__' + return "{}||{}||{}||{}".format(category, ">>".join(command_args), input_string, tag) + + +class ReplayContext(object): + """ + The class holding context object for capturing or replaying command result + """ + + categories = ['command', 'ldap', 'other'] + + def __init__(self, replay_dir, is_replaying=True, is_capturing=False): + """ + ReplayContext constructor + + :param replay_dir: base directory for storing or retrieving command result + :param is_capturing: True when capturing, otherwise it's in replay mode + """ + self.replay_dir = replay_dir + self.replay_entries = None + self.is_replaying = is_replaying + self.is_capturing = is_capturing + self.tag = None + + def get_replay_entry(self, key) -> ReplayEntry: + """ + Get replay entry based on the key + + :param key: key to be used to retrieve the command result + """ + if self.replay_entries is None: + self.load_all_replays() + return self.replay_entries.get(key) + + def store_result(self, category, command_args, command_input, return_code, stdout, stderr): + """ + Store the command result + + The file will be stored in //replay__<1st command_args>_.json + """ + command = command_args[0].split('/')[-1] + timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S_%f') + file_name = "{}/replay_{}_{}_{}.json".format(self.replay_dir, category, + command, timestamp) + with open(file_name, 'w') as file: + content = { + "command_args": ReplayContext.encode_bytes(command_args), + "input": command_input if command_input != subprocess.DEVNULL else '__/dev/null__', + "tag": self.tag, + "timestamp": timestamp, + "return_code": return_code, + "stdout": self.encode_bytes(stdout), + "stderr": self.encode_bytes(stderr) + } + try: + file.write(json.dumps(content, indent=4)) + except TypeError as te: + raise CommandExecutionError("Unserializable object: {}".format(content)) + + if self.is_replaying: + self.add_replay_entry(category, command_args, command_input, self.tag, timestamp, + return_code, stdout, stderr) + + def set_tag(self, tag): + self.tag = tag + + def load_all_replays(self): + """ + Load all replay entries + """ + self.replay_entries = {} + for category in ReplayContext.categories: + for file in sorted(glob.glob("{}/replay_{}_*.json".format(self.replay_dir, category))): + with open(file, 'r') as f: + replay = json.load(f) + command_args = ReplayContext.decode_bytes(replay['command_args']) + command_input = replay['input'] + stdout = ReplayContext.decode_bytes(replay['stdout']) + stderr = ReplayContext.decode_bytes(replay['stderr']) + self.add_replay_entry(category, command_args, command_input, replay['tag'], replay['timestamp'], + replay['return_code'], stdout, stderr) + + def add_replay_entry(self, category, command_args, command_input, tag, timestamp, + return_code, stdout, stderr): + """ + Add replay entry + """ + key = ReplayEntry.generate_key(category, command_args, command_input, tag) + entry = self.get_replay_entry(key) + if entry is None: + entry = ReplayEntry(category, command_args, command_input, tag) + self.replay_entries[key] = entry + entry.add_result(timestamp, return_code, stdout, stderr) + + def reset_context(self): + for entry in self.replay_entries: + entry.used_count = 0 + + def get_execution_result(self, category, command_args, command_input) -> (int, str, str): + """ + Get the previous execution result based on {category}, {command_args}, and {command_input} + """ + key = ReplayEntry.generate_key(category, command_args, command_input, self.tag) + entry = self.get_replay_entry(key) + if entry is None: + if self.is_capturing: + return None + else: + raise ReplayEntryNotFound("Replay entry not found for key {}".format(key)) + result = entry.results[entry.used_count] + entry.used_count = (entry.used_count + 1) % len(entry.results) + return result[1:] + + @staticmethod + def get_replay_context(reload=False): + """ + Static method to get ReplayContext instance from other modules + + :param reload: if True, it will dispose the previous object and create a new instance + """ + global replay_context + env = Environment.get_environment() + is_remote = env.get_value('VCERT_REMOTE_EXEC') is True + is_replay = env.get_value('VCERT_REMOTE_EXEC_REPLAY') is True + is_capture = env.get_value('VCERT_REMOTE_EXEC_CAPTURE') is True + if reload: + replay_context = None + if is_remote and (is_replay or is_capture) and replay_context is None: + remote_hostname = env.get_value('VCERT_REMOTE_HOSTNAME') + replay_dir = "{}/{}".format(env.get_value('VCERT_REMOTE_EXEC_REPLAY_DIR'), + remote_hostname) + if not os.path.exists(replay_dir): + os.makedirs(replay_dir) + replay_context = ReplayContext(replay_dir, is_replay, is_capture) + return replay_context + + @staticmethod + def encode_bytes(obj): + if isinstance(obj, bytes): + return "__base64__({})".format(base64.b64encode(obj).decode('utf-8')) + if isinstance(obj, list): + for idx, value in enumerate(obj): + obj[idx] = ReplayContext.encode_bytes(value) + elif isinstance(obj, dict): + for key in obj.keys(): + obj[key] = ReplayContext.encode_bytes(obj[key]) + elif isinstance(obj, Mapping): + new_obj = dict() + for key in obj.keys(): + new_obj[key] = ReplayContext.encode_bytes(obj[key]) + obj = new_obj + return obj + + @staticmethod + def decode_bytes(obj): + if isinstance(obj, str) and obj.startswith('__base64__(') and obj.endswith(')'): + return base64.b64decode(obj[11:-1]) + if isinstance(obj, list): + for idx, value in enumerate(obj): + obj[idx] = ReplayContext.decode_bytes(value) + elif isinstance(obj, dict): + for key in obj.keys(): + obj[key] = ReplayContext.decode_bytes(obj[key]) + return obj diff --git a/vCert-6.1.1-20260401/lib/host_utils.py b/vCert-6.1.1-20260401/lib/host_utils.py new file mode 100644 index 0000000..82abec6 --- /dev/null +++ b/vCert-6.1.1-20260401/lib/host_utils.py @@ -0,0 +1,502 @@ +# Copyright (c) 2024-2025 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import ipaddress +import glob +import logging +import os +import pathlib +import re +import shutil +import socket +import sys +import xml.etree.ElementTree as ET +from enum import Enum + +from lib.console import print_text_warning +from lib.constants import RHTTPPROXY_CONFIG_FILE_PATH, STS_SERVER_CONFIG_FILE_PATH, STS_SERVER_CONFIG_PROPERTY_FILE_PATH +from lib.environment import Environment +from lib.execution_replay import ReplayContext +from lib.command_runner import CommandRunner +from lib.java_utils import load_property_file +from lib.text_utils import TextFilter + +VMAFD_CLI = '/usr/lib/vmware-vmafd/bin/vmafd-cli' +LWREGSHELL_CLI = '/opt/likewise/bin/lwregshell' +TEE_CMD = '/usr/bin/tee' +FILE_CMD = '/usr/bin/file' +CHMOD_CMD = '/bin/chmod' + + +logger = logging.getLogger(__name__) + + +class VcVersion(Enum): + """ + Define the recognized versions of VC. + """ + # We assume the needed version values are only major and minor releases, + # and not patches for example. This allows us to represent the versions as + # floating point numbers for relative comparison. For code specific to + # patch releases, the tool resorts to build numbers, which are checked as + # needed. + # + # Also, the order of enum definitions must be kept in increasing order, + # including minor releases. + Invalid = None + V7 = '7' + V8 = '8' + V9 = '9' + + @classmethod + def min(cls): + """Minimum supported vCenter version""" + return min([float(v) for v in cls if v.value is not None]) + + @classmethod + def max(cls): + """Maximum supported vCenter version""" + return max([float(v) for v in cls if v.value is not None]) + + @property + def major(self): + """Return the major release number for the release.""" + if self.value is None: + return None + # Floats that are actually integers always end with ".0" as a string. + return str(float(self)).split('.')[0] + + @property + def minor(self): + """Return the minor release number for the release.""" + if self.value is None: + return None + # Floats that are actually integers always end with ".0" as a string. + return str(float(self)).split('.')[1] + + def _validate(self, other): + if not isinstance(other, VcVersion): + raise ValueError(other) + + def __float__(self): + # If there is no value (for the Invalid entry) then return float NaN + # so all comparison operations return False. (However, we provide + # special direct comparison overrides for != and == so that comparisons + # with VcVersion.Invalid work as expected.) + return float('nan') if self.value is None else float(self.value) + + def __lt__(self, other): + self._validate(other) + return float(self) < float(other) + + def __le__(self, other): + self._validate(other) + return float(self) <= float(other) + + def __eq__(self, other): + self._validate(other) + return self.value == other.value + + def __ne__(self, other): + self._validate(other) + return self.value != other.value + + def __gt__(self, other): + self._validate(other) + return float(self) > float(other) + + def __ge__(self, other): + self._validate(other) + return float(self) >= float(other) + + +def init_env_host(): + """ + Obtain basic information from VC server + """ + vpxd_info = CommandRunner('vpxd', '-v').run_and_get_output().strip().split(' ') + vc_version_long = vpxd_info[2] + vc_version = '.'.join(vc_version_long.split('.')[:-1]) + vc_build = vpxd_info[3].split('-')[1] + hostname = CommandRunner('hostname', '-f').run_and_get_output().strip() + pnid = CommandRunner(VMAFD_CLI, 'get-pnid', '--server-name', 'localhost').run_and_get_output().strip() + machine_id = CommandRunner(VMAFD_CLI, 'get-machine-id', '--server-name', 'localhost').run_and_get_output().strip() + ifconfig_output = CommandRunner('/usr/sbin/ifconfig', 'eth0').run_and_get_output() + ip_address = TextFilter(ifconfig_output).head(2).tail(1).get_text().strip().split(' ')[0].replace(':', ' ').split(' ')[-1] + sso_domain = CommandRunner(VMAFD_CLI, 'get-domain-name', '--server-name', 'localhost').run_and_get_output().strip() + sso_site = CommandRunner(VMAFD_CLI, 'get-site-name', '--server-name', 'localhost').run_and_get_output().strip() + sso_domain_dn = "dc={}".format(",dc=".join(sso_domain.split('.'))) + vmdir_account_dn, vmdir_account_password = get_vmdir_machine_account() + local_hostname = socket.getfqdn() + + env = Environment.get_environment() + env.set_value('VC_VERSION', vc_version) + env.set_value('VC_VERSION_LONG', vc_version_long) + env.set_value('VC_BUILD', vc_build) + env.set_value('HOSTNAME', hostname) + env.set_value('LOCAL_HOSTNAME', local_hostname) + env.set_value('PNID', pnid) + env.set_value('MACHINE_ID', machine_id) + env.set_value('IP_ADDRESS', ip_address) + env.set_value('SSO_DOMAIN', sso_domain) + env.set_value('SSO_SITE', sso_site) + env.set_value('SSO_DOMAIN_DN', sso_domain_dn) + env.set_value('VMDIR_MACHINE_ACCOUNT_DN', vmdir_account_dn) + env.set_value('VMDIR_MACHINE_ACCOUNT_PASSWORD', vmdir_account_password) + + smart_card_filter_file = get_smart_card_filter_file() + env.set_value('SMART_CARD_FILTER_FILE', smart_card_filter_file) + + init_env_solution_users() + + +def get_vmdir_machine_account(): + """ + Get machine account from likewise + :return: a tupple of ( account_dn, account_password) + """ + output = CommandRunner(LWREGSHELL_CLI, 'list_values', + r'[HKEY_THIS_MACHINE\services\vmdir]').run_and_get_output() + + account_dn = TextFilter(output).contain('"dcAccountDN"').cut('REG_SZ', [1]).get_text().strip()[1:-1]\ + .replace('\\"', r'"').replace('\\\\', '\\') + account_password = TextFilter(output).contain('dcAccountPassword').cut('REG_SZ', [1]).get_text().strip()[1:-1]\ + .replace('\\"', r'"').replace('\\\\', '\\') + return account_dn, account_password + + +def is_file_exists(file): + """ + Wrapper for checking file existence. + This wrapper is required to redirect operation when VCERT_REMOTE_EXEC is True + + :param file: file path to be checked + :return: True if file is exist + """ + if not is_remote_exec(): + return os.path.exists(file) + + return_code, _, _ = CommandRunner('/bin/ls', file, expected_return_code=None).run() + return return_code == 0 + + +def get_config_file_path(config_file): + env = Environment.get_environment() + if os.path.isabs(config_file): + if is_file_exists(config_file): + return config_file + else: + print_text_warning("Unable to find configuration file '{}'".format(config_file)) + print_text_warning('Script cannot continue. Exiting...') + sys.exit(1) + else: + vcert_relative_config_path = "{}/{}".format(env.get_value('SCRIPT_DIR'), config_file) + cwd_relative_config_path = "{}/{}".format(os.getcwd(), config_file) + if is_file_exists(vcert_relative_config_path): + return vcert_relative_config_path + elif is_file_exists(cwd_relative_config_path): + return cwd_relative_config_path + else: + print_text_warning("Configuration file does not exist in the vCert script directory ({}),".format(vcert_relative_config_path)) + print_text_warning("nor in the current working directory ({}).".format(cwd_relative_config_path)) + print_text_warning('Script cannot continue. Exiting...') + sys.exit(1) + + +def make_directory(path): + """ + Wrapper for making directory. + This wrapper is required to redirect operation when VCERT_REMOTE_EXEC is True + + :param path: directory path to be created + :return: True if the operation success, otherwise False + """ + if is_remote_exec(): + command_runner = CommandRunner('/bin/mkdir', '-p', path, expected_return_code=None) + return_code, _, _ = command_runner.run() + return return_code == 0 + + try: + pathlib.Path(path).mkdir(parents=True, exist_ok=True) + return True + except Exception: + return False + + +def remove_directory(path, remove_all=False): + """ + Wrapper for removing directory + This wrapper is required to redirect operation when VCERT_REMOTE_EXEC is True + :param path: Directory path to remove + :param remove_all: if True, this method will remove the directory contents before trying to + remove the directory + :return: True if the operation success, otherwise False + """ + if is_remote_exec(): + if remove_all: + command_runner = CommandRunner('/bin/rmdir', '-r', path, expected_return_code=None) + else: + command_runner = CommandRunner('/bin/rmdir', path, expected_return_code=None) + return_code, _, _ = command_runner.run() + return return_code == 0 + + try: + if remove_all: + shutil.rmtree(path) + else: + pathlib.Path(path).rmdir() + except Exception: + return False + + +def get_file_contents(file): + """ + Wrapper for reading file contents. + This wrapper is required to redirect operation when VCERT_REMOTE_EXEC is True + + :param file: File path to load + :return: File contents + """ + if not is_remote_exec(): + with open(file, 'r') as f: + return f.read() + + command_runner = CommandRunner('/bin/cat', file, expected_return_code=0) + _, stdout, _ = command_runner.run() + return stdout + + +def get_hostname(): + """ + Wrapper obtaining remote hostname + This wrapper is required to redirect operation when VCERT_REMOTE_EXEC is True + + :return: return VCERT_REMOTE_HOSTNAME value if VCERT_REMOTE_EXEC is True, + otherwise return 'localhost' + """ + if is_remote_exec(): + hostname = Environment.get_environment().get_value('VCERT_REMOTE_HOSTNAME') + else: + hostname = 'localhost' + return hostname + + +def is_remote_exec(): + """ + Check if remote execution is set + :return: True if remote execution is set, otherwise False + """ + return Environment.get_environment().get_value('VCERT_REMOTE_EXEC') is True + + +def get_vc_version(): + """ + Get the VC version. The value is obtained from environment variable VC_VERSION + + :return: VC version as VcVersion enum + """ + vc_version = Environment.get_environment().get_value('VC_VERSION') + # Versions in VcVersion are kept in increasing order. Thus we search the + # versions in reverse order to bulletproof the code against versions that + # are represented just as a major release versus ones that are represented + # as major.minor. + for definedVcVersion in reversed(VcVersion): + if ( definedVcVersion.value is not None + and vc_version.startswith(definedVcVersion.value)): + return definedVcVersion + return VcVersion.Invalid + + +def init_env_solution_users(): + """ + Initialize set of solution users to be checked, saved the list into environment + variable 'SOLUTION_USERS' + """ + solutionUserDB = { + # Solution User Version Constraint (None means all) + # ------------------- ----------------------------------- + 'machine': None, + 'vsphere-webclient': None, + 'vpxd': None, + 'vpxd-extension': None, + 'hvc': None, + 'wcp': [VcVersion.V7, VcVersion.V8], + 'wcpsvc': [VcVersion.V9], + } + + env = Environment.get_environment() + vc_version = get_vc_version() + + solution_users = [] + for solutionUser, versionConstraint in solutionUserDB.items(): + if versionConstraint and not vc_version in versionConstraint: + continue + solution_users.append(solutionUser) + + env.set_value('SOLUTION_USERS', solution_users) + + +def is_valid_ip_address(hostname_or_ip): + try: + ipaddress.ip_address(hostname_or_ip) + return True + except ValueError: + return False + + +def get_ip_address(hostname_or_ip): + if is_valid_ip_address(hostname_or_ip): + return hostname_or_ip + else: + return socket_gethostbyname(hostname_or_ip) + + +def socket_gethostbyname(hostname): + category = 'other' + command_args = ['socket', 'gethostbyname', 'hostname', hostname] + context = ReplayContext.get_replay_context() + if context and context.is_replaying: + result = context.get_execution_result(category, command_args, None) + if result is not None: + _, ip_address, _ = result + return ip_address + + try: + ip_address = socket.gethostbyname(hostname) + except socket.gaierror: + ip_address = '' + if context and context.is_capturing: + context.store_result(category, command_args, None, 0, ip_address, None) + + return ip_address + + +def save_text_to_file(text, filename): + """ + A wrapper to write {text} to file {filename} + :param filename: the output filename + :param text: file contents + """ + if is_remote_exec(): + CommandRunner(TEE_CMD, filename, command_input=text, expected_return_code=0).run() + return + + with open(filename, 'w') as file: + file.write(text) + + +def append_text_to_file(text, filename, start_new_line=True): + """ + A wrapper to append {text} to file {filename} + :param text: text to append + :param file: the existing filename + :param start_new_line: start appending data on a new line + """ + if is_remote_exec(): + CommandRunner(TEE_CMD, '-a', filename, command_input=text, expected_return_code=0).run() + return + + with open(filename, 'a') as file: + if start_new_line: + file.write("\n{}".format(text)) + else: + file.write(text) + +def find_files(pattern): + """ + Wrapper for glob.glob({pattern}) method + :param pattern: glob patter for matching + """ + + if is_remote_exec(): + output = CommandRunner('/usr/bin/sh', '-c', "eval ls -1 '{}'".format(pattern)).run_and_get_output() + return output.splitlines() + + return glob.glob(pattern) + + +def get_file_type(filename): + """ + Get file type returned by /usr/bin/file command + """ + output = CommandRunner(FILE_CMD, filename, + expected_return_code =0).run_and_get_output() + return TextFilter(output).cut(delimiter=':', fields=[1]).get_text().strip() + + +def set_file_mode(file_path, mode): + """ + A wrapper for setting file mode on {filename} + :param file_path: File path to be modified + :param mode: permission value, the exact permission value to be passed to os.chmod() + """ + if is_remote_exec(): + mode_string = "{:04o}".format(mode) + CommandRunner(CHMOD_CMD, mode_string, file_path, expected_return_code=0).run() + else: + os.chmod(file_path, mode) + + +def get_smart_card_filter_file(): + def revproxy_get_file(): + # Before 7.0 U3i the location of the Smart Card filter file is defined + # in the reverse proxy config. + config = ET.parse(RHTTPPROXY_CONFIG_FILE_PATH) + root = config.getroot() + client_ca_list_file = root.find('clientCAListFile') + if client_ca_list_file is None: + filter_file = '' + else: + filter_file = client_ca_list_file.text + filter_file = '/etc/vmware-rhttpproxy/{}'.format(filter_file) if filter_file.startswith('/') is False else filter_file + return filter_file + + def xml_config_get_file(): + # After 7.0 U3i the location of the Smart Card filter file is + # hard-coded in the STS server config which is an XML file. + config = ET.parse(STS_SERVER_CONFIG_FILE_PATH) + root = config.getroot() + for connector in root.iter('Connector'): + if connector.attrib['port'] == '${bio-ssl-clientauth.https.port}': + return connector.find('SSLHostConfig').attrib['truststoreFile'] + return '' # Not found + + def prop_config_get_file(): + # Starting with 9.0 the Smart Card filter file is in the STS server + # configuration file which is a Java property file. + config = load_property_file(STS_SERVER_CONFIG_PROPERTY_FILE_PATH) + return config.get('vmidentity.server.connector.ssl.client.auth.truststore.file', '') + + # In the following DB, search stops on first match. + # Order of entries in the DB must be from most specific to least specific + # (for example an entry with a version specifying a build must be before + # the same version with no build). + smartCardConfigDB = [ + # Version Build Retrieval Function + # ------------- -------- --------------------- + (VcVersion.V7, 20845200, xml_config_get_file), + (VcVersion.V7, None, revproxy_get_file), + (VcVersion.V8, None, xml_config_get_file), + (VcVersion.V9, None, prop_config_get_file), + ] + + env = Environment.get_environment() + vc_version = get_vc_version() + vc_build = int(env.get_value('VC_BUILD')) + + # Resolve the retrieval function. We break on first match. + get_file = None + for versionConstraint, buildConstraint, func in smartCardConfigDB: + if vc_version != versionConstraint: + continue + if buildConstraint and vc_build < buildConstraint: + continue + + # Found a match. + get_file = func + break + + # If found, execute the retrieval function; otherwise return an empty + # string. + return get_file() if get_file else '' diff --git a/vCert-6.1.1-20260401/lib/input.py b/vCert-6.1.1-20260401/lib/input.py new file mode 100644 index 0000000..a038250 --- /dev/null +++ b/vCert-6.1.1-20260401/lib/input.py @@ -0,0 +1,97 @@ +# Copyright (c) 2024 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import getpass + +from lib.console import ColorKey, print_text +from lib.environment import Environment +from lib.text_utils import translate_text + + +class MenuInput(object): + """ + Class for getting user input. This class is separated from the rest of menu + related classes due to a circular module dependency. + """ + + def __init__(self, text, acceptable_inputs=None, default_input=None, allow_empty_input=False, + case_insensitive=True, masked=False): + """ + MenuInput constructor + + :param text: Text to be displayed on console before obtaining the user input + :param acceptable_inputs: All acceptable input user can enter + :param default_input: The default input to be shown and to be used when the user entered an empty input + :param allow_empty_input: If this parameter is True, the input can return empty string when + no default_input was specified + :param case_insensitive: Ignore text case. If this option is True, MenuInput will return input with + upper case + :param masked: If this is True, user typed characters will not be shown on the console (e.g. for password + input + """ + self.text = text + if acceptable_inputs and case_insensitive: + self.acceptable_inputs = [s.upper() for s in acceptable_inputs] + else: + self.acceptable_inputs = acceptable_inputs + self.default_input = default_input + self.allow_empty_input = allow_empty_input + self.case_insensitive = case_insensitive + self.masked = masked + + def get_acceptable_input_str(self) -> str: + """ + Return acceptable input is a simplified text + + This method expects that the acceptable_inputs will be in the following order: + 1, 2, ..., , X, Y + For above case, this method will return string like '1-, X, Y' + + :return: Returns acceptable input text + """ + keys = [s for s in self.acceptable_inputs if not s.isnumeric()] + numeric_keys_count = len(self.acceptable_inputs) - len(keys) + if numeric_keys_count == 1: + keys.insert(0, '1') + elif numeric_keys_count > 1: + keys.insert(0, "{}-{}".format(1, numeric_keys_count)) + return ", ".join(keys) + + @staticmethod + def builtin_input_wrapper(text, masked): + """ + A wrapper method for the builtin method 'input'. This method is used to simplify + mocking user input for the unit testing + """ + if masked: + return getpass.getpass(text) + return input(text) + + def get_input(self) -> str: + """ + Executing MenuInput: show the text, obtain and validate the user input + """ + if self.default_input is not None: + env = Environment.get_environment() + env.set_value('CURRENT_MENU', {'__DEFAULT__': self.default_input}) + while True: + text = translate_text(self.text) + input_text = self.builtin_input_wrapper(text, self.masked) + if self.case_insensitive: + input_text = input_text.upper() + if not input_text: + if self.default_input is not None: + return self.default_input + if self.allow_empty_input: + return '' + else: + print_text('{}Invalid input.{}'.format(ColorKey.YELLOW, ColorKey.NORMAL)) + continue + elif self.acceptable_inputs: + if input_text in self.acceptable_inputs: + return input_text + else: + print_text("\n{}Invalid input. The acceptable inputs: {}{}\n".format(ColorKey.YELLOW, self.get_acceptable_input_str(), ColorKey.NORMAL)) + else: + return input_text diff --git a/vCert-6.1.1-20260401/lib/java_utils.py b/vCert-6.1.1-20260401/lib/java_utils.py new file mode 100644 index 0000000..5f4da49 --- /dev/null +++ b/vCert-6.1.1-20260401/lib/java_utils.py @@ -0,0 +1,60 @@ +#!/usr/bin/python3 + +# Copyright (c) 2025 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. +# +# Java property file support is provided because for VC 9.0 the file: +# /usr/lib/vmware-sso/vmware-sts/conf/server.xml +# Indicated in lib/constants.py by: +# STS_SERVER_CONFIG_FILE_PATH +# has been changed to the Java property file: +# /usr/lib/vmware-sso/vmware-sts/conf/catalina.properties +# Indicated in lib/constants.py by: +# STS_SERVER_CONFIG_PROPERTY_FILE_PATH + +def load_property_file(file=None): + """ + Load Java property list file. + """ + properties = {} + with open(file) as f: + for line in f: + line = line.strip() + if line.startswith('#') or '=' not in line: + continue + keypath, value = line.split('=', 1) + + # The following commented out code loads the property file using + # nested dictionaries, following the hierarchy defined by the + # dotted key path. However, current usage doesn't require that, so + # for now we simply load the file as a simple dictionary to + # simplify key lookups with the current code. + # + # keys = keypath.split('.') + # keyDict = properties + # for key in keys[:-1]: + # if key not in keyDict: + # keyDict[key] = {} + # keyDict = keyDict[key] + # keyDict[keys[-1]] = value + + properties[keypath] = value + + return properties + +if __name__ == '__main__': + # Simple command-level driver for testing Java property file parsing. + import argparse + parser = argparse.ArgumentParser() + parser.add_argument( + '--file', + help='Java property file', + required=True + ) + + options = parser.parse_args() + + properties = load_property_file(file=options.file) + for key, value in properties.items(): + print("%s: %s" % (key, value)) diff --git a/vCert-6.1.1-20260401/lib/ldap_utils.py b/vCert-6.1.1-20260401/lib/ldap_utils.py new file mode 100644 index 0000000..8b318fa --- /dev/null +++ b/vCert-6.1.1-20260401/lib/ldap_utils.py @@ -0,0 +1,266 @@ +# Copyright (c) 2024 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import copy +import hashlib +import json +import logging +import ldap3 as ldap + +from lib.environment import Environment +from lib.exceptions import LdapException +from lib.execution_replay import ReplayContext + +logger = logging.getLogger(__name__) + + +class Ldap: + + @staticmethod + def open_ldap_connection(node, user_dn, password): + """ + Open ldap connection to the ldap server provided with + ldap admin user credentials. + :param node: hostname of the ldap server + :param user_dn: User DN for authentication + :param password: Password for authentication + :return: + """ + logger.info("Opening connection to {} with user {}".format(node, user_dn)) + server = ldap.Server(get_uri_from_hostname(node), get_info=ldap.ALL) + + env = Environment.get_environment() + is_remote_exec = env.get_value('VCERT_REMOTE_EXEC') is True + is_replay = env.get_value('VCERT_REMOTE_EXEC_REPLAY') is True + is_capture = env.get_value('VCERT_REMOTE_EXEC_CAPTURE') is True + if is_remote_exec and (is_replay or is_capture): + context = ReplayContext.get_replay_context() + ldap_connection = LdapConnectionReplay(context, server, user_dn, password) + else: + ldap_connection = ldap.Connection(server, user=user_dn, password=password) + + if not ldap_connection.bind(): + logger.error("Failed to do LDAP bind with host %s with %s error", + node, ldap_connection.result['description']) + raise LdapException(ldap_connection.result['result'], ldap_connection.result['description']) + return ldap_connection + + @staticmethod + def close_ldap_connection(ldap_connection) -> None: + """ + Close the ldap bind connection + :param ldap_connection:ldap connection to be closed + :return: + """ + logger.info("Closing LDAP connection") + ldap_connection.unbind() + if not ldap_connection.closed: + logger.error("Error closing connection. Error Msg: %s", ldap_connection.result["message"]) + + @staticmethod + def ldap_search(ldap_connection, base_dn, ldap_filter, ldap_scope, ldap_attributes) -> bool: + """ + This method takes the ldap connection, base dn, filter , scope + and list of ldap attributes to be returned. + :param ldap_connection connection to ldap server + :param base_dn dn where the search starts + :param ldap_filter to filter the search the results + :param ldap_scope scope of the search + :param ldap_attributes list of ldap attributes to be returned + """ + logger.info("LDAP search with\n base DN:%s\n filter: %s\n scope: %s\n attributes: %s", + base_dn, ldap_filter, str(ldap_scope), str(" ".join(ldap_attributes))) + if ldap_connection is None: + raise LdapException("-1", "No LDAP connection") + else: + if not ldap_connection.bind(): + raise LdapException(ldap_connection.result['result'], ldap_connection.result['description']) + result = ldap_connection.search(base_dn, ldap_filter, ldap_scope, attributes=ldap_attributes) + # When a filter doesn't match any entry result will be false + if result or (ldap_connection.result and ldap_connection.result['result'] == 0): + return True + logger.error("LDAP search failed. Error message: %s", ldap_connection.result["message"]) + return False + + @staticmethod + def get_attribute(ldap_entry, attribute) -> str: + """ + get the attribute value for a given ldap entry + This function can only be used for single value attributes. + :param ldap_entry: ldap entry + :param attribute: attribute to be returned + :return: value which is string + """ + val = ldap_entry['attributes'][attribute] + if isinstance(val, str): + return val + return val[0] + + @staticmethod + def ldap_delete(ldap_connection, entry_dn): + """ + This method takes ldap connection and deletes a given entry + :param ldap_connection: LDAP connection + :param entry_dn: entry DN to be delete + :return: + """ + logger.info("LDAP delete with DN: %s", entry_dn) + if ldap_connection is None: + logger.debug("No ldap connection") + raise LdapException("-1", "No LDAP connection") + else: + if not ldap_connection.bind(): + raise LdapException(ldap_connection.result['result'], ldap_connection.result['description']) + if ldap_connection.delete(entry_dn): + logger.debug("Deleted entry DN: %s", entry_dn) + return True + else: + if ldap_connection.result['result'] == 32: + logger.debug("Entry to be deleted %s doesn't exist", entry_dn) + return True + logger.error("Error deleting entry DN: %s, error code %s error msg %s", + entry_dn, ldap_connection.result['result'], + ldap_connection.result['description']) + return False + + @staticmethod + def ldap_modify(ldap_connection, dn, attribute, operation, value=None): + """ + This method takes ldap connection, DN and single attribute modification + for that particular DN + :param ldap_connection: + :param dn: + :param attribute: + :param operation: + :param value: + :return: + """ + logger.info("LDAP modify for DN %s with\n attribute: %s\n operation: %s\n value: %s", + dn, attribute, operation, value) + if ldap_connection is None: + logger.debug("No ldap connection") + raise LdapException("-1", "No Ldap connection") + if not ldap_connection.bind(): + raise LdapException(ldap_connection.result['result'], ldap_connection.result['description']) + if value is None: + value = [] + changes = [(operation, value)] if type(value) is list else [(operation, [value])] + result = ldap_connection.modify(dn, {attribute: changes}) + if result: + logger.info("Modified DN:%s", dn) + return True + + logger.error("Modifying entry %s failed with error code %s, description %s", + dn, ldap_connection.result['result'], ldap_connection.result['description']) + return False + + @staticmethod + def ldap_add(ldap_connection, dn, object_class, attributes): + """ + Add new LDAP entry with DN and attributes {attributes} + :param ldap_connection: LDAP connection + :param dn: DN to add + :param attributes: attributes + """ + logger.info("LDAP add for DN %s with\n attributes: %s\n", dn, str(attributes)) + if ldap_connection is None: + logger.debug("No ldap connection") + raise LdapException("-1", "No Ldap connection") + if not ldap_connection.bind(): + raise LdapException(ldap_connection.result['result'], ldap_connection.result['description']) + result = ldap_connection.add(dn, object_class, attributes) + if result: + logger.info("Added DN:%s", dn) + return True + + logger.error("Adding entry %s failed with error code %s, description %s", + dn, ldap_connection.result['result'], ldap_connection.result['description']) + return False + + +class LdapConnectionReplay(object): + """ + Class for capturing and replaying the previous LDAP operation results + """ + def __init__(self, context: ReplayContext, server, user_dn, password): + self.context = context + self.server = server + self.user_dn = user_dn + self.password = password + self.credential_hash = hashlib.sha1("{}:{}".format(user_dn, password).encode('utf-8')).hexdigest() + self.ldap_connection = ldap.Connection(server, user_dn, password) if self.context.is_capturing else None + self.closed = True + self.result = None + self.response = None + + def operation_wrapper(self, name, func, args, pass_connection=True): + final_args = [name] + final_args.extend(args) + if self.context.is_replaying: + prev_result = self.context.get_execution_result('ldap', final_args, self.credential_hash) + if prev_result is not None: + return_value, result_text, _ = prev_result + result = ReplayContext.decode_bytes(json.loads(result_text)) + self.result = result['_result_'] + self.response = result['_response_'] + self.closed = result['_closed_'] + if self.ldap_connection and func == self.ldap_connection.unbind: + self.ldap_connection.unbind() + return return_value + + return_value = func(self.ldap_connection, *args) if pass_connection else func(*args) + self.result = self.ldap_connection.result + self.response = self.ldap_connection.response + self.closed = self.ldap_connection.closed + result = dict() + result['_result_'] = ReplayContext.encode_bytes(copy.deepcopy(self.result)) + result['_response_'] = ReplayContext.encode_bytes(copy.deepcopy(self.response)) + result['_closed_'] = self.closed + try: + result_text = json.dumps(result, skipkeys=True) + except TypeError as te: + raise LdapException("Unserializable object: {}".format(result), str(te)) + self.context.store_result('ldap', final_args, self.credential_hash, return_value, result_text, None) + return return_value + + def bind(self): + func = self.ldap_connection.bind if self.ldap_connection else None + return self.operation_wrapper('bind', func, [], False) + + def unbind(self): + func = self.ldap_connection.unbind if self.ldap_connection else None + return self.operation_wrapper('unbind', func, [], False) + + def search(self, search_base, search_filter, search_scope, attributes): + args = [search_base, search_filter, search_scope, attributes] + return self.operation_wrapper('search', Ldap.ldap_search, args) + + def modify(self, dn, changes): + attribute = list(changes.keys())[0] + operation, values = changes[attribute][0] + args = [dn, attribute, operation, values] + return self.operation_wrapper('modify', Ldap.ldap_modify, args) + + def delete(self, dn): + return self.operation_wrapper('delete', Ldap.ldap_delete, [dn]) + + def add(self, dn, object_class, attributes): + return self.operation_wrapper('add', Ldap.ldap_add, [dn, object_class, attributes]) + + +def get_uri_from_hostname(hostname): + return "ldap://{}".format(hostname) + + +def get_domain_dn(domain): + if '@' in domain: + domain = domain.split('@', 2)[1] + domain_parts = domain.split('.') + return "dc={}".format(",dc=".join(domain_parts)) + + +def get_user_dn(user_upn: str): + username, domain = tuple(user_upn.split('@')) + domain_dn = get_domain_dn(domain) + return "cn={},cn=users,{}".format(username, domain_dn) diff --git a/vCert-6.1.1-20260401/lib/menu.py b/vCert-6.1.1-20260401/lib/menu.py new file mode 100644 index 0000000..cf7e988 --- /dev/null +++ b/vCert-6.1.1-20260401/lib/menu.py @@ -0,0 +1,438 @@ +# Copyright (c) 2024-2025 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import glob +import os +import yaml +import logging + +from lib.console import print_header, print_text, ColorKey, set_text_color, print_text_error +from lib.environment import Environment +from lib.exceptions import MenuExitException, OperationFailed +from lib.host_utils import get_config_file_path +from lib.input import MenuInput +from lib.operation import Operation +from lib.text_utils import translate_text +from lib.vmdir import get_identity_sources + +logger = logging.getLogger(__name__) + +class MenuItem(object): + """ + Class for holding menu item information + """ + def __init__(self, text, method, method_args=None, key=None, is_disabled=False, + is_hidden=False, is_default=False, use_label_as_key=False): + """ + Initialize MenuItem object + + :param text: label for the menu item when displayed as part of menu + :param method: the method to be called if the menu item is executed + :param method_args: arguments to be supplied to method + :param key: specify menu key explicitly. If this key not specified, + the key will be generated using a sequenced number + :param is_disabled: show menu as disabled menu item + :param is_default: use this menu item if the input return empty key + :param use_label_as_key: use the label as the key for default input + """ + self.text = text + self.method = method + self.method_args = method_args + self.key = key + self.is_disabled = is_disabled + self.is_hidden = is_hidden + self.is_default = is_default + self.use_label_as_key = use_label_as_key + + +class Menu(object): + """ + Class for representing a single menu dialog, providing the method to + define and show menu list, the handling of accepted inputs and the + execution of menu item + """ + def __init__(self): + self.title = '' + self.sub_title = '' + self.items = [] + self.input_text = None + self.run_once = False + + def set_menu_options(self, title, sub_title=None, run_once=False, input_text=None): + """ + Set the menu option: menu title and the input text + """ + self.title = title + self.sub_title = sub_title + self.input_text = input_text + self.run_once = run_once + + def add_menu_item(self, label, method=None, method_args=None, key=None, is_disabled=False, + is_hidden=False, is_default=False, use_label_as_key=False): + """ + Add menu item entry + + :param label: label for the menu item when displayed as part of menu + :param method: method object to be called if the menu item is executed + :param method_args: arguments to be supplied to method + :param key: specify menu key explicitly. If this key not specified, + the key will be auto-generated using sequence number + :param is_disabled: show menu as disabled menu item + :param is_hidden: if True, hidden the menu entry + :param is_default: use this menu item if the input return empty key + :param use_label_as_key: use the label as the key for default input + """ + # validate that the new menu item entry doesn't have conflict with the existing ones + if key is not None: + key = key.upper() + for item in self.items: + if key is not None and key == item.key: + raise ValueError("Duplicating menu item key: {}".format(key)) + if is_default and item.is_default: + raise ValueError('Duplicating default menu item') + if is_hidden and key is None: + raise ValueError('Hidden menu item requires explicit key value') + self.items.append(MenuItem(label, method, method_args, key, is_disabled, is_hidden, + is_default, use_label_as_key)) + + def get_all_menu_keys(self): + """ + Get all menu keys defined or generated for the current menu entries + + :returns all menu keys + """ + user_keys = [] + for item in self.items: + if item.key is not None: + user_keys.append(item.key) + keys = [str(i) for i in range(1, len(self.items) - len(user_keys) + 1)] + keys.extend(user_keys) + return keys + + def get_all_menu_keys_string(self) -> str: + """ + Get all menu keys as a single string + + The auto-generated keys will be simplified using range expression. + Example: When get_all_menu_keys() returns ['1', '2', '3', 'E'], + this method will return '1-3, E' + + :return meny keys in a single-line string + """ + keys = [] + for item in self.items: + if item.key is not None: + keys.append(item.key) + num_others = len(self.items) - len(keys) + if num_others == 1: + keys.insert(0, "1") + elif num_others > 1: + keys.insert(0, "{}-{}".format(1, num_others)) + return ", ".join(keys) + + def get_menu_item(self, key): + """ + Get menu item using key {key} + + :param key: key to be used to search the menu item + :return: MenuItem object + """ + key = key.upper() + item_key = 1 + for item in self.items: + if item.key is None: + if key == str(item_key): + return item + else: + item_key += 1 + elif item.key == key: + return item + return None + + def get_input(self): + """ + Get user input from console + + :return: key entered by user + """ + keys = self.get_all_menu_keys() + # get default key + default_key = None + seq = 0 + for item in self.items: + if not item.key: + seq += 1 + if item.is_default: + default_key = item.key if item.key else str(seq) + default_key_text = item.text if item.use_label_as_key else default_key + + Environment.get_environment().get_value('CURRENT_MENU')['__DEFAULT__'] = default_key + break + # compose default input text if required + input_text = self.input_text + if self.input_text is None: + if default_key is not None: + input_text = "Select an option [{key}]: ".format(key=default_key_text) + else: + input_text = 'Select an option: ' + + menu_input = MenuInput(translate_text(input_text), acceptable_inputs=keys, default_input=default_key) + return menu_input.get_input() + + def show_menu(self): + """ + Show the menu list + """ + if self.title: + print() + print_header(self.title) + if self.sub_title: + print_text(self.sub_title) + seq = 1 + for item in self.items: + if item.is_hidden: + continue + key = item.key + if not key: + key = str(seq) + seq += 1 + + if item.is_disabled: + set_text_color(ColorKey.YELLOW) + print_text("{:>2}. {}".format(key, translate_text(item.text))) + if item.is_disabled: + set_text_color(ColorKey.NORMAL) + + def run(self): + """ + Execute the menu: show the menu list, get user input, and execute + the selected menu item + """ + Environment.get_environment().set_value('CURRENT_MENU', dict()) + while True: + self.show_menu() + print() + try: + key = self.get_input() + item = self.get_menu_item(key) + except KeyboardInterrupt: + raise MenuExitException('KeyboardInterrupt exception') + logger.info('Running menu item: {}'.format(item)) + if item is None: + raise RuntimeError('Menu item not found') + elif item.is_disabled: + print() + print_text_error('Operation is not available') + return + elif item.method == Menu.run_navigation_exit: + raise MenuExitException('Exit requested') + elif item.method == Menu.run_navigation_return: + return + try: + if item.method_args is not None: + item.method(**item.method_args) + else: + item.method() + except OperationFailed as e: + print() + print_text_error('Operation failed: {}'.format(str(e))) + except KeyboardInterrupt: + raise MenuExitException('KeyboardInterrupt exception') + + if self.run_once: + return + + @staticmethod + def load_menu_item_from_file(config_file): + """ + Load menu item definition from yaml config file + + :param config_file: file to be loaded + :return: menu item type, label, and enable condition string + """ + config_file = get_config_file_path(config_file) + with open(config_file, 'r') as file: + item_config = yaml.safe_load(file) + label = item_config.get('label') + condition = item_config.get('condition') + if label is None: + label = item_config['title'] + return item_config['type'], label, condition + + @staticmethod + def load_menu_items_from_files(config_files): + """ + Load multiple menu item definitions from yaml config files, specified + using a file pattern (glob) + + :param config_files: file pattern to be loaded. + :return: list of a tuple of menu item type, label, and condition + """ + items = [] + env = Environment.get_environment() + config_files = config_files if os.path.isabs(config_files) \ + else "{}/{}".format(env.get_value('SCRIPT_DIR'), config_files) + for config in sorted(glob.glob(config_files)): + with open(config, 'r') as file: + item_config = yaml.safe_load(file) + logger.info('Configuration from {} is: {}'.format(config, item_config)) + label = item_config.get('label') + condition = item_config.get('condition') + if label is None: + label = item_config['title'] + items.append((item_config['type'], label, config, condition)) + return items + + @staticmethod + def load_menu_from_config(config_file): + """ + Load a full menu context from config file + + :param config_file: a yaml config file to be loaded + :return: Menu object created from the config file + """ + env = Environment.get_environment() + menu = Menu() + config_file = get_config_file_path(config_file) + with open(config_file, 'r') as file: + menu_config = yaml.safe_load(file) + title = menu_config['title'] + sub_title = menu_config.get('sub_title', '') + run_once = menu_config.get('run_once', False) + input_text = None + if menu_config.get('input'): + input_text = menu_config['input']['text'] + menu.set_menu_options(title, sub_title, run_once, input_text) + + for item_config in menu_config['items']: + item_type = item_config['type'] + item_label = item_config.get('label') + is_default = item_config.get('default') is True + condition = item_config.get('condition') + is_disabled = env.get_value(condition) is not True if condition else False + is_hidden = item_config.get('hidden') is True + use_label_as_key = item_config.get('use_label_as_key', False) + key = item_config.get('key') + if item_type == 'single': + submenu_config = item_config['config'] + submenu_type, submenu_label, submenu_condition = Menu.load_menu_item_from_file(submenu_config) + if item_label is None: + item_label = submenu_label + if not is_disabled: + is_disabled = env.get_value(submenu_condition) is not True if submenu_condition else False + handler = Menu.run_menu_from_config if submenu_type == 'menu' else Menu.run_operation_from_config + menu.add_menu_item(item_label, handler, {'config': submenu_config}, key=key, + is_disabled=is_disabled, is_hidden=is_hidden, is_default=is_default) + elif item_type == 'multiple': + items = Menu.load_menu_items_from_files(item_config['config']) + for submenu_type, submenu_label, submenu_config, submenu_condition in items: + is_disabled = env.get_value(submenu_condition) is not True if submenu_condition else False + handler = Menu.run_menu_from_config \ + if submenu_type == 'menu' else Menu.run_operation_from_config + menu.add_menu_item(submenu_label, handler, {'config': submenu_config}, + is_disabled=is_disabled) + elif item_type == 'navigation:exit': + menu.add_menu_item(item_label, Menu.run_navigation_exit, key=key, is_disabled=is_disabled, + is_hidden=is_hidden, is_default=is_default) + elif item_type == 'navigation:return': + menu.add_menu_item(item_label, Menu.run_navigation_return, key=key, is_disabled=is_disabled, + is_hidden=is_hidden, is_default=is_default, use_label_as_key=use_label_as_key) + return menu + + @staticmethod + def get_menu_label_and_condition_from_config(config): + """ + Obtain menu label and condition only from a menu config file + + :param config: config file to be loaded + :return: tuple of menu item label and condition + """ + config = get_config_file_path(config) + with open(config, 'r') as file: + menu_config = yaml.safe_load(file) + label = menu_config.get('label') + if label is None: + label = menu_config['title'] + + condition = menu_config.get('condition') + return label, condition + + @staticmethod + def run_menu_from_config(config): + """ + Method to be used by MenuItem that load another menu (submenu) + + :param config: config to be load in the submenu + """ + menu = Menu.load_menu_from_config(config) + menu.run() + + @staticmethod + def run_operation_from_config(config): + """ + Method to be used by MenuItem that execute operation + + :param config: config to be loaded to run the operation + """ + operation = Operation.load_operation_from_config(config) + try: + operation.run() + except OperationFailed as e: + print() + print_text_error("Operation failed: {}".format(str(e))) + + + @staticmethod + def run_navigation_exit(): + """ + Method to be used by MenuItem that cause application exit + """ + pass + + + @staticmethod + def run_navigation_return(): + """ + Method to be used by MenuItem that cause return to parent menu + """ + pass + + + @staticmethod + def set_menu_conditions(): + Menu.set_ldaps_condition() + Menu.set_machine_ssl_csr_condition() + Menu.set_backup_store_condition() + + @staticmethod + def set_ldaps_condition(): + env = Environment.get_environment() + identity_sources = get_identity_sources(use_machine_account=True) + value = True if identity_sources else False + env.set_value('HAS_LDAPS_IDENTITY_SOURCE', value) + + + @staticmethod + def set_machine_ssl_csr_condition(): + # XXX Issue 43: Import lib.vecs in the function to break circular + # dependency with lib.vecs which imports lib.menu. + from lib.vecs import get_certificate_aliases + + env = Environment.get_environment() + machine_ssl_aliases = get_certificate_aliases('MACHINE_SSL_CERT') + value = True if '__MACHINE_CSR' in machine_ssl_aliases else False + env.set_value('HAS_MACHINE_SSL_CSR', value) + + + @staticmethod + def set_backup_store_condition(): + # XXX Issue 43: Import lib.vecs in the function to break circular + # dependency with lib.vecs which imports lib.menu. + from lib.vecs import get_store_list + + env = Environment.get_environment() + backup_stores = ['BACKUP_STORE', 'BACKUP_STORE_H5C'] + vecs_stores = get_store_list() + value = any(store in vecs_stores for store in backup_stores) + env.set_value('HAS_BACKUP_STORE', value) diff --git a/vCert-6.1.1-20260401/lib/operation.py b/vCert-6.1.1-20260401/lib/operation.py new file mode 100644 index 0000000..ed9bf3c --- /dev/null +++ b/vCert-6.1.1-20260401/lib/operation.py @@ -0,0 +1,230 @@ +# Copyright (c) 2024-2025 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import glob +import importlib +import logging +import os +import sys +import yaml + +from lib.console import print_header, print_text_error +from lib.environment import Environment +from lib.exceptions import OperationFailed, CommandExecutionError +from lib.host_utils import get_config_file_path +from lib.input import MenuInput + +logger = logging.getLogger(__name__) + +class ValueSource(object): + """ + Class for providing operation arguments as part of config file + """ + def __init__(self, value): + self.value = value + + def get_value(self): + return self.value + + +class EnvironmentSource(object): + """ + Class for providing operation argument from environment variable + """ + def __init__(self, key): + self.key = key + + def get_value(self): + """ + Return value from environment + """ + env = Environment.get_environment() + return env.get_value(self.key) + + +class InputSource(object): + """ + Class for providing operation arguments via user input dialog + """ + def __init__(self, text, acceptable_inputs, default_input=None, allow_empty_input=True, case_insensitive=False, + masked=False): + """ + InputSource constructor. All arguments will be passed to MenuInput + """ + self.text = text + self.acceptable_inputs = acceptable_inputs + self.default_input = default_input + self.allow_empty_input = allow_empty_input + self.case_insensitive = case_insensitive + self.masked = masked + + def get_value(self): + """ + Return value obtained from MenuInput execution + """ + source_input = MenuInput(self.text, self.acceptable_inputs, self.default_input, self.allow_empty_input, + self.case_insensitive, self.masked) + return source_input.get_input() + + +class OperationArgument(object): + """ + Class for defining operation argument + """ + def __init__(self, name, source): + self.name = name + self.source = source + + +class Operation(object): + """ + Class for defining operation object + """ + def __init__(self, title, module_name, method_name, condition_key): + """ + Operation constructor method + + :param title: text to be displayed when the operation is executed + :param module_name: module to be load to find the method for this operation + :param method_name: method to be called for this operation + :param condition_key: key to be used to evaluate if the operation is disabled + """ + env = Environment.get_environment() + sys.path.append(env.get_value('SCRIPT_DIR')) + self.title = title + self.module = importlib.import_module(module_name) + obj = self.module + for attr_name in method_name.split('.'): + obj = getattr(obj, attr_name) + self.method = obj + self.arguments = [] + self.condition_key = condition_key + + def add_argument(self, name, source): + self.arguments.append(OperationArgument(name, source)) + + def get_argument_values(self): + """ + Get operation arguments as dict. This method will also reset CURRENT_MENU + environment with this mapping + """ + args = dict() + env = Environment.get_environment() + env.set_value('CURRENT_MENU', args) + for arg in self.arguments: + value = arg.source.get_value() + args[arg.name] = value + return args + + def is_disabled(self): + """ + Check if the operation is disabled + """ + if self.condition_key: + return Environment.get_environment().get_value(self.condition_key) is not True + else: + return False + + def run(self): + """ + Execute the operation + """ + if self.is_disabled(): + print_text_error('Operation is disabled!') + print() + return + + if self.title: + print_header(self.title) + try: + return self.method(**self.get_argument_values()) + except CommandExecutionError as e: + raise OperationFailed(str(e)) + + + class OperationGroup(object): + """ + Class for defining operation that aggregate other operations + """ + def __init__(self, title): + self.title = title + self.operations = [] + + def add_operation(self, op): + self.operations.append(op) + + def run(self): + for op in self.operations: + op.run() + + @staticmethod + def load_operation_from_config_obj(config): + """ + Load operation from loaded config object + + :param config: config file to be loaded + """ + entry_point = config['entry_point'] + operation = Operation(config.get('title'), entry_point['module'], entry_point['method'], + config.get('condition')) + arguments = config.get('arguments') + if arguments: + for arg in config.get('arguments'): + arg_name = arg['name'] + if arg.get('value') is not None: + operation.add_argument(arg_name, ValueSource(arg['value'])) + continue + + source_config = arg['source'] + if source_config['type'] == 'input': + operation.add_argument( + arg_name, + InputSource(text=source_config['input_text'], + acceptable_inputs=source_config.get('acceptable_inputs'), + default_input=source_config.get('default_input'), + case_insensitive=(source_config.get('case_insensitive') is True), + masked=(source_config.get('masked') is True))) + elif source_config['type'] == 'environment': + operation.add_argument(arg_name, EnvironmentSource(source_config['key'])) + return operation + + @staticmethod + def load_operation_from_config(config_file): + """ + Load operation from a yaml config file + + :param config_file: config file to be loaded + :return: Return Operation or OperationGroup object + """ + env = Environment.get_environment() + config_file = get_config_file_path(config_file) + logger.info("Loading operation from config file {}".format(config_file)) + with open(config_file, 'r') as file: + config = yaml.safe_load(file) + if config['type'] == "operation": + return Operation.load_operation_from_config_obj(config) + + op_group = Operation.OperationGroup(config.get('title')) + for operation in config['operations']: + if operation['type'] == 'single': + if operation.get('config'): + op = Operation.load_operation_from_config(operation['config']) + else: + op = Operation.load_operation_from_config_obj(operation) + op_group.add_operation(op) + continue + # logger.info("Parsing multiple operations in {}".format(glob.glob(operation['config']))) + + # Only use the SCRIPT_DIR environment setting if the config + # path isn't absolute. + opconfig = operation['config'] + if os.path.isabs(opconfig): + globPath = opconfig + else: + globPath = os.path.join(env.get_value('SCRIPT_DIR'), opconfig) + + for config_file in sorted(glob.glob(globPath)): + op = Operation.load_operation_from_config(config_file) + op_group.add_operation(op) + return op_group diff --git a/vCert-6.1.1-20260401/lib/services.py b/vCert-6.1.1-20260401/lib/services.py new file mode 100644 index 0000000..3479bbf --- /dev/null +++ b/vCert-6.1.1-20260401/lib/services.py @@ -0,0 +1,119 @@ +# Copyright (c) 2024 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import logging +import sys + +from lib.command_runner import CommandRunner +from lib.console import print_text_warning +from lib.host_utils import get_file_contents +from lib.text_utils import TextFilter +from lib.vmdir import get_vmdir_state + +SERVICE_CONTROL_CLI = '/usr/bin/service-control' +VMON_CLI = '/usr/sbin/vmon-cli' + +logger = logging.getLogger(__name__) + +def get_vmon_service_profile(): + """ + Get vmon service profile + """ + vmon_service_profile = get_file_contents('/storage/vmware-vmon/defaultStartProfile') + return ['--vmon-profile', 'HAActive'] if 'HACore' in vmon_service_profile else ['--all'] + + +def list_vmware_services(): + """ + Get a list of services present in the VC + :param: + :return: List of services + """ + output = CommandRunner(SERVICE_CONTROL_CLI, '--list-services').run_and_get_output().strip() + services = TextFilter(output).cut(fields=[0]).get_lines() + return services + + +def start_vmware_services(service=None): + """ + Start VMware services + :param service: to be started. If it's None, all service will be restarted + :return: True if service(s) are started successfully. Otherwise, False + """ + if service is None: + service_profile = get_vmon_service_profile() + ret_code, _, stderr = CommandRunner(SERVICE_CONTROL_CLI, '--start', *service_profile).run() + else: + ret_code, _, stderr = CommandRunner(SERVICE_CONTROL_CLI, '--start', service).run() + + if ret_code != 0: + logger.error("Failed to start VMware services: {}".format(stderr)) + return False + return True + + +def stop_vmware_services(service=None): + """ + Stop VMware service + :param service: to be stopped. If it's None, all service will be stopped + :return: True if service(s) are started successfully. Otherwise, False + """ + if service is None: + service_profile = get_vmon_service_profile() + ret_code, _, stderr = CommandRunner(SERVICE_CONTROL_CLI, '--stop', *service_profile).run() + else: + ret_code, _, stderr = CommandRunner(SERVICE_CONTROL_CLI, '--stop', service).run() + + if ret_code != 0: + logger.error("Failed to stop VMware services: {}".format(stderr)) + return False + return True + + +def check_services_status(): + services = [['envoy', True, True], + ['rhttpproxy', True, True], + ['vmafdd', False, True], + ['vmdird', False, True], + ['vmware-vpostgres', True, False]] + for service_info in services: + service_name = service_info[0] + managed_by_vmon = service_info[1] + exit_if_stopped = service_info[2] + if not is_service_running(service_name, managed_by_vmon): + if exit_if_stopped: + print_text_warning('The {} service is not running, the script cannot continue. Exiting...'.format(service_name)) + sys.exit(1) + else: + print_text_warning('\n-------------------------!!! Warning !!!-------------------------') + print_text_warning('The {} service is not running, which may impact certain script operations.'.format(service_name)) + print_text_warning('For best results, please start the {} service.'.format(service_name)) + elif service_name == 'vmdird': + vmdir_state = get_vmdir_state() + logger.info('The VMware Directory service state is: {}'.format(vmdir_state)) + if vmdir_state not in ['Normal', 'Standalone']: + print_text_warning('The VMware Directory service is in the following state: {}. The script cannot continue. Exiting...'.format(vmdir_state)) + sys.exit(1) + return + + +def is_service_running(service, managed_by_vmon=True): + if managed_by_vmon: + # determining status of services managed by vMon is quicker using vmon-cli as oppsed to service-control + service_output = CommandRunner(VMON_CLI, '-s', '{}'.format(service)).run_and_get_output() + run_state = TextFilter(service_output).start_with('RunState').cut(':', [1]).get_text().strip() + logger.info('Service {} running state: {}'.format(service, run_state)) + if run_state == 'STARTED': + return True + else: + return False + else: + # determining status of services managed by systemd is quicker using systemctl as oppsed to service-control + service_output = CommandRunner('systemctl', 'status', '{}'.format(service)).run_and_get_output() + run_state = TextFilter(service_output).contain('Active:').cut(':', [1]).cut(' ', [1]).get_text() + logger.info('Service {} running state: {}'.format(service, run_state)) + if run_state == 'active': + return True + else: + return False \ No newline at end of file diff --git a/vCert-6.1.1-20260401/lib/text_utils.py b/vCert-6.1.1-20260401/lib/text_utils.py new file mode 100644 index 0000000..97ed431 --- /dev/null +++ b/vCert-6.1.1-20260401/lib/text_utils.py @@ -0,0 +1,274 @@ +# Copyright (c) 2024 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import re + +from lib.environment import Environment + + +class TextFilter(object): + """ + A utility class for filtering multi-line text. The filtering methods in this + class will return self to allow method chaining + + Example of usage: + result = TextFilter(text).contain(filter).head(1).remove(unwanted_chars).get_text() + """ + + def __init__(self, text): + self.lines = text.splitlines() + self.before_lines = 0 + self.after_lines = 0 + self.invert_match = False + + def get_text(self): + """ + Return the result as a string, joined by '\n' + """ + return "\n".join(self.lines) + + def get_lines(self): + """ + Return the current filtering result as a string list + """ + return self.lines + + def set_options(self, before_lines=0, after_lines=0, invert_match=False): + """ + Set the matching option + + :param before_lines: the result will also contain the before_lines lines + before the matched line + :param after_lines: the result will also contain the after_lines lines + after the matched lines + :param invert_match: If True, the match operations will return lines that + that don't match to the search pattern + """ + self.before_lines = before_lines + self.after_lines = after_lines + self.invert_match = invert_match + return self + + def reset_options(self): + """ + Reset the matching option + """ + self.before_lines = self.after_lines = 0 + self.invert_match = False + return self + + def mod_match_result(self, match_result): + return not match_result if self.invert_match else match_result + + def get_lines_with_extended_indices(self, indices): + """ + Extend the matching lines indices by applying before_lines and after_lines + matching options + + :param indices: indices to extend + :return: extended indices + """ + if self.before_lines > 0 or self.after_lines > 0: + new_indices = [] + for index in indices: + if self.before_lines > 0: + begin_index = index - self.before_lines + if begin_index < 0: + begin_index = 0 + new_indices.extend([idx for idx in range(begin_index, index)]) + if self.after_lines > 0: + end_index = index + self.after_lines + 1 + if end_index > len(self.lines): + end_index = len(self.lines) + new_indices.extend([idx for idx in range(index + 1, end_index)]) + indices.extend(new_indices) + indices = sorted(set(indices)) + return [line for idx, line in enumerate(self.lines) if idx in indices] + + def match(self, pattern): + """ + Filter lines using regular expression match + + :param pattern: regular expression pattern for matching the lines + """ + indices = [idx for idx, line in enumerate(self.lines) + if self.mod_match_result(re.match(pattern, line))] + self.lines = self.get_lines_with_extended_indices(indices) + return self + + def contain(self, keyword): + """ + Filter the lines using simple keywords + + :param keyword: keyword for matching the lines + """ + indices = [idx for idx, line in enumerate(self.lines) + if self.mod_match_result(keyword in line)] + self.lines = self.get_lines_with_extended_indices(indices) + return self + + def start_with(self, text): + """ + Filter the lines that started with {text} + + :param text: text for filtering + """ + indices = [idx for idx, line in enumerate(self.lines) + if self.mod_match_result(line.find(text) == 0)] + self.lines = self.get_lines_with_extended_indices(indices) + return self + + def head(self, count=1): + """ + Include only the first {count} lines to be included in the result + + :param count: total of lines to be included in the result. If the value is + a negative value between -1 and -[number of lines], it will include all + lines except of the last {count} lines at the end + """ + if count < 0: + count += len(self.lines) + if count < 0: + count = 0 + self.lines = [line for i, line in enumerate(self.lines) if i < count] + return self + + def tail(self, count=1): + """ + Includes only the last {count} lines + + :param count: total of lines to be included. If the value is a negative + value between -1 and -[#lines], it will include all lines except the + first {count} lines at the beginning + :return: + """ + length = len(self.lines) + if count < 0: + count += length + self.lines = [line for i, line in enumerate(self.lines) if i >= length - count] + return self + + def match_block(self, pattern_begin, pattern_end, concatenate=False): + """ + Filter the lines using begin and end pattern + + :param pattern_begin: the pattern for starting the matching block + :param pattern_end: the pattern for stopping the matching block + :param concatenate: if True, the matching block will be concatenated + as a single string joined using `\n' character + """ + lines = [] + block = [] + matching = False + for line in self.lines: + if not matching and re.match(pattern_begin, line): + matching = True + block = [] + if matching: + block.append(line) + if matching and re.match(pattern_end, line): + matching = False + if concatenate: + lines.append('\n'.join(block)) + else: + lines.extend(block) + self.lines = lines + return self + + def apply(self, method): + """ + Apply method on all lines. The lines will be replaced using the values returned + by the method + + :param method: method to be applied + """ + self.lines = [method(line) for line in self.lines] + return self + + def replace(self, keyword1, keyword2): + """ + Update the lines by replacing keywords + + :param keyword1: keyword to be replaced + :param keyword2: keyword to be used for replacing + """ + self.lines = [line.replace(keyword1, keyword2) for line in self.lines] + return self + + def remove(self, keyword): + """ + Update all lines by removing {keyword} + + :param keyword: text to be removed + """ + return self.replace(keyword, '') + + def remove_white_spaces(self): + """ + Remove white spaces in all lines + """ + return self.apply(remove_white_spaces_method) + + def cut(self, delimiter=' ', fields=None, new_delimiter=' ', include_empty=False): + """ + Split the line using delimiter and include only the required fields. + This method tries to mimics cut(1) command + + :param delimiter: The delimiter to be used for split the line + :param fields: list of indices to be included (0 based index) + :param new_delimiter: If specified, the result will be concenated back + using the new_delimiter + :param include_empty: If True, the result may also contain empty lines + when the request fields are not available (default: False) + """ + lines = [] + if fields is None: + fields = [] + for line in self.lines: + result = [] + words = line.split(delimiter) + for idx in fields: + if idx == -1: + idx = len(words) - 1 + if idx < len(words): + result.append(words[idx]) + if result or include_empty: + lines.append(new_delimiter.join(result)) + self.lines = lines + return self + + def get_count(self): + """ + Get the count of lines in the current result + """ + return len(self.lines) + + def dump(self): + """ + Dump the current contents. This can be use for debugging purpose + """ + print('Contents:', '\n'.join(self.lines), sep='\n') + return self + + +def remove_white_spaces_method(text): + """ + Method for removing white spaces to be specified to TextUtil.apply + """ + return re.sub(r'\s+', '', text) + + +def translate_text(*text, sep=''): + """ + Reformat text using mapping in the environment variables + + :param text: String parameters + :param sep: Separator if the text contains multiple string parameters + :return: reformatted text + """ + text = sep.join(text) + if text and '{' in text: + env_map = Environment.get_environment().get_map() + text = text.format_map(env_map) + return text diff --git a/vCert-6.1.1-20260401/lib/vcdb.py b/vCert-6.1.1-20260401/lib/vcdb.py new file mode 100644 index 0000000..250a5a1 --- /dev/null +++ b/vCert-6.1.1-20260401/lib/vcdb.py @@ -0,0 +1,155 @@ +# Copyright (c) 2024 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +from lib.command_runner import CommandRunner +from lib.host_utils import get_vc_version, VcVersion +from lib.text_utils import TextFilter + +PSQL_CLI = '/usr/bin/psql' + +def get_extension_thumbprints(extensions): + """ + Get extension thumbprints from VCDB + :param extensions: list of extensions to fetch + :return: dictionary of tuple (extension name, thumbprint, cert PEM) + """ + + query = "SELECT ext_id, thumbprint FROM vpx_ext WHERE ext_id IN ('{}') ORDER BY ext_id" \ + .format('\', \''.join(extensions)) + output = CommandRunner(PSQL_CLI, '-d', 'VCDB', '-U', 'postgres', + '-c', query, '-t').run_and_get_output() + extensions = TextFilter(output).contain('|').cut(delimiter='|', fields=[0]).remove_white_spaces().get_lines() + thumbprints = TextFilter(output).contain('|').cut(delimiter='|', fields=[1]).remove_white_spaces().get_lines() + result = dict() + for index, extension in enumerate(extensions): + if get_vc_version() >= VcVersion.V9: + query = "SELECT certificate FROM vpx_ext WHERE ext_id='{}'".format(extension) + output = CommandRunner(PSQL_CLI, '-d', 'VCDB', '-U', 'postgres', + '-c', query, '-t', '-A').run_and_get_output() + pem_cert = TextFilter(output).get_text().strip() + else: + pem_cert = None + result[extension] = (thumbprints[index], pem_cert) + return result + + +def get_extension_thumbprint(extension): + """ + Get thumbprint for a specific extension + :param extension: extension name + :return: extension thumbprint + """ + query = "SELECT thumbprint FROM vpx_ext WHERE ext_id = '{}'".format(extension) + output = CommandRunner(PSQL_CLI, '-d', 'VCDB', '-U', 'postgres', + '-c', query, '-t').run_and_get_output() + thumbprint = TextFilter(output).head(1).get_text().strip() + return thumbprint + + +def update_extension_thumbprint(extension, thumbprint, cert_pem=None): + if cert_pem is None: + query = "UPDATE vpx_ext SET thumbprint = '{}' WHERE ext_id = '{}'".format(thumbprint, extension) + else: + one_line_pem = cert_pem.strip().replace('\n', '\\n') + query = "UPDATE vpx_ext SET thumbprint = '{}', certificate = E'{}' WHERE ext_id = '{}'".format(thumbprint, one_line_pem, extension) + ret_code, _, _ = CommandRunner(PSQL_CLI, '-d', 'VCDB', '-U', 'postgres', '-c', + query, '-t').run() + return ret_code == 0 + + +def get_certificate_management_mode(): + """ + Get certificate management node + """ + query = 'SELECT value FROM vpx_parameter WHERE name=\'vpxd.certmgmt.mode\'' + output = CommandRunner(PSQL_CLI, '-d', 'VCDB', '-U', 'postgres', + '-c', query, '-t').run_and_get_output() + return TextFilter(output).head(1).get_text().strip() + + +def get_number_of_hosts(): + """ + Get the number of hosts + """ + query = 'SELECT COUNT(id) FROM vpx_host WHERE enabled=1' + output = CommandRunner(PSQL_CLI, '-d', 'VCDB', '-U', 'postgres', + '-c', query, '-t').run_and_get_output() + return TextFilter(output).head(1).get_text().strip() + + +def get_hosts(): + """ + Get the hosts + """ + query = 'SELECT id, dns_name, ip_address FROM vpx_host WHERE enabled=1 ORDER BY dns_name ASC, ip_address ASC' + + output = CommandRunner(PSQL_CLI, '-d', 'VCDB', '-U', 'postgres', '-c', query, '-t').run_and_get_output() + return TextFilter(output).get_text().strip().splitlines() + + +def get_host_and_cluster_id(): + """ + Get the hosts id and cluster id it belongs too + """ + query = '''SELECT ent.id, ent.parent_id FROM vpx_entity as ent LEFT JOIN vpx_object_type AS obj ON + ent.type_id = obj.id WHERE obj.name = 'HOST' ''' + + output = CommandRunner(PSQL_CLI, '-d', 'VCDB', '-U', 'postgres', + '-c', query, '-t').run_and_get_output() + return TextFilter(output).get_text().strip().splitlines() + + +def get_clusters(): + query = '''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' ''' + + output = CommandRunner(PSQL_CLI, '-d', 'VCDB', '-U', 'postgres', + '-c', query, '-t').run_and_get_output() + return TextFilter(output).get_text().strip().splitlines() + + +def get_ssl_thumbprint_from_vcdb(access_string): + """ + Get the expected_ssl_thumbprint and host_ssl_thumbprint + """ + query = 'SELECT expected_ssl_thumbprint,host_ssl_thumbprint FROM vpx_host where ' \ + 'dns_name = \'{0}\' or ip_address = \'{0}\' '.format(access_string) + + output = CommandRunner(PSQL_CLI, '-d', 'VCDB', '-U', 'postgres', + '-c', query, '-t').run_and_get_output() + return TextFilter(output).get_text().strip().splitlines() + + +def get_vmca_configuration_from_vcdb(): + """ + Get the vpxd.certmgmt.mode and vpxd.certmgmt.cn.* values from the database + """ + query = "SELECT name,value FROM vpx_parameter WHERE name='vpxd.certmgmt.mode' OR name LIKE 'vpxd.certmgmt.certs.cn.%' ORDER BY name" + + output = CommandRunner(PSQL_CLI, '-d', 'VCDB', '-U', 'postgres', + '-c', query, '-t').run_and_get_output() + return TextFilter(output).get_text().strip().splitlines() + +def scheduled_task_table_exists(): + """ + Determine if the vpx_sched_persistent_user_token table exists + """ + query = "SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema='vc' AND table_name='vpx_sched_persistent_user_token')" + + output = CommandRunner(PSQL_CLI, '-d', 'VCDB', '-U', 'postgres', + '-c', query, '-t').run_and_get_output() + + return TextFilter(output).get_text().strip().lower() == 't' + + +def get_number_scheduled_tasks(): + """ + Get number of user-defined Scheduled Tasks in vCenter + """ + query = "SELECT COUNT(usertoken_id) FROM vpx_sched_persistent_user_token" + + output = CommandRunner(PSQL_CLI, '-d', 'VCDB', '-U', 'postgres', + '-c', query, '-t').run_and_get_output() + + return TextFilter(output).get_text().strip() \ No newline at end of file diff --git a/vCert-6.1.1-20260401/lib/vecs.py b/vCert-6.1.1-20260401/lib/vecs.py new file mode 100644 index 0000000..daf61a0 --- /dev/null +++ b/vCert-6.1.1-20260401/lib/vecs.py @@ -0,0 +1,305 @@ +# Copyright (c) 2024-2025 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import logging +import json +import re + +from lib.console import ( + print_task, print_task_status, print_header, ColorKey, print_text +) +from lib.environment import Environment +from lib.command_runner import CommandRunner +from lib.host_utils import VcVersion, get_vc_version, is_file_exists +from lib.menu import MenuInput +from lib.text_utils import TextFilter +from lib.exceptions import (OperationFailed, CommandExecutionError) + + + +VECS_CLI = '/usr/lib/vmware-vmafd/bin/vecs-cli' +logger = logging.getLogger(__name__) + + +def get_all_certificates(store): + """ + Get certificates and aliases from 'vecs-cli entry list' output + + :param store: Certificate store + :return: tuple of certificate list and alias list + """ + args = [VECS_CLI, 'entry', 'list', '--store', store] + client = CommandRunner(*args, expected_return_code=0) + return_code, stdout, stderr = client.run() + certs_pem = TextFilter(stdout).match_block( + r'.*\s+-----BEGIN CERTIFICATE-----|.*\s+-----BEGIN X509 CRL-----', + '-----END CERTIFICATE-----|-----END X509 CRL-----', concatenate=True)\ + .remove('Certificate :\t').get_lines() + aliases = TextFilter(stdout).start_with('Alias').remove_white_spaces().cut(':', [1]).get_lines() + return certs_pem, aliases + + +def get_all_ca_certificates(): + """ + Get all trusted root CA certificates from VECS + """ + return get_all_certificates('TRUSTED_ROOTS') + + +def get_certificate_aliases(store): + """ + Get aliases from VECS via 'vecs-cli entry list' command output + + :param store: Certificate store + :return: list of aliases + """ + args = [VECS_CLI, 'entry', 'list', '--store', store] + client = CommandRunner(*args, expected_return_code=0) + return_code, stdout, stderr = client.run() + return TextFilter(stdout).start_with('Alias').remove_white_spaces().cut(':', [1]).get_lines() + + +def get_store_list(): + """ + Get store list in VECS via 'vecs-cli store list' command output + :return: list of certificate stores in VECS + """ + args = [VECS_CLI, 'store', 'list'] + client = CommandRunner(*args, expected_return_code=0) + return_code, stdout, stderr = client.run() + return stdout.splitlines() + + +def get_expected_stores_to_check(): + storeDB = { + # Store Name Version Constraint (None means all) + # ---------- ----------------------------------- + 'MACHINE_SSL_CERT': None, + 'TRUSTED_ROOTS': None, + 'TRUSTED_ROOT_CRLS': None, + 'machine': None, + 'vsphere-webclient': None, + 'vpxd': None, + 'vpxd-extension': None, + 'SMS': None, + 'APPLMGMT_PASSWORD': None, + 'data-encipherment': None, + 'hvc': None, + 'wcp': [VcVersion.V7, VcVersion.V8], + 'wcpsvc': [VcVersion.V9], + } + + vc_version = get_vc_version() + + stores = [] + for store, constraint in storeDB.items(): + if not constraint or vc_version in constraint: + stores.append(store) + + return stores + + +def get_missing_stores(): + current_stores = get_store_list() + expected_stores = get_expected_stores_to_check() + missing_stores = [] + for store in expected_stores: + if store not in current_stores: + missing_stores.append(store) + + return missing_stores + + +def get_certificate(store, alias): + """ + Get a specific certificate from VECS via 'vecs-cli entry getcert' command output + + :param store: Certificate store in VECS + :param alias: Certificate alias + :return: Certificate in PEM format + """ + args = [VECS_CLI, 'entry', 'getcert', '--store', store, '--alias', alias] + client = CommandRunner(*args, expected_return_code=0) + return_code, stdout, stderr = client.run() + return stdout + + +def get_key(store, alias): + """ + Get a specific key entry from VECS via 'vecs-cli entry getkey' command output + + :param store: Certificate store in VECS + :param alias: certificate alias + :return: key in PEM format + """ + args = [VECS_CLI, 'entry', 'getkey', '--store', store, '--alias', alias] + command = CommandRunner(*args, expected_return_code=0) + return_code, stdout, stderr = command.run() + return stdout + + +def add_entry(store, alias, cert_file, key_file=None): + """ + Add new entry into VECS + + :param store: the store name + :param alias: alias for the new entry + :param cert_file: certificate file (PEM) + :param key_file: key file (PEM) + """ + args = [VECS_CLI, 'entry', 'create', '--store', store, '--alias', alias, '--cert', cert_file] + if key_file: + args.extend(['--key', key_file]) + CommandRunner(*args, expected_return_code=0).run() + + +def delete_entry(store, alias): + """ + Delete certificate/key entry from VECS + :param store: store name + :param alias: certificate/key alias + """ + args = [VECS_CLI, 'entry', 'delete', '--store', store, '--alias', alias, '-y'] + client = CommandRunner(*args, expected_return_code=0).run() + + +def get_current_vecs_store_permissions(): + current_vecs_permissions = dict() + stores = get_expected_stores_to_check() + for store in stores: + owner, read_users, write_users = get_vecs_store_permission(store) + current_vecs_permissions[store] = {'owner' : owner, + 'read' : read_users, + 'write' : write_users} + + return current_vecs_permissions + + +def get_template_vecs_store_permissions(template_file): + desired_vecs_permissions = dict() + if is_file_exists(template_file): + try: + template_file_handler = open(template_file) + template_data = json.load(template_file_handler) + template_file_handler.close() + except Exception as e: + error_message = 'Unable to open template file: {}'.format(str(e)) + logger.error(error_message) + raise OperationFailed(error_message) + + for store, permissions in template_data.items(): + desired_vecs_permissions[store] = dict() + desired_vecs_permissions[store]['owner'] = permissions['owner'] + desired_vecs_permissions[store]['read'] = permissions['read'] + desired_vecs_permissions[store]['write'] = permissions['write'] + + return desired_vecs_permissions + + +def get_vecs_store_permission(store): + """ + Get VECS store permissions + + :param store: VECS store + :return: tuple (owner, list of users with read permission, list of users with write permissions) + """ + args = [VECS_CLI, 'store', 'get-permissions', '--name', store] + client = CommandRunner(*args, expected_return_code=0) + _, stdout, _ = client.run() + owner = TextFilter(stdout).start_with('OWNER').cut(':', [1]).get_text().strip() + read_users = TextFilter(stdout).match('.*read$').cut('\t', [0]).get_lines() + write_users = TextFilter(stdout).match('.*write$').cut('\t', [0]).get_lines() + return owner, read_users, write_users + + +def force_refresh(): + """ + Force VECS refresh from VMDir + """ + CommandRunner(VECS_CLI, 'force-refresh', expected_return_code=0).run() + + +def grant_vecs_permission(store, user, perm): + args = [VECS_CLI, 'store', 'permission', '--name', store, '--user', user, '--grant', perm] + try: + CommandRunner(*args, expected_return_code=0).run() + except CommandExecutionError: + error_message = 'Unable to assign {} permission to user {} on store {}'.format(perm, user, store) + logger.error(error_message) + raise OperationFailed(error_message) + + +def create_store(store): + """ + Create store in VECS via 'vecs-cli store create' command + """ + args = [VECS_CLI, 'store', 'create', '--name', store] + try: + CommandRunner(*args, expected_return_code=0).run() + except CommandExecutionError: + error_message = "Unable to create VECS store {}".format(store) + logger.error(error_message) + raise OperationFailed(error_message) + + +def delete_store(store): + """ + Create store in VECS via 'vecs-cli store create' command + """ + args = [VECS_CLI, 'store', 'delete', '--name', store, '-y'] + try: + CommandRunner(*args, expected_return_code=0).run() + except CommandExecutionError: + error_message = "Unable to delete VECS store {}".format(store) + logger.error(error_message) + raise OperationFailed(error_message) + + +def create_vecs_store_template(template_file): + print_header('Create VECS Store Template') + print_task('Creating template file') + template = get_current_vecs_store_permissions() + try: + with open(template_file, 'w') as outfile: + json.dump(template, outfile) + print_task_status('OK') + except Exception as e: + error_message = 'Unable to write template file: {}'.format(str(e)) + logger.error(error_message) + raise OperationFailed(error_message) + + +def manage_missing_vecs_stores(missing_stores): + prompt = MenuInput('Some VECS stores are missing, recreate them? [N]: ', acceptable_inputs=['Y', 'N'], default_input='N') + print() + if prompt.get_input() == 'Y': + print_header('Recreate VECS Store') + for store in missing_stores: + print_task(store) + create_store(store) + print_task_status('OK') + + +def manage_missing_vecs_permissions(missing_permissions): + prompt = MenuInput('Some VECS stores are missing expected permissions, reassign them? [N]: ', acceptable_inputs=['Y', 'N'], default_input='N') + print() + if prompt.get_input() == 'Y': + print_header('Reassign VECS Store Permissions') + if missing_permissions['read']: + print_text('Reassigning READ permissions:') + for store, missing_read_users in missing_permissions['read'].items(): + print_text(' Store {}:'.format(store)) + for user in missing_read_users: + print_task(' {}'.format(user)) + grant_vecs_permission(store, user, 'read') + print_task_status('OK') + + if missing_permissions['write']: + print_text('Reassigning WRITE permissions:') + for store, missing_write_users in missing_permissions['write'].items(): + print_text(' Store {}:'.format(store)) + for user in missing_write_users: + print_task(' {}'.format(user)) + grant_vecs_permission(store, user, 'write') + print_task_status('OK') diff --git a/vCert-6.1.1-20260401/lib/vmdir.py b/vCert-6.1.1-20260401/lib/vmdir.py new file mode 100644 index 0000000..0e6bbdd --- /dev/null +++ b/vCert-6.1.1-20260401/lib/vmdir.py @@ -0,0 +1,607 @@ +# Copyright (c) 2024-2025 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import ldap3 as ldap +import logging +import re +from lib import certificate_utils as certutil +from lib.environment import Environment +from lib.certificate_utils import build_pem_certificate +from lib.command_runner import CommandRunner +from lib.console import print_task, print_text_error +from lib.host_utils import VcVersion, get_hostname, get_vc_version +from lib.ldap_utils import Ldap, LdapException, get_user_dn +from lib.text_utils import TextFilter +from lib.console import (print_task_status) +from lib.exceptions import OperationFailed, CommandExecutionError + + +DIR_CLI = '/usr/lib/vmware-vmafd/bin/dir-cli' +VDCADMINTOOL = '/usr/lib/vmware-vmdir/bin/vdcadmintool' +cache_ca_keyids = None +cache_ca_certificate_map = dict() + +logger = logging.getLogger(__name__) + + +def get_ldap_connection(use_machine_account=False): + env = Environment.get_environment() + if use_machine_account: + user_dn = env.get_value('VMDIR_MACHINE_ACCOUNT_DN') + user_password = env.get_value('VMDIR_MACHINE_ACCOUNT_PASSWORD') + else: + sso_username = env.get_value('SSO_USERNAME') + user_password = env.get_value('SSO_PASSWORD') + user_dn = get_user_dn(sso_username) + hostname = get_hostname() + + return Ldap.open_ldap_connection(node=hostname, user_dn=user_dn, password=user_password) + + +def close_ldap_connection(ldap_connection): + Ldap.close_ldap_connection(ldap_connection) + + +def perform_ldap_search(search_base, search_filter, search_attributes, search_scope=ldap.SUBTREE, + use_machine_account=False, throw_exception=False): + """ + Utility method to perform LDAP search + + :param search_base: LDAP search base DN + :param search_filter: LDAP search filter + :param search_attributes: LDAP search attribute + :param search_scope: LDAP search scope (default: ldap.SUBTREE) + :param use_machine_account: Flag to use VMDir machine account instead of SSO user account + :param throw_exception: When LDAP search fails, throw exception instead of empty result + :return: list of matching object, as dict with keys from search_attributes paramerter + """ + results = [] + connection = None + try: + connection = get_ldap_connection(use_machine_account) + if not Ldap.ldap_search(connection, search_base, search_filter, search_scope, search_attributes): + logger.error("Unable to perform LDAP search base_dn={}, filter={}" \ + .format(search_base, search_filter)) + if throw_exception: + raise LdapException(connection.result['result'], connection.result['description']) + else: + return results + if connection.result: + for entry in connection.response: + attributes = entry['attributes'] + if 'dn' in search_attributes and not attributes['dn']: + attributes['dn'] = entry['dn'].replace(', cn=', ',cn=') + results.append(attributes) + finally: + if connection: + close_ldap_connection(connection) + + return results + + +def perform_ldap_add(service_dn, object_class, attributes, use_machine_account=False): + """ + A wrapper for LDAP modify operation + """ + connection = None + try: + connection = get_ldap_connection(use_machine_account) + if not Ldap.ldap_add(connection, service_dn, object_class, attributes): + logger.error("Unable to add LDAP entry dn={}, attributes={}".format(service_dn, str(attributes))) + raise LdapException(connection.result['result'], connection.result['description']) + finally: + if connection: + close_ldap_connection(connection) + + +def perform_ldap_modify(service_dn, attribute, value, operation=ldap.MODIFY_REPLACE, + use_machine_account=False): + """ + A wrapper for LDAP modify operation + """ + connection = None + try: + connection = get_ldap_connection(use_machine_account) + if not Ldap.ldap_modify(connection, service_dn, attribute, operation, value): + logger.error("Unable to modify LDAP entry dn={}, attribute={}".format(service_dn, attribute)) + raise LdapException(connection.result['result'], connection.result['description']) + finally: + if connection: + close_ldap_connection(connection) + + +def perform_ldap_delete(service_dn, use_machine_account=False): + """ + A wrapper for LDAP delete operation + """ + connection = None + try: + connection = get_ldap_connection(use_machine_account) + if not Ldap.ldap_delete(connection, service_dn): + logger.error("Unable to delete LDAP entry dn={}".format(service_dn)) + raise LdapException(connection.result['result'], connection.result['description']) + finally: + if connection: + close_ldap_connection(connection) + + +def get_solution_users(): + """ + Get solution users list from VMDir via LDAP + + :return: list of solution users + """ + env = Environment.get_environment() + machine_id = env.get_value('MACHINE_ID') + domain_dn = env.get_value('SSO_DOMAIN_DN') + + postfix = "-{}".format(machine_id) + search_base = "cn=ServicePrincipals,{}".format(domain_dn) + search_filter = "(&(objectClass=vmwServicePrincipal)(cn=*{}))".format(postfix) + search_attributes = ['cn'] + results = perform_ldap_search(search_base, search_filter, search_attributes) + return [entry['cn'].replace(postfix, '') for entry in results] + + +def get_sts_tenant_certificates(include_tenant_credential=True, + include_certificate_chain=True): + """ + Get STS tenant user certificates + :return: dict(cn: certificate list) + """ + env = Environment.get_environment() + sso_domain = env.get_value('SSO_DOMAIN') + domain_dn = env.get_value('SSO_DOMAIN_DN') + + base_dn = "cn={},cn=Tenants,cn=IdentityManager,cn=Services,{}".format(sso_domain, domain_dn) + if include_certificate_chain and include_tenant_credential: + ldap_filter = \ + '(|(objectClass=vmwSTSTenantCredential)(&(objectclass=vmwSTSTenantTrustedCertificateChain)(cn=TrustedCertChain*)))' + elif include_tenant_credential: + ldap_filter = '(objectClass=vmwSTSTenantCredential)' + elif include_certificate_chain: + ldap_filter = '(&(objectclass=vmwSTSTenantTrustedCertificateChain)(cn=TrustedCertChain*))' + else: + return None + + ldap_attributes = ['cn', 'userCertificate'] + results = perform_ldap_search(base_dn, ldap_filter, ldap_attributes) + + tenant_certs = dict() + for entry in results: + tenant_certs[entry['cn']] = [build_pem_certificate(cert) for cert in entry['userCertificate']] + + return tenant_certs + + +def get_solution_user_certificate(solution_user): + """ + Get solution user certificate from VMDir via LDAP + + :param solution_user: solution user + :return: Solution user certificate in PEM format + """ + env = Environment.get_environment() + machine_id = env.get_value('MACHINE_ID') + domain_dn = env.get_value('SSO_DOMAIN_DN') + + base_dn = "cn={}-{},cn=ServicePrincipals,{}".format(solution_user, machine_id, domain_dn) + ldap_filter = '(objectClass=vmwServicePrincipal)' + ldap_attributes = ['userCertificate'] + results = perform_ldap_search(base_dn, ldap_filter, ldap_attributes, search_scope=ldap.BASE) + certs = [] + for entry in results: + for cert in entry['userCertificate']: + # only expect single certificate + certs.append(build_pem_certificate(cert)) + return '\n'.join(certs) + + +def get_all_ca_subject_keyids(use_cache=False): + """ + Get list of subject keyId of trusted CA certificates via 'dir-cli trustedcert list' output + The result will be cached + + :param use_cache: If True, the previous cached result will be used (default: True) + :return: list of subject keyIds of trusted CA certificates + """ + global cache_ca_keyids + if use_cache and cache_ca_keyids is not None: + return cache_ca_keyids + + env = Environment.get_environment() + sso_username = env.get_value('SSO_USERNAME') + sso_password = env.get_value('SSO_PASSWORD') + args = [DIR_CLI, 'trustedcert', 'list', '--login', sso_username, '--password', sso_password] + ret, stdout, _ = CommandRunner(*args).run() + cache_ca_keyids = TextFilter(stdout).start_with('CN(id):').cut(':', [1]).remove_white_spaces().get_lines() + return cache_ca_keyids + + +def get_ca_certificate(subject_keyid, use_cache=True): + """ + Get CA certificate in VMDir via 'dir-cli trustedcert get' command output + + :param subject_keyid: the certificate' subject keyId + :param use_cache: if True, the previous cached result will be used instead + :return: trusted CA certificate in PEM format + """ + global cache_ca_certificate_map + if not subject_keyid: + return None + if use_cache: + cert_cache = cache_ca_certificate_map.get(subject_keyid) + if cert_cache: + return cert_cache + + env = Environment.get_environment() + sso_username = env.get_value('SSO_USERNAME') + sso_password = env.get_value('SSO_PASSWORD') + args = [DIR_CLI, 'trustedcert', 'get', '--login', sso_username, '--password', sso_password, + '--id', subject_keyid, '--outcert', '/dev/stdout'] + ret, stdout, _ = CommandRunner(*args).run() + lines = TextFilter(stdout).match_block('-----BEGIN CERTIFICATE-----', + '-----END CERTIFICATE-----').get_lines() + pem_cert = '\n'.join(lines) + cache_ca_certificate_map[subject_keyid] = pem_cert + return pem_cert + + +def get_all_ca_certificates(): + """ + Get all trusted CA certificates in VMDir via dir-cli command) + + :return: list of CA certificates in PEM format + """ + subject_keyids = get_all_ca_subject_keyids(False) + certs = [] + for subject_keyid in subject_keyids: + pem_cert = get_ca_certificate(subject_keyid) + certs.append(pem_cert) + return certs, subject_keyids + + +def get_service_principals(): + """ + Get service principal list from VMDir via dir-cli + + :return: list of service principals + """ + env = Environment.get_environment() + sso_username = env.get_value('SSO_USERNAME') + sso_password = env.get_value('SSO_PASSWORD') + args = [DIR_CLI, 'service', 'list', '--login', sso_username, '--password', sso_password] + ret, stdout, _ = CommandRunner(*args).run() + return TextFilter(stdout).cut(fields=[1]).get_lines() + + +def init_env_identity_source(): + env = Environment.get_environment() + if env.get_map().get('SSO_USERNAME') is None or \ + env.get_value('EXTERNAL_IDENTITY_SOURCE_CONFIGURED') is not None: + return + + logger.info('Checking identity source settings') + identity_sources = get_identity_sources() + source_types = [item['type'] for item in identity_sources] + logger.info("Identity sources: {}".format(source_types)) + + env.set_value('EXTERNAL_IDENTITY_SOURCE_CONFIGURED', len(source_types) > 0) + + +def get_identity_sources(use_machine_account=False): + """ + Get the identity sources settings + + return: list of configured identity source represented in the following dict object: + { + "type": one of ('AD over LDAP', 'ADFS', 'OpenLDAP') + "domain_name": domain name + "certificates": CA certificate for this identity source + } + """ + logger.info('Obtaining identity source settings') + env = Environment.get_environment() + sso_domain = env.get_value('SSO_DOMAIN') + domain_dn = env.get_value('SSO_DOMAIN_DN') + search_list = [ + ('AD over LDAP', 'IdentityProviders', 'IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING'), + ('ADFS', 'VCIdentityProviders', 'IDENTITY_STORE_TYPE_LDAP_WITH_AD_MAPPING'), + ('OpenLDAP', 'IdentityProviders', 'IDENTITY_STORE_TYPE_LDAP') + ] + + identity_sources = [] + connection = None + try: + connection = get_ldap_connection(use_machine_account=use_machine_account) + ldap_attributes = ['vmwSTSDomainName', 'userCertificate'] + for type_name, provider_cn, provider_type in search_list: + base_dn = "cn={},cn={},cn=Tenants,cn=IdentityManager,cn=Services,{}"\ + .format(provider_cn, sso_domain, domain_dn) + ldap_filter = "(vmwSTSProviderType={})".format(provider_type) + if not Ldap.ldap_search(connection, base_dn, ldap_filter, ldap.SUBTREE, ldap_attributes): + # this search may return noSuchObject due to an invalid base_dn + if connection.result['result'] == 32: + continue + logger.error('Unable to perform LDAP search for the identity source setting') + raise LdapException(connection.result['result'], connection.result['description']) + if not connection.result: + continue + for entry in connection.response: + attributes = entry['attributes'] + result = dict() + result['type'] = type_name + result['domain_name'] = attributes['vmwSTSDomainName'] + certs = [] + for cert in attributes['userCertificate']: + # only expect single certificate + certs.append(build_pem_certificate(cert)) + result['certificates'] = certs + identity_sources.append(result) + logger.info("Identity source: type={}, domain={}".format(type_name, result['domain_name'])) + finally: + if connection: + close_ldap_connection(connection) + + return identity_sources + + +def get_sso_domain_nodes(): + logger.info('Obtaining SSO domain nodes') + domain_dn = Environment.get_environment().get_value('SSO_DOMAIN_DN') + base_dn_list = ["ou=Domain Controllers,{}".format(domain_dn), "ou=Computers,{}".format(domain_dn)] + ldap_filter = '(objectClass=computer)' + ldap_attributes = ['cn'] + nodes = [] + for base_dn in base_dn_list: + results = perform_ldap_search(base_dn, ldap_filter, ldap_attributes) + for entry in results: + node = entry['cn'] + if node not in nodes: + nodes.append(node) + logger.info("Found node: {}".format(node)) + + return nodes + + +def get_all_lookup_service_endpoints(search_base=None): + """ + Get all endpoint entries + :return: + """ + logger.info('Obtaining all endpoints') + domain_dn = Environment.get_environment().get_value('SSO_DOMAIN_DN') + if search_base is None: + search_base = "cn=Sites,cn=Configuration,{}".format(domain_dn) + search_filter = '(|(objectclass=vmwLKUPServiceEndpoint)(objectClass=vmwLKUPEndpointRegistration))' + search_attributes = ['dn', 'objectClass', 'vmwLKUPURI', 'vmwLKUPEndpointSslTrust', 'vmwLKUPSslTrustAnchor'] + return perform_ldap_search(search_base, search_filter, search_attributes) + + +def get_node_trust_anchors(fqdn_or_ip): + """ + Get SSL trust anchors by filtering the endpoint URI using {fqdn_or_ip} + :param fqdn_or_ip: + :return: + """ + logger.info("Obtaining node trust anchors: fqdn_or_ip: {}".format(fqdn_or_ip)) + trust_anchors = [] + pattern = "^https://{0}:.*|^https://{0}/.*".format(fqdn_or_ip) + endpoints = get_all_lookup_service_endpoints() + for endpoint in endpoints: + if not re.match(pattern, endpoint['vmwLKUPURI']): + continue + for cert in sum([endpoint['vmwLKUPEndpointSslTrust'], endpoint['vmwLKUPSslTrustAnchor']], []): + pem_cert = certutil.build_pem_certificate(cert) + if pem_cert and pem_cert not in trust_anchors: + trust_anchors.append(pem_cert) + logger.info('Trust anchors found for {}: {}'.format(fqdn_or_ip, trust_anchors)) + return trust_anchors + + +def get_endpoint_service_type(service_id): + domain_dn = Environment.get_environment().get_value('SSO_DOMAIN_DN') + search_base = "cn=Sites,cn=Configuration,{}".format(domain_dn) + search_filter = "(cn={})".format(service_id) + search_attributes = ['vmwLKUPType'] + result = perform_ldap_search(search_base, search_filter, search_attributes) + if result: + return result[0]['vmwLKUPType'] + + search_filter = '(objectClass=vmwLKUPService)' + search_attributes = ['dn', 'vmwLKUPServiceType'] + result = perform_ldap_search(search_base, search_filter, search_attributes) + for entry in result: + if service_id in entry['dn']: + # remove prefix 'urn:' + return entry['vmwLKUPServiceType'][4:] + return '' + + +def get_registered_vcenters(): + """ + Get registered VCenters + :return: list of tuple (deployment_node_id, node_dn) + """ + logger.info('Obtaining list of registered vCenters') + domain_dn = Environment.get_environment().get_value('SSO_DOMAIN_DN') + base_dn = "cn=Sites,cn=Configuration,{}".format(domain_dn) + ldap_filter = '(vmwLKUPType=vcenterserver)' + ldap_attributes = ['vmwLKUPDeploymentNodeId', 'dn'] + results = perform_ldap_search(base_dn, ldap_filter, ldap_attributes) + return [(entry['vmwLKUPDeploymentNodeId'], entry['dn']) for entry in results] + + +def get_endpoint_registrations(base_dn): + ldap_filter = '(objectClass=vmwLKUPEndpointRegistration)' + ldap_attributes = ['vmwLKUPURI'] + results = perform_ldap_search(base_dn, ldap_filter, ldap_attributes) + return [entry['vmwLKUPURI'] for entry in results] + + +def init_env_cac(): + env = Environment.get_environment() + # if env.get_map().get('SSO_USERNAME') is None or \ + # env.get_value('CAC_CONFIGURED') is not None: + # return + if env.get_value('CAC_CONFIGURED') is not None: + return + + vc_version = get_vc_version() + if vc_version >= VcVersion.V9: + logger.info('SmartCards are not supported since version {}'.format(VcVersion.V9.value)) + env.set_value('CAC_CONFIGURED', False) + return + + logger.info('Checking SmartCard Authentication settings') + sso_domain = env.get_value('SSO_DOMAIN') + domain_dn = env.get_value('SSO_DOMAIN_DN') + search_base = "cn={},cn=Tenants,cn=IdentityManager,cn=Services,{}".format(sso_domain, domain_dn) + search_filter = '(objectclass=vmwSTSTenant)' + search_attributes = ['vmwSTSAuthnTypes'] + results = perform_ldap_search(search_base, search_filter, search_attributes, use_machine_account=True) + is_cac_configured = False + logger.info('LDAP search results: {}'.format(results)) + for entry in results: + if entry['vmwSTSAuthnTypes'] == 4 or 4 in entry['vmwSTSAuthnTypes']: + is_cac_configured = True + env.set_value('CAC_CONFIGURED', is_cac_configured) + + +def get_smart_card_issuing_ca_certs(): + logger.info('Obtaining SmartCard issuing CA certificates') + env = Environment.get_environment() + sso_domain = env.get_value('SSO_DOMAIN') + domain_dn = env.get_value('SSO_DOMAIN_DN') + search_base = \ + "cn=DefaultClientCertCAStore,cn=ClientCertAuthnTrustedCAs,cn=Default,cn=ClientCertificatePolicies,"\ + "cn={},cn=Tenants,cn=IdentityManager,cn=Services,{}".format(sso_domain, domain_dn) + search_filter = '(objectClass=vmwSTSTenantTrustedCertificateChain)' + search_attributes = ['userCertificate'] + + result = perform_ldap_search(search_base, search_filter, search_attributes, use_machine_account=True) + certs = [] + for entry in result: + certs.extend(build_pem_certificate(cert) for cert in entry['userCertificate']) + + return certs + + +def get_all_sso_sites(): + domain_dn = Environment.get_environment().get_value('SSO_DOMAIN_DN') + search_base = "cn=Sites,cn=Configuration,{}".format(domain_dn) + search_filter = '(objectClass=*)' + search_attributes = ['cn'] + result = perform_ldap_search(search_base, search_filter, search_attributes, search_scope=ldap.LEVEL) + return [entry['cn'] for entry in result] + + +def unpublish_trusted_certificate(cert_file): + """ + Unpublish certificate in VMDir + :param cert_file: Certificate file to unpublish + """ + env = Environment.get_environment() + sso_username = env.get_value('SSO_USERNAME') + sso_password = env.get_value('SSO_PASSWORD') + CommandRunner(DIR_CLI, 'trustedcert', 'unpublish', '--login', sso_username, '--password', + sso_password, '--cert', cert_file, expected_return_code=0).run() + + +def publish_trusted_certificate(cert_file, is_chain=False): + """ + Publish certificate in VMDir + :param cert_file: Certificate file to unpublish + :param is_chain: Publish all certificate in chain + """ + env = Environment.get_environment() + sso_username = env.get_value('SSO_USERNAME') + sso_password = env.get_value('SSO_PASSWORD') + args = [DIR_CLI, 'trustedcert', 'publish', '--login', sso_username, '--password', sso_password, + '--cert', cert_file] + if is_chain: + args.append('--chain') + CommandRunner(*args, expected_return_code=0).run() + + +def remove_ca_certificate_from_ldap(subject_keyid): + domain_dn = Environment.get_environment().get_value('SSO_DOMAIN_DN') + cert_dn = "cn={},cn=Certificate-Authorities,cn=Configuration,{}".format(subject_keyid, domain_dn) + perform_ldap_delete(cert_dn) + + +def get_sddc_manager(): + # 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 '^$') + env = Environment.get_environment() + sso_domain = env.get_value('SSO_DOMAIN') + domain_dn = env.get_value('SSO_DOMAIN_DN') + search_base = "cn={},cn=Tenants,cn=IdentityManager,cn=Services,{}".format(sso_domain, domain_dn) + search_filter = '(objectClass=vmwSTSTenant)' + search_attributes = ['vmwSTSLogonBanner'] + result = perform_ldap_search(search_base, search_filter, search_attributes, search_scope=ldap.LEVEL) + return [entry['cn'] for entry in result] + + +def update_solution_user_certificate_in_vmdir(soluser, cert_file): + env = Environment.get_environment() + sso_username = env.get_value('SSO_USERNAME') + sso_password = env.get_value('SSO_PASSWORD') + machine_id = env.get_value('MACHINE_ID') + args = [DIR_CLI, 'service', 'update', '--name', f"{soluser}-{machine_id}", '--cert', + cert_file, '--login', sso_username, '--password', sso_password] + try: + CommandRunner(*args, expected_return_code=0).run_and_get_output() + except CommandExecutionError: + error_message = f"Unable to update {soluser}-{machine_id} solution user certificate in VMDir" + logger.error(error_message) + raise OperationFailed(error_message) + + +# ------------------------------ +# Replace a Solution User certificate in VMDir +# ------------------------------ +def replace_service_principal_certificates(soluser, cert_file): + print_task(f" {soluser}") + update_solution_user_certificate_in_vmdir(soluser, cert_file) + print_task_status("OK") + + +def verify_service_principals(): + print_task('Verifying Service Principal entries exist') + service_principals = get_service_principals() + if service_principals: + env = Environment.get_environment() + machine_id = env.get_value('MACHINE_ID') + solution_users = env.get_value('SOLUTION_USERS') + missing_service_principals = [] + for solution_user in solution_users: + service_principal = "{}-{}".format(solution_user, machine_id) + if service_principal not in service_principals: + missing_service_principals.append(service_principal) + + if missing_service_principals: + print_text_error('ERROR') + print_text_error("\n------------------------!!! Attention !!!------------------------ ") + print_text_error('The following Service Principal entries are missing:') + for sp in missing_service_principals: + print_text_error(" - {}".format(sp)) + + print_text_error('\nPlease refer to the following Knowledge Base article') + print_text_error('on using the lsdoctor utility to recreate the missing') + print_text_error('Solution User/Service Principal entries:') + print_text_error('https://knowledge.broadcom.com/external/article/320837/using-the-lsdoctor-tool.html') + else: + print_task_status("OK") + else: + print_text_error('Could not get list of Service Principal entries from VMware Directory') + + +def get_vmdir_state(): + state_output = CommandRunner(VDCADMINTOOL, command_input='6', expected_return_code=0).run_and_get_output() + logger.info('VMware Directory state: {}'.format(state_output)) + service_state = TextFilter(state_output).contain('VmDir State').cut('-', [1]).get_text().strip() + return service_state diff --git a/vCert-6.1.1-20260401/operation/.gitkeep b/vCert-6.1.1-20260401/operation/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vCert-6.1.1-20260401/operation/__init__.py b/vCert-6.1.1-20260401/operation/__init__.py new file mode 100644 index 0000000..dd028da --- /dev/null +++ b/vCert-6.1.1-20260401/operation/__init__.py @@ -0,0 +1,6 @@ +# Copyright (c) 2024 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +from . import view_certificate +from . import check_certificate diff --git a/vCert-6.1.1-20260401/operation/check_certificate.py b/vCert-6.1.1-20260401/operation/check_certificate.py new file mode 100644 index 0000000..334cc22 --- /dev/null +++ b/vCert-6.1.1-20260401/operation/check_certificate.py @@ -0,0 +1,981 @@ +# 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 diff --git a/vCert-6.1.1-20260401/operation/check_configuration.py b/vCert-6.1.1-20260401/operation/check_configuration.py new file mode 100644 index 0000000..1b84c2d --- /dev/null +++ b/vCert-6.1.1-20260401/operation/check_configuration.py @@ -0,0 +1,234 @@ +# Copyright (c) 2024-2025 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import logging +import xml.etree.ElementTree as ET + +from lib.console import ( + print_task, print_task_status, print_task_status_error, + print_task_status_warning, print_header, ColorKey, print_text +) +from lib.constants import STS_SERVER_CONFIG_FILE_PATH, STS_SERVER_CONFIG_PROPERTY_FILE_PATH +from lib.environment import Environment +from lib.exceptions import OperationFailed +from lib.host_utils import VcVersion, get_vc_version, is_file_exists +from lib.input import MenuInput +from lib.java_utils import load_property_file +from lib.vcdb import get_vmca_configuration_from_vcdb +from lib.vecs import (create_vecs_store_template, get_current_vecs_store_permissions, get_template_vecs_store_permissions, + get_missing_stores, manage_missing_vecs_stores, manage_missing_vecs_permissions +) +from lib.vmdir import get_sso_domain_nodes, perform_ldap_search +from operation.check_certificate import CertificateStatus, add_certificate_status + +logger = logging.getLogger(__name__) + +def check_sts_configuration(): + print_header('Checking STS Server Configuration') + check_sts_certificate_configuration() + check_sts_connectionstring_configuration() + + +def check_sts_certificate_configuration(): + env = Environment.get_environment() + vc_version = get_vc_version() + vc_build = int(env.get_value('VC_BUILD')) + + print_task('Checking VECS store configuration') + + # List of (versionConstraint, buildConstraint, key) tuples that identify + # what VECS stores are valid for the provided vCenter version. + versionConfigDB = [ + # Version Build VECS Key + # ------------- ----- ---------------------------- + (VcVersion.V7, None, 'localhost_connector_store'), + (VcVersion.V8, None, 'localhost_connector_store'), + (VcVersion.V9, None, 'localhost_connector_store'), + + (VcVersion.V7, None, 'localhost_certificate_store'), + (VcVersion.V8, None, 'localhost_certificate_store'), + # Beginning with 9.0 there is just one certificate store. + (VcVersion.V9, None, 'certificate_store'), + + (VcVersion.V7, 20845200, 'clientauth_connector_store'), + (VcVersion.V8, None, 'clientauth_connector_store'), + (VcVersion.V9, None, 'clientauth_connector_store'), + + (VcVersion.V7, 20845200, 'clientauth_certificate_store'), + (VcVersion.V8, None, 'clientauth_certificate_store'), + ] + + # Initialize the STS configuration based on the VC version constraints. + sts_config = {} + for versionConstraint, buildConstraint, key in versionConfigDB: + if vc_version != versionConstraint: + continue + if buildConstraint and vc_build < buildConstraint: + continue + sts_config[key] = '' + + # Obtain the STS configuration from vCenter. + if vc_version >= VcVersion.V9: + # Note: All entries in the versionConfigDB that match 9.0 must have an + # entry in the keyMap. + keyMap = { + 'vmidentity.server.connector.ssl.localhost.certificate.keystore.file': 'localhost_connector_store', + 'vmidentity.server.connector.ssl.store': 'certificate_store', + 'vmidentity.server.connector.ssl.client.auth.certificate.keystore.file': 'clientauth_connector_store', + } + + config = load_property_file(STS_SERVER_CONFIG_PROPERTY_FILE_PATH) + + for key, value in config.items(): + if key in keyMap: + sts_config[keyMap[key]] = value + else: + config = ET.parse(STS_SERVER_CONFIG_FILE_PATH) + root = config.getroot() + + conn_cfg = {} + for connector in root.iter('Connector'): + if connector.attrib['port'] == '${bio-ssl-localhost.https.port}': + conn_cfg['localhost_connector_store'] = connector.attrib['store'] + conn_cfg['localhost_certificate_store'] = connector.find('SSLHostConfig').find('Certificate').attrib['certificateKeystoreFile'] + elif connector.attrib['port'] == '${bio-ssl-clientauth.https.port}': + conn_cfg['clientauth_connector_store'] = connector.attrib['store'] + conn_cfg['clientauth_certificate_store'] = connector.find('SSLHostConfig').find('Certificate').attrib['certificateKeystoreFile'] + + for key, value in conn_cfg.items(): + if key in sts_config: + sts_config[key] = value + else: + # This should never happen. It means somehow a key is in the + # STS server config file that doesn't belong for the given VC + # version, as indicated by versionConfigDB. + logger.warning('Unexpected STS server config key: %s' % key) + + logger.info('Checking STS configuration on vCenter {} build {}'.format(vc_version, vc_build)) + + vecs_stores_to_replace = set() + update_sts_config = False + for key, value in sts_config.items(): + description = key.replace('_', ' ').capitalize() + logger.info('{}: {}'.format(description, value)) + + if value != 'MACHINE_SSL_CERT': + update_sts_config = True + vecs_stores_to_replace.add(value) + sts_config[key] = value + + sts_config['vecs_stores_to_replace'] = vecs_stores_to_replace + + if update_sts_config: + add_certificate_status(CertificateStatus.CERT_STATUS_STS_VECS_CONFIG) + print_task_status_warning('LEGACY') + else: + print_task_status('OK') + + return update_sts_config, sts_config + + +def check_sts_connectionstring_configuration(): + domain_dn = Environment.get_environment().get_value('SSO_DOMAIN_DN') + sso_domain = Environment.get_environment().get_value('SSO_DOMAIN') + print_task('Checking STS ConnectionStrings') + search_dn = 'cn={},cn=IdentityProviders,cn={},cn=Tenants,cn=IdentityManager,cn=Services,{}'.format(sso_domain, sso_domain, domain_dn) + search_filter = '(objectclass=vmwSTSIdentityStore)' + search_attributes = ['vmwSTSConnectionStrings'] + result = perform_ldap_search(search_dn, search_filter, search_attributes) + if result: + if len(get_sso_domain_nodes()) == 1: + print_task_status('OK') + return True, search_dn + sts_connection_strings = result[0]['vmwSTSConnectionStrings'] + if len(sts_connection_strings) > 1: + add_certificate_status(CertificateStatus.CERT_STATUS_STS_CONNECTION_STRINGS_NUMBER) + print_task_status_warning('MISCONFIG') + return False, search_dn + if sts_connection_strings[0] != 'ldap://localhost:389': + add_certificate_status(CertificateStatus.CERT_STATUS_STS_CONNECTION_STRINGS_HOSTNAME) + print_task_status_warning('MISCONFIG') + return False, search_dn + + print_task_status('OK') + return True, search_dn + else: + print_task_status_error('FAILED') + error_message = 'Unable to get the vmwSTSConnectionStrings attribute from VMware Directory' + raise OperationFailed(error_message) + + +def check_vmca_configuration_vcdb(): + settings =get_vmca_configuration_from_vcdb() + for setting in settings: + setting_parts = setting.split('|') + setting_name = setting_parts[0].strip() + setting_value = setting_parts[1].strip() + if not setting_value: + add_certificate_status(CertificateStatus.CERT_STATUS_VMCA_EMPTY_CONFIG) + print_text("{:<48}{}'EMPTY'{}".format(setting_name, ColorKey.YELLOW, ColorKey.NORMAL)) + elif setting_name == 'vpxd.certmgmt.mode' and 'setting_value' == 'thumbprint': + add_certificate_status(CertificateStatus.CERT_STATUS_VMCA_MODE) + print_text("{:<48}{}'{}'{}".format(setting_name, ColorKey.YELLOW, setting_value, ColorKey.NORMAL)) + else: + print_text("{:<48}{}'{}'{}".format(setting_name, ColorKey.GREEN, setting_value, ColorKey.NORMAL)) + + +def check_vecs_store_permissions(check_only=True): + env = Environment.get_environment() + vc_version = env.get_value('VC_VERSION_LONG') + vc_build = env.get_value('VC_BUILD') + vecs_permissions_template = '{}/config/vecs_permissions/vcsa-{}-{}.json'.format(env.get_value('SCRIPT_DIR'), vc_version, vc_build) + if is_file_exists(vecs_permissions_template): + print_text('Checking status and permissions for VECS stores:') + current_permissions = get_current_vecs_store_permissions() + template_permissions = get_template_vecs_store_permissions(vecs_permissions_template) + missing_stores = get_missing_stores() + missing_permissions = {'read' : {}, + 'write' : {}} + + for store, permissions in template_permissions.items(): + print_task(' {}'.format(store)) + + for expected_read_permission in permissions['read']: + if expected_read_permission not in current_permissions[store]['read']: + if store not in missing_permissions['read']: + missing_permissions['read'][store] = [] + missing_permissions['read'][store].append(expected_read_permission) + + for expected_write_permission in permissions['write']: + if expected_write_permission not in current_permissions[store]['write']: + if store not in missing_permissions['write']: + missing_permissions['write'][store] = [] + missing_permissions['write'][store].append(expected_write_permission) + + if store in missing_stores: + print_task_status_warning('MISSING') + add_certificate_status(CertificateStatus.CERT_STATUS_STORE_MISSING) + elif store in missing_permissions['read'] or store in missing_permissions['write']: + print_task_status_warning('PERMISSIONS') + add_certificate_status(CertificateStatus.CERT_STATUS_STORE_PERMISSIONS) + else: + print_task_status('OK') + + if check_only: + return + + if missing_stores: + manage_missing_vecs_stores(missing_stores) + + if missing_permissions['read'] or missing_permissions['write']: + manage_missing_vecs_permissions(missing_permissions) + else: + print_text('{}VECS store template for {}-{} not found.{}'.format(ColorKey.YELLOW, vc_version, vc_build, ColorKey.NORMAL)) + + if check_only: + return + + prompt = MenuInput('Create VECS store template? [N]: ', acceptable_inputs=['Y', 'N'], default_input='N') + print() + if prompt.get_input() == 'Y': + create_vecs_store_template(vecs_permissions_template) + + diff --git a/vCert-6.1.1-20260401/operation/common.py b/vCert-6.1.1-20260401/operation/common.py new file mode 100644 index 0000000..07853e1 --- /dev/null +++ b/vCert-6.1.1-20260401/operation/common.py @@ -0,0 +1,206 @@ +# Copyright (c) 2024-2025 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import ldap3 as ldap +import logging + +from lib import vecs +from lib import vmdir +from lib.certificate_utils import get_certificate_fingerprint, get_x509_certificate, split_certificates_from_pem +from lib.console import print_text_error +from lib.constants import VMCAM_CERT_FILE_PATH +from lib.environment import Environment +from lib.exceptions import CommandExecutionError +from lib.host_utils import VcVersion, get_file_contents, get_vc_version, get_hostname +from lib.input import MenuInput +from lib.ldap_utils import Ldap, LdapException, get_user_dn + +logger = logging.getLogger(__name__) + + +def prevalidate_credential(**_): + """ + Enforce that SSO credentials are properly populated + """ + env = Environment.get_environment() + _ = env.get_value('SSO_USERNAME') + _ = env.get_value('SSO_PASSWORD') + + vmdir.init_env_identity_source() + vmdir.init_env_cac() + + +def verify_sso_credential(sso_username, sso_password): + """ + Verify the SSO credential via LDAP authentication + + :param sso_username: Single Sign-On user name + :param sso_password: Single Sign-on password + :return: True if the credential can be verified, otherwise False + """ + hostname = get_hostname() + user_dn = get_user_dn(sso_username) + connection = None + try: + connection = Ldap.open_ldap_connection(node=hostname, user_dn=user_dn, + password=sso_password) + if Ldap.ldap_search(connection, "cn=schemacontext", "(objectClass=*)", + ldap.BASE, ["cn"]): + logger.info("Password verified successfully for user %s", user_dn) + return True + except LdapException: + pass + finally: + if connection: + Ldap.close_ldap_connection(connection) + + logger.error("Password verification failed for user %s on host %s", user_dn, hostname) + return False + + +def populate_sso_credential(): + """ + Callback method to populate SSO_USERNAME and SSO_PASSWORD environment variables + + :return: dict containing both verified SSO_USERNAME and SSO_PASSWORD values + :raise CommandExecutionError: if the credential verification failed + """ + env = Environment.get_environment() + default_input = 'administrator@{}'.format(env.get_value('SSO_DOMAIN')) + counter_username = counter_password = 0 + sso_username = None + print() + while counter_username < 3 and counter_password < 3: + if sso_username: + default_input = sso_username + menu_input = MenuInput('Please enter a Single Sign-On administrator account [{}]: '.format(default_input), + default_input=default_input, case_insensitive=False) + sso_username = menu_input.get_input() + if not check_sso_credential(sso_username): + sso_username = None + counter_username += 1 + continue + menu_input = MenuInput('Please provide the password for {}: '.format(sso_username), + masked=True, + case_insensitive=False) + sso_password = menu_input.get_input() + if not verify_sso_credential(sso_username, sso_password): + counter_password += 1 + continue + print() + return [('SSO_USERNAME', sso_username), ('SSO_PASSWORD', sso_password)] + raise CommandExecutionError('Invalid SSO credential') + + +def check_sso_credential(sso_username, sso_password=None): + """ + Check and verify the SSO credentials. It will validate that the SSO domain matches to + values obtained from VC. If password is provided, it will try to verify the credential + by performing LDAP authentication + + :param sso_username: SSO user name + :param sso_password: SSO user password + :return: True if the validation passed, otherwise False + """ + env = Environment.get_environment() + sso_domain = env.get_value('SSO_DOMAIN') + user_name = None + user_sso_domain = None + if '@' in sso_username: + user_name, user_sso_domain = tuple(sso_username.split('@', 2)) + if user_sso_domain != sso_domain: + print_text_error('Invalid domain, please provide an account in the SSO domain [{}].'.format(sso_domain)) + return False + elif not user_name: + print_text_error('Invalid user name') + return False + if sso_password is None: + return True + return verify_sso_credential(sso_username, sso_password) + + +def get_vcenter_extensions(): + """ + Get list of vCenter extensions + """ + env = Environment.get_environment() + vc_version = get_vc_version() + vc_build = int(env.get_value('VC_BUILD')) + + # The following table entries are based on the output of: + # /usr/bin/psql -d VCDB -U postgres -c "SELECT ext_id, thumbprint FROM vpx_ext" -t + # Only the entries for a VC version that have a defined thumbprint should + # be in the table. + vcenterExtensionDB = [ + # Version Build Extension + # ------------- -------- ------------------------- + (VcVersion.V7, None, 'com.vmware.vsan.health'), + (VcVersion.V8, None, 'com.vmware.vsan.health'), + (VcVersion.V9, None, 'com.vmware.vsan.health'), + + (VcVersion.V7, None, 'com.vmware.vcIntegrity'), + (VcVersion.V8, None, 'com.vmware.vcIntegrity'), + (VcVersion.V9, None, 'com.vmware.vcIntegrity'), + + (VcVersion.V7, None, 'com.vmware.rbd'), + # At or after the following build number com.vmware.rbd is excluded. + (VcVersion.V8, 22385739, 'com.vmware.rbd'), + + (VcVersion.V7, None, 'com.vmware.imagebuilder'), + (VcVersion.V8, None, 'com.vmware.imagebuilder'), + + (VcVersion.V7, None, 'com.vmware.vmcam'), + (VcVersion.V8, None, 'com.vmware.vmcam'), + (VcVersion.V9, None, 'com.vmware.vmcam'), + + (VcVersion.V7, None, 'com.vmware.vim.eam'), + (VcVersion.V8, None, 'com.vmware.vim.eam'), + (VcVersion.V9, None, 'com.vmware.vim.eam'), + + (VcVersion.V8, None, 'com.vmware.vlcm.client'), + (VcVersion.V9, None, 'com.vmware.vlcm.client'), + ] + + vcenter_extensions = [] + for versionConstraint, buildConstraint, key in vcenterExtensionDB: + if vc_version != versionConstraint: + continue + if buildConstraint and vc_build >= buildConstraint: + continue # The build number is an exclusion not inclusion. + vcenter_extensions.append(key) + + return vcenter_extensions + + +def get_vcenter_extension_expected_thumbprints(vcenter_extensions): + """ + Get expected vcenter extension's thumbprint + :param vcenter_extensions: list of vcenter extension + :return: dict { vc_extension: (thumbprint, cert_type, cert_pem) } + """ + vpxd_ext_pem_cert = vecs.get_certificate('vpxd-extension', 'vpxd-extension') + vpxd_ext_thumbprint = get_certificate_fingerprint(get_x509_certificate(vpxd_ext_pem_cert)) + + machine_ssl_pem_cert = vecs.get_certificate('MACHINE_SSL_CERT', '__MACHINE_CERT') + machine_ssl_thumbprint = get_certificate_fingerprint(get_x509_certificate(machine_ssl_pem_cert)) + + vmcam_pem_cert = get_file_contents(VMCAM_CERT_FILE_PATH) + vmcam_thumbprint = get_certificate_fingerprint(get_x509_certificate(vmcam_pem_cert)) + + result = dict() + for extension in vcenter_extensions: + if extension == 'com.vmware.vmcam': + expected_thumbprint = vmcam_thumbprint + expected_cert_type = 'Authentication Proxy' + cert_pem = split_certificates_from_pem(vmcam_pem_cert)[0] + elif extension == 'com.vmware.vsan.health': + expected_thumbprint = machine_ssl_thumbprint + expected_cert_type = 'Machine SSL' + cert_pem = split_certificates_from_pem(machine_ssl_pem_cert)[0] + else: + expected_thumbprint = vpxd_ext_thumbprint + expected_cert_type = 'vpxd-extension' + cert_pem = split_certificates_from_pem(vpxd_ext_pem_cert)[0] + result[extension] = (expected_thumbprint, expected_cert_type, cert_pem) + return result diff --git a/vCert-6.1.1-20260401/operation/esxi_certificate_operations.py b/vCert-6.1.1-20260401/operation/esxi_certificate_operations.py new file mode 100644 index 0000000..8dc2267 --- /dev/null +++ b/vCert-6.1.1-20260401/operation/esxi_certificate_operations.py @@ -0,0 +1,884 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2024-2025 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + + +import stat + +from lib.console import print_text, print_task_status, print_task_status_warning, ColorKey, \ + print_text_warning, print_header, print_task_status_error + +import logging +import os +import urllib3 +import re + +from lib.command_runner import CommandRunner +from lib.exceptions import OperationFailed, CommandExecutionError +from lib.input import MenuInput +from lib.menu import Menu + +from lib import vcdb, vmdir +from lib import vecs +from lib.environment import Environment +from lib.certificate_utils import ( + get_x509_certificate, build_certification_path, get_certificate_fingerprint, split_certificates_from_pem, + get_certificate_from_host, get_certificate_end_date, get_certificate_start_date, + get_subject_and_issuer_dn, get_subject_alternative_names, get_certificate_fetcher_from_list, + is_ca_certificate, detect_and_convert_to_pem, get_key_modulus_from_pem_text, generate_csr +) +from lib.console import ( + print_task, print_text_error +) +from lib.constants import SPACE + +from lib.host_utils import ( + get_file_contents, save_text_to_file, find_files, + set_file_mode, is_file_exists +) +from operation.manage_certificate import find_matched_private_key, verify_certificate_and_key, obtain_ca_chain, \ + get_csr_info, get_san_entries, generate_openssl_config, clear_csr_info, get_timestamp + +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) +logger = logging.getLogger(__name__) + + +def check_esxi_vcenter_trust(): + """ + Entry point for checking esxi vcenter certificate trust + """ + + num_hosts = vcdb.get_number_of_hosts() + + title = "Check ESXi/vCenter certificate trust" + text_color = ColorKey.YELLOW if num_hosts == 0 else ColorKey.GREEN + sub_title = "There are {}{}{} hosts connected to vCenter.\n".format(text_color, num_hosts, ColorKey.NORMAL) + + menu = Menu() + if not get_hosts(): + menu.add_menu_item('Return to the previous menu', Menu.run_navigation_return, is_hidden=False, key='R', + is_default=True) + else: + menu.set_menu_options(title, sub_title, input_text='Select an option [Return to the previous menu]: ') + menu.add_menu_item("Perform check on all hosts (requires uniform root password on all hosts)", + view_certificate_for_all_esxi_hosts) + menu.add_menu_item("Perform check on all hosts in a cluster (requires uniform root password on all hosts)", + view_esxi_for_all_cluster) + menu.add_menu_item("Perform check on single host", view_esxi_certificate_for_specific_host) + menu.add_menu_item('Return to the previous menu', Menu.run_navigation_return, is_hidden=True, key='R', + is_default=True) + + menu.run() + + +def view_esxi_test_response(): + print(True) + + +def display_num_hosts(): + num_hosts = vcdb.get_number_of_hosts() + text_color = ColorKey.YELLOW if num_hosts == 0 else ColorKey.GREEN + print_text("There are {}{}{} hosts connected to vCenter.".format(text_color, num_hosts, ColorKey.NORMAL)) + + if num_hosts == 0: + print_text_error("Hosts not detected on the system") + print("\n") + + +def get_hosts(): + hosts_str = vcdb.get_hosts() + + if hosts_str == "": + print_text("No hosts available") + return [] + + hosts = [] + for host_info in hosts_str: + host_info = host_info.strip() + cluster_id = host_info.split("|")[0].strip() + name = host_info.split("|")[1].strip() + ip = host_info.split("|")[2].strip() + host = [cluster_id, name, ip] + hosts.append(host) + return hosts + + +def get_host_password(pass_str): + password = "" + while password == "": + password = MenuInput(pass_str, masked=True, case_insensitive=False).get_input() + print() + return password + + +def get_host_details(hosts): + host_detail = "" + while host_detail == "": + host_detail = MenuInput('Enter FQDN or IP of the ESXi host: ', + allow_empty_input=False, case_insensitive=False).get_input() + + logger.info("The entered FQDN or IP is : {}".format(host_detail)) + if host_detail == "": + print_text("Please enter valid input") + continue + for cluster_id, name, ip in hosts: + if host_detail == name or host_detail == ip: + print() + return [name, ip] + + host_detail = "" + print_text("Please enter valid input from below list") + print(flush=True) + for cluster_id, name, ip in hosts: + print_text("Host name : {} Host ip : {}".format(name, ip)) + print(flush=True) + + print() + return [] + + +def get_host_name_or_ip(host_name, host_ip): + if host_name == "": + return host_ip + else: + return host_name + + +def get_certificate_management_mode(): + return vcdb.get_certificate_management_mode() + + +def view_certificate_for_all_esxi_hosts(): + hosts = get_hosts() + print("\n") + password = get_host_password("Enter root password for all ESXi hosts: ") + certificate_management_mode = get_certificate_management_mode() + print_text("Certificate Management Mode = {}{}{}\n".format(ColorKey.GREEN, certificate_management_mode, ColorKey.NORMAL)) + for cluster_id, name, ip in hosts: + print_text("Host : {}{}{}".format(ColorKey.CYAN, get_host_name_or_ip(name, ip), ColorKey.NORMAL)) + view_esxi_certificate_trust(get_host_name_or_ip(name, ip), certificate_management_mode, password) + print("\n\n") + + +def view_esxi_certificate_for_specific_host(): + host_name, host_ip = get_host_details(get_hosts()) + host_name_or_ip = get_host_name_or_ip(host_name, host_ip) + password = get_host_password("Enter root password for ESXi host {}: ".format(host_name_or_ip)) + certificate_management_mode = get_certificate_management_mode() + print_text("Certificate Management Mode = {}{}{}".format(ColorKey.GREEN, certificate_management_mode, ColorKey.NORMAL)) + print_text("\nHost : {}{}{}".format(ColorKey.CYAN, host_name_or_ip, ColorKey.NORMAL)) + view_esxi_certificate_trust(host_name_or_ip, certificate_management_mode, password) + print("\n\n") + + +def get_host_and_cluster(): + host_and_cluster_id = vcdb.get_host_and_cluster_id() + + if host_and_cluster_id == "": + print_text("No hosts or Clusters available") + return {} + + host_and_cluster_id_dict = {} + + for host_and_cluster_id_info in host_and_cluster_id: + host_info = host_and_cluster_id_info.strip() + host_id = host_info.split("|")[0].strip() + cluster_id = host_info.split("|")[1].strip() + host_and_cluster_id_dict[host_id] = cluster_id + + return host_and_cluster_id_dict + + +def find_hosts_in_cluster(input_cluster_id, hosts, host_and_cluster): + hosts_in_cluster = [] + + for host_id, name, ip in hosts: + if host_and_cluster[host_id] == input_cluster_id: + host_in_cluster = [host_id, name, ip] + hosts_in_cluster.append(host_in_cluster) + + if not hosts_in_cluster: + print_text("No hosts were found in cluster : {} ".format(input_cluster_id)) + return [] + + return hosts_in_cluster + + +def get_input_cluster_id(cluster_no_dict, cluster_dict): + input_cluster_id = "" + while input_cluster_id == "": + try: + input_cluster_id = MenuInput('Select custer: ', + allow_empty_input=False, case_insensitive=False).get_input() + logger.info("The entered cluster id is: {}".format(input_cluster_id)) + + if input_cluster_id not in cluster_no_dict: + print_text("Please enter a valid cluster id from below list : \n") + for id_no, cluster_id in cluster_no_dict.items(): + print_text("cluster_id : {} cluster : {} ".format(id_no, cluster_dict[cluster_id])) + input_cluster_id = "" + except ValueError: + print("Invalid input. Please enter appropriate entry") + for id_no, cluster_id in cluster_no_dict.items(): + print_text("cluster id : {} cluster name : {} ".format(id_no, cluster_dict[cluster_id])) + continue + except IndexError: + print_text("Please enter a valid cluster id from below list : \n") + for id_no, cluster_id in cluster_no_dict.items(): + print_text("cluster id : {} cluster name : {} ".format(id_no, cluster_dict[cluster_id])) + continue + print("\n") + return cluster_no_dict[input_cluster_id] + + +def get_clusters(): + clusters = vcdb.get_clusters() + print_text("\nCompute clusters:") + + if clusters == "": + print_text("There are no clusters present ") + return {} + + cluster_no = 1 + cluster_dict = {} + cluster_no_dict = {} + for cluster_details in clusters: + cluster_id = cluster_details.split("|")[0].strip() + cluster_name = cluster_details.split("|")[1].strip() + cluster_no_dict["{}".format(cluster_no)] = cluster_id + cluster_dict[cluster_id] = cluster_name + print_text(" {}. {}".format(cluster_no, cluster_name)) + cluster_no += 1 + print_text("\n") + return cluster_no_dict, cluster_dict + + +def view_esxi_for_all_cluster(): + cluster_no_dict, cluster_dict = get_clusters() + if not cluster_dict: + return + + input_cluster_id = get_input_cluster_id(cluster_no_dict, cluster_dict) + + hosts_list = get_hosts() + host_and_cluster_id_dict = get_host_and_cluster() + hosts_in_cluster = find_hosts_in_cluster(input_cluster_id, hosts_list, host_and_cluster_id_dict) + + if hosts_in_cluster: + password = get_host_password("Enter root password for all ESXi hosts in cluster: ") + certificate_management_mode = get_certificate_management_mode() + print_text("Certificate Management Mode = {}{}{}".format(ColorKey.GREEN, certificate_management_mode, ColorKey.NORMAL)) + for cluster_id, name, ip in hosts_in_cluster: + print_text("\nHost : {}{}{}".format(ColorKey.CYAN, get_host_name_or_ip(name, ip), ColorKey.NORMAL)) + view_esxi_certificate_trust(get_host_name_or_ip(name, ip), certificate_management_mode, password) + + +def delete_and_validate_vecs_entry(store, alias): + vecs.delete_entry(store, alias) + # This is to check if pem_cert has been deleted + vecs_cert = vecs.get_certificate(store, alias) + if not vecs_cert: + return True + else: + return False + + +def create_vecs_certificate(access_string, cert_file, vecs_store, vecs_alias): + pem_certs = get_certificate_from_host(access_string, 443) + if pem_certs: + for cert in pem_certs: + # Get and write x509 certificates to TEMP_DIR + save_text_to_file(cert, cert_file) + + vecs.add_entry(vecs_store, vecs_alias, cert_file) + updated_vecs_certs = vecs.get_certificate(vecs_store, vecs_alias) + if updated_vecs_certs: + print_text("{:<6}IOFILTER provider certificate created!".format(SPACE)) + logger.info("vecs certificate Created") + else: + print_text_warning("Unable to re-create the IOFILTER provider certificate in VECS!") + logger.info("Unable to create vecs certificate") + return True + else: + print_text_warning("Unable to obtain host's {} SSL certificate on port 443!".format(access_string)) + logger.info("Unable to obtain host's {} SSL certificate on port 443!".format(access_string)) + + return False + + +def display_host_certificate_details(host_hash): + output_format = '%b %e %H:%M:%S %Y GMT' + + x509_cert = get_x509_certificate(host_hash) + host_cert_subject, host_cert_issuer = get_subject_and_issuer_dn(x509_cert) + host_cert_valid_start = get_certificate_start_date(x509_cert).strftime(output_format) + host_cert_valid_end = get_certificate_end_date(x509_cert).strftime(output_format) + host_cert_fingerprint = get_certificate_fingerprint(x509_cert, 'sha1') + host_cert_algorithm = x509_cert.get_signature_algorithm().decode('utf-8') + host_cert_san = get_subject_alternative_names(x509_cert) + + host_cert_list = [] + for certificate in split_certificates_from_pem(host_hash): + x509_certificate = get_x509_certificate(certificate) + host_cert_subject, host_cert_issuer = get_subject_and_issuer_dn(x509_certificate) + host_cert_list.append([host_cert_subject, host_cert_issuer]) + + print_text("{:<3}Issuer: {}".format(SPACE, host_cert_issuer)) + print_text("{:<3}Subject: {}".format(SPACE, host_cert_subject)) + print_text("{:<6}Not Before: {}".format(SPACE, host_cert_valid_start)) + print_text("{:<6}Not After: {}".format(SPACE, host_cert_valid_end)) + print_text("{:<6}SHA1 Fingerprint: {}".format(SPACE, host_cert_fingerprint)) + print_text("{:<6}Signature Algorithm: {}".format(SPACE, host_cert_algorithm)) + print_text("{:<6}Subject Alternative Name entries:".format(SPACE)) + if host_cert_san: + for entry in host_cert_san.split(', '): + print_text("{:<9}|_{}".format(SPACE, entry)) + + print_text("{:<6}Certificates in the rui.crt file:".format(SPACE)) + if host_cert_list: + i=1 + for subject, issuer in host_cert_list: + if i > 1: + print_text('{:<9}|'.format(SPACE)) + print_text("{:<9}|_Subject={} ".format(SPACE, subject)) + print_text("{:<9}|_Issuer={} ".format(SPACE, issuer)) + i += 1 + + return host_cert_fingerprint + + +def display_host_certificate_for_cert_management_thumbprint(access_string, host_cert_fingerprint): + env = Environment.get_environment() + + store = "SMS" + alias = "https:/{}:9080/version.xml".format(access_string) + pem_cert = vecs.get_certificate(store, alias) + current_host_sms_thumbprint = get_certificate_fingerprint(get_x509_certificate(pem_cert)) + cert_file = "{}/{}.crt".format(env.get_value('TEMP_DIR'), access_string) + + if current_host_sms_thumbprint: + print_text("{:<6}Host IOFILTER provider found in VECS, checking certificate...".format(SPACE)) + print(flush=True) + if current_host_sms_thumbprint != host_cert_fingerprint: + print_task_status_warning("Mismatch found, re-creating entry...") + + if delete_and_validate_vecs_entry(store, alias): + logger.info("vecs cert deleted. Will recreate.") + create_vecs_certificate(access_string, cert_file, store, alias) + else: + print_task_status_warning("Unable to delete the IOFILTER provider certificate from VECS!") + else: + print_task_status("Certificates match. No need to update") + else: + print_text("{:<3}Host IOFILTER provider certificate not found in VECS. Creating entry...".format(SPACE)) + print(flush=True) + logger.info("Attempting to create vecs certificate") + create_vecs_certificate(access_string, cert_file, store, alias) + + +def url_request(method, url, username=None, password=None, data=None): + # we use plain curl instead of requests library for automatic replay support in CommandRunner + env = Environment.get_environment() + temp_dir = env.get_value('TEMP_DIR') + output_file = "{}/url_request_{}_{}_output.dat".format(temp_dir, method, get_timestamp()) + args = ['curl', '-k', '-X', method, url, '-o', output_file, '-s', '-w', '%{http_code}'] + if username and password: + args.extend(['-u', "{}:{}".format(username, password)]) + if data: + data_file = "{}/url_request_{}_{}_data.dat".format(temp_dir, method, get_timestamp()) + save_text_to_file(data, data_file) + args.extend(['--data-binary', '@{}'.format(data_file)]) + status_code = CommandRunner(*args).run_and_get_output() + output = get_file_contents(output_file) + return status_code, output + + +def update_ca_store(access_string, password, vcenter_machine_ssl_cert, sps_cert): + env = Environment.get_environment() + url_text = "https://{}/host/castore".format(access_string) + status_code, content = url_request('GET', url_text, 'root', password) + + logger.info("Getting castore.pem file from host {}".format(access_string)) + logger.info("Response from host {} - HTTP status code: {}".format(access_string, status_code)) + + if status_code == '200': + save_text_to_file(content, '{}/{}-castore.pem'.format(env.get_value('TEMP_DIR'), access_string)) + cert_list = split_certificates_from_pem(content) + + print_task("{:<6}vCenter Machine SSL cert: ".format(SPACE)) + if cert_list: + if check_for_ca_certs(vcenter_machine_ssl_cert, cert_list): + print_task_status("Trusted by host") + else: + print_task_status("Not trusted by host") + + print_task("{:<6}SPS service connection: ".format(SPACE)) + if check_for_ca_certs(sps_cert, cert_list): + print_task_status("Trusted by host") + else: + print_task_status("Not trusted by host (maybe)") + else: + print_task_status_warning(" No CA certs in /etc/vmware/ssl/castore.pem") + print("\n") + return True + elif status_code == '401': + print_task("{:<6}vCenter Machine SSL cert:".format(SPACE)) + print_task_status_warning("unknown (possible bad ESXi root password)") + print_task("{:<6}SPS service connection: ".format(SPACE)) + print_task_status_warning("unknown (possible bad ESXi root password)") + else: + print_task("{:<6}vCenter Machine SSL cert:".format(SPACE)) + print_task_status_warning(" unknown") + print_task("{:<6}SPS service connection: ".format(SPACE)) + print_task_status_warning("unknown") + + print("\n") + return False + + +def view_esxi_reverse_proxy_cert_status(host_rhttpproxy_cert, host_iofilterrvp_cert): + input_search_cert_list = find_files("/etc/vmware-vpx/docRoot/certs/*") + + search_cert_list = [] + for cert_file in input_search_cert_list: + pattern = re.compile(r".*\.r[0-9]*") + if not pattern.match(cert_file): + search_cert_list.append(cert_file) + + print_task("{:<6}Reverse Proxy cert (port 443): ".format(SPACE)) + certificate_list = [] + for search_cert in search_cert_list: + certificate_list.append(get_file_contents(search_cert)) + if check_for_ca_certs(host_rhttpproxy_cert, certificate_list): + print_task_status("Trusted by vCenter") + else: + print_task_status("Not trusted by vCenter") + + print_task("{:<6}IOFilter VASA provider cert (port 9080): ".format(SPACE)) + if host_iofilterrvp_cert: + certificate_list = [] + for search_cert in search_cert_list: + certificate_list.append(get_file_contents(search_cert)) + + if check_for_ca_certs(host_iofilterrvp_cert, certificate_list): + print_task_status("Trusted by vCenter") + else: + print_task_status("Not trusted by vCenter") + else: + print_task_status_warning("unknown") + + +def view_esxi_certificate_trust(access_string, certificate_management_mode, password): + + host_hash = get_certificate_from_host(access_string, 443) + + if host_hash: + host_cert_fingerprint = display_host_certificate_details(host_hash) + + print_text("\n{:<3}Certificate Trusts:".format(SPACE)) + if certificate_management_mode == "thumbprint": + display_host_certificate_for_cert_management_thumbprint(access_string, host_cert_fingerprint) + else: + host_rhttpproxy_cert = get_certificate_from_host(access_string, 443) + host_iofilterrvp_cert = get_certificate_from_host(access_string, 9080) + vcenter_machine_ssl_cert = vecs.get_certificate("MACHINE_SSL_CERT", "__MACHINE_CERT") + sps_cert = vecs.get_certificate("SMS", "sms_self_signed") + + view_esxi_reverse_proxy_cert_status(host_rhttpproxy_cert, host_iofilterrvp_cert) + update_ca_store(access_string, password, vcenter_machine_ssl_cert, sps_cert) + else: + print_task_status_warning("Unable to obtain SSL certificate from host {}".format(access_string)) + + +def check_for_ca_certs(cert, search_cert_list): + subject_keyids, fetcher = get_certificate_fetcher_from_list(search_cert_list) + + cert_path = build_certification_path(cert, subject_keyids, fetcher) + if cert_path: + return True + + return False + + +def check_esxi_certificate_against_vcdb(): + # Entry point for checking esxi vcenter certificate trust against VCDB + + num_hosts = vcdb.get_number_of_hosts() + + title = "Checking ESXi SSL Thumbprints Against vCenter Database" + text_color = ColorKey.YELLOW if num_hosts == 0 else ColorKey.GREEN + sub_title = "There are {}{}{} hosts connected to vCenter.\n".format(text_color, num_hosts, ColorKey.NORMAL) + + menu = Menu() + if not get_hosts(): + menu.add_menu_item('Return to the previous menu', Menu.run_navigation_return, is_hidden=False, key='R', + is_default=True) + else: + menu.set_menu_options(title, sub_title, input_text='Select an option [Return to the previous menu]: ') + menu.add_menu_item("Perform check on all hosts", check_certificate_for_all_esxi_hosts_against_vcdb) + menu.add_menu_item("Perform check on all hosts in a cluster", check_clustered_esxi_against_vcdb) + menu.add_menu_item("Perform check on single host", check_specific_esxi_certificate_against_vcdb) + menu.add_menu_item('Return to the previous menu', Menu.run_navigation_return, is_hidden=True, key='R', + is_default=True) + + menu.run() + + +def check_certificate_for_all_esxi_hosts_against_vcdb(): + hosts = get_hosts() + if hosts: + certificate_management_mode = get_certificate_management_mode() + access_strings = [] + for cluster_id, name, ip in hosts: + access_strings.append(get_host_name_or_ip(name, ip)) + check_esxi_against_vcdb(access_strings, certificate_management_mode) + + +def check_clustered_esxi_against_vcdb(): + cluster_no_dict, cluster_dict = get_clusters() + input_cluster_id = get_input_cluster_id(cluster_no_dict, cluster_dict) + + hosts_list = get_hosts() + host_and_cluster_id_dict = get_host_and_cluster() + hosts_in_cluster = find_hosts_in_cluster(input_cluster_id, hosts_list, host_and_cluster_id_dict) + + if hosts_in_cluster: + certificate_management_mode = get_certificate_management_mode() + access_strings = [] + for cluster_id, name, ip in hosts_in_cluster: + access_strings.append(get_host_name_or_ip(name, ip)) + check_esxi_against_vcdb(access_strings, certificate_management_mode) + + +def check_specific_esxi_certificate_against_vcdb(): + certificate_management_mode = get_certificate_management_mode() + host_name, host_ip = get_host_details(get_hosts()) + host_name_or_ip = get_host_name_or_ip(host_name, host_ip) + check_esxi_against_vcdb([host_name_or_ip], certificate_management_mode) + + +def check_esxi_against_vcdb(access_strings, certificate_management_mode): + if certificate_management_mode == "thumbprint": + print_task_status_warning("The Certificate Management mode has been set to thumbprint") + else: + for access_string in access_strings: + print_text("\n{}{}{}".format(ColorKey.CYAN, access_string, ColorKey.NORMAL)) + view_esxi_against_vcdb(access_string) + + +def get_ssl_thumbprint(access_string): + ssl_thumbprint_str = vcdb.get_ssl_thumbprint_from_vcdb(access_string) + if ssl_thumbprint_str == "": + print_text_warning("No ssl thumbprints available") + return ['', ''] + + ssl_thumbprint = [] + for thumbprint_info in ssl_thumbprint_str: + expected_ssl_thumbprint = thumbprint_info.split("|")[0].strip() + host_ssl_thumbprint = thumbprint_info.split("|")[1].strip() + ssl_thumbprint = [expected_ssl_thumbprint, host_ssl_thumbprint] + return ssl_thumbprint + + +def view_esxi_against_vcdb(access_string): + expected_ssl_thumbprint, host_ssl_thumbprint = get_ssl_thumbprint(access_string) + pem_certs = get_certificate_from_host(access_string, 443) + certs = "" + if pem_certs: + certs = split_certificates_from_pem(pem_certs) + + actual_ssl_thumbprint = get_certificate_fingerprint(get_x509_certificate(certs[0])) + + if expected_ssl_thumbprint: + print_text("{:<3}Expected Thumbprint (VCDB) : {}".format(SPACE, expected_ssl_thumbprint)) + else: + print_task_status_warning("Expected Thumbprint missing") + + if host_ssl_thumbprint: + print_text("{:<3}Host SSL Thumbprint (VCDB) : {}".format(SPACE, host_ssl_thumbprint)) + else: + print_task_status_warning("Host SSL Thumbprint missing") + + if actual_ssl_thumbprint: + print_text("{:<3}Actual Thumbprint (openssl) : {} ".format(SPACE, actual_ssl_thumbprint)) + if expected_ssl_thumbprint != actual_ssl_thumbprint and host_ssl_thumbprint != actual_ssl_thumbprint \ + and expected_ssl_thumbprint != host_ssl_thumbprint: + print_text("{:<3}Status : {}MISMATCH{}".format(SPACE, ColorKey.YELLOW, ColorKey.NORMAL)) + else: + print_text("{:<3}Status : {}MATCH{}".format(SPACE, ColorKey.GREEN, ColorKey.NORMAL)) + else: + print_text( + "{:<3}Actual Thumbprint (openssl) : {}Cannot connect to host{}".format(SPACE, ColorKey.YELLOW, ColorKey.NORMAL)) + print_text("{:<3}Status : {}UNKNOWN{}".format(SPACE, ColorKey.YELLOW, ColorKey.NORMAL)) + + +def replace_esxi_certificate(): + """ + Entry point for checking esxi vcenter certificate trust + """ + num_hosts = vcdb.get_number_of_hosts() + + title = "Replace ESXi certificate" + text_color = ColorKey.YELLOW if num_hosts == 0 else ColorKey.GREEN + sub_title = "There are {}{}{} hosts connected to vCenter.\n".format(text_color, num_hosts, ColorKey.NORMAL) + + menu = Menu() + + if not get_hosts(): + menu.add_menu_item('Return to the previous menu', Menu.run_navigation_return, is_hidden=False, key='R', + is_default=True) + else: + menu.set_menu_options(title, sub_title, input_text='Select an option [Return to the previous menu]: ') + menu.add_menu_item("Generate Certificate Signing Request and Private Key", + generate_cert_signing_request_non_custom) + menu.add_menu_item("Generate Certificate Signing Request and Private Key from custom OpenSSL configuration " + "file", generate_cert_signing_request_with_custom_openssl) + menu.add_menu_item("Import CA-signed certificate and key", import_ca_signed_cert) + menu.add_menu_item('Return to the previous menu', Menu.run_navigation_return, is_hidden=True, key='R', + is_default=True) + menu.run() + + +def get_esxi_cn_input(): + esxi_cn_input = "" + while not esxi_cn_input: + esxi_cn_input = MenuInput("Enter a value for the {}CommonName{} of the certificate: ".format( + ColorKey.CYAN, ColorKey.NORMAL), allow_empty_input=False, case_insensitive=False).get_input() + + logger.info("The entered esxi cn input : {}".format(esxi_cn_input)) + if not esxi_cn_input: + print_text("Please enter valid input") + continue + print() + return esxi_cn_input + + +def generate_cert_signing_request_non_custom(): + generate_cert_signing_request(True) + + +def generate_cert_signing_request_with_custom_openssl(): + generate_cert_signing_request(False) + + +def generate_csr_and_private_key(esxi_cn_input, custom_openssl_config=False): + """ + Entry point for CSR generation + :param esxi_cn_input: esxi cn input + :param custom_openssl_config: True if a custom OpenSSL config is used + """ + + clear_csr_info() + + env = Environment.get_environment() + request_dir = env.get_value('REQUEST_DIR') + timestamp = get_timestamp() + csr_file = "{}/{}-{}.csr".format(request_dir, esxi_cn_input, timestamp) + key_file = "{}/{}-{}.key".format(request_dir, esxi_cn_input, timestamp) + if custom_openssl_config: + logger.info('User has chosen to generate the ESXi SSL private key and CSR from a custom OpenSSL ' + 'configuration file') + user_input = MenuInput('Enter path to custom OpenSSL configuration file: ', allow_empty_input=False, + case_insensitive=False) + print() + while True: + config_file = user_input.get_input() + if not is_file_exists(config_file): + print_text_error('Error: file not found') + continue + break + else: + logger.info('User has chosen to generate the Esxi SSL private key and CSR') + hostname = env.get_value('HOSTNAME') + config_file = "{}/{}.cfg".format(request_dir, esxi_cn_input) + + csr_info = get_csr_info(esxi_cn_input) + san_entries = get_san_entries('ESXi', csr_info, esxi_cn_input) + generate_openssl_config(config_file, csr_info, esxi_cn_input, san_entries) + + try: + print_header("Replace ESXi Certificate") + print_task("Generating Certificate Signing Request") + generate_csr(config_file, csr_file, key_file) + # OpenSSL 1.0 on VC 7.x doesn't set the file permission correctly + set_file_mode(key_file, stat.S_IRUSR | stat.S_IWUSR) + except CommandExecutionError as e: + error_message = "Unable to generate Certificate Signing Request and Private Key: {}".format(str(e)) + logger.error(error_message) + raise OperationFailed(error_message) + + print_task_status("OK") + print() + print_text("Certificate Signing Request generated at {}{}{}".format(ColorKey.CYAN, csr_file, ColorKey.NORMAL)) + print_text("Private Key generated at {}{}{}".format(ColorKey.CYAN, key_file, ColorKey.NORMAL)) + print("\n") + + +def generate_cert_signing_request(generate_csr_and_private_key_option): + clear_csr_info() + + esxi_cn_input = get_esxi_cn_input() + + if generate_csr_and_private_key_option: + generate_csr_and_private_key(esxi_cn_input, False) + else: + generate_csr_and_private_key(esxi_cn_input, True) + + +def path_to_new_esxi_cert(): + file_path = "" + while file_path == "": + file_path = MenuInput("Enter path to new ESXi certificate: ", + allow_empty_input=False, case_insensitive=False).get_input() + + logger.info("Entered path to new ESXi certificate: {}".format(file_path)) + + if file_path == "" or not is_file_exists(file_path): + file_path = "" + print_text("Please enter valid input. File not found, enter path to new ESXi certificate:") + + return file_path + + +def get_valid_new_cert(): + cert_file = path_to_new_esxi_cert() + + pem_cert, pem_cert_key = detect_and_convert_to_pem(cert_file, allow_input=True) + + while is_ca_certificate(get_x509_certificate(pem_cert)): + print_task_status_warning("Provided certificate is designated as a Certificate Authority, and is " + "not an appropriate replacement.") + cert_file = path_to_new_esxi_cert() + pem_cert, pem_cert_key = detect_and_convert_to_pem(cert_file, allow_input=True) + + env = Environment.get_environment() + temp_dir = env.get_value('TEMP_DIR') + + pem_cert_file_name = "{}/{}-converted.pem".format(temp_dir, os.path.basename(cert_file[:cert_file.rfind('.')])) + + save_text_to_file(pem_cert, pem_cert_file_name) + logger.info("Provided new certificate file : {}".format(pem_cert_file_name)) + + pem_key_file_name = "{}/{}-converted.key".format(temp_dir, os.path.basename(cert_file[:cert_file.rfind('.')])) + logger.info("Provided new key file : {}".format(pem_key_file_name)) + + if not pem_cert_key: + cert_modulus = get_key_modulus_from_pem_text(pem_cert) + pem_cert_key = find_matched_private_key(cert_modulus, cert_file) + + if not pem_cert_key: + raise OperationFailed('Failed to obtain key file') + save_text_to_file(pem_cert_key, pem_key_file_name) + logger.info("Provided new certificate key file : {}".format(pem_key_file_name)) + + return pem_cert_file_name, pem_key_file_name, cert_file, pem_cert, pem_cert_key + + +def import_ca_signed_cert(): + logger.info("User has chosen to import a CA-signed ESXi SSL certificate and key") + host_name, host_ip = get_host_details(get_hosts()) + + host_name_or_ip = get_host_name_or_ip(host_name, host_ip) + password = get_host_password("Enter root password for ESXi host {}: ".format(host_name_or_ip)) + + pem_cert_file_name, pem_key_file_name, cert_file, pem_cert, pem_cert_key = get_valid_new_cert() + + verify_certificate_and_key(pem_cert_file_name, pem_key_file_name, pem_cert_file_name, None) + + ca_pem, ca_certs_pem, cert_pem_updated = obtain_ca_chain(pem_cert) + + env = Environment.get_environment() + temp_dir = env.get_value('TEMP_DIR') + pem_ca_file_name = "{}/{}-ca_file.pem".format(temp_dir, os.path.basename(cert_file[:cert_file.rfind('.')])) + + if cert_pem_updated is not None: + cert_pem = cert_pem_updated + save_text_to_file(cert_pem, pem_cert_file_name) + save_text_to_file(ca_pem, pem_ca_file_name) + + print_header("Replace ESXi Certificate") + + print_task('Publish CA signing certificates') + vmdir.publish_trusted_certificate(pem_ca_file_name) + try: + vecs.force_refresh() + print_task_status('OK') + except CommandExecutionError as e: + error_message = "Unable to perform a force-refresh of CA certificates to VECS: {}".format(str(e)) + logger.error(error_message) + raise OperationFailed(error_message) + + input_search_cert_list = find_files("/etc/vmware-vpx/docRoot/certs/*") + search_cert_list = [] + pattern = re.compile(r".*\.r[0-9]*") + for cert_file in input_search_cert_list: + if not pattern.match(cert_file): + search_cert_list.append(cert_file) + + env = Environment.get_environment() + temp_dir = env.get_value('TEMP_DIR') + overwrite_file = '{}/{}-castore.pem'.format(temp_dir, host_name_or_ip) + if find_files(overwrite_file): + save_text_to_file("", overwrite_file) + + certs_pem, aliases = vecs.get_all_ca_certificates() + + cert_text = "" + for cert_pem in certs_pem: + if is_ca_certificate(get_x509_certificate(cert_pem)): + cert_text += cert_pem + cert_text += '\n' + + cert_text += vecs.get_certificate('SMS', 'sms_self_signed') + + save_text_to_file(cert_text, overwrite_file) + + print_task("Replace ESXi certificate") + logger.info("Replacing ESXi certificate with {}".format(overwrite_file)) + url_text = "https://{}/host/ssl_cert".format(host_name_or_ip) + status_code, _ = url_request('PUT', url_text, 'root', password, data=pem_cert) + + if status_code == '200': + print_task_status("OK") + else: + error_message = "Unable to replace certificate, HTTP return code: {}".format(status_code) + print_task_status_error(error_message) + logger.error(error_message) + raise OperationFailed(error_message) + + print_task("Replace ESXi private key") + logger.info("Replacing ESXi private key with {}".format(pem_key_file_name)) + url_text = "https://{}/host/ssl_key".format(host_name_or_ip) + status_code, _ = url_request('PUT', url_text, 'root', password, data=pem_cert_key) + if status_code == '200': + print_task_status("OK") + else: + error_message = "Unable to replace private key, HTTP return code: {}".format(status_code) + print_task_status_error(error_message) + logger.error(error_message) + raise OperationFailed(error_message) + + print_task("Replace castore.pem") + logger.info("Replacing ESXi private key with {}".format(pem_key_file_name)) + url_text = "https://{}/host/castore".format(host_name_or_ip) + status_code, _ = url_request('PUT', url_text, 'root', password, data=cert_text) + if status_code == '200': + print_task_status("OK") + else: + error_message = "Unable to replace castore.pem, HTTP return code: {}\n".format(status_code) + print_task_status_error(error_message) + logger.error(error_message) + raise OperationFailed(error_message) + + addition_steps_text = """\nAdditional 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 + + + """ + + print_text_warning(addition_steps_text) diff --git a/vCert-6.1.1-20260401/operation/generate_report.py b/vCert-6.1.1-20260401/operation/generate_report.py new file mode 100644 index 0000000..f01ca9f --- /dev/null +++ b/vCert-6.1.1-20260401/operation/generate_report.py @@ -0,0 +1,432 @@ +# Copyright (c) 2024-2025 Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. +# and/or its subsidiaries. + +import datetime +import os +import pathlib +import re + +from OpenSSL.crypto import load_crl, dump_crl, FILETYPE_PEM, FILETYPE_TEXT + +from lib import certificate_utils as certutil +from lib import vcdb +from lib import vmdir +from lib import vecs +from lib.certificate_utils import get_x509_certificate +from lib.constants import ( + VCERT_DESC, VMCAM_CERT_FILE_PATH, RBD_CERT_FILE_PATH, RHTTPPROXY_CONFIG_FILE_PATH, + VMCA_CERT_FILE_PATH, REPORT_FILE_PATH +) +from lib.environment import Environment +from lib.exceptions import CommandExecutionError +from lib.console import init_env_colormap, print_text_warning, print_text_error +from lib.host_utils import get_file_contents +from lib.services import is_service_running +from lib.text_utils import TextFilter + +from operation.common import get_vcenter_extensions + + +def write_report(file, text, end='\n', sep=' '): + print(text, file=file, end=end, sep=sep) + print(text, end=end, sep=sep) + + +def generate_header(): + if not is_service_running('vmware-vpostgres'): + print_text_error( + 'The vPostgres service is stopped!\n' + 'Please ensure this service is running before generating a certificate report.\n' + 'Hint: Check the number of CRL entries in VECS') + raise CommandExecutionError('vPostgres is not running') + + report_path = pathlib.Path(REPORT_FILE_PATH) + if not os.path.exists(report_path.parent): + # this may necessary for remote execution on a restricted environment + report_file_path = str(pathlib.Path('/tmp').joinpath(report_path.relative_to('/'))) + else: + report_file_path = REPORT_FILE_PATH + + env = Environment.get_environment() + try: + file = open(report_file_path, 'w') + set_report_file(file) + except IOError as e: + error_message = "Failed to create report file {}: {}".format(REPORT_FILE_PATH, e) + print_text_error(error_message) + raise CommandExecutionError(error_message) + + env.set_value('COLORS_DISABLED', True) + init_env_colormap() + + write_report(file, '=' * 130) + write_report(file, 'SSL Certificate Report') + write_report(file, VCERT_DESC) + write_report(file, "Host: {}".format(env.get_value('HOSTNAME'))) + write_report(file, "Date: {}".format(datetime.datetime.now().ctime())) + write_report(file, "Version: {}".format(env.get_value('VC_VERSION'))) + write_report(file, "Build: {}".format(env.get_value('VC_BUILD'))) + write_report(file, "Machine ID: {}".format(env.get_value('MACHINE_ID'))) + write_report(file, "PNID: {}".format(env.get_value('PNID'))) + write_report(file, "Certificate Management Mode: {}".format(vcdb.get_certificate_management_mode())) + write_report(file, '=' * 130) + + +def report_certificate_details(file, pem_cert, options=None): + x509_cert = certutil.get_x509_certificate(pem_cert) + sign_alg = x509_cert.get_signature_algorithm().decode('utf-8') + subject_kid = certutil.get_subject_keyid(x509_cert) + extensions = certutil.get_certificate_extensions(x509_cert) + + report_certificate_basic(file, x509_cert) + write_report(file, " Signature Algorithm: {}".format(sign_alg)) + write_report(file, " Subject Key Identifier: {}".format(subject_kid)) + + write_report(file, ' Authority Key Identifier:') + auth_kid_list = get_authority_keyid_list(x509_cert) + if auth_kid_list: + for auth_key in auth_kid_list: + write_report(file, " |_{}".format(auth_key)) + else: + akid = certutil.get_authority_keyid(x509_cert) + if akid: + write_report(file, " |_keyid:{}".format(akid)) + + write_report(file, ' Key Usage:') + key_usage = extensions.get('keyUsage') + if key_usage: + for ku in key_usage.split(', '): + write_report(file, " |_{}".format(ku)) + + write_report(file, ' Extended Key Usage:') + ext_key_usage = extensions.get('extendedKeyUsage') + if ext_key_usage: + for ku in ext_key_usage.splitlines(): + write_report(file, " |_{}".format(ku)) + + write_report(file, ' Subject Alternative Name entries:') + san = extensions.get('subjectAltName') + if san: + for entry in san.split(', '): + if re.match('^[A-Za-z]+: ', entry): + entry = re.sub('^([A-Za-z]+:) ', '\\1', entry) + write_report(file, " |_{}".format(entry)) + + write_report(file, ' Other Information:') + write_report(file, " |_Is a Certificate Authority: {}" + .format('Yes' if certutil.is_ca_certificate(x509_cert) else 'No')) + + auth_keyid_alt = certutil.get_authority_keyid(x509_cert, allow_alternate=True) + subject_kids_in_vecs = get_subject_keyids_in_vecs() + subject_kids_in_vmdir = get_subject_keyids_in_vmdir() + found_in_vecs = auth_keyid_alt in subject_kids_in_vecs + found_in_vmdir = auth_keyid_alt in subject_kids_in_vmdir + if not found_in_vmdir and not found_in_vecs: + status = "No{}".format(' (Self-Signed)' if certutil.is_self_signed_certificate(x509_cert) + else '') + elif found_in_vmdir and not found_in_vecs: + status = 'Yes, in VMware Directory' + elif not found_in_vmdir and found_in_vecs: + status = 'Yes, in VECS' + else: + status = 'Yes, in both' + write_report(file, " |_Issuing CA in VMware Directory/VECS: {}".format(status)) + + if options: + if 'checkCurrentMachineSSLUsage' in options: + report_current_machine_ssl_usage(file) + if 'checkCurrentExtensionThumbprints' in options: + report_current_extension_thumbprints(file) + + +def report_certificate_basic(file, x509_cert): + subject_dn, issuer_dn = certutil.get_subject_and_issuer_dn(x509_cert) + output_format = '%b %e %H:%M:%S %Y GMT' + start_date = certutil.get_certificate_start_date(x509_cert).strftime(output_format) + end_date = certutil.get_certificate_end_date(x509_cert).strftime(output_format) + fingerprint = certutil.get_certificate_fingerprint(x509_cert) + + write_report(file, " Issuer: {}".format(issuer_dn)) + write_report(file, " Subject: {}".format(subject_dn)) + write_report(file, " Not Before: {}".format(start_date)) + write_report(file, " Not After : {}".format(end_date)) + write_report(file, " SHA1 Fingerprint: {}".format(fingerprint)) + + +def report_current_machine_ssl_usage(file): + machine_cert = certutil.get_certificate_from_host('localhost', 443) + machine_cert_fingerprint = certutil.get_certificate_fingerprint(get_x509_certificate(machine_cert)) \ + if machine_cert else '' + write_report(file, " |_Current certificate used by the reverse proxy: {}" + .format(machine_cert_fingerprint)) + + vpxd_cert = certutil.get_certificate_from_host('localhost', 8089) + vpxd_cert_fingerprint = certutil.get_certificate_fingerprint(get_x509_certificate(vpxd_cert)) \ + if vpxd_cert else '' + write_report(file, " |_Current certificate used by vCenter (vpxd) : {}" + .format(vpxd_cert_fingerprint)) + + +def report_current_extension_thumbprints(file): + vcenter_extensions = get_vcenter_extensions() + max_len = len(max(vcenter_extensions, key=len)) + + write_report(file, ' |_Thumbprints in VCDB for extensions that should use the vpxd-extension certificate') + + for extension in vcenter_extensions: + thumbprint = vcdb.get_extension_thumbprint(extension) + write_report(file, " |_{:<{}}: {}".format(extension, max_len, thumbprint)) + + +def report_crl_details(file, pem_text): + crl = load_crl(FILETYPE_PEM, pem_text) + output = dump_crl(FILETYPE_TEXT, crl).decode('utf-8') + issuer = TextFilter(output).contain('Issuer:').cut('Issuer:', [1]).get_text().strip() + if issuer.startswith('/'): + issuer = re.sub('/([A-Z]+=)', ', \\1', issuer[1:]) + last_update = TextFilter(output).contain('Last Update:').cut('Update:', [1]).get_text().strip() + next_update = TextFilter(output).contain('Next Update:').cut('Update:', [1]).get_text().strip() + sign_alg = TextFilter(output).contain('Signature Algorithm:').head(1).cut('Algorithm:', [1]).get_text().strip() + write_report(file, " Issuer: {}".format(issuer)) + write_report(file, " Last Update: {}".format(last_update)) + write_report(file, " Next Update: {}".format(next_update)) + write_report(file, " Signature Algorithm: {}".format(sign_alg)) + + +def get_authority_keyid_list(x509_cert): + """ + Get Authority KeyId list from X509 certificate + + :param x509_cert: X509Certificate object + :return: list of as keyId:, or DirName:.../serial: + """ + extensions = certutil.get_certificate_extensions(x509_cert) + akid = extensions.get('authorityKeyIdentifier') + if akid: + return TextFilter(akid).match('^keyid:.*|^DirName:.*|^serial:.*').get_lines() + else: + return [] + + +def get_subject_keyids_in_vecs(pem_certs=None): + env = Environment.get_environment() + subject_kids = env.get_value('SUBJECT_KEYIDS_IN_VECS') + if subject_kids is not None: + return subject_kids + if pem_certs is None: + pem_certs, _ = vecs.get_all_ca_certificates() + subject_kids = [] + for pem_cert in pem_certs: + x509_cert = get_x509_certificate(pem_cert) + subject_kids.append(certutil.get_subject_keyid(x509_cert, allow_alternate=True)) + + env.set_value('SUBJECT_KEYIDS_IN_VECS', subject_kids) + return subject_kids + + +def get_subject_keyids_in_vmdir(pem_certs=None): + env = Environment.get_environment() + subject_kids = env.get_value('SUBJECT_KEYIDS_IN_VMDIR') + if subject_kids is not None: + return subject_kids + if pem_certs is None: + pem_certs, _ = vmdir.get_all_ca_certificates() + subject_kids = [] + for pem_cert in pem_certs: + x509_cert = get_x509_certificate(pem_cert) + subject_kids.append(certutil.get_subject_keyid(x509_cert, allow_alternate=True)) + + env.set_value('SUBJECT_KEYIDS_IN_VMDIR', subject_kids) + return subject_kids + + +def generate_report_ca_certs_in_vmdir(): + file = get_report_file() + write_report(file, 'VMware Directory Certificates') + write_report(file, ' CA Certificates') + pem_certs, aliases = vmdir.get_all_ca_certificates() + get_subject_keyids_in_vmdir(pem_certs) + get_subject_keyids_in_vecs() + for pem_cert, alias in zip(pem_certs, aliases): + write_report(file, " CN(id): {}".format(alias)) + report_certificate_details(file, pem_cert) + + +def generate_report_ca_certs_in_vecs(): + file = get_report_file() + write_report(file, 'VECS Certificates') + get_subject_keyids_in_vecs() + get_subject_keyids_in_vmdir() + for store in vecs.get_store_list(): + if store == 'APPLMGMT_PASSWORD': + continue + write_report(file, " Store: {}".format(store)) + pem_certs, aliases = vecs.get_all_certificates(store) + for pem_text, alias in zip(pem_certs, aliases): + write_report(file, " Alias: {}".format(alias)) + if store == 'TRUSTED_ROOT_CRLS': + report_crl_details(file, pem_text) + else: + if store == 'MACHINE_SSL_CERT' and alias == '__MACHINE_CERT': + options = ['checkCurrentMachineSSLUsage'] + elif store == 'vpxd-extension' and alias == 'vpxd-extension': + options = ['checkCurrentExtensionThumbprints'] + else: + options = None + report_certificate_details(file, pem_text, options) + + +def generate_report_solution_user_certs_in_vmdir(): + file = get_report_file() + write_report(file, ' Service Principal (Solution User) Certificates') + solution_users = vmdir.get_solution_users() + machine_id = Environment.get_environment().get_value('MACHINE_ID') + for solution_user in solution_users: + write_report(file, " Service Principal: {}-{}".format(solution_user, machine_id)) + pem_cert = vmdir.get_solution_user_certificate(solution_user) + report_certificate_details(file, pem_cert) + + +def generate_report_sts_tenant_certs(): + file = get_report_file() + write_report(file, ' Single Sign-On Secure Token Service Certificates') + tenant_certs = vmdir.get_sts_tenant_certificates() + for tenant in tenant_certs.keys(): + certs = tenant_certs[tenant] + for cert in certs: + x509_cert = get_x509_certificate(cert) + cert_type = 'CA Certificate' if certutil.is_ca_certificate(x509_cert) else 'Signing Certificate' + write_report(file, " {} {}".format(tenant, cert_type)) + report_certificate_details(file, cert) + + +def generate_report_certs_in_filesystem(): + file = get_report_file() + write_report(file, 'Filesystem Certificates') + + write_report(file, ' VMCA Certificate') + write_report(file, " Certificate: {}".format(VMCAM_CERT_FILE_PATH)) + pem_cert = get_file_contents(VMCA_CERT_FILE_PATH) + report_certificate_details(file, pem_cert) + + write_report(file, ' Authentication Proxy Certificate') + write_report(file, " Certificate: {}".format(VMCAM_CERT_FILE_PATH)) + pem_cert = get_file_contents(VMCAM_CERT_FILE_PATH) + report_certificate_details(file, pem_cert) + + write_report(file, ' Auto Deploy CA Certificate') + write_report(file, " Certificate: {}".format(RBD_CERT_FILE_PATH)) + pem_cert = get_file_contents(RBD_CERT_FILE_PATH) + report_certificate_details(file, pem_cert) + + config = get_file_contents(RHTTPPROXY_CONFIG_FILE_PATH) + cert_file = TextFilter(config).match('.*.*.*')\ + .set_options(invert_match=True).contain('