Press "Enter" to skip to content

C贸mo firmar im谩genes Docker utilizando Docker Content Trust

Hace poco, tuvimos el requerimiento de un cliente para realizar el firmado de sus im谩genes Docker almacenadas en Azure Container Registry (ACR). Dado que era una tarea relativamente nueva para nosotros, tuvimos que documentarnos en el tema y realizar varias pruebas de concepto.

Luego de algunos intentos y de resolver varios problemas que se nos presentaron en el camino, logramos definir un proceso claro y repetible para el firmado de las im谩genes Docker, a la vez que lo implementamos exitosamente en el cliente.

Debido a la dificultad que present贸 realizar esta tarea y a la falta de consistencia en la documentaci贸n encontrada decid铆 escribir este art铆culo, en el cual trato de integrar todo el conocimiento y herramientas necesarias para realizar el correcto firmado de im谩genes Docker siguiendo el protocolo Docker Content Trust (DCT).

馃敯 Requerimientos Iniciales

馃寜 La Arquitectura de Docker Content Trust (DCT)

Los componentes necesarios para realizar el proceso de firmado de im谩genes Docker bajo el esquema DCT son los siguientes:

馃攽 Las Keys para el firmado

Para el firmado de im谩genes Docker necesitaremos principalmente 3 tipos de Keys (claves). Las describimos a continuaci贸n:

  • Root Key: Es la clave ra铆z para todo un registro de contenedores (container registry) y es la clave base sobre la cual se crean las Repository keys. En caso de perder la Root Key, es imposible recuperarla.
  • Repository Key: Es una clave correspondiente a un repositorio de im谩genes en particular y servir谩 para firmar s贸lo los tags pertenecientes a dicho repositorio. Es creada a partir de la Root Key por lo que de perderse 茅sta ya no podr谩n crearse nuevas Repository Keys.
  • Delegation Key: Es la key correspondiente a un usuario con permisos para firmar las im谩genes de un repositorio particular. Estos permisos se asignan mediante un proceso llamado delegaci贸n.
Different Key Types to sign Docker images
Different Key Types to sign Docker images

馃洝锔 El Notary Server

Cuando se generan las firmas de las im谩genes Docker, 茅stas no se guardan con la imagen Docker en s铆 sino que se almacenan en un servidor Llamado Notary Server. Hay que tener en cuentan que no todos los registros de contenedores de im谩genes soportan DCT y por ende cuentan con un Notary Server habilitado.

En nuestro caso usaremos Azure Container Registry (ACR) para almacenar nuestras im谩genes el cual nos permite habilitar el protocolo DCT en sus registros de contenedores. En este caso, el Notary Server es accesible por la misma URL del registro de contenedor (usando https) como en la imagen de ejemplo.

Azure Container Registry ACR, Notary Server and DCT
Azure Container Registry and Notary Server

Cuando activamos el Notary Server, 茅ste se habilita para el registro de contenedores, es decir para todos los repositorios almacenados en dicho registro. Adicionalmente, un repositorio de im谩genes puede tener tags firmados y no firmados conviviendo sin problemas.

馃殌 Desplegando un registro de contenedores en Azure (ACR)

Antes de todo debemos tener desplegado un registro de contenedores Docker, en nuestro caso haremos uso de Azure Container Registry (ACR) puesto que incluye el soporte de Docker Content Trust para el firmado de im谩genes.

馃惓 Creando el registro

Al momento de crear el registro de contenedores, le damos un nombre y seleccionamos como SKU a Premium puesto que este Tier es el que nos permitir谩 habilitar el servicio de Docker Content Trust:

Creating an Azure Container Registry
Creating an Azure Container Registry

En la siguiente p谩gina habilitamos el acceso p煤blico al registro, dejamos las dem谩s opciones por defecto y creamos el registro:

Creating an Azure Container Registry
Creating an Azure Container Registry

Una vez creado el registro debemos habilitar DCT, para ello en la lista de opciones del registro (panel izquierdo) vamos a Policies Content trust, lo habilitamos seleccionando Status Enabled y guardamos (Save):

Enabling Docker Content Trust in ACR
Enabling Docker Content Trust in ACR

馃懃 Asignando permisos para el firmado de im谩genes

Adicionalmente debemos asignar permisos (rol) a nuestro usuario para que 茅ste tenga la capacidad de firmar las im谩genes almacenadas en nuestro registro.

