Raymii.org
Quis custodiet ipsos custodes?Home | About | All pages | Cluster Status | RSS Feed
nameConstraints on your Self Signed Root CA in Kubernetes with cert-manager
Published: 17-07-2024 23:22 | Author: Remy van Elst | Text only version of this article
Table of Contents
If you have set up a Self Signed Root CA 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 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.
We'll end up with the following error for certificates not under your homelab domain name:
Kubernetes cert-manager
has experimental support for nameConstraints
,
quoting the documentation:
This is an Alpha Feature and is only enabled with the
--feature-gates=NameConstraints=true
option set on both thecontroller
andwebhook
components.
More info on feature gates can be found here:
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
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 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.
Adding nameConstraints to a Root Certificate
We have to add the following to the spec
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:
If you manually want to set up nameConstraints
with openssl
, this is a
good guide.