Secrets

Los objetos de tipo Secret en Kubernetes te permiten almacenar y administrar información confidencial, como contraseñas, tokens OAuth y llaves ssh. Poniendo esta información en un Secret es más seguro y más flexible que ponerlo en la definición de un Pod o en un container image. Ver Secrets design document para más información.

Introducción a Secrets

Un Secret es un objeto que contiene una pequeña cantidad de datos confidenciales como contraseñas, un token, o una llave. Tal información podría ser puesta en la especificación de un Pod o en una imagen; poniendolo en un objeto de tipo Secret permite mayor control sobre como se usa, y reduce el riesgo de exposicición accidental.

Los usuarios pueden crear Secrets, y el sistema también puede crearlos.

Para usar un Secret, un Pod debe hacer referencia a este. Un Secret puede ser usado con un Pod de dos formas: como archivos en un volume montado en uno o más de sus contenedores, o utilizados por el kubelet al extraer imágenes del pod.

Secrets incorporados

Las Cuentas de Servicio Crean y Adjuntan Secrets con las Credenciales de la API

Kubernetes crea automaticamente Secrets que contienen credenciales para acceder a la API y modifica automáticamente sus pods para usar este tipo de Secret.

La creación y el uso automático de las credenciales de la API, pueden desabilitarse o anularse si se desea. Sin embargo, si todo lo que necesita hacer es acceder de forma segura al apiserver, este es el flujo de trabajo recomendado.

Ver la documentación de Service Account para más información sobre cómo funcionan las Cuentas de Servicio.

Creando tu propio Secret

Creando un Secret Usando kubectl create Secret

Pongamos como ejemplo el caso de una grupo de pods que necesitan acceder a una base de datos. El nombre y contraseña que los pods deberían usar están en los archivos: ./username.txt y ./password.txt en tu máquina local.

# Crear archivos necesarios para el resto del ejemplo.
echo -n 'admin' > ./username.txt
echo -n '1f2d1e2e67df' > ./password.txt

El comando kubectl create secret empaqueta esos archivos en un Secret y crea el objeto en el Apiserver.

kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
Secret "db-user-pass" created

Puedes comprobar que el Secret se haya creado, así:

kubectl get secrets
NAME                  TYPE                                  DATA      AGE
db-user-pass          Opaque                                2         51s
kubectl describe secrets/db-user-pass
Name:            db-user-pass
Namespace:       default
Labels:          <none>
Annotations:     <none>

Type:            Opaque

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

Ver Decodificando un Secret para ver el contenido de un Secret.

Creando un Secret Manualmente

Puedes crear también un Secret primero en un archivo, en formato json o en yaml, y luego crear ese objeto. El Secret contiene dos mapas: data y stringData. El campo de data es usado para almacenar datos arbitrarios, codificado usando base64. El campo stringData se proporciona para su conveniencia, y le permite proporcionar datos secretos como cadenas no codificadas.

Por ejemplo, para almacenar dos cadenas en un Secret usando el campo data, conviértalos a base64 de la siguiente manera:

echo -n 'admin' | base64
YWRtaW4=
echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm

Escribe un secret que se vea así:

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

Ahora escribe un Secret usando kubectl apply:

kubectl apply -f ./secret.yaml
secret "mysecret" created

Para ciertos escenarios, es posible que desee utilizar el campo de stringData field en su lugar. Este campo le permite poner una cadena codificada que no sea base64 directamente en el Secret, y la cadena será codificada para ti cuando el Secret es creado o actualizado.

Un ejemplo práctico de esto podría ser donde está implementando una aplicación que usa un Secret para almacenar un archivo de configuración, y desea completar partes de ese archivo de configuración durante su proceso de implementación.

Si su aplicación usa el siguiente archivo de configuración:

apiUrl: "https://my.api.com/api/v1"
username: "user"
password: "password"

Podrías almacenarlo en un Secret usando lo siguiente:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
stringData:
  config.yaml: |-
    apiUrl: "https://my.api.com/api/v1"
    username: {{username}}
    password: {{password}}    

Su herramienta de despliegue podría entonces reemplazar el {{username}} y {{password}} variables de plantilla antes de ejecutar kubectl apply.

stringData es un campo de conveniencia de solo lectura. Nunca se muestran cuando se recuperan Secrets. Por ejemplo, si ejecuta el siguiente comando:

kubectl get secret mysecret -o yaml

La salida será similar a:

apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: 2018-11-15T20:40:59Z
  name: mysecret
  namespace: default
  resourceVersion: "7225"
  selfLink: /api/v1/namespaces/default/secrets/mysecret
  uid: c280ad2e-e916-11e8-98f2-025000000001
type: Opaque
data:
  config.yaml: YXBpVXJsOiAiaHR0cHM6Ly9teS5hcGkuY29tL2FwaS92MSIKdXNlcm5hbWU6IHt7dXNlcm5hbWV9fQpwYXNzd29yZDoge3twYXNzd29yZH19

Si se especifica un campo tanto de data y stringData, el valor de StringData es usado. Por ejemplo, la siguiente definición de Secret:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
stringData:
  username: administrator

Los resultado en el siguiente Secret:

apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: 2018-11-15T20:46:46Z
  name: mysecret
  namespace: default
  resourceVersion: "7579"
  selfLink: /api/v1/namespaces/default/secrets/mysecret
  uid: 91460ecb-e917-11e8-98f2-025000000001
type: Opaque
data:
  username: YWRtaW5pc3RyYXRvcg==

Donde YWRtaW5pc3RyYXRvcg== decodifica a administrator.

Las llaves de data y stringData deben consistir en caracteres alfanuméricos, '-', '_' or '.'.

Nota de codificación: Los valores serializados JSON y YAML de los datos secretos estan codificadas como cadenas base64. Las nuevas lineas no son válidas dentro de esa cadena y debe ser omitido. Al usar base64 en Darwin/macOS, los usuarios deben evitar el uso de la opción -b para dividir líneas largas. Por lo contratio los usuarios de Linux deben añadir la opción -w 0 a los comandos base64 o al pipeline base64 | tr -d '\n' si la opción -w no esta disponible.

Creando un Secret a partir de Generador

Kubectl soporta managing objects using Kustomize desde 1.14. Con esta nueva característica, puedes tambien crear un Secret a partir de un generador y luego aplicarlo para crear el objeto en el Apiserver. Los generadores deben ser especificados en un kustomization.yaml dentro de un directorio.

Por ejemplo, para generar un Secret a partir de los archivos ./username.txt y ./password.txt

# Crear un fichero llamado kustomization.yaml con SecretGenerator
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: db-user-pass
  files:
  - username.txt
  - password.txt
EOF

Aplica el directorio kustomization para crear el objeto Secret.

$ kubectl apply -k .
secret/db-user-pass-96mffmfh4k created

Puedes verificar que el secret fue creado de la siguiente manera:

$ kubectl get secrets
NAME                             TYPE                                  DATA      AGE
db-user-pass-96mffmfh4k          Opaque                                2         51s

$ kubectl describe secrets/db-user-pass-96mffmfh4k
Name:            db-user-pass
Namespace:       default
Labels:          <none>
Annotations:     <none>

Type:            Opaque

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

Por ejemplo, para generar un Secret a partir de literales username=admin y password=secret, puedes especificar el generador del Secret en kustomization.yaml como:

# Crea un fichero kustomization.yaml con SecretGenerator
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: db-user-pass
  literals:
  - username=admin
  - password=secret
EOF

Aplica el directorio kustomization para crear el objeto Secret.

kubectl apply -k .
secret/db-user-pass-dddghtt9b5 created

Decodificando un Secret

Los Secrets se pueden recuperar a través del comando kubectl get secret . Por ejemplo, para recuperar el Secret creado en la sección anterior:

kubectl get secret mysecret -o yaml
apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: 2016-01-22T18:41:56Z
  name: mysecret
  namespace: default
  resourceVersion: "164619"
  selfLink: /api/v1/namespaces/default/secrets/mysecret
  uid: cfee02d6-c137-11e5-8d73-42010af00002
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm

Decodifica el campo de contraseña:

echo 'MWYyZDFlMmU2N2Rm' | base64 --decode
1f2d1e2e67df

Usando Secrets

Los Secrets se pueden montar como volúmenes de datos o ser expuestos como variables de entorno para ser usados por un contenedor en un pod. También pueden ser utilizados por otras partes del sistema, sin estar directamente expuesto en el pod. Por ejemplo, pueden tener credenciales que otras partes del sistema usan para interactuar con sistemas externos en su nombre.

Usando Secrets como Archivos de un Pod

Para consumir un Secret en un volumen en un Pod:

  1. Crear un Secret o usar uno existente. Múltiples pods pueden referenciar el mismo Secret.
  2. Modifique la definición del Pod para agregar un volumen debajo de .spec.volumes[]. Asigne un nombre al volumen y tenga un campo .spec.volumes[].secret.secretName igual al nombre del objeto del Secret.
  3. Agrega un .spec.containers[].volumeMounts[] a cada contenedor que necesite un Secret. Especifica .spec.containers[].volumeMounts[].readOnly = true y .spec.containers[].volumeMounts[].mountPath a un nombre de directorio no utilizado donde desea que aparezca los Secrets.
  4. Modifique la imagen y/o linea de comando para que el programa busque archivos en ese directorio. Cada llave en el data map del los Secrets se convierte en el nombre del archivo bajo mountPath.

Este es un ejemplo de un pod que monta un Secret en un volumen:

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

Cada Secret que desea usar debe mencionarse en .spec.volumes.

Si hay múltiples contenedores en un Pod, entonces cada contenedor necesita su propio bloque volumeMounts , pero solo un .spec.volumes se necesita por Secret.

Puede empaquetar muchos archivos en un Secret, o usar muchos Secrets, lo que sea conveniente.

Proyección de llaves Secret a rutas específicas

También podemos controlar las rutas dentro del volumen donde se proyectan las llaves Secrets. Puede usar el campo .spec.volumes[].secret.items para cambiar la ruta de destino de cada clave:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username

Lo que sucederá:

  • El Secret username se almacena bajo el archivo /etc/foo/my-group/my-username en lugar de /etc/foo/username.
  • El Secret password no se proyecta

Si se utiliza .spec.volumes[].secret.items , solo se proyectan las llaves específicadas en los items. Para consumir todas las llaves del Secret, Todas deben ser enumeradas en el campo items. Todas las llaves enumeradas deben existir en el Secret correspondiente. De lo contrario, el volumen no se crea.

Permisos de archivos Secrets

Tambien puede especificar el modo de permiso de los archivos de bits que tendrá una parte de un Secret. Si no especifica ninguno, 0644 es usado por defecto. Puede especificar un modo predeterminado para todo el volumen del Secret y sobreescribir por llave si es necesario.

Por ejemplo, puede especificar un modo predeterminado como este:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      defaultMode: 256

Entonces, el Secret será montado en /etc/foo y todos los archivos creados por el montaje del volumen del Secret tendrán permiso 0400.

Tenga en cuenta que la especificación JSON no soporta la notación octal, entonces use el valor 256 para permisos 0400. Si usa yaml en lugar de json para el pod, puede usar notación octal para especificar permisos de una manera más natural.

También puede usar el mapeo, como en el ejemplo anterior, y especificar diferentes permisos para diferentes archivos como:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username
        mode: 511

En este caso, el archivo resultante en /etc/foo/my-group/my-username tendrá un valor de permiso 0777. Debido a las limitaciones de JSON, debe especificar el modo en notación decimal.

Tenga en cuenta que este valor de permiso puede mostrarse en notación decimal si lo lee después.

Consumir Valores Secrets de Volúmenes

Dentro del contenedor que monta un volumen del Secret, las llaves del Secret aparece como archivos y los valores del Secret son decodificados en base-64 y almacenados dentro de estos archivos. Este es el resultado de comandos ejecutados dentro del contenedor del ejemplo anterior:

ls /etc/foo/
username
password
cat /etc/foo/username
admin
cat /etc/foo/password
1f2d1e2e67df

El programa en un contenedor es responsable de leer los Secrets de los archivos.

Los Secrets Montados se actualizan automáticamente

Cuando se actualiza un Secret que ya se está consumiendo en un volumen, las claves proyectadas también se actualizan eventualmente. Kubelet está verificando si el Secret montado esta actualizado en cada sincronización periódica. Sin embargo, está usando su caché local para obtener el valor actual del Secret. El tipo de caché es configurable usando el (campo ConfigMapAndSecretChangeDetectionStrategy en KubeletConfiguration struct). Puede ser propagado por el reloj (default), ttl-based, o simplemente redirigiendo todas las solicitudes a kube-apiserver directamente. Como resultado, el retraso total desde el momento en que se actualiza el Secret hasta el momento en que se proyectan las nuevas claves en el Pod puede ser tan largo como el periodo de sincronización de kubelet + retraso de propagación de caché, donde el retraso de propagación de caché depende del tipo de caché elegido. (Es igual a mirar el retraso de propagación, ttl of cache, o cero correspondientemente).

Usando Secrets como Variables de Entorno

Para usar un Secret en una variable de entorno en un pod:

  1. Crea un Secret o usa uno existente. Múltiples pods pueden hacer referencia a un mismo Secret.
  2. Modifique la definición de su Pod en cada contenedor que desee consumir el valor de una llave Secret para agregar una variable de entorno para cada llave Secret que deseas consumir. La variable de entorno que consume la llave Secret debe completar el nombre y la llave del Secret en env[].valueFrom.secretKeyRef.
  3. Modifique su imagen y/o linea de comandos para que el programa busque valores en las variables de entorno especificadas.

Esto es un ejemplo de un pod que usa Secrets de variables de entorno:

apiVersion: v1
kind: Pod
metadata:
  name: secret-env-pod
spec:
  containers:
  - name: mycontainer
    image: redis
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: password
  restartPolicy: Never

Consumiendo Valores Secrets a partir de Variables de Entorno

Dentro de un contenedor que consume un Secret en una variable de entorno, las claves Secrets aparecen como variables de entorno normal que contienen valores decodificados de base-64 de los datos del Secret. Este es el resultado de comandos ejecutados dentro del contenedor del ejemplo anterior.

echo $SECRET_USERNAME
admin
echo $SECRET_PASSWORD
1f2d1e2e67df

Usando imagePullSecrets

Una imagePullSecret es una forma de pasar a kubelet un Secret que contiene las credenciales para un registro de imagenes de Docker (u otro) para que pueda obtener una imagen privada en nombre de su pod.

Especificar manualmente una imagePullSecret

El uso de imagePullSecrets se desccriben en la documentación de las imágenes images documentation

Organización de imagePullSecrets para que se Adjunte Automáticamente

Puede crear manualmente un imagePullSecret, y hacer referencia a él desde un serviceAccount. Cualquier pod creado con ese serviceAccount o ese valor predeterminado para usar serviceAccount, obtendrá su campo imagePullSecret establecido en el service account. Ver Agregar ImagePullSecrets a una cuenta de servicio para una explicación detallada de ese proceso.

Montaje Automatico de Secrets Creados Manualmente

Secrets creados Manualmente, (por ejemplo uno que contiene un token para acceder a una cuenta github) se puede conectar automáticamente a los pods según su cuenta de servicio. Vea Inyección de infromación en pods usando un a PodPreset para una explicación detallada de este proceso.

Detalles

Restricciones

Las fuentes del volumen del Secret se validan para garantizar que la referencia del objeto especificado apunte a un objeto de tipo Secret. Por lo tanto, se debe crear un Secret antes de que cualquier pod dependa de él.

Los objetos API Secret residen en namespace. Solo pueden ser referenciados por pods en el mismo namespace.

Los Secrets individuales estan limitados a 1MiB de tamaño. Esto es para desalentar la creación de Secrets muy grandes que agotarían la memoria del apiserver y de kubelet. Sin embargo la creación de muchos Secrets más pequeños también podría agotar la memoria. Límites más completos en el uso de memoria debido a Secret es una característica planificada.

Kubelet solo admite el uso de Secret para Pods que obtiene del API server. Esto incluye cualquier pods creado usando kubectl, o indirectamente a través de un contralador de replicación. No incluye pods creados a través de los kubelets --manifest-url flag, its --config flag, o su REST API (estas no son formas comunes de crear pods.)

Los Secrets deben crearse antes de que se consuman en pod como variables de entono a menos que estén marcados como optional. Referencias a Secrets que no existen evitarán que el pod inicie. Las referencias a través de secretKeyRef a claves que no existen en un Secret con nombre evitarán que el pod se inicie.

Los Secrets que se utilizan para poblar variables de entorno a través de envFrom que tienen claves que se consideran nombres de variables de entorno no validos, tendran esas claves omitidas. El Pod se permitira reiniciar. Habrá un evento cuyo motivo es InvalidVariableNames y el mensaje contendrá la lista de claves no validas que se omitieron. El ejemplo muestra un pod que se refiere al default/mysecret que contiene 2 claves no validas, 1 badkey y 2 alsobad.

kubectl get events
LASTSEEN   FIRSTSEEN   COUNT     NAME            KIND      SUBOBJECT                         TYPE      REASON
0s         0s          1         dapi-test-pod   Pod                                         Warning   InvalidEnvironmentVariableNames   kubelet, 127.0.0.1      Keys [1badkey, 2alsobad] from the EnvFrom secret default/mysecret were skipped since they are considered invalid environment variable names.

Interacción del Secret y Pod de por vida

Cuando se crea un Pod a través de la API, no se verifica que exista un recreto referenciado. Una vez que se programa el Pod, kubelet intentará obtener el valor del Secret. Si el Secret no se puede recuperar será por que no existe o por una falla temporal de conexión al servidor API, kubelet volverá a intentarlo periodicamente. Enviará un evento sobre el pod explicando las razones por la que aún no se inició. Una vez que el Secret es encontrado, kubelet creará y montará el volumen que lo contiene. Ninguno de los contenedorees del pod se iniciará hasta que se monten todos los volúmes del pod.

Casos de uso

Caso de Uso: Pod con llaves ssh

Cree un fichero kustomization.yaml con SecretGenerator conteniendo algunas llaves ssh:

kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub
secret "ssh-key-secret" created

Ahora podemos crear un pod que haga referencia al Secret con la llave ssh key y lo consuma en un volumen:

apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
  labels:
    name: secret-test
spec:
  volumes:
  - name: secret-volume
    secret:
      secretName: ssh-key-secret
  containers:
  - name: ssh-test-container
    image: mySshImage
    volumeMounts:
    - name: secret-volume
      readOnly: true
      mountPath: "/etc/secret-volume"

Cuando se ejecuta el comando del contenedor, las partes de la llave estarán disponible en:

/etc/secret-volume/ssh-publickey
/etc/secret-volume/ssh-privatekey

El contenedor es libre de usar los datos del Secret para establecer conexión ssh.

Caso de uso: Pods con credenciales prod / test

Este ejemplo ilustra un pod que consume un Secret que contiene credenciales de prod y otro pod que consume un Secret con credenciales de entorno de prueba.

Crear un fichero kustomization.yaml con SecretGenerator

kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
secret "prod-db-secret" created
kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests
secret "test-db-secret" created

Ahora haz los pods:

cat <<EOF > pod.yaml
apiVersion: v1
kind: List
items:
- kind: Pod
  apiVersion: v1
  metadata:
    name: prod-db-client-pod
    labels:
      name: prod-db-client
  spec:
    volumes:
    - name: secret-volume
      secret:
        secretName: prod-db-secret
    containers:
    - name: db-client-container
      image: myClientImage
      volumeMounts:
      - name: secret-volume
        readOnly: true
        mountPath: "/etc/secret-volume"
- kind: Pod
  apiVersion: v1
  metadata:
    name: test-db-client-pod
    labels:
      name: test-db-client
  spec:
    volumes:
    - name: secret-volume
      secret:
        secretName: test-db-secret
    containers:
    - name: db-client-container
      image: myClientImage
      volumeMounts:
      - name: secret-volume
        readOnly: true
        mountPath: "/etc/secret-volume"
EOF

Añade los pods a el mismo fichero kustomization.yaml

cat <<EOF >> kustomization.yaml
resources:
- pod.yaml
EOF

Aplique todos estos objetos en el Apiserver por

kubectl apply --k .

Ambos contenedores tendrán los siguientes archivos presentes en sus sistemas de archivos con valores para el entorno de cada contenedor:

/etc/secret-volume/username
/etc/secret-volume/password

observe cómo las especificaciones para los dos pods difieren solo en un campo; esto facilita la creación de pods con diferentes capacidades de una plantilla de configuración de pod común.

Deberías simplificar aún más la especificación del pod base utilizando dos Cuentas de Servicio: uno llamado, prod-user con el prod-db-secret, y otro llamado, test-user con el test-db-secret. Luego, la especificación del pod se puede acortar a, por ejemplo:

apiVersion: v1
kind: Pod
metadata:
  name: prod-db-client-pod
  labels:
    name: prod-db-client
spec:
  serviceAccount: prod-db-client
  containers:
  - name: db-client-container
    image: myClientImage

Caso de uso: Dotfiles en el volume del Secret

Para hacer que los datos esten 'ocultos' (es decir, en un file dónde el nombre comienza con un caracter de punto), simplemente haga que esa clave comience con un punto. Por ejemplo, cuando el siguiente Secret es montado en un volumen:

apiVersion: v1
kind: Secret
metadata:
  name: dotfile-secret
data:
  .secret-file: dmFsdWUtMg0KDQo=
---
apiVersion: v1
kind: Pod
metadata:
  name: secret-dotfiles-pod
spec:
  volumes:
  - name: secret-volume
    secret:
      secretName: dotfile-secret
  containers:
  - name: dotfile-test-container
    image: k8s.gcr.io/busybox
    command:
    - ls
    - "-l"
    - "/etc/secret-volume"
    volumeMounts:
    - name: secret-volume
      readOnly: true
      mountPath: "/etc/secret-volume"

El secret-volume contendrá un solo archivo, llamado .secret-file, y el dotfile-test-container tendrá este fichero presente en el path /etc/secret-volume/.secret-file.

Caso de uso: Secret visible para un contenedor en un pod

Considere un programa que necesita manejar solicitudes HTTP, hacer una lógica empresarial compleja y luego firmar algunos mensajes con un HMAC. Debido a que tiene una lógica de aplicación compleja, puede haber una vulnerabilidad de lectura remota de archivos inadvertida en el servidor, lo que podría exponer la clave privada a un atacante.

Esto podría dividirse en dos procesos en dos contenedores: un contenedor de frontend que maneja la interacción del usuario y la lógica empresarial. pero que no puede ver la clave privada; y un contenedor de firmante que puede ver la clave privada, y responde a solicitudes de firma simples del frontend (ejemplo, a través de redes de localhost).

Con este enfoque particionado, un atacante ahora tiene que engañar a un servidor de aplicaciones para que haga algo bastante arbitrario, lo que puede ser más difícil que hacer que lea un archivo.

Mejores prácticas

Clientes que usan la API de Secrets

Al implementar aplicaciones que interactuan con los API Secrets, el acceso debe limitarse utilizando authorization policies como RBAC.

Los Secrets a menudo contienen valores que abarcan un espectro de importancia, muchos de los cuales pueden causar escalamientos dentro de Kubernetes (ejememplo, tokens de cuentas de servicio) y a sistemas externos. Incluso si una aplicación individual puede razonar sobre el poder de los Secrets con los que espera interactuar, otras aplicaciones dentro dle mismo namespace pueden invalidar esos supuestos.