En el panel izquierdo vamos a Access control (IAM) y en la parte inferior del panel principal, en la secci贸n Grant access to this resource seleccionamos la opci贸n Add role assignment:

Adding AcrImageSigner role to User
Accessing to Access control (IAM) in ACR

En la lista de roles que se mostrar谩n buscamos el rol AcrImageSigner, lo seleccionamos y hacemos click en Next:

Adding AcrImageSigner role to User: Selecting Role
Adding AcrImageSigner role to User: Selecting Role

En la p谩gina siguiente seleccionamos +Select members y se nos mostrar谩 a la derecha un panel en el cual buscaremos a nuestro usuario ingresando el email. Seleccionamos a nuestro usuario y ya aparecer谩 en la secci贸n Members del panel principal. Seleccionamos Review + assign guardando los cambios:

Adding AcrImageSigner role to User: Applying
Adding AcrImageSigner role to User: Applying

Verificamos los permisos asignados regresando a la p谩gina inicial de Access control (IAM), seleccionamos la opci贸n View my access y se nos mostrar谩 un panel derecho con la lista de roles asignados a nuestro usuario. Revisamos que el rol AcrImageSigner se encuentre en la lista:

Adding AcrImageSigner role to User: Verifying
Adding AcrImageSigner role to User: Verifying

Con esto ya contamos con un registro de contenedores con DCT habilitado y con los permisos adecuados para realizar el firmado de im谩genes.

Para el caso de los registros de contenedores que no soporten DCT, la soluci贸n viene por desplegar un Notary Server de manera independiente. El procedimiento se sale del alcance del presente art铆culo, pero usted puede guiarse del proyecto oficial de Notary en GitHub.

鉁掞笍 El Proceso de Firmado

Antes de todo debemos iniciar sesi贸n en Azure y el registro de contenedores sobre el cual trabajaremos:

az login
az acr login --name johnrc.azurecr.io

Verificamos que estamos correctamente conectados ejecutando:

# az acr list -o table
NAME   RESOURCE  LOCATION SKU     LOGIN             CREATION             ADMIN
       GROUP                      SERVER            DATE                 ENABLED
------ --------- -------- ------- ----------------- -------------------- -------
johnrc rg_johnrc eastus   Premium johnrc.azurecr.io 2023-05-01T01:27:43Z False

馃棟锔 Generando las Delegation Keys

Lo primero que tenemos que realizar es generar una Delegation Key para la firma de las im谩genes. Para ello, ejecutamos:

docker trust key generate john

Se nos pedir谩 un password para encriptar la nueva Delegation Key generada. El comando crear谩 un par de archivos, una clave p煤blica y una clave privada. El archivo de clave privada se almacena siempre en la carpeta ~/.docker/trust/private, y el archivo de clave p煤blica en la carpeta desde la cual ejecutamos el comando:

~/files # docker trust key generate john
Generating key for john...
Enter passphrase for new john key with ID 6d6ade9:
Repeat passphrase for new john key with ID 6d6ade9:
Successfully generated and loaded private key. Corresponding public key available: /root/files/john.pub
Generating Delegation Key
Generating a Delegation Key using DCT

Podemos verificar la creaci贸n de la delegation key con el cliente de Notary, ejecutamos:

notary key list
# notary key list

ROLE    GUN    KEY ID           LOCATION
----    ---    -------------    --------
john           6d6ade9e896df    /root/.docker/trust/private

En caso de que ya contemos con un par de archivos de clave p煤blica y privada que queramos reutilizar (creadas anteriormente con OpenSSL por ejemplo), podemos cargar la clave privada con el siguiente comando:

docker trust key load private.key --name john
~/files # docker trust key load private.key --name john
Loading key from "private.key"...
Enter passphrase for new john key with ID 778705c:
Repeat passphrase for new john key with ID 778705c:
Successfully imported key from private.key
Loading a Delegation Private Key from file
Loading a Delegation Private Key from file

馃攼 Creando las reglas de Delegaci贸n

Una regla de delegaci贸n permite indicar quien puede firmar im谩genes en un repositorio espec铆fico. Para ello necesitamos el nombre del usuario, el archivo de clave p煤blica generado en el paso anterior y el repositorio sobre el cual se asignar谩n los permisos.

El archivo de clave p煤blica puede estar en distintos formatos (.pub/.crt/.pem), esto depende de si generamos la delegation key con docker trust, o de si la creamos manualmente con una utilidad como OpenSSL.

docker trust signer add --key john.pub john johnrc.azurecr.io/java11-utils

En este ejemplo el nombre del usuario es john, el archivo de clave p煤blica es john.pub y el repositorio johnrc.azurecr.io/java11-utils.

 # docker trust signer add --key john.pub john johnrc.azurecr.io/java11-utils
Adding signer "john" to johnrc.azurecr.io/java11-utils...
Initializing signed repository for johnrc.azurecr.io/java11-utils...
You are about to create a new root signing key passphrase. This passphrase
will be used to protect the most sensitive key in your signing system. Please
choose a long, complex passphrase and be careful to keep the password and the
key file itself secure and backed up. It is highly recommended that you use a
password manager to generate the passphrase and keep it safe. There will be no
way to recover this key. You can find the key in your config directory.
Enter passphrase for new root key with ID 86b2ff1:
Repeat passphrase for new root key with ID 86b2ff1:
Enter passphrase for new repository key with ID a2dbb4a:
Repeat passphrase for new repository key with ID a2dbb4a:
Successfully initialized "johnrc.azurecr.io/java11-utils"
Successfully added signer: john to johnrc.azurecr.io/java11-utils

Dado que es la primera vez que creamos una delegaci贸n en el repositorio johnrc.azurecr.io/java11-utils, DCT procede a inicializarlo y para ello se crean la Root Key y Repository Key correspondientes. Se nos pedir谩n passwords para encriptar la Root Key y Repository Key. Se recomienda usar passwords complejos y diferentes as铆 como guardarlos en un lugar seguro para su uso posterior.

Verificamos las claves creadas. Podemos ver las claves ra铆z (root), de usuario (john) y de repositorio (targets):

# notary key list
ROLE     GUN                        KEY ID               LOCATION
----     ---                        ------               --------
root                                18d9dffb748b55280ee  /root/.docker/trust/private
john                                42e9f806d309c9c4828  /root/.docker/trust/private
targets  ...zurecr.io/java11-utils  5a9647e8d848082f4d1  /root/.docker/trust/private

La pr贸xima vez que creemos una delegaci贸n sobre un repositorio diferente, por ejemplo johnrc.azurecr.io/python9-utils, s贸lo se crear谩 una nueva Repository Key para dicho repositorio y se reutilizar谩 la Root Key ya creada anteriormente. Vale recordar que la Root Key se crea s贸lo una vez y las diferentes Repository Keys se generan basadas en ella.

Creating a Delegation Rule using a public key file
Creating a Delegation Rule using a public key file

鉁嶏笍 Firmando y subiendo una imagen Docker

Para realizar nuestra prueba de firmado, crearemos una imagen Docker de ejemplo en el repositorio johnrc.azurecr.io/java11-utils coloc谩ndole el tag signed:

# docker build -t johnrc.azurecr.io/java11-utils:signed .
[+] Building 25.1s (6/6) FINISHED
 => [internal] load build definition from Dockerfile_alpine_java11               0.0s
 => => transferring dockerfile: 269B                                             0.0s
 => [internal] load .dockerignore                                                0.0s
 => => transferring context: 2B                                                  0.0s
 => [internal] load metadata for docker.io/library/alpine:3.17.2                 1.2s
 => CACHED [1/2] FROM docker.io/library/alpine:3.17.2@sha256:ff6bdca1701f3a      0.0s
 => [2/2] RUN apk add git util-linux && apk add openjdk11                       22.8s
 => exporting to image 1.1s
 => => exporting layers 1.1s
 => => writing image sha256:1baba7d5e04a91b85fb4e4347aeb03ba456e4b36ef6bae       0.0s
 => => naming to johnrc.azurecr.io/java11-utils:signed
#
# docker images
REPOSITORY                       TAG       IMAGE ID       CREATED      SIZE
johnrc.azurecr.io/java11-utils   signed    1baba7d5e04a   3 hours ago   297MB

Ya estamos listos para firmar y subir nuestra nueva imagen Docker al registro de contenedor de Azure. En el momento de subir la imagen, la informaci贸n de firmado se guardar谩 en el Notary Server. Ejecutamos:

docker push --disable-content-trust=false johnrc.azurecr.io/java11-utils:signed

