Using OpenShift 4 As A Certificates CA
Why this Article
Today the very basic of securing your application is running it with a TLS encryption. When running the application on top of OpenShift we can utilized the Kubernetes Abilities and sign the application certificate when the CA is already part of OpenShift.
In OpenShift the CA certificate are being recycled as well so we do need to make sure that all the certificates are up 2 date.
What do we need ?
Openshift, That is it…. well almost we do need to create the CSR and the KEY. For that we need to create an answer file and use openssl to generate the 2 files.
Getting Started
The steps for generating the Certificate are simple. First we will generate the key and the the answer file for the CSR followed by the CSR. Once the two files are obtained we will create the “CertificateSigningRequest” resource and sign the certificate.
OpenSSL
First (as root) let’s make sure the package is installed on the server :
# dnf install -y openssl
Now we can generate the key first .
$ openssl genrsa -out tls.key 2048
Next we can do one of 2 options. Create an answer file that will provide answers to the certificate request (suitable for DNS alternate names). and the other option is to generate the CSR in one liner.
NOTE!!!
the common name has a string limit in regards to the FQDN we need to input. As such if we have a long FQDN string we will not be able to get it generated without DNS alter names.
Answer file (Option 1)
to generate the answer file I will first export our URL short name (the first part of the FQDN) and then the domain with environment variables :
# export DOMAIN="<my.long.and-annoying.really-long.domain.me>"
# export SHORT_NAME="<the name>"
Once both variables are set we can generate the answer file :
$ cat > ${SHORT_NAME}_answer.txt << EOF
[req]
default_bits = 2048
prompt = no
default_md = sha256
x509_extensions = req_ext
req_extensions = req_ext
distinguished_name = dn[ dn ]
C=US
ST=New York
L=New York
O=MyOrg
OU=MyOrgUnit
emailAddress=me@working.me
CN = ${SHORT_NAME}[ req_ext ]
subjectAltName = @alt_names[ alt_names ]
DNS.1 = ${SHORT_NAME}
DNS.2 = ${SHORT_NAME}.${DOMAIN}
EOF
In the DN section you can go ahead and change any parameter you want (except for the CN)
Next we can generate the CSR with the answer file :
# openssl req -new -key tls.key -out tls.csr -config <( cat ${SHORT_NAME}_answer.txt )
One liner (Option 2)
We will first export our URL short name (the first part of the FQDN) and then the domain with environment variables :
# export DOMAIN="<my.short.name>"
# export SHORT_NAME="<the name>"
For short name in the CN we can simple create a one liner CN :
# openssl req -new -key tls.key -out tls.csr -subj "/CN=${SHORT_NAME}.${DOMAIN}/O=devops/O=My Org/C=US/ST=New York/L=New York" -addext "subjectAltName = DNS:${SHORT_NAME},DNS:${SHORT_NAME}.${DOMAIN}"
Signing the CSR
(From the Official Kubernetes page) : “The CertificateSigningRequest resource type allows a client to ask for an X.509 certificate be issued, based on a signing request. The CertificateSigningRequest object includes a PEM-encoded PKCS#10 signing request in the spec.request
field. The CertificateSigningRequest denotes the signer (the recipient that the request is being made to) using the spec.signerName
field. Note that spec.signerName
is a required key after API version certificates.k8s.io/v1
. In Kubernetes v1.22 and later, clients may optionally set the spec.expirationSeconds
field to request a particular lifetime for the issued certificate. The minimum valid value for this field is 600
, i.e. ten minutes.”
In order to sign our certificate we first need to generate a base64 string from it and add it to the CertificateSigningRequest CR.
# CERT_BASE64=$(cat tls.csr | base64 -w0)
Now let’s create the CR :
# cat > tls-csr.yaml << EOF
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: ${SHORT_NAME}
spec:
request: ${CERT_BASE64}
signerName: kubernetes.io/kube-apiserver-client
expirationSeconds: 7776000 # three months
usages:
- client auth
EOF
Some points to note:
usages
has to be 'client auth
'expirationSeconds
could be made longer (i.e. 15552000 for half a year ) or shorter (i.e.3600
for one hour)request
is the base64 encoded value of the CSR file content.
And Apply it :
# oc apply -f tls-csr.yaml
we can now view the certificate request (if we have the right permissions by running the “oc get csr” command :
# oc get csr
NAME AGE SIGNERNAME REQUESTOR CONDITION
my-cert 17s kubernetes.io/kube-apiserver-client system:admin Pending
As we can see the certificate status is “Pending” which is waiting for the admin (or user with the right permission) to approve the certificate.
Let’s go ahead and approve the certificate :
# oc adm certificate approve my-cert
certificatesigningrequest.certificates.k8s.io/my-cert approved
Now, if we look at the CSR request we are going to see the certificates again but the state has changed :
# oc get csr
NAME AGE SIGNERNAME REQUESTOR CONDITION
my-cert 2m51s kubernetes.io/kube-apiserver-client system:admin Approved,Issued
Now that our certificate has been generated we can go ahead and extract it. sense the certificate is been saved as base64 we will need to decode the output
# oc get csr my-cert -o jsonpath='{.status.certificate}' | base64 -d
The output should look as such :
-----BEGIN CERTIFICATE-----
.....
-----END CERTIFICATE-----
If you are getting the same results we are good to go.
CA certificate
For the last part we need to extract our CA certificate which OpenShift had used to sign the certificate. On Openshift there is a simple config map which contains all of the necessary CAs :
# oc get secret csr-signer -n openshift-kube-controller-manager-operator -o template='{{ index .data "tls.crt"}}' | base64 -d > route-ca.crt
Now we have everything we need in place and we go ahead and use the certificate.
That is it
If you have any question feel free to respond/ leave a comment.
You can find on linkedin at : https://www.linkedin.com/in/orenoichman
Or twitter at : https://twitter.com/ooichman