Checkpoint Lab — Port Publishing, NAT, Firewall Interaction, Direct Routing, Host Binding, and Exposure Security
Produce an exposure matrix for one service through internal-only, loopback-published, and all-interface-published states, then restore the safest local-only state with an evidence packet.
Learning objectives
- Predict and verify container listener, Docker publish, host bind, and client-path changes across three controlled exposure states.
- Capture an evidence packet containing versions/context, image/container identity, port mappings, host-path observations, and assumptions.
- Demonstrate the difference between internal-only service availability, loopback-only host access, and broader host-interface publication.
- Use optional read-only firewall/routing evidence without making host policy changes.
- Finish by restoring the safest local-only state and bridging the operating model to persistent data in Chapter 20.
1. Workspace, evidence directory, and preflight
mkdir -p da19-checkpoint/evidence
cd da19-checkpoint
{
date -Iseconds
docker version
docker info
docker context show
docker compose version || true
docker buildx version || true
} > evidence/platform.txt 2>&1
docker pull busybox:1.36.1
docker image inspect busybox:1.36.1 --format 'ID={{.Id}} RepoDigests={{json .RepoDigests}}' > evidence/image-identity.txt
docker ps -a --filter label=devops-academy.lab=ch19-checkpoint > evidence/preexisting-lab-resources.txt
2. Write predictions before execution
Record at least these predictions in
evidence/predictions.txt before starting containers:
-
Prediction A: internal-only service responds
inside the container but has no
docker portmapping and no host URL. -
Prediction B: loopback publication creates a
HostIp of
127.0.0.1and the host can request the service at the chosen high port. - Prediction C: all-interface publication records an unspecified/all-interface HostIp and may permit authorized peer access depending on host/network policy.
- Prediction D: after rollback to loopback-only, the broader mapping no longer exists.
cat > evidence/predictions.txt <<'EOF'
A. Internal-only: app works inside container; no host mapping exists.
B. Loopback: HostIp is 127.0.0.1; localhost request succeeds.
C. All-interface: mapping is broader; peer result depends on authorized host/network policy.
D. Final rollback: only loopback publication remains.
EOF
3. State 1 — internal-only
docker run -d \
--name da19-cp-internal \
--label devops-academy.lab=ch19-checkpoint busybox:1.36.1 sh -c 'mkdir -p /www; echo state-internal >/www/index.html; exec httpd -f -p 8080 -h /www'
docker exec da19-cp-internal wget -qO- http://127.0.0.1:8080/ | tee evidence/state1-internal-response.txt
docker port da19-cp-internal > evidence/state1-docker-port.txt
docker inspect da19-cp-internal > evidence/state1-inspect.json
Verify state1-docker-port.txt is empty. This is not a
failure: the checkpoint intentionally proves that internal service
availability does not imply host exposure.
4. State 2 — loopback publication
Use host port 18280 unless it is already occupied;
record any substitution. Keep state 1 running so the evidence shows
separate objects, not a mutation hidden in history.
docker run -d \
--name da19-cp-loopback \
--label devops-academy.lab=ch19-checkpoint -p 127.0.0.1:18280:8080 busybox:1.36.1 sh -c 'mkdir -p /www; echo state-loopback >/www/index.html; exec httpd -f -p 8080 -h /www'
docker port da19-cp-loopback | tee evidence/state2-docker-port.txt
docker inspect da19-cp-loopback > evidence/state2-inspect.json
curl -fsS http://127.0.0.1:18280/ | tee evidence/state2-host-response.txt
ss -lnt 2>/dev/null | grep ':18280 ' > evidence/state2-host-socket.txt || true
5. State 3 — controlled all-interface publication
Safety gate: perform this only on a
trusted/disposable host. If the machine is on an uncontrolled LAN or
public interface, skip the runtime creation and write
SKIPPED: unsafe environment to the evidence file. You
can still explain the expected Docker mapping from the documented
semantics.
docker run -d \
--name da19-cp-all \
--label devops-academy.lab=ch19-checkpoint -p 18281:8080 busybox:1.36.1 sh -c 'mkdir -p /www; echo state-all >/www/index.html; exec httpd -f -p 8080 -h /www'
docker port da19-cp-all | tee evidence/state3-docker-port.txt
docker inspect da19-cp-all > evidence/state3-inspect.json
curl -fsS http://127.0.0.1:18281/ | tee evidence/state3-host-response.txt
If an authorized second host exists, test the Docker host's LAN address on port 18281 and record success/failure plus the peer identity/trust zone. Do not weaken host firewall policy merely to make the peer test pass.
6. Optional native-Linux packet-filter evidence
# Optional/read-only and often requires administrative authorization:
sudo iptables -t nat -S 2>/dev/null | grep -E 'DOCKER|1828' > evidence/iptables-nat.txt || true
sudo iptables -S 2>/dev/null | grep -E 'DOCKER|1828' > evidence/iptables-filter.txt || true
sudo nft list ruleset 2>/dev/null | grep -E 'docker|1828' > evidence/nftables.txt || true
For Docker Desktop, replace missing native-Linux rule evidence with a note that the Desktop backend/VM owns forwarding. Docker mappings and host/client tests remain valid evidence.
7. Build the exposure matrix
| State | Internal response | Docker HostIp | Host response | Authorized peer | Expected security meaning |
|---|---|---|---|---|---|
| Internal only | Success | None | No mapped URL | No | Service exists only on Docker network namespace/path. |
| Loopback | Success | 127.0.0.1 | Success | Normally no direct LAN path in current NAT semantics | Local-host development/admin boundary. |
| All interfaces | Success | Unspecified/all interfaces | Success | Environment-dependent; test only if authorized | Broader host exposure requiring host/network policy. |
Fill the matrix with your actual evidence filenames and observed values. Do not replace failed observations with expected answers; discrepancies are the most valuable checkpoint data.
8. Restore the safest local-only state
The final intended state is one loopback-published container. Remove the broader all-interface container and the internal-only comparison container, then verify only the loopback lab object remains.
docker rm -f da19-cp-all da19-cp-internal 2>/dev/null || true
docker ps --filter label=devops-academy.lab=ch19-checkpoint > evidence/final-resources.txt
docker port da19-cp-loopback | tee evidence/final-loopback-mapping.txt
curl -fsS http://127.0.0.1:18280/ | tee evidence/final-loopback-response.txt
If you skipped state 3, the rollback command remains safe because it targets only the exact disposable name.
9. Evidence packet checklist
- Engine/CLI/Compose/Buildx versions, active context, host/Desktop assumption.
- BusyBox image ID and RepoDigest when available.
- Three container IDs/inspect records for attempted states.
- In-container application response proving listener health.
-
docker portand inspect mapping for each state. - Host HTTP response for loopback/all-interface states.
- Optional host socket and firewall/NAT rule snapshots.
- Optional authorized peer result, or explicit reason it was skipped.
- Predictions versus observations table.
- Final resource inventory and final loopback mapping.
- Assumptions/limitations note covering firewall, VPN, Desktop VM, and route environment.
cat > evidence/assumptions.txt <<'EOF'
Chapter 19 checkpoint assumptions:
- Disposable local Docker resources only.
- No production endpoints or credentials.
- Host firewall/daemon settings were not changed.
- All-interface testing was performed only if the host network was trusted.
- Direct-routing gateway modes were studied but not enabled by the mandatory lab.
- Docker Desktop host forwarding may differ from native Linux rule visibility.
EOF
tar -czf da19-evidence.tgz evidence
ls -lh da19-evidence.tgz
10. Final cleanup after evidence review
After confirming the evidence archive exists, remove the final loopback container. Keep the evidence archive if you want to compare future Docker versions or host configurations.
docker rm -f da19-cp-loopback 2>/dev/null || true
docker ps -a --filter label=devops-academy.lab=ch19-checkpoint
# Do not delete unrelated images/networks/caches.
11. What Chapter 19 adds to the production operating model
You can now separate service health from exposure, express host-binding scope deliberately, prove exact publish mappings, understand where NAT/firewall/direct-routing behavior sits in the packet path, and diagnose exposure failures without destructive host changes. Most importantly, you can recognize accidental broad publication as a declarative security bug rather than “a firewall problem.”
Chapter 20 moves from network exposure to data persistence. The same evidence discipline applies: container writable state, named-volume identity, mount ownership, backup/restore artifacts, and cleanup intent must be proven independently.
Knowledge check
Why does the checkpoint keep internal-only and loopback containers as separate objects?
It preserves before/after evidence and prevents a lifecycle mutation from hiding what each exposure state actually configured.
What should you do if an all-interface step is unsafe on the current network?
Skip it, record the safety constraint, and use documented mapping semantics plus the safe states; do not open an uncontrolled host merely to satisfy a lab.
What is the required final exposure state?
Loopback-only during verification, followed by exact cleanup after the evidence archive is confirmed.
If localhost works but an authorized LAN peer fails, should you rebuild the image?
No. The app and local publication are already proven; investigate host firewall/interface/routing and peer path.
What is the key distinction between EXPOSE and
-p?
EXPOSE is image metadata documenting an intended container port;
-p creates runtime host publication state.
Official references and version notes
- Docker Docs — Port publishing and mapping — bind addresses, NAT/PAT, direct routing, gateway modes, and default binding behavior.
- Docker Docs — Packet filtering and firewalls — Docker-created firewall rules, iptables/nftables backends, forwarding, firewalld, and UFW interaction.
- Docker Docs — Docker with nftables — experimental Engine 29 nftables backend and migration cautions.
-
Docker CLI — docker container run
—
--publish,--publish-all, host-IP binding, and runtime networking flags. - Dockerfile reference — EXPOSE — image metadata versus actual host publication.
- Docker Desktop networking — Desktop forwarding architecture, host binding, and firewall integration differences.
- Docker Docs — Host network driver — host namespace semantics and why publish flags do not apply in host mode.
- Docker Engine 29 release notes — Engine 29 networking changes and experimental nftables support.
Docker Engine 29.8.1 is the current Engine 29 release. On Linux, Docker uses iptables by default and the Engine 29 nftables backend remains experimental. Docker Desktop implements host exposure through its managed VM/backend and can differ from native Linux host rule inspection. The labs therefore make Docker API/container-visible evidence mandatory and host firewall inspection optional/read-only.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.