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.
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.