Module 4: Secrets Environments And Identity
1. Introduction: who can apply what, and with which key
Description
Module 3 left ci.yml running fmt, validate, and plan on every Pull Request, against the same LocalStack as always. If you looked at that file carefully, you already saw something this module is going to name head-on: the terraform-checks job's env: block has AWS_ACCESS_KEY_ID: test and AWS_SECRET_ACCESS_KEY: test written directly in the YAML, in plain text, inside a file already committed to Git's history. It works because they're LocalStack's dummy credentials, harmless by design. But the shape of that block —a credential written directly where anyone with access to the repository can read it— is exactly the antipattern that, according to the market audit motivating this entire guide, no competitor teaches how to avoid.
This module answers three questions, in order: why should a credential never live where it lives right now in ci.yml? What mechanism does GitHub offer to get it out of there without breaking anything? And what exists beyond "getting it out of the YAML" —OIDC federation, which completely avoids there being a long-lived key to store? By the end, you're going to have migrated Andes Cargo's real ci.yml to read its credentials from a Secret instead of having them written in the file, you're going to understand why that still isn't the complete solution a production pipeline would use against a real AWS account, and you're going to know how to name that complete solution —OIDC— even though this guide doesn't build it end to end here.
Connection to the module
Lessons 2 through 4 build the vocabulary and the why: what exact antipattern the market audit flags (lesson 2), how GitHub organizes the Secrets you've already used since Module 2 with act --secret-file (lesson 3), and what OIDC federation is conceptually, before seeing a single line of production YAML (lesson 4). Lesson 5 shows that complete YAML —the pattern a real pipeline against a real AWS account would use— labeled, from the first line, as representative: it doesn't run in this guide, with the exact technical reason. Lesson 6 does the same with GitHub Environments and human approval as a deployment control. Lessons 7 and 8 return to executed ground: you migrate ci.yml to read its credentials from a gitignored .secrets instead of having them written in the file, with real act output confirming the change breaks nothing.
This module's map: the 8 lessons
MODULE 4 — SECRETS, ENVIRONMENTS AND IDENTITY
L1 Introduction (this one) the map, the gap cited by the market
L2 Why a credential never lives the exact antipattern; what happens
in the repo when it leaks in a commit
L3 GitHub Secrets: repo vs. Settings → Secrets and variables;
environment secrets.AWS_ACCESS_KEY_ID in the YAML
L4 What OIDC federation is conceptual: short-lived JWT,
IAM trust policy
L5 The OIDC pattern in YAML, REPRESENTATIVE — act doesn't issue
named OIDC tokens (verified, cited)
L6 Environments: dev and prod required reviewers — REPRESENTATIVE,
act ignores environment: (cited)
L7 Hands-on: dummy credentials EXECUTED — .secrets migrated to
for LocalStack ci.yml, real act output
L8 Project: Andes Cargo's EXECUTED (LocalStack part) +
secrets and environments plan the real deployment document
| # | Lesson | What you practice |
|---|---|---|
| 1 | Introduction (this one) | The complete map; why this is the competition's most-cited security gap |
| 2 | Why a credential never lives in the repo | The exact antipattern; Git's history as the real problem, not the file |
| 3 | GitHub Secrets: repo vs. environment | Settings → Secrets and variables; secrets.AWS_ACCESS_KEY_ID in YAML |
| 4 | What OIDC federation is | Short-lived JWT, permissions: id-token: write, IAM trust policy |
| 5 | The OIDC pattern in YAML, named | Representative: aws-actions/configure-aws-credentials + role-to-assume, explained step by step |
| 6 | Environments: dev and prod | Representative: required reviewers as an approval control |
| 7 | Hands-on: dummy credentials for LocalStack | Executed: .secrets migrated to ci.yml, run with act |
| 8 | Project: Andes Cargo's secrets and environments plan | Executed (LocalStack) + the real deployment plan, not built |
Why this module exists: the most-cited gap
This guide's DISENO.md doesn't invent this priority — it copies it, almost verbatim, from the complete ecosystem's market audit (src/paths/aws-cloud-ecosystem/VALIDACION.md, Jul-2026, "high" evidence confidence). About this guide's specific security gap, the quote is this direct:
"No curriculum mentions OIDC authentication from GitHub Actions to AWS instead of long-lived access keys. The antipattern is still being taught."
It's not an isolated quote. That same audit points out this phrase, almost word for word, shows up twice, in evidence from two independent guides in this same ecosystem: here, and in cloud-security-and-guardrails-guide. Two different research lenses arrived, separately, at the same conclusion: the CI/CD and cloud-security training market shares the same blind spot. That's not a coincidence — it's the signal that the problem is real and that nobody else is solving it with the honesty it demands.
The reason this matters so much isn't abstract. A traditional IAM user's AWS access key (AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY) doesn't expire on its own. It can live active for years, on someone's laptop, in an .env file, in a GitHub Secret — and if it ends up exposed (an accidental commit, a log that prints it, a repository that goes from private to public without anyone remembering to rotate it), it remains valid until someone, actively, revokes it. OIDC federation —lessons 4 and 5's central topic— attacks exactly that problem at the root: instead of a key that lives forever until someone kills it, it issues a token that's already expired at birth, in minutes, every time a workflow needs it, with no key at all to store, leak, or rotate.
What you're going to build in this module, executed vs. representative
Same hard rule as the three previous guides: nothing gets labeled executed if it didn't really run. This module has two representative pieces —each with its exact technical reason, researched and cited, not assumed:
| Piece | Lesson | Status | Exact technical reason |
|---|---|---|---|
.secrets migrated to ci.yml (secrets.AWS_ACCESS_KEY_ID) | 3, 7, 8 | Executed | It's exactly the mechanism you already used in Module 2, lesson 7 — act --secret-file — now applied to the real pipeline |
| The complete OIDC pattern, production YAML | 5 | Representative | act doesn't implement OIDC token issuance (confirmed, nektos/act discussion #2029); there's also no real AWS account to federate against |
Environments with required reviewers | 6 | Representative | act completely ignores the environment: field (confirmed, nektos/act issue #1714, open, labeled confirmed/not-planned) — it runs the job anyway, without waiting for approval |
Lessons 5 and 6 don't skip the YAML out of laziness — they show it complete, line by line, because it's exactly what you'd write in a real pipeline. What they don't do is pretend act executes it when the tool itself, verified, can't.
Common mistakes
Thinking "LocalStack dummy credentials" means "the antipattern doesn't apply here" (conceptual). What happens: someone sees AWS_ACCESS_KEY_ID: test in ci.yml and concludes that, since it's not a real key, there's nothing to learn about security in this module. Why it happens: the real risk —an actual AWS key, capable of deleting production— isn't present in this lab, and it's easy to confuse "the concrete danger doesn't exist here" with "the pattern doesn't matter." How to spot it: if your reasoning is "this is just an exercise, in real life I'd never write a key in a YAML." How to fix it: lesson 2 shows, with Andes Cargo's real ci.yml, that the antipattern's shape is already in your project — the fact that the concrete credential is harmless doesn't change the problem's structure. This module teaches you to fix the shape, not just to avoid it when the content is dangerous.
Expecting this module to build OIDC end to end against real AWS (expectation-based). What happens: someone, motivated by VALIDACION's quote about the antipattern, expects to finish this module with a real IAM role, a configured identity provider, and a role-to-assume that actually works. Why it happens: it's the logical conclusion of "this guide is going to solve the market's most-cited gap" — but solving the gap conceptually and in YAML isn't the same as building it against a real account. How to spot it: if, on reaching lesson 5, you expect to run a command and see a role actually assumed. How to fix it: lesson 5 is explicit, from its first line, about why that doesn't happen here — and it points to cloud-security-and-guardrails-guide, the guide that does build OIDC end to end against a real AWS account.
Exercises
Exercise 1 — Locate the antipattern in your own ci.yml. Open the ci.yml you built in Module 3. Without looking at lesson 2 yet, identify exactly which line or lines have the shape of the antipattern VALIDACION flags, even though the content is harmless.
See solution
The terraform-checks job's env: block in ci.yml:
env:
AWS_ACCESS_KEY_ID: test
AWS_SECRET_ACCESS_KEY: test
These two lines have the exact shape of the antipattern: an AWS access credential, written in plain text, directly inside a YAML file already part of the repository's Git history. That the value is the literal word test —the dummy credential LocalStack accepts without validating— doesn't change the structure: if someone copied this same pattern into a real pipeline and wrote a genuine AWS key there, the problem would be exactly the same, just with real consequences.
Exercise 2 — Predict the difference between "getting the credential out of the YAML" and "eliminating the need for a credential." In two sentences, explain the conceptual difference between what you're going to build in lesson 3 (moving the credential to a Secret) and what you're only going to name in lesson 5 (OIDC). Why is OIDC a qualitative leap, not just "a safer place to store the same thing"?
See solution
A complete answer sounds, roughly, like this: "Moving the credential to a GitHub Secret gets the key out of the YAML file, but the key still exists — it's still a long-lived credential, stored somewhere, that someone with the right permission could leak or misuse. OIDC is different: there's no long-lived key to store anywhere — the pipeline receives, at the exact moment it runs, a token GitHub signs and that expires in minutes, and that token is the only thing that exists to authenticate that specific run." The difference is between "hiding the same key better" and "no longer needing a key that can get lost."
Exercise 3 — Name this module's two representative pieces, from memory. Without looking back at this lesson, name the two things this module labels representative, which lesson each one appears in, and each one's exact technical reason.
See solution
Lesson 5, the complete OIDC pattern: representative because act doesn't implement OIDC token issuance (confirmed against nektos/act discussion #2029), and because this guide has no real AWS account to federate against. Lesson 6, Environments with required reviewers: representative because act completely ignores a job's environment: field (confirmed against nektos/act issue #1714, open), so there's no way to demonstrate the approval block under act — only to describe it with the real click-path.
Summary and next step
In this lesson you saw this module's 8-lesson map, and the exact quote from the market audit justifying why it exists: the antipattern of long-lived credentials instead of federated OIDC is the competition's most-cited security gap, flagged twice, by two independent guides in this ecosystem. You also saw, in your own ci.yml, exactly where that antipattern lives today, in its most harmless possible form.
Before moving on you should be able to: cite, from memory, VALIDACION's exact phrase about the OIDC antipattern; point to ci.yml's exact line carrying its shape; and name this module's two representative pieces with their technical reason.
Lesson 2 stops on the question this entire module answers: what exactly happens when a credential leaks in a commit? The answer isn't "the file gets deleted" — it's much more uncomfortable than that.
Resources
src/paths/aws-cloud-ecosystem/VALIDACION.md(NIEVA, market audit, Jul-2026) — the source of the quote about the OIDC antipattern, cited twice by independent evidence.- GitHub Docs — Security hardening for GitHub Actions — the official index of security practices this module develops, including the OIDC section.
- nektos/act discussion #2029 — the technical confirmation, by a project maintainer, that
actdoesn't implement OIDC token issuance. Cited in depth in lesson 5. - nektos/act issue #1714 — the technical confirmation, open and labeled
confirmed/not-planned, thatactignores theenvironment:field. Cited in depth in lesson 6.