martes, julio 02, 2019

Gestionar la política de seguridad de pods en Kubernetes “Like a Boss” (Parte 1 de 3)

Volvemos de nuevo con otro artículo relacionado con Docker, del cual hemos hablado varias veces en este blog. Recuerda que tienes disponible nuestro libro Docker:SecDevOps de la editorial de 0xWord, el cual recomendamos si quieres adentrarte en el apasionante mundo de los contenedores y el SecDevOps. Esta vez queremos centrarnos de nuevo Kubernetes (sobre el cual hacemos una introducción, además de Docker, en el curso de TSS Docker & SecDevOps), y en concreto de la seguridad de los pods.

Figura 1: Gestionar la política de seguridad de pods en Kubernetes “Like a Boss”
(Parte 1 de 3)

Los pods (uno o más contenedores los cuales comparten configuraciones de red y almacenamiento) son el componente principal de Kubernetes. Por lo tanto, su seguridad es primordial y tiene que ser implementada desde los primeros pasos de su diseño, utilizando políticas de seguridad (security policies). Vamos a ver con algunos ejemplos, cómo aplicar estas políticas de seguridad en nuestra implementación con Kubernetes.

Figura 2: Libro Docker: SecDevOps de 0xWord

De acuerdo con la documentación oficial, una política de seguridad de pods es un recurso a nivel de clúster que controla aspectos sobre la seguridad de un pod. Dichas políticas de seguridad se definen a través del objeto PodSecurityPolicy, a través del cual podemos definir las condiciones con las que un pod debe cumplir para ser aceptado en el sistema, y también nos permite definir los valores por defecto de los campos que no son explícitamente asignados.

Políticas de Seguridad en Kubernetes

Una política de seguridad se define como prácticamente todo en Kubernetes, a través de un fichero manifiesto, normalmente en formato yaml. Vamos a ver un ejemplo sencillo:

    apiVersion: policy/v1beta1
    kind: PodSecurityPolicy
    metadata:
      name: permissive
    spec:
      privileged: true
      hostNetwork: true
      hostIPC: true
      hostPID: true
      seLinux:
        rule: RunAsAny
      supplementalGroups:
        rule: RunAsAny
      runAsUser:
        rule: RunAsAny
      fsGroup:
        rule: RunAsAny
      hostPorts:
      - min: 0
        max: 65535
      volumes:
      - '*'

En dicho ejemplo se puede apreciar como la política definida es muy permisiva. Prácticamente nos permite ejecutar un pod con todo tipo de privilegios. Por ejemplo, podríamos ejecutarlo en modo privilegiado (privileged: true), con lo que podríamos tener acceso a partes del host, compartir el espacio de nombres de red, procesos e IPC (Inter-Process Communication) del host, correr el contenedor o contenedores como root, etcétera. A menos que haya una buena razón, este tipo de configuraciones se deberían evitar.

Las políticas de seguridad de pods, nos permiten (a los administradores) controlar los siguientes aspectos:
Contenedores en modo privilegiado: está característica permite o no permitir la ejecución de contenedores en modo privilegiado. El campo que establece este aspecto se llama privileged. Por defecto los contenedores corren en modo no privilegiado.
Espacio de nombres del host: existen cuatro campos que nos permiten definir el comportamiento de un contenedor con respecto al acceso a ciertas partes del host:
o HostPID: controla si los contenedores de un pod comparten el mismo espacio de procesos (IDs) del host.

o HostIPC: controla si los contenedores de un pod comparten el espacio IPC del host.

o HostNetwork: controla si un pod puede hacer uso del mismo espacio de red del host. Esto implica que el pod tendría acceso al dispositivo loopback, y a los procesos que estén corriendo en dicho host.

o HostPorts: define rangos de puertos permitidos en el espacio de red del host. Este rango viene dado por el campo HostPortRange y los atributos min y max que definen el rango de puertos, los valores de ambos atributos son incluidos en el rango.
Volúmenes y sistemas de ficheros:
o Volumes: provee de una lista de volúmenes permitidos. Estos corresponden a la fuente usada para la creación del volumen. Los tipos que se recomiendan como mínimo son: 
 configMap
 downwardAPI
 emptyDir
 persistentVolumeClaim
 secret
 projected
o FSGroup: controla que grupo suplementario que se aplica a algunos volúmenes. Se puede controlar a través de los siguientes campos:
MustRunAs: rango de IDs permitidos. Si no se especifica el valor, se usa el ID más pequeño de dicho rango por defecto.
MayRunAs: rango de IDs permitidos, pero en este caso no se determina el valor por defecto como en el caso anterior.
RunAsAny: permite que el ID del fsGroup se especificado sin restricciones de rango.
o AllowedHostPaths: especifica una lista de paths permitidos para ser usados por los volúmenes. Una lista vacía implicaría que no hay restricciones. Dicha lista está definida por dos attributos: pathPrefix y readOnly. 
En el primero de los casos podemos especificar el prefijo del path, y el segundo atributo nos permite establecer si el volumen se monta como sólo lectura o no. Por ejemplo:
allowedHostPaths:
- pathPrefix: "/foo"
readOnly: true
En dicho ejemplo se podría montar un volumen en: "/foo", "/foo/", "/foo/bar", etc. como sólo lectura, y no se podría montar un volumen en: "/fool", "/etc/foo" etc. o "/foo/../" 
o ReadOnlyRootFilesystem: requiere que los contenedores corran con el sistema de ficheros *root* en modo de sólo lectura.
Usuarios y grupos:
o RunAsUser: especifica con qué usuario corren los contenedores dentro del pod. 
MustRunAs: requiere al menos un rango de IDs. El ID especificado en el pod debe estar dentro de dicho rango, si no se provee de un ID, se usa el ID más bajo del rango. 
MustRunAsNonRoot: requiere que el pod sea definido sea definido con la opción runAsUser y que éste sea un ID distinto de 0 (root). Si en la definición del pod no se especifica runAsNonRoot o runAsUser, se establece runAsNonRoot=true, por lo que requiere que se especifique el usuario dentro de la imagen (USER en Dockerfile). Se recomienda encarecidamente el uso de allowPrivilegeEscalation=false. 
RunAsAny: permite asignar a *runAsUser* cualquier ID.
o RunAsGroup: especifica con que ID de grupo corren los contenedores dentro del pod. 
MustRunAs: requiere al menos un rango de IDs. El ID especificado en el pod debe estar dentro de dicho rango, si no se provee de un ID, se usa el ID más bajo del rango.  
MayRunAs: no especifica un valor por defecto, pero si se especifica RunAsGroup, el ID debe pertenecer a dicho rango.  
RunAsAny: permite asignar a runAsUser cualquier ID. 
o SupplementalGroups: especifica con que ID de grupo se añaden a los contenedores. 
MustRunAs: requiere al menos un rango de IDs. El ID especificado en el pod debe estar dentro de dicho rango, si no se provee de un ID, se usa el ID más bajo del rango.  
MayRunAs: no especifica un valor por defecto, pero si se especifica supplementalGroups, el ID debe pertenecer a dicho rango.  
RunAsAny: permite asignar a runAsUser cualquier ID.

Figura 3: Asignación de roles en un pod

Escalada de privilegios: básicamente controla la opción no_new_privs del proceso del contenedor. Esta opción evita que los binarios con la opción setuid cambien el ID efectivo del usuario, y previene la habilitación de capacidades nuevas extras. Esta opción al altamente recomendada cuando usamos MustRunAsNonRoot.
o AllowPrivilegeEscalation: especifica si se permite o no establecer el contexto de seguridad del contenedor. Por defecto, allowPrivilegeEscalation=true para evitar problemas con binarios con *setuid* activo. 
Si asignamos el valor *false* a dicho campo, nos aseguramos que cualquier proceso hijo no pueda obtener más privilegios que el proceso padre. 
o DefaultAllowPrivilegeEscalation: permite establecer la opción por defecto de AllowPrivilegeEscalation
Capacidades: las capacidades en GNU/Linux son una serie de privilegios de super usuario, los cuales pueden ser habilitados o deshabilitados de forma independientes. Los siguientes campos aceptan las capacidades en forma de lista, sin el prefijo CAP_ (todas las capacidades en GNU/Linux comienzan por dicho prefijo).
o AllowedCapabilities: lista de capacidades que pueden ser añadidas a un contenedor. Por defecto todas las capacidades son permitidas. Si se especifica este campo de forma vacía, implica que no se pueden añadir capacidades a un contenedor, más allá de las definidas por defecto. Se puede usar el asterisco (*) para referirse a todas las capacidades. 
o RequiredDropCapabilities: lista de capacidades que deben ser removidas del contenedor. Estas son removidas del grupo de capacidades por defecto. Las capacidades incluidas en este campo, no deben ser incluidas en AllowedCapabilities o DefaultAddCapabilities. 
o DefaultAddCapabilities: capacidades añadidas por defecto a un contenedor por defecto.
También existen políticas de seguridad asociadas a SELinux, AppArmor y Seccomp que no vamos a ver en esta entrada, así como políticas relacionadas a Sysctl y el montaje del directorio /proc. Vamos a continuar en la segunda entrada de este artículo con más opciones.

Fran Ramírez, (@cyberhadesblog) es investigador de seguridad y miembro del equipo de Ideas Locas en CDO en Telefónica, co-autor del libro "Microhistorias: Anécdotas y Curiosidades de la historia de la informática (y los hackers)", del libro "Docker: SecDevOps" y del blog Cyberhades.

Rafael Troncoso (@tuxotron) es Senior Software Engineer en SAP Concur, co-autor del libro "Microhistorias: Anécdotas y Curiosidades de la historia de la informática (y los hackers)", del libro "Docker: SecDevOps" y del blog Cyberhades.

**************************************************************************************************
- Gestionar la política de seguridad de pods en Kubernetes “Like a Boss” (1 de 3)
- Gestionar la política de seguridad de pods en Kubernetes “Like a Boss” (2 de 3)
- Gestionar la política de seguridad de pods en Kubernetes “Like a Boss” (3 de 3)
**************************************************************************************************

No hay comentarios:

Entrada destacada

10 maneras de sacarle el jugo a tu cuenta de @MyPublicInbox si eres un Perfil Público

Cuando doy una charla a algún amigo, conocido, o a un grupo de personas que quieren conocer MyPublicInbox , siempre se acaban sorprendiendo ...

Entradas populares