Allowing simple SAN requests with openssl on my intranet Certificate Authority.
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.
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
So I need to build a newer version. I found a nice guide at this site which I followed - abbreviated version here.
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*
./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:
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:
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.
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.
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
[ 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!
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