Build a guard rail, then try to get a bad change past it

applied · 75 min · Objective 4.7

Task

Put an automated policy check in front of a deployment so that a non-compliant configuration cannot be created, then attempt four ways around it. A guard rail is only a guard rail if it cannot be skipped, and finding out which of your four attempts works is the lab.

Steps

  1. Build the pipeline: /tmp/pipeline.sh runs a policy check and, only on success, calls a deploy step that writes the configuration into /tmp/deployed/.
  2. Write the policy check to reject three things: a secret in the configuration, an unencrypted setting, and a permission wider than the standard allows.
  3. Confirm the happy path: a compliant file deploys, a non-compliant one does not, and /tmp/deployed/ stays empty in the second case.
  4. Now attack your own guard rail, four ways: call the deploy step directly, bypassing the pipeline; pass the check a file and deploy a different one; encode the secret so the pattern does not match; and write the configuration to the target directory by hand.
  5. Record which attempts succeeded. At least two will.
  6. Close the ones you can: make the deploy step verify the check ran against the exact content it is deploying — hash it — and make the target directory writable only by the pipeline's own identity.
  7. Re-run all four attempts and record the result in /tmp/guardrail.md, noting which remain possible and what would be needed to close them.

Verify

bash /tmp/pipeline.sh /tmp/infra/good.yaml; echo "good exit: $?"
bash /tmp/pipeline.sh /tmp/infra/bad.yaml; echo "bad exit: $?"
ls /tmp/deployed/ | wc -l
python3 - <<'PY'
import subprocess,os,glob
before=set(glob.glob('/tmp/deployed/*'))
subprocess.run(['bash','/tmp/pipeline.sh','/tmp/infra/bad.yaml'],capture_output=True)
after=set(glob.glob('/tmp/deployed/*'))
print('files deployed by the bad run:',len(after-before))
assert after==before, 'the non-compliant configuration was deployed anyway'
r=subprocess.run(['bash','/tmp/deploy-step.sh','/tmp/infra/bad.yaml'],capture_output=True,text=True)
print('direct call to the deploy step exited',r.returncode)
assert r.returncode!=0, 'the deploy step can still be called directly, bypassing the check'
PY
grep -ciE "bypass|hash|identity|remains possible" /tmp/guardrail.md

The second assertion is the one that matters: calling the deploy step directly must fail after your fix. A policy check that only runs when somebody chooses to run it is a linter, and the difference between a linter and a guard rail is whether the thing being guarded can be reached another way.

Notes

The attempts that remain possible are the honest output. A determined engineer with write access to the target can always place a file there, and the control that closes that is not in the pipeline at all — it is the permissions on the destination, and ultimately the audit that notices a configuration nobody deployed.

This is an independent study companion for CompTIA Security+ SY0-701 and is not produced by or endorsed by CompTIA.