Skip to main content

Pregunta 42 | CKS Challenge 2 - Seguridad Multi-Entorno

¡Llévame al laboratorio!

Ten en cuenta que el estado de competición para los Desafíos CKS ha terminado. Por favor no envíes una solución. No será puntuada.

Se han desplegado varias aplicaciones en los namespaces dev, staging y prod. Hay algunos problemas de seguridad con estas aplicaciones.

Inspecciona los problemas en detalle haciendo clic en los iconos del diagrama de arquitectura interactivo en el laboratorio y completa las tareas para asegurar las aplicaciones. Una vez hecho, haz clic en el botón Check para validar tu trabajo.

Realiza las tareas en este orden:

dockerfile

  • Ejecutar como no root (en su lugar, usar el usuario de aplicación correcto)
  • Evitar exponer puertos innecesarios
  • Evitar copiar el Dockerfile y otros archivos y directorios innecesarios en la imagen. Mueve los archivos y directorios requeridos (app.py, requirements.txt y el directorio templates) a un subdirectorio llamado app bajo webapp y actualiza la instrucción COPY en el Dockerfile en consecuencia.
  • Una vez que se corrijan los problemas de seguridad, reconstruye esta imagen localmente con la etiqueta kodekloud/webapp-color:stable

Las primeras dos subtareas implican limpiar el Dockerfile...

cd /root/webapp
vi Dockerfile

Cambia el Dockerfile así:

Se nos pide mover la aplicación al subdirectorio app, así que cambia la instrucción COPY en anticipación a esto

COPY ./app /opt

Ejecutar como no root

Se ha creado un usuario con RUN adduser -D worker, pero no estamos cambiando a él, sino que estamos cambiando a root.

Cambia la línea USER root a USER worker

Evitar exponer puertos innecesarios. No necesitamos el puerto 22 (SSH) para la aplicación, así que elimina las siguientes líneas

## Expose port 22
EXPOSE 22

Ahora guarda y sal de vi

Mueve la aplicación y archivos relacionados a un nuevo subdirectorio app

mkdir app
mv app.py app/
mv requirements.txt app/
mv templates app/

Reconstruir imagen

docker build -t kodekloud/webapp-color:stable .

Volver al directorio home

cd ~

kubesec

  • Corrige problemas con el archivo /root/dev-webapp.yaml que se usó para desplegar el pod dev-webapp en el namespace dev.
  • Redesplegar el pod dev-webapp una vez que los problemas se corrijan con la imagen kodekloud/webapp-color:stable
  • Corrige problemas con el archivo /root/staging-webapp.yaml que se usó para desplegar el pod staging-webapp en el namespace staging.
  • Redesplegar el pod staging-webapp una vez que los problemas se corrijan con la imagen kodekloud/webapp-color:stable

Al ejecutar kubesec podemos usar jq para extraer la parte de la salida JSON que es relevante para identificar problemas críticos con el manifiesto escaneado. Ejecuta sin | jq y todo después para ver el informe completo.

dev-webapp.yaml

kubesec scan /root/dev-webapp.yaml | jq '.[] | .scoring.critical'

Nota que CapSysAdmin y AllowPrivilegeEscalation están señalados.

Edita el manifiesto:

  • Elimina la capacidad SYS_ADMIN
  • Establece allowPrivilegeEscalation a false
  • Establece la imagen del contenedor a kodekloud/webapp-color:stable (que construimos anteriormente)

No recrees el pod aún. Hay más que hacer en la siguiente etapa.

staging-webapp.yaml

kubesec scan /root/dev-webapp.yaml | jq '.[] | .scoring.critical'

Nota que esto tiene exactamente los mismos problemas que dev-webapp.yaml. Realiza exactamente los mismos pasos que para staging-webapp.yaml.

dev-webapp

