Los repositorios de imágenes privados son una práctica bastante común cuando se utilizan contenedores, ya que aseguran que las imágenes construidas sólo pueden ser utilizadas por usuarios y clústers con las credenciales adecuadas. Además, en algunos casos, estas credenciales permiten evitar las limitaciones a la hora de descargar imágenes de DockerHub.

El proceso de autenticación para los registros de imágenes es bastante sencillo cuando se utiliza un container runtime en local, como Docker. En este caso, sólo es necesario adecuar la configuración local para que utilice las credenciales adecuadas. En el caso de Docker, puede hacerse ejecutando el comando 'docker login'. No obstante, en el caso de un clúster de Kubernetes, cada nodo ejecuta su propio container runtime, por lo que el proceso es más complejo.
Utilizando imágenes privadas 📦🔒
Cuando queremos desplegar imágenes de repositorios privados en Kubernetes, en primer lugar tenemos que especificar los credenciales para que el container runtime del nodo pueda autenticarse antes de descargar la imagen para ejecutarla. Las credenciales se almacenan normalmente en un secret de Kubernetes de tipo docker-registry, que almacena los parámetros para autenticar al runtime en formato JSON.
kubectl create secret docker-registry mysecret \
-n <your-namespace> \
--docker-server=<your-registry-server> \
--docker-username=<your-name> \
--docker-password=<your-password> \
--docker-email=<your-email>
Las credenciales pueden utilizarse de dos maneras pricipalmente:
- En primer lugar, es posible referenciar el secret con la directiva imagePullSecrets directamente en el manifiesto del pod, como en el extracto de código a continuación. En este caso, las credenciales sólo se utilizarán en los pods que referencien el secret.
apiVersion: v1
kind: Pod
metadata:
name: foo
namespace: default
spec:
containers:
- name: foo
image: janedoe/awesomeapp:v1
imagePullSecrets:
- name: mysecret
- Otra forma de hacerlo es añadiendo la directiva a la service account por defecto del namespace. En este caso, todos los pods del namespace utilizarán el secret.
kubectl patch serviceaccount default \
-p "{\"imagePullSecrets\": [{\"name\": \"mysecret\"}]}" \
-n default
Esta segunda estrategia, puede utilizarse para autenticar el clúster por completo, pero para que funcione, es necesario crear un secret de forma manual en cada namespace y configurar la service account por defecto del namespace para que lo utilice. Además, si se crean nuevos namespaces, no se actualizan para utilizar las credenciales de forma automática.
Es por esto, que la herramienta que vamos a presentar puede ayudarnos en este caso.
Imagepullsecret-patcher 🤖🔒
Imagepullsecret-patcher es un proyecto open source desarrollado por Titansoft, una empresa de software de Singapur. Esta solución permite establecer credenciales a nivel de clúster para utilizar registros de imágenes privados.
Está implementado como una imagen que contiene una aplicación con un cliente de Go , que se comunica con la API de Kubernetes para crear un imagePullSecret en cada namespace, y configura las service accounts por defecto de cada namespace para que lo utilicen. Además, es capaz de detectar la creación de nuevos namespaces, y ejecuta este mismo proceso en cada nuevo namespace.
NOTA: Puede haber algunos casos de uso especiales en los que no sea necesario que un namespace utilice las credenciales del imagePullSecrets , por ello, es posible desabilitar el imagepullsecret-patcher para un namespace específico añadiendo la siguiente annotation al manifiesto del namespace.
k8s.titansoft.com/imagepullsecret-patcher-exclude: true
Desplegando la solución ➡️☸️
Primero, crearemos un namespace específico para el imagepullsecret-patcher. Puedes hacerlo ejecutando el siguiente comando.
kubectl create namespace imagepullsecret-patcher
Una vez que el namespace se haya creado, es necesario generar un secreto con las credenciales del repositorio de imágenes. Para hacerlo, ejecuta el siguiente comando con tus propios valores. Si utilizas Dockerhub, usa https://index.docker.io/v1/ en el campo –docker-server .
kubectl create secret docker-registry registry-credentials \
-n imagepullsecret-patcher \
--docker-server=<your-registry-server> \
--docker-username=<your-name> \
--docker-password=<your-password> \
--docker-email=<your-email>
Por último, debemos desplegar el resto de recursos que el imagepullsecret-patcher necesita. El siguiente comando deplegará un ClusterRole con permisos para crear y modificar secrets y service accounts, así como la propia service account del propio patcher. Esta service account y el ClusterRole se referencian por medio de un ClusterRoleBinding. Por último, este código generará un deployment que monta el secreto que creamos anteriormente y utiliza la service account creada.
kubectl apply -f https://raw.githubusercontent.com/mifonpe/pullsecrets-cluster-demo/main/manifests.yaml
Si quieres echarle un ojo a los recursos que acabamos de comentar, los tienes en este fichero del repo de ejemplo.
Una vez que el despliegue haya terminado, vamos a comprobar si funciona. Si listas los recursos en el namespace imagepullsecret-patcher , deberías ver algo como esto.

Utilizándolo ⚙️☸️
Una vez que la solución esté desplegada, puedes comprobar el namespace default y sus secrets. Podrás ver como el patcher ha creado un secret en ese namespace.
NOTA: k es un alias de kubetcl

Si compruebas la service account por defecto del namespace default, verás que utiliza el nuevo secret creado por el patcher.

Ahora, vamos a crear un nuevo namespace para ver cómo funciona. Ejecuta el siguiente comando.
kubectl create namespace test-patcher
Si comprobamos los secrets y la service account por defecto de este nuevo namespace, podremos comprobar que efectivamente funciona.
Puedes chequear también los logs del propio imagepullsecret-patcher, para ver cómo se detectó la creación de un nuevo namespace.
Para terminar, vamos a hacer pull de una imagen privada. Para este ejemplo utilizaré una imagen privada de DockerHub que construí para otro post del blog.

Para crear un deployment de forma imperativa que genere un pod con dicha imagen, ejecuta el siguiente comando.
kubectl run test --image <your-private-image>:<your-tag>
Ahora comprobemos que el pod está corriendo.
Si haces un describe del pod, podrás ver como la imagen privada fue descargada por el container runtime del nodo.

¿Cómo mejorarlo? ➕🤖
En mi caso de uso personal, gestiono la mayoría de los add-ons de mis clústers de Kubernetes usando GitLab CI y Helmfile (si nunca has oído hablar de esta herramienta, puedes encontrar un artículo aquí). Mi objetivo con esta estrategia, es evitar intervenciones manuales en cualquier clúster, para que los procesos de creación y Disaster Recovery sean completamente automáticos.
Para ello, instalo el imagepullsecret-patcher como una release de Helm e inyecto las variables (parámetros de autenticación) directamente desde GitLab CI, evitando así la creación manual de secrets, y minimizando el riesgo de filtración de credenciales. En línea con esto, utilizo el templatizado de Helm para inyectar los datos de autenticación que se almacenan el las variables de GitLab CI. El siguiente extracto de código te dará una idea de cómo implementar esta solución.
Esta solución puede extenderse a cualquier plataforma de CI/CD. Los credenciales se configuran una sola vez en tu almacén de secretos, y el CI se encargará de desplegar todo!
apiVersion: v1
kind: Secret
type: kubernetes.io/dockerconfigjson
metadata:
name: image-pull-secret-src
namespace: imagepullsecret-patcher
data:
.dockerconfigjson: {{ requiredEnv "DOCKERHUB_AUTH" }}
Otros Artículos
Karpenter vs Cluster Autoscaler ☸️ ⚒️
Getting the size of a Kubernetes cluster right is not an easy task, if the number of nodes provisioned is too high, resources might be…
Read MoreAutenticación a nivel de clúster para tus registries privados 🌐🔐
Using private container image registries is a common practice in the industry, as it ensures applications packaged within the images are just accessible for users…
Read MoreKaniko: construyendo imágenes sin Docker🐳🔫
Building container images is a pretty common stage within the modern CI flows, in which these images are the main artifact used to distribute and…
Read MoreKubernetes en 5 Minutos ⏱️☸️
English https://www.youtube.com/watch?v=N8LDO9pHY8I Spanish https://www.youtube.com/watch?v=TC6VkqQ835U Other Articles
Read MoreKsniff: capturando tráfico en Kubernetes 🕵️♂️📦
Troubleshooting containers in Kubernetes is a recurring topic that you may face regularly. Logging and monitoring tools come in really handy here as they help…
Read MoreHelmfile: superpoderes para Helm ☸️🔧
Helm is definitely one the most used tools in the Kubernetes world. It comes in pretty handy as it is a deployment tool, a package…
Read MoreTerraboard: Gestionando estados de Terrafom visualmente🌍🖥
Dealing with multiple terraform remote states can become a rather complex task. Besides, querying resources with terraform CLI isn’t very visual 😅. In this post…
Read Moreeksctl: el CLI para EKS ☸️ 🎮
Let’s be honest, managing Kubernetes clusters is not an easy task, even when it comes to managed Kubernetes services. Controlling the overall infrastructure, performing cluster…
Read MoreWerf: GitOps totalmente customizable 🛥️⚙️
This is the third post of a collection of GitOps tools articles. In this post, a continuous integration and continuous deployment tool for Kubernetes is…
Read More