vmware / vsphere-automation-sdk-python

Python samples, language bindings, and API reference documentation for vSphere, VMC, and NSX-T using the VMware REST API
MIT License
746 stars 313 forks source link

Import a self-signed certificate in vCenter from a host/server #329

Closed VedaNiks closed 1 year ago

VedaNiks commented 2 years ago

Is your feature request related to a problem? Please describe.

I want to import a self-signed certificate in vCenter. Please see this kb article for more info: kb article

Describe the solution you'd like

I want to know if it is possible to import a self-signed certificate on a vCenter using an API call or a method call.

Describe alternatives you've considered

Given below are the two functions used on vCenter server to import a self-signed certificate from a host/server. We have to provide the IP adddress/FQDN of the host/server to the function and it imports the certificate present on the host/server into vCenter. The problem with this method is I have to SSH to vCenter and run this command manually. I am trying to automate this procedure.

def getCertHash(args):
    '''
    Utility function to get the hash of certificate of a server

    @param args: Namespace object containing parsed arguments

    @return The hash of certificate of the server
    '''

    server = args.server
    port = str(args.port)

    # Step 1: Get the cert of server
    command = ['openssl',
               's_client',
               '-connect', server + ':' + port,
               '-showcerts']

    logging.info("Run command: '%s'", COMMAND_SEP.join(command))
    rc, cert, error = run_command(command, quiet=True)
    if rc:
        msg = "Failed to get the cert from " + server + ". err: " + error
        logging.error(msg)
        raise LoggedException(msg)
    logging.info("Got the cert from %s successfully", server)

    # Step 2: Save the cert into a temporary file
    try:
        with open(SELF_SIGNED_CRT, 'w') as f:
            f.write(cert)
            logging.info("Saved the cert to %s", SELF_SIGNED_CRT)
    except Exception as e:
        msg = "Failed to save the cert to " + SELF_SIGNED_CRT + \
              ". err: " + str(e)
        logging.error(msg)
        raise LoggedException(msg)

    # Step 3: Calculate the hash of cert
    command = ['openssl',
               'x509',
               '-hash',
               '-noout',
               '-in', SELF_SIGNED_CRT]

    logging.info("Run command: '%s'", COMMAND_SEP.join(command))
    rc, hash, error = run_command(command, quiet=True)
    if rc:
        msg = "Failed to calculate the hash of cert. err: " + error
        logging.error(msg)
        raise LoggedException(msg)

    # Step 4: Trim and return the hash
    hash = hash.strip()
    logging.info("Calculated the hash of cert: %s", hash)
    return hash

def installCert(args):
    '''
    Utility function to install the certificate of a server on VC

    @param args: Namespace object containing parsed arguments

    @return 0 if succeeded or error code if failed.
    '''

    server = args.server
    logging.info("installCert called")

    # Get the hash of cert
    try:
        hash = getCertHash(args)
    except Exception as e:
        logging.error("Failed to get the hash of cert. err: %s", str(e))
        return ERR_GET_CERT_HASH
    destCertPath = os.path.join(CERTS_DIR, \
                                VUM_CERT_PREFIX + hash + VUM_CERT_EXT)

    # Copy the cert to CERTS_DIR
    try:
        shutil.copy2(SELF_SIGNED_CRT, destCertPath)
        logging.info("Copied the cert to %s", destCertPath)
    except Exception as e:
        logging.error("Failed to copy the cert to %s. err: %s", \
                      destCertPath, str(e))
        return ERR_COPY_CERT

    # Rehash CERTS_DIR to install the cert
    command = ['c_rehash',
               '-n',
               CERTS_DIR]

    logging.info("Run command: '%s'", COMMAND_SEP.join(command))
    rc, output, error = run_command(command, quiet=True)
    if rc:
        logging.error("Failed to install the cert. err: %s", error)
        return ERR_INSTALL_CERT

    msg = "Installed the certificate of " + server + " to vCenter successfully"
    logging.info(msg)
    print(msg)
    return 0

Additional context

Adding a new online depot to vCenter Life Cycle Manager from UI or via REST API, you see an error message similar to :

"Online Depot URL '<your-online-depot-URL>' is not valid or cannot be reached now."

This occurs because vCenter server does not trust the certificate present on the server. To resolve this, you have to manually import the server certificate using this kb: kb article

kunal-pmj commented 1 year ago

Hi @VedaNiks

The steps provided in the KB article are provided to add certificates to /etc/ssl/certs/ directory. Unfortunately, there is no direct API to add/remove certificate from this location.

There are APIs to manage VC Trusted roots. Perhaps, you can try following API to add self signed certificate to VECS. The trusted roots are synced to /etc/ssl/certs/ location.

https://developer.vmware.com/apis/vsphere-automation/latest/vcenter/api/vcenter/certificate-management/vcenter/trusted-root-chains/post/

Thanks Kunal

kunal-pmj commented 1 year ago

Hi @VedaNiks

Closing the issue. Reach back to us if you still have SDK related issue.

Thanks Kunal