306 lines
10 KiB
Python
306 lines
10 KiB
Python
# 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')
|