LINUX

Cómo crear y usar secretos de Kubernetes

El artículo actual abordará una de las características más importantes de los kubernetes, que se llama Secretos. Antes de profundizar en esta función, me gustaría mencionar que a lo largo de este artículo usaré los términos Kubernetes y K8s indistintamente.

Bueno, el propósito del artículo es recorrer los conceptos más importantes detrás de K8s Secrets y cómo maneja la información confidencial dentro de nuestro clúster. Estaré demostrando estos conceptos usando minikube que es una herramienta para ejecutar un clúster local de K8. Sí, todas las funciones de K8 están funcionando en mi computadora portátil, lo cual es muy bueno, ¿no? La instalación es muy sencilla y no dude en enviarme un ping si está atascado en algún lugar.

Entonces, lo que aprenderemos:

  • Explore los secretos en el ecosistema de K8s
  • Comprender el concepto detrás de Secrets
  • Juega con secretos con casos de uso de palabras reales

Bueno, el resto de este artículo está organizado de la siguiente manera:

  • Introducción
  • Visión general
  • Creación de secretos
  • Usabilidad de secretos
    • Como volúmenes
    • Como variables de entorno
  • Conclusión

Introducción

Secrets está diseñado para almacenar y manejar información sensible que pueda ser necesaria por recursos internos o externos, desde pods, imágenes y contenedores desde el punto de vista. Por ejemplo, credenciales, contraseñas, tokens, claves, certificados ssh, etc.necesarios / utilizados por API, puntos finales, servidores, bases de datos, etc.De hecho, los secretos proporcionan no solo una forma flexible de administrar datos confidenciales, sino que lo más importante es que los secretos administran dicha información en un de una manera más segura que incorporarlo en texto simple dentro de contenedores o vainas.

Resumen y mapa conceptual

En términos generales, Secrets es un objeto que contiene una pequeña cantidad de datos confidenciales como contraseñas, claves y tokens, etc. Se puede dibujar un breve mapa conceptual de Secrets dentro del ecosistema de K8 como se muestra a continuación:

Mapa conceptual secreto de K8s

Básicamente, los pods son parte de un espacio de nombres que es envidiosamente parte de un nodo de clúster. Los contenedores que pertenecen a un pods pueden compartir volúmenes montados, estos contenedores operan en los objetos Secrets para interactuar con sistemas internos o externos. Para que esto suceda, las vainas deben hacer referencia a los secretos necesarios. Por lo tanto, existen principalmente tres formas de hacerlo, la primera mediante el uso de volúmenes, la segunda mediante el uso de variables de entorno y la última a través de kubelet.

Con respecto al objeto secreto en sí, podemos distinguir entre dos tipos, los secretos del usuario y del sistema, por ejemplo, los K8 crean sus propios secretos automáticamente para acceder al servidor API de K8s (el principal punto de entrada para administrar el cerrador en K8s) y todos los usuarios creados los pods están detrás de las anulaciones de escena para usar los secretos incorporados. Verifiquemos si hay secretos del sistema en mi entorno, antes de crear cualquier objeto secreto, para hacerlo, podemos seguir la API de comando kubectl get de K8s, que es kubectl obtener secretos

➜ ~ kubectl get secrets
NAME TYPE DATA AGE
default-token-xny9c kubernetes.io/service-account-token 3 13d
tls-certs Opaque 4 13d

Puedo ver que la cuenta de servicio, por ejemplo, ya está creada: token-predeterminado-xny9c que es una construcción en secreto.

Creación de secretos

Supongamos que un pods necesita acceso a la base de datos de Redis, principalmente un nombre de usuario y una contraseña que se almacenan en archivos, por ejemplo. ./nombredeusuario.txt y ./password.txt. Tenga en cuenta que, por razones de simplicidad, utilizaré los mismos archivos en mi demostración durante el resto de este artículo. Entonces, creemos y coloquemos algunos datos falsos en estos dos archivos:

➜ ~ echo -n 'zombie' > ./username.txt
➜ ~ echo -n '1f2d1e2e67df' > ./password.txt
➜ ~ cat username.txt
zombie% ➜ ~ cat password.txt
1f2d1e2e67df

De hecho, hay dos formas de crear un secreto en K8s, la primera usando el comando kubectl crear secreto y el segundo manualmente desde un archivo de especificaciones; Se permite la serialización de datos JSON o YAML.

Creando un objeto secreto usando la línea de comando

Para crear un objeto secreto usamos el comando así:

➜ ~ kubectl create secret generic db-zombie-pass --from-file=./username.txt --from-file=./password.txt
secret "db-zombie-pass" created

Una vez más para comprobar los secretos de creación:

➜ ~ kubectl get secrets
NAME TYPE DATA AGE
db-zombie-pass Opaque 2 27m
default-token-xny9c kubernetes.io/service-account-token 3 14d
tls-certs Opaque 4 13d

Ahora que hemos creado nuestro primer objeto secreto, describámoslo usando kubectl describe mando:

➜ ~ kubectl describe secret db-zombie-pass
Name: db-zombie-pass
Namespace: default
Labels:
Annotations:

Type: Opaque

Data
====
password.txt: 12 bytes
username.txt: 6 bytes

Tenga en cuenta que el último comando muestra los archivos incluidos en nuestro objeto secreto, pero no el contenido en sí, lo cual es muy importante ya que evita que el secreto se exponga a otros usuarios que utilizan el entorno k8s.

Crear un objeto secreto manualmente usando un archivo de especificaciones

La creación de un objeto secreto de forma manual se puede realizar mediante un archivo de especificaciones, ya sea mediante la serialización de datos JSON o YAML. Los valores secretos están codificados en una cadena base64. Por lo tanto, primero para crear un objeto secreto utilizando un archivo de especificaciones, el usuario debe codificar los valores secretos como se ilustra a continuación:

➜  ~ echo -n 'zombie' | base64
em9tYmll
➜  ~ echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm

En segundo lugar, abra su editor favorito y edite el archivo secreto de la siguiente manera, llamémoslo mi-secreto.yaml

apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: em9tYmll
password: MWYyZDFlMmU2N2Rm

Luego, podemos crear el objeto secreto a partir del archivo de especificaciones ejecutando el siguiente comando:

➜  ~ vim my-secrte.yaml
➜  ~ kubectl create -f ./my-secrte.yaml
secret "mysecret" created

Bueno, lo vi kubectl describe no muestra el contenido del objeto secreto, pero ¿qué pasa si alguien quiere verificar este contenido? bueno, podemos usar el comando kubectl obtener secreto proporcionando el nombre del objeto secreto, por ejemplo, para nuestro primer secreto creado db-zombie-pass podemos comprobar el contenido de esta manera:

➜  ~ kubectl get secret db-zombie-pass -o yaml
apiVersion: v1
data:
password.txt: MWYyZDFlMmU2N2Rm
username.txt: em9tYmll
kind: Secret
metadata:
creationTimestamp: 2016-11-30T17:07:17Z
name: db-zombie-pass
namespace: default
resourceVersion: "364840"
selfLink: /api/v1/namespaces/default/secrets/db-zombie-pass
uid: 72b890fd-b71f-11e6-84fe-2aa787ee170e
type: Opaque

Tal vez recuerdes que usé «zombi«como nombre de usuario pero estoy recibiendo»em9tYmll«… cualquier idea …! codificación de cadena base64 como se mencionó anteriormente. Por lo tanto, para verificar los valores debemos decodificar estos valores como lo hicimos antes para la codificación. Por ejemplo, decodifiquemos el nombre de usuario:

➜ ~ echo 'em9tYmll' | base64 --decode
zombie%

Usabilidad de secretos

Bueno, como se mencionó anteriormente, los secretos se pueden usar como volúmenes montados o como variables de entorno, que son la moda más utilizada de secretos en el ecosistema de K8 que describiremos en el artículo actual.
Como volumen montado:

  • En primer lugar, necesitamos crear un secreto como se describe anteriormente.
  • En segundo lugar, la especificación del pod debe modificarse para agregar un volumen debajo de la matriz de volúmenes especificando
    el campo secret.secretName para hacer referencia al nombre del objeto secreto.
  • En tercer lugar, debemos afectar el volumen secreto de cada contenedor en la vaina debajo
    contenedores[].volumeMounts[] y debemos especificar también ambos contenedores[].volumeMounts[].readOnly = true para que el volumen pueda estar en modo de solo lectura y la ruta de la carpeta del volumen montado en contenedores[].volumeMounts[].mountPath

Un ejemplo final de dicha configuración usando la especificación YAML se ve a continuación:

apiVersion: v1
kind: Pod
metadata:
name: "mypod"
namespace: "production"
spec:
containers:
- name: mypod
image: "redis"
volumeMounts:
- name: foo
mountPath: "/etc/baz"
readOnly: true
volumes:
- name: foo
secret:
secretName: "mysecret"

Notas:

  • Si hay varios contenedores que necesitan datos secretos, cada uno de ellos debe especificar volumeMounts
  • Es posible agrupar muchos archivos en un objeto secreto o usar muchos secretos en un archivo de especificaciones de pods
  • También es posible utilizar diferentes claves dentro de la ruta de diferentes archivos, este concepto se conoce como proyección de claves secretas. Ahora el nombre de usuario se almacenará en / etc / baz / ruta específica / nombre de usuario en vez de / etc / baz / nombre de usuario (vea el fragmento de shell a continuación).
  • Por favor, no que la contraseña no se proyecte y luego no se pueda usar, por lo tanto, la regla es que, una vez que se especifica la matriz de elementos, solo la clave especificada del secreto estará disponible para el pod y sus contenedores subyacentes.
  • Si una clave especificada no existe en el objeto secreto, el volumen nunca se creará
…
volumes:
- name: foo
secret:
secretName: “mysecret”
items:
- key: “username”
path: “specific-path/username”

Como variables de entorno

Al igual que para los volúmenes montados, debemos hacer un pequeño cambio en el archivo de especificaciones de los pods para poder usar secretos como variables env dentro de los pods y sus contenedores subyacentes agregando la etiqueta env como se ilustra a continuación, llamemos este archivo de especificaciones redis-pod.yaml:

apiVersion: v1

tipo: Pod

metadatos:

nombre: secreto-env-pod

Especificaciones:

contenedores:

– nombre: mycontainer

imagen: redis

env:

– nombre: SECRET_USERNAME

valueFrom:

secretKeyRef:

nombre: mysecret

clave: nombre de usuario

– nombre: SECRET_PASSWORD

valueFrom:

secretKeyRef:

nombre: mysecret

clave: contraseña

Una vez que se crean los pods, las variables env SECRET_USERNAME y SECRET_PASSWORD estarán disponibles dentro de los pods y listas para usar.

En el fragmento de shell a continuación, crearé los pods a partir del archivo de especificaciones de arriba y luego ssh los pods para verificar las dos variables env usando el comando kubectl exec nombre_de_los_pods -i -t – sh.

➜ ~ kubectl create -f redis-pod.yaml
pod "secret-env-pod" created
➜ ~ kubectl exec secret-env-pod -i -t -- sh
# echo $SECRET_USERNAME
zombie
# echo $SECRET_PASSWORD
1f2d1e2e67df
#

Tenga en cuenta que la creación de los pods puede llevar un poco de tiempo, por lo tanto, es posible que deba esperar un poco antes de poder ssh los pods, así que tenga paciencia. El estado de un pods específico se puede verificar ejecutando el comando kubectl obtener como abajo:

➜ ~ kubectl get pods secret-env-pod
NAME READY STATUS RESTARTS AGE
secret-env-pod 1/1 Running 0 5m

Casos de uso

En términos de caso de uso, proporcionaré dos casos de uso de uso frecuente en el ecosistema de devops principalmente, utilizando certificados ssh y secretos de credenciales sobre la marcha.

SSH

Uno de los casos de uso real de usar secreto en el ecosistema K8s es manejar claves ssh públicas y privadas, para ilustrar esto, voy a generar una clave ssh RSA, digamos, para Gitlab y después de eso voy a crear un objeto secreto para almacenar tanto la clave privada como la pública:

➜ ~ ssh-keygen -t rsa -b 4096 -C “zombie.zombie@mail.zom”
Generating public/private rsa key pair.
Enter file in which to save the key (.ssh/id_rsa): gitlab_rsa
Enter passphrase (empty for no passphrase):
…
➜ ~ kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=gitlab_rsa --from-file=ssh-publickey=gitlab_rsa.pub
secret "ssh-key-secret" created

Este secreto se puede usar como se muestra a continuación en la matriz de volúmenes en el archivo de especificaciones de los pods:

spec:
containers:
- name: mypod
image: "redis"
volumeMounts:
- name: foo
mountPath: “/etc/ssh-secret-vol“
readOnly: true
volumes:
- name: foo
secret:
secretName: "ssh-key-secret"

Una vez que los volúmenes están montados, las carpetas /etc/ssh-secret-vol/gitlab_rsa y /etc/ssh-secret- vol/gitlab_rsa.pub estará disponible.

Credenciales sobre la marcha

A veces, el usuario puede necesitar, digamos, un nombre de usuario y credenciales de contraseña para realizar alguna tarea, por ejemplo: depuración, inspección de la base de datos, etc., esto se puede lograr usando –de-literal argumento como el siguiente:

➜ ~ kubectl create secret generic debugger-secret --from-literal=username=debugger --from-literal=password=super-strong-pwd
secret "debugger-secret" created

Conclusión

Me gustaría concluir este artículo diciendo que realmente las API de kubectl están muy bien diseñadas, lo que las hace simples y especialmente fáciles de usar, por ejemplo, incluso si no mencioné cómo podemos eliminar manualmente un objeto secreto del que el usuario podría adivinarlo. los comandos usados ​​arriba como kubectl get pods name_of_the_pods o kubectl crear … cual es Kubectl eliminar pods name_of_the_pods.

Con respecto a la documentación oficial, los K8 traen más precauciones de seguridad con objetos secretos debajo del capó, por ejemplo, evitando crear secretos en el disco tanto como sea posible, enviando un secreto solo a los pods que lo requieran, la transferencia de secretos está protegida mediante el canal interno SSL / TLS , K8s también garantiza la actualización de secretos montados como volúmenes o como variables de entorno siempre que se actualicen los secretos asociados.

Leer también:

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba
Cerrar