Although Traefik has some capabilities to handle certificates, it's better to use a service specialized on such task. Enter cert-manager, a popular certificate management service in the Kubernetes landscape.
The first time I tried to deploy cert-manager, the deployment failed because the nodes (in particular the server node) didn't had enough CPU cores to run the process properly. And when I managed to deploy cert-manager successfully, I noticed how my cluster's performance degraded severely. Eventually, I could fix these performance issues just by increasing the cores assigned as vCPUs to each VM in my K3s cluster.
Therefore, be aware that, depending on how you've configured your VMs, you may need to improve their assigned hardware capabilities (CPU in particular).
At the time of writing this, there's no official Kustomize way for deploying cert-manager. The default method is by applying a yaml manifest, but you can build your own Kustomize procedure with it (as you've done for the metrics-server deployment in the previous G028 guide).
-
In your kubectl client system, create a folder structure for cert-manager.
$ mkdir -p $HOME/k8sprjs/cert-manager/deploymentThe deployment project will be in its own
deploymentsubfolder because, later, you'll need to create another project for creating a self-signed wildcard certificate. This second Kustomize project will require the cert-manager service already deployed in your cluster, but must be kept independent from its deployment process. -
Create a
kustomization.yamlfile in thedeploymentsubfolder.$ touch $HOME/k8sprjs/cert-manager/deployment/kustomization.yaml -
Edit the
kustomization.yamlfile so it has the yaml content below.# cert-manager setup apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - 'https://github.com/jetstack/cert-manager/releases/download/v1.6.1/cert-manager.yaml'
You can find the URL for the most recent version of cert-manager right in the official documentation about the installation procedure, although you can also find it on the assets list of each release.
-
Deploy cert-manager with
kubectl.$ kubect apply -k $HOME/k8sprjs/cert-manager/deployment/You'll get a long output of lines indicating the many resources created by the deployment.
After the deployment has finished successfully, give it a minute or so to allow cert-manager to initialize itself and start its pods. Then, you can verify that cert-manager has deployed properly just by checking if its pods are Running.
$ kubectl -n cert-manager get pods
NAME READY STATUS RESTARTS AGE
cert-manager-cainjector-967788869-wg8pd 1/1 Running 0 116s
cert-manager-55658cdf68-6m5k2 1/1 Running 0 116s
cert-manager-webhook-6668fbb57d-f5xmc 1/1 Running 0 116sNotice the namespace cert-manager specified with the -n option in the kubectl command. The cert-manager service deploys itself in its own cert-manager namespace, as MetalLB deployed in its own metallb-system namespace.
Remember that you can also check out the all existing namespaces in your K3s cluster with kubectl.
$ kubectl get namespaces
NAME STATUS AGE
default Active 2d1h
kube-system Active 2d1h
kube-public Active 2d1h
kube-node-lease Active 2d1h
metallb-system Active 47h
cert-manager Active 2m51sTo help you to manage the certificates you put in your cluster, cert-manager has a plugin for kubectl. You'll have to install it in your kubectl client system and make it reachable through your user's $PATH. Assuming the same scenario as in the G026 guide, where you saw how to set up your kubectl client system, the installation of this cert-manager plugin requires the following steps
-
From the cert-manager GitHub releases page, download the
tar.gzfile that corresponds to the cert-manager version you installed in your cluster and to your kubectl client system. In this guide, you'll install cert-managerv1.6.1and the client system is assumed to be a Linux OS running on an amd64 hardware.$ wget https://github.com/jetstack/cert-manager/releases/download/v1.6.1/kubectl-cert_manager-linux-amd64.tar.gz -O $HOME/bin/kubectl-cert_manager-linux-amd64.tar.gz -
Extract the content of the downloaded
kubectl-cert_manager-linux-amd64.tar.gz.$ cd $HOME/bin $ tar xf kubectl-cert_manager-linux-amd64.tar.gz
This will extract two files, the
kubectl-cert_managerbinary and aLICENSEStext file that you can remove together with thekubectl-cert_manager-linux-amd64.tar.gz.$ rm kubectl-cert_manager-linux-amd64.tar.gz LICENSES
-
Restrict the binary's permissions.
$ chmod 700 kubectl-cert_manager
-
Test the cert-manager plugin with
kubectlby checking its version.$ kubectl cert-manager version Client Version: util.Version{GitVersion:"v1.6.1", GitCommit:"5ecf5b5617a4813ea8115da5dcfe3cd18b8ff047", GitTreeState:"clean", GoVersion:"go1.17.1", Compiler:"gc", Platform:"linux/amd64"} Server Version: &versionchecker.Version{Detected:"v1.6.1", Sources:map[string]string{"crdLabelVersion":"v1.6.1", "webhookPodImageVersion":"v1.6.1", "webhookPodLabelVersion":"v1.6.1", "webhookServiceLabelVersion":"v1.6.1"}} -
You can also check if the cert-manager API is accessible.
$ kubectl cert-manager check api The cert-manager API is ready
Know that the cert-manager's kubectl plugin has other several commands available, check them out in its official page.
In Kubernetes, a certificate has an associated secret object which is what truly contains the encrypted key. This is a problem in the sense that secret and configmap objects are not shared among namespaces. For instance, if you created a certificate in the cert-manager namespace, you wouldn't be able to use it directly (meaning, its associated secret) in the kube-system namespace. You need to replicate, or sync, the secret somehow in all the namespaces you want to use it.
What can you do to sync secrets among namespaces in your K3s cluster? There are some options to solve this problem.
-
The most basic procedure would be to create a certificate on each namespace in which it's required. This method would force you to create those certificates configured for concrete domains, since you don't want to have several different certificates for the same wildcard domain. Doable only when dealing with a small number of certificates, hard to maintain when their number increases.
-
Other procedure is creating one certificate and then cloning it on each namespace. The problem is that, although initially the certificates' secrets would be the same, right after the first renovation, those secrets would change and stop being in sync. This would force you to "sync" them (that is, overwriting the cloned secrets with the one you want to have in common) manually, something you can imagine is rather cumbersome.
-
In this GitHub page there's a definition for a Kubernetes object called ClusterSecret. As it's name implies, this object is designed to be a secret shared cluster wide. It's a very interesting and valid option, but it has been designed with only secret objects in mind, and also implies some manual tinkering on your certificates' secrets.
-
The ideal option is to have an addon capable of handling certificates cluster wide properly. And no, cert-manager is not capable of doing this, but in its documentation they recommend using an addon called kubed: "Kubed can keep ConfigMaps and Secrets synchronized across namespaces and/or clusters". Although it sounds that it could fit our case, this addon presents two problems:
- Kubed doesn't handle the cert-manager certificates themselves, you would still be forced to tinker with their corresponding secrets so they can be handled properly by kubed.
- Kubed needs Helm to be installed, no matter what. This is not a problem per se (it would only imply installing Helm in your kubectl client system), but in this guide series I want to stick with
kubectlsince it's the standard basic way of managing any Kubernetes cluster.
-
There's another addon, called Reflector, which not only handles "mirroring" of secrets and configmaps among different namespaces present in a cluster, but also has an extension that explicitly manages the secrets of cert-manager certificates automatically. Furthermore, it has a manifest file deployable with
kubectl. Hence why I've chosen this addon to make your domain's certificate available cluster wide.
Let's create its own Kustomize project for Reflector and deploy it in your K3s cluster.
-
Create the folder for the Kustomize project.
$ mkdir -p $HOME/k8prjs/reflector -
Create the
kustomization.yamlfile.$ touch $HOME/k8prjs/reflector/kustomization.yaml -
Put in
kustomization.yamlthe following content.# Reflector setup apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: kube-system resources: - https://github.com/emberstack/kubernetes-reflector/releases/latest/download/reflector.yaml
See that the link to the yaml manifest specifies "latest" as version which, at the time of writing this, is the v6.0.42 one. Also notice how there's a
namespaceparameter pointing tokube-system, this will make Reflector be deployed in that namespace. -
Deploy the Reflector Kustomize project.
$ kubectl apply -k $HOME/k8prjs/reflector/ -
The addon will be deployed in the
kube-systemnamespace and start a pod on one of your agents. Check it out withkubectl.$ kubectl -n kube-system get pods NAME READY STATUS RESTARTS AGE helm-install-traefik-crd--1-bjv95 0/1 Completed 0 2d4h helm-install-traefik--1-zb5gb 0/1 Completed 1 2d4h local-path-provisioner-64ffb68fd-zxm2v 1/1 Terminated 5 (60m ago) 2d4h coredns-85cb69466-9l6ws 1/1 Terminated 5 (146m ago) 2d4h traefik-74dd4975f9-tdv42 1/1 Terminated 5 (146m ago) 2d2h metrics-server-5b45cf8dbb-nv477 1/1 Terminated 3 (146m ago) 21h reflector-5f484c4868-8wgkz 1/1 Running 0 64s
Also notice how other pods appear with their
STATUSasTerminated, although they count asREADYand, therefore, they should have theRunningstatus like your newest reflector pod. This is an example of an odd consequence of configuring the graceful shutdown on your K3s nodes, as I already warned you about in the last section of the G025 guide.
You have the tools deployed in your cluster, now you can create a wildcard certificate for a domain. In this case, I'll configure the certificate for the domain I've been using all along this guide series, deimos.cloud, as an example.
-
Create a folder structure for a Kustomize project within the already existing
cert-managerpath.$ mkdir -p $HOME/k8sprjs/cert-manager/certificates/resources -
In the
resourcesdirectory, create three empty files as follows.$ touch $HOME/k8sprjs/cert-manager/certificates/resources/{certificates.namespace.yaml,cluster-issuer-selfsigned.cluster-issuer.cert-manager.yaml,wildcard.deimos.cloud-tls.certificate.cert-manager.yaml}Each one will contain the yaml describing a particular resource required for setting up the certificate.
-
In
certificates.namespace.yamlput the following yaml.apiVersion: v1 kind: Namespace metadata: name: certificates
This is the
certificatesnamespace, which will help you to organize your own certificates and distinguish them from any other certificates and secrets already present in your cluster. -
In the
cluster-issuer-selfsigned.cluster-issuer.cert-manager.yamlfile, copy the yaml below.# Generic self-signed cluster-wide issuer for certificates apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: cluster-issuer-selfsigned spec: selfSigned: {}
This is the issuer that will sign your certificates. Notice several things in the short yaml above.
-
The
apiVersionpoints to the cert-manager API, not to the Kubernetes one. -
The
kindisClusterIssuer(a cert-manager kind, not a Kubernetes one), meaning this particular issuer will be available cluster wide. -
The
nameis a descriptive string, like the yaml filename. -
Within the
specsection, you see the empty parameterselfSigned. This means that this issuer is of the simplest type you can have, the self signed one. It's not trusted by browsers, but it's enough to generate certificates that you can use within your own local or home network.
-
-
In
wildcard.deimos.cloud-tls.certificate.cert-manager.yaml, copy the whole yaml below.# Wilcard certificate for deimos.cloud apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: wildcard.deimos.cloud-tls namespace: certificates spec: secretName: wildcard.deimos.cloud-tls secretTemplate: annotations: reflector.v1.k8s.emberstack.com/reflection-allowed: "true" reflector.v1.k8s.emberstack.com/reflection-allowed-namespaces: "kube-system" reflector.v1.k8s.emberstack.com/reflection-auto-enabled: "true" reflector.v1.k8s.emberstack.com/reflection-auto-namespaces: "kube-system" duration: 8760h # No certificate should last more than a year renewBefore: 720h # Certificates must be renewed some time before they expire (30 days) isCA: false subject: organizations: - "Deimos" privateKey: algorithm: ECDSA size: 384 encoding: PKCS8 rotationPolicy: Always dnsNames: - "*.deimos.cloud" - "deimos.cloud" issuerRef: name: cluster-issuer-selfsigned kind: ClusterIssuer group: cert-manager.io
To know more about all the parameters shown above, check the cert-manager v1 api document here and also the Reflector's documentation about its cert-manager support. Still, I'll explain below some particular details of this yaml file.
-
Many of the parameters are optional, and there are more that are not used here.
-
Here the API is also a cert-manager one. Be careful of the
apiVersionyou use. Cert-manager has several, each with its own API documentation. -
The
spec.secretTemplatesection allows you to put metadata annotations in the secret generated for this certificate, something you need to use to put the values Reflector needs to clone this secret in other namespaces. Thereflector.v1.k8s.emberstack.comparameters are the ones that enable Reflector to manage the secret of this certificate.reflection-allowedallows Reflector to mirror this certificate's secret in other namespaces.reflection-allowed-namespacescontains the list of namespaces in which Reflector has to clone this certificate's secret.reflection-auto-enabledallows Reflector to clone automatically this certificate's secret in other namespaces.reflection-auto-namespacesis the list of namespaces in which Reflector can clone this certificate's secret automatically.
BEWARE!
Reflector won't notices the changes done to the annotations in the certificate resource itself. It's only aware of what's specified in the directly related secret generated from this certificate. In upcoming guides I'll show you how to deal with changes in these annotations so Reflector does its thing as expected. -
The parameter
spec.isCAallows you to turn a certificate into a Certificate Authority. When the value istrue, you can use this certificate to sign other certificates issued by other issuers that rely on this CA's secret. In this case is left asfalsefor not complicating things further at this point. You can find an example of how to bootstrap an issuer with a self-signed CA in this cert-manager page. -
In the
spec.privateKeysection, be careful of always havingrotationPolicyset asAlways. This makes cert-manager regenerate the certificate's secret rather than reusing the current one. This policy about private key rotation is also described in the cert-manager documentation. -
In the
spec.dnsNamesyou can put any domain names you like, not necessarily just the ones related to a particular main domain. For instance, you can haveyour.domain.comandanother.domain.ioput in that list. -
In the
spec.issuerRefyou specify the issuer of this certificate, in this case thecluster-issuer-selfsigned-mainone you created in previous steps. Be careful of always also specifying itskind, in particular forClusterIssuertypes, so you know clearly what kind of issuer you've used with each certificate.
-
-
Next, create the
kustomization.yamlfile in thecertificatesfolder.$ touch $HOME/k8sprjs/cert-manager/certificates/kustomization.yaml -
Copy in
kustomization.yamlthe following yaml.# Certificates deployment apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - resources/certificates.namespace.yaml - resources/cluster-issuer-selfsigned.cluster-issuer.cert-manager.yaml - resources/wildcard.deimos.cloud-tls.certificate.cert-manager.yaml
-
Apply the Kustomize project into your cluster.
$ kubectl apply -k $HOME/k8sprjs/cert-manager/certificates -
Confirm that the resources have been deployed in the cluster.
$ kubectl get namespaces NAME STATUS AGE default Active 2d6h kube-system Active 2d6h kube-public Active 2d6h kube-node-lease Active 2d6h metallb-system Active 2d4h cert-manager Active 4h41m certificates Active 87s $ kubectl -n kube-system get clusterissuer NAME READY AGE cluster-issuer-selfsigned True 3m22s $ kubectl -n certificates get certificate NAME READY SECRET AGE wildcard.deimos.cloud-tls True wildcard.deimos.cloud-tls 4m $ kubectl get secrets -A | grep wildcard certificates wildcard.deimos.cloud-tls kubernetes.io/tls 3 4m30s kube-system wildcard.deimos.cloud-tls kubernetes.io/tls 3 4m29sThe cluster issuer shows up and also your certificate is there, in the namespace
certificates. Your certificate's secret is too in the samecertificatesnamespace, but Reflector has done its job automatically and has reflected the secret in thekube-systemnamespace.
BEWARE!
If you delete the certificate from the cluster, the copies of its secret won't be removed with it. You'll have to delete them manually.
Remember that the kubectl cert-manager plugin can help you in handling your certificates. For instance, you would execute the following command to see the status of the certificate you've created before.
$ kubectl cert-manager status certificate -n certificates wildcard.deimos.cloud-tls
Name: wildcard.deimos.cloud-tls
Namespace: certificates
Created at: 2021-11-30T18:41:42+01:00
Conditions:
Ready: True, Reason: Ready, Message: Certificate is up to date and has not expired
DNS Names:
- *.deimos.cloud
- deimos.cloud
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Issuing 10m cert-manager Issuing certificate as Secret does not exist
Normal Generated 10m cert-manager Stored new private key in temporary Secret resource "wildcard.deimos.cloud-tls-6rfrc"
Normal Requested 10m cert-manager Created new CertificateRequest resource "wildcard.deimos.cloud-tls-b4jx4"
Normal Issuing 10m cert-manager The certificate has been successfully issued
Issuer:
Name: cluster-issuer-selfsigned
Kind: ClusterIssuer
Conditions:
Ready: True, Reason: IsReady, Message:
Events: <none>
Secret:
Name: wildcard.deimos.cloud-tls
Issuer Country:
Issuer Organisation: Deimos
Issuer Common Name:
Key Usage: Digital Signature, Key Encipherment
Extended Key Usages:
Public Key Algorithm: ECDSA
Signature Algorithm: ECDSA-SHA384
Subject Key ID:
Authority Key ID:
Serial Number: efd14f0cb15d9f179b9e5c68bb6a3205
Events: <none>
Not Before: 2021-11-30T18:41:43+01:00
Not After: 2022-11-30T18:41:43+01:00
Renewal Time: 2022-10-31T18:41:43+01:00
No CertificateRequest found for this CertificateYou can find the Kustomize projects for the cert-manager and Reflector deployments in the following attached folders.
k8sprjs/cert-managerk8sprjs/reflector
$HOME/bin$HOME/k8sprjs/cert-manager$HOME/k8sprjs/cert-manager/deployment$HOME/k8sprjs/cert-manager/certificates$HOME/k8sprjs/cert-manager/certificates/resources$HOME/k8sprjs/reflector
$HOME/bin/kubectl-cert_manager$HOME/k8sprjs/cert-manager/deployment/kustomization.yaml$HOME/k8sprjs/cert-manager/certificates/kustomization.yaml$HOME/k8sprjs/cert-manager/certificates/resources/certificates.namespace.yaml$HOME/k8sprjs/cert-manager/certificates/resources/cluster-issuer-selfsigned.cluster-issuer.cert-manager.yaml$HOME/k8sprjs/cert-manager/certificates/resources/wildcard.deimos.cloud-tls.certificate.cert-manager.yaml$HOME/k8sprjs/reflector/kustomization.yaml
- cert-manager official site
- cert-manager on GitHub
- cert-manager installation with Kubectl
- Using Kubectl's new Kustomize support for per-environment deployment of cert-manager resources
- cert-manager compatibility with Kubernetes Platform Providers
- cert-manager kubectl plugin
- cert-manager API reference docs
- cert-manager docs
- cert-manager on GitHub
- Installing and using Cert-Manager in Kubernetes
- Use of Let's Encrypt wildcard certs in Kubernetes
- Setting up HTTPS with cert-manager (self-signed, LetsEncrypt) in kubernetes
- Creating Self Signed Certificates on Kubernetes
- Installing and using Cert-Manager in Kubernetes
- Install Certificate Manager Controller in Kubernetes
- How to configure Traefik on Kubernetes with Cert-manager?
- PKCS#1 and PKCS#8 format for RSA private key
- Add to documentation: change default port of webhook when using hostNetwork and default Kubelet port settings
- add "webhook.hostNetwork" to helm chart
- Compatibility with Kubernetes Platform Providers. AWS EKS
- ClusterSecret
- Kubed official page
- Kubed on GitHub
- Faq cert-manager. Syncing Secrets Across Namespaces
- Sharing secret across namespaces
<< Previous (G028. K3s cluster setup 11) | +Table Of Contents+ | Next (G030. K3s cluster setup 13) >>