Skip to content

Pod Security Admission en Kubernetes

En este laboratorio, los alumnos aprenderán a configurar y utilizar Pod Security Admission en un clúster de Kubernetes. Pod Security Admission es una funcionalidad que permite aplicar políticas de seguridad a los pods, asegurando que cumplan con ciertos estándares de seguridad antes de ser creados o modificados.

Niveles de Seguridad en Pod Security Admission: Privileged, Baseline y Restricted

Pod Security Admission en Kubernetes proporciona tres niveles de seguridad predefinidos: privileged, baseline y restricted. Cada uno de estos niveles tiene diferentes restricciones y políticas de seguridad que se aplican a los pods para asegurar el cumplimiento de algunas prácticas de seguridad sugeridas. Aquí se explican las diferencias entre estos niveles:

Privileged

El nivel privileged es el menos restrictivo de los tres niveles. Permite la ejecución de pods con configuraciones privilegiadas y menos seguras. Este nivel está diseñado para entornos en el que se requiere un alto grado de flexibilidad y no se aplican restricciones de seguridad estrictas.

Características:

  • Permite el uso de todos los controles de seguridad contextuales.
  • Permite el uso de capacidades elevadas (capabilities).
  • Permite el uso de volúmenes privilegiados, como hostPath.
  • No impone restricciones en la escalación de privilegios (allowPrivilegeEscalation).

Uso: - Entornos de desarrollo y prueba donde la seguridad no es una preocupación principal. - Situaciones donde se requiere acceso total al host.

Baseline

El nivel baseline proporciona un equilibrio entre seguridad y funcionalidad. Aplica restricciones moderadas para mitigar riesgos comunes sin afectar significativamente la funcionalidad de los pods. Está diseñado para aplicaciones que necesitan algunas capacidades avanzadas pero aún requieren un cierto nivel de seguridad.

Características:

  • Permite ciertas capacidades elevadas, pero restringe las más peligrosas (NET_RAW).
  • Permite el uso de volúmenes no privilegiados.
  • Prohíbe la escalación de privilegios (allowPrivilegeEscalation debe estar deshabilitado).
  • Prohíbe el uso de hostPath y otros volúmenes privilegiados.
  • Requiere que los contenedores se ejecuten como no root (runAsNonRoot).

Uso:

  • Aplicaciones de producción que no requieren capacidades privilegiadas pero necesitan cierta flexibilidad.
  • Entornos donde se busca un equilibrio entre seguridad y funcionalidad.

Restricted

El nivel restricted es el más seguro de los tres niveles. Aplica las restricciones de seguridad más estrictas, limitando significativamente las capacidades del pod para mitigar riesgos de seguridad. Está diseñado para aplicaciones que necesitan el máximo nivel de seguridad.

Características: - Restringe todas las capacidades elevadas. - Prohíbe el uso de volúmenes privilegiados como hostPath. - Prohíbe la escalación de privilegios (allowPrivilegeEscalation debe estar deshabilitado). - Requiere que los contenedores se ejecuten como no root (runAsNonRoot). - Requiere la definición de un contexto de seguridad (seccompProfile, runAsUser, runAsGroup, etc.).

Uso: - Aplicaciones críticas para la seguridad donde la reducción de la superficie de ataque es primordial. - Entornos de producción que manejan datos sensibles o críticos y necesitan una configuración de seguridad estricta.

Rancher Kubernetes Engine unicamente soporta los niveles de seguridad: privileged y restricted

Guía Paso a Paso: Laboratorio de Pod Security Admission en Kubernetes

En este laboratorio, los alumnos aprenderán a configurar y utilizar Pod Security Admission en un clúster de Kubernetes. Pod Security Admission es una funcionalidad que permite aplicar políticas de seguridad a los pods, asegurando que cumplan con ciertos estándares de seguridad antes de ser creados o modificados.

Requisitos Previos

  • Un clúster de Kubernetes desplegado y funcionando.
  • kubectl configurado para interactuar con el clúster.
  • Acceso a la máquina desde donde se administrará el clúster.

