When running services at home, the proliferation of self-signed certificates and browser errors to click through can become a pain point. By creating our own internal Certificate Authority (CA) and loading that certificate onto relevant machines, we can access these services securely and simply.
To do this, I use Cloudflare's cfssl
and follow a simple scheme I've encoded in my certs
repo, orchestrated by the Makefile
. This system is based on a pattern Rob Blackbourn wrote about.
Make
Our goal is to create a Certificate Authority (CA), Intermediate CA, and certificates signed by the Intermediate CA for each of our "servers" or services. If I want to construct a certificate just for my VMWare server, then my top-level goal could be:
.PHONY: certs
certs: \
servers/vms/vms.home.arpa-server.pem \
servers/vms/vms.home.arpa-server-key.pem
All this says is that there is a goal named certs
that is not a file (.PHONY
), and it requires two files: the public and private certificates, which I name after the fully qualfied domain of the service on my network: vms.home.arpa
. Given this goal, make
looks for a way to create the two named files.
To do this, I have a pattern rules which will construct set of public and private keys:
servers/%-server.pem servers/%-server-key.pem: servers/%.json intermediate-ca.pem intermediate-ca-key.pem cfssl.json
cfssl gencert -ca intermediate-ca.pem -ca-key intermediate-ca-key.pem -config cfssl.json -profile=server $< | cfssljson -bare $(basename $<)-server
This rule requires a configuration file, servers/vms/vms.home.arpa.json
along with keys for the Intermediate CA adn the global config. The two configs we will have to provide, and are illustrated below:
The file servers/vms/vms.home.arpa.json
looks like:
{
"CN": "vms.home.arpa",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"ST": "Arkansas",
"L": "Little Rock",
"O": "Heavy Computer",
"OU": "Heavy Computer Registry"
}
],
"hosts": [
"vms.home.arpa",
"localhost",
"10.0.3.1"
]
}
For the CN
I use the fully qualified internal domain. I also add it in hosts
alongside the persistent IP address configured via DHCP and localhost
for convenience and debugging purposes. The key
section determines what size and algorithm the certificate will use, we use an RSA key of length 2048 which is the current NIST suggested minimum.1 The names
section is not required to be accurate.
The file cfssl.json
looks like:
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"intermediate-ca": {
"usages": [
"signing",
"digital signature",
"key encipherment",
"cert sign",
"crl sign",
"server auth",
"client auth"
],
"expiry": "87600h",
"ca_constraint": {
"is_ca": true,
"max_path_len": 0,
"max_path_len_zero": true
}
},
"peer": {
"usages": [
"signing",
"digital signature",
"key encipherment",
"client auth",
"server auth"
],
"expiry": "87600h"
},
"server": {
"usages": [
"signing",
"digital signing",
"key encipherment",
"server auth"
],
"expiry": "87600h"
},
"client": {
"usages": [
"signing",
"digital signature",
"key encipherment",
"client auth"
],
"expiry": "87600h"
}
}
}
}
At the moment we are only using the server
profile as indicated by -profile=server
in our pattern, but we will later reference intermediate-ca
. I use ten years for all the expirations, I am not concerned about man-in-the-middle attacks using leaked certificates; for your use-case set whatever is appropriate.
Intermediate CA
Now that we've covered the config files, our rule still needs an Intermediate CA, which is where the next rule comes in:
intermediate-ca.pem intermediate-ca-key.pem: ca.pem intermediate-ca.json cfssl.json
cfssl gencert -initca intermediate-ca.json | cfssljson -bare intermediate-ca
cfssl sign -ca ca.pem -ca-key ca-key.pem -config cfssl.json -profile intermediate-ca intermediate-ca.csr | cfssljson -bare intermediate-ca
This rule creates an Intermediate CA adn signs it with the CA. The rule requires the CA certificates, Intermediate CA configuration, and global configuration. The global configuration is covered above, so I'll reproduce intermediate-ca.json
below:
{
"CN": "Heavy Computer Intermediate CA",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"ST": "Arkansas",
"L": "Little Rock",
"O": "Heavy Computer",
"OU": "Heavy Computer Intermediate CA"
}
],
"ca": {
"expiry": "87600h"
}
}
You'll notice its very similar to the vms.home.arpa.json
configuration above, but with a CA expiry field configured to ten years.
CA
The CA is constructed by another rule:
ca.pem ca-key.pme: ca.json
cfssl gencert -initca ca.json | cfssljson -bare ca
# Add to macOS keychain
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ca.pem
This rule not only constructs the CA, but adds it to my MacBook's system keychain. It requires a CA configuration, ca.json
is reproduced below:
{
"CN": "Heavy Computer Root CA",
"CA": {
"expiry": "87600h"
},
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"O": "Heavy Computer",
"OU": "Heavy Computer Root CA",
"L": "Little Rock",
"ST": "Arkansas",
"C": "US"
}
]
}
You'll notice its almost identical to the Intermediate CA, except ca.expiry
has moved to CA.expiry
.
Sync
At the end of the Makefile, there's a sync rule:
\.synced: certs
gsutil -q -m rsync -u -x '(?!^.*\.pem$$)' -r . gs://certs.connor.zip
gsutil -q -m rsync -u -x '(?!^.*\.pem$$)' -r gs://certs.connor.zip .
date -u +'%Y-%m-%dT%H:%M:%SZ' >$@
This rule copies all generated .pem
files to a GCS bucket using gsutil rsync
, then syncs any files from the bucket back to our local filesystem.
Usage
You can clone my certs
repo and use it to generate your own certs. Simply:
- Delete or reconfigure the
\.synced
rule in theMakefile
. - If not on macOS, remove the
sudo security add-trusted-cert ...
line from the CA rule. - Remove folders under
clients/
andservers/
and replace them with services of your own. - Edit the
certs
rule in theMakefile
to reflect only your new folders, each must contain a config with the same prefix as your.pem
files. For instance,servers/vms/vms.home.arpa.json
matchesservers/vms/vms.home.arpa-server.pem
andservers/vms/vms.home.arpa-server-key.pem
. - Run
brew install cfssl
to install the utility.
Then just run make
from the root of the repository to generate all required certificates.
Follow similar instructions except in clients
to create client certificates like those for IRC.
Configuring Services
This section outlines instructions for some services I use on my network.
VMWare
To install a new certificate on VMWare 6.x, follow the instructions here.
- Navigate to the Web UI and from the Host page click Actions, Services, Enable Secure Shell (SSH)
- Create a combined certificates file:
cat servers/vms/vms.home.arpa-server.pem intermediate-ca.pem ca.pem > vms.home.arpa-chain.pem
- Copy the certificates, using the hostname on your network:
scp vms.home.arpa-chain.pem servers/vms/vms.home.arpa-server-key.pem vms.home.arpa:.
- Log into the node using SSH:
ssh vms.home.arpa
- Move the existing certs to backup files:
mv /etc/vmware/ssl/rui.crt /etc/vmware/ssl/orig.rui.crt mv /etc/vmware/ssl/rui.key /etc/vmware/ssl/orig.rui.key
- Copy our new certificates into position:
mv vms.home.arpa-chain.pem /etc/vmware/ssl/rui.crt mv vms.home.arpa-server-key.pem /etc/vmware/ssl/rui.key
- Reboot VMWare
Once the machine comes back up, navigating to the UI should show no TLS errors from our browser, assuming our CA is in the system keychain.
IRC
Client certificates (generated under clients/
) can be used for CertFP authentication with IRC networks.
To get the fingerprint for e.g. OFTC (see Automatically Identifying Using SSL + CertFP):
cat clients/irssi/irssi-client-key.pem clients/irssi/irssi-client.pem | openssl x509 -noout -fingerprint -sha1 | awk -F= '{gsub(":",""); print $2}'
Or for Libera (see Using CertFP), which uses SHA-512:
cat clients/irssi/irssi-client-key.pem clients/irssi/irssi-client.pem | openssl x509 -noout -fingerprint -sha512 | awk -F= '{gsub(":",""); print tolower($2)}'
To get a certificate to paste into ZNC's User Modules > Certificate form:
cat clients/irssi/irssi-client-key.pem clients/irssi/irssi-client.pem | pbcopy
For more information on ZNC, see my more in-depth article.
-
NIST Special Publication 800-57 Part 3, Recommendation for Key Management summarizes recommendations in section 2.2.1 Recommended Key Sizes and Algorithms, Table 2-1. Reproduced below:
Key Type Algorithms and Key Sizes Digital Signature keys used for authentication (for Users or Devices) RSA (2048 bits), ECDSA (Curve P-256) Digital Signature keys used for non-repudiation (for Users or Devices) RSA (2048 bits), ECDSA (Curves P-256 or P-384) CA and OCSP Responder Signing Keys RSA (2048 or 3072bits), ECDSA (Curves P-256 or P-384) Key Establishment keys (for Users or Devices) RSA (2048 bits), Diffie-Hellman (2048 bits), ECDH (Curves P-256 or P-384)