Información sensible en los contenedores con Docker Secrets

Escrito por el .
planeta-codigo software-libre
Enlace permanente Comentarios

Parte de la información que usan los contenedores de Docker se debe proteger de accesos no deseados. Anteriormente en algunos casos se usaban variables de entorno para lanzar los contenedores lo que no es seguro si se listan los procesos del sistema con sus parámetros, incluir archivos en las imágenes de los contenedores tampoco es recomendable. Docker Secrets permite proporcionar y mantener segura la información sensible que usen los contenedores.

Docker

Los contenedores de Docker necesitan acceder a algunos datos sensibles desde el punto de vista de la seguridad como usuarios y contraseñas, certificados SSL, claves privadas SSH o cualquier otra información de acceso restringido. Algunos de estos datos en Docker se proporcionan mediante variables de entorno al lanzar los contenedores, esto es inseguro ya que al hacer un listado de los procesos con sus parámetros de invocación los relativos a Docker mostrarán esta información, lo que es un posible problema de seguridad.

Con Docker Secrets se puede gestionar esta información que se necesita en tiempo de ejecución pero que no se quiere almacenar en la imagen de Docker o en el repositorio de código fuente. Algunos ejemplos de información sensible son:

  • Nombres de usuario y contraseñas.
  • Certificados TLS y claves.
  • Claves SSH.
  • Otra información sensible como el nombre de una base de datos o el nombre de un servidor interno.

Los secretos de Docker se proporcionan a los contenedores que los necesitan y se transmiten de forma cifrada al nodo en el que se ejecuten. Los secretos se montan en el sistema de archivos en la ruta /run/secrets/<secret_name> de forma descifrada al que el servicio del contenedor puede acceder.

Algunos comandos para manejar los secretos son los siguientes:

  • docker secret create secreto: crea un secreto.
  • docker secret inspect secreto: muestra los detalles de un secreto.
  • docker secret ls: lista los secretos creados.
  • docker secret rm secreto: elimina un secreto.
  • Se usa el parámetro --secret para docker service create y --secret-add y --secret-rm flags para docker service update.

Usando un stack de servicios con un archivo de Docker Compose en la sección secrets de los servicios se indica cuales usa, en la sección secrets se definen los secretos de los servicios con sus nombres y su contenido referenciando archivos que pueden ser binarios o de text no superior a 500 KiB.

Al servicio de nginx la clave privada y certificado para configurar el acceso mediante el protocolo seguro HTTPS se le proporciona a través de secretos que son referenciados en el archivo de configuración del servidor web nginx.conf.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
version: "3.1"

services:
  nginx:
    image: nginx:stable-alpine
    ports:
      - "80:80"
      - "443:443"
    networks:
      - proxy
    secrets:
      - nginx.conf
      - localhost.pem
      - localhost.key
    command: sh -c "exec nginx -c /run/secrets/nginx.conf -g 'daemon off;'"
  app:
    image: localhost:5000/spring-boot-app:1.0
    ports:
      - "8080:8080"
    networks:
      - proxy
    volumes:
      - app:/data
    secrets:
      - message.txt
networks:
  proxy:
volumes:
  app:
    driver: rexray
    external: true
secrets:
  nginx.conf:
    file: ./nginx.conf
  localhost.pem:
    file: ./localhost.pem
  localhost.key:
    file: ./localhost.key
  message.txt:
    file: ./message.txt
docker-compose-stack-app.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
...

http {
    ...

    upstream app {
    	server app_app:8080;
    }

    server {
        listen       80;
        server_name  localhost;

        return 301 https://$host$request_uri;
    }

    server {
        listen       443;
        server_name  localhost;

        ssl                  on;
        ssl_certificate      /run/secrets/localhost.pem;
        ssl_certificate_key  /run/secrets/localhost.key;

        ssl_session_timeout  5m;

        ssl_protocols  SSLv2 SSLv3 TLSv1;
        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers   on;

        location / {
            proxy_pass http://app;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}
nginx.conf

Por otra parte la aplicación Java con Spring Boot lista el contenido de los secretos incorporados en el contenedor cuando se solicita en la URL https://192.168.99.100/system/info/, esto no se debe hacer porque se pierde la seguridad que proporcionan los secretos pero sirve a modo de muestra en el ejemplo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package io.github.picodotdev.blogbitix.dockerswarm;

...

@Component
public class HostInfoContributor implements InfoContributor {

    @Override
    public void contribute(Info.Builder builder) {
        Map<String, Object> details = new HashMap<>();
        try {
            ...

            File[] secrets = FileSystems.getDefault().getPath("/run/secrets/").toFile().listFiles();
            for(File file : secrets) {
                try {
                    String content = Files.lines(file.toPath()).collect(Collectors.joining("\n"));
                    details.put(file.getName().toString(), content);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        builder.withDetails(details);
    }
}
HostInfoContributor.java
1
2
{...,"message.txt":"Hello World!"}

info.json

Contenido del archivo message.txt

Contenido del archivo message.txt

Para probar el ejemplo hay que ejecutar varios comandos, la secuencia completa es la siguiente:

1
2
3
4
5
6
7
8
9
$ ./01-cluster-create.sh
$ ./02-vboxwebsrv.sh
$ ./03-rex-ray-install.sh
$ ./04-docker-compose-stack-deploy-registry.sh
$ ./05-spring-boot-app-build.sh
$ ./06-create-volumes.sh
$ ./06-docker-compose-stack-deploy-app.sh

$ curl -k https://192.168.99.100/system/info
run.sh
Terminal

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub.


Comparte el artículo: