Managing public trust in kubernetes with trust-manager
Last Verified: 19 June 2023
In this tutorial we will walk through how we can use trust-manager to distribute publicly trusted Certificate Authority (CA) certificates inside a Kubernetes cluster. Once distributed we will also show:
- How you can automatically reload applications when your trust bundle changes
- How you can enforce applications to use your distributed CA bundle
From there we will use a simple curl
pod to show how to automatically mount
the trusted CA Bundle
, so it can be used without having to configure curl
manually. This mimics how an application would not need any additional
configuration to make use of your trusted CA certificates bundle.
In this tutorial we will be limiting the scope of our changes to only impact
the team-a
namespace. To get the most out of these features you will want
to remove this limitation.
Note: All resources provided are demonstrative and should be reviewed properly before using in production environments.
Prerequisites
💻 Software
- kubectl: The Kubernetes command-line tool which allows you to configure Kubernetes clusters.
- helm: A package manager for Kubernetes.
- yq: A command line tool for parsing YAML with helpful coloring.
Distribute Public CA Trust
Let us first setup trust-manager and have our public CAs distributed to our
demo namespace: team-a
.
Setup Application & Bundle
-
Install trust-manager following the instructions here.
-
Create your first
Bundle
resource including only Public CA certificatesapiVersion: trust.cert-manager.io/v1alpha1kind: Bundlemetadata:name: public-bundlespec:sources:- useDefaultCAs: truetarget:configMap:key: "ca-certificates.crt"namespaceSelector:matchLabels:trust: enabledkubectl apply -f - <<EOFapiVersion: trust.cert-manager.io/v1alpha1kind: Bundlemetadata:name: public-bundlespec:sources:- useDefaultCAs: truetarget:configMap:key: "ca-certificates.crt"namespaceSelector:matchLabels:trust: enabledEOF -
Let's create a namespace where our application will run:
kubectl apply -f - <<EOFapiVersion: v1kind: Namespacemetadata:labels:trust: enabledname: team-aEOFNote that this namespace is labelled with
trust: enabled
which matches thenamespaceSelector
criteria in theBundle
resource:namespaceSelector:matchLabels:trust: enabledNote: this is to limit the scope of our trust bundle to only the
team-a
namespace as mentioned previously. -
Verify that the trust-manager controller has correctly propagated the CA bundle to the namespace:
kubectl get configmap -n team-a public-bundle -o yamlNote that this output should be quite long. This is because the default public bundle that we use has a lot of public CAs in it.
Mount Trust Bundle to Application with Automatic Use
To use our trusted CAs we will mount them to the application in a default
location that most applications expect to find a ca-certificates.crt
file.
The benefit to this approach is that most application code inside the container
will use this file by default and not require any additional configuration. There is the added benefit that you will be mounting over the top of
/etc/ssl/certs
which will remove existing CA certificates, usually
present from a container base image or pulled in during CI builds.
WARNING: We have chosen one well known location in this example which is used by alpine and
curl
for sourcing trusted CAs. This is not the only location that can be used, so a container may have other default locations. If you want to see where default CAs are located you can use paranoia to inspect a built container image.
-
Apply the application deployment:
apiVersion: apps/v1kind: Deploymentmetadata:labels:app: sleep-autoname: sleep-autonamespace: team-aspec:replicas: 1revisionHistoryLimit: 3selector:matchLabels:app: sleep-autostrategy: {}template:metadata:labels:app: sleep-autospec:containers:- command:- /bin/sh- -c- sleep 1dimage: quay.io/zenlab/curl:latestname: curlresources: {}volumeMounts:- mountPath: /etc/ssl/certs/name: ca-certificate-onlyreadOnly: truevolumes:- name: ca-certificate-onlyconfigMap:name: public-bundledefaultMode: 0644optional: falseitems:- key: ca-certificates.crtpath: ca-certificates.crtkubectl apply -f - <<EOFapiVersion: apps/v1kind: Deploymentmetadata:labels:app: sleep-autoname: sleep-autonamespace: team-aspec:replicas: 1revisionHistoryLimit: 3selector:matchLabels:app: sleep-autotemplate:metadata:labels:app: sleep-autospec:containers:- command:- /bin/sh- -c- sleep 1dimage: quay.io/zenlab/curl:latestname: curlvolumeMounts:- mountPath: /etc/ssl/certs/name: ca-certificate-onlyreadOnly: truevolumes:- name: ca-certificate-onlyconfigMap:name: public-bundledefaultMode: 0644optional: falseitems:- key: ca-certificates.crtpath: ca-certificates.crtEOF -
Create a shell inside the running pod:
kubectl exec -n team-a -ti $(kubectl get po -n team-a -l app=sleep-auto -o jsonpath='{.items[0].metadata.name}') -- /bin/sh -
List the contents of
/etc/ssl/certs/
to validate that only your trustedca-certificates.crt
is present.ls -ltr /etc/ssl/certs/Output should look similar to:
~ $ ls -ltr /etc/ssl/certs/total 0lrwxrwxrwx 1 root root 26 Apr 14 15:12 ca-certificates.crt -> ..data/ca-certificates.crtNote that normally this container image the output would look something like the following, when there is no volume overriding this directory:
~ $ ls -ltr /etc/ssl/certs/total 608-rw-r--r-- 1 root root 214222 Apr 14 01:11 ca-certificates.crtlrwxrwxrwx 1 root root 52 Apr 14 01:11 ca-cert-vTrus_Root_CA.pem -> /usr/share/ca-certificates/mozilla/vTrus_Root_CA.crtlrwxrwxrwx 1 root root 56 Apr 14 01:11 ca-cert-vTrus_ECC_Root_CA.pem -> /usr/share/ca-certificates/mozilla/vTrus_ECC_Root_CA.crt...lrwxrwxrwx 1 root root 53 Apr 14 01:11 02265526.0 -> ca-cert-Entrust_Root_Certification_Authority_-_G2.pemlrwxrwxrwx 1 root root 31 Apr 14 01:11 002c0b4f.0 -> ca-cert-GlobalSign_Root_R46.pem -
Make a HTTPS call out to a well known site to validate
curl
works without having to pass the additional--cacert
flag:curl -v https://bbc.co.uk/newsSuccess will result in a valid TLS connection such as:
* Trying 151.101.0.81:443...* Connected to bbc.co.uk (151.101.0.81) port 443 (#0)* ALPN: offers h2,http/1.1* TLSv1.3 (OUT), TLS handshake, Client hello (1):* CAfile: /etc/ssl/certs/ca-certificates.crt* CApath: none* TLSv1.3 (IN), TLS handshake, Server hello (2):* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):* TLSv1.3 (IN), TLS handshake, Certificate (11):* TLSv1.3 (IN), TLS handshake, CERT verify (15):* TLSv1.3 (IN), TLS handshake, Finished (20):* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):* TLSv1.3 (OUT), TLS handshake, Finished (20):* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256* ALPN: server accepted h2* Server certificate:* subject: C=GB; ST=London; L=London; O=BRITISH BROADCASTING CORPORATION; CN=www.bbc.com* start date: Mar 14 06:16:13 2023 GMT* expire date: Apr 14 06:16:12 2024 GMT* subjectAltName: host "bbc.co.uk" matched cert's "bbc.co.uk"* issuer: C=BE; O=GlobalSign nv-sa; CN=GlobalSign RSA OV SSL CA 2018* SSL certificate verify ok.
- Exit the container:
exit
Configure Real Applications
Based on the example above, Kubernetes is able to mount over the top of the default CA certificate bundle. You can use this with applications assuming you know where the default locations they retrieve CA certificates from.
For example with Go
your application is configurable with either
SSL_CERT_FILE
or SSL_CERT_DIR
to point to the default CA certificate
file location.
See more details here and for the default locations on various OS bases, check here
// Possible certificate files; stop after finding one.var certFiles = []string{"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc."/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6"/etc/ssl/ca-bundle.pem", // OpenSUSE"/etc/pki/tls/cacert.pem", // OpenELEC"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7"/etc/ssl/cert.pem", // Alpine Linux}// Possible directories with certificate files; all will be read.var certDirectories = []string{"/etc/ssl/certs", // SLES10/SLES11, https://golang.org/issue/12139"/etc/pki/tls/certs", // Fedora/RHEL"/
Having checked Python the ssl
library uses the same two environment variables
for finding the trusted CAs: SSL_CERT_DIR
and / or SSL_CERT_FILE
. You can
verify this in documentation
and from a python3
runtime:
>>> import ssl>>> ssl.get_default_verify_paths()DefaultVerifyPaths(cafile=None, capath='/usr/lib/ssl/certs', openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='/usr/lib/ssl/cert.pem', openssl_capath_env='SSL_CERT_DIR', openssl_capath='/usr/lib/ssl/certs')
This should mean that any CAs mounted in a file and any of the following files
will be trusted by any python application runtime, similar to Go
:
- '/usr/lib/ssl/cert.pem'
- '/usr/lib/ssl/certs/*'
Similar could be achieved with other languages.
Automate and Enforce
So now we have mounted trust-manager's bundle manually, you might be thinking:
- What happens if the CA Bundle is changed, how do I get that change to my application?
- How do I ensure that my CA Bundle is mounted to all applications in my cluster without having to request changes from my tenants?
Let's tackle both of these scenarios using additional Open Source tools.
Rollout CA Bundle Changes
If your CA bundle changes, those changes will be synced to the namespaces
pretty quickly. This change will be reflected in the volume attached to the
container, but most applications will not pickup on the file system change.
The common approach is restarting the client application deployment, through
the use of kubectl rollout restart deployment <DEPLOY_NAME>
. There is an
option to automate this process through a third party piece of open-source
software.
Using Stakater Reloader it is
possible to reload or rollout a deployment whenever a ConfigMap
or Secret
changes. So whenever the Bundle
's target is synced, the Reloader component
can pick up this change and rollout applications mounting those resource
as volumes or environment variables.
Please note that there are many alternative pieces of software that you could bundle or write into your application container. They would simply watch the file system for changes and trigger a reload of the application process. Such an approach requires container image or code changes and this could be difficult to implement with many tenants. The advantage to using reloader here is that it's a generic solution, applicable to all applications running in a cluster.
-
Continuing with the reloader, it can be installed with helm:
helm repo add stakater https://stakater.github.io/stakater-chartshelm repo updatehelm install reloader stakater/reloader -n stakater-reloader --create-namespace --set fullnameOverride=reloader -
We can reuse the deployment
sleep-auto
from the previous section and configured it to enabled the reload functionality:kubectl annotate deployment -n team-a sleep-auto reloader.stakater.com/auto="true"Please note there are several configuration options to configure the reloader tooling and this is only the most basic example. Refer to the documentation for more detailed examples.
-
In another terminal watch the application rollout:
kubectl get po -n team-a -w -
To test this change we can edit our
Bundle
resource to remove all the default Public CA certificates and only provide one CA certificate instead:apiVersion: trust.cert-manager.io/v1alpha1kind: Bundlemetadata:name: public-bundlespec:sources:- inLine: |-----BEGIN CERTIFICATE-----MIIETjCCAzagAwIBAgINAe5fFp3/lzUrZGXWajANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEQMA4GA1UECxMHUm9vdCBDQTEbMBkGA1UEAxMSR2xvYmFsU2lnbiBSb290IENBMB4XDTE4MDkxOTAwMDAwMFoXDTI4MDEyODEyMDAwMFowTDEgMB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDMJXaQeQZ4Ihb1wIO2hMoonv0FdhHFrYhy/EYCQ8eyip0EXyTLLkvhYIJG4VKrDIFHcGzdZNHr9SyjD4I9DCuul9e2FIYQebs7E4B3jAjhSdJqYi8fXvqWaN+JJ5U4nwbXPsnLJlkNc96wyOkmDoMVxu9bi9IEYMpJpij2aTv2y8gokeWdimFXN6x0FNx04Druci8unPvQu7/1PQDhBjPogiuuU6Y6FnOM3UEOIDrAtKeh6bJPkC4yYOlXy7kEkmho5TgmYHWyn3f/kRTvriBJ/K1AFUjRAjFhGV64l++td7dkmnq/X8ET75ti+w1s4FRpFqkD2m7pg5NxdsZphYIXAgMBAAGjggEiMIIBHjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUj/BLf6guRSSuTVD6Y5qL3uLdG7wwHwYDVR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9yb290cjEwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5nbG9iYWxzaWduLmNvbS9yb290LmNybDBHBgNVHSAEQDA+MDwGBFUdIAAwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xvYmFsc2lnbi5jb20vcmVwb3NpdG9yeS8wDQYJKoZIhvcNAQELBQADggEBACNw6c/ivvVZrpRCb8RDM6rNPzq5ZBfyYgZLSPFAiAYXof6r0V88xjPy847dHx0+zBpgmYILrMf8fpqHKqV9D6ZX7qw7aoXW3r1AY/itpsiIsBL89kHfDwmXHjjqU5++BfQ+6tOfUBJ2vgmLwgtIfR4uUfaNU9OrH0Abio7tfftPeVZwXwzTjhuzp3ANNyuXlava4BJrHEDOxcd+7cJiWOx37XMiwor1hkOIreoTbv3Y/kIvuX1erRjvlJDKPSerJpSZdcfL03v3ykzTr1EhkluEfSufFT90y1HonoMOFm8b50bOI7355KKL0jlrqnkckSziYSQtjipIcJDEHsXo4HA=-----END CERTIFICATE-----target:configMap:key: "ca-certificates.crt"namespaceSelector:matchLabels:trust: enabledkubectl apply -f - <<EOFapiVersion: trust.cert-manager.io/v1alpha1kind: Bundlemetadata:name: public-bundlespec:sources:- inLine: |-----BEGIN CERTIFICATE-----MIIETjCCAzagAwIBAgINAe5fFp3/lzUrZGXWajANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEQMA4GA1UECxMHUm9vdCBDQTEbMBkGA1UEAxMSR2xvYmFsU2lnbiBSb290IENBMB4XDTE4MDkxOTAwMDAwMFoXDTI4MDEyODEyMDAwMFowTDEgMB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDMJXaQeQZ4Ihb1wIO2hMoonv0FdhHFrYhy/EYCQ8eyip0EXyTLLkvhYIJG4VKrDIFHcGzdZNHr9SyjD4I9DCuul9e2FIYQebs7E4B3jAjhSdJqYi8fXvqWaN+JJ5U4nwbXPsnLJlkNc96wyOkmDoMVxu9bi9IEYMpJpij2aTv2y8gokeWdimFXN6x0FNx04Druci8unPvQu7/1PQDhBjPogiuuU6Y6FnOM3UEOIDrAtKeh6bJPkC4yYOlXy7kEkmho5TgmYHWyn3f/kRTvriBJ/K1AFUjRAjFhGV64l++td7dkmnq/X8ET75ti+w1s4FRpFqkD2m7pg5NxdsZphYIXAgMBAAGjggEiMIIBHjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUj/BLf6guRSSuTVD6Y5qL3uLdG7wwHwYDVR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9yb290cjEwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5nbG9iYWxzaWduLmNvbS9yb290LmNybDBHBgNVHSAEQDA+MDwGBFUdIAAwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xvYmFsc2lnbi5jb20vcmVwb3NpdG9yeS8wDQYJKoZIhvcNAQELBQADggEBACNw6c/ivvVZrpRCb8RDM6rNPzq5ZBfyYgZLSPFAiAYXof6r0V88xjPy847dHx0+zBpgmYILrMf8fpqHKqV9D6ZX7qw7aoXW3r1AY/itpsiIsBL89kHfDwmXHjjqU5++BfQ+6tOfUBJ2vgmLwgtIfR4uUfaNU9OrH0Abio7tfftPeVZwXwzTjhuzp3ANNyuXlava4BJrHEDOxcd+7cJiWOx37XMiwor1hkOIreoTbv3Y/kIvuX1erRjvlJDKPSerJpSZdcfL03v3ykzTr1EhkluEfSufFT90y1HonoMOFm8b50bOI7355KKL0jlrqnkckSziYSQtjipIcJDEHsXo4HA=-----END CERTIFICATE-----target:configMap:key: "ca-certificates.crt"namespaceSelector:matchLabels:trust: enabledEOFYou should see immediately that the application deployment is rolled out in your other terminal.
-
Once the new pod is running, use the following to confirm that you have only one CA certificate
ca-certificates.crt
file, the one we have just applied:kubectl exec -ti -n team-a $(kubectl get po -n team-a -l app=sleep-auto -o jsonpath='{.items[0].metadata.name}') -- cat /etc/ssl/certs/ca-certificates.crtYou should get exactly this output:
-----BEGIN CERTIFICATE-----MIIETjCCAzagAwIBAgINAe5fFp3/lzUrZGXWajANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEQMA4GA1UECxMHUm9vdCBDQTEbMBkGA1UEAxMSR2xvYmFsU2lnbiBSb290IENBMB4XDTE4MDkxOTAwMDAwMFoXDTI4MDEyODEyMDAwMFowTDEgMB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDMJXaQeQZ4Ihb1wIO2hMoonv0FdhHFrYhy/EYCQ8eyip0EXyTLLkvhYIJG4VKrDIFHcGzdZNHr9SyjD4I9DCuul9e2FIYQebs7E4B3jAjhSdJqYi8fXvqWaN+JJ5U4nwbXPsnLJlkNc96wyOkmDoMVxu9bi9IEYMpJpij2aTv2y8gokeWdimFXN6x0FNx04Druci8unPvQu7/1PQDhBjPogiuuU6Y6FnOM3UEOIDrAtKeh6bJPkC4yYOlXy7kEkmho5TgmYHWyn3f/kRTvriBJ/K1AFUjRAjFhGV64l++td7dkmnq/X8ET75ti+w1s4FRpFqkD2m7pg5NxdsZphYIXAgMBAAGjggEiMIIBHjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUj/BLf6guRSSuTVD6Y5qL3uLdG7wwHwYDVR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9yb290cjEwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5nbG9iYWxzaWduLmNvbS9yb290LmNybDBHBgNVHSAEQDA+MDwGBFUdIAAwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xvYmFsc2lnbi5jb20vcmVwb3NpdG9yeS8wDQYJKoZIhvcNAQELBQADggEBACNw6c/ivvVZrpRCb8RDM6rNPzq5ZBfyYgZLSPFAiAYXof6r0V88xjPy847dHx0+zBpgmYILrMf8fpqHKqV9D6ZX7qw7aoXW3r1AY/itpsiIsBL89kHfDwmXHjjqU5++BfQ+6tOfUBJ2vgmLwgtIfR4uUfaNU9OrH0Abio7tfftPeVZwXwzTjhuzp3ANNyuXlava4BJrHEDOxcd+7cJiWOx37XMiwor1hkOIreoTbv3Y/kIvuX1erRjvlJDKPSerJpSZdcfL03v3ykzTr1EhkluEfSufFT90y1HonoMOFm8b50bOI7355KKL0jlrqnkckSziYSQtjipIcJDEHsXo4HA=-----END CERTIFICATE----- -
This CA certificate is one that can be used to verify the authenticity of the website
https://bbc.co.uk
. We can validate this by usingcurl
from that container:kubectl exec -ti -n team-a $(kubectl get po -n team-a -l app=sleep-auto -o jsonpath='{.items[0].metadata.name}') -- curl -v https://bbc.co.ukWe can also verify that from this pod, we can no longer talk to google.com.
kubectl exec -ti -n team-a $(kubectl get po -n team-a -l app=sleep-auto -o jsonpath='{.items[0].metadata.name}') -- curl -v https://google.comExample TLS failure:
* Trying 142.250.200.46:443...* Connected to google.com (142.250.200.46) port 443 (#0)* ALPN: offers h2,http/1.1* TLSv1.3 (OUT), TLS handshake, Client hello (1):* CAfile: /etc/ssl/certs/ca-certificates.crt* CApath: none* TLSv1.3 (IN), TLS handshake, Server hello (2):* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):* TLSv1.3 (IN), TLS handshake, Certificate (11):* TLSv1.3 (OUT), TLS alert, unknown CA (560):* SSL certificate problem: unable to get local issuer certificate* Closing connection 0curl: (60) SSL certificate problem: unable to get local issuer certificateMore details here: https://curl.se/docs/sslcerts.htmlcurl failed to verify the legitimacy of the server and therefore could notestablish a secure connection to it. To learn more about this situation andhow to fix it, please visit the web page mentioned above.command terminated with exit code 60
Enforce Usage of Your CA Bundle
Using tools such as
Gatekeeper
& Kyverno we can require that particular
volume
& volumeMount
configurations are enforced when applications are
deployed to Kubernetes. With this method, a cluster administrator can setup
rules to automatically insert the relevant configuration to every pod.
This may be beneficial to enforce configuration but can be more opaque to your
cluster's application teams or tenants.
In this tutorial we will show how to use Gatekeeper
Gatekeeper
See the Gatekeeper directory for two example Assign
policies
designed to enforce the CA bundle being mounted on all pods from the
configMap
that trust-manager produces in each namespace.
-
Install Gatekeeper onto your cluster:
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/chartshelm repo updatehelm install gatekeeper/gatekeeper --name-template=gatekeeper --namespace gatekeeper-system --create-namespace --version v3.11.0 -
Create a policy that applies to all
pod
resources in theteam-a
namespace which mutatesPod
resource as they are applied with the requiredvolumes
configuration:apiVersion: mutations.gatekeeper.sh/v1kind: Assignmetadata:name: demo-trust-ca-volumespec:applyTo:- groups: [""]kinds: ["Pod"]versions: ["v1"]match:scope: Namespacedkinds:- apiGroups: ["*"]kinds: ["Pod"]namespaces: ["team-a"]excludedNamespaces: ["kube-system", "public", "kyverno", "gatekeeper-system"]# Mounting the volume named "ca-certificates" from ConfigMaplocation: "spec.volumes[name:ca-certificates]"parameters:assign:value:name: ca-certificatesconfigMap:name: public-bundledefaultMode: 0644optional: falseitems:- key: ca-certificates.crtpath: ca-certificates.crtkubectl apply -f gatekeeper/gatekeeper-trust-pod-ca-volume.yaml -
Create a policy that applies to all
pod
resources in theteam-a
namespace which mutates them with a requiredvolumeMounts
configuration:apiVersion: mutations.gatekeeper.sh/v1kind: Assignmetadata:name: demo-trust-ca-volumemountspec:applyTo:- groups: [""]kinds: ["Pod"]versions: ["v1"]match:scope: Namespacedkinds:- apiGroups: ["*"]kinds: ["Pod"]namespaces: ["team-a"]excludedNamespaces: ["kube-system", "public", "kyverno", "gatekeeper-system"]# All containers in a pod mounting to volumeMount named "ca-certificates"location: "spec.containers[name:*].volumeMounts[name:ca-certificates]"parameters:assign:value:mountPath: /etc/ssl/certs/name: ca-certificatesreadOnly: truekubectl apply -f gatekeeper/gatekeeper-trust-pod-ca-volumemount.yaml -
Switch to the
team-a
namespace and create the deployment with no priorvolume
orvolumeMount
configuration to see the mutations take effect once the pod is running:apiVersion: apps/v1kind: Deploymentmetadata:labels:app: test-assignname: test-assignnamespace: team-aspec:replicas: 1revisionHistoryLimit: 3selector:matchLabels:app: test-assignstrategy: {}template:metadata:labels:app: test-assignspec:containers:- command:- /bin/sh- -c- sleep 1dimage: quay.io/zenlab/curl:latestname: curlresources: {}volumeMounts: []volumes: []kubectl apply -f gatekeeper/deploy-novol.yaml -
Once applied validate that the
volume
andvolumeMount
have been applied:kubectl get po -n team-a -l app=test-assign -o yaml | yq '.items[0].spec' - -
Execute a shell into the pod and attempt to make an HTTPS connection to any publicly trusted website, for example:
https://bbc.co.uk
kubectl exec -n team-a -ti $(kubectl get pod -n team-a -l app=test-assign -o jsonpath='{.items[0].metadata.name}') -- curl -v https://bbc.co.ukSuccess looks like a valid 200 response from the web page.
Note that this should now work without any additional configuration. If you get an SSL error at the point, check that the correct
configMap
is referenced in thevolumes
section. -
Lastly we can apply a similar pod, except this time the CA certificates have been mounted explicitly. This is to show that with the Gatekeeper policies no action is taken if the relevant configuration is already present:
apiVersion: apps/v1kind: Deploymentmetadata:labels:app: test-assign-noopname: test-assign-noopnamespace: team-aspec:replicas: 1revisionHistoryLimit: 3selector:matchLabels:app: test-assign-nooptemplate:metadata:labels:app: test-assign-noopspec:containers:- command:- /bin/sh- -c- sleep 1dimage: quay.io/zenlab/curl:latestname: curlvolumeMounts:- mountPath: /etc/ssl/certs/name: ca-certificatesreadOnly: truevolumes:- name: ca-certificatesconfigMap:name: exampledefaultMode: 0644optional: falseitems:- key: ca-certificates.crtpath: ca-certificates.crtkubectl apply -f gatekeeper/deploy-withvol.yamlkubectl exec -n team-a -ti $(kubectl get pod -n team-a -l app=test-assign-noop -o jsonpath='{.items[0].metadata.name}') -- curl -v https://bbc.co.uk
Note: If you have problems with your Assign
policy resource, try checking
the Kubernetes events (kubectl get events
) for issues.
Public Trust with trust-manager
In this tutorial we have shown how you can manage Certificate Authority
certificates at a cluster level using trust-manager, and how to consume this
trusted Bundle
manually or by enforcing it with Gatekeeper. We have seen how
you can setup applications to auto deploy on changes to your trusted CAs.
Whilst this may appear to be more work for something that currently "works" in your environment, consider how this solution positions you to handle situations where you no longer trust a particular Certificate Authority.
Next time we will look at how simple it is to integrate Private Certificate Authorities into this trust management process.
Cleanup
To remove all resources deployed in this tutorial:
kubectl delete deployment -n team-a sleep-auto test-assign test-assign-noopkubectl delete bundle public-bundlekubectl delete assign demo-trust-ca-volume demo-trust-ca-volumemounthelm uninstall -n gatekeeper-system gatekeeperhelm uninstall -n stakater-reloader reloaderhelm uninstall -n cert-manager trust-managerhelm uninstall -n cert-manager cert-managerkubectl delete namespace cert-manager team-a stakater-reloader gatekeeper-systemkubectl delete crd -l gatekeeper.sh/system=yes