Docker Secrets
Nadie tiene dudas de que la arquitectura de microservicios ya ha demostrado ser eficiente. Sin embargo, implementar seguridad, principalmente en un contexto de infraestructura inmutable, ha sido un gran desafío.
Con cuestiones que van desde cómo separar la contraseña del template en una imagen hasta cómo cambiar la contraseña de acceso a un servicio sin interrumpirlo, no faltan workarounds. Pero como siempre se puede mejorar, en la versión 1.13 nuestros queridos amigos de Docker lanzaron lo que se llamó Docker Secrets.
Docker Secrets es la solución de Docker para trabajar con toda la parte de secrets en un ambiente multi-node y multi-container. En otras palabras, un "swarm mode" cluster. El secret puede contener cualquier cosa, pero debe tener un tamaño máximo de 500 KB. Por ahora esta maravilla no está disponible fuera del contexto de Docker Swarm -- en realidad, no está claro si algún día será diferente. Por ahora se nos recomienda usar un service para hacer deploy de containers individuales.
14.1. El comando docker secret
El comando "docker secret" viene con algunos subcomandos. Son:
docker secret --help
Usage: docker secret COMMAND
Manage Docker secrets
Options:
--help Print usage
Commands:
create Create a secret from a file or STDIN as content
inspect Display detailed information on one or more secrets
ls List secrets
rm Remove one or more secrets
Run 'docker secret COMMAND --help' for more information on a command.
-
create -- Crea un secret a partir del contenido de un archivo o STDIN.
-
inspect -- Muestra información detallada de uno o más secrets.
-
ls -- Lista los secrets existentes.
-
rm -- Elimina uno o más secrets.
Create
Como se mencionó, el "create" acepta contenido tanto de STDIN...
root@linuxtips:~# echo 'minha secret' | docker secret create minha_secret -
jxr0pilzhtqsiqi1f1fjmmg4t
root@linuxtips:~#
...como de un archivo:
root@linuxtips:~# docker secret create minha_secret minha_secret.txt
ci7mse43i5ak378sg3qc4xt04
root@linuxtips:~#
Inspect
Quédate tranquilo, el "inspect" no muestra el contenido de tu secret! :P
En cambio, muestra una serie de informaciones sobre el secret, incluyendo su creación y modificación (aunque no hay, en realidad, forma de modificar un secret; una vez creado, no puede ser actualizado vía CLI, pero ya hay un endpoint en la API de Docker Swarm para update de secret -- "/secrets/id//update", ¡vamos a esperar!)
root@linuxtips:~# docker secret inspect minha_secret
[
{
"ID": "ci7mse43i5ak378sg3qc4xt04",
"Version": {
"Index": 808
},
"CreatedAt": "2017-07-02T17:17:18.143116694Z",
"UpdatedAt": "2017-07-02T17:17:18.143116694Z",
"Spec": {
"Name": "minha_secret",
"Labels": {}
}
}
]
root@linuxtips:~#
El "inspect" acepta más de un secret a la vez y mostrará el resultado en la misma secuencia.
ls && rm
Sirven respectivamente para listar tus secrets y eliminarlos.
root@linuxtips:~# docker secret ls
ID NAME CREATED UPDATED
ci7mse43i5ak378sg3qc4xt04 minha_secret About a minute ago About a minute ago
root@linuxtips:~#
root@linuxtips:~# docker secret rm minha_secret
minha_secret
root@linuxtips:~#
14.2. Está bien, ¿pero cómo uso esto?
Los secrets son consumidos por servicios, como ya mencionamos, y esto sucede a través de una asociación explícita, usando el flag "--secret" al momento de crear un servicio. Vamos a un ejemplo.
Primero vamos a crear un secret con la contraseña de la base de datos de nuestra aplicación fake.
root@linuxtips:~# echo 'senha_do_banco' | docker secret create db_pass -
kxzgmhlu3ytv64hbqzg30nc8u
root@linuxtips:~#
Ahora, vamos a asociarlo a nuestra app, creando un servicio.
root@linuxtips:~# docker service create --name app --detach=false --secret db_pass minha_app:1.0
npfmry3vcol61cmcql3jiljk2
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Waiting 1 seconds to verify that tasks are stable...
root@linuxtips:~# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
npfmry3vcol6 app replicated 1/1 minha_app:1.0
root@linuxtips:~# docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
65d1533f5b50 minha_app:1.0 "/bin/sh -c ./scri..." 40 seconds ago Up 39 seconds app.1.molbmj0649c7xkzfermkuwrx2
root@linuxtips:~#
También es posible dar acceso a keys para servicios ya creados, a través del flag "--secret-add" del comando "docker service update", así como revocarlas, usando el flag "--secret-rm" en el mismo comando. Ver el tema "Actualizando el secret de un servicio".
14.3. Accediendo al secret
Con el servicio creado, el secret estará disponible para todos los containers de ese service y estará en archivos dentro del directorio "/run/secrets", montado en tmpfs. Si tu secret se llama "db_pass", como en el ejemplo, el contenido estará en "/run/secrets/db_pass".
Es posible incluir algunos parámetros al momento de agregar un secret a un servicio, como, por ejemplo, el target, que cambia el nombre del archivo en el destino y hasta elementos de seguridad, como uid, gid y mode:
docker service create --detach=false --name app --secret source=db_pass,target=password,uid=2000,gid=3000,mode=0400 minha_app:1.0
Dentro del container quedaría así:
root@4dd6b9cbff1a:/app# ls -lhart /run/secrets/
total 12K
-r-------- 1 2000 3000 15 Jul 2 17:44 password
drwxr-xr-x 7 root root 4.0K Jul 2 17:44 ..
drwxr-xr-x 2 root root 4.0K Jul 2 17:44 .
root@4dd6b9cbff1a:/app#
Y entonces basta que tu aplicación lea el contenido del archivo.
root@8b16b5335334:/app# python
Python 2.7.12 (default, Nov 19 2016, 06:48:10)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> secret = open('/run/secrets/password').read()
>>>
>>> print "minha secret e: %s" % (secret)
minha secret e: nova_senha
>>>
14.4. Actualizando el secret de un servicio
Los secrets fueron creados para ser inmutables, es decir, si quieres cambiar el secret de un servicio, necesitarás crear otro secret. El cambio del secret es muy fácil. Por ejemplo, para cambiar el secret "db_pass" del ejemplo anterior, tendríamos que hacer lo siguiente:
Crear un nuevo secret:
root@linuxtips:~# echo 'nova_senha' | docker secret create db_pass_1 -
221uzilbsl1ybna7g7014hoqr
root@linuxtips:~#
Agregar el secret a la app creada anteriormente:
root@linuxtips:~# docker service update --secret-rm db_pass --detach=false --secret-add source=db_pass_1,target=password app
app
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Waiting 1 seconds to verify that tasks are stable...
root@linuxtips:~#
Después de la actualización, basta eliminar el secret antiguo:
root@linuxtips:~# docker secret rm db_pass
db_pass
root@linuxtips:~#