Pasos

  1. Ingresar asl sistema bastion con el usuario student.

  2. Instalaremos el comando yq, para validar y consultar el archivo de configuración de PSS.

    sudo zypper install -y yq
    

  3. Revisar la configuracion de rke y revisar que las pod security configuration esten correcta. Establecer una sesión como el usuario student al servidor: lab-#-master

      ssh student@lab-${LAB}-master 'sudo cat  /etc/rancher/rke2/rke2-pss.yaml '|yq
    
    Salida de ejemplo:
    apiVersion: apiserver.config.k8s.io/v1
    kind: AdmissionConfiguration
    plugins:
    - name: PodSecurity
      configuration:
        apiVersion: pod-security.admission.config.k8s.io/v1beta1
        kind: PodSecurityConfiguration
        defaults:
          enforce: "privileged"
          enforce-version: "latest"
        exemptions:
          usernames: []
          runtimeClasses: []
          namespaces: []
    
    ssh student@lab-${LAB}-master 'sudo cat  /etc/rancher/rke2/rke2-pss.yaml' |yq '.plugins[]| select(.name == "PodSecurity").configuration.defaults.enforce'
    
    Salida ejemplo:
    privileged
    
    El nivel por defecto es privileged en este cluster.

  4. Activaremos la configuracion de KUBECONFIG, para utilizar el clúster cluster1.

    export KUBECONFIG=/home/student/rke2_conn/cluster1/cluster1_kubeconfig.yaml
    

  5. Configurar Políticas de Seguridad de Pods, etiquetemos el namespace secure-ns con el nivel de seguridad restricted en modo warn. Esta etiqueta nos permite ALERTAR acerca de las violaciones a la política, pero permitira la ejecución de la carga de trabajo.
    kubectl create namespace secure-ns
    
    kubectl label namespace \
    secure-ns pod-security.kubernetes.io/warn=restricted
    
  6. Cree un una definición de pod utilizando la imagen docker.io/nginx:latest.
    kubectl run pod1 -n secure-ns  --image=docker.io/nginx:latest -o yaml --dry-run=client > pod1.yaml
    
  7. Explore la definición del recurso.
    cat pod1.yaml
    
    apiVersion: v1
    kind: Pod
    metadata:
      creationTimestamp: null
      labels:
        run: pod1
      name: pod1
      namespace: secure-ns
    spec:
      containers:
      - image: docker.io/nginx:latest
        name: pod1
        resources: {}
      dnsPolicy: ClusterFirst
      restartPolicy: Always
    status: {}
    
  8. Aplique la configuración en el cluster.
    kubectl apply -f pod1.yaml
    
    Note los mensajes en la terminal:
    Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false \
    (container "pod1" must set securityContext.allowPrivilegeEscalation=false), \
    unrestricted capabilities (container "pod1" must set securityContext.capabilities.drop=["ALL"]), \
    runAsNonRoot != true (pod or container "pod1" must set securityContext.runAsNonRoot=true), \
    seccompProfile (pod or container "pod1" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
    pod/pod1 created
    
    Sin importar que no se cumple con la política, se permite ejecutar la carga de trabajo. Esto debido a que no hemos definido el modo warn a nivel del namespace.
  9. Examine el pod creado. Estado del pod.

    kubectl get pods -n secure-ns
    
    Salida ejemplo:
    NAME   READY   STATUS    RESTARTS   AGE
    pod1   1/1     Running   0          3m46s
    
    Examine los logs.
    kubectl logs -n secure-ns pod1
    
    Salida de ejemplo:
    ...
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
    /docker-entrypoint.sh: Configuration complete; ready for start up
    2024/11/28 02:35:45 [notice] 1#1: using the "epoll" event method
    2024/11/28 02:35:45 [notice] 1#1: nginx/1.27.3
    2024/11/28 02:35:45 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)
    2024/11/28 02:35:45 [notice] 1#1: OS: Linux 6.4.0-150600.23.22-default
    2024/11/28 02:35:45 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 10
    ...
    
    No deben mostrarse errores y el pod debe estar en ejecución

  10. Procederemos a realizar las modificaciones que nos indica el WARNING. Editando el archivo pod1.yaml. Ajustar la definición del pod a la siguiente:

    apiVersion: v1
    kind: Pod
    metadata:
      creationTimestamp: null
      labels:
        run: pod1
      name: pod1
      namespace: secure-ns
    spec:
      securityContext:
        runAsNonRoot: true
        seccompProfile:
          type: RuntimeDefault
      containers:
      - image: docker.io/nginx:latest
        name: pod1
        resources: {}
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
      dnsPolicy: ClusterFirst
      restartPolicy: Always
    status: {}
    

  11. Eliminaremos el recurso y procederemos crearlo nuevamente, utilizando el archivo modificado pod1.yaml:

    kubectl delete -f pod1.yaml
    kubectl apply -f pod1.yaml
    
    Se han agregado las recomendaciones para la política restricted. Note que no se devuelve algun mensaje de Error o Warning en la termina.

  12. Examine el pod creado. Estado del pod.

    kubectl get pods -n secure-ns
    
    Salida ejemplo:
    NAME   READY   STATUS    RESTARTS   AGE
    pod1   1/1     CreateContainerConfigError   0          3m46s
    
    Esto nos indica que exite un error en con configuracion aplicada. Examine los eventos del namespace secure-ns.
    kubectl get events -n secure-ns
    
    Salida de ejemplo:
    ...
    95s         Warning   Failed      pod/pod1   Error: container has runAsNonRoot and image will run as root (pod: "pod1_secure-ns(c9d5fb11-e6ea-4ef7-833a-ebaec36fb755)", container: pod1)
    ...
    
    El mensaje nos indica que el contenedor tiene la configuración para que se ejecute como root, pero la imagen esta preparada para ejecutarse como este usuario.

  13. Procederemos a realizar las modificaciones que nos indica el Warning en los eventos. Editando el archivo pod1.yaml. Ajustar la definición del pod a la siguiente:

    apiVersion: v1
    kind: Pod
    metadata:
      creationTimestamp: null
      labels:
        run: pod1
      name: pod1
      namespace: secure-ns
    spec:
      securityContext:
        runAsUser: 1001
        runAsGroup: 0
        runAsNonRoot: true
        seccompProfile:
          type: RuntimeDefault
      containers:
      - image: docker.io/nginx:latest
        name: pod1
        resources: {}
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
      dnsPolicy: ClusterFirst
      restartPolicy: Always
    status: {}
    

  14. Eliminaremos el recurso y procederemos a crearlo nuevamente, utilizando el archivo modificado pod1.yaml:

    kubectl delete -f pod1.yaml
    kubectl apply -f pod1.yaml
    
    Se han agregado las configuraciones para ejecutar los contenedores del por como el usuario (1001) y el grupo (0) (restricted). No se muestra error en la terminal. Examine los logs.
    kubectl logs -n secure-ns pod1
    
    Salida de ejemplo:
    /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
    10-listen-on-ipv6-by-default.sh: info: can not modify /etc/nginx/conf.d/default.conf (read-only file system?)
    /docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
    /docker-entrypoint.sh: Configuration complete; ready for start up
    2024/11/28 03:06:55 [warn] 1#1: the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2
    nginx: [warn] the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2
    2024/11/28 03:06:55 [emerg] 1#1: mkdir() "/var/cache/nginx/client_temp" failed (13: Permission denied)
    nginx: [emerg] mkdir() "/var/cache/nginx/client_temp" failed (13: Permission denied)
    
    En este caso la imagen no está preparada para utilizarse como un usuario diferente a root, debe modificarse la imagen o utilizar otra.

  15. Utilizaremos otra imagen.

    apiVersion: v1
    kind: Pod
    metadata:
      creationTimestamp: null
      labels:
        run: pod1
      name: pod1
      namespace: secure-ns
    spec:
      securityContext:
        runAsUser: 1001
        runAsGroup: 0
        runAsNonRoot: true
        seccompProfile:
          type: RuntimeDefault
      containers:
      - image: cgr.dev/chainguard/nginx
        name: pod1
        resources: {}
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
      dnsPolicy: ClusterFirst
      restartPolicy: Always
    status: {}
    

  16. Examine el pod creado. Estado del pod.

    kubectl get pods -n secure-ns
    
    Salida ejemplo:
    NAME   READY   STATUS    RESTARTS   AGE
    pod1   1/1     Running   0          3m46s
    
    Examine los logs.
    kubectl logs -n secure-ns pod1
    
    Salida de ejemplo:
    ...
    nginx: [warn] the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2
    2024/11/28 03:19:23 [notice] 1#1: using the "epoll" event method
    2024/11/28 03:19:23 [notice] 1#1: nginx/1.27.2
    2024/11/28 03:19:23 [notice] 1#1: OS: Linux 6.4.0-150600.23.22-default
    2024/11/28 03:19:23 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
    2024/11/28 03:19:23 [notice] 1#1: start worker processes
    2024/11/28 03:19:23 [notice] 1#1: start worker process 6
    2024/11/28 03:19:23 [notice] 1#1: start worker process 7
    ...
    
    No deben mostrarse errores y el pod debe estar en ejecución

  17. Realice la misma prueba utiizando la imagen docker.io/nginxinc/nginx-unprivileged:stable. No deben mostrarse error y el pod debe estar en ejecución

  18. Crear un Pod Cumpliendo la Política Restricted: En el namespace secure-ns, creamos un pod que cumple con la política restricted:

    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
      name: compliant-pod
      namespace: secure-ns
    spec:
      securityContext:
        runAsUser: 1000
      containers:
      - name: nginx
        image: nginx
        securityContext:
          runAsNonRoot: true
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
          seccompProfile:
            type: RuntimeDefault
    EOF
    

  19. Cree la definición de un nuevo pod llamado pod2.

    sed 's/pod1/pod2/g' pod1.yaml  > pod2.yaml
    

  20. Modificaremos el atributo, allowPrivilegeEscalation de false a true.

    sed -i 's/\(.*allowPrivilegeEscalation:\).*/\1 true/g' pod2.yaml
    

  21. Removemos la etiqueta de la politica restricted en warn.

      kubectl label namespace   secure-ns pod-security.kubernetes.io/warn-  
    

  22. Agregamos la etiqueta de la politica restricted en enforce.

      kubectl label namespace   secure-ns pod-security.kubernetes.io/enforce=restricted  
    

  23. Aplique la configuración en el cluster.

    kubectl apply -f pod2.yaml
    
    Salida de Ejemplo:
    Error from server (Forbidden): error when creating "pod2.yaml": pods "pod2"
    is forbidden: violates PodSecurity "restricted:latest": allowPrivilegeEscalation != false 
    (container "pod2" must set securityContext.allowPrivilegeEscalation=false)
    
    El pod2 fue rechazado por la política de seguridad restricted.

  24. Modificaremos el atributo, allowPrivilegeEscalation de true a false.

    sed -i 's/\(.*allowPrivilegeEscalation:\).*/\1 false/g' pod2.yaml
    

  25. Aplique la configuración en el cluster.

    kubectl apply -f pod2.yaml
    
    El comando debe ser exitoso

Referencias: