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
-
Ingresar asl sistema bastion con el usuario student.
-
Instalaremos el comando
yq, para validar y consultar el archivo de configuración de PSS.sudo zypper install -y yq -
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-#-masterSalida de ejemplo:ssh student@lab-${LAB}-master 'sudo cat /etc/rancher/rke2/rke2-pss.yaml '|yqapiVersion: 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: []Salida ejemplo:ssh student@lab-${LAB}-master 'sudo cat /etc/rancher/rke2/rke2-pss.yaml' |yq '.plugins[]| select(.name == "PodSecurity").configuration.defaults.enforce'El nivel por defecto esprivilegedprivilegeden este cluster. -
Activaremos la configuracion de KUBECONFIG, para utilizar el clúster
cluster1.export KUBECONFIG=/home/student/rke2_conn/cluster1/cluster1_kubeconfig.yaml - Configurar Políticas de Seguridad de Pods, etiquetemos el namespace secure-ns con el nivel de seguridad
restricteden modowarn. Esta etiqueta nos permiteALERTARacerca de las violaciones a la política, pero permitira la ejecución de la carga de trabajo.kubectl create namespace secure-nskubectl label namespace \ secure-ns pod-security.kubernetes.io/warn=restricted - 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 - Explore la definición del recurso.
cat pod1.yamlapiVersion: 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: {} - Aplique la configuración en el cluster.
Note los mensajes en la terminal:
kubectl apply -f pod1.yamlSin importar que no se cumple con la política, se permite ejecutar la carga de trabajo. Esto debido a que no hemos definido el modoWarning: 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 createdwarna nivel delnamespace. -
Examine el pod creado. Estado del pod.
Salida ejemplo:kubectl get pods -n secure-nsExamine los logs.NAME READY STATUS RESTARTS AGE pod1 1/1 Running 0 3m46sSalida de ejemplo:kubectl logs -n secure-ns pod1No deben mostrarse errores y el pod debe estar en ejecución... /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 ... -
Procederemos a realizar las modificaciones que nos indica el
WARNING. Editando el archivopod1.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: {} -
Eliminaremos el recurso y procederemos crearlo nuevamente, utilizando el archivo modificado
pod1.yaml:Se han agregado las recomendaciones para la políticakubectl delete -f pod1.yaml kubectl apply -f pod1.yamlrestricted. Note que no se devuelve algun mensaje de Error o Warning en la termina. -
Examine el pod creado. Estado del pod.
Salida ejemplo:kubectl get pods -n secure-nsEsto nos indica que exite un error en con configuracion aplicada. Examine los eventos del namespaceNAME READY STATUS RESTARTS AGE pod1 1/1 CreateContainerConfigError 0 3m46ssecure-ns.Salida de ejemplo:kubectl get events -n secure-nsEl mensaje nos indica que el contenedor tiene la configuración para que se ejecute como... 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) ...root, pero la imagen esta preparada para ejecutarse como este usuario. -
Procederemos a realizar las modificaciones que nos indica el
Warningen los eventos. Editando el archivopod1.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: {} -
Eliminaremos el recurso y procederemos a crearlo nuevamente, utilizando el archivo modificado
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 delete -f pod1.yaml kubectl apply -f pod1.yamlSalida de ejemplo:kubectl logs -n secure-ns pod1En este caso la imagen no está preparada para utilizarse como un usuario diferente a root, debe modificarse la imagen o utilizar otra./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) -
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: {} -
Examine el pod creado. Estado del pod.
Salida ejemplo:kubectl get pods -n secure-nsExamine los logs.NAME READY STATUS RESTARTS AGE pod1 1/1 Running 0 3m46sSalida de ejemplo:kubectl logs -n secure-ns pod1No deben mostrarse errores y el pod debe estar en ejecución... 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 ... -
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 -
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 -
Cree la definición de un nuevo pod llamado
pod2.sed 's/pod1/pod2/g' pod1.yaml > pod2.yaml -
Modificaremos el atributo,
allowPrivilegeEscalationdefalseatrue.sed -i 's/\(.*allowPrivilegeEscalation:\).*/\1 true/g' pod2.yaml -
Removemos la etiqueta de la politica restricted en
warn.kubectl label namespace secure-ns pod-security.kubernetes.io/warn- -
Agregamos la etiqueta de la politica restricted en
enforce.kubectl label namespace secure-ns pod-security.kubernetes.io/enforce=restricted -
Aplique la configuración en el cluster.
Salida de Ejemplo:kubectl apply -f pod2.yamlEl pod2 fue rechazado por la política de seguridad restricted.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) -
Modificaremos el atributo,
allowPrivilegeEscalationdetrueafalse.sed -i 's/\(.*allowPrivilegeEscalation:\).*/\1 false/g' pod2.yaml -
Aplique la configuración en el cluster.
El comando debe ser exitosokubectl apply -f pod2.yaml