(Personal lab · DevSecOps)

Azure CI/CD Breach Simulation

Attacker + Defender, written side-by-side in Terraform and GitHub Actions

Year

2025

Role

DevSecOps + Red Team

Stack

Azure, Terraform, GitHub Actions

Angle

Attacker + Defender

Goce Petrov’s personal lab: two live Azure stacks, one deliberately vulnerable and one hardened. A malicious app exfiltrates CLIENT_SECRET through the insecure pipeline. Forensic evidence and an incident response playbook sit next to the fix.

OIDC Federation · Terraform · GitHub Actions · Bandit · Forensics · Incident Response

The infrastructure, the data flow, and where each security control lives.

Numbered steps trace the hardened secure-deploy.yml flow, 1 through 7. R1 and R2 are the insecure-deploy.yml shortcuts.

  1. Git push: the developer commits to main in the GitHub repo.

  2. Trigger: the push starts secure-deploy.yml on the Actions runner.

  3. OIDC: the runner asks Entra ID for a federated token.

  4. Federated token: Entra ID exchanges it for the Managed Identity, which only gets short-lived tokens.

  5. Secret get: the secret is pulled from Key Vault, allowed by RBAC.

  6. Inject secret: the secret goes into the App Service, which runs under the Managed Identity.

  7. HTTPS: the App Service serves the app over TLS 1.3, inside a subnet behind an NSG.

R1: a static service principal secret (insecure). R2: the secret leaks to the logs through stdout.

Red path: insecure-deploy.yml

Runner uses long-lived CLIENT_SECRET → app.py prints it to stdout → lands in Log Analytics → any reader with Log Analytics access reads the secret.

Green path: secure-deploy.yml

Runner requests OIDC federated token → Entra ID exchanges for Managed Identity → pulls secret from Key Vault over RBAC → App Service runs under Managed Identity. No static secret exists.

Same Azure subscription, same services. Two credential paths, one with a standing secret and one without.

Why it matters

Pipeline breaches are not exotic. Long-lived secrets in CI, silenced scanners, and unrestricted deploy paths are the attack surface most teams actually carry.

The point is not to prove Azure can be hardened. The point is the two stacks next to each other, so the cost of the shortcut is obvious.

What this shows about me

I read attack paths, not compliance checklists.

The insecure stack works. It is not a strawman.

I write Terraform and GitHub Actions to a review bar.

Input validation, OIDC, environment gates, compliance constraints.

I capture evidence.

Forensics folder + IR playbook alongside the fix, not a commit message.

I can walk a CISO or a CTO through this in ten minutes.

Both sides on one page, no jargon, with a demo to run.

Outcome

A working demonstration of a CI/CD breach and the exact Terraform + workflow changes that stop it. Evidence lives in the repo. The incident response doc reads like a real writeup, not a checklist.

Vulnerabilities demonstrated 20+

Stacks deployed 2 (insecure + secure)

Primary fix OIDC federation

Forensic artifacts Bandit JSON + screenshots

Docs shipped COMPARISON.md + IR

Verifiable Live Azure, not localhost