Dominando las Variables de Entorno en Docker: Configuración vs. Secretos
Docker ha revolucionado la forma en que se construyen, distribuyen y ejecutan las aplicaciones, proporcionando un entorno consistente en varias etapas del desarrollo. Un aspecto fundamental de la gestión de aplicaciones contenidas es su configuración, y las variables de entorno son un mecanismo principal para esto. Sin embargo, no todos los datos son iguales; parte de la configuración es inofensiva, mientras que otra información, como las claves de API o las credenciales de bases de datos, es altamente sensible. Confundir estas dos categorías puede conducir a vulnerabilidades de seguridad significativas.
Este artículo profundiza en la distinción crítica entre usar variables de entorno para la configuración general y los métodos apropiados y seguros para gestionar datos sensibles, comúnmente denominados "secretos". Exploraremos las diversas formas de pasar la configuración a sus contenedores Docker, destacaremos los riesgos inherentes de tratar los secretos como variables de entorno comunes e introduciremos las soluciones dedicadas de Docker para la gestión segura de secretos. Al final, tendrá una comprensión clara de cuándo y cómo usar cada enfoque, asegurando que sus aplicaciones sean tanto flexibles como seguras.
Entendiendo las Variables de Entorno para la Configuración
Las variables de entorno son un método sencillo y ampliamente adoptado para pasar la configuración de tiempo de ejecución a las aplicaciones, incluidas las que se ejecutan en contenedores Docker. Permiten modificar el comportamiento de una aplicación sin necesidad de reconstruir la imagen de Docker, haciendo que sus contenedores sean más flexibles y portátiles. Esto es ideal para configuraciones dinámicas no sensibles, como los números de puerto de la aplicación, las banderas de depuración o las URL de servicios de terceros.
Métodos para Pasar Variables de Configuración
Docker proporciona varias formas de definir e inyectar variables de entorno en sus contenedores:
1. Instrucción ENV en Dockerfile
La instrucción ENV establece una variable de entorno predeterminada que estará disponible dentro del contenedor cuando se ejecute. Esto es adecuado para variables que es poco probable que cambien o que proporcionan valores predeterminados sensatos para su aplicación.
FROM alpine:latest
ENV APP_PORT=8080
ENV DEBUG_MODE=false
COPY ./app /app
WORKDIR /app
CMD ["/app/start.sh"]
Consejo: Aunque ENV establece valores predeterminados, estos pueden anularse en tiempo de ejecución.
2. Bandera -e o --env con docker run
Al iniciar un solo contenedor, puede usar la bandera -e o --env para pasar variables de entorno directamente. Esto es común para pruebas ad-hoc o para proporcionar configuraciones específicas que difieren de los valores predeterminados de Dockerfile.
docker run -d -p 80:8080 --name my_app_instance \n -e APP_PORT=80 \n -e DEBUG_MODE=true \n my_app_image:latest
3. env_file en Docker Compose
Para gestionar múltiples variables de entorno, especialmente en varios servicios definidos en un archivo docker-compose.yml, la opción env_file es muy conveniente. Permite cargar variables desde uno o más archivos .env, manteniendo su docker-compose.yml más limpio.
docker-compose.yml:
version: '3.8'
services:
webapp:
image: my_app_image:latest
ports:
- "80:8080"
env_file:
- ./config/app.env
./config/app.env:
APP_PORT=8080
DEBUG_MODE=false
API_ENDPOINT=https://api.example.com/v1
4. Clave environment en Docker Compose
Alternativamente, puede definir variables de entorno directamente dentro de la sección environment de un servicio en docker-compose.yml. Esto a menudo se prefiere para un número pequeño de variables o para variables que son específicas de un único servicio.
version: '3.8'
services:
webapp:
image: my_app_image:latest
ports:
- "80:8080"
environment:
APP_PORT: 8080
DEBUG_MODE: false
Los Peligros de Usar Variables de Entorno para Secretos
Si bien las variables de entorno son excelentes para la configuración, fundamentalmente no son seguras para gestionar datos sensibles (secretos) como contraseñas de bases de datos, claves de API o claves SSH privadas. Esta es una vulnerabilidad de seguridad crítica que a menudo se pasa por alto, especialmente en entornos de desarrollo.
Por Qué las Variables de Entorno No Son Seguras para Secretos:
-
Visibilidad a través de
docker inspect: Cualquiera con acceso al host de Docker puede ver fácilmente las variables de entorno de un contenedor en ejecución usandodocker inspect <container_id>. Esto significa que sus secretos son claramente visibles en texto plano.```bash
Ejemplo de exposición de un secreto (NO HAGA ESTO EN PRODUCCIÓN)
docker run -d -e DB_PASSWORD=mysecretpassword --name insecure_app nginx:latest
Cualquiera puede ver la contraseña
docker inspect insecure_app | grep DB_PASSWORD
``` -
Inspección de Procesos: Dentro del contenedor, otros procesos o usuarios (si existen múltiples usuarios) podrían leer variables de entorno, especialmente si la aplicación se ejecuta como root o tiene privilegios elevados.
-
Registro e Historial: Las variables de entorno pueden terminar inadvertidamente en registros, historial de canalizaciones de CI/CD o historial de shell, lo que lleva a una exposición accidental.
-
Capas de Imagen: Si utiliza
ENVen un Dockerfile con un secreto, ese secreto queda incrustado en una capa de imagen y permanece allí, incluso si intenta anularlo (unset) en una capa posterior. Esto hace que el secreto sea recuperable desde la propia imagen. -
Compartición Accidental: Los archivos
.envodocker-compose.ymlque contienen secretos a menudo se incluyen en sistemas de control de versiones o se comparten de manera inapropiada, lo que conduce a una exposición generalizada.
Advertencia: Tratar la información sensible como variables de entorno normales es un error de seguridad común. Siempre asuma que las variables de entorno son visibles públicamente en el host y dentro del contenedor.
Gestión Segura de Secretos en Docker
Para abordar las deficiencias de seguridad de las variables de entorno para datos sensibles, Docker proporciona capacidades dedicadas de gestión de secretos, principalmente a través de Docker Secrets (para Docker Swarm) y herramientas externas como Docker Compose con funcionalidad secrets (que puede aprovechar los secretos de Docker Swarm o simplemente montar archivos).
Docker Secrets (Modo Swarm de Docker)
Docker Secrets es una característica integrada con el modo Swarm de Docker que proporciona una forma segura de transmitir y almacenar datos sensibles para los servicios. Los secretos son:
- Cifrados en reposo en los registros Raft del administrador de Swarm.
- Transmitidos de forma segura a las tareas de servicio autorizadas.
- Montados como archivos en memoria dentro del sistema de archivos del contenedor, generalmente en
/run/secrets/<nombre_secreto>, en lugar de exponerse como variables de entorno. - Accesibles solo por los servicios a los que se les concede explícitamente el acceso.
Cómo Usar Docker Secrets (Modo Swarm)
-
Inicializar Swarm (si aún no está hecho):
bash docker swarm init -
Crear un Secreto: Los secretos se crean a partir de un archivo o entrada estándar.
bash echo "my_secure_db_password" | docker secret create db_password_secret - echo "SG.your_api_key_here" | docker secret create sendgrid_api_key - -
Implementar un Servicio con el Secreto: Los servicios hacen referencia a los secretos por nombre. Docker monta el secreto en el contenedor.
bash docker service create --name my-webapp \n --secret db_password_secret \n --secret sendgrid_api_key \n my_app_image:latest -
Acceder a los Secretos en el Contenedor: Las aplicaciones leen el secreto desde la ruta del archivo montado.
```python
En el código de su aplicación Python (o similar para otros lenguajes)
with open('/run/secrets/db_password_secret', 'r') as f:
db_password = f.read().strip()
with open('/run/secrets/sendgrid_api_key', 'r') as f:
sendgrid_key = f.read().strip()
```
Docker Compose y Secretos (para host único o Swarm)
Docker Compose versión 3.1 y superior introdujo una sección secrets, que le permite definir y hacer referencia a secretos dentro de su docker-compose.yml. Cuando se ejecuta en modo Swarm, Compose aprovecha los secretos nativos de Docker Swarm. Cuando se ejecuta en un solo host sin modo Swarm, Compose sigue admitiendo secretos montando archivos desde el host al contenedor de forma segura, aunque sin el cifrado en reposo proporcionado por Swarm.
Uso de secrets en docker-compose.yml
-
Definir Secretos: Puede definir secretos haciendo referencia a un archivo externo o convirtiéndolo en un secreto externo (secreto de Swarm preexistente).
```yaml
docker-compose.yml
version: '3.8'
services:
webapp:
image: my_app_image:latest
ports:
- "80:8080"
secrets:
- db_password
- sendgrid_api_keysecrets:
db_password:
file: ./secrets/db_password.txt # Ruta a un archivo en el host que contiene la contraseña
sendgrid_api_key:
external: true # Se refiere a un secreto de Docker Swarm preexistente llamado 'sendgrid_api_key'
``` -
Crear Archivos de Secreto Locales (si se usa
file):
bash mkdir secrets echo "my_local_db_password" > ./secrets/db_password.txt -
Implementar con Compose:
docker compose up -dimplementará sus servicios, haciendo que los secretos estén disponibles en/run/secrets/<nombre_secreto>dentro de los contenedores.```bash
Dentro del contenedor, el contenido de ./secrets/db_password.txt estará en:
/run/secrets/db_password
```
Elegir la Herramienta Adecuada: Configuración vs. Secretos
La decisión sobre si usar una variable de entorno para la configuración o una solución dedicada de gestión de secretos se reduce a una pregunta principal:
¿Son sensibles los datos?
- Si la respuesta es Sí (datos sensibles): Utilice Docker Secrets (con Swarm) o un sistema similar de gestión de secretos (ejemplo: Kubernetes Secrets, HashiCorp Vault). Para configuraciones de Compose en un solo host, utilice la sección
secretspara montar archivos de forma segura. - Si la respuesta es No (configuración no sensible): Utilice variables de entorno (a través de
ENVen Dockerfile, bandera-e,env_fileoenvironmenten Compose).
| Característica | Variables de Entorno (para config) | Docker Secrets (para datos sensibles) |
|---|---|---|
| Propósito | Configuración de aplicación no sensible | Datos sensibles (contraseñas, claves de API) |
| Visibilidad | Visible a través de docker inspect, ps -e |
Montados como archivos; no en docker inspect |
| Seguridad | Inseguro para datos sensibles | Transmisión y almacenamiento cifrados y seguros |
| Acceso en la App | Leer desde os.environ (o similar) |
Leer desde el archivo /run/secrets/<nombre_secreto> |
| Casos de Uso | Números de puerto, banderas de depuración, URL no sensibles | Contraseñas de bases de datos, tokens de API, claves privadas |
Mejores Prácticas para Ambos
Para Configuración (Variables de Entorno):
- Proporcione valores predeterminados sensatos en su Dockerfile usando
ENV. Esto hace que sus imágenes se puedan ejecutar listas para usar y documenta claramente las variables esperadas. - Externalice la configuración siempre que sea posible. Utilice archivos
.envcondocker composeo servicios de configuración externos para implementaciones más grandes. - Documente todas las opciones de configuración y sus valores esperados, quizás en un
README.mdo documentación de la aplicación. - Evite codificar valores que puedan cambiar entre entornos (desarrollo, staging, producción).
Para Secretos (Docker Secrets y más allá):
- Nunca incluya secretos (ejemplo: archivos
.envque contienen secretos,db_password.txt) en sistemas de control de versiones como Git. - Cambié los secretos periódicamente. Esto minimiza la ventana de exposición si se ve comprometido un secreto.
- Conceda el mínimo privilegio. Solo dé acceso a los servicios a los secretos que absolutamente necesitan.
- Evite registrar valores secretos. Asegúrese de que el registro de su aplicación y su infraestructura no muestre el contenido secreto.
- Para implementaciones empresariales a gran escala, considere soluciones dedicadas de gestión de secretos como HashiCorp Vault, AWS Secrets Manager o Azure Key Vault, que ofrecen características más avanzadas como auditoría, generación de secretos dinámicos e integración con Gestión de Identidades y Accesos (IAM).
Conclusión
Dominar las variables de entorno en Docker significa más que solo saber cómo pasarlas; significa comprender la diferencia fundamental entre la configuración genérica y los secretos sensibles. Si bien las variables de entorno ofrecen una flexibilidad sin precedentes para la configuración de la aplicación, son inherentemente inseguras para los datos sensibles.
Al aprovechar Docker Secrets para información sensible dentro de un entorno Swarm, o al usar cuidadosamente la función secrets de Docker Compose para implementaciones de un solo host, puede mejorar significativamente la postura de seguridad de sus aplicaciones contenidas. Siempre priorice la seguridad utilizando la herramienta adecuada para el trabajo, adhiriéndose a las mejores prácticas y asegurándose de que sus datos sensibles permanezcan protegidos contra la exposición accidental. Este enfoque disciplinado conducirá a implementaciones de Docker más robustas, mantenibles y seguras.