This is a text-only version of the following page on https://raymii.org: --- Title : nameConstraints on your Self Signed Root CA in Kubernetes with cert-manager Author : Remy van Elst Date : 17-07-2024 23:22 URL : https://raymii.org/s/tutorials/nameConstraints_on_your_Self_Signed_Root_CA_in_Kubernetes_with_cert_manager.html Format : Markdown/HTML --- If you have [set up a Self Signed Root CA](/s/tutorials/Self_signed_Root_CA_in_Kubernetes_with_k3s_cert-manager_and_traefik.html) for your local Kubernetes Cluster and have trusted the Root Certificate, you are at risk if the key is compromised. If the key is stolen, it can be used to create trusted certificates for everything. Luckily there is something we can do, using `nameConstraints` to limit the scope of the Root Certificate to, in our case, a single domain (`k3s.homelab.mydomain.org`). This means that if your key would be compromised, it would only be able to issue certificates for anything under that domain, not your bank for example.

Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below:

I'm developing an open source monitoring app called Leaf Node Monitoring, for windows, linux & android. Go check it out!

Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.

You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $200 credit for 60 days. Spend $25 after your credit expires and I'll get $25!

[RFC 5280](https://tools.ietf.org/html/rfc5280) provides for something called `Name Constraints`, which allow an X.509 CA to have a scope limited to certain names, including the parent domains of the certificates issued by the CA. For example, a host constraint of `.example.com` allows the CA to issue certificates for anything under `.example.com`, but not any other host. For other hosts, clients will fail to validate the chain. [More info here] (https://web.archive.org/web/20240415204151/http://www.pkiglobe.org/name_constraints.html). We'll end up with the following error for certificates not under your homelab domain name: ![firefox error](/s/inc/img/k3s-cert-4.png) Kubernetes `cert-manager` has experimental support for `nameConstraints`, quoting [the documentation] (https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.CertificateSpec): > This is an Alpha Feature and is only enabled with the `--feature-gates=NameConstraints=true` option set on both the `controller` and `webhook` components. More info on [feature gates](https://web.archive.org/web/20240717192139/https://cert-manager.io/docs/installation/configuring-components/) can be found [here](https://web.archive.org/web/20240717192139/https://cert-manager.io/docs/installation/configuring-components/): > Alpha: feature is not yet stable and might be removed or changed in the future. Alpha features are disabled by default and need to be explicitly enabled by the user (to test the feature). ### Configure the feature gate on cert-manager If you have followed my [previous guide on setting up a Self Signed Root CA](/s/tutorials/Self_signed_Root_CA_in_Kubernetes_with_k3s_cert-manager_and_traefik.html) you will already have a working setup for `cert-manager` via the Helm chart. If not, go follow that guide. If you have a different installation method, check [here](https://web.archive.org/web/20240717192139/https://cert-manager.io/docs/installation/configuring-components/) for more info on configuring feature gates. You have append the following two parameters to the Helm install command to enable the feature gate for `NameConstraints`: --set webhook.featureGates="NameConstraints=true" \ --set featureGates="NameConstraints=true" The full Helm install command then becomes: helm upgrade --install \ cert-manager jetstack/cert-manager \ --namespace cert-manager \ --create-namespace \ --version v1.15.1 \ --set crds.enabled=true \ --set webhook.timeoutSeconds=4 \ --set replicaCount=2 \ --set podDisruptionBudget.enabled=true \ --set podDisruptionBudget.minAvailable=1 \ --set webhook.featureGates="NameConstraints=true" \ --set featureGates="NameConstraints=true" You can also choose to create a `values.yaml` file for this Helm install. All possible options can be found [here](https://artifacthub.io/packages/helm/cert-manager/cert-manager). ### Adding nameConstraints to a Root Certificate We have to add the following [to the `spec`](https://web.archive.org/web/20240717191851/https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.CertificateSpec) of our Root CA: vim spnw-root-ca.yaml Add under `spec`: nameConstraints: critical: true permitted: dnsDomains: - k3s.homelab.mydomain.org Repeat this for `spnw-intermediate-ca1.yaml` You have to delete the current CA and corresponding secret first: kubectl -n cert-manager delete secret spnw-root-ca-secret kubectl -n cert-manager delete -f spnw-root-ca.yaml kubectl -n cert-manager delete -f spnw-intermediate-ca1.yaml Apply the changes: kubectl -n cert-manager apply -f . If you have not enabled the feature gate you will receive an error: for: "spnw-root-ca.yaml": error when patching "spnw-root-ca.yaml": admission webhook "webhook.cert-manager.io" denied the request: spec.nameConstraints: Forbidden: feature gate NameConstraints must be enabled You can now get the `Secret` and check to see if the `nameConstraint` is present: kubectl get secret spnw-root-ca-secret -n cert-manager -o json | jq -r '.data["tls.crt"]' | base64 --decode | openssl x509 -noout -text Output: Certificate: Data: Version: 3 (0x2) [...] X509v3 extensions: [...] X509v3 Basic Constraints: critical CA:TRUE X509v3 Name Constraints: critical Permitted: DNS:k3s.homelab.mydomain.org The following part: X509v3 Name Constraints: critical Permitted: DNS:k3s.homelab.mydomain.org means that any certificate issued for a hostname not within `k3s.homelab.mydomain.org` will fail to validate, which is exactly what we want. You must remove the old certificate from your trusted root store (Windows `certmgr.msc` or Firefox) and add this new certificate. Then you must re-issue all certificates from services you have already issued. ### Validating `nameConstraints` To test the constraint, I've set up a wildcard DNS domain to `k8s.homelab.mydomain.org` (note `k8s` instead of `k3s`) and issued a certificate for my `echoapp` test service. Using OpenSSL to validate that fails with a `permitted subtree violation`, which is the exact error for this failure, the domain does not match the `nameConstraints`: openssl verify -CAfile <(kubectl -n cert-manager get secret spnw-root-ca-secret -o jsonpath='{.data.tls\.crt}' | base64 --decode) -untrusted <(kubectl -n cert-manager get secret spnw-intermediate-ca1-secret -o jsonpath='{.data.tls\.crt}' | base64 --decode) <(kubectl -n echoapp get secret echo-cert -o jsonpath='{.data.tls\.crt}' | base64 --decode) Output: CN = echo.k8s.homelab.mydomain.org error 47 at 0 depth lookup: permitted subtree violation error /dev/fd/61: verification failed Firefox also complains with an `SEC_ERROR_CERT_NOT_IN_NAME_SPACE` error: ![firefox error](/s/inc/img/k3s-cert-4.png) If you manually want to set up `nameConstraints` with `openssl`, [this is a good guide] (https://web.archive.org/web/20240717202700/https://systemoverlord.com/2020/06/14/private-ca-with-x-509-name-constraints.html). --- License: All the text on this website is free as in freedom unless stated otherwise. This means you can use it in any way you want, you can copy it, change it the way you like and republish it, as long as you release the (modified) content under the same license to give others the same freedoms you've got and place my name and a link to this site with the article as source. This site uses Google Analytics for statistics and Google Adwords for advertisements. You are tracked and Google knows everything about you. Use an adblocker like ublock-origin if you don't want it. All the code on this website is licensed under the GNU GPL v3 license unless already licensed under a license which does not allows this form of licensing or if another license is stated on that page / in that software: This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Just to be clear, the information on this website is for meant for educational purposes and you use it at your own risk. I do not take responsibility if you screw something up. Use common sense, do not 'rm -rf /' as root for example. If you have any questions then do not hesitate to contact me. See https://raymii.org/s/static/About.html for details.