« Video: "6 Months In: What I've Learned About Apache Projects" | Main | Slides from my CloudStack Collab Talk: 6 Months In: What I've Learned about Apache Projects »
Wednesday
Jan022013

Undocumented Feature: Using Certificate Chains in CloudStack

In deploying CloudStack to production, one of the challenges that I bumped into is the need to provide a full certificate chain for the console proxy VM's certificate. This situation happens when the certificate you are going to use comes from an intermediate CA, and not one of the trusted root CAs. For browsers to trust the site's certificate, they need to be provided the full chain (root CA to intermediate CA to site certificate). There is a good description of how chains work here.

The CloudStack UI doesn't support adding custom root and intermediate CA certificates to the system, but the API does (although it isn't clearly documented).

For this walkthrough, I'm using the CloudStack Python API wrapper from Jason Hancock (@jsnby) (available on GitHub here). The relevant API call is uploadCustomCertificate.

To get all of the levels of the certificate chain into the system, you need to make API calls for each level of the chain. In this example, there are three levels: root, intermediate and server certificate. The important API parameter is the id param. It's what allows a newly uploaded certificate to assume it's rightful place in the chain, as the system constructs the keystore for the system VMs to use.

The script below walks through the three API call's that are needed:

#!/usr/bin/python

import CloudStack

# These are specific to you!
api = 'http://localhost:8083/client/api'
apikey = 'yourkey'
secret = 'yoursecret'

# Open a client session
cloudstack = CloudStack.Client(api, apikey, secret)

print 'Loading Root CA certificate'
result = cloudstack.uploadCustomCertificate({
    'id':'1',
    'certificate':'your root cert',
    'domainsuffix':'yourdomain.com', 
    'name':'root' })
print result

print 'Loading intermediate CA certificate'
result = cloudstack.uploadCustomCertificate({
    'id':'2', 
    'certificate':'your intermediate ca cert',
    'domainsuffix':'yourdomain.com',
    'name':'intermediate_ca' })
print result

print 'Loading your site certificate and private key'
result = cloudstack.uploadCustomCertificate({
    'certificate':'your site certificate',
    'privatekey':'your site private key',
    'domainsuffix':'yourdomain.com'})
print result

The certificate and private key parameters need to have the full text of the objects in question (in PEM encoded format, i.e.: text format), including all newline and header/footer text. The first time I uploaded my certificates, I left out the newline characters. While it worked on the console proxy VMs, the secondary storage VMs had problems working with the generated keystore.

For example, your certificate string should look something like this:

'certificate':'-----BEGIN CERTIFICATE-----\nMIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwasdfKasdfsjANBgkqhkiG9w0BAQUFADA6\nMRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJp\ndHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAX\nBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJTQSBTZWN1cml0eSAy\nMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A455BCgKCAQEAt49VcdKA3Xtp\neafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7Jylg\n/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGl\nwSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnh\nAMFRD0xS+ARaqn1y07iHKrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2\nPcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpu\nAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB\nBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NR\nMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYc\nHnmYv/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/\nZb5gEydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+\nf00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVO\nrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEkllgVsRch\n6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kApKnXwiJPZ9d3\n7CAFYd4=\n-----END CERTIFICATE-----'

After running the script, three async job IDs will be returned. You can either use the API to get the status of the jobs, or review them via the UI. As part of the certificate upload process, the relevant system VMs should restart and download / use the new certificate chain (You may have to manually restart them).