En este ejemplo habilitamos el firmado de la imagen con la opci贸n –disable-content-trust=false e indicamos el repositorio y tag de la imagen a firmar como johnrc.azurecr.io/java11-utils:signed

# docker push --disable-content-trust=false johnrc.azurecr.io/java11-utils:signed
The push refers to repository [johnrc.azurecr.io/java11-utils]
6cb6697bc6eb: Layer already exists
7cd52847ad77: Layer already exists
signed: digest: sha256:6ed4ea21547483944b71568e53d6f7664a6ea4b3c94d8a0a8640d1d030879b0c size: 741
Signing and pushing trust metadata
Enter passphrase for john key with ID 778705c:
Successfully signed johnrc.azurecr.io/java11-utils:signed

En este ejemplo tenemos localmente 2 im谩genes: la que acabamos de crear y subir firmada (tag signed) y otra ya existente (tag v1) que se subi贸 sin la opci贸n –disable-content-trust=false (sin firmar):

# docker images
REPOSITORY                       TAG       IMAGE ID       CREATED       SIZE
johnrc.azurecr.io/java11-utils   signed    1baba7d5e04a   3 hours ago   297MB
johnrc.azurecr.io/java11-utils   v1        8d25badcd502   3 hours ago   14.4MB

馃攳 Verificando las firmas e identificando tags firmados

El hecho de que le hayamos colocado el tag signed a una imagen no quiere decir que la imagen ya est茅 firmada. Para verificar si una imagen est谩 realmente firmada o no vamos a utilizar el siguiente comando:

docker trust inspect --pretty johnrc.azurecr.io/java11-utils:signed
# docker trust inspect --pretty johnrc.azurecr.io/java11-utils:signed

Signatures for johnrc.azurecr.io/java11-utils:signed

SIGNED TAG   DIGEST                                                         SIGNERS
signed       6ed4ea21547483944b71568e53d6f7664a6ea4b3c94d8a0a8640d1d03087   john

List of signers and their keys for johnrc.azurecr.io/java11-utils:signed

SIGNER    KEYS
john      778705c2e4f4

Administrative keys for johnrc.azurecr.io/java11-utils:signed

  Repository Key:  a62a0297649e16318b32853af85dd8bcbc7fb276da62fed6f5d875fbda8f
  Root Key:  4f719a5c838d9d22eccec72ed59c49935783fb2bb92ba289d5334bad14d5

Podemos ver que se indica qui茅n firm贸 la imagen (john), el id de la clave de delegaci贸n utilizada (778705c2e4f4), y el Repository/Root Keys utilizados.

En caso analizemos el tag v1 con el mismo comando, veremos que el resultado es diferente:

# docker trust inspect --pretty johnrc.azurecr.io/java11-utils:v1

No signatures for johnrc.azurecr.io/java11-utils:v1

List of signers and their keys for johnrc.azurecr.io/java11-utils:v1

SIGNER    KEYS
john      42e9f806d309

Administrative keys for johnrc.azurecr.io/java11-utils:v1

  Repository Key:       5a9647e8d84808d7003b4416f29f18810da9dbbd55c62a90cbf93c02f4d1
  Root Key:     368988454a765d285cd9489aec92d0a676dc797f07d94f92096bc08d0b086

El resultado indica claramente que la imagen analizada no cuenta con firma alguna.

Otra manera de verificar los tags firmados y no firmados en un repositorio Docker pero que funciona s贸lo para Azure Container Registry es ejecutando el siguiente comando de Azure CLI:

az acr repository show-tags -n johnrc.azurecr.io --repository java11-utils --detail -o table

Podemos ver en la columna Name los tags almacenados en el registro y en la columna Signed si dicho tag est谩 firmado o no:

# az acr repository show-tags -n johnrc.azurecr.io --repository java11-utils --detail -o table
CreatedTime    Digest                          LastUpdateTime       Name    Signed
-------------  ------------------------------  -------------------  ------  --------
2023-04-21T17  sha256:826dc034c5846fa3599bdec  2023-04-21T17:06:37  latest  False
2023-04-21T19  sha256:6ed4ea21547483944b71568  2023-04-21T19:25:53  signed  True

0 0 votos
Calificaci贸n del art铆culo
Suscribirse
Notificar de
guest
0 Comentarios
Inline Feedbacks
View all comments
error: Contenido protegido!
0
D茅janos tus comentarios!x
()
x