Secrets Encryption
Introducción
Todas las APIs en Kubernetes que permiten escribir datos persistentes de recursos API admiten el cifrado via REST. Por ejemplo, puedes habilitar el cifrado via REST para los recursos tipo
Secrets. Este cifrado es adicional a cualquier cifrado a nivel de sistema para el clúster de etcd o para los sistemas de archivos en los hosts donde se ejecuta el kube-apiserver.La protección de datos sensibles es un pilar fundamental en la administración de clústeres Kubernetes. En este contexto, el cifrado en reposo se presenta como una capa adicional de seguridad que protege recursos críticos como los Secrets, garantizando que los datos almacenados en el clúster estén cifrados y seguros frente a accesos no autorizados.
En este tema, nos enfocaremos en cómo habilitar, validar y gestionar el cifrado de recursos tipo Secret en RKE2. También abordaremos la rotación de claves de cifrado, una práctica crucial para mantener la integridad y seguridad de los datos a lo largo del tiempo. A través de teoría y práctica, los estudiantes podrán adquirir habilidades esenciales para implementar estas medidas de seguridad en sus entornos.
Objetivo
Objetivo General:
- En esta guía aprenderemos cómo habilitar y configurar el cifrado de datos de la API via REST.
Desarrollo del Tema
En la versión utilizada en este curso, esta activo por defecto el cifrado para este tipo de recurso. El contenido del recurso tipo secret, estará encriptado en
etcd, esto nos ayudaréa a proteger información sensible de alguna extracción de una copia de este componente.
Laboratorio para el Estudiante
Indicaciones:
En el siguiente laboratorio, se validará el estado del cifrado de recursos via API Rest y se rotará la llave. Este procedimiento incluye reinicios del servicio
rke2-server, en un orden en particular luego de la ejecución del subcomandosecrets-encrypt.
Validando cifrado para el recurso tipo secret en RKE2
Pasos:
- Establecer una sesión como el usuario student a
lab-#-masterexport LAB=Xssh student@lab-${LAB}-master - Cambiar al usuario
rootsudo -i - Habilitar PATH y KUBECONFIG para administrar el cluster.
export KUBECONFIG=/etc/rancher/rke2/rke2.yamlexport PATH=$PATH:/var/lib/rancher/rke2/bin - Obtener el estado de cifrado en el cluster.
Salida de ejemplo:
rke2 secrets-encrypt statusNotarEncryption Status: Enabled Current Rotation Stage: start Server Encryption Hashes: All hashes match Active Key Type Name ------ -------- ---- * AES-CBC aescbckeyEncryption Status: EnabledyName aescbckey. - Procederemos a crear un proyecto llamado
test-secrets.kubectl create ns test-secrets - Procederemos a crear 1000 recursos de tipo
secreten el proyectotest-secrets. Esto puede tardar unos minutos.La creación de esta cantidad de recursos, nos ayuda a validar el proceso al rotar la llave y el proceso de cifrado.for i in {1..1000}; do kubectl create secret generic secret${i} --from-literal=password=mypassword -n test-secrets; done - El nombre de la llave implementada en el cluster actualmente es
aescbckey. Podemos confirmar el valor de esta llave consultando el siguiente archivo de configuración en el 1 nodo master.Salida de ejemplo:jq < /var/lib/rancher/rke2/server/cred/encryption-config.json{ "kind": "EncryptionConfiguration", "apiVersion": "apiserver.config.k8s.io/v1", "resources": [ { "resources": [ "secrets" ], "providers": [ { "aescbc": { "keys": [ { "name": "aescbckey", "secret": "<BASE 64 ENCODED SECRET>" } ] } }, { "identity": {} } ] } ] } - Validaremos la versión de etcd en ejecución en el cluster.
Salida e ejemplo:
POD_ETCD=$(kubectl get pods -n kube-system --selector=component=etcd -o name |grep `hostname -f`) kubectl exec -it -n kube-system ${POD_ETCD} -- etcdctl versionetcdctl version: 3.5.13 API version: 3.5 - Descargaremos la versión de
etcd, para obtener acceso al comando`etcdctl.ETCD_VERSION=3.5.13 # Utiliza la versión obtenida anteriormente curl -LO https://github.com/etcd-io/etcd/releases/download/v${ETCD_VERSION}/etcd-v${ETCD_VERSION}-linux-amd64.tar.gz mkdir etcd-test tar -xvzf etcd-v${ETCD_VERSION}-linux-amd64.tar.gz -C etcd-test --strip-components=1 - Crearemos un archivo con las variables para poder utilizar etcd, con el mismo certificado que utiliza RKE2.
cat << EOF |tee etcd-test/vars.sh export ETCDCTL_ENDPOINTS='https://127.0.0.1:2379' export ETCDCTL_CACERT='/var/lib/rancher/rke2/server/tls/etcd/server-ca.crt' export ETCDCTL_CERT='/var/lib/rancher/rke2/server/tls/etcd/server-client.crt' export ETCDCTL_KEY='/var/lib/rancher/rke2/server/tls/etcd/server-client.key' export ETCDCTL_API=3 EOF -
Validaremos la conexión a etcd.
Salida de ejemplo:source etcd-test/vars.sh ./etcd-test/etcdctl endpoint statushttps://127.0.0.1:2379, 1d387b6842a8e192, 3.5.13, 18 MB, true, false, 2, 16653, 16653,IMPORTANTE: ESTO PUEDE SER PELIGROSO PARA SU CLUSTER, PROCEDA CON CAUTELA. ESTO SE REALIZA CON FINES DIDACTICOS.
-
Obtendremos el valor de una llave utilizando etcd.
Salide de ejemplo:./etcd-test/etcdctl get /registry/secrets/test-secrets/secret1|hexdump -CNote en la salida de su comando que el recurso almacenado inicie con el nombre00000000 2f 72 65 67 69 73 74 72 79 2f 73 65 63 72 65 74 |/registry/secret| 00000010 73 2f 74 65 73 74 2d 73 65 63 72 65 74 73 2f 73 |s/test-secrets/s| 00000020 65 63 72 65 74 31 0a 6b 38 73 3a 65 6e 63 3a 61 |ecret1.k8s:enc:a| 00000030 65 73 63 62 63 3a 76 31 3a 61 65 73 63 62 63 6b |escbc:v1:aescbck| 00000040 65 79 3a 85 3e 0e 14 60 28 76 04 12 58 3b 7a 04 |ey:.>..`(v..X;z.| 00000050 24 f2 9d a8 bb e0 fe 10 88 d6 55 55 e2 2c 7d b2 |$.........UU.,}.| 00000060 5f 66 a3 f6 67 40 74 6e 15 f2 db 74 1d 8f 53 6c |_f..g@tn...t..Sl| 00000070 6e 41 76 7b 8a 8c 3c d3 bc cc 92 11 ba 08 9e 73 |nAv{..<........s| 00000080 88 da 5a 73 5c 9a 62 fd c9 74 e2 57 1e e2 72 3d |..Zs\.b..t.W..r=| 00000090 42 2f 67 eb 6b b2 ce c6 8f 0c ac a7 33 25 70 e0 |B/g.k.......3%p.| 000000a0 65 af 98 63 f9 72 cb c0 b6 7e f0 94 16 f2 63 c3 |e..c.r...~....c.| [...] 000000e0 9f f0 73 8a 2b 2e 82 ef ce 19 1a 2b 85 b3 1d 38 |..s.+......+...8| 000000f0 01 6d 63 14 17 f3 cf 4a 35 4f 21 4a af 39 b3 9f |.mc....J5O!J.9..| 00000100 77 8d 06 71 2d 67 0c 50 aa fe c7 82 72 72 c3 38 |w..q-g.P....rr.8| 00000110 c9 3f ac 3f 23 d6 d1 97 24 4c d6 9d cf c2 d5 f3 |.?.?#...$L......| 00000120 12 66 f7 3a cb 2b 41 7a 8a 8b a8 bc 21 de c2 8c |.f.:.+Az....!...| 00000130 c4 84 8a 52 3d 45 fd 25 44 ab f6 26 c7 8b e9 b0 |...R=E.%D..&....| 00000140 0f e6 76 3b f2 e5 9a 28 84 09 0d 0f ec 6d 9b 65 |..v;...(.....m.e| 00000150 05 93 1d 0a |....| 00000154k8s:enc:aescbc:v1:aescbckey. Lo que nos indica que el data ha sido cifrada utilizando esa llave. El nombre de la llave, debe coincidir con el resultado del comando del paso(4). -
Confirmaremos que el valor puede ser obtenido por medio del Cluster de RKE2.
Salida ejemplo:kubectl get secret/secret1 -n test-secrets -o yaml | yq .data.password|base64 -dmypasswordHemos validado el funcionamiento del cifrado por defecto en RKE2 para el recurso tipo secret.
Rotación de llave de cifrado para recurso tipo secret en RKE2
Pasos:
NOTA: Debe haber relizado la guía anterior "Validando cifrado para el recurso tipo secret en RKE2".
ALERTA: La secuencia de los pasos es importante o puede causar daño en su cluster.
- Obtener el estado de cifrado en el cluster.
Salida de ejemplo:
rke2 secrets-encrypt statusNotarEncryption Status: Enabled Current Rotation Stage: start Server Encryption Hashes: All hashes match Active Key Type Name ------ -------- ---- * AES-CBC aescbckeyEncryption Status: EnabledyName aescbckey. - Realizaremos una copia de seguridad del componenete etcd. Si falla su procedimiento, siga la guía del tema Backup / Restore para recuperar su ambiente.
rke2 etcd-snapshot save --name pre-secret-key-rotation - En un entorno de Alta Disponibilidad deberá seleccionar 1 servidor tipo master para ejecutar estas tareas.
# Se ha seleccionado el mismo nodo en el que hemos creado el backup hostname -f - Prepararemos la nueva llave de cifrado.
Salida ejemplo:
rke2 secrets-encrypt prepareprepare completed successfully - Identificaremos la nueva llave y su valor generada en el paso anterior.
Salida de ejemplo:
jq < /var/lib/rancher/rke2/server/cred/encryption-config.jsonNote que existen dos llaves, la generado por la instalación de RKE2 y la nueva llave con el formato{ "kind": "EncryptionConfiguration", "apiVersion": "apiserver.config.k8s.io/v1", "resources": [ { "resources": [ "secrets" ], "providers": [ { "aescbc": { "keys": [ { "name": "aescbckey", "secret": "<BASE 64 ENCODED SECRET>" }, { "name": "aescbckey-YYY-MM-DDT20:33:52Z", "secret": "<BASE 64 ENCODED SECRET>" } ] } }, { "identity": {} } ] } ] }aescbckey-YYY-MM-DDT20:33:52Z. -
Reiniciaremos de forma secuencial en cada nodo master del cluster el servicio de
rke2-server. En nuesto ambiente solo contamos con un nodos master.Espere a que finalice este proceso, para continar.systemctl restart rke2-server.service -
Ejecutar el proceso de rotación de llave de cifrado (en el nodo selecionado).
Salida de ejemplo:rke2 secrets-encrypt rotaterotate completed successfully - Reiniciaremos el servicio de
rke2-server(en el nodo selecionado).Espere a que finalice este proceso, para continar.systemctl restart rke2-server.service - Si se cuenta con más nodos master, se debe de reiniciar el servicio
rke2-serverde forma secuancial. - Ejecutar el proceso de recifrado de RKE2 (en el nodo selecionado).
Salida de ejemplo:
rke2 secrets-encrypt reencryptreencryption started - Consultaremos la bitácora del servicio
rke2-server.Salida ejemplo:journalctl -u rke2-server -n 10 --no-pagerNote las lineas con el contenidoNov 10 20:41:06 lab-1-master rke2[8985]: time="2024-11-20T20:41:06Z" level=info msg="Encryption keys right rotated" Nov 10 20:41:06 lab-1-master rke2[8985]: time="2024-11-20T20:41:06Z" level=info msg="Saving cluster bootstrap data to datastore" Nov 10 20:43:25 lab-1-master rke2[8985]: time="2024-11-20T20:43:25Z" level=info msg="Creating secrets-reencrypt event broadcaster" Nov 10 20:43:25 lab-1-master rke2[8985]: time="2024-11-20T20:43:25Z" level=info msg="Updating TLS secret for kube-system/rke2-serving (count: 10): map[listener.cattle.io/cn-10.142.15.231:10.142.15.231 listener.cattle.io/cn-10.39.0.1:10.39.0.1 listener.cattle.io/cn-127.0.0.1:127.0.0.1 listener.cattle.io/cn-__1-f16284:::1 listener.cattle.io/cn-kubernetes:kubernetes listener.cattle.io/cn-kubernetes.default:kubernetes.default listener.cattle.io/cn-kubernetes.default.svc:kubernetes.default.svc listener.cattle.io/cn-kubernetes.default.svc.cluster.local:kubernetes.default.svc.cluster.local listener.cattle.io/cn-lab-1-master.c.mx-g01.internal:lab-1-master.c.mx-g01.internal listener.cattle.io/cn-localhost:localhost listener.cattle.io/fingerprint:SHA1=DD24B5B14FCB56EF9E4AE111FE2458CDB84D24C9]" Nov 10 20:43:25 lab-1-master rke2[8985]: time="2024-11-20T20:43:25Z" level=info msg="Active TLS secret kube-system/rke2-serving (ver=31161) (count 10): map[listener.cattle.io/cn-10.142.15.231:10.142.15.231 listener.cattle.io/cn-10.39.0.1:10.39.0.1 listener.cattle.io/cn-127.0.0.1:127.0.0.1 listener.cattle.io/cn-__1-f16284:::1 listener.cattle.io/cn-kubernetes:kubernetes listener.cattle.io/cn-kubernetes.default:kubernetes.default listener.cattle.io/cn-kubernetes.default.svc:kubernetes.default.svc listener.cattle.io/cn-kubernetes.default.svc.cluster.local:kubernetes.default.svc.cluster.local listener.cattle.io/cn-lab-1-master.c.mx-g01.internal:lab-1-master.c.mx-g01.internal listener.cattle.io/cn-localhost:localhost listener.cattle.io/fingerprint:SHA1=DD24B5B14FCB56EF9E4AE111FE2458CDB84D24C9]" Nov 10 20:43:33 lab-1-master rke2[8985]: time="2024-11-20T20:43:33Z" level=info msg="Removing key: Name: aescbckey, Secret: [REDACTED]" Nov 10 20:43:33 lab-1-master rke2[8985]: time="2024-11-20T20:43:33Z" level=info msg="Saving cluster bootstrap data to datastore"Creating secrets-reencrypt event broadcasted,Removing key: Name: aescbckey. - Identificaremos la nueva llave y su valor generada en el paso anterior.
Salida de ejemplo:
jq < /var/lib/rancher/rke2/server/cred/encryption-config.jsonNote que esta presente únicamente la nueva llave de cifrado{ "kind": "EncryptionConfiguration", "apiVersion": "apiserver.config.k8s.io/v1", "resources": [ { "resources": [ "secrets" ], "providers": [ { "aescbc": { "keys": [ { "name": "aescbckey-YYY-MM-DDT20:33:52Z", "secret": "<BASE 64 ENCODED SECRET>" } ] } }, { "identity": {} } ] } ] }aescbckey-YYY-MM-DDT20:33:52Z. -
Antes te continuar, verifique el estado del proceso de re cifrado.
Salida ejemplo:rke2 secrets-encrypt statusDeberá aparecer únicaente la nueva llave y el estadoEncryption Status: Enabled Current Rotation Stage: reencrypt_finished Server Encryption Hashes: All hashes match Active Key Type Name ------ -------- ---- * AES-CBC aescbckey-YYY-MM-DDT20:33:52ZCurrent Rotation Stage: reencrypt_finished. -
Reiniciaremos el servicio de
rke2-server(en el nodo selecionado).systemctl restart rke2-server.service - Confirmaremos que el valor puede ser obtenido por medio del Cluster de RKE2.
Salida ejemplo:
kubectl get secret/secret1 -n test-secrets -o yaml | yq .data.password|base64 -dmypassword - Obtendremos el valor de una llave utilizando etcd.
Salide de ejemplo:
./etcd-test/etcdctl get /registry/secrets/test-secrets/secret1|hexdump -CNote en la salida de su comando que el recurso almacenado inicie con el nombre00000000 2f 72 65 67 69 73 74 72 79 2f 73 65 63 72 65 74 |/registry/secret| 00000010 73 2f 74 65 73 74 2d 73 65 63 72 65 74 73 2f 73 |s/test-secrets/s| 00000020 65 63 72 65 74 31 0a 6b 38 73 3a 65 6e 63 3a 61 |ecret1.k8s:enc:a| 00000030 65 73 63 62 63 3a 76 31 3a 61 65 73 63 62 63 6b |escbc:v1:aescbck| 00000040 65 79 2d 32 30 32 34 2d 31 31 2d 32 30 54 32 30 |ey-YYYY-MM-DDT20| 00000050 3a 33 33 3a 35 32 5a 3a bb 18 c1 48 27 e9 7f a0 |:33:52Z:...H'...| 00000060 73 22 d2 9f 02 ed 3c 56 8a 32 54 74 e7 cd 7a 2f |s"....<V.2Tt..z/| 00000070 10 e9 b8 c4 ef 9b 74 0f 0b c3 c3 4b cd b3 0f 0a |......t....K....| 00000080 87 6e 2d bc 5a 6c ee 05 5f fa 73 38 89 f8 a2 63 |.n-.Zl.._.s8...c| 00000090 c8 11 4a a6 7c f7 3d 77 3e b8 e3 94 4c c9 f6 f3 |..J.|.=w>...L...| [...] 000000d0 33 4a 6d 56 23 83 0f e1 ed e9 92 93 b7 c4 c8 9c |3JmV#...........| 000000e0 62 4c 6e 56 24 f6 9d 1b 8f c0 09 04 2e b9 8b 1b |bLnV$...........| 000000f0 d2 f0 e0 01 13 bd 93 ad 95 81 6c 94 7e c7 43 90 |..........l.~.C.| 00000100 20 da 60 06 5b bf b4 50 9a ae da b5 a3 0c 1f d1 | .`.[..P........| 00000110 ad 18 e7 dd 84 00 7e db 36 b5 a5 13 f6 50 7f ad |......~.6....P..| 00000120 6e 21 27 4e 4e af e0 64 90 89 38 01 79 aa 95 6d |n!'NN..d..8.y..m| 00000130 74 bd 5e b7 3a e9 55 67 1f a2 7e f7 27 35 64 49 |t.^.:.Ug..~.'5dI| 00000140 7b 54 19 9f 4d eb 29 5c 98 31 f2 71 bd 3d 4e 27 |{T..M.)\.1.q.=N'| 00000150 b7 92 d0 78 16 02 c4 8d 7d c3 f6 65 d1 86 00 96 |...x....}..e....| 00000160 5c 34 96 f6 91 a7 95 71 0a |\4.....q.| 00000169aescbckey-YYY-MM-DDT20:33:52Z. Lo que nos indica que el data ha sido re cifrada utilizando esa llave. El nombre de la llave, debe coincidir con el resultado del comando del paso(5).
Recursos Adicionales
Conclusión
El cifrado de datos confidenciales via REST es una práctica fundamental para proteger la información sensible dentro de un clúster de Kubernetes, como los recursos de tipo Secret. A lo largo de este tema, hemos explorado cómo validar el cifrado de estos recursos en RKE2 y cómo realizar la rotación de las claves de cifrado para mantener la seguridad del clúster frente a posibles riesgos.
El laboratorio proporcionó una experiencia práctica que permitió a los estudiantes no solo validar la configuración del cifrado, sino también comprender los pasos necesarios para la rotación de claves, asegurando que las mejores prácticas de seguridad sean implementadas y mantenidas.
Con estas habilidades, los administradores están mejor preparados para manejar escenarios que requieran la protección de datos críticos, contribuyendo a un entorno más seguro y resiliente. Para profundizar en estos conceptos, recomendamos revisar los recursos adicionales proporcionados.