Gestión de configuración y secretos en GitHub Actions: un enfoque sistemático
Publicado el 18.02.2026
Cuando un proyecto sale del ámbito del desarrollo local y comienza a usar CI/CD, surge una pregunta fundamental: ¿cómo transmitir de forma segura los parámetros de configuración a los pipelines de build y despliegue? No se trata solo de contraseñas de bases de datos, sino también de tokens de registros de contenedores, claves SSH, claves de firma JWT, cadenas de conexión a colas de mensajes y cualquier otro parámetro que no deba almacenarse en texto claro.
La inclusión de datos sensibles en un repositorio Git no es simplemente una mala práctica. El historial de commits se guarda para siempre. Incluso si el secreto se elimina en un commit posterior, ya forma parte del historial y puede extraerse con git log, git show o al clonar un fork. En entornos corporativos esto se convierte en un riesgo real durante auditorías, crecimiento del equipo o si hay una filtración de acceso al repositorio.
Para resolver este problema, GitHub proporciona mecanismos integrados de gestión de configuración en GitHub Actions: Secrets y Variables.
Diferencia arquitectónica: Secrets y Variables
La separación de datos en dos tipos no es una formalidad, sino un elemento de la arquitectura de seguridad.
Secrets
Diseñados para almacenar datos sensibles:
MYSQL_PASSWORDREDIS_PASSWORDJWT_PRIVATE_KEYSSH_PRIVATE_KEYGHCR_TOKENAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY
La propiedad principal de los secretos es que GitHub enmascara automáticamente sus valores en los logs de ejecución. Si el valor de un secreto aparece en stdout, será reemplazado por ***. Esto reduce significativamente el riesgo de filtración accidental a través de la salida de comandos.
Variables
Se usan para parámetros de configuración no sensibles:
APP_ENVDOMAINMYSQL_HOSTREDIS_PORTDOCKER_IMAGE_NAMEDEPLOY_PATH
Las variables no se enmascaran en los logs. Permanecen legibles, lo que facilita el diagnóstico de errores y la depuración.
Niveles de almacenamiento: organización, repositorio, entorno
GitHub proporciona tres niveles para almacenar parámetros:
- Organization
- Repository
- Environment
Nivel de organización
Apropiado para tokens usados en muchos servicios. Por ejemplo, si un GHCR Personal Access Token se utiliza en varios microservicios, tiene sentido almacenarlo de forma centralizada.
Nivel de repositorio
La opción más común. Se accede a través de:
Settings → Secrets and variables → Actions
Allí los datos están separados en las pestañas Secrets y Variables.
Environment
Permite:
- almacenar diferentes conjuntos de secretos para
stagingyproduction; - restringir el acceso al entorno;
- configurar aprobaciones obligatorias para despliegues;
- establecer branch protection para un entorno específico.
Esto es especialmente importante para la infraestructura de producción.
Uso en el workflow
Dentro del workflow, el acceso a los datos se realiza a través de distintos contextos:
${{ secrets.NAME }}${{ vars.NAME }}
Ejemplo:
env:
DB_PASSWORD: ${{ secrets.MYSQL_PASSWORD }}
DB_HOST: ${{ vars.MYSQL_HOST }}
DB_PORT: ${{ vars.MYSQL_PORT }}
La sustitución se realiza a nivel del GitHub Expression Engine antes de enviar el job al runner.
Autenticación en el registro de contenedores
Un escenario típico es la autenticación en GHCR o Docker Hub:
- name: Iniciar sesión en GHCR
run: |
echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io \
-u "${{ github.actor }}" \
--password-stdin
El uso de --password-stdin es preferible a pasar el token como argumento, ya que los argumentos de proceso pueden ser visibles mediante ps en un runner self-hosted.
Generación dinámica de .env
Almacenar el archivo .env en el repositorio es un error arquitectónico. Es más seguro generarlo dinámicamente:
- name: Crear .env
run: |
cat > .env <<EOF
APP_ENV=${{ vars.APP_ENV }}
DB_URL=mysql://${{ vars.MYSQL_USER }}:${{ secrets.MYSQL_PASSWORD }}@${{ vars.MYSQL_HOST }}:3306/${{ vars.MYSQL_DB }}
CACHE_URL=redis://${{ vars.REDIS_USER }}:${{ secrets.REDIS_PASSWORD }}@${{ vars.REDIS_HOST }}:${{ vars.REDIS_PORT }}
EOF
Ventajas:
- evitar almacenar secretos en Git;
- gestión centralizada de la configuración;
- rotación de datos simplificada.
Integración con Docker Compose
services:
app:
env_file:
- .env
Uso de Environments
Ejemplo de entorno de producción:
jobs:
deploy:
environment: production
GitHub automáticamente:
- cargará los secretos de production;
- aplicará las reglas de aprobación;
- registrará el historial de despliegues.
Esto protege contra el despliegue accidental de la configuración de staging en production.
Trabajo con claves multilínea
Las claves PEM suelen causar problemas por los saltos de línea.
Codificación local:
base64 -w 0 private.pem > private.pem.b64
En el workflow:
- name: Restaurar clave privada
run: |
echo "${{ secrets.JWT_PRIVATE_KEY_B64 }}" | base64 -d > private.pem
Esto evita la corrupción de la estructura de la clave.
Seguridad del self-hosted runner
Si se usa un self-hosted runner:
- los secretos se le transmiten en texto claro;
- el runner debe estar aislado;
- no se recomienda ejecutarlo en servidores compartidos;
- es necesario restringir el acceso root;
- es recomendable usar runners efímeros.
De lo contrario, CI/CD se convierte en un punto de compromiso de toda la infraestructura.
Rotación de secretos
Actualizar una contraseña se reduce a:
- Cambiar el valor en GitHub.
- Volver a ejecutar el workflow.
No es necesario:
- editar archivos en el servidor;
- reconstruir imágenes manualmente;
- conectarse por SSH.
Errores típicos
- Usar
set -xen bash. - Ejecutar
printenven un job de producción. - Loguear variables de entorno.
- Pasar secretos en Docker build args.
- Almacenar
.enven el repositorio. - Usar el mismo token para staging y production.
Conclusión
La separación en Secrets y Variables es un pilar de la arquitectura CI/CD.
Un sistema bien diseñado permite:
- centralizar la configuración;
- minimizar el riesgo de filtraciones;
- asegurar despliegues controlados;
- facilitar auditorías;
- acelerar la rotación de credenciales;
- delimitar responsabilidades entre desarrolladores y administradores.
En una práctica DevOps madura, la configuración se considera una capa gestionada de la infraestructura. GitHub Actions proporciona todas las herramientas necesarias para construir esa capa: solo queda aplicarlas de forma sistemática y disciplinada.