Módulo 4: Secrets Environments And Identity
3. GitHub Secrets: de repositorio y de ambiente
Descripción
act --secret-file .secrets, el mecanismo que usaste desde el Módulo 2, existe porque simula algo que GitHub ya resuelve de forma nativa en un repositorio real: Secrets, valores gestionados desde la interfaz web de GitHub, nunca escritos en un archivo que Git rastree, inyectados en un workflow únicamente en el momento en que corre. Esta lección te muestra dónde vive ese mecanismo en github.com —el camino de clics exacto—, cómo se referencia dentro de un workflow con la sintaxis secrets.<NOMBRE>, y la distinción entre un Secret de repositorio (disponible para cualquier workflow del repo) y un Secret de ambiente (disponible solo cuando un job declara ese environment: específico) — el adelanto directo de lo que la lección 6 construye a fondo.
Conexión con el módulo
La lección 2 explicó por qué una credencial nunca debería vivir en un archivo commiteado. Esta lección construye, del lado de GitHub, el lugar correcto donde sí debería vivir. Ya conoces la mitad de este mecanismo desde el Módulo 2, lección 7: act --secret-file .secrets lee un archivo local y lo expone al contexto secrets dentro del workflow, exactamente como GitHub expondría un Secret real configurado en la web. Lo que esta lección agrega es el lado de GitHub que act está simulando: dónde se configuran esos valores cuando el repositorio es real, y por qué el mismo nombre —secrets.AWS_ACCESS_KEY_ID— funciona sin cambiar una sola línea de YAML entre tu laboratorio local y un repositorio real en producción.
Analogía: el casillero de la oficina, con dos niveles de llave
Piensa en un Secret de repositorio como el casillero general de una oficina: cualquier empleado con acceso al edificio puede abrirlo, sin importar en qué proyecto esté trabajando ese día. Un Secret de ambiente es distinto: es un casillero que solo se abre cuando estás asignado específicamente al proyecto "producción" — si estás trabajando en "desarrollo", ese casillero ni siquiera aparece en tu lista de opciones, aunque tengas acceso general al edificio. La credencial de dev y la de prod pueden llamarse igual (AWS_ACCESS_KEY_ID en ambos casos) sin chocar entre sí, porque cada una vive en su propio casillero, visible solo para quien está trabajando en ese ambiente específico.
Dónde viven los Secrets en un repositorio real: el camino de clics
Esto describe la interfaz real de github.com — no hay forma de ejecutar esta navegación con act, porque es configuración de la plataforma, no un workflow. Si en algún momento creas un repositorio real en GitHub (opcional, nunca obligatorio para completar esta guía — el Módulo 8 vuelve sobre esto), este es el camino exacto:
github.com/tu-usuario/andes-cargo-infra
└── Settings (pestaña del repositorio, requiere permiso de administrador)
└── Secrets and variables (menú lateral izquierdo)
└── Actions
├── Repository secrets ← disponibles para CUALQUIER workflow del repo
│ └── [New repository secret]
│ Name: AWS_ACCESS_KEY_ID
│ Value: ●●●●●●●●●●●●●●●● (nunca visible después de guardarlo)
│
└── Environment secrets ← disponibles SOLO si el job declara ese environment
└── (requiere haber creado un Environment primero — lección 6)
Dos detalles verificados contra el comportamiento real de GitHub, importantes para lo que viene: un Secret, una vez guardado, no se puede volver a leer desde la interfaz — solo se puede sobrescribir con un valor nuevo o eliminar. GitHub ni siquiera te lo muestra a ti, el administrador que lo creó. Y los Secrets nunca aparecen en los logs de una corrida por defecto: si un workflow imprimiera accidentalmente el valor de un Secret con echo $AWS_ACCESS_KEY_ID, GitHub Actions lo detecta y lo enmascara automáticamente en el log con *** — la misma razón por la que el Módulo 2, lección 7, te enseñó a imprimir la longitud del secreto, no su valor: es el hábito correcto, aunque act en tu máquina no aplique ese enmascarado automático de la misma forma que GitHub real.
Cómo se referencia dentro de un workflow: la sintaxis secrets.<NOMBRE>
Ya usaste esta sintaxis, sin nombrarla formalmente, desde el Módulo 2, lección 7. El contexto secrets es una de las variables especiales que GitHub Actions expone dentro de la expresión ${{ }} —de la misma familia que github.event (Módulo 2, lección 6) o github.run_id—, reservado específicamente para leer el valor de un Secret configurado, sin importar si viene de la interfaz web real o, en tu caso, de act --secret-file.
El ci.yml real de Andes Cargo, antes de esta lección, tenía las credenciales dummy escritas directamente:
env:
AWS_ACCESS_KEY_ID: test
AWS_SECRET_ACCESS_KEY: test
La lección 7 de este módulo hace el cambio real, ejecutado, pero la sintaxis que vas a escribir es esta:
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
secrets.AWS_ACCESS_KEY_ID le dice a GitHub Actions —o a act, simulándolo— "busca un Secret llamado exactamente AWS_ACCESS_KEY_ID y sustituye su valor aquí, en el momento en que arranca este job". El nombre entre paréntesis del lado derecho (secrets.AWS_ACCESS_KEY_ID) tiene que coincidir, letra por letra, con el nombre que configuraste en Settings → Secrets and variables → Actions (o, en tu caso, con el nombre a la izquierda del = en .secrets) — el mismo error de sintaxis que ya te advirtió el Módulo 2, lección 7, sigue aplicando aquí sin cambios.
Repo-scoped vs. environment-scoped: el adelanto de la lección 6
Un Secret de repositorio (Repository secrets) está disponible para cualquier job de cualquier workflow de ese repositorio, sin ninguna condición adicional. Es el tipo de Secret que este módulo usa en las lecciones 7 y 8, porque ci.yml —el workflow de plan en cada PR— no necesita distinguir entre dev y prod: corre igual, sobre el mismo LocalStack, sin importar la rama.
Un Secret de ambiente (Environment secrets) solo existe dentro de un Environment con nombre —dev, prod, el nombre que quieras—, y solo se inyecta en un job que declare explícitamente environment: <nombre>. Esto permite algo que un Secret de repositorio no puede: dos valores distintos con el mismo nombre de variable, aislados el uno del otro. Un ejemplo real del propio Andes Cargo, que la lección 6 construye a fondo: el job de apply.yml que corre contra prod podría leer un AWS_ACCESS_KEY_ID completamente distinto del que lee el mismo job corriendo contra dev —sin que el YAML cambie una sola línea—, simplemente porque cada Environment resuelve el nombre secrets.AWS_ACCESS_KEY_ID contra su propio casillero.
Repository secrets Environment secrets
AWS_ACCESS_KEY_ID = clave-única environment: dev
(mismo valor para AWS_ACCESS_KEY_ID = clave-de-dev
cualquier job, cualquier environment: prod
ambiente) AWS_ACCESS_KEY_ID = clave-de-prod
(mismo NOMBRE, valores DISTINTOS,
aislados por ambiente)
Este módulo, en las lecciones 7 y 8, usa Secrets de repositorio (el equivalente de .secrets sin distinción de ambiente) porque ci.yml no necesita esa separación — pero la lección 6 muestra el mecanismo completo de Environments, incluida esta capacidad de scoping, aplicada al caso real donde sí importa: apply.yml, que si corriera contra una cuenta AWS real, sí necesitaría distinguir dev de prod.
Errores comunes
Esperar poder "leer de vuelta" un Secret ya guardado (de expectativa). Qué pasa: alguien configura un Secret en la interfaz de GitHub, y semanas después quiere confirmar su valor exacto entrando de nuevo a Settings → Secrets and variables. Por qué pasa: la mayoría de los formularios de configuración sí muestran el valor guardado al volver a abrirlos. Cómo detectarlo: si buscas un botón "mostrar valor" o similar en la pantalla de edición de un Secret, y no lo encuentras. Cómo corregirlo: es una decisión de diseño deliberada de GitHub, no una limitación — un Secret solo se puede sobrescribir o eliminar, nunca volver a leer, ni siquiera por quien lo creó. Si necesitas confirmar qué valor tiene configurado, la única forma es la que ya usaste en el Módulo 2, lección 7: un step que confirme su longitud, nunca su contenido.
Confundir el nombre de la variable de entorno con el nombre del Secret (de sintaxis, ya advertido en el Módulo 2 y repetido aquí porque sigue siendo el error más común de esta sintaxis). Qué pasa: alguien escribe env: { AWS_KEY: ${{ secrets.AWS_ACCESS_KEY_ID }} } —con un nombre distinto a la izquierda del : — y después un step usa $AWS_ACCESS_KEY_ID en vez de $AWS_KEY, esperando que funcione porque "es el mismo Secret". Cómo detectarlo: la variable de entorno sale vacía dentro del step, sin ningún error explícito de GitHub Actions ni de act. Cómo corregirlo: el nombre a la izquierda del : en env: es el nombre de la variable de entorno del sistema operativo dentro del contenedor — puede llamarse como quieras. El nombre dentro de secrets.<NOMBRE> es el nombre del Secret configurado, y tiene que coincidir exactamente con Settings → Secrets and variables (o con .secrets). Son dos espacios de nombres distintos que, por convención y para evitar justo este error, esta guía mantiene siempre iguales.
Ejercicios
Ejercicio 1 — Traza el camino de clics de memoria. Sin mirar hacia atrás en esta lección, escribe el camino de clics completo, en orden, para llegar a la pantalla donde se configura un Secret de repositorio en github.com.
Ver solución
Settings (pestaña del repositorio) → Secrets and variables (menú lateral izquierdo) → Actions → pestaña Repository secrets → botón New repository secret. Requiere permiso de administrador sobre el repositorio.
Ejercicio 2 — Decide entre Secret de repositorio y de ambiente. Para cada caso, indica cuál usarías y por qué: (a) un token de una API externa que usa el mismo valor sin importar en qué rama o ambiente corra el workflow; (b) la clave de AWS que apply.yml necesita para desplegar contra la cuenta de prod, distinta de la que usaría contra dev.
Ver solución
(a) Secret de repositorio — no hay ninguna razón para distinguir por ambiente si el valor es siempre el mismo; agregar un Environment innecesariamente solo agrega complejidad sin ningún beneficio. (b) Secret de ambiente, uno configurado dentro del Environment prod y otro distinto dentro del Environment dev — es exactamente el caso de uso que justifica que existan los Secrets de ambiente: el mismo nombre de variable (AWS_ACCESS_KEY_ID), resuelto a un valor distinto según qué environment: declare el job, sin tener que inventar nombres como AWS_ACCESS_KEY_ID_PROD y AWS_ACCESS_KEY_ID_DEV en el YAML.
Ejercicio 3 — Explica el enmascarado automático de logs. Un colega, revisando el log de una corrida real de GitHub Actions, ve AWS_ACCESS_KEY_ID: *** en vez del valor real, aunque el step solo hacía echo $AWS_ACCESS_KEY_ID. ¿Qué está pasando, y por qué esto no reemplaza el hábito de imprimir solo la longitud de un secreto, como enseñó el Módulo 2?
Ver solución
GitHub Actions detecta automáticamente cuándo el valor de un Secret configurado aparece en la salida de un log, y lo sustituye por *** antes de mostrártelo — una protección real, no una casualidad. Pero esta protección tiene límites: solo enmascara el valor exacto del Secret tal como está guardado; si un step transforma el valor de alguna forma (lo codifica en base64, lo concatena con otro texto, lo divide en partes), el enmascarado automático puede no reconocerlo. Por eso seguir el hábito de imprimir solo la longitud —nunca depender únicamente del enmascarado automático de la plataforma— sigue siendo la práctica más segura, sin importar qué tan confiable sea la protección de GitHub.
Resumen y siguiente paso
En esta lección viste dónde vive un Secret en un repositorio real de GitHub (Settings → Secrets and variables → Actions), la sintaxis exacta con la que un workflow lo referencia (secrets.<NOMBRE>, la misma que act --secret-file ya simulaba desde el Módulo 2), y la distinción entre Secrets de repositorio (un solo valor, disponible siempre) y de ambiente (valores distintos, aislados por Environment) — el adelanto directo de la lección 6.
Antes de avanzar deberías poder: trazar el camino de clics completo para configurar un Secret de repositorio; explicar por qué un Secret, una vez guardado, no se puede volver a leer; y distinguir cuándo un caso real necesita un Secret de repositorio frente a uno de ambiente.
Tienes el vocabulario y la mecánica de Secrets. La lección 4 da el siguiente paso conceptual: ¿qué pasaría si, en vez de guardar una credencial en absoluto —ni en el YAML, ni en un Secret—, el pipeline pudiera demostrar su identidad sin necesitar ninguna clave que guardar? Esa es la federación OIDC.
Recursos
- GitHub Docs — Using secrets in GitHub Actions — documentación oficial completa de Secrets, incluida la creación, el límite de tamaño, y el enmascarado automático de logs.
- GitHub Docs — Security hardening for GitHub Actions — el índice de prácticas de seguridad de la plataforma, referenciado también en la lección 1 de este módulo.
- Módulo 2, lección 7 de esta guía (
07-hands-on-passing-secrets-to-act.md) — el mecanismo deact --secret-fileque esta lección conecta con su equivalente real en GitHub.