OpenShift Certificate from IPA on RHEL 8
OpenShift and IPA Series
This post is part of a collection of blog posts related to OpenShift and FreeIPA (aka idM).
- OpenShift Certificate from IPA on RHEL 8
- OpenShift authentication with IPA
- OpenShift Group Syncing with IPA (Not yet published)
- Automated Certificate Management with IPA and cert-manager (Not yet published)
Introduction
I’m rebuilding my lab with a focus on OpenShift 4 and during this rebuild, I’m working on “modernizing” several aspects of my configuration. This includes attempting (again) to integrate in FreeIPA for managing hosts, certificates and other items.
OpenShift 4’s default node operating system, CoreOS, does not support being directly attached to FreeIPA in the way a traditional Linux host would be. But, OpenShift 4 still requires certain certificates as well as can integrate with an LDAP provider for authentication and authorization.
This post describes creating the certificates for the ingress controller and API endpoints for OpenShift. This isn’t nearly as straightforward as it would seem.
For this example, I’m using the version of FreeIPA included with RHEL 8, which has an official product name of Identity Management (idM). For this article, I’ll be calling everything FreeIPA to keep it generic.
Environment Description
First, let’s look at the environment. This process requires only a few pieces of information:
- FreeIPA base domain:
private.opequon.net
- OpenShift base domain:
ocp4.private.opequon.net
- API and Ingress Controller IP Address:
172.31.0.120
Creating the Certificates
Creating DNS Zones and Host Entries
First create the ocp4.private.opequon.net
zone. This will be the
base name of my cluster.
ipa dnszone-add ocp4.private.opequon.net --admin-email=admin@private.opequon.net
Next create entries for each required OpenShift hostname. apps
must
be created, even though only the wildcard is used, because it’s used
to create the certificate later on.
ipa dnsrecord-add ocp4.private.opequon.net api --a-rec=172.31.0.120
ipa dnsrecord-add ocp4.private.opequon.net api-int --a-rec=172.31.0.120
ipa dnsrecord-add ocp4.private.opequon.net apps --a-rec=172.31.0.120
ipa dnsrecord-add ocp4.private.opequon.net *.apps --a-rec=172.31.0.120
Notice, I’m not creating a reverse entries here as it is not
specifically required and since 172.31.0.120
is shared between the
API and Ingress Controller, I’m not sure what I’d want it to be. In a
real production situation, I would likely want reverse entries for
everything.
Finally, create the host principal.
ipa host-add apps.ocp4.private.opequon.net
ipa host-add api.ocp4.private.opequon.net
Wildcard Profile
With FreeIPA, certificates are generated based on a profile and the
default profile is acceptable for most normal certificates, like our
api
certificate.
By default, FreeIPA does not include a profile for creating wildcard certificates and much of the documentation has warnings around wildcard certificates being deprecated. While this may be true in some sense, wildcard certificates are still very much a part of the OpenShift installation.
In order to support wildcard certificates, we need to create a new
certprofile, which defines how these certificates should be created.
This profile will take care of prefixing the certificate’s Common Name
and Subject Alternate Names (SANs) with *.
.
To define a new profile, first, we extract the default certprofile:
ipa certprofile-show caIPAserviceCert --out wildcard.cfg
Next we need to modify the cert profile to automatically prefix the
Subject and SAN fields with *.
. The instructions for how to do this
were found on
Fraser Tweedale’s blog
and are an extension of
Documenation on the FreeIPA wiki.
The diff below shows the changes needed to the wildcard.cfg file:
[root@ipa ocpcerts]# diff wildcard.cfg wildcard.cfg.orig
19c19
< policyset.serverCertSet.1.default.params.name=CN=*.$request.req_subject_name.cn$, O=PRIVATE.OPEQUON.NET
---
> policyset.serverCertSet.1.default.params.name=CN=$request.req_subject_name.cn$, O=PRIVATE.OPEQUON.NET
32,40c32,33
< policyset.serverCertSet.12.default.class_id=subjectAltNameExtDefaultImpl
< policyset.serverCertSet.12.default.name=Subject Alternative Name Extension Default
< policyset.serverCertSet.12.default.params.subjAltNameNumGNs=2
< policyset.serverCertSet.12.default.params.subjAltExtGNEnable_0=true
< policyset.serverCertSet.12.default.params.subjAltExtType_0=DNSName
< policyset.serverCertSet.12.default.params.subjAltExtPattern_0=*.$request.req_subject_name.cn$
< policyset.serverCertSet.12.default.params.subjAltExtGNEnable_1=true
< policyset.serverCertSet.12.default.params.subjAltExtType_1=DNSName
< policyset.serverCertSet.12.default.params.subjAltExtPattern_1=$request.req_subject_name.cn$
---
> policyset.serverCertSet.12.default.class_id=commonNameToSANDefaultImpl
> policyset.serverCertSet.12.default.name=Copy Common Name to Subject Alternative Name
119c112
< profileId=wildcard
---
> profileId=caIPAserviceCert
After updating the wildcard.cfg
file, it needs to be imported into
IPA, a profile created, and appropriate hosts (in this case
apps.ocp4.private.opequon.net
) associated to that profile.
ipa certprofile-import wildcard --file ./wildcard.cfg --desc 'Wildcard certificates' --store 1
ipa caacl-add-profile wildcard-hosts --certprofiles wildcard
ipa caacl-add-host wildcard-hosts --hosts apps.ocp4.private.opequon.net
Referenced in the documentation is the need to add the ipa
certificate authority to that profile. I’m not sure if this is
actually needed.
ipa caacl-add-ca wildcard-hosts --cas ipa
Generating CSRs
Once the wildcard profile is in place, a certificate signing request (CSR) must be created for each domain.
In the CSR for the *.apps.private.opequon.net
, it’s important to
note that the CSR’s Common Name does not include the *.
portion,
just apps. The wildcard profile created in the previous section will
prefix *.
to the certificate it generates based on the CSR.
# openssl req -newkey rsa:4096 -keyout apps.key -out apps.csr
Generating a RSA private key
.........................................................................................................................................................++++
.............................................................++++
writing new private key to 'apps.key'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:US
State or Province Name (full name) []:VA
Locality Name (eg, city) [Default City]:Reston
Organization Name (eg, company) [Default Company Ltd]:Opequon Networks
Organizational Unit Name (eg, section) []:OpenShift
Common Name (eg, your name or your server's hostname) []:apps.ocp4.private.opequon.net
Email Address []:admin@private.opequon.net
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
This creates a csr file and an encrypted key, which we will later decrypt for loading into OpenShift.
I generally rename the keyfiles to indicate that they are encrypted:
mv -iv apps.key apps.enc.key
We will want generate a CSR for both apps.ocp4.private.opequon.net
and api.ocp4.private.opequon.net
Generating Certificates
The csr file is input to the ipa cert-request
command. For the
*.apps
domain we specify the --profile wildcard
option to generate
the certificate with our wildcard profile.
# ipa cert-request apps.csr --principal host/apps.ocp4.private.opequon.net --profile wildcard
Issuing CA: ipa
Certificate: MIIFvTCC<TRUNCATED>
Subject: CN=*.apps.ocp4.private.opequon.net,O=PRIVATE.OPEQUON.NET
Issuer: CN=Certificate Authority,O=PRIVATE.OPEQUON.NET
Not Before: Mon Nov 02 17:55:28 2020 UTC
Not After: Thu Nov 03 16:55:28 2022 UTC
The api
certificate is generated in the same fashion, but without the --profile wildcard
flag.
ipa cert-request api.csr --principal host/api.ocp4.private.opequon.net
I have not found a good way to actually extract the certificate PEM
file from IPA, so I just copy the output of ipa cert-request
into a
textfile (e.g. apps.crt
, but the name doesn’t really matter) in the
normal PEM format:
-----BEGIN CERTIFICATE-----
MIIFvTCC<TRUNCATED>
-----END CERTIFICATE-----
Finally, when creating the CSRs, openssl
requires the key file be encrypted with a password. OpenShift (and most other users of SSL) expect the key file to be unencrypted. So to decrypt the key, use
openssl rsa -in apps.enc.key -out apps.key
A final item to gather is the IPA certificate, which can be found in
/etc/ipa/ca.crt
on any host enrolled to the FreeIPA server.
At the conclusion of this process, I should have a set of certificates, key files, CSR files, and the IPA Certificate Authority.
[root@yavanna ssl]# ls -la
-rw-r--r--. 1 root root 2071 Nov 2 13:47 api.crt
-rw-r--r--. 1 root root 1789 Nov 2 13:46 api.csr
-rw-------. 1 root root 3414 Nov 2 13:45 api.enc.key
-rw-------. 1 root root 3243 Nov 2 13:50 api.key
-rw-r--r--. 1 root root 2119 Nov 2 13:44 apps.crt
-rw-r--r--. 1 root root 1793 Nov 2 13:43 apps.csr
-rw-------. 1 root root 3414 Nov 2 13:42 apps.enc.key
-rw-------. 1 root root 3243 Nov 2 13:50 apps.key
-rw-r--r--. 1 root root 1667 Nov 2 13:51 ca.crt
Loading New Certificates into OpenShift
Now that we have gathered all the materials, we can load these into an OpenShift cluster. There are three primary steps.
Loading the Certificate Authority
Since my FreeIPA Certificate Authority is self generated, it’s not included in the default CoreOS certificate bundle. In order to ensure that all CoreOS nodes trust my Certificate Authority, it must be loaded into the cluster-wide proxy configuration.
Confusingly enough, this step is included with the documentation for Replacing the default ingress certificate although it’s not directly related to that action.
First create a configmap containing the root CA which we gathered in the previous section:
oc create configmap custom-ca \
--from-file=ca-bundle.crt=</path/to/example-ca.crt> \
-n openshift-config
The name of this configmap is custom-ca
. The name of the configmap can be changed but it must match the the next patch command!
Next, patch the cluster-wide proxy to include this certificate authority.
oc patch proxy/cluster \
--type=merge \
--patch='{"spec":{"trustedCA":{"name":"custom-ca"}}}'
Again, if the ConfigMap was created with a different name than
custom-ca
then this patch command must be updated to match the name
of the ConfigMap!
After applying this patch, the MachineConfigOperator will update all nodes in the cluster. Certain nodes may be down, and it may be desirable to wait to peform the next steps until all nodes are back up and the ClusterOperators all are in good state.
Loading the Ingress Controller Certificate
See Replacing the default ingress certificate for the official documentation.
To load the Ingress Controller, first we create a certificate chain
file which contains the *.apps.ocp4.private.opequon.net
certificate,
then any intermediate certificates and finally the certificate
authority. My simple FreeIPA set up does not have any intermediates,
so we can just concatenate the apps.crt and ca.crt files together.
cat apps.crt ca.crt > appsChain.crt
Do be careful about line endings. It’s important that when certificates are contatenated together that each certificate begins on it’s own line. For example:
GOOD:
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
BAD:
-----END CERTIFICATE----------BEGIN CERTIFICATE-----
Once the chain file is created, then we can use it with the key file
to create a secret. This example names the secret
custom-ingress-secret
. As in the prior section, the name of this
secret can be changed, but it must be consistent between all commands!
oc create secret tls custom-ingress-secret \
--cert=</path/to/appsChain.crt> \
--key=</path/to/apps.key> \
-n openshift-ingress
With the secret in place, then we can patch the IngressController object:
oc patch ingresscontroller.operator default \
--type=merge -p \
'{"spec":{"defaultCertificate": {"name": "custom-ingress-secret"}}}' \
-n openshift-ingress-operator
Again, it’s very important that the name of the secret match in the patch command!
This should cause Ingress Controller pods to be recreated in the
openshift-ingress
project. Once these new pods are ready, the
endpoint should be using this new certificate.
Loading the API Certificate
See Adding API server certificates for the official documentation.
Loading the API certificate is the most dangerous part of this activity. If done incorrectly, then the apiservers can become unavailable. Luckily, if this is the case, then the OpenShift web console will still be available via the ingress controller.
The overall process is very similar to updating the Ingress Controller certificate.
To start, we again need to create a certificate chain:
cat api.crt ca.crt > apiChain.crt
Next we need to create a secret again:
oc create secret tls custom-api-certificate \
--cert=</path/to/apiChain.crt> \
--key=</path/to/api.key> \
-n openshift-config
Finally, we need to patch the APIServer object:
oc patch apiserver cluster \
--type=merge -p \
'{"spec":{"servingCerts": {"namedCertificates":
[{"names": ["api.ocp4.private.opequon.net"],
"servingCertificate": {"name": "custom-api-certificate"}}]}}}'
Again, the name of the secret must match what is put into the above patch. Additionally, notice that we must specify the hostname associated with this certificate.
This will cause the API pods on the OpenShift masters to restart with the new certificate. During such time, there may be small outages of the API depending on the load balancer setup.
Important to note that after applying this change, the kubeconfig
file generated during installation may not work or may throw
certificate errors. The kubeadmin username and password should
continue to work.
Next Steps
After loading certificates, the next step would be to integrate FreeIPA’s LDAP as an authentication provider. This topic is covered in the next part.
- OpenShift Certificate from IPA on RHEL 8
- OpenShift authentication with IPA
- OpenShift Group Syncing with IPA (Not yet published)
- Automated Certificate Management with IPA and cert-manager (Not yet published)