Kubernetes is an incredible tool for deploying, scaling, and managing containerized applications. One crucial aspect of kubernetes security is ensuring that communication between different entities is secure. By default, kubernetes management network is secure and pod network is handled by 3rd party plugin which mostly support encryption.
Today we will focus on properly securing outside-in web communication to our cluster with Cert-Manager and self-signed certificates. We assume that you have access to working kubernetes cluster with ingress controller.
Setting up the certificate chain
Self-signed certificates are digital certificates issued by the same entity that will be using them. In other words, you’re essentially issuing a certificate to yourself. This approach is often preferred when you don’t have access to a public CA or when you want to keep your traffic private.
Usually you don’t have just one cluster on premise but a combination of cluster, vms, servers etc. To make management of certificates easier, we will build a certificate chain and use dedicated intermediate certificate to sign certificates in the cluster.
# file: create-certificates.yaml---- name:Ensure ca server has needed certificateshosts:localhostconnection:localtasks:- name:Ensure ssl directories are createdansible.builtin.file:path:"{{ item.path }}"state:directorymode:"{{ item.mode }}"loop:- {path:"./private", mode:"0700"}- {path:"./certs", mode:"0755"}- {path:"./csr", mode:"0755"}- name:Ensure root ca private key is presentcommunity.crypto.openssl_privatekey:path:./private/root-ca.keysize:4096- name:Ensure root ca csr is presentcommunity.crypto.openssl_csr:path:./csr/root-ca.csrprivatekey_path:./private/root-ca.keycommon_name:Ansible CAbasic_constraints:- "CA:TRUE"basic_constraints_critical:truekey_usage:- keyCertSignkey_usage_critical:true- name:Ensure root ca self signed OpenSSL certificate is presentcommunity.crypto.x509_certificate:path:./certs/root-ca.crtprivatekey_path:./private/root-ca.keycsr_path:./csr/root-ca.csrprovider:selfsignedselfsigned_not_after:"+36500d"- name:Ensure kubernetes intermediate OpenSSL private key is presentcommunity.crypto.openssl_privatekey:path:./private/intermediate-kubernetes.keysize:4096- name:Ensure kubernetes intermediate csr is presentcommunity.crypto.openssl_csr:path:./csr/intermediate-kubernetes.csrprivatekey_path:./private/intermediate-kubernetes.keycommon_name:Kubernetes CAbasic_constraints:- "CA:TRUE"basic_constraints_critical:truekey_usage:- keyCertSignkey_usage_critical:true- name:Ensure kubernetes intermediate self signed OpenSSL certificate is presentcommunity.crypto.x509_certificate:path:./certs/intermediate-kubernetes.crtprivatekey_path:./private/intermediate-kubernetes.keycsr_path:./csr/intermediate-kubernetes.csrprovider:owncaownca_path:./certs/root-ca.crtownca_privatekey_path:./private/root-ca.keyownca_not_after:"+36500d"- name:Set certificate order for cert-manageransible.builtin.set_fact:intermediate_kubernetes_chain_files:- ./certs/intermediate-kubernetes.crt- ./certs/root-ca.crt- name:Read certificate content in ordercommand:awk 1 {{ intermediate_kubernetes_chain_files | join(" ") }}register:intermediate_kubernetes_chain_contents- name:Ensure kubernetes intermediate chain file is presentcopy:dest:./certs/intermediate-kubernetes-chain.crtcontent:"{{ intermediate_kubernetes_chain_contents.stdout_lines | unique |join('\n') }}"
Securing and managing your certificates is outside of the scope of this post but make sure you follow best practice for managing sensitive files.
1
ansible-playbook create-certificates.yaml
After executing the playbook you will have access to intermediate certificate file ./certs/intermediate-kubernetes-chain.crt and corresponding private key ./private/intermediate-kubernetes.key.
Setting Up Cert-Manager with Self-Signed Certificates
Cert-Manager is a tool for managing TLS/SSL certificates in Kubernetes. Install Cert-Manager using following command. Make sure that the version is compatible with your current kubernetes version.
Once installed, create a secret object to hold information about the certificate and the private key from previous step. Both certificate and the key values need to be base64 encoded. You can encode the values with:
1
base64 -w 0 <file>
Secret has to be in the same namespace as Cert-Manager, by default that is cert-manager. Secret will be used by cluster issuer object to sign certificates in your cluster. Find manifest file below.
# cluster-issuer-and-secret.yaml---apiVersion:v1kind:Secretmetadata:name:nginx-ca-secretnamespace:cert-managertype:Opaquedata:tls.crt:### enter base 64 encoded certificate from step beforetls.key:### enter base 64 encoded key from step before---apiVersion:cert-manager.io/v1kind:ClusterIssuermetadata:name:nginx-clusterissuerspec:ca:secretName:nginx-ca-secret
Create kubernetes objects and check that cluster issuer is in ready state.
1
2
kubectl apply -f cluster-issuer-and-secret.yaml
kubectl get clusterissuer
Example Ingress with Self-Signed Certificates
Now lets see it all in action. We will issue new certificate with cluster issuer and use it with our test application and ingress.
Test that certificate is issued and is in ready state with kubectl get certificate. By default self signed certificates are not trusted by browsers. Open your browser and add your root certificate ./certs/root-ca.crt to trusted certificates on your local machine.
Ensure that the domain in your ingress object resolves to your ingress controller. For testing purposes you can edit your local /etc/hosts/ file.
Test that you have secure access to the application on https://test.mycluster.local and that you are not getting any warnings in your browser.
Conclusion
In this post, we explored how to use Cert-Manager with self-signed certificates to secure our Kubernetes cluster. We issued root certificate and dedicated intermediate certificate for kubernetes cluster. After that we created and configured cluster issuer object for issuing certificates in the cluster and tested everything with test application.