Chapter 2. Adding custom TLS certificates for Red Hat OpenStack Services on OpenShift When you deploy Red Hat OpenStack Services on OpenShift (RHOSO), TLS-e (TLS everywhere) is enabled by default. TLS is handled by cert-manager , which applies both ingress (public) encryption, as well as reencryption to each pod. Currently, disabling TLS on RHOSO is not supported. 2.1. TLS in Red Hat OpenStack Services on OpenShift When you deploy Red Hat OpenStack Services on OpenShift (RHOSO), most API connections are protected by TLS. Note TLS is not currently available for the internal Alert Manager Web UI service endpoint. You might be required to protect public APIs using your own internal certificate authority. In order to replace the automatically generated certificates you must create a secret that contains your additional ca certs, including all certificates in needed chains of trust. You can apply trusted certificates from your own internal certificate authority (CA) to public interfaces on RHOSO. The public interface is where ingress traffic meets the service's route. Do not attempt to manage encryption on internal (pod level) interfaces. If you decide to apply trusted certificates from your own internal certificate authority (CA), you will need the following information. DNS names For each service you apply your own custom certificate to, you will need its DNS hostname for the process of generating the certificate. You can get a list of public hostnames using the following command: oc get -n openstack routes Note To use a single certificate for two or more services, use a wildcard in the DNS name field, or list multiple DNS names in the subject alt names field. If you do not use a wildcard, then you must update the certificate in the event of a route hostname change. Duration To update a service's certificate in OpenShift, the service must be restarted. The duration for the certificate is the longest amount of time a service can stay live without being restarted, subject to your internal security policies. Usages You must include - key encipherment , digital signature , and server auth within the list of usages in your certificate. Updating TLS to use custom certificates requires edits to both the control plane and the data plane. The following is the default TLS settings that are used if not annotated and changed: To create a custom TLS certificate for each public service see Updating the control plane with custom certificates for public services . To create a single custom TLS certificate to apply to the public services, see Updating the control plane with a single custom certificate for public services . 2.2. Updating the control plane with custom certificates for public services You might be required to protect public APIs by using your own internal certificate authority (CA). To replace the automatically generated route certificates with common signed certificates from your CA, you must create a secret that contains your additional CA certificate, and all certificates in the chain of trust. Prerequisites You have a list of each of the public services for which to apply your custom service certificates. You can get this list using the oc route list -n openstack command. Use this information for the number of certificates you must create, the DNS names for those certificates, as well as finding the relevant services to edit in the openstack_control_plane.yaml custom resource (CR). You have a service certificate for the public services Procedure Create a manifest file called cacerts.yaml that includes all CA certificates. Include all certificates in chains of trust if applicable: 1 Run this command, replacing mybundle.pem with the name of your certificate or certificate bundle. The results are pasted as the value of the myBundleExample field. 2 Run this command, replacing cacert.pem with the name of your CA certificate. Create the secret from the manifest file: Create a manifest file for each secret named api_certificate_<service>_secret.yaml : 1 Replace <service> with the name of the service that this secret is for. 2 Run this command, replacing tlscrt.pem with the name of your signed certificate. 3 Run this command, replacing tlskey.pem with the name of your private key. 4 Run this command, replacing cacrt.pem with the name of your CA certificate. Create the secret Edit the openstack_control_plane.yaml custom resource and add your bundle as the parameter for caBundleSecretName : apiVersion: core.openstack.org/v1beta1 kind: OpenStackControlPlane metadata: name: myctlplane spec: tls: podLevel: enabled: true caBundleSecretName: cacerts Apply the secret service certificates to each of the public services under the apiOverride field. For example enter the following for the Identity service (keystone): The edits for the Compute service (nova) and NoVNCProxy appear as the following: Apply the control plane changes 2.3. Updating the control plane with a single custom certificate for public services You might be required to protect public APIs by using your own internal certificate authority (CA). To replace the automatically generated route certificates with a common signed certificate from your CA, you must create a secret that contains your CA certificate, and all certificates in the chain of trust. Prerequisites You have a list of each of the public services for which to apply your custom service certificate. You can get this list by using the oc route list -n openstack command. Use this information for the DNS names for the certificate, as well as for finding the relevant services to edit in the openstack_control_plane.yaml custom resource (CR). Procedure Create a signed certificate that includes the hostname for every service in the alt_names section: Create a manifest file called cacerts.yaml that includes all CA certificates. Include all certificates in chains of trust if applicable: 1 Run this command, replacing mybundle.pem with the name of your certificate or certificate bundle. The results are pasted as the value of the myBundleExample field. 2 Run this command, replacing cacert.pem with the name of your CA certificate. Create the secret from the manifest file: Create a manifest file for a secret named certificate-secret.yaml : 1 Run this command, replacing tlscrt.pem with the name of your signed certificate. 2 Run this command, replacing tlskey.pem with the name of your private key. 3 Run this command, replacing cacrt.pem with the name of your CA certificate. Create the secret Edit the openstack_control_plane.yaml custom resource and add your bundle as the parameter for caBundleSecretName : apiVersion: core.openstack.org/v1beta1 kind: OpenStackControlPlane metadata: name: myctlplane spec: tls: podLevel: enabled: true caBundleSecretName: cacerts Apply the secret service certificates to each of the public services under the apiOverride field. For example, enter the following for the Identity service (keystone): The edits for the Compute service (nova) and NoVNCProxy appear as the following: Apply the control plane changes 2.4. Using your CA certs on remote clients If you do not use a trusted CA from a public entity, openstack client commands fail with an SSL verification error, requiring the --insecure command option to succeed. You can securely communicate with OpenStack API using a private certificate authority using the following steps. Prerequisites You have deployed RHOSO with default certificates, or have used custom certificates that are not signed by a public certificate authority. Procedure Log onto OpenShift with global administrative permissions. Extract the ca cert for the public endpoints from the rootca-public secret. Transfer the ca.crt file to the client that accesses the OpenStack API. Update your authentication file with the path to ca.crt . If you use a clouds.yml authentication file, add the cacert parameter: Replace </path/to/ca.crt> with the absolute path and name of the CA cert on your system. If you use a resource credentials file, update the file with the exported CACERT variable: Replace </path/to/ca.crt> with the absolute path and name of the CA cert on your system.

