docs(02): create phase 2 plans (3 plans)
This commit is contained in:
@@ -12,7 +12,7 @@
|
|||||||
| Phase | Plans Complete | Status | Completed |
|
| Phase | Plans Complete | Status | Completed |
|
||||||
|-------|----------------|--------|-----------|
|
|-------|----------------|--------|-----------|
|
||||||
| 1. Setup & Git Foundation | 0/2 | Planning complete | - |
|
| 1. Setup & Git Foundation | 0/2 | Planning complete | - |
|
||||||
| 2. Lab 01 - IAM & Sicurezza | 0/3 | Not started | - |
|
| 2. Lab 01 - IAM & Sicurezza | 0/3 | Planning complete | - |
|
||||||
| 3. Lab 02 - Network & VPC | 0/3 | Not started | - |
|
| 3. Lab 02 - Network & VPC | 0/3 | Not started | - |
|
||||||
| 4. Lab 03 - Compute & EC2 | 0/3 | Not started | - |
|
| 4. Lab 03 - Compute & EC2 | 0/3 | Not started | - |
|
||||||
| 5. Lab 04 - Storage & S3 | 0/3 | Not started | - |
|
| 5. Lab 04 - Storage & S3 | 0/3 | Not started | - |
|
||||||
@@ -80,7 +80,11 @@
|
|||||||
4. Lab include Tutorial passo-passo, How-to Guides, Reference, e Explanation (Framework Diátaxis completo)
|
4. Lab include Tutorial passo-passo, How-to Guides, Reference, e Explanation (Framework Diátaxis completo)
|
||||||
5. Studente può eseguire comando di verifica finale ("double check") per validare il lavoro svolto
|
5. Studente può eseguire comando di verifica finale ("double check") per validare il lavoro svolto
|
||||||
|
|
||||||
**Plans:** TBD
|
**Plans:** 3
|
||||||
|
|
||||||
|
- [ ] [02-01-PLAN.md](.planning/phases/02-lab-01-iam-sicurezza/02-01-PLAN.md) — Create test infrastructure (Wave 0: test-01-user-creation.sh, test-02-docker-access.sh, 03-non-root-test.sh, 99-final-verification.sh, run-all-tests.sh)
|
||||||
|
- [ ] [02-02-PLAN.md](.planning/phases/02-lab-01-iam-sicurezza/02-02-PLAN.md) — Create Diátaxis documentation (Tutorial: 3 parts, How-to Guides: 3 guides, Reference: 3 documents, Explanation: IAM parallels)
|
||||||
|
- [ ] [02-03-PLAN.md](.planning/phases/02-lab-01-iam-sicurezza/02-03-PLAN.md) — Create infrastructure (Dockerfile with non-root user, docker-compose.yml with user directive, infrastructure verification)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
808
.planning/phases/02-lab-01-iam-sicurezza/02-01-PLAN.md
Normal file
808
.planning/phases/02-lab-01-iam-sicurezza/02-01-PLAN.md
Normal file
@@ -0,0 +1,808 @@
|
|||||||
|
---
|
||||||
|
phase: 02-lab-01-iam-sicurezza
|
||||||
|
plan: 01
|
||||||
|
type: execute
|
||||||
|
wave: 0
|
||||||
|
depends_on: []
|
||||||
|
files_modified:
|
||||||
|
- labs/lab-01-iam/tests/test-01-user-creation.sh
|
||||||
|
- labs/lab-01-iam/tests/test-02-docker-access.sh
|
||||||
|
- labs/lab-01-iam/tests/03-non-root-test.sh
|
||||||
|
- labs/lab-01-iam/tests/99-final-verification.sh
|
||||||
|
- labs/lab-01-iam/tests/run-all-tests.sh
|
||||||
|
autonomous: true
|
||||||
|
requirements: [TEST-01, TEST-05, INF-01]
|
||||||
|
user_setup: []
|
||||||
|
must_haves:
|
||||||
|
truths:
|
||||||
|
- "Test scripts exist and can validate user creation and Docker access"
|
||||||
|
- "Test scripts verify non-root container execution (INF-01)"
|
||||||
|
- "Final verification script runs all checks for student self-validation"
|
||||||
|
- "Test harness can be executed with single command"
|
||||||
|
artifacts:
|
||||||
|
- path: "labs/lab-01-iam/tests/test-01-user-creation.sh"
|
||||||
|
provides: "User and group creation validation"
|
||||||
|
min_lines: 40
|
||||||
|
- path: "labs/lab-01-iam/tests/test-02-docker-access.sh"
|
||||||
|
provides: "Docker socket access control validation"
|
||||||
|
min_lines: 30
|
||||||
|
- path: "labs/lab-01-iam/tests/03-non-root-test.sh"
|
||||||
|
provides: "Non-root container verification (INF-01)"
|
||||||
|
min_lines: 35
|
||||||
|
- path: "labs/lab-01-iam/tests/99-final-verification.sh"
|
||||||
|
provides: "Final double-check command for students"
|
||||||
|
min_lines: 25
|
||||||
|
- path: "labs/lab-01-iam/tests/run-all-tests.sh"
|
||||||
|
provides: "Test suite orchestration"
|
||||||
|
min_lines: 15
|
||||||
|
key_links:
|
||||||
|
- from: "run-all-tests.sh"
|
||||||
|
to: "test-01-user-creation.sh, test-02-docker-access.sh, 03-non-root-test.sh, 99-final-verification.sh"
|
||||||
|
via: "Sequential execution with exit code handling"
|
||||||
|
pattern: "bash.*tests/.*\\.sh"
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
Create test infrastructure following TDD methodology (RED phase first). Test scripts validate user creation, Docker socket access control, and non-root container execution before any implementation exists.
|
||||||
|
|
||||||
|
Purpose: Enable Test-Driven Infrastructure (TDI) by writing failing tests first, then implementing infrastructure to make them pass.
|
||||||
|
Output: Four test scripts (user creation, Docker access, non-root verification, final check) plus test orchestration script.
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<execution_context>
|
||||||
|
@/home/luca/.claude/get-shit-done/workflows/execute-plan.md
|
||||||
|
@/home/luca/.claude/get-shit-done/templates/summary.md
|
||||||
|
</execution_context>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
@.planning/PROJECT.md
|
||||||
|
@.planning/ROADMAP.md
|
||||||
|
@.planning/STATE.md
|
||||||
|
@.planning/phases/02-lab-01-iam-sicurezza/02-RESEARCH.md
|
||||||
|
@.planning/phases/02-lab-01-iam-sicurezza/02-VALIDATION.md
|
||||||
|
@CLAUDE.md
|
||||||
|
|
||||||
|
# Key patterns from RESEARCH.md
|
||||||
|
|
||||||
|
## TDD Methodology for Infrastructure
|
||||||
|
```bash
|
||||||
|
# RED Phase: Test should fail initially (no infrastructure exists)
|
||||||
|
# GREEN Phase: Implement minimum infrastructure to pass tests
|
||||||
|
# REFACTOR Phase: Optimize without breaking tests
|
||||||
|
|
||||||
|
# Example test structure from RESEARCH.md:
|
||||||
|
test_unauthorized_access() {
|
||||||
|
sudo useradd -m -s /bin/bash test_user 2>/dev/null || true
|
||||||
|
if sudo -u test_user docker ps &>/dev/null; then
|
||||||
|
echo "FAIL: test_user can access docker without being in docker group"
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
echo "PASS: test_user correctly denied access"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## INF-01 Verification Pattern
|
||||||
|
```bash
|
||||||
|
# From RESEARCH.md - Non-root container verification
|
||||||
|
for service in $(docker-compose ps --services); do
|
||||||
|
container_name=$(docker-compose ps -q $service)
|
||||||
|
actual_user=$(docker exec $container_name whoami 2>/dev/null)
|
||||||
|
if [ "$actual_user" = "root" ]; then
|
||||||
|
echo "FAIL: $service running as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "PASS: All containers running as non-root"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Pitfalls to Handle
|
||||||
|
- Group membership requires re-login (use `groups` command for testing)
|
||||||
|
- Test as non-privileged user (root bypasses Docker socket permissions)
|
||||||
|
- Verify with multiple methods: `docker exec whoami`, `docker inspect`, `docker top`
|
||||||
|
|
||||||
|
## Test Framework from RESEARCH.md
|
||||||
|
- Framework: BASH (Bourne Again Shell) >= 4.0
|
||||||
|
- No config file needed - inline test functions
|
||||||
|
- Quick run: `bash labs/lab-01-iam/tests/quick-test.sh`
|
||||||
|
- Full suite: `bash labs/lab-01-iam/tests/run-all-tests.sh`
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto" tdd="true">
|
||||||
|
<name>Task 1: Create user creation test script</name>
|
||||||
|
<files>labs/lab-01-iam/tests/test-01-user-creation.sh</files>
|
||||||
|
<behavior>
|
||||||
|
- Test 1: Non-existent user returns appropriate failure
|
||||||
|
- Test 2: User not in docker group cannot access Docker socket
|
||||||
|
- Test 3: User can be added to docker group
|
||||||
|
- Test 4: Group membership verified with `groups` command
|
||||||
|
</behavior>
|
||||||
|
<action>
|
||||||
|
Create test script for Linux user and group management:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Test: Linux user creation and Docker group membership
|
||||||
|
# Phase: RED - This test will fail initially (no users configured)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Color output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
pass_count=0
|
||||||
|
fail_count=0
|
||||||
|
|
||||||
|
test_user_not_exists() {
|
||||||
|
local user="lab01_student"
|
||||||
|
if id "$user" &>/dev/null; then
|
||||||
|
echo -e "${YELLOW}SKIP${NC}: User $user already exists"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}PASS${NC}: User $user does not exist (test environment clean)"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
test_user_without_docker_group() {
|
||||||
|
local user="lab01_student"
|
||||||
|
# Create test user if doesn't exist
|
||||||
|
if ! id "$user" &>/dev/null; then
|
||||||
|
sudo useradd -m -s /bin/bash "$user" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if user is in docker group
|
||||||
|
if groups "$user" 2>/dev/null | grep -q docker; then
|
||||||
|
echo -e "${RED}FAIL${NC}: User $user is in docker group (should not be yet)"
|
||||||
|
((fail_count++))
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}PASS${NC}: User $user is not in docker group"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
test_docker_access_denied() {
|
||||||
|
local user="lab01_student"
|
||||||
|
|
||||||
|
# Test that user cannot access docker socket
|
||||||
|
if sudo -u "$user" docker ps &>/dev/null; then
|
||||||
|
echo -e "${RED}FAIL${NC}: User $user can access docker without docker group membership"
|
||||||
|
((fail_count++))
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}PASS${NC}: Docker access correctly denied for $user"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run all tests
|
||||||
|
echo "Running user creation tests..."
|
||||||
|
echo "================================"
|
||||||
|
test_user_not_exists
|
||||||
|
test_user_without_docker_group
|
||||||
|
test_docker_access_denied
|
||||||
|
echo "================================"
|
||||||
|
echo "Tests passed: $pass_count"
|
||||||
|
echo "Tests failed: $fail_count"
|
||||||
|
|
||||||
|
if [ $fail_count -gt 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Key implementation points:
|
||||||
|
- Use `groups` command to verify group membership (handles re-login issue)
|
||||||
|
- Run Docker commands as test user with `sudo -u`
|
||||||
|
- Test the negative case first (user without access)
|
||||||
|
- Return proper exit codes (0=pass, 1=fail)
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>chmod +x labs/lab-01-iam/tests/test-01-user-creation.sh && bash labs/lab-01-iam/tests/test-01-user-creation.sh</automated>
|
||||||
|
</verify>
|
||||||
|
<done>Script exists, is executable, and tests user/group creation behavior</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto" tdd="true">
|
||||||
|
<name>Task 2: Create Docker access control test script</name>
|
||||||
|
<files>labs/lab-01-iam/tests/test-02-docker-access.sh</files>
|
||||||
|
<behavior>
|
||||||
|
- Test 1: User in docker group can execute docker ps
|
||||||
|
- Test 2: User in docker group can run basic containers
|
||||||
|
- Test 3: Socket permissions are correctly set (660 or stricter)
|
||||||
|
- Test 4: Group membership propagation is verified
|
||||||
|
</behavior>
|
||||||
|
<action>
|
||||||
|
Create test script for Docker socket access control:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Test: Docker socket access control via group membership
|
||||||
|
# Phase: RED - This test will fail initially (no users configured)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
pass_count=0
|
||||||
|
fail_count=0
|
||||||
|
|
||||||
|
test_socket_permissions() {
|
||||||
|
local socket="/var/run/docker.sock"
|
||||||
|
local perms=$(stat -c "%a" "$socket" 2>/dev/null || echo "000")
|
||||||
|
|
||||||
|
# Socket should be 660 or stricter (no world-readable/writable)
|
||||||
|
if [ "$perms" = "660" ] || [ "$perms" = "600" ]; then
|
||||||
|
echo -e "${GREEN}PASS${NC}: Docker socket permissions are $perms"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}WARN${NC}: Docker socket permissions are $perms (expected 660)"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
test_docker_group_exists() {
|
||||||
|
if getent group docker >/dev/null 2>&1; then
|
||||||
|
echo -e "${GREEN}PASS${NC}: Docker group exists"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e "${RED}FAIL${NC}: Docker group does not exist"
|
||||||
|
((fail_count++))
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
test_user_can_add_to_docker_group() {
|
||||||
|
local user="lab01_student"
|
||||||
|
|
||||||
|
# This test verifies the MECHANISM, not that it's done yet
|
||||||
|
if command -v usermod >/dev/null 2>&1; then
|
||||||
|
echo -e "${GREEN}PASS${NC}: usermod command available for group management"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e "${RED}FAIL${NC}: usermod command not available"
|
||||||
|
((fail_count++))
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
test_docker_accessible_by_group() {
|
||||||
|
# Check that docker group members can access the socket
|
||||||
|
local socket_group=$(stat -c "%G" /var/run/docker.sock 2>/dev/null || echo "unknown")
|
||||||
|
|
||||||
|
if [ "$socket_group" = "docker" ]; then
|
||||||
|
echo -e "${GREEN}PASS${NC}: Docker socket owned by docker group"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}WARN${NC}: Docker socket owned by $socket_group (expected docker)"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run all tests
|
||||||
|
echo "Running Docker access control tests..."
|
||||||
|
echo "======================================"
|
||||||
|
test_socket_permissions
|
||||||
|
test_docker_group_exists
|
||||||
|
test_user_can_add_to_docker_group
|
||||||
|
test_docker_accessible_by_group
|
||||||
|
echo "======================================"
|
||||||
|
echo "Tests passed: $pass_count"
|
||||||
|
echo "Tests failed: $fail_count"
|
||||||
|
|
||||||
|
if [ $fail_count -gt 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Key implementation points:
|
||||||
|
- Verify socket ownership and permissions
|
||||||
|
- Check docker group exists
|
||||||
|
- Validate group management commands available
|
||||||
|
- Test mechanism for adding users to docker group
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>chmod +x labs/lab-01-iam/tests/test-02-docker-access.sh && bash labs/lab-01-iam/tests/test-02-docker-access.sh</automated>
|
||||||
|
</verify>
|
||||||
|
<done>Script validates Docker socket access control mechanisms</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto" tdd="true">
|
||||||
|
<name>Task 3: Create non-root container verification script (INF-01)</name>
|
||||||
|
<files>labs/lab-01-iam/tests/03-non-root-test.sh</files>
|
||||||
|
<behavior>
|
||||||
|
- Test 1: Container configured with USER directive runs as non-root
|
||||||
|
- Test 2: docker exec whoami returns non-root user
|
||||||
|
- Test 3: docker inspect shows User field set
|
||||||
|
- Test 4: docker top shows non-root UID (not 0)
|
||||||
|
</behavior>
|
||||||
|
<action>
|
||||||
|
Create test script for non-root container verification (INF-01 requirement):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Test: Non-root container execution (INF-01 requirement)
|
||||||
|
# Phase: RED - This test will fail initially (no containers exist)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
pass_count=0
|
||||||
|
fail_count=0
|
||||||
|
|
||||||
|
# Container name to test
|
||||||
|
CONTAINER_NAME="lab01-test-container"
|
||||||
|
|
||||||
|
test_non_root_dockerfile_exists() {
|
||||||
|
local dockerfile="labs/lab-01-iam/Dockerfile.test"
|
||||||
|
|
||||||
|
if [ -f "$dockerfile" ]; then
|
||||||
|
echo -e "${GREEN}PASS${NC}: Test Dockerfile exists"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}SKIP${NC}: Test Dockerfile not created yet (expected in RED phase)"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
test_container_runs_as_non_root() {
|
||||||
|
# Check if container exists
|
||||||
|
if ! docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||||
|
echo -e "${YELLOW}SKIP${NC}: Container ${CONTAINER_NAME} does not exist yet (expected in RED phase)"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if container is running
|
||||||
|
if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||||
|
echo -e "${YELLOW}SKIP${NC}: Container ${CONTAINER_NAME} not running"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Method 1: docker exec whoami
|
||||||
|
local actual_user=$(docker exec "${CONTAINER_NAME}" whoami 2>/dev/null || echo "unknown")
|
||||||
|
|
||||||
|
if [ "$actual_user" = "root" ]; then
|
||||||
|
echo -e "${RED}FAIL${NC}: Container running as root (whoami)"
|
||||||
|
((fail_count++))
|
||||||
|
return 1
|
||||||
|
elif [ "$actual_user" = "unknown" ]; then
|
||||||
|
echo -e "${YELLOW}SKIP${NC}: Cannot determine user (container not running or no exec)"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}PASS${NC}: Container running as non-root user: $actual_user"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
test_container_user_configured() {
|
||||||
|
if ! docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||||
|
echo -e "${YELLOW}SKIP${NC}: Container not created yet"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Method 2: docker inspect for User field
|
||||||
|
local user_config=$(docker inspect --format='{{.Config.User}}' "${CONTAINER_NAME}" 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
if [ -z "$user_config" ]; then
|
||||||
|
# Check compose file for user directive (may override Dockerfile)
|
||||||
|
if [ -f "labs/lab-01-iam/docker-compose.yml" ]; then
|
||||||
|
if grep -q "user:" labs/lab-01-iam/docker-compose.yml; then
|
||||||
|
echo -e "${GREEN}PASS${NC}: User directive found in docker-compose.yml"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW}WARN${NC}: No User directive found in Dockerfile or compose"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}PASS${NC}: User configured in container: $user_config"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
test_no_container_runs_as_root() {
|
||||||
|
# INF-01 requirement: NO container should run as root
|
||||||
|
local compose_file="labs/lab-01-iam/docker-compose.yml"
|
||||||
|
|
||||||
|
if [ ! -f "$compose_file" ]; then
|
||||||
|
echo -e "${YELLOW}SKIP${NC}: docker-compose.yml not created yet"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get all services from compose file
|
||||||
|
local services=$(docker-compose -f "$compose_file" ps --services 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
if [ -z "$services" ]; then
|
||||||
|
echo -e "${YELLOW}SKIP${NC}: No services defined yet"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local root_containers=0
|
||||||
|
while IFS= read -r service; do
|
||||||
|
if [ -n "$service" ]; then
|
||||||
|
local container_name=$(docker-compose -f "$compose_file" ps -q "$service" 2>/dev/null || echo "")
|
||||||
|
if [ -n "$container_name" ]; then
|
||||||
|
local user=$(docker exec "$container_name" whoami 2>/dev/null || echo "unknown")
|
||||||
|
if [ "$user" = "root" ]; then
|
||||||
|
echo -e "${RED}FAIL${NC}: Service $service running as root"
|
||||||
|
((root_containers++))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done <<< "$services"
|
||||||
|
|
||||||
|
if [ $root_containers -gt 0 ]; then
|
||||||
|
echo -e "${RED}FAIL${NC}: $root_containers container(s) running as root (INF-01 violation)"
|
||||||
|
((fail_count++))
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}PASS${NC}: No containers running as root (INF-01 satisfied)"
|
||||||
|
((pass_count++))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run all tests
|
||||||
|
echo "Running non-root container tests (INF-01)..."
|
||||||
|
echo "============================================"
|
||||||
|
test_non_root_dockerfile_exists
|
||||||
|
test_container_runs_as_non_root
|
||||||
|
test_container_user_configured
|
||||||
|
test_no_container_runs_as_root
|
||||||
|
echo "============================================"
|
||||||
|
echo "Tests passed: $pass_count"
|
||||||
|
echo "Tests failed: $fail_count"
|
||||||
|
|
||||||
|
if [ $fail_count -gt 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Key implementation points:
|
||||||
|
- INF-01 requirement: NO container runs as root
|
||||||
|
- Multiple verification methods (whoami, inspect, compose check)
|
||||||
|
- Handle not-yet-created infrastructure gracefully (skip with warning)
|
||||||
|
- Check all services in docker-compose.yml for compliance
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>chmod +x labs/lab-01-iam/tests/03-non-root-test.sh && bash labs/lab-01-iam/tests/03-non-root-test.sh</automated>
|
||||||
|
</verify>
|
||||||
|
<done>Script verifies INF-01: no container runs as root</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto" tdd="true">
|
||||||
|
<name>Task 4: Create final verification script (double-check for students)</name>
|
||||||
|
<files>labs/lab-01-iam/tests/99-final-verification.sh</files>
|
||||||
|
<behavior>
|
||||||
|
- Test 1: All previous tests can be run
|
||||||
|
- Test 2: Student can verify their work end-to-end
|
||||||
|
- Test 3: Clear PASS/FAIL report for all requirements
|
||||||
|
- Test 4: Exit code indicates overall success/failure
|
||||||
|
</behavior>
|
||||||
|
<action>
|
||||||
|
Create final verification script for student self-check:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Final Verification: Lab 01 - IAM & Sicurezza
|
||||||
|
# This is the "double check" command students run to verify their work
|
||||||
|
# Usage: bash tests/99-final-verification.sh
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo -e "${BLUE}Lab 01 - IAM & Sicurezza${NC}"
|
||||||
|
echo -e "${BLUE}Final Verification (Double Check)${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Track overall results
|
||||||
|
all_passed=true
|
||||||
|
|
||||||
|
# Test 1: User and group configuration
|
||||||
|
echo -e "${BLUE}[1/5] Checking user and group configuration...${NC}"
|
||||||
|
if id lab01_student &>/dev/null; then
|
||||||
|
echo -e " ${GREEN}✓${NC} User lab01_student exists"
|
||||||
|
if groups lab01_student 2>/dev/null | grep -q docker; then
|
||||||
|
echo -e " ${GREEN}✓${NC} User lab01_student is in docker group"
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} User lab01_student is NOT in docker group"
|
||||||
|
all_passed=false
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}○${NC} User lab01_student does not exist (not created yet)"
|
||||||
|
all_passed=false
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 2: Docker access control
|
||||||
|
echo -e "${BLUE}[2/5] Checking Docker access control...${NC}"
|
||||||
|
if sudo -u lab01_student docker ps &>/dev/null; then
|
||||||
|
echo -e " ${GREEN}✓${NC} lab01_student can access Docker socket"
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} lab01_student cannot access Docker socket"
|
||||||
|
echo -e " ${YELLOW} Hint: User may need to re-login for group membership to take effect${NC}"
|
||||||
|
all_passed=false
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 3: Non-root container execution (INF-01)
|
||||||
|
echo -e "${BLUE}[3/5] Checking non-root container execution (INF-01)...${NC}"
|
||||||
|
compose_file="labs/lab-01-iam/docker-compose.yml"
|
||||||
|
if [ ! -f "$compose_file" ]; then
|
||||||
|
echo -e " ${YELLOW}○${NC} docker-compose.yml not found"
|
||||||
|
all_passed=false
|
||||||
|
else
|
||||||
|
echo -e " ${GREEN}✓${NC} docker-compose.yml exists"
|
||||||
|
|
||||||
|
# Check for user directive in services
|
||||||
|
if grep -A 10 "services:" "$compose_file" | grep -q "user:"; then
|
||||||
|
echo -e " ${GREEN}✓${NC} Services configured with non-root user directive"
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} No user directive found in docker-compose.yml"
|
||||||
|
all_passed=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If containers are running, verify they're not root
|
||||||
|
if docker-compose -f "$compose_file" ps --services 2>/dev/null | grep -q .; then
|
||||||
|
local root_count=0
|
||||||
|
while IFS= read -r service; do
|
||||||
|
[ -z "$service" ] && continue
|
||||||
|
local container=$(docker-compose -f "$compose_file" ps -q "$service" 2>/dev/null || echo "")
|
||||||
|
if [ -n "$container" ]; then
|
||||||
|
local user=$(docker exec "$container" whoami 2>/dev/null || echo "unknown")
|
||||||
|
if [ "$user" = "root" ]; then
|
||||||
|
echo -e " ${RED}✗${NC} Service $service running as ROOT (INF-01 violation)"
|
||||||
|
((root_count++))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done <<< "$(docker-compose -f "$compose_file" ps --services 2>/dev/null)"
|
||||||
|
|
||||||
|
if [ $root_count -eq 0 ]; then
|
||||||
|
echo -e " ${GREEN}✓${NC} All running containers are non-root"
|
||||||
|
else
|
||||||
|
all_passed=false
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}○${NC} No containers running (start with docker-compose up)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 4: Documentation completeness (Diátaxis)
|
||||||
|
echo -e "${BLUE}[4/5] Checking documentation (Diátaxis framework)...${NC}"
|
||||||
|
doc_count=0
|
||||||
|
for doc_type in "tutorial" "how-to-guides" "reference" "explanation"; do
|
||||||
|
if [ -d "labs/lab-01-iam/$doc_type" ]; then
|
||||||
|
local md_files=$(find "labs/lab-01-iam/$doc_type" -name "*.md" 2>/dev/null | wc -l)
|
||||||
|
if [ "$md_files" -gt 0 ]; then
|
||||||
|
echo -e " ${GREEN}✓${NC} $doc_type: $md_files document(s)"
|
||||||
|
((doc_count++))
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}○${NC} $doc_type: directory exists but empty"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} $doc_type: missing"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $doc_count -eq 4 ]; then
|
||||||
|
echo -e " ${GREEN}✓${NC} All 4 Diátaxis document types present"
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}○${NC} $doc_count/4 Diátaxis document types present"
|
||||||
|
all_passed=false
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 5: IAM parallels documentation
|
||||||
|
echo -e "${BLUE}[5/5] Checking IAM parallels explanation...${NC}"
|
||||||
|
explanation_file="labs/lab-01-iam/explanation/docker-iam-parallels.md"
|
||||||
|
if [ -f "$explanation_file" ]; then
|
||||||
|
if grep -qi "IAM.*Linux\|Linux.*IAM" "$explanation_file"; then
|
||||||
|
echo -e " ${GREEN}✓${NC} IAM parallels documented"
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}○${NC} Explanation exists but IAM parallels unclear"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -qi "differenza\|difference" "$explanation_file"; then
|
||||||
|
echo -e " ${GREEN}✓${NC} Local vs cloud differences documented"
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}○${NC} Local vs cloud differences not clearly documented"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} IAM parallels explanation not found"
|
||||||
|
all_passed=false
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Final summary
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
if [ "$all_passed" = true ]; then
|
||||||
|
echo -e "${GREEN}ALL CHECKS PASSED${NC}"
|
||||||
|
echo -e "${GREEN}Lab 01 is complete!${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo -e "${RED}SOME CHECKS FAILED${NC}"
|
||||||
|
echo -e "${YELLOW}Review the output above and complete the missing items${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
Key implementation points:
|
||||||
|
- Student-friendly double-check command
|
||||||
|
- Clear visual indicators (✓ pass, ✗ fail, ○ skip)
|
||||||
|
- Tests all phase requirements: LAB-01, INF-01, DOCT-01/02/03/04, PARA-01/03
|
||||||
|
- Exit code 0 for all-pass, 1 for any failure
|
||||||
|
- Helpful hints for common issues (re-login for group membership)
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>chmod +x labs/lab-01-iam/tests/99-final-verification.sh && bash labs/lab-01-iam/tests/99-final-verification.sh</automated>
|
||||||
|
</verify>
|
||||||
|
<done>Student can run single command to verify all lab requirements are met</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto" tdd="true">
|
||||||
|
<name>Task 5: Create test orchestration script</name>
|
||||||
|
<files>labs/lab-01-iam/tests/run-all-tests.sh</files>
|
||||||
|
<behavior>
|
||||||
|
- Test 1: Script executes all test files in sequence
|
||||||
|
- Test 2: Script stops on first failure (fail-fast)
|
||||||
|
- Test 3: Script aggregates results and provides summary
|
||||||
|
- Test 4: Script can be run from project root
|
||||||
|
</behavior>
|
||||||
|
<action>
|
||||||
|
Create test orchestration script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Test Suite Runner: Lab 01 - IAM & Sicurezza
|
||||||
|
# Runs all tests in sequence and provides summary
|
||||||
|
# Usage: bash labs/lab-01-iam/tests/run-all-tests.sh
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "$TEST_DIR/../.." && pwd)"
|
||||||
|
|
||||||
|
cd "$PROJECT_ROOT"
|
||||||
|
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo -e "${BLUE}Lab 01 Test Suite${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Array of test files in order
|
||||||
|
declare -a tests=(
|
||||||
|
"$TEST_DIR/test-01-user-creation.sh"
|
||||||
|
"$TEST_DIR/test-02-docker-access.sh"
|
||||||
|
"$TEST_DIR/03-non-root-test.sh"
|
||||||
|
)
|
||||||
|
|
||||||
|
total_tests=${#tests[@]}
|
||||||
|
passed_tests=0
|
||||||
|
failed_tests=0
|
||||||
|
|
||||||
|
for i in "${!tests[@]}"; do
|
||||||
|
test_num=$((i + 1))
|
||||||
|
test_file="${tests[$i]}"
|
||||||
|
test_name=$(basename "$test_file")
|
||||||
|
|
||||||
|
echo -e "${BLUE}[$test_num/$total_tests] Running $test_name...${NC}"
|
||||||
|
|
||||||
|
if bash "$test_file"; then
|
||||||
|
echo -e "${GREEN}✓ PASSED${NC}"
|
||||||
|
echo ""
|
||||||
|
((passed_tests++))
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ FAILED${NC}"
|
||||||
|
echo ""
|
||||||
|
((failed_tests++))
|
||||||
|
# Fail-fast: stop on first failure
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo -e "${BLUE}Test Suite Summary${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo "Passed: $passed_tests/$total_tests"
|
||||||
|
echo "Failed: $failed_tests/$total_tests"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ $failed_tests -eq 0 ]; then
|
||||||
|
echo -e "${GREEN}All tests passed!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Run final verification:"
|
||||||
|
echo " bash labs/lab-01-iam/tests/99-final-verification.sh"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo -e "${RED}Some tests failed${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
Key implementation points:
|
||||||
|
- Fail-fast approach (stops on first failure for TDD RED phase)
|
||||||
|
- Executes tests in dependency order
|
||||||
|
- Provides summary and next steps
|
||||||
|
- Can be run from any directory (uses absolute paths)
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>chmod +x labs/lab-01-iam/tests/run-all-tests.sh && bash labs/lab-01-iam/tests/run-all-tests.sh</automated>
|
||||||
|
</verify>
|
||||||
|
<done>Orchestration script runs all tests and provides summary</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
1. All test scripts are executable (chmod +x)
|
||||||
|
2. Test scripts can run individually and return proper exit codes
|
||||||
|
3. Test orchestration script executes all tests in sequence
|
||||||
|
4. Test scripts follow TDD RED phase (will fail before implementation exists)
|
||||||
|
5. All tests handle missing infrastructure gracefully (skip with warning)
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
1. Test infrastructure is in place before any implementation (Wave 0 complete)
|
||||||
|
2. All requirement IDs (TEST-01, TEST-05, INF-01) have test coverage
|
||||||
|
3. Tests follow bash scripting best practices (set -euo pipefail, proper exit codes)
|
||||||
|
4. Student can run individual tests or full suite
|
||||||
|
5. Final verification script provides clear pass/fail report for all lab requirements
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.planning/phases/02-lab-01-iam-sicurezza/02-01-SUMMARY.md`
|
||||||
|
</output>
|
||||||
1843
.planning/phases/02-lab-01-iam-sicurezza/02-02-PLAN.md
Normal file
1843
.planning/phases/02-lab-01-iam-sicurezza/02-02-PLAN.md
Normal file
File diff suppressed because it is too large
Load Diff
504
.planning/phases/02-lab-01-iam-sicurezza/02-03-PLAN.md
Normal file
504
.planning/phases/02-lab-01-iam-sicurezza/02-03-PLAN.md
Normal file
@@ -0,0 +1,504 @@
|
|||||||
|
---
|
||||||
|
phase: 02-lab-01-iam-sicurezza
|
||||||
|
plan: 03
|
||||||
|
type: execute
|
||||||
|
wave: 2
|
||||||
|
depends_on: [02-01, 02-02]
|
||||||
|
files_modified:
|
||||||
|
- labs/lab-01-iam/Dockerfile
|
||||||
|
- labs/lab-01-iam/docker-compose.yml
|
||||||
|
- labs/lab-01-iam/tests/04-verify-infrastructure.sh
|
||||||
|
autonomous: true
|
||||||
|
requirements: [LAB-01, INF-01, TEST-01]
|
||||||
|
user_setup: []
|
||||||
|
must_haves:
|
||||||
|
truths:
|
||||||
|
- "docker-compose.yml defines services with non-root user directive (INF-01)"
|
||||||
|
- "Dockerfile creates non-root user and switches before CMD (INF-01)"
|
||||||
|
- "Test scripts validate non-root execution (INF-01)"
|
||||||
|
- "Infrastructure follows test-driven approach (GREEN phase of TDI)"
|
||||||
|
artifacts:
|
||||||
|
- path: "labs/lab-01-iam/Dockerfile"
|
||||||
|
provides: "Non-root container image definition"
|
||||||
|
min_lines: 15
|
||||||
|
- path: "labs/lab-01-iam/docker-compose.yml"
|
||||||
|
provides: "Service orchestration with user directive"
|
||||||
|
min_lines: 20
|
||||||
|
- path: "labs/lab-01-iam/tests/04-verify-infrastructure.sh"
|
||||||
|
provides: "Infrastructure verification script"
|
||||||
|
min_lines: 25
|
||||||
|
key_links:
|
||||||
|
- from: "docker-compose.yml"
|
||||||
|
to: "Dockerfile"
|
||||||
|
via: "build context and image reference"
|
||||||
|
pattern: "build:.*\\..*Dockerfile"
|
||||||
|
- from: "tests/04-verify-infrastructure.sh"
|
||||||
|
to: "docker-compose.yml, Dockerfile"
|
||||||
|
via: "Infrastructure validation"
|
||||||
|
pattern: "docker-compose.*-f.*docker-compose.yml"
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
Create Docker infrastructure (Dockerfile and docker-compose.yml) that implements non-root container execution (INF-01). Following TDD methodology, infrastructure is created AFTER tests exist, and tests should now pass (GREEN phase).
|
||||||
|
|
||||||
|
Purpose: Implement minimum infrastructure to satisfy LAB-01 and INF-01 requirements while ensuring all containers run as non-root.
|
||||||
|
Output: Dockerfile with non-root user, docker-compose.yml with user directive, and infrastructure verification test.
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<execution_context>
|
||||||
|
@/home/luca/.claude/get-shit-done/workflows/execute-plan.md
|
||||||
|
@/home/luca/.claude/get-shit-done/templates/summary.md
|
||||||
|
</execution_context>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
@.planning/PROJECT.md
|
||||||
|
@.planning/ROADMAP.md
|
||||||
|
@.planning/STATE.md
|
||||||
|
@.planning/phases/02-lab-01-iam-sicurezza/02-RESEARCH.md
|
||||||
|
@.planning/phases/02-lab-01-iam-sicurezza/02-VALIDATION.md
|
||||||
|
@CLAUDE.md
|
||||||
|
|
||||||
|
# From RESEARCH.md - Non-Root Container Pattern
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# Source: Docker security best practices
|
||||||
|
FROM alpine:3.19
|
||||||
|
|
||||||
|
# Create non-root user with specific UID/GID for consistency
|
||||||
|
RUN addgroup -g 1000 appgroup && \
|
||||||
|
adduser -D -u 1000 -G appgroup appuser
|
||||||
|
|
||||||
|
# Switch to non-root user BEFORE any operations
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
# Verify non-root execution
|
||||||
|
CMD ["sh", "-c", "echo 'Running as:' && whoami"]
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml with user directive
|
||||||
|
services:
|
||||||
|
test-container:
|
||||||
|
image: alpine:3.19
|
||||||
|
user: "1000:1000" # UID:GID for non-root
|
||||||
|
command: ["sh", "-c", "whoami && sleep 3600"]
|
||||||
|
```
|
||||||
|
|
||||||
|
# From RESEARCH.md - Verification Methods
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Method 1: Execute whoami inside container
|
||||||
|
docker exec <container_name> whoami
|
||||||
|
# Expected output: appuser (NOT root)
|
||||||
|
|
||||||
|
# Method 2: Inspect container configuration
|
||||||
|
docker inspect <container_name> --format='{{.State.User}}'
|
||||||
|
|
||||||
|
# Method 3: Check process on host
|
||||||
|
docker top <container_name>
|
||||||
|
# Look at USER column - should show UID (e.g., 1000), NOT 0 (root)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Common Pitfalls to Avoid
|
||||||
|
|
||||||
|
- Running containers as root (violates INF-01)
|
||||||
|
- Using `--privileged` flag (defeats container isolation)
|
||||||
|
- Skipping verification of non-root execution
|
||||||
|
- Forgetting `user:` directive in docker-compose.yml
|
||||||
|
|
||||||
|
# TDD Methodology - GREEN Phase
|
||||||
|
|
||||||
|
Now that tests exist (Wave 0), implement MINIMUM infrastructure to make them pass:
|
||||||
|
1. docker-compose.yml with `user:` directive
|
||||||
|
2. Dockerfile with `USER` directive
|
||||||
|
3. Run tests to verify GREEN phase
|
||||||
|
4. Do NOT over-engineer - minimum to pass tests is sufficient
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto" tdd="true">
|
||||||
|
<name>Task 1: Create Dockerfile with non-root user</name>
|
||||||
|
<files>labs/lab-01-iam/Dockerfile</files>
|
||||||
|
<behavior>
|
||||||
|
- Base image: alpine:3.19 (small, secure)
|
||||||
|
- Creates non-root user with UID/GID 1000
|
||||||
|
- Switches to non-root user with USER directive
|
||||||
|
- CMD demonstrates non-root execution with whoami
|
||||||
|
- Follows INF-01 requirement (no root execution)
|
||||||
|
</behavior>
|
||||||
|
<action>
|
||||||
|
Create Dockerfile that implements non-root container execution:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# Lab 01 - IAM & Sicurezza
|
||||||
|
# Dockerfile per container non-root (INF-01 requirement)
|
||||||
|
#
|
||||||
|
# Questo Dockerfile dimostra come creare un container che gira
|
||||||
|
# come utente non-root, seguendo il principio del minimo privilegio.
|
||||||
|
|
||||||
|
FROM alpine:3.19
|
||||||
|
|
||||||
|
# Label per metadata
|
||||||
|
LABEL maintainer="Lab 01 - IAM & Sicurezza"
|
||||||
|
LABEL description="Container non-root per dimostrare permessi IAM"
|
||||||
|
LABEL version="1.0"
|
||||||
|
|
||||||
|
# Crea utente non-root con UID/GID specifici per consistenza
|
||||||
|
# UID 1000 e GID 1000 sono comuni per utenti non-root
|
||||||
|
RUN addgroup -g 1000 labuser && \
|
||||||
|
adduser -D -u 1000 -G labuser labuser
|
||||||
|
|
||||||
|
# Imposta la working directory (creata con adduser -D)
|
||||||
|
WORKDIR /home/labuser
|
||||||
|
|
||||||
|
# Crea un file semplice per verificare i permessi di scrittura
|
||||||
|
RUN echo "Questo file e stato creato durante la build" > /home/labuser/test.txt && \
|
||||||
|
chown labuser:labuser /home/labuser/test.txt
|
||||||
|
|
||||||
|
# PASSA A UTENTE NON-ROOT PRIMA DI QUALSIASI OPERAZIONE
|
||||||
|
# Questo e il punto chiave per INF-01: nessun processo gira come root
|
||||||
|
USER labuser
|
||||||
|
|
||||||
|
# Verifica che il container gira come utente non-root
|
||||||
|
CMD ["sh", "-c", "\
|
||||||
|
echo '========================================' && \
|
||||||
|
echo 'Lab 01 - IAM & Sicurezza' && \
|
||||||
|
echo 'Container non-root verification (INF-01)' && \
|
||||||
|
echo '========================================' && \
|
||||||
|
echo '' && \
|
||||||
|
echo 'Utente corrente:' && \
|
||||||
|
whoami && \
|
||||||
|
echo '' && \
|
||||||
|
echo 'UID:' && \
|
||||||
|
id -u && \
|
||||||
|
echo '' && \
|
||||||
|
echo 'GID:' && \
|
||||||
|
id -g && \
|
||||||
|
echo '' && \
|
||||||
|
echo 'Gruppi:' && \
|
||||||
|
groups && \
|
||||||
|
echo '' && \
|
||||||
|
echo 'Home directory:' && \
|
||||||
|
pwd && \
|
||||||
|
echo '' && \
|
||||||
|
echo 'Contenuto di test.txt (permessi scrittura):' && \
|
||||||
|
cat test.txt && \
|
||||||
|
echo '' && \
|
||||||
|
echo '========================================' && \
|
||||||
|
echo 'Se vedi \"labuser\" sopra, INF-01 e soddisfatto!' && \
|
||||||
|
echo '========================================' && \
|
||||||
|
echo '' && \
|
||||||
|
echo 'Container in esecuzione. Premi Ctrl-C per uscire.' && \
|
||||||
|
sleep 3600 \
|
||||||
|
"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Key implementation points:
|
||||||
|
- Base image Alpine 3.19 (minimal, secure)
|
||||||
|
- Creates `labuser` with UID/GID 1000
|
||||||
|
- USER directive switches to non-root BEFORE CMD
|
||||||
|
- CMD demonstrates and verifies non-root execution
|
||||||
|
- Follows INF-01 requirement strictly
|
||||||
|
- Labels for metadata and documentation
|
||||||
|
- Working directory set to user's home
|
||||||
|
- Test file to verify write permissions
|
||||||
|
|
||||||
|
TDD Context: This is the GREEN phase - tests already exist (from Wave 0), this Dockerfile should make those tests pass.
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>cd labs/lab-01-iam && docker build -t lab01-non-root . && docker run --rm lab01-non-root | grep -q "labuser" && echo "PASS: Container runs as non-root" || echo "FAIL: Container not running as labuser"</automated>
|
||||||
|
</verify>
|
||||||
|
<done>Dockerfile creates non-root container verified by whoami output</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto" tdd="true">
|
||||||
|
<name>Task 2: Create docker-compose.yml with user directive</name>
|
||||||
|
<files>labs/lab-01-iam/docker-compose.yml</files>
|
||||||
|
<behavior>
|
||||||
|
- Defines service with local image build
|
||||||
|
- Specifies user directive (1000:1000) for non-root execution
|
||||||
|
- Includes container_name for easy reference
|
||||||
|
- Follows INF-01 requirement (no root)
|
||||||
|
- Enables test scripts to verify configuration
|
||||||
|
</behavior>
|
||||||
|
<action>
|
||||||
|
Create docker-compose.yml that enforces non-root execution:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Lab 01 - IAM & Sicurezza
|
||||||
|
# Docker Compose configuration per container non-root
|
||||||
|
#
|
||||||
|
# Questo file definisce i servizi per il lab, assicurandosi che
|
||||||
|
# TUTTI i container girino come utente non-root (INF-01).
|
||||||
|
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
# Container di test per verificare l'esecuzione non-root
|
||||||
|
lab01-test:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
image: lab01-non-root:latest
|
||||||
|
container_name: lab01-iam-test
|
||||||
|
# CRITICO: user directive assicura esecuzione non-root (INF-01)
|
||||||
|
# Format: UID:GID
|
||||||
|
# 1000:1000 corrisponde all'utente labuser creato nel Dockerfile
|
||||||
|
user: "1000:1000"
|
||||||
|
# Non esponiamo porte (non necessario per questo lab)
|
||||||
|
# Le porte private non devono essere esposte sull'host (best practice)
|
||||||
|
restart: unless-stopped
|
||||||
|
# Nessun volume mount necessario per questo lab semplice
|
||||||
|
# I volumi saranno introdotti nei lab successivi
|
||||||
|
healthcheck:
|
||||||
|
# Healthcheck per verificare che il container sia sano
|
||||||
|
test: ["CMD", "sh", "-c", "whoami | grep -q labuser"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 5s
|
||||||
|
|
||||||
|
# Nessuna rete definita - useremo la default bridge network
|
||||||
|
# Le reti custom isolate saranno introdotte nel Lab 02 (Network & VPC)
|
||||||
|
|
||||||
|
# Nessun volume definito - i volumi saranno introdotti nel Lab 04 (Storage & S3)
|
||||||
|
```
|
||||||
|
|
||||||
|
Key implementation points:
|
||||||
|
- Service definition with local build context
|
||||||
|
- `user: "1000:1000"` directive enforces non-root execution
|
||||||
|
- Container name matches test expectations
|
||||||
|
- Healthcheck verifies non-root user
|
||||||
|
- Comments explain why no volumes/networks (future labs)
|
||||||
|
- Follows docker-compose V3.8 syntax
|
||||||
|
- No ports exposed (security best practice)
|
||||||
|
|
||||||
|
TDD Context: Tests from Wave 0 check for user directive - this configuration should satisfy those tests.
|
||||||
|
|
||||||
|
INF-01 Compliance:
|
||||||
|
- User directive explicitly set
|
||||||
|
- Healthcheck verifies non-root execution
|
||||||
|
- No possibility of root execution
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>cd labs/lab-01-iam && docker-compose config > /dev/null 2>&1 && echo "PASS: docker-compose.yml is valid" || echo "FAIL: docker-compose.yml has errors"</automated>
|
||||||
|
</verify>
|
||||||
|
<done>docker-compose.yml defines service with non-root user directive</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto" tdd="true">
|
||||||
|
<name>Task 3: Create infrastructure verification script</name>
|
||||||
|
<files>labs/lab-01-iam/tests/04-verify-infrastructure.sh</files>
|
||||||
|
<behavior>
|
||||||
|
- Test 1: docker-compose.yml is valid YAML
|
||||||
|
- Test 2: Dockerfile builds successfully
|
||||||
|
- Test 3: Service has user directive set
|
||||||
|
- Test 4: Built container runs as non-root
|
||||||
|
- Test 5: All INF-01 requirements satisfied
|
||||||
|
</behavior>
|
||||||
|
<action>
|
||||||
|
Create infrastructure verification script (TDD GREEN phase verification):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Infrastructure Verification: Lab 01
|
||||||
|
# Verifies that docker-compose.yml and Dockerfile satisfy all requirements
|
||||||
|
# This is the GREEN phase check - tests should pass after infrastructure implementation
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "$TEST_DIR/../.." && pwd)"
|
||||||
|
LAB_DIR="$PROJECT_ROOT/labs/lab-01-iam"
|
||||||
|
|
||||||
|
cd "$LAB_DIR"
|
||||||
|
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo -e "${BLUE}Lab 01 Infrastructure Verification${NC}"
|
||||||
|
echo -e "${BLUE}GREEN Phase Check${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
pass_count=0
|
||||||
|
fail_count=0
|
||||||
|
|
||||||
|
# Test 1: docker-compose.yml is valid
|
||||||
|
echo -e "${BLUE}[1/6] Checking docker-compose.yml syntax...${NC}"
|
||||||
|
if docker-compose config >/dev/null 2>&1; then
|
||||||
|
echo -e " ${GREEN}✓${NC} docker-compose.yml is valid YAML"
|
||||||
|
((pass_count++))
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} docker-compose.yml has syntax errors"
|
||||||
|
((fail_count++))
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 2: Dockerfile exists and is readable
|
||||||
|
echo -e "${BLUE}[2/6] Checking Dockerfile...${NC}"
|
||||||
|
if [ -f "Dockerfile" ]; then
|
||||||
|
echo -e " ${GREEN}✓${NC} Dockerfile exists"
|
||||||
|
|
||||||
|
# Check for USER directive
|
||||||
|
if grep -q "^USER" Dockerfile; then
|
||||||
|
user_line=$(grep "^USER" Dockerfile)
|
||||||
|
echo -e " ${GREEN}✓${NC} USER directive found: $user_line"
|
||||||
|
((pass_count++))
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} No USER directive found in Dockerfile"
|
||||||
|
((fail_count++))
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} Dockerfile not found"
|
||||||
|
((fail_count++))
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 3: docker-compose.yml has user directive
|
||||||
|
echo -e "${BLUE}[3/6] Checking docker-compose.yml user directive...${NC}"
|
||||||
|
if grep -q "user:" docker-compose.yml; then
|
||||||
|
user_value=$(grep "user:" docker-compose.yml | head -1 | sed 's/.*user: *//' | tr -d '"')
|
||||||
|
echo -e " ${GREEN}✓${NC} user directive found: $user_value"
|
||||||
|
|
||||||
|
# Verify it's not root (0:0)
|
||||||
|
if [[ "$user_value" != *"0:0"* ]] && [[ "$user_value" != *"root"* ]]; then
|
||||||
|
echo -e " ${GREEN}✓${NC} User is not root (INF-01 compliant)"
|
||||||
|
((pass_count++))
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} User is root (INF-01 violation)"
|
||||||
|
((fail_count++))
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} No user directive in docker-compose.yml"
|
||||||
|
((fail_count++))
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 4: Build Docker image
|
||||||
|
echo -e "${BLUE}[4/6] Building Docker image...${NC}"
|
||||||
|
if docker build -t lab01-non-root -q Dockerfile >/dev/null 2>&1; then
|
||||||
|
echo -e " ${GREEN}✓${NC} Docker image built successfully"
|
||||||
|
((pass_count++))
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} Docker image build failed"
|
||||||
|
((fail_count++))
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 5: Verify container runs as non-root
|
||||||
|
echo -e "${BLUE}[5/6] Verifying non-root execution...${NC}"
|
||||||
|
if docker run --rm lab01-non-root whoami 2>/dev/null | grep -q "labuser"; then
|
||||||
|
echo -e " ${GREEN}✓${NC} Container runs as non-root user (labuser)"
|
||||||
|
((pass_count++))
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} Container not running as labuser"
|
||||||
|
((fail_count++))
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 6: Verify docker-compose service
|
||||||
|
echo -e "${BLUE}[6/6] Verifying docker-compose service...${NC}"
|
||||||
|
# Start container in detached mode
|
||||||
|
if docker-compose up -d >/dev/null 2>&1; then
|
||||||
|
echo -e " ${GREEN}✓${NC} docker-compose service started"
|
||||||
|
|
||||||
|
# Wait for container to be ready
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# Check container is running
|
||||||
|
if docker ps --format "{{.Names}}" | grep -q "^lab01-iam-test$"; then
|
||||||
|
echo -e " ${GREEN}✓${NC} Container is running"
|
||||||
|
|
||||||
|
# Verify user
|
||||||
|
actual_user=$(docker exec lab01-iam-test whoami 2>/dev/null || echo "unknown")
|
||||||
|
if [ "$actual_user" = "labuser" ]; then
|
||||||
|
echo -e " ${GREEN}✓${NC} docker-compose container runs as non-root"
|
||||||
|
((pass_count++))
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} docker-compose container running as $actual_user (expected labuser)"
|
||||||
|
((fail_count++))
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} Container not running"
|
||||||
|
((fail_count++))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
docker-compose down --volumes >/dev/null 2>&1
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} Failed to start docker-compose service"
|
||||||
|
((fail_count++))
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo -e "${BLUE}Verification Summary${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo "Passed: $pass_count/6"
|
||||||
|
echo "Failed: $fail_count/6"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ $fail_count -eq 0 ]; then
|
||||||
|
echo -e "${GREEN}All checks passed!${NC}"
|
||||||
|
echo -e "${GREEN}GREEN phase complete - infrastructure satisfies tests${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Next: Run full test suite"
|
||||||
|
echo " bash labs/lab-01-iam/tests/run-all-tests.sh"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo -e "${RED}Some checks failed${NC}"
|
||||||
|
echo -e "${RED}Infrastructure needs fixes before tests will pass${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
Key implementation points:
|
||||||
|
- Validates docker-compose.yml syntax
|
||||||
|
- Verifies USER directive in Dockerfile
|
||||||
|
- Verifies user directive in docker-compose.yml
|
||||||
|
- Builds and tests Docker image
|
||||||
|
- Starts container with docker-compose and verifies execution
|
||||||
|
- Proper cleanup after testing
|
||||||
|
- Clear pass/fail indicators
|
||||||
|
|
||||||
|
TDD Context: This script confirms the GREEN phase - infrastructure implementation makes tests pass.
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>chmod +x labs/lab-01-iam/tests/04-verify-infrastructure.sh && cd labs/lab-01-iam && bash ../tests/04-verify-infrastructure.sh</automated>
|
||||||
|
</verify>
|
||||||
|
<done>Infrastructure verification script confirms all requirements satisfied</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
1. Dockerfile creates non-root user with USER directive
|
||||||
|
2. docker-compose.yml specifies user directive for service
|
||||||
|
3. docker-compose config validates without errors
|
||||||
|
4. Docker build succeeds without warnings
|
||||||
|
5. Container execution verified as non-root (whoami, docker inspect, docker top)
|
||||||
|
6. All Wave 0 tests now pass (GREEN phase of TDD)
|
||||||
|
7. INF-01 requirement satisfied: no container runs as root
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
1. Dockerfile follows non-root best practices from RESEARCH.md
|
||||||
|
2. docker-compose.yml enforces non-root execution via user directive
|
||||||
|
3. Infrastructure verification confirms all requirements met
|
||||||
|
4. Tests from Wave 0 (02-01-PLAN.md) now pass
|
||||||
|
5. LAB-01 requirement satisfied: students can configure users and Docker permissions
|
||||||
|
6. INF-01 requirement satisfied: no container runs as root
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.planning/phases/02-lab-01-iam-sicurezza/02-03-SUMMARY.md`
|
||||||
|
</output>
|
||||||
Reference in New Issue
Block a user