Chapter 23 — Cloud and container security: methods I
Lesson map
Lesson 23 published. Map, example, EX067.
flowchart TD
N0["Learning outcomes"]
N1["23.1 Method"]
N2["Lab — Lab — registry review"]
N3["23.2 Lab result"]
N4["23.3 Prioritising"]
N5["23.4 Verifying fixes"]
N6["23.5 Prevention"]
N7["Writing the review up"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7
N7 --> EX["Worked example"]
EX --> Q["Exercise EX067"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["23.1 Method"]
N2["Lab — Lab — registry review"]
N3["23.2 Lab result"]
N4["23.3 Prioritising"]
N5["23.4 Verifying fixes"]
N6["23.5 Prevention"]
N7["Writing the review up"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7 --> Q["EX067"]
[Learning outcomes]
|
v
[23.1 Method]
|
v
[Lab — Lab — registry review]
|
v
[23.2 Lab result]
|
v
[23.3 Prioritising]
|
v
[23.4 Verifying fixes]
|
v
[23.5 Prevention]
|
v
[Writing the review up]
|
v
[Worked example] --> [Exercise EX067]
Learning outcomes
You will (1) review registry configuration systematically; (2) check anonymous access, image age and scanning; (3) prioritise fixes; (4) verify results.
23.1 Method
For each repository: is anonymous pull enabled? How old is the newest image? Has it ever been scanned? Who owns it?
Each question links to a risk from the opening case or later chapters. Anonymous pull exposes images; old images are likely unpatched; unscanned images may carry anything; and a repository without an owner has nobody to fix its problems. Asking the same four questions of every repository makes the review repeatable and comparable between rounds. The answers go into a table, one row per repository.
Lab — Lab — registry review
"""Review synthetic container registry repositories: anonymous pull, image age, scan status (stdlib only)."""
repos = [("studyhub/web", False, 12, "scanned"), ("studyhub/records-api", True, 30, "scanned"),
("studyhub/legacy-import", True, 540, "never"), ("tools/base-python", False, 200, "scanned")]
for name, anon, age, scan in repos:
f = [x for x, bad in (("anonymous pull enabled", anon), (f"newest image {age} d old", age > 180), ("never scanned", scan == "never")) if bad]
print(f"{name:24} {'; '.join(f) or 'ok'}")
Run with python3 lab_registry.py. Verified output:
studyhub/web ok
studyhub/records-api anonymous pull enabled
studyhub/legacy-import anonymous pull enabled; newest image 540 d old; never scanned
tools/base-python newest image 200 d old
23.2 Lab result
studyhub/web is ok. studyhub/records-api has anonymous pull enabled. studyhub/legacy-import has anonymous pull enabled, its newest image is 540 days old and it was never scanned. tools/base-python's newest image is 200 days old, which matters because many services build on it.
The four results match the opening case: records-api's anonymous pull is the setting the internal review found. legacy-import adds three problems at once and was not in the original findings, which shows the value of reviewing every repository rather than only the ones already known to be risky.
23.3 Prioritising
Anonymous pull is fixed first because it exposes images to anyone. The stale base image is next because its age spreads to dependent images. legacy-import is checked for use; unused repositories are archived.
Ordering by reach and ease of exploitation puts anonymous pull first, since an outsider needs nothing to exploit it. The stale base comes next because its age is inherited by every image built on it. legacy-import's problems may vanish entirely if it is unused, so checking before fixing saves effort.
23.4 Verifying fixes
After changes, an anonymous pull test from outside the network must be refused, and the review is rerun.
Testing from outside the network matters because a test from inside might succeed through internal access and look like the setting still works. The refused request is the evidence that the fix took effect. Samuel records the time, the source and the response in his evidence log for each repository he tests. Rerunning the review on fresh exports then confirms that the settings and the behaviour agree. If they ever disagree, for instance a setting showing private while an anonymous pull succeeds, that disagreement is itself a serious finding.
23.5 Prevention
Registry settings are defined in infrastructure code with private access by default; the organisation guardrail rejects public registry settings.
With private access as the default in code, a new repository is private unless someone deliberately changes it and a reviewer approves. The guardrail adds a second layer that blocks public settings even if the code is bypassed. Together they make the opening case's mistake much harder to repeat. The repository definitions also require an owner field, so a repository cannot be created without someone responsible for it, which addresses the fourth question of the method directly and keeps future reviews short.
Writing the review up
For each finding, the review records repository, setting, evidence (an export line or test result), owner and recommended action. Owners receive their findings directly with a deadline. Samuel follows up after a week and records the outcome. The written record lets the team show auditors that the registry was reviewed and fixed, and becomes the template for later reviews of other services.
Repeat schedule
The review repeats quarterly, with results compared to the previous round.
Worked example
Checking legacy-import shows it was last used by a migration finished a year earlier. The repository is archived and deleted after confirming with its owner, removing an unscanned, publicly pullable image entirely.
Synthetic case. Repositories are synthetic.
Errors and limits
Disabling anonymous access can break external consumers; check who pulls before changing.
Exercises
- EX067 — Lab findings. What did the lab find for legacy-import? Rubric: 3 = anonymous, 540 d, never scanned; 2 = two; 1 = one; 0 = none.
- EX068 — Priority. Why fix anonymous pull first? Rubric: 3 = exposes images to anyone; 2 = partial; 1 = vague; 0 = none.
- EX069 — legacy-import outcome. What happened to legacy-import? Rubric: 3 = archived and deleted; 2 = partial; 1 = vague; 0 = none.
Synthesis
Review registries systematically and fix exposure first.
Continue studying — chapter 23 of 40
← Chapter 22 (draft) · Course chapter index · Chapter 24 (draft) →