Harbour como Registro de Imágenes y Kyverno para forzar políticas

Hace unos meses nos preguntamos: ¿Sabemos qué imágenes usamos en nuestros entornos? ¿Son las que fueron aprobadas por Seguridad? ¿Podría un atacante desplegar imágenes adulteradas? Hicimos esta entrada en el blog donde creamos políticas en nuestro clúster Kubernetes con Kyverno para permitir solo el despliegue de imágenes firmadas y aprobadas por Seguridad. No es nada nuevo decir que la seguridad es como una capa de cebolla, así que me pregunté: ¿Y si alojamos el Registro de Imágenes? ¡Manos a la obra!

Para ello, vamos a usar Harbour, un registro open source que ofrece una variedad de características, como escaneo, firma, autenticación, auditoría, etc. Además, acercamos estas imágenes a nuestro entorno en tiempo de ejecución.

Instalación de Harbor

Primero deberíamos tener nginx ingress para poder consumir la instalación de Harbour. Vamos a pegar una mirada a nuestro cluster.

Vamos agregar los helm's necesarios y editar alguna linea, para configurar la interfaz a consumir.

helm repo add harbor https://helm.goharbor.io

Vamos a descargar el helm para editar esos archivos.

helm fetch harbor/harbor --untar

Deberíamos poder abrir un editor y ver estos archivos.

Edito el FQDN que voy a utilizar, de manera local.

y

Ahora si instalamos.

helm install harbor ./harbor -n harbor --create-namespace

Vamos a ver el ingress y apunto la resolucion local al Cluster de Kubernetes.

Vamos a probar el ingreso, con las credenciales por defecto.

Username: admin
Password: Harbor12345

¡Perfecto! Ya tenemos nuestro servidor de registro listo.

Creación de Proyecto

Vamos a crear un proyecto, llamado blog.

Push Imagen Local

Ahora que tenemos nuestro registro de imágenes, vamos a subir una imagen a Harbor. Para ello, necesitamos autenticarnos.

En nuestro caso, como tenemos un certificado no válido, vamos a configurar el Engine de Docker para aceptar un registro no estándar. Esto no es seguro, pero para nuestra prueba de concepto, es suficiente.

Agregamos la sentencia

  "insecure-registries": [
    "harbor.esprueba.com"
  ]

Y reiniciamos Docker.

Ahora si podemos autenticarnos.

Vamos a descargar la imagen de Nginx, luego la "etiquetamos" y la subimos a Harbor.

Listo, ya está en el servidor de imagenes.

Policy as a Code

Podemos verla en la entrada que les comente al principio, si no estaremos el paso a paso aca. Una vez instalado vamos a crear una política que solo acepte imágenes de nuestra instancia de Harbor en un Namespace específico.

Instalación Kyverno

Vamos a agregar el repositorio de helm para luego instalarlo.

helm repo add kyverno-repo https://kyverno.github.io/kyverno/
helm install kyverno kyverno-repo/kyverno -n kyverno-ns --create-namespace

¡Listo! Ya tenemos Kyverno en el clúster con todos sus componentes.

Creación Política

Vamos a crear la política que comentamos anteriormente, para que solo acepte imagenes de Harbor y se pueda deployar en el namespace nginx-app.

apiVersion : kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: check-images
spec:
  validationFailureAction: Enforce
  background: false
  rules:
  - name: check-registry
    match:
      any:
      - resources:
          kinds:
          - Pod
          namespaces:
          - nginx-app

    preconditions:
      any:
      - key: "{{request.operation}}"
        operator: NotEquals
        value: DELETE
    validate:
      message: "unknown registry"
      foreach:
      - list: "request.object.spec.initContainers"
        pattern:
          image: "harbor.esprueba.com/*"
      - list: "request.object.spec.containers"
        pattern:
          image: "harbor.esprueba.com/*"

Vamos a crear un deployment para la prueba de concepto, usando una imagen de DockerHub. Si todo ha salido bien, no debería poder hacer el deployment.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: nginx-app
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - containerPort: 80

Excelente! No se está pudiendo crear el contenedor porque Kyverno lo esta parando.

Ahora si, vamos a ver si el registro es Harbor que pasa.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: nginx-app
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: harbor.esprueba.com/blog/nginx:alpine
        ports:
        - containerPort: 80

Listo se estan creando los contenedores. La política de Kyveno que creamos permite que las imagenes de harbor.esprueba.com/blog/nginx:alpine se desplieguen en el namespace nginx-app.

Espero que les sirva, nos vemos en la próxima entrada.

0
Subscribe to my newsletter

Read articles from Santiago Fernandez directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Santiago Fernandez
Santiago Fernandez

I have a bachelor's degree in Technology from the University of Palermo, a Master in Information Security from the University of Murcia and different certifications such as CISSP | CISM | CDPSE | CCSK | CSX | MCSA | SMAC™️ | DSOE | DEPC | CSFPC | CSFPC | 5x AWS Certified. He is currently CISO at Klar, a Mexican Fintech. He was fortunate to be awarded as CISO of the Year in Argentina in 2021 and was among the Top 100 CISO's in the World in 2022. A lover of new technologies, he has developed a career in DevSecOps and Cloud Security at Eko Party, the largest security conference in Latin America.