Self-signed certificate made easy
TLDR; The simple way is at the bottom of the article
I have heard the terrible stories about certificates multiple times. Too many times. How hard could it be? When I was on CFCR and PKS we have deployed multiple test clusters every day. We generated certificates during each deployment.
From another side, Kubernetes even includes generating certificates for Kubelets. Why fix what is not broken?
Today we tried to generate a certificate for the test. We are adding mTLS support for Eirini. It is mostly there, but some code has to be added for Eirini staging. And we have to test it. The easiest way is to spin up a test HTTP server and run client calls. So as a result, we need — 2 CA certificates, one key pair for the server and one for the client.
We use httptest Go package to run a fake server. It returns 127.0.0.1 as a default address, so we need the IP address and have to add it as a Subject Alternative Name. Sounds not too tricky.
We decided to go with OpenSSL cli, since it is what all the cool kidz. There is one thing that I don’t want to do — create partially documented files. I would like to go with some simple descriptive declarative file. We need the certificate only for testing. There are not many manuals on how to do this. One of the best I found is https://gist.github.com/fntlnz/cf14feb5a46b2eda428e000157447309 The only annoying part is modifying SSL config or creating a custom file for the alternative name. (And we need it for IP address). OpenSSL 1.1.1 added a new flag to add extensions such as SAN to the certificate. It is not included by default in macOS and not in the default channel of HomeBrew, but you can install 1.1 version.
Unfortunately, we haven’t found a way to pass the extensions when you are signing the certificate. It definitely exists in signing request, but not in the resulting certificate.
$ /usr/local/opt/openssl@1.1/bin/openssl req -in eirini.csr -noout -text
Certificate Request:
Data:
Version: 1 (0x0)
Subject: C = IE, ST = Leinster, L = Dublin, O = Cloud Foundry, OU = Eirini, CN = localhost
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (4096 bit)
Modulus:
...
Exponent: 65537 (0x10001)
Attributes:
Requested Extensions:
X509v3 Subject Alternative Name:
IP Address:127.0.0.1
Signature Algorithm: sha256WithRSAEncryption
...
$ /usr/local/opt/openssl@1.1/bin/openssl x509 -req -in eirini.csr -CA eirini-ca.crt -CAkey eirini-ca.key -CAcreateserial -out eirini.crt -days 3650 -sha256
$ openssl x509 -in eirini.crt -text -noout
Certificate:
Data:
Version: 1 (0x0)
Serial Number: 17064803642309737592 (0xecd25c7fdda9fc78)
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=eirini-ca
Validity
Not Before: Jul 23 14:53:31 2019 GMT
Not After : Jul 20 14:53:31 2029 GMT
Subject: C = IE, ST = Leinster, L = Dublin, O = Cloud Foundry, OU = Eirini, CN = localhost
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (4096 bit)
....
Exponent: 65537 (0x10001)
Signature Algorithm: sha256WithRSAEncryption
So, there is no way to run without a file. But the file itself is not documented well. I followed the makefile referenced in the comments for the manual above.
Now the easy way.
Bosh is the answer.
First, you have to create a YAML file with the certificate definition:
variables:
- name: client_ca
options:
common_name: ca
is_ca: true
type: certificate
- name: tls-test
options:
alternative_names: []
ca: client_ca
organization: eirini
common_name: eirini-client
extended_key_usage:
- client_auth
type: certificate
- name: server_ca
options:
common_name: ca
is_ca: true
type: certificate
- name: tls-server
options:
alternative_names:
- 127.0.0.1
ca: server_ca
common_name: eirini.server
extended_key_usage:
- server_auth
type: certificate
Then you download bosh-cli
tool.
Then you run bosh interpolate <definition_file> --vars-store certificates.yml
. Now all your certificates are stored in certificates.yml
. You can extract them manually, or use bosh interpolate certificates.yml --path /tls-server/private_key
There are several disadvantages to this method:
- The certificates will be valid only for a year. But it is really easy to regenerate them, just delete the file and run interpolate command again.
- The certificate will have the organization “Cloud Foundry” and country “USA”.
- You can only set extended_key_usage and not just key_usage.