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:
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: