CA with easy SAN from terminal

homelab CA SAN SSL walkthrough

Allowing simple SAN requests with openssl on my intranet Certificate Authority.

Background

In my homelab, I have two hypervisors. One is an HP z820 running Proxmox, the other is a repurposed Google search appliance (Dell R710) running ovirt. I create VMs either to serve some purpose or just practice setting up and configuring the infrastructure. Lately I've been working on using ansible for bootstrapping/config management. I was working on a basic apache httpd setup, and decided to set up my own intranet Certificate Authority so I could have a single internal trusted root cert. I found this very nice (but a few years old) guide: OpenSSL Certificate Authority which was very easy to follow and I won't duplicate it here. However, once I was done (and I had imported my root cert on my device to test) I realized that Google Chrome still flagged certs from my CA as bad because they didn't have anything in the SAN (Subject Alternate Name) field. This post covers what I did to fix that.

Search results

Searching for solutions, most seem to involve creating or modifying an openssl.cnf file for each certificate as there was (note past tense) no way to add extensions from the openssl command line. Now, that wouldn't be hard, I do already have a nice openssl.cnf file from Jamie's tutorial, but it seems inelegant when all I want to do is add a single SAN line to my csr. Another option involves the use of environment variables, which I'm sure also works but still feels like a hack.

However, one of the stackoverflow posts I read pointed out that openssl recently added a new parameter, -addext, which does exactly what I want. (Commit)

So, in theory I can just do:

openssl req -config intermediate/openssl.cnf \
-key intermediate/private/www.example.com.key.pem \
-new -sha256 -addext "subjectAltName = DNS:[SAN]"    
-out intermediate/csr/www.example.com.csr.pem

And it will add the SAN. However… I'm on Centos 7, not exactly known for bleeding edge packages..

# openssl version
OpenSSL 1.0.2k-fips  26 Jan 2017

New OpenSSL

So I need to build a newer version. I found a nice guide at this site which I followed - abbreviated version here.

Dependencies:

sudo yum install libtool perl-core zlib-devel -y

Get and unzip the file:

curl -O -L https://www.openssl.org/source/openssl-1.1.1b.tar.gz
tar -xzvf openssl-1.1.1b.tar.gz
cd openssl*

Build, install:

./config --prefix=/usr/local/openssl --openssldir=/usr/local/openssl shared zlib
make
make test
make install

Then, create the file /etc/profile.d/openssl.sh with the contents:

pathmunge /usr/local/openssl/bin

At this point, it's installed, but you'll get a linking error, so create the file /etc/ld.so.conf.d/openssl.conf with contents:

/usr/local/openssl/lib

And now after a quick logout, we have a shiny new openssl:

# openssl version
OpenSSL 1.1.1b  26 Feb 2019

So that problem is solved.

Unique Subject

Now I go back and make a new csr with the -addext "subjectAltName = DNS:www.example.com" syntax, and it appears to be working, but when I go to sign it, I get an error:

ERROR:There is already a certificate for [cert details]

Since I don't really care if I have issued more than one cert for the same host, I add the line

unique_subject = no

to my openssl.cnf file, and also change index.txt.attr to say the same thing. Now I can proceed.

Copy Extensions

So I sign my CSR as before with Jamie's instructions, and… nothing, the result has no SAN field. I'm suspicious that it might be because the extensions in my intermediate/openssl.cnf file are overriding the extension in the request. A bit of searching and it turns out that yes - if you want to copy the extensions from the csr instead of overwriting, you need:

copy_extensions = copy

in the [ CA_default ] section (or somewhere else relevant).

Note - this could be a security concern if you are signing CSRs made by other people, as if you do not carefully read the CSR, they could potentially request extensions that you do not want them to have. In my case, that doesn't matter, as this CA is for my personal homelab use.

After adding this line, I verify the cert with:

openssl x509 -noout -text -in /path/to/cert

And it does have both the X509V3 extensions from the openssl.cnf file and my SAN name, just as I was hoping. Deploying the cert to my test webserver with my ansible config gives me the happy padlock symbol in chrome now too, so it's working as I hoped!

Conclusion

This was a fun project, and will be nice to have working for future projects in my lab. I'm grateful in particular to Jamie Nguyen for his tutorial, and to Richard Levitte for the openssl patch that adds -addext.

Previous Post