In this proof of concept (POC), we will explore the installation and management of a stateful Key Encryption Service (KES) within a Kubernetes (k8s) ecosystem. This guide facilitates cryptographic operations seamlessly, without exposing sensitive key material to consuming applications.
Implementing KES within Kubernetes in a stateful configuration ensures the persistence of encryption keys through pod lifecycle events and restarts. This setup offers an architectural resilience that is especially crucial in environments where relying on external Key Management Systems (KMS) is not an option or preferred.
When you have several million billion objects generated from AI/ML workloads such as LLMs, as several hundreds of applications access them simultaneously, you need to ensure the data encryption/decryption layer does not become the bottleneck and is as fast as possible since each object requires its own unique key. By storing these potentially several billions of keys in a stateful KES backend, even if the KMS goes offline for an extended period, keys can be saved to the KES until the KMS comes back online.
Prerequisites Before proceeding, ensure you have:
Kubernetes CLI (kubectl) installed and configured. Access to a Kubernetes cluster with appropriate permissions. MinIO Operator and CLI (mc) ready for deployment. Basic knowledge of Kubernetes and encryption concepts. Deploy MinIO Operator Setup Kubernetes Environment Delete Previous Cluster To avoid conflicts, remove any existing clusters:
kind delete clusters kind 
Create New Cluster Set up a fresh Kubernetes cluster with the desired configuration using this kind-config.yaml   file::
kind create cluster --config ~/operator/testing/kind-config.yaml 
Deploy MinIO Operator Deploy the MinIO Operator using kubectl for managing MinIO instances in the cluster:
kubectl apply -k github.com/minio/operator/ 
Deploy MinIO Tenant Deploy the MinIO Tenant with a lightweight configuration suitable for development and testing purposes:
kubectl apply -k github.com/minio/operator/examples/kustomization/tenant-lite 
Create Ubuntu Pod Deploy an Ubuntu pod to host the KES service within the MinIO tenant namespace:
cat <<EOF | kubectl apply -f - 
apiVersion: v1 
kind: Pod 
metadata: 
  name: ubuntu 
  namespace: tenant-lite 
  labels: 
	 app: ubuntu 
spec: 
  containers: 
  - image: ubuntu 
	 command: 
   	 - "sleep" 
   	 - "604800" 
	 imagePullPolicy: IfNotPresent 
	 name: ubuntu 
  restartPolicy: Always 
EOF 
Install KES and mc Execute these commands within the Ubuntu pod to install KES and MinIO Client (mc):
apt update 
apt install wget 
wget https://github.com/minio/kes/releases/latest/download/kes-linux-amd64 
mv kes-linux-amd64 kes 
chmod +x kes 
mv kes /usr/local/bin/kes 
wget https://dl.min.io/client/mc/release/linux-amd64/mc 
chmod +x mc 
mv mc /usr/local/bin/mc 
Persistent Directory Creation Create a directory within the Ubuntu pod for KES configuration and data files where KES is located:
rm -rf ~/kes 
mkdir ~/kes 
cd ~/kes 
touch init.yml 
Identity Management Generate the necessary identities required for KES and MinIO authentication:
cd ~/kes 
kes identity new --key sys-admin.key --cert sys-admin.crt kes-sys-admin 
kes identity new --key minio-admin.key --cert minio-admin.crt minio-admin 
kes identity new --key minio.key --cert minio.crt minio 
kes identity new --ip "10.244.2.7" localhost # will generate private.key and public.crt 
                        	 | 
                        	 |____ IP Address of the Ubuntu Pod. 
Expected 4 identities.
root@ubuntu:/# cd ~/kes 
root@ubuntu:~/kes# ls 
data  init.yml  minio-admin.crt  minio-admin.key  minio.crt  minio.key  private.key  public.crt  sys-admin.crt  sys-admin.key 
                   	 |            	 |           	 |       	 |                                 	 |             	 | 
                   	 |            	 |           	 |__________|___ minio                        	 |________________|___ kes-sys-admin 
                   	 |            	 | 
                   	 |_______________|___ minio-admin 
root@ubuntu:~/kes# 
Create a KES unseal key.
cat /dev/urandom | head -c 32 | base64 # put the result in the .bashrc 
vi ~/.bashrc 
export KES_UNSEAL_KEY=<VALUE-FROM-ABOVE-COMMAND> 
source ~/.bashrc 
echo $KES_UNSEAL_KEY # it should print the value 
Initialize KES Deployment 
KES Configuration - Edit/Create KES config file Configure the KES service by setting up the necessary configuration parameters:
cd ~/kes 
echo "version: v1" > ~/kes/init.yml 
echo "address: 0.0.0.0:7373" >> ~/kes/init.yml 
echo "" >> ~/kes/init.yml 
echo "tls:" >> ~/kes/init.yml 
echo "  key: private.key" >> ~/kes/init.yml 
echo "  cert: public.crt" >> ~/kes/init.yml 
echo "  client:" >> ~/kes/init.yml 
echo " 	 verify_cert: false" >> ~/kes/init.yml 
echo "" >> ~/kes/init.yml 
echo "system:" >> ~/kes/init.yml 
echo "  admin:" >> ~/kes/init.yml 
echo " 	 identity: $(kes identity of sys-admin.crt)" >> ~/kes/init.yml 
echo "" >> ~/kes/init.yml 
echo "unseal:" >> ~/kes/init.yml 
echo "  environment:" >> ~/kes/init.yml 
echo " 	 name: KES_UNSEAL_KEY" >> ~/kes/init.yml 
echo "" >> ~/kes/init.yml 
echo "enclave:" >> ~/kes/init.yml 
echo "  default:" >> ~/kes/init.yml 
echo " 	 admin:" >> ~/kes/init.yml 
echo "   	 identity: $(kes identity of minio-admin.crt)" >> ~/kes/init.yml 
echo " 	 policy:" >> ~/kes/init.yml 
echo "   	 minio:" >> ~/kes/init.yml 
echo "     	 allow:" >> ~/kes/init.yml 
echo "     	 - /v1/api" >> ~/kes/init.yml 
echo "     	 - /v1/log/audit" >> ~/kes/init.yml 
echo "     	 - /v1/log/error" >> ~/kes/init.yml 
echo "     	 - /v1/key/create/*" >> ~/kes/init.yml 
echo "     	 - /v1/key/generate/*" >> ~/kes/init.yml 
echo "     	 - /v1/key/decrypt/*" >> ~/kes/init.yml 
echo "     	 - /v1/key/bulk/decrypt/*" >> ~/kes/init.yml 
Initialization Initialize the KES server with the newly created configuration file:
cd ~/kes # where init.yml is saved 
kes init --config init.yml ~/kes/data 
Expected is
root@ubuntu:~/kes# cd ~/kes # where init.yml is saved 
kes init --config init.yml ~/kes/data 
TLS: 
  · Private Key:  private.key 
  · Certificate:  public.crt 
System: 
  · Identity:  1a65f6f86c3268b30528fe4aab88fc6994730346e1c1863052fa3fa192d77d3e 
Unseal: 
  · Environment:  KES_UNSEAL_KEY 
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ 
│                                                     	 Initialized KES v0.22.3 in /root/kes/data                                                     	 │ 
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ 
root@ubuntu:~/kes# 
Start KES Server Start the KES server and verify it's running correctly:
Expected is:
root@ubuntu:~/kes# kes server ~/kes/data 
Copyright  MinIO, Inc.  https://min.io 
License 	 GNU AGPLv3   https://www.gnu.org/licenses/agpl-3.0.html 
Version 	 v0.22.3   	 linux/amd64 
Endpoints  https://127.0.0.1:7373 
        	 https://10.244.4.2:7373 
mTLS    	 skip verify  Client certificates are not verified 
Mem Lock   off       	 Failed to lock RAM pages. Consider granting CAP_IPC_LOCK 
Assign Policies Link MinIO and KES together by assigning the appropriate policies and identities.
In Ubuntu Pod Terminal where KES is located: Assign MinIO identity to MinIO policy.
cd ~/kes 
export KES_SERVER=https://127.0.0.1:7373 
export KES_CLIENT_KEY=minio-admin.key 
export KES_CLIENT_CERT=minio-admin.crt 
kes policy assign -k minio $(kes identity of minio.crt) 
Expected:
root@ubuntu:/# cd ~/kes 
export KES_SERVER=https://127.0.0.1:7373 
export KES_CLIENT_KEY=minio-admin.key 
export KES_CLIENT_CERT=minio-admin.crt 
kes policy assign -k minio $(kes identity of minio.crt) 
root@ubuntu:~/kes# 
root@ubuntu:~/kes# 
root@ubuntu:~/kes# 
root@ubuntu:~/kes# 
root@ubuntu:~/kes# 
root@ubuntu:~/kes# printenv | grep KES_SERVER 
KES_SERVER=https://127.0.0.1:7373 
root@ubuntu:~/kes# 
MinIO Server Setup Establish the connection between MinIO and KES, defining the encryption endpoint and credentials:
Create kes-minio secret Copy ~/kes/minio.key and ~/kes/minio.crt to your laptop from the ubuntu pod These two files below come from this line:  kes identity new --key minio.key --cert minio.crt minio
# /Users/aj/minio/private.key is ~/kes/minio.key 
# /Users/aj/minio/public.crt is ~/kes/minio.crt 
kubectl create secret generic kes-minio -n tenant-lite --from-file=/Users/aj/minio/private.key --from-file=/Users/aj/minio/public.crt 
Create kes-minio-public secret:Copy ~/kes/private.key and ~/kes/public.crt to your laptop from the ubuntu pod These two files below comes from this line: kes identity new --ip "10.244.2.7" localhost:   
# /Users/aj/minio/private.key is ~/kes/private.key 
# /Users/aj/minio/public.crt is ~/kes/public.crt 
kubectl create secret generic kes-minio-public -n tenant-lite --from-file=/Users/aj/minio/private.key --from-file=/Users/aj/minio/public.crt 
k edit tenant -n tenant-lite 
apiVersion: minio.min.io/v2 
kind: Tenant 
metadata: 
  name: storage 
  namespace: minio-tenant 
spec: 
  # externalClientCertSecrets is to share the secret with the MinIO Pods: 
  # Under: /tmp/certs/client-0 You will find: 
  # client.crt and client.key 
  # And we can use these files to setup KES in k8s 
  externalClientCertSecrets: 
  - name: kes-minio 
	 type: Opaque 
  - name: kes-minio-public 
	 type: Opaque 
  env: 
	 # Set MINIO_KMS_KES_ENDPOINT 
	 # It is the IP of the Ubuntu Pod. 
	 - name: MINIO_KMS_KES_ENDPOINT 
   	 value: "https://<IP-ADDRESS-OF-UBUNTU-POD>:7373" 
	 # Set MinIO Client Credentials, it comes from kes-minio secret 
	 - name: MINIO_KMS_KES_CERT_FILE 
   	 value: "/tmp/certs/client-0/client.crt" 
	 # Set MinIO Client Credentials, it comes from kes-minio secret 
	 - name: MINIO_KMS_KES_KEY_FILE 
   	 value: "/tmp/certs/client-0/client.key" 
	 # Set MinIO Default Key 
	 - name: MINIO_KMS_KES_KEY_NAME 
   	 value: "minio-default-key" 
	 # Trust the KES Server Certificate, it comes from kes-minio-public secret 
	 - name: MINIO_KMS_KES_CAPATH 
   	 value: "/tmp/certs/client-1/client.crt" 
	 # Root User 
	 - name: MINIO_ROOT_USER 
   	 value: minio 
	 # ROOT Password: 
	 - name: MINIO_ROOT_PASSWORD 
   	 value: minio123 
Encryption Operations Conduct encryption operations by creating an encrypted bucket and configuring server-side encryption:
mc alias set myminio https://minio.tenant-lite.svc.cluster.local:443 minio minio123 
mc rb myminio/my-bucket --force # remove previous bucket to start fresh 
mc mb myminio/my-bucket # create new bucket 
mc admin kms key create myminio minio-my-bucket # create key 
mc encrypt set sse-kms minio-my-bucket myminio/my-bucket # encrypt bucket 
Verification and Testing of the Results Ensure that your setup is correct by verifying that keys can be created and accessed through MinIO:
root@ubuntu:/# mc alias set myminio https://minio.tenant-lite.svc.cluster.local:443 minio minio123 
Added `myminio` successfully. 
root@ubuntu:/# mc rb myminio/my-bucket --force # remove previous bucket to start fresh 
mc: <ERROR> Unable to validate target `myminio/my-bucket`. Bucket `my-bucket` does not exist. 
root@ubuntu:/# mc mb myminio/my-bucket # create new bucket 
Bucket created successfully `myminio/my-bucket`. 
root@ubuntu:/# mc admin kms key create myminio minio-my-bucket # create key 
Created master key `minio-my-bucket` successfully 
root@ubuntu:/# mc encrypt set sse-kms minio-my-bucket myminio/my-bucket # encrypt bucket 
Auto encryption configuration has been set successfully for myminio/my-bucket 
root@ubuntu:/# 
Final Thoughts 
After following the outlined steps, you have successfully deployed a stateful KES in your Kubernetes cluster. This configuration allows you to manage encryption keys within your cluster, simplifying your deployment and reducing dependencies on external KMS solutions. This ensures your AI/ML workloads perform smoothly without interruption that depend on this critical feature in the infrastructure.
The integration of stateful KES in Kubernetes with this hands-on experience reinforces your skills as a DevOps engineer as you continue to explore. If you have any questions on deploying stateful KES in your Kubernetes cluster be sure to reach out to us on Slack  !