Manage TLS Certificates in a Cluster
Kubernetes provides a certificates.k8s.io
API, which lets you provision TLS
certificates signed by a Certificate Authority (CA) that you control. These CA
and certificates can be used by your workloads to establish trust.
certificates.k8s.io
API uses a protocol that is similar to the ACME
draft.
certificates.k8s.io
API are signed by a
dedicated CA. It is possible to configure your cluster to use the cluster root
CA for this purpose, but you should never rely on this. Do not assume that
these certificates will validate against the cluster root CA.
Before you begin
You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. It is recommended to run this tutorial on a cluster with at least two nodes that are not acting as control plane hosts. If you do not already have a cluster, you can create one by using minikube or you can use one of these Kubernetes playgrounds:
You need the Some steps in this page use the Trusting the custom CA from an application running as a pod usually requires
some extra application configuration. You will need to add the CA certificate
bundle to the list of CA certificates that the TLS client or server trusts. For
example, you would do this with a golang TLS config by parsing the certificate
chain and adding the parsed certificates to the Even though the custom CA certificate may be included in the filesystem (in the
ConfigMap If you want to use a custom certificate authority for your workloads, you should generate
that CA separately, and distribute its CA certificate using a
ConfigMap that your pods
have access to read. The following section demonstrates how to create a TLS certificate for a
Kubernetes service accessed through DNS. Generate a private key and certificate signing request (or CSR) by running
the following command: Where This command generates two files; it generates Generate a CSR manifest (in YAML), and send it to the API server. You can do that by
running the following command: Notice that the The CSR should now be visible from the API in a Pending state. You can see
it by running: Approving the certificate signing request
is either done by an automated approval process or on a one off basis by a cluster
administrator. If you're authorized to approve a certificate request, you can do that
manually using You should now see the following: This means the certificate request has been approved and is waiting for the
requested signer to sign it. Next, you'll play the part of a certificate signer, issue the certificate, and upload it to the API. A signer would typically watch the CertificateSigningRequest API for objects with its You need an authority to provide the digital signature on the new certificate. First, create a signing certificate by running the following: You should see output similar to: This produces a certificate authority key file ( Use a You should see the output similar to: This produces a signed serving certificate file, Finally, populate the signed certificate in the API object's status: Once the CSR is approved and the signed certificate is uploaded, run: The output is similar to: Now, as the requesting user, you can download the issued certificate
and save it to a Now you can populate Finally, you can populate A Kubernetes administrator (with appropriate permissions) can manually approve
(or deny) CertificateSigningRequests by using the The ability to approve CSRs decides who trusts whom within your environment. The
ability to approve CSRs should not be granted broadly or lightly. You should make sure that you confidently understand both the verification requirements
that fall on the approver and the repercussions of issuing a specific certificate
before you grant the Whether a machine or a human using kubectl as above, the role of the approver is
to verify that the CSR satisfies two requirements: If and only if these two requirements are met, the approver should approve
the CSR and otherwise should deny the CSR. For more information on certificate approval and access control, read
the Certificate Signing Requests
reference page. This page assumes that a signer is setup to serve the certificates API. The
Kubernetes controller manager provides a default implementation of a signer. To
enable it, pass the cfssl
tool. You can download cfssl
from
jq
tool. If you don't have jq
, you can
install it via your operating system's software sources, or fetch it from
https://stedolan.github.io/jq/.Trusting TLS in a cluster
RootCAs
field in the
kube-root-ca.crt
),
you should not use that certificate authority for any purpose other than to verify internal
Kubernetes endpoints. An example of an internal Kubernetes endpoint is the
Service named kubernetes
in the default namespace.Requesting a certificate
Create a certificate signing request
cat <<EOF | cfssl genkey - | cfssljson -bare server
{
"hosts": [
"my-svc.my-namespace.svc.cluster.local",
"my-pod.my-namespace.pod.cluster.local",
"192.0.2.24",
"10.0.34.2"
],
"CN": "my-pod.my-namespace.pod.cluster.local",
"key": {
"algo": "ecdsa",
"size": 256
}
}
EOF
192.0.2.24
is the service's cluster IP,
my-svc.my-namespace.svc.cluster.local
is the service's DNS name,
10.0.34.2
is the pod's IP and my-pod.my-namespace.pod.cluster.local
is the pod's DNS name. You should see the output similar to:2022/02/01 11:45:32 [INFO] generate received request
2022/02/01 11:45:32 [INFO] received CSR
2022/02/01 11:45:32 [INFO] generating key: ecdsa-256
2022/02/01 11:45:32 [INFO] encoded CSR
server.csr
containing the PEM
encoded server-key.pem containing the PEM encoded key to the certificate that
is still to be created.Create a CertificateSigningRequest object to send to the Kubernetes API
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: my-svc.my-namespace
spec:
request: $(cat server.csr | base64 | tr -d '\n')
signerName: example.com/serving
usages:
- digital signature
- key encipherment
- server auth
EOF
server.csr
file created in step 1 is base64 encoded
and stashed in the .spec.request
field. You are also requesting a
certificate with the "digital signature", "key encipherment", and "server
auth" key usages, signed by an example example.com/serving
signer.
A specific signerName
must be requested.
View documentation for supported signer names
for more information.kubectl describe csr my-svc.my-namespace
Name: my-svc.my-namespace
Labels: <none>
Annotations: <none>
CreationTimestamp: Tue, 01 Feb 2022 11:49:15 -0500
Requesting User: yourname@example.com
Signer: example.com/serving
Status: Pending
Subject:
Common Name: my-pod.my-namespace.pod.cluster.local
Serial Number:
Subject Alternative Names:
DNS Names: my-pod.my-namespace.pod.cluster.local
my-svc.my-namespace.svc.cluster.local
IP Addresses: 192.0.2.24
10.0.34.2
Events: <none>
Get the CertificateSigningRequest approved
kubectl
; for example:kubectl certificate approve my-svc.my-namespace
certificatesigningrequest.certificates.k8s.io/my-svc.my-namespace approved
kubectl get csr
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
my-svc.my-namespace 10m example.com/serving yourname@example.com <none> Approved
Sign the CertificateSigningRequest
signerName
,
check that they have been approved, sign certificates for those requests,
and update the API object status with the issued certificate.Create a Certificate Authority
cat <<EOF | cfssl gencert -initca - | cfssljson -bare ca
{
"CN": "My Example Signer",
"key": {
"algo": "rsa",
"size": 2048
}
}
EOF
2022/02/01 11:50:39 [INFO] generating a new CA key and certificate from CSR
2022/02/01 11:50:39 [INFO] generate received request
2022/02/01 11:50:39 [INFO] received CSR
2022/02/01 11:50:39 [INFO] generating key: rsa-2048
2022/02/01 11:50:39 [INFO] encoded CSR
2022/02/01 11:50:39 [INFO] signed certificate with serial number 263983151013686720899716354349605500797834580472
ca-key.pem
) and certificate (ca.pem
).Issue a certificate
{
"signing": {
"default": {
"usages": [
"digital signature",
"key encipherment",
"server auth"
],
"expiry": "876000h",
"ca_constraint": {
"is_ca": false
}
}
}
}
server-signing-config.json
signing configuration and the certificate authority key file
and certificate to sign the certificate request:kubectl get csr my-svc.my-namespace -o jsonpath='{.spec.request}' | \
base64 --decode | \
cfssl sign -ca ca.pem -ca-key ca-key.pem -config server-signing-config.json - | \
cfssljson -bare ca-signed-server
2022/02/01 11:52:26 [INFO] signed certificate with serial number 576048928624926584381415936700914530534472870337
ca-signed-server.pem
.Upload the signed certificate
kubectl get csr my-svc.my-namespace -o json | \
jq '.status.certificate = "'$(base64 ca-signed-server.pem | tr -d '\n')'"' | \
kubectl replace --raw /apis/certificates.k8s.io/v1/certificatesigningrequests/my-svc.my-namespace/status -f -
jq
, you can also save the JSON output to a file, populate this field manually, and
upload the resulting file.
kubectl get csr
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
my-svc.my-namespace 20m example.com/serving yourname@example.com <none> Approved,Issued
Download the certificate and use it
server.crt
file by running the following:kubectl get csr my-svc.my-namespace -o jsonpath='{.status.certificate}' \
| base64 --decode > server.crt
server.crt
and server-key.pem
in a
Secret
that you could later mount into a Pod (for example, to use with a webserver
that serves HTTPS).kubectl create secret tls server --cert server.crt --key server-key.pem
secret/server created
ca.pem
into a ConfigMap
and use it as the trust root to verify the serving certificate:kubectl create configmap example-serving-ca --from-file ca.crt=ca.pem
configmap/example-serving-ca created
Approving CertificateSigningRequests
kubectl certificate approve
and kubectl certificate deny
commands. However if you intend
to make heavy usage of this API, you might consider writing an automated
certificates controller.approve
permission.
Configuring your cluster to provide signing
--cluster-signing-cert-file
and
--cluster-signing-key-file
parameters to the controller manager with paths to
your Certificate Authority's keypair.