Chapter 35Lesson 05~220 minutes

Checkpoint Lab — Docker Socket Security, Docker-in-Docker, Socket Mounting, Build Services, and CI Isolation Tradeoffs

Produce a CI isolation decision record and run a disposable two-job build isolation exercise that proves scoped builder/cache/output identities, then document the stronger boundary required for truly untrusted repositories.

CheckpointCI decision recordBuilder isolationEvidence packetChapter 36 bridge

Learning objectives

  • Create a CI isolation decision record for trusted and untrusted repository workflows.
  • Implement a disposable two-job build-state isolation exercise without handing either simulated job a Docker socket.
  • Verify exact builder identities, separate output roots and separate cache accounting.
  • Document what the simulation proves and what requires a stronger per-job/remote/rootless daemon boundary.
  • Produce an evidence packet and exact cleanup record suitable for operational review.

1. Checkpoint scope and honest claim

The checkpoint has two deliverables:

  1. a decision record that selects a CI architecture for trusted and untrusted repositories; and
  2. a local isolation exercise using two Buildx builder instances provisioned by a trusted controller.

The local exercise proves builder/cache/output scoping. It does not falsely claim that a job with host Engine API access is isolated. Your decision record must state that truly untrusted repository code should receive an ephemeral/isolated worker or a narrow rootless/remote builder endpoint instead of the host Docker socket.

2. Create checkpoint workspace and capture assumptions

set -eu
LAB=dca35-checkpoint
EVIDENCE="$LAB/evidence"
mkdir -p "$LAB/src" "$LAB/out-trusted" "$LAB/out-untrusted" "$EVIDENCE"

date -u +%Y-%m-%dT%H:%M:%SZ | tee "$EVIDENCE/time-start.txt"
docker context show | tee "$EVIDENCE/context.txt"
docker version | tee "$EVIDENCE/docker-version.txt"
docker info | tee "$EVIDENCE/docker-info.txt"
docker compose version | tee "$EVIDENCE/compose-version.txt" || true
docker buildx version | tee "$EVIDENCE/buildx-version.txt"
docker buildx ls | tee "$EVIDENCE/builders-before.txt"

3. Write the CI isolation decision record before building

# dca35-checkpoint/decision-record.txt
Trusted release repository/job:
  trust basis = protected branch/tag + reviewed workflow
  required operation = build + push/sign in later release stage
  proposed worker = dedicated or ephemeral trusted builder
  host Docker socket = not required by repository code
  credentials = short-lived scoped release credentials

Untrusted PR/fork job:
  trust basis = attacker-controlled code is possible
  required operation = build/test only
  proposed worker = ephemeral hosted runner OR isolated rootless/remote BuildKit
  host Docker socket = prohibited
  privileged nested Docker = avoid by default
  credentials = no release secrets; minimal fetch token only if required

Shared policy:
  cleanup = exact job/builder identity only
  cache = per trust domain / authenticated export
  evidence = source revision + builder identity + logs + digest/output

4. Predict state changes before executing them

Prediction 1:
  dca35-trusted and dca35-untrusted will resolve to two distinct BuildKit workers.
Prediction 2:
  each builder will report its own cache accounting after one build.
Prediction 3:
  outputs will be exported only to their own local directories and will contain different job markers.
Prediction 4:
  cleanup will remove only the two named builders; no image/container/volume prune is needed.

5. Create deterministic source

cat > dca35-checkpoint/src/Dockerfile <<'EOF'
# syntax=docker/dockerfile:1
FROM alpine:3.22
ARG JOB_CLASS
RUN printf 'job-class=%s
' "$JOB_CLASS" > /result.txt
FROM scratch
COPY --from=0 /result.txt /result.txt
EOF

sha256sum dca35-checkpoint/src/Dockerfile   | tee "$EVIDENCE/dockerfile.sha256"

6. Provision two builder identities from the trusted controller

docker buildx create --name dca35-trusted   --driver docker-container --bootstrap

docker buildx create --name dca35-untrusted   --driver docker-container --bootstrap

docker buildx inspect dca35-trusted   | tee "$EVIDENCE/trusted-builder.txt"
docker buildx inspect dca35-untrusted   | tee "$EVIDENCE/untrusted-builder.txt"

These builders are disposable lab workers. In a real untrusted CI design, provisioning should be performed outside repository-controlled code, and the job should receive only its assigned builder endpoint.

7. Run the two builds with no job socket mount

docker buildx build \
  --builder dca35-trusted \
  --build-arg JOB_CLASS=trusted \
  --progress=plain \
  --output type=local,dest=dca35-checkpoint/out-trusted   dca35-checkpoint/src   | tee "$EVIDENCE/build-trusted.log"

docker buildx build \
  --builder dca35-untrusted \
  --build-arg JOB_CLASS=untrusted \
  --progress=plain \
  --output type=local,dest=dca35-checkpoint/out-untrusted   dca35-checkpoint/src   | tee "$EVIDENCE/build-untrusted.log"

8. Verify output separation

cat dca35-checkpoint/out-trusted/result.txt   | tee "$EVIDENCE/output-trusted.txt"
cat dca35-checkpoint/out-untrusted/result.txt   | tee "$EVIDENCE/output-untrusted.txt"

test "$(cat dca35-checkpoint/out-trusted/result.txt)" = 'job-class=trusted'
test "$(cat dca35-checkpoint/out-untrusted/result.txt)" = 'job-class=untrusted'

9. Verify builder and cache separation independently

docker buildx inspect dca35-trusted   | tee "$EVIDENCE/trusted-builder-after.txt"
docker buildx inspect dca35-untrusted   | tee "$EVIDENCE/untrusted-builder-after.txt"

docker buildx du --builder dca35-trusted   | tee "$EVIDENCE/trusted-cache.txt"
docker buildx du --builder dca35-untrusted   | tee "$EVIDENCE/untrusted-cache.txt"

Each report is queried against a different builder identity. This demonstrates separate BuildKit state namespaces in the lab. Because the trusted controller can query both builders, the evidence also demonstrates why the controller itself is outside the simulated job boundary.

10. Credential-isolation proof by absence

No registry login, cloud credential, SSH key, BuildKit secret, signing key, or Docker config directory is injected into either build. Record that absence as part of the checkpoint. In a production release workflow, credential evidence should state provider, scope, TTL, recipient worker and revocation event—never the secret value itself.

grep -R -nE '(BEGIN .*PRIVATE KEY|password=|token=|secret=)'   dca35-checkpoint/src dca35-checkpoint/out-trusted dca35-checkpoint/out-untrusted   > "$EVIDENCE/credential-pattern-scan.txt" || true
cat "$EVIDENCE/credential-pattern-scan.txt"

11. Stronger production proof required for untrusted repositories

For an actual untrusted repository, add evidence from the isolation mechanism you chose:

  • Ephemeral VM/hosted runner: worker instance identity and destruction event after the job.
  • Rootless BuildKit: non-root BuildKit process identity, user namespace/root, unique worker/cache root, and endpoint available only to the assigned job.
  • Remote BuildKit: mTLS/client identity, worker tenancy, cache namespace, network policy and short-lived credential scope.

The key proof is that job A cannot address job B’s control endpoint or retained credentials—not merely that job A chooses not to do so.

12. Exact cleanup

docker buildx rm dca35-trusted dca35-untrusted

docker buildx ls | tee "$EVIDENCE/builders-after-cleanup.txt"
date -u +%Y-%m-%dT%H:%M:%SZ | tee "$EVIDENCE/time-final.txt"

Keep the checkpoint directory until review is complete. Then remove only dca35-checkpoint. Do not perform system-wide Docker cleanup.

13. Required evidence packet

Evidence family Required evidence
Trust decision trusted/untrusted classification and decision record
Host/client UTC timestamps; Docker/Compose/Buildx versions; platform/context
Control plane context/daemon identity used by trusted controller; statement that jobs did not receive socket mounts
Builder identity before/after buildx inspect for both builders
Source Dockerfile checksum and human-readable Alpine base identity
Outputs trusted and untrusted result files with independent assertions
Cache builder-specific buildx du reports
Credentials statement of no credential injection in lab; pattern scan; production scope design
Cleanup exact two builder names removed; no broad prune
Limitations lab demonstrates build-state separation, not full host isolation; production untrusted jobs require inaccessible peer control endpoints

14. What Chapter 35 adds to the production operating model

You can now evaluate Docker-in-CI designs by the authority they grant rather than by whether commands happen to run inside containers. A secure operating model classifies job trust first, keeps host daemon authority in trusted infrastructure, scopes build workers/cache/credentials to that trust domain, uses ephemeral or rootless/remote build services for untrusted code, records endpoint and builder identity, and cleans only exact owned state.

Chapter 36

Docker in CI/CD: Reproducible Builds, Buildx, Registry Caching, Ephemeral Runners, and Release Promotion

Chapter 36 builds on this isolation boundary to design reproducible CI pipelines, registry-backed caches, ephemeral runners, immutable release promotion, and digest-bound handoffs.

Knowledge check

What does this checkpoint intentionally prove, and what does it not prove?

For a public pull request, what evidence is stronger than “we use a separate builder name”?

Why is credential absence useful evidence in this synthetic checkpoint?

Why are two builder-specific cache reports required?

What is the correct cleanup boundary?

Official references and version notes

Checkpoint baseline: 2026-09-22. The runnable exercise is deliberately compatible with a normal local Docker/Buildx installation and does not require a commercial CI platform, host-socket mounting into jobs, or privileged nested containers. Production untrusted-code isolation must be proven at the runner/builder endpoint boundary.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.