Bandera: Русский Русский Bandera: English English

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_PASSWORD
  • REDIS_PASSWORD
  • JWT_PRIVATE_KEY
  • SSH_PRIVATE_KEY
  • GHCR_TOKEN
  • AWS_ACCESS_KEY_ID
  • AWS_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_ENV
  • DOMAIN
  • MYSQL_HOST
  • REDIS_PORT
  • DOCKER_IMAGE_NAME
  • DEPLOY_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:

  1. Organization
  2. Repository
  3. 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 staging y production;
  • 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:

  1. Cambiar el valor en GitHub.
  2. Volver a ejecutar el workflow.

No es necesario:

  • editar archivos en el servidor;
  • reconstruir imágenes manualmente;
  • conectarse por SSH.

Errores típicos

  1. Usar set -x en bash.
  2. Ejecutar printenv en un job de producción.
  3. Loguear variables de entorno.
  4. Pasar secretos en Docker build args.
  5. Almacenar .env en el repositorio.
  6. 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.

¿Necesitas ayuda?

Escríbeme y te ayudaré a resolver el problema

Publicaciones relacionadas