Asegúrate de que el pod dev-webapp sea inmutable:

  • Se puede acceder a este pod usando el comando kubectl exec. Queremos asegurarnos de que esto no suceda. Usa una startupProbe para eliminar todos los shells antes del inicio del contenedor. Usa initialDelaySeconds y periodSeconds de 5. Pista: ¡Para que esto funcione tendrías que ejecutar el contenedor como root!
  • Imagen usada: kodekloud/webapp-color:stable (Ya hemos hecho esto arriba)
  • Redesplegar el pod según las recomendaciones anteriores y asegurarse de que la aplicación esté funcionando.

Verifica qué shells están presentes en el contenedor - los comandos de shell se encuentran en el directorio /bin y usualmente terminan con sh, por ejemplo sh mismo, bash, etc.

kubectl exec -n dev dev-webapp -- ls /bin | grep sh

Salida:

fdflush no es un shell, pero los otros dos sí. ash es un shell normalmente empaquetado con Alpine Linux.

Crea una startup probe según la especificación, y asegúrate de que la startup probe pueda ejecutarse como root. Ten en cuenta que las probes no se ven afectadas por el comando USER en el Dockerfile.

Edita dev-webapp.yaml Añade lo siguiente bajo securityContext, si no está ya ahí

runAsUser: 0

Inserta la probe

startupProbe:
exec:
command:
- rm
- /bin/sh
- /bin/ash
initialDelaySeconds: 5
periodSeconds: 5

Ahora recrea el pod en ejecución con todo lo que cambiamos en el paso 2 y este paso

kubectl replace -f dev-webapp.yaml --force

staging-webapp

Asegúrate de que el pod dev-webapp sea inmutable:

  • Se puede acceder a este pod usando el comando kubectl exec. Queremos asegurarnos de que esto no suceda. Usa una startupProbe para eliminar todos los shells antes del inicio del contenedor. Usa initialDelaySeconds y periodSeconds de 5. Pista: ¡Para que esto funcione tendrías que ejecutar el contenedor como root!
  • Imagen usada: kodekloud/webapp-color:stable (Ya hemos hecho esto arriba)
  • Redesplegar el pod según las recomendaciones anteriores y asegurarse de que la aplicación esté funcionando.

Sigue los mismos pasos que para dev-webapp arriba, ajusta staging-webapp.yaml y recrea el pod.

prod-web

  • El deployment tiene un secret codificado. En su lugar, crea un secret llamado prod-db para todos los valores codificados y consume los valores del secret como variables de entorno dentro del deployment.

Examina el manifiesto del deployment para ver cuál es este secret

kubectl get deployment -n prod prod-web -o yaml

Podemos ver que hay 3 variables de entorno con valores.

Crea un secret para estas vars

kubectl create secret generic prod-db -n prod \
--from-literal DB_Host=prod-db \
--from-literal DB_User=root \
--from-literal DB_Password=paswrd

Edita el deployment y cambia la sección env para obtener los valores del secret

kubectl edit deployment -n prod prod-web

Reemplaza las variables bajo el bloque env con

    - name: DB_User
valueFrom:
secretKeyRef:
key: DB_User
name: prod-db
- name: DB_Host
valueFrom:
secretKeyRef:
key: DB_Host
name: prod-db
- name: DB_Password
valueFrom:
secretKeyRef:
key: DB_Password
name: prod-db

Prueba esto presionando el botón prod-web arriba del terminal. Después de aplicar la network policy a continuación, esto ya no funcionará

prod-netpol

  • Usa una network policy llamada prod-netpol que solo permitirá tráfico dentro del namespace prod. Todo el tráfico desde otros namespaces debe ser denegado.

Ten en cuenta que todos los namespaces tienen una etiqueta predefinida kubernetes.io/metadata.name que es muy útil al crear network policies restringidas a namespace.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: prod-netpol
namespace: prod
spec:
podSelector: {} # aplicar a todos los pods en el namespace prod
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {} # cualquier pod...
namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: prod # ...que esté solo en el namespace prod

Una vez que se completen todas las tareas anteriores, haz clic en el botón Check.