Kubernetes Secrets Encryption
This integration guide focuses on securing Kubernetes Secrets using Thales Luna HSM and the K8S-KMS-Plugin. Kubernetes Secrets contain sensitive data such as passwords, keys, and certificates. To enhance security, Kubernetes has developed a feature that utilizes a KMS (Key Management Service) encryption provider for encrypting Secrets data at rest. This method leverages an envelope encryption scheme to secure data stored in ETCD.
In this scheme, data is encrypted using a Data Encryption Key (DEK), with a new DEK generated for each encryption process. These DEKs are then encrypted with a Key Encryption Key (KEK), which is stored and managed in a remote KMS. Communication between Kubernetes and the KMS provider is facilitated through gRPC, with a specific KMS plugin acting as a gRPC server. This plugin is deployed on the same host(s) as the Kubernetes master(s) and is responsible for all interactions with the remote KMS.
Thales has developed a KMS plugin, known as the K8S-KMS-Plugin, which interfaces with the Thales Luna HSM to manage Secrets data encryption. This integration guide details the process of setting up and configuring this integration, highlighting the benefits of using Luna HSM along with the K8S-KMS-Plugin to generate encryption keys that protect Kubernetes Secrets data.
Following are some of the benefits of using Luna HSM along with K8S-KMS-Plugin to generate encryption keys that protect Secrets data for Kubernetes Secrets encryption:
-
Secure generation, storage, and protection of the identity signing private keys using either FIPS 140-2 or FIPS 140-3 Level 3 validated hardware.
-
Full life cycle management of the keys to ensure their integrity and reliability throughout their usage.
-
Maintenance of a comprehensive HSM audit trail for transparency and accountability in key operations.
-
Significant performance enhancements by offloading cryptographic operations from application servers.
Supported Platforms
The following platforms are certified on Thales Luna HSM for this integration:
Kubernetes Version | KMS Plugin | Operating System |
---|---|---|
v1.30.0 | K8S-KMS-Plugin | RHEL 8 |
v1.19.0 | K8S-KMS-Plugin | CentOS 7 |
This integration is compatible with all Luna HSM models, as long as a supported version of the Luna Client is used.
Prerequisites
Before proceeding with the integration, ensure the following tasks are completed:
Configure Luna HSM
As the first step to accomplish this integration, you need to set up On-Premise Luna HSM. Follow these steps to set up your on-premise Luna HSM:
Ensure that the HSM is set up, initialized, provisioned, and ready for deployment. For more information, refer to Luna HSM documentation.
Create a partition that will be later on used by Kubernetes.
Create and exchange certificate between the Luna Network HSM and client system. Register client and assign partition to create an NTLS connection.
Initialize Crypto Officer and Crypto User roles for the registered partition.
Run the following command to verify that the partition has been successfully registered and configured:
/usr/safenet/lunaclient/bin/lunacm
Upon successful execution, you should observe an output similar to the example provided below:
lunacm (64-bit) v10.4.0-417. Copyright (c) 2021 Thales Group. All rights reserved.
Available HSMs:
Slot Id -> 0
Label -> KubeKMS01
Serial Number -> 1238696044948
Model -> LunaSA 7.4.0
Firmware Version -> 7.4.0
Configuration -> Luna User Partition With SO (PW) Key Export With Cloning Mode
Slot Description -> Net Token Slot
FM HW Status -> Non-FM
Slot Id -> 1
Label -> KubeKMS02
Serial Number -> 1238696044908
Model -> LunaSA 7.4.0
Firmware Version -> 7.4.0
Configuration -> Luna User Partition With SO (PW) Signing With Cloning Mode
Slot Description -> Net Token Slot
FM HW Status -> Non-FM
Slot Id -> 8
HSM Label -> KubeKMS-HA
HSM Serial Number -> 11238696044948
HSM Model -> LunaVirtual
HSM Firmware Version -> 7.4.0
HSM Configuration -> Luna Virtual HSM (PW) Signing With Cloning Mode
HSM Status -> N/A - HA Group
HSM Certificates -> *** Test Certs ***
Current Slot Id: 0
Refer to Luna HSM documentation for detailed steps on creating NTLS connection, initializing the partitions, and assigning various user roles.
For proper configuration of a PED-based Luna HSM, it is recommended to activate partition policies 22 and 23, allowing for both activation and auto-activation.
Set up Luna HSM High-Availability Group
Refer to Luna HSM documentation for HA steps and details regarding configuring and setting up two or more HSM boxes on host systems. You must enable the HAOnly
setting in HA for failover to work so that if the primary goes down due to any reason, all calls get automatically routed to the secondary until the primary recovers and starts up.
This integration has been tested and is verified to work in both HA mode and FIPS mode.
Set up the Kubernetes Cluster
Refer to the Kubernetes documentation for instructions on installing and running a Kubernetes cluster. For the purposes of this demonstration, the Kubernetes cluster is configured with one Master node and two Worker nodes on VMware. After installation, verify that the Kubernetes cluster is operational and running successfully.
Configure Luna HSM with KMS plugin for Kubernetes Secrets encryption
Kubernetes Secrets encryption utilizes a KMS provider for encryption/decryption requests. The KMS provider calls the K8S-KMS-Plugin to communicate with the Luna HSM. Complete the following tasks on the Master node of the Kubernetes cluster. Any configuration updates to the Kubernetes Master node will automatically deploy on all Worker nodes connected to the Master node:
Configure Luna HSM with K8S-KMS-Plugin
Deploy K8S-KMS-Plugin as KMS Provider
Configure Luna HSM with K8S-KMS-Plugin
Steps to configure Luna HSM with K8S-KMS-Plugin:
Ensure that the Luna Client is installed and the NTL Service is configured with the Luna HSM partition on the master node of the Kubernetes cluster.
Access the master host using a root account or an account with administrative privileges.
Define the KMSPATH and create the necessary directory structure:
export KMSPATH=/opt/kms
mkdir -p $KMSPATH/src/github.com/thalescpl-io
cd $KMSPATH/src/github.com/thalescpl-io
Execute the following commands to clone the k8s-kms-plugin repository and check out the specified branch:
git clone https://github.com/thalescpl-io/k8s-kms-plugin.git
cd k8s-kms-plugin
git checkout -b luna_fips_mode_fix origin/luna_fips_mode_fix
cd $KMSPATH/src/github.com/thalescpl-io
Copy the Luna Minimal Client package to the k8s-kms-plugin directory:
cp /home/LunaClient-Minimal-10.2.0-111.x86_64.tar k8s-kms-plugin/
Copy the Luna Configuration file (/etc/Chrystoki.conf
) to the k8s-kms-plugin directory:
cp /etc/Chrystoki.conf k8s-kms-plugin/
Modify the Chrystoki.conf
file located in the k8s-kms-plugin
directory to update the library paths and the Chrystoki2
and Secure Trusted Channel
sections as follows:
Chrystoki2 = {
LibUNIX = /usr/safenet/lunaclient/libs/64/libCryptoki2.so;
LibUNIX64 = /usr/safenet/lunaclient/libs/64/libCryptoki2_64.so;
}
...
Secure Trusted Channel = {
ClientTokenLib = /usr/safenet/lunaclient/libs/64/libSoftToken.so;
SoftTokenDir = /usr/safenet/lunaclient/stc/token;
ClientIdentitiesDir = /usr/safenet/lunaclient/stc/client_identities;
PartitionIdentitiesDir = /usr/safenet/lunaclient/stc/partition_identities;
}
Copy the necessary certificates and keys to the k8s-kms-plugin
directory to enable connectivity to Luna HSM:
cp -r /usr/safenet/lunaclient/cert/server k8s-kms-plugin/
cp -r /usr/safenet/lunaclient/cert/client k8s-kms-plugin/
Create a start.sh
file in the k8s-kms-plugin
directory with the necessary flags for Luna HSM:
- Navigate to the
k8s-kms-plugin
directory:
cd k8s-kms-plugin/
- Create the
start.sh
file:
echo '#!/usr/bin/env bash
/k8s-kms-plugin serve --provider luna --p11-lib /usr/safenet/lunaclient/libs/64/libCryptoki2_64.so --p11-label KubeKMS-HA --p11-pin userpin1' > start.sh
- Ensure the script has execute permissions:
chmod +x start.sh
Ensure the p11-label
and p11-pin
are replaced with the appropriate partition name and password, respectively.
Specify additional options for k8s-kms-plugin
: The k8s-kms-plugin
provides several additional options which can be specified as per your requirements:
Option | Description |
---|---|
Usage: | serve [flags] |
Flags: | |
--allow-any |
Allow any device (accepts all ids/secrets) |
--disable-socket |
Disable socket-based server |
--enable-server |
Enable TLS-based server |
-h, --help |
Display help for serve |
--socket string |
Unix Socket (default: /tmp/run/hsm-plugin-server.sock ) |
--tls-ca string |
TLS CA certificate (default: certs/ca.crt ) |
--tls-certificate string |
TLS server certificate (default: certs/tls.crt ) |
--tls-key string |
TLS server key (default: certs/tls.key ) |
Global Flags: | |
--auto-create |
Auto-create the keys if needed (default: true) |
--ca-id string |
Certificate ID for CA certificate record (default: 1c3d30d5-dfa8-4167-a9f9-2c768464181b ) |
--config string |
Configuration file path |
--host string |
TCP Host (default: 0.0.0.0 ) |
--kek-id string |
Key ID for KMS KEK (default: a37807cd-6d1a-4d75-813a-e120f30176f7 ) |
-p, --native-path string |
Path to key store for native provider (files only) (default: .keys ) |
--output string |
Log output format (text or JSON supported) (default: text ) |
--p11-key-label string |
Key Label to use for encrypt/decrypt (default: k8s-dek ) |
--p11-label string |
P11 token label |
--p11-lib string |
Path to P11 library/client |
--p11-pin string |
P11 PIN |
--p11-slot int |
P11 token slot |
--port int |
TCP Port for gRPC service (default: 31400 ) |
--provider string |
Provider (default: p11 ) |
Create a new Dockerfile or edit the existing one in the k8s-kms-plugin
directory. This Dockerfile will be used to create a Docker image containing k8s-kms-plugin
and all required resources for Luna HSM Client to communicate with the Luna HSM partition. Ensure that you provide the correct file name and path for all resources being copied from the host to the Docker image.
# Dockerfile
## Build Stage
FROM goboring/golang:1.14.6b4 as build
WORKDIR /app
ADD go.mod /app/go.mod
ADD go.sum /app/go.sum
ADD tools.go /app/pkg/tools.go
ADD vendor /app/vendor
ADD pkg /app/pkg
ADD apis /app/apis
ADD cmd/ /app/cmd/
ENV GOOS linux
ENV GOARCH amd64
ENV CGO_ENABLED 1
ENV GOFLAGS -mod=vendor
RUN go build -o k8s-kms-plugin ./cmd/k8s-kms-plugin
## Plugin Server
FROM goboring/golang:1.14.6b4 as kms-server
WORKDIR /
COPY --from=build /app/k8s-kms-plugin /k8s-kms-plugin
COPY LunaClient-Minimal-10.2.0-111.x86_64.tar /tmp/
RUN mkdir -p /usr/safenet/lunaclient
RUN mkdir -p /usr/safenet/lunaclient/cert/client
RUN mkdir -p /usr/safenet/lunaclient/cert/server
RUN tar -xvf /tmp/LunaClient-Minimal-10.2.0-111.x86_64.tar --strip 1 -C /usr/safenet/lunaclient
RUN cp /usr/safenet/lunaclient/openssl.cnf /usr/safenet/lunaclient/bin
ENV ChrystokiConfigurationPath=/etc
COPY Chrystoki.conf /etc/Chrystoki.conf
COPY server/CAFile.pem /usr/safenet/lunaclient/cert/server
COPY client/k8s-master.kube.comKey.pem /usr/safenet/lunaclient/cert/client
COPY client/k8s-master.kube.com.pem /usr/safenet/lunaclient/cert/client
COPY start.sh /start.sh
RUN chmod +x /start.sh
ENTRYPOINT ["/start.sh"]
Create an image using the Dockerfile with the following command:
docker build . -t kms-server
You will see a confirmation message when the image is built successfully.
List all Docker images to ensure that the kms-server
image has been created successfully. Use the following command:
docker images
The output will display a list of images, including kms-server
.
Create a pod manifest YAML file named kms-plugin.yaml
. This file will define the Kubernetes pod configuration for the k8s-kms-plugin
server. Ensure that the file contains the following contents:
# kms-plugin.yaml
apiVersion: v1
kind: Pod
metadata:
name: k8s-kms-plugin-server
labels:
app.kubernetes.io/name: k8s-kms-plugin-server
spec:
containers:
- name: plugin-server
image: kms-server
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /certs/
name: certstore
- mountPath: /tmp/run
name: socket
hostNetwork: true
priorityClassName: system-node-critical
volumes:
- name: certstore
emptyDir: {}
- name: ca
hostPath:
path: /etc/
- name: socket
hostPath:
path: /tmp/run
type: DirectoryOrCreate
status: {}
This configuration ensures that the k8s-kms-plugin
server will run as a pod in your Kubernetes cluster with the necessary volume mounts for certificates and socket communication.
Copy the kms-plugin.yaml
file to the Kubernetes pod manifest location used by the kubelet service for running all static pods. Use the following command:
cp kms-plugin.yaml /etc/kubernetes/manifests/
Ensure that the path /etc/kubernetes/manifests/
matches the static pod manifest location for your cluster deployment. This location may vary, so confirm you are using the correct directory.
Kubernetes will automatically deploy the k8s-kms-plugin
pod from the manifest location. Verify the deployment status with the following command:
kubectl get pods
On the master host, verify that the socket file is created under the /tmp/run
directory specified in kms-plugin.yaml
. Use the following command:
ls -ltr /tmp/run
Connect to the pod to verify that k8s-kms-plugin
is integrated with Luna HSM and that the encryption key is generated. Execute the following command on the master host:
kubectl exec -it k8s-kms-plugin-server-k8s-master.kube.com -- /bin/bash
Ensure that the pod has access to the Luna HSM partition by running the following command inside the pod:
/usr/safenet/lunaclient/bin/64/vtl listslots
Verify that the encryption key has been generated on the Luna HSM via the KMS-Plugin by checking the contents of the registered Luna HSM partition. Run the following command inside the pod:
/usr/safenet/lunaclient/bin/64/cmu list
By completing these steps, you ensure that the KMS plugin is configured to use the Luna HSM. The UNIX socket hsm-plugin-server.sock
will be created on the master node where the API Server is running and will be ready to serve requests from the API Server.
Deploy K8S-KMS-Plugin as KMS Provider
In this section, we will deploy the K8S-KMS-Plugin as the KMS provider and configure the API Server to use this KMS provider for Kubernetes Secrets encryption. Follow the steps below to complete the deployment:
When the KMS-Plugin is ready to be used as an encryption provider, create the encryption configuration file encryption-config.yaml
on the master node where the API server is running. The file should contain the following content:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- kms:
name: k8s-kms-plugin
endpoint: unix:///tmp/run/hsm-plugin-server.sock
cachesize: 100
timeout: 3s
- identity: {}
Save the encryption-config.yaml
file at a suitable location on the Master host. For example, you can save it at:
/etc/kubernetes/kms/encryption-config.yaml
Ensure that the file is correctly placed and accessible by the API server for the next steps in configuring Kubernetes Secrets encryption.
To configure the API server to use the encryption configuration file created in the previous step, follow these instructions:
-
Open the
kube-apiserver.yaml
file from the static pod manifest location (e.g.,/etc/kubernetes/manifests/kube-apiserver.yaml
). -
Edit the file to add the
--encryption-provider-config
flag to the command list ofkube-apiserver
. The flag must point to theencryption-config.yaml
file. Ensure the correct indentation is maintained.
spec:
containers:
- command:
- kube-apiserver
- --encryption-provider-config=/etc/kubernetes/kms/encryption-config.yaml
- --encryption-provider-config-automatic-reload=true
- --advertise-address=10.164.76.80
- --allow-privileged=true
- --authorization-mode=Node,RBAC
...
- For Kubernetes version 1.29 and later, the v1 implementation of KMS is disabled by default. To enable this feature, add the
--feature-gates=KMSv1=true
flag tokube-apiserver.yaml
. Ensure to set both flags when using Kubernetes v1.29 and later.
spec:
containers:
- command:
- kube-apiserver
- --encryption-provider-config=/etc/kubernetes/kms/encryption-config.yaml
- --feature-gates=KMSv1=true
- --encryption-provider-config-automatic-reload=true
- --advertise-address=10.164.76.80
- --allow-privileged=true
- --authorization-mode=Node,RBAC
...
To allow the kube-apiserver
pod to communicate with the k8s-kms-plugin
, access to the directories where the encryption-config.yaml
file and UNIX socket are located is required. Add mount points in kube-apiserver.yaml
as shown below, ensuring correct indentation:
- Add Volume Mounts:
...
volumeMounts:
- mountPath: /etc/kubernetes/kms
name: kms
readOnly: true
- mountPath: /tmp/run
name: socket
...
- Add Volumes:
...
volumes:
- hostPath:
path: /etc/kubernetes/kms
type: DirectoryOrCreate
name: kms
- hostPath:
path: /tmp/run
type: DirectoryOrCreate
name: socket
...
After making the necessary changes to the kube-apiserver.yaml
file, save and close the file. The API server will automatically restart upon saving the changes. Ensure that your cluster comes back online without any failures and that the API server is READY and running.
kubectl get pods --all-namespaces
This command will help you verify that all pods, including the API server, are running as expected.
If the API server is configured successfully to use the k8s-kms-plugin
, the connection will be established. You can verify this by running the following command on the Master node to check the UNIX socket:
ss -a --unix -p | grep hsm-plugin-server.sock
This command will list the active UNIX sockets, and you should see the hsm-plugin-server.sock
if the connection is established correctly.
The API server is now configured to use the k8s-kms-plugin
as the KMS provider. The k8s-kms-plugin
is ready to serve requests from the API server for Secrets encryption and decryption.
Verify Kubernetes Secrets encryption using KMS Provider
Kubernetes Secrets are encrypted when written to etcd. After restarting your kube-apiserver, any newly created or updated Secret will be encrypted when stored. To verify, you can use the etcdctl
command line program to retrieve the contents of your Secret. To verify the Secrets encryption using KMS Provider:
Create a new Secret called mysecret
in the default namespace. The encrypted Secret gets saved in etcd.
kubectl create secret generic mysecret -n default --from-literal=mykey=mys3cr3t
Use the etcdctl
command to retrieve Secrets from etcd:
- Set up an alias for
etcdctl3
:
alias etcdctl3="ETCDCTL_API=3 /path/to/etcdctl --endpoints=https://127.0.0.1:2379 --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt --key=/etc/kubernetes/pki/apiserver-etcd-client.key --cacert=/etc/kubernetes/pki/etcd/ca.crt"
Ensure to replace /path/to/etcdctl
with the actual path to your etcdctl
binary.
- Retrieve the Secret from etcd:
etcdctl3 get /registry/secrets/default/mysecret
This command will fetch the Secret from etcd, and you should see the encrypted data, indicating that the KMS provider is correctly encrypting Secrets.
Ensure that the stored Secret is prefixed with k8s:enc:kms:v1:k8s-kms-plugin
, which indicates that the KMS Provider has encrypted the resulting data.
etcdctl3 get /registry/secrets/default/mysecret | hexdump -C
Ensure that the Secret is correctly decrypting when retrieved via the API Server.
kubectl describe secret mysecret -n default
Run the following command to encrypt all pre-existing Secrets. The command reads all Secrets and then encrypts all Secrets using the KMS Provider.
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
If an error occurs due to a conflicting write, retry the command. For larger clusters, it is recommended to subdivide the Secrets by namespace or script an update.
Switch from a local encryption provider to the KMS provider
Follow these steps to switch from the native encryption provider to the KMS provider and re-encrypt all Secrets using the KMS provider:
On the Master host, edit the encryption-config.yaml
file. Add the KMS provider as the first entry in the configuration file, as shown in the example below:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- kms:
name: k8s-kms-plugin
endpoint: unix:///tmp/run/socket.sock
cachesize: 100
timeout: 3s
- aescbc:
keys:
- name: key1
secret: <BASE 64 ENCODED SECRET>
In this configuration, key1
is the name of your key and <BASE 64 ENCODED SECRET>
is the actual key of the native encryption provider.
Ensure that the Unix Socket path is mounted in the Kube-API server manifest YAML.
After updating the encryption configuration file, restart all kube-apiserver
processes to apply the changes.
Command to restart kube-apiserver processes will vary based on your setup.
Ensure you follow the proper procedure to restart the kube-apiserver on your Kubernetes setup.
Run the following command to force all Secrets to be re-encrypted using the KMS Provider:
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
This completes the process of switching to the KMS provider for Kubernetes Secrets encryption using the k8s-kms-plugin. By using Luna HSM to secure the encryption keys on FIPS-validated hardware security modules, you provide a higher level of security than locally stored encryption keys.