exercises: ex16-19 hinzugefügt, commands.sh → commands.txt

- ex16: lokale Registry + VisitCounter pushen
- ex17: vollständiger Update-Zyklus (edit → build → push → restart)
- ex18: VisitCounter mit Quadlets deployen
- ex19: Container schrittweise härten (read-only, cap-drop, no-new-priv)
- alle commands.sh umbenannt in commands.txt, Shebang entfernt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Frank Engel 2026-05-21 09:09:51 +02:00
parent 5ad7435542
commit b32d8c2472
12 changed files with 132 additions and 8 deletions

View file

@ -1,4 +1,3 @@
#!/bin/bash
# Exercise 5 — Container Networking
podman network create appnet

View file

@ -1,4 +1,3 @@
#!/bin/bash
# Exercise 6 — Bind Mounts
# Terminal 1: write dates to /tmp/output (detached)

View file

@ -1,4 +1,3 @@
#!/bin/bash
# Exercise 7 — Named Volumes (shared between containers)
podman volume create pizza

View file

@ -1,4 +1,3 @@
#!/bin/bash
# Exercise 8 — Volume Persistence (data survives container removal)
podman volume create pasta

View file

@ -1,4 +1,3 @@
#!/bin/bash
# Exercise 9 — Environment Variables
# Single variables via -e

View file

@ -1,4 +1,3 @@
#!/bin/bash
# Exercise 13 — Multi-stage Build
# Build

View file

@ -1,4 +1,3 @@
#!/bin/bash
# Exercise 14 — Push image to local registry
# Tag and push

View file

@ -1,4 +1,3 @@
#!/bin/bash
# Exercise 15 — Query the local registry API
# List all repositories in the registry

View file

@ -0,0 +1,19 @@
# Exercise 16 — Start a local registry and push the VisitCounter
# Start the registry container
podman run -d --name local-registry -p 5000:5000 registry:2
# Build the VisitCounter image
cd listings/visitcounter/app
podman build -t localhost:5000/visitcounter:latest .
# Push to the local registry
podman push --tls-verify=false localhost:5000/visitcounter:latest
# Verify: list all repositories
curl http://localhost:5000/v2/_catalog
# → {"repositories":["visitcounter"]}
# Verify: list tags
curl http://localhost:5000/v2/visitcounter/tags/list
# → {"name":"visitcounter","tags":["latest"]}

View file

@ -0,0 +1,18 @@
# Exercise 17 — Full update cycle (edit → build → push → restart)
# 1. Change the page title in main.go
sed -i 's|<h1>VisitCounter</h1>|<h1>My Counter</h1>|' \
listings/visitcounter/app/main.go
# 2. Rebuild
cd listings/visitcounter/app
podman build -t localhost:5000/visitcounter:latest .
# 3. Push
podman push --tls-verify=false localhost:5000/visitcounter:latest
# 4. Restart the Quadlet service
systemctl --user restart visitcounter-app.service
# 5. Verify — new title should appear
curl localhost:8080

View file

@ -0,0 +1,31 @@
# Exercise 18 — Deploy VisitCounter via Quadlets
# Create the Quadlet directory
mkdir -p ~/.config/containers/systemd/
# Copy all Quadlet unit files
cp listings/visitcounter/quadlets/* ~/.config/containers/systemd/
# Reload systemd to pick up the new units
systemctl --user daemon-reload
# Check that units were generated
systemctl --user list-unit-files | grep visitcounter
# → visitcounter-app.service generated
# → visitcounter-db.service generated
# → visitcounter-proxy.service generated
# Start only the proxy — systemd resolves After= dependencies automatically
systemctl --user start visitcounter-proxy.service
# Verify all three services are running
systemctl --user status visitcounter-db.service
systemctl --user status visitcounter-app.service
systemctl --user status visitcounter-proxy.service
# Test
curl localhost:8080
# Bonus: kill the app container and watch systemd restart it
podman stop systemd-visitcounter-app
systemctl --user status visitcounter-app.service

View file

@ -0,0 +1,64 @@
# Exercise 19 — Harden a container step by step
# Baseline — works fine
podman run -d --name insecure -p 8080:80 nginx:1.27
curl localhost:8080
podman rm -f insecure
# Step 1: read-only filesystem
podman run -d --name step1 -p 8080:80 --read-only nginx:1.27
podman logs step1
# → fails: mkdir /var/cache/nginx/client_temp: Read-only file system
podman rm -f step1
podman run -d --name step1 -p 8080:80 \
--read-only \
--tmpfs /var/cache/nginx \
--tmpfs /var/run \
nginx:1.27
curl localhost:8080
podman rm -f step1
# Step 2: no-new-privileges
podman run -d --name step2 -p 8080:80 \
--read-only \
--tmpfs /var/cache/nginx \
--tmpfs /var/run \
--security-opt no-new-privileges \
nginx:1.27
curl localhost:8080
podman rm -f step2
# Step 3: drop all capabilities
podman run -d --name step3 -p 8080:80 \
--read-only \
--tmpfs /var/cache/nginx \
--tmpfs /var/run \
--security-opt no-new-privileges \
--cap-drop ALL \
nginx:1.27
podman logs step3
# → fails: bind() to 0.0.0.0:80 failed (13: Permission denied)
podman rm -f step3
# Step 4: add back only what nginx needs
podman run -d --name secure -p 8080:80 \
--read-only \
--tmpfs /var/cache/nginx \
--tmpfs /var/run \
--security-opt no-new-privileges \
--cap-drop ALL \
--cap-add CHOWN \
--cap-add SETUID \
--cap-add SETGID \
--cap-add NET_BIND_SERVICE \
nginx:1.27
curl localhost:8080
# → 200 OK
# Inspect the final capability set
podman inspect secure \
--format 'Add: {{.HostConfig.CapAdd}} Drop: {{.HostConfig.CapDrop}}'
podman rm -f secure