Securing OpenStack Client Connections Part 1
December 18, 2012 at 12:12 PM | categories: openstack, devstack
We all know that the difference between https and http is the addition of encryption, right? Of course, but less attention is paid to the other purposes of SSL and TLS: to verify one or both of the parties involved in the connection and to validate that the objects used in the verification meet certain criteria. [1] In the common case of a user directing a web browser to a 'secure' site, only one side is potentially validated, that being the server. Maybe.
Browsers generally go a good job of performing server certificate verification and validation but other https clients may not do so well. Sometimes the web browser will pop up a box saying that the server is untrusted for one reason or another. This can be the result of a failure to validate the server certificate or verifying that the name used to reach the server matches who the server claims to be. It will usually contain the reason for the failure but generally it also contains a button to click on that allows the connection to proceed. This effectively neuters the validation process and opens the door for server spoofing and man-in-the-middle attacks.
The point here is not to rehash the merits of that behaviour but to investigate the use of https in the OpenStack Python clients and to validate their certificate verification. And validation. Mmmmmm...recursion...
But First
Before we can delve into the world of Python modules and SSL wrappers we must first have a way to test against some known secure servers. This means uncorking some OpenSSL-foo and entering the dark world of certificate authorities (CA). "But that's been covered to death!" you may say. Yes it has [2], but generally only to the level of self-sigining a cert for use in private servers. I believe the common name for these certs is 'snakeoil'. However, certificates in the real world are not only self-signed, but often signed by a CA that is itself signed by one or more parent CAs. The root CA certificates are included in the lists shipped with most operating systems, browsers and even some programming libraries.
Our goal then is to build a server certificate that is a) signed by an intermediate CA and b) has the basic attributes and extensions common in real-world certificates. To get that we need a root CA and an intermediate CA. To get that we need some shell scripts and OpenSSL configuration files for each operation.
Note that in this exercise we do not encrypt the private keys as this is all used for development and testing of the SSL/TLS connections and should not be used for production.
Root CA
A self-signed certificate has the unique characteristic of the Issuer matches the Subject. Creating a self-signed root CA is a common operation and has been covered many times elsewhere. So here are the basics:
Create the root CA directory structure:
for i in certs crl newcerts private; do mkdir -p CA/root-ca/$i done chmod 710 CA/root-ca/private echo "01" >CA/root-ca/serial touch CA/root-ca/index.txt
Generate the root CA configuration file CA/root-ca/ca.conf:
echo ' [ ca ] default_ca = CA_default [ CA_default ] dir = ./CA/root-ca policy = policy_match database = $dir/index.txt serial = $dir/serial certs = $dir/certs crl_dir = $dir/crl new_certs_dir = $dir/newcerts certificate = $dir/cacert.pem private_key = $dir/private/cacert.key RANDFILE = $dir/private/.rand default_md = default [ req ] default_bits = 1024 default_md = sha1 prompt = no distinguished_name = ca_distinguished_name x509_extensions = ca_extensions [ ca_distinguished_name ] organizationName = Example Inc. organizationalUnitName = Certificate Authority emailAddress = ca@example.com commonName = Example Inc Root CA [ policy_match ] countryName = optional stateOrProvinceName = optional organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional [ ca_extensions ] basicConstraints = critical,CA:true subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always, issuer keyUsage = cRLSign, keyCertSign ' >CA/root-ca/ca.conf
The ca_distinguished_name section defines the full name of the root CA.
Generate a private key and self-signed certificate:
openssl req -config CA/root-ca/ca.conf \ -x509 \ -nodes \ -newkey rsa \ -days 21360 \ -keyout CA/root-ca/private/cacert.key \ -out CA/root-ca/cacert.pem \ -outform PEM
Take a peek at the root certificate:
openssl x509 -noout -text -in CA/root-ca/cacert.pem
Intermediate CA
Creating an Intermediate CA is very similar except the root CA must do the signing so the process takes a couple of additional steps:
Create the intermediate CA directory structure:
for i in certs crl newcerts private; do mkdir -p CA/int-ca/$i done chmod 710 CA/int-ca/private echo "01" >CA/int-ca/serial touch CA/int-ca/index.txt
Generate the intermediate CA configuration file CA/int-ca/ca.conf:
echo ' [ ca ] default_ca = CA_default [ CA_default ] dir = ./CA/int-ca policy = policy_match database = $dir/index.txt serial = $dir/serial certs = $dir/certs crl_dir = $dir/crl new_certs_dir = $dir/newcerts certificate = $dir/cacert.pem private_key = $dir/private/cacert.key RANDFILE = $dir/private/.rand default_md = default [ req ] default_bits = 1024 default_md = sha1 prompt = no distinguished_name = ca_distinguished_name x509_extensions = ca_extensions [ ca_distinguished_name ] organizationName = Example Inc. organizationalUnitName = Certificate Authority emailAddress = ca@example.com commonName = Example Inc Intermediate CA [ policy_match ] countryName = optional stateOrProvinceName = optional organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional [ ca_extensions ] basicConstraints = critical,CA:true subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always, issuer keyUsage = cRLSign, keyCertSign ' >CA/int-ca/ca.conf
Note that the commonName in the ca_distinguished_name section is different than the root CA. The ca_extensions section is also critical here, specifically the keyUsage attribute containing the keyCertSign so the certificate signed can be used itself to sign other certificates.
Generate a private key and certificate signing request:
openssl req -config CA/int-ca/ca.conf \ -sha1 \ -nodes \ -newkey rsa \ -keyout CA/int-ca/private/cacert.key \ -out CA/int-ca/int-ca.csr \ -outform PEM
Sign the CSR:
openssl ca -config CA/root-ca/ca.conf \ -extensions ca_extensions \ -days 365 \ -notext \ -in CA/int-ca/int-ca.csr \ -out CA/int-ca/cacert.pem \ -batch
Create a trust chain, a file that contains the CA certificates from the immediate signing CA back to the root CA:
cat CA/root-ca/cacert.pem CA/int-ca/cacert.pem >>CA/int-ca/ca-chain.pem
Take a peek at the intermediate certificate:
openssl x509 -noout -text -in CA/int-ca/cacert.pem
Server Certificate
This process is repeated for every server that needs a certificate.
Generate the intermediate CA signing configuration file CA/int-ca/sign.conf:
echo ' [ ca ] default_ca = CA_default [ CA_default ] dir = ./CA/int-ca policy = policy_match database = $dir/index.txt serial = $dir/serial certs = $dir/certs crl_dir = $dir/crl new_certs_dir = $dir/newcerts certificate = $dir/cacert.pem private_key = $dir/private/cacert.key RANDFILE = $dir/private/.rand default_md = default [ req ] default_bits = 1024 default_md = sha1 prompt = no distinguished_name = req_distinguished_name x509_extensions = req_extensions [ req_distinguished_name ] organizationName = Example Inc. [ policy_match ] countryName = optional stateOrProvinceName = optional organizationName = match organizationalUnitName = optional commonName = supplied [ req_extensions ] basicConstraints = CA:false subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always, issuer keyUsage = digitalSignature, keyEncipherment, keyAgreement extendedKeyUsage = serverAuth, clientAuth subjectAltName = $ENV::SUBJECT_ALT_NAME ' >CA/int-ca/sign.conf
The primary difference is in the req_extensions section setting up for the certificates signed by this CA. One of the bits often missed is the inclusion of the subjectAltName that matches the commonName and optionally includes additional names that the certificate is valid for.
Generate a signing request:
openssl req \ -sha1 \ -nodes \ -newkey rsa \ -keyout CA/int-ca/private/cert.example.com.key \ -out CA/int-ca/cert.example.com.csr \ -subj '/O=Example Inc./OU=Servers/CN=cert.example.com'
Sign the CSR, with an optional value for subjectAltName:
SUBJECT_ALT_NAME="DNS: cert.example.com" \ openssl ca -config CA/int-ca/sign.conf -extensions req_extensions -days 365 -notext \ -out CA/int-ca/cert.example.com.crt \ -in CA/int-ca/cert.example.com.csr \ -batch
Setting the SUBJECT_ALT_NAME environment variable is the easiest way to set subjectAltName without rewriting the config file for each certificate.
Take a peek at the server certificate:
openssl x509 -noout -text -in CA/int-ca/cert.example.com.pem
Setting Up the Server
Server configurations differ but most will require the server certificate fiel created in the last step, the provate key file created in the CSR step and the CA chain file created in the previous section. This is where encrypting the private key can make testing difficult and why we did not do that here.
Later installations in this series will demonstrate how to do this using the SSL/TLS proxy server stud.
[1] | Find more about http vs https |
[2] | Find more about creating a CA with OpenSSL |