Commands:
apiVersion: core.openstack.org/v1beta1 kind: OpenStackControlPlane metadata: name: myctlplane spec: tls: default: ingress: ca: duration: 87600h cert: duration: 43800h enabled: true podLevel: enabled: true internal: ca: duration: 87600h cert: duration: 43800h libvirt: ca: duration: 87600h cert: duration: 43800h ovn: ca: duration: 87600h cert: duration: 43800h
apiVersion: v1 kind: Secret metadata: name: cacerts namespace: openstack type: Opaque data: myBundleExample: <cat mybundle.pem | base64 -w0> 1 CACertExample: <cat cacert.pem | base64 -w0> 2
apply -f cacerts.yaml
apiVersion: v1 kind: Secret metadata: name: api_certificate_<service>_secret 1 namespace: openstack type: kubernetes.io/tls data: tls.crt: <cat tlscrt.pem | base64 -w0> 2 tls.key: <cat tlskey.pem | base64 -w0> 3 ca.crt: <cat cacrt.pem | base64 -w0> 4
apply -f api_certificate_<service>_secret.yaml
apiVersion: core.openstack.org/v1beta1 kind: OpenStackControlPlane metadata: name: myctlplane spec: tls: podLevel: enabled: true caBundleSecretName: cacerts
apiVersion: core.openstack.org/v1beta1 kind: OpenStackControlPlane metadata: name: myctlplane namespace: openstack spec: keystone: apiOverride: tls: secretName: api_certificate_keystone_secret
apiVersion: core.openstack.org/v1beta1 kind: OpenStackControlPlane metadata: name: myctlplane namespace: openstack spec: nova: apiOverride: tls: secretName: api_certificate_nova_secret route: {} cellOverride: cell1: NoVNCProxy: tls: secretName: api_certificate_novavncproxy_secret
apply -f openstack_control_plane.yaml
[alt_names] DNS.1 = barbican-public-openstack.apps.ocp.openstack.lab DNS.2 = cinder-public-openstack.apps.ocp.openstack.lab DNS.3 = glance-default-public-openstack.apps.ocp.openstack.lab DNS.4 = horizon-openstack.apps.ocp.openstack.lab DNS.5 = keystone-public-openstack.apps.ocp.openstack.lab DNS.6 = manila-public-openstack.apps.ocp.openstack.lab DNS.7 = neutron-public-openstack.apps.ocp.openstack.lab DNS.8 = nova-novncproxy-cell1-public-openstack.apps.ocp.openstack.lab DNS.9 = nova-public-openstack.apps.ocp.openstack.lab DNS.10 = placement-public-openstack.apps.ocp.openstack.lab
apiVersion: v1 kind: Secret metadata: name: cacerts namespace: openstack type: Opaque data: myBundleExample: <cat mybundle.pem | base64 -w0> 1 CACertExample: <cat cacert.pem | base64 -w0> 2
apply -f cacerts.yaml
apiVersion: v1 kind: Secret metadata: name: certificate-secret namespace: openstack type: kubernetes.io/tls data: tls.crt: <cat tlscrt.pem | base64 -w0> 1 tls.key: <cat tlskey.pem | base64 -w0> 2 ca.crt: <cat cacrt.pem | base64 -w0> 3
apply -f certificate-secret.yaml
apiVersion: core.openstack.org/v1beta1 kind: OpenStackControlPlane metadata: name: myctlplane spec: tls: podLevel: enabled: true caBundleSecretName: cacerts
apiVersion: core.openstack.org/v1beta1 kind: OpenStackControlPlane metadata: name: myctlplane namespace: openstack spec: keystone: apiOverride: tls: secretName: certificate-secret
apiVersion: core.openstack.org/v1beta1 kind: OpenStackControlPlane metadata: name: myctlplane namespace: openstack spec: nova: apiOverride: tls: secretName: certificate-secret route: {} cellOverride: cell1: NoVNCProxy: tls: secretName: certificate-secret
apply -f openstack_control_plane.yaml
get secret rootca-public -o json | jq -r '.data."ca.crt"' | base64 -d > ca.crt
clouds: secure: cacert: </path/to/ca.crt>
export OC_CACERT=</path/to/ca.crt>