26 KiB
26 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, user_setup, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | user_setup | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 02-lab-01-iam-sicurezza | 01 | execute | 0 |
|
true |
|
|
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.
<execution_context> @/home/luca/.claude/get-shit-done/workflows/execute-plan.md @/home/luca/.claude/get-shit-done/templates/summary.md </execution_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.mdKey patterns from RESEARCH.md
TDD Methodology for Infrastructure
# 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
# 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
groupscommand 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
#!/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
groupscommand 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) chmod +x labs/lab-01-iam/tests/test-01-user-creation.sh && bash labs/lab-01-iam/tests/test-01-user-creation.sh Script exists, is executable, and tests user/group creation behavior
#!/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 chmod +x labs/lab-01-iam/tests/test-02-docker-access.sh && bash labs/lab-01-iam/tests/test-02-docker-access.sh Script validates Docker socket access control mechanisms
#!/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 chmod +x labs/lab-01-iam/tests/03-non-root-test.sh && bash labs/lab-01-iam/tests/03-non-root-test.sh Script verifies INF-01: no container runs as root
#!/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) chmod +x labs/lab-01-iam/tests/99-final-verification.sh && bash labs/lab-01-iam/tests/99-final-verification.sh Student can run single command to verify all lab requirements are met
#!/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) chmod +x labs/lab-01-iam/tests/run-all-tests.sh && bash labs/lab-01-iam/tests/run-all-tests.sh Orchestration script runs all tests and provides summary
<success_criteria>
- Test infrastructure is in place before any implementation (Wave 0 complete)
- All requirement IDs (TEST-01, TEST-05, INF-01) have test coverage
- Tests follow bash scripting best practices (set -euo pipefail, proper exit codes)
- Student can run individual tests or full suite
- Final verification script provides clear pass/fail report for all lab requirements </success_criteria>