Por esas razones las solicitudes de watch y list dentro de un espacio de nombres son extremadamente poderosos y deben evitarse, dado que listar Secrets permiten a los clientes inspecionar los valores de todos los Secrets que estan en el namespace. La capacidad para watch and list todos los Secrets en un cluster deben reservarse solo para los componentes de nivel de sistema más privilegiados.

Las aplicaciones que necesitan acceder a la API de Secrets deben realizar solicitudes de get de los Secrets que necesitan. Esto permite a los administradores restringir el acceso a todos los Secrets mientras white-listing access to individual instances que necesita la aplicación.

Para un mejor rendimiento sobre un bucle get, los clientes pueden diseñar recursos que hacen referencia a un Secret y luego un Secret watch el recurso, al volver a solicitar el Secret cuando cambie la referencia. Además,, un "bulk watch" API para que los clientes puedan watch recursos individuales, y probablemente estará disponible en futuras versiones de Kubernetes.

Propiedades de seguridad

Protecciones

Debido a que los objetos Secret se pueden crear independientemente de los Pods que los usan, hay menos riesgo de que el Secret expuesto durante el flujo de trabajo de la creación, visualización, y edición de pods. El sistema también puede tomar precausiones con los objetosSecret, tal como eviar escribirlos en el disco siempre que sea posible.

Un Secret solo se envía a un nodo si un pod en ese nodo lo requiere. Kubelet almacena el Secret en un tmpfs para que el Secret no se escriba en el almacenamiento de disco. Una vez que se elimina el pod que depende del Secret, kubelet eliminará su copia local de los datos de Secrets.

Puede haber Secrets para varios Pods en el mismo nodo. Sin embargo, solo los Secrets que solicita un Pod son potencialmente visibles dentro de sus contenedores. Por lo tanto, un Pod no tiene acceso a los Secrets de otro Pod.

Puede haber varios contenedores en un Pod. Sin embargo, cada contenedor en un pod tiene que solicitar el volumen del Secret en su volumeMounts para que sea visible dentro del contenedor. Esto se puede usar para construir particiones de seguridad útiles en el Pod level](#use-case-secret-visible-to-one-container-in-a-pod).

En la mayoría de las distribuciones Kubernetes-project-maintained, la comunicación entre usuario a el apiserver, y del apiserver a kubelets, ista protegido por SSL/TLS. Los Secrets estan protegidos cuando se transmiten por estos canales.

FEATURE STATE: Kubernetes v1.13 [beta]

Puedes habilitar encryption at rest para datos secretos, para que los Secrets no se almacenen en claro en etcd.

Riesgos

  • En el servidor API, los datos de los Secrets se almacenan en etcd; por lo tanto:
    • Los adminsitradores deben habilitar el cifrado en reposo para los datos del cluster (requiere v1.13 o posterior)
    • Los administradores deben limitar el acceso a etcd a los usuarios administradores
    • Los administradores pueden querer borrar/destruir discos usados por etcd cuando ya no estén en uso
    • Si ejecuta etcd en un clúster, los administradores deben asegurarse de usar SSL/TSL para la comunicación entre pares etcd.
  • Si configura el Secret a través de un archivo de (JSON o YAML) que tiene los datos del Secret codificados como base64, compartir este archivo o registrarlo en un repositorio de origen significa que el Secret está comprometido. La codificación Base64 no es un método de cifrado y se considera igual que un texto plano.
  • Las aplicaciones aún necesitan proteger el valor del Secret después de leerlo del volumen, como no registrarlo accidentalmente o transmitirlo a una parte no confiable.
  • Un usuario que puede crear un pod que usa un Secret también puede ver el valor del Secret. Incluso si una política del apiserver no permite que ese usuario lea el objeto Secret, el usuario puede ejecutar el pod que expone el Secret.
  • Actualmente, cualquier persona con root en cualquier nodo puede leer cualquier secret del apiserver, haciéndose pasar por el kubelet. Es una característica planificada enviar Secrets a los nodos que realmente lo requieran, para restringir el impacto de una explosión de root en un single node.

Siguientes pasos

Última modificación January 15, 2023 at 12:50 PM PST: [ES] Fix broken links for KubeletConfiguration struct (94437843f)