feat(lab-02): complete Phase 3 - Network & VPC lab
Implement Lab 02 with Docker bridge networks simulating VPC/Subnets. Test Infrastructure (RED phase): - 6 bash test scripts for network creation, isolation, INF-02 compliance - Fail-fast orchestration with run-all-tests.sh - Quick validation script for development Documentation (Diátaxis framework): - 3 tutorials: VPC creation, container deployment, isolation verification - 4 how-to guides: create network, inspect config, test isolation, cleanup - 3 reference docs: Docker network commands, Compose syntax, VPC mapping - 1 explanation: Docker ↔ VPC parallels (PARA-01/02/03/04) Infrastructure (GREEN phase): - docker-compose.yml with VPC networks (10.0.1.0/24, 10.0.2.0/24) - 5 services: web, app, db, test-public, test-private - INF-02 compliant: 127.0.0.1 bindings only, no 0.0.0.0 - Private network with --internal flag - Multi-homed app container (public + private networks) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
194
labs/lab-02-network/tests/01-network-creation-test.sh
Executable file
194
labs/lab-02-network/tests/01-network-creation-test.sh
Executable file
@@ -0,0 +1,194 @@
|
||||
#!/bin/bash
|
||||
# Test 01: Network Creation Validation
|
||||
# Validates Docker bridge network creation with custom subnets (VPC simulation)
|
||||
# Usage: bash labs/lab-02-network/tests/01-network-creation-test.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Get script directory
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$TEST_DIR/../.." && pwd)"
|
||||
|
||||
# Counter helpers to handle set -e
|
||||
pass_count=0
|
||||
fail_count=0
|
||||
skip_count=0
|
||||
|
||||
inc_pass() { ((pass_count++)) || true; }
|
||||
inc_fail() { ((fail_count++)) || true; }
|
||||
inc_skip() { ((skip_count++)) || true; }
|
||||
|
||||
# Test helper functions
|
||||
print_header() {
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BLUE}$1${NC}"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
|
||||
}
|
||||
|
||||
print_test() {
|
||||
echo -e "\n${BLUE}[TEST]${NC} $1"
|
||||
}
|
||||
|
||||
print_pass() {
|
||||
echo -e "${GREEN}[PASS]${NC} $1"
|
||||
inc_pass
|
||||
}
|
||||
|
||||
print_fail() {
|
||||
echo -e "${RED}[FAIL]${NC} $1"
|
||||
inc_fail
|
||||
}
|
||||
|
||||
print_skip() {
|
||||
echo -e "${YELLOW}[SKIP]${NC} $1"
|
||||
inc_skip
|
||||
}
|
||||
|
||||
# Cleanup function
|
||||
cleanup() {
|
||||
echo -e "\n${BLUE}[*] Cleaning up test networks...${NC}"
|
||||
docker network rm test-vpc-public 2>/dev/null || true
|
||||
docker network rm test-vpc-private 2>/dev/null || true
|
||||
docker network rm test-net1 2>/dev/null || true
|
||||
docker network rm test-net2 2>/dev/null || true
|
||||
echo -e "${GREEN}[✓] Cleanup complete${NC}"
|
||||
}
|
||||
|
||||
# Set trap for cleanup
|
||||
trap cleanup EXIT
|
||||
|
||||
# Start testing
|
||||
print_header "Lab 02 - Test 01: Network Creation Validation"
|
||||
|
||||
# Test 1: Verify Docker is available
|
||||
print_test "Test 1: Verify Docker is available"
|
||||
if command -v docker &> /dev/null; then
|
||||
print_pass "Docker command is available"
|
||||
docker --version
|
||||
else
|
||||
print_fail "Docker command not found. Please install Docker."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 2: Create public network with custom subnet (VPC simulation)
|
||||
print_test "Test 2: Create public network with custom subnet (10.0.1.0/24)"
|
||||
if docker network create --driver bridge --subnet 10.0.1.0/24 --gateway 10.0.1.1 test-vpc-public &> /dev/null; then
|
||||
print_pass "Created public network test-vpc-public with subnet 10.0.1.0/24"
|
||||
docker network inspect test-vpc-public --format='Subnet: {{range .IPAM.Config}}{{.Subnet}}{{end}}'
|
||||
else
|
||||
print_fail "Failed to create public network with custom subnet"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 3: Create private network with --internal flag
|
||||
print_test "Test 3: Create private network with --internal flag (10.0.2.0/24)"
|
||||
if docker network create --driver bridge --internal --subnet 10.0.2.0/24 --gateway 10.0.2.1 test-vpc-private &> /dev/null; then
|
||||
print_pass "Created private network test-vpc-private with --internal flag"
|
||||
docker network inspect test-vpc-private --format='Internal: {{.Internal}}'
|
||||
else
|
||||
print_fail "Failed to create private network with --internal flag"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 4: Verify networks appear in docker network ls
|
||||
print_test "Test 4: Verify networks appear in docker network ls"
|
||||
public_found=$(docker network ls --format '{{.Name}}' | grep -c "^test-vpc-public$" || true)
|
||||
private_found=$(docker network ls --format '{{.Name}}' | grep -c "^test-vpc-private$" || true)
|
||||
|
||||
if [[ $public_found -eq 1 && $private_found -eq 1 ]]; then
|
||||
print_pass "Both test-vpc-public and test-vpc-private found in network list"
|
||||
docker network ls --filter "name=test-vpc-" --format "table {{.Name}}\t{{.Driver}}\t{{.Scope}}"
|
||||
else
|
||||
print_fail "Networks not found in list. public_found=$public_found, private_found=$private_found"
|
||||
fi
|
||||
|
||||
# Test 5: Verify network inspection shows correct subnet
|
||||
print_test "Test 5: Verify network inspection shows correct subnet configuration"
|
||||
public_subnet=$(docker network inspect test-vpc-public --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' 2>/dev/null || echo "")
|
||||
private_subnet=$(docker network inspect test-vpc-private --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' 2>/dev/null || echo "")
|
||||
|
||||
if [[ "$public_subnet" == "10.0.1.0/24" && "$private_subnet" == "10.0.2.0/24" ]]; then
|
||||
print_pass "Network subnets correctly configured"
|
||||
echo " Public: $public_subnet"
|
||||
echo " Private: $private_subnet"
|
||||
else
|
||||
print_fail "Network subnets mismatch. public=$public_subnet (expected 10.0.1.0/24), private=$private_subnet (expected 10.0.2.0/24)"
|
||||
fi
|
||||
|
||||
# Test 6: Verify private network has internal flag set
|
||||
print_test "Test 6: Verify private network has --internal flag set"
|
||||
internal_flag=$(docker network inspect test-vpc-private --format '{{.Internal}}' 2>/dev/null || echo "false")
|
||||
|
||||
if [[ "$internal_flag" == "true" ]]; then
|
||||
print_pass "Private network correctly marked as internal (no external access)"
|
||||
else
|
||||
print_fail "Private network internal flag not set (value: $internal_flag)"
|
||||
fi
|
||||
|
||||
# Test 7: Verify bridge driver is used
|
||||
print_test "Test 7: Verify bridge driver is used for both networks"
|
||||
public_driver=$(docker network inspect test-vpc-public --format '{{.Driver}}' 2>/dev/null || echo "")
|
||||
private_driver=$(docker network inspect test-vpc-private --format '{{.Driver}}' 2>/dev/null || echo "")
|
||||
|
||||
if [[ "$public_driver" == "bridge" && "$private_driver" == "bridge" ]]; then
|
||||
print_pass "Both networks use bridge driver"
|
||||
else
|
||||
print_fail "Driver mismatch. public=$public_driver, private=$private_driver (expected: bridge)"
|
||||
fi
|
||||
|
||||
# Test 8: Check if docker-compose.yml exists (optional - may not exist yet in RED phase)
|
||||
print_test "Test 8: Check if lab docker-compose.yml exists"
|
||||
COMPOSE_FILE="$PROJECT_ROOT/labs/lab-02-network/docker-compose.yml"
|
||||
if [[ -f "$COMPOSE_FILE" ]]; then
|
||||
print_pass "docker-compose.yml found at $COMPOSE_FILE"
|
||||
|
||||
# Test 9: Verify docker-compose config is valid
|
||||
print_test "Test 9: Validate docker-compose.yml syntax"
|
||||
if docker compose -f "$COMPOSE_FILE" config &> /dev/null; then
|
||||
print_pass "docker-compose.yml is valid YAML"
|
||||
else
|
||||
print_fail "docker-compose.yml has syntax errors"
|
||||
fi
|
||||
else
|
||||
print_skip "docker-compose.yml not found yet (expected in RED phase - will be created in GREEN phase)"
|
||||
fi
|
||||
|
||||
# Test 10: Verify networks can be removed
|
||||
print_test "Test 10: Verify test networks can be removed"
|
||||
# This will be handled by cleanup trap, but we verify the command works
|
||||
if docker network rm test-vpc-public test-vpc-private &> /dev/null; then
|
||||
print_pass "Test networks successfully removed"
|
||||
|
||||
# Recreate for cleanup trap
|
||||
docker network create --driver bridge --subnet 10.0.1.0/24 --gateway 10.0.1.1 test-vpc-public &> /dev/null
|
||||
docker network create --driver bridge --internal --subnet 10.0.2.0/24 --gateway 10.0.2.1 test-vpc-private &> /dev/null
|
||||
else
|
||||
print_fail "Failed to remove test networks"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
print_header "Test Summary"
|
||||
echo -e "Total tests run: $((pass_count + fail_count + skip_count))"
|
||||
echo -e "${GREEN}Passed: $pass_count${NC}"
|
||||
if [[ $fail_count -gt 0 ]]; then
|
||||
echo -e "${RED}Failed: $fail_count${NC}"
|
||||
fi
|
||||
if [[ $skip_count -gt 0 ]]; then
|
||||
echo -e "${YELLOW}Skipped: $skip_count${NC}"
|
||||
fi
|
||||
|
||||
# Exit with error code if any tests failed
|
||||
if [[ $fail_count -gt 0 ]]; then
|
||||
echo -e "\n${RED}Some tests failed. Please review the output above.${NC}"
|
||||
exit 1
|
||||
else
|
||||
echo -e "\n${GREEN}All tests passed! Network creation is working correctly.${NC}"
|
||||
exit 0
|
||||
fi
|
||||
260
labs/lab-02-network/tests/02-isolation-verification-test.sh
Executable file
260
labs/lab-02-network/tests/02-isolation-verification-test.sh
Executable file
@@ -0,0 +1,260 @@
|
||||
#!/bin/bash
|
||||
# Test 02: Isolation Verification
|
||||
# Validates network isolation between Docker bridge networks
|
||||
# Usage: bash labs/lab-02-network/tests/02-isolation-verification-test.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Get script directory
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Counter helpers
|
||||
pass_count=0
|
||||
fail_count=0
|
||||
skip_count=0
|
||||
|
||||
inc_pass() { ((pass_count++)) || true; }
|
||||
inc_fail() { ((fail_count++)) || true; }
|
||||
inc_skip() { ((skip_count++)) || true; }
|
||||
|
||||
# Test helper functions
|
||||
print_header() {
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BLUE}$1${NC}"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
|
||||
}
|
||||
|
||||
print_test() {
|
||||
echo -e "\n${BLUE}[TEST]${NC} $1"
|
||||
}
|
||||
|
||||
print_pass() {
|
||||
echo -e "${GREEN}[PASS]${NC} $1"
|
||||
inc_pass
|
||||
}
|
||||
|
||||
print_fail() {
|
||||
echo -e "${RED}[FAIL]${NC} $1"
|
||||
inc_fail
|
||||
}
|
||||
|
||||
print_skip() {
|
||||
echo -e "${YELLOW}[SKIP]${NC} $1"
|
||||
inc_skip
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
# Cleanup function
|
||||
cleanup() {
|
||||
echo -e "\n${BLUE}[*] Cleaning up test containers and networks...${NC}"
|
||||
|
||||
# Stop and remove containers
|
||||
for container in c1 c2 c3 c4; do
|
||||
docker stop "$container" 2>/dev/null || true
|
||||
docker rm "$container" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# Remove networks
|
||||
for network in test-net1 test-net2 test-isolated-net; do
|
||||
docker network rm "$network" 2>/dev/null || true
|
||||
done
|
||||
|
||||
echo -e "${GREEN}[✓] Cleanup complete${NC}"
|
||||
}
|
||||
|
||||
# Set trap for cleanup
|
||||
trap cleanup EXIT
|
||||
|
||||
# Start testing
|
||||
print_header "Lab 02 - Test 02: Network Isolation Verification"
|
||||
|
||||
# Test 1: Create two isolated networks
|
||||
print_test "Test 1: Create two isolated bridge networks (10.0.1.0/24 and 10.0.2.0/24)"
|
||||
if docker network create --driver bridge --subnet 10.0.1.0/24 --gateway 10.0.1.1 test-net1 &> /dev/null && \
|
||||
docker network create --driver bridge --subnet 10.0.2.0/24 --gateway 10.0.2.1 test-net2 &> /dev/null; then
|
||||
print_pass "Created two isolated networks successfully"
|
||||
docker network ls --filter "name=test-net" --format "table {{.Name}}\t{{.Driver}}\t{{.Scope}}"
|
||||
else
|
||||
print_fail "Failed to create test networks"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 2: Create containers in same network
|
||||
print_test "Test 2: Create containers in the same network (test-net1)"
|
||||
if docker run -d --name c1 --network test-net1 --hostname c1 alpine:3.19 sleep 3600 &> /dev/null && \
|
||||
docker run -d --name c2 --network test-net1 --hostname c2 alpine:3.19 sleep 3600 &> /dev/null; then
|
||||
print_pass "Created containers c1 and c2 in test-net1"
|
||||
docker ps --filter "name=c[12]" --format "table {{.Names}}\t{{.Networks}}\t{{.Status}}"
|
||||
else
|
||||
print_fail "Failed to create containers in test-net1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 3: Containers in same network CAN communicate (ping should succeed)
|
||||
print_test "Test 3: Containers in same network can communicate (ping test)"
|
||||
if docker exec c1 ping -c 2 -W 1 c2 &> /dev/null; then
|
||||
print_pass "c1 can successfully ping c2 (same-network communication works)"
|
||||
docker exec c1 ping -c 2 -W 1 c2 | grep "packets transmitted"
|
||||
else
|
||||
print_fail "c1 cannot ping c2 (same-network communication should work)"
|
||||
fi
|
||||
|
||||
# Test 4: Containers in same network can resolve by DNS name
|
||||
print_test "Test 4: Containers in same network can resolve each other by DNS name"
|
||||
if docker exec c1 nslookup c2 &> /dev/null; then
|
||||
print_pass "DNS resolution works within same network"
|
||||
docker exec c1 nslookup c2 | grep "Address" | head -1
|
||||
else
|
||||
print_fail "DNS resolution failed within same network"
|
||||
fi
|
||||
|
||||
# Test 5: Create container in different network
|
||||
print_test "Test 5: Create container c3 in different network (test-net2)"
|
||||
if docker run -d --name c3 --network test-net2 --hostname c3 alpine:3.19 sleep 3600 &> /dev/null; then
|
||||
print_pass "Created container c3 in isolated network test-net2"
|
||||
print_info "c1 and c2 are in test-net1, c3 is in test-net2 (isolated)"
|
||||
else
|
||||
print_fail "Failed to create container c3 in test-net2"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 6: Containers in DIFFERENT networks CANNOT communicate (isolation test)
|
||||
print_test "Test 6: Containers in different networks CANNOT communicate (isolation verification)"
|
||||
print_info "This test EXPECTS ping to FAIL (proves isolation works)"
|
||||
if docker exec c1 ping -c 2 -W 1 c3 &> /dev/null; then
|
||||
print_fail "c1 CAN ping c3 - ISOLATION FAILED! Networks are not isolated!"
|
||||
print_fail "This is a security issue - containers should not reach across networks"
|
||||
else
|
||||
print_pass "c1 CANNOT ping c3 - Network isolation is working correctly!"
|
||||
print_info "This is the expected behavior - isolation prevents cross-network communication"
|
||||
fi
|
||||
|
||||
# Test 7: Cross-network DNS resolution should fail
|
||||
print_test "Test 7: Cross-network DNS resolution should fail"
|
||||
print_info "This test EXPECTS DNS lookup to FAIL (proves DNS isolation)"
|
||||
if docker exec c1 nslookup c3 &> /dev/null; then
|
||||
print_fail "c1 CAN resolve c3 by DNS - DNS isolation FAILED!"
|
||||
else
|
||||
print_pass "c1 CANNOT resolve c3 - DNS isolation is working correctly!"
|
||||
fi
|
||||
|
||||
# Test 8: Create a container connected to both networks (multi-homed)
|
||||
print_test "Test 8: Create multi-homed container c4 connected to BOTH networks"
|
||||
if docker run -d --name c4 --network test-net1 alpine:3.19 sleep 3600 &> /dev/null && \
|
||||
docker network connect test-net2 c4 &> /dev/null; then
|
||||
print_pass "Created container c4 connected to both networks"
|
||||
docker inspect c4 --format 'Networks: {{range $k, $v := .NetworkSettings.Networks}}{{$k}} {{end}}'
|
||||
else
|
||||
print_fail "Failed to create multi-homed container"
|
||||
fi
|
||||
|
||||
# Test 9: Multi-homed container can reach containers in both networks
|
||||
print_test "Test 9: Multi-homed container c4 can reach both c1 (net1) and c3 (net2)"
|
||||
c4_to_c1_result=$(docker exec c4 ping -c 1 -W 1 c1 &> /dev/null && echo "OK" || echo "FAIL")
|
||||
c4_to_c3_result=$(docker exec c4 ping -c 1 -W 1 c3 &> /dev/null && echo "OK" || echo "FAIL")
|
||||
|
||||
if [[ "$c4_to_c1_result" == "OK" && "$c4_to_c3_result" == "OK" ]]; then
|
||||
print_pass "Multi-homed container can reach both networks (c4->c1: OK, c4->c3: OK)"
|
||||
else
|
||||
print_fail "Multi-homed container connectivity issue (c4->c1: $c4_to_c1_result, c4->c3: $c4_to_c3_result)"
|
||||
fi
|
||||
|
||||
# Test 10: Verify isolation still works - c1 cannot ping c3 despite c4 being multi-homed
|
||||
print_test "Test 10: Verify isolation - c1 still cannot reach c3 (despite c4 bridging networks)"
|
||||
if docker exec c1 ping -c 1 -W 1 c3 &> /dev/null; then
|
||||
print_fail "c1 CAN ping c3 - ISOLATION BROKEN! Multi-homing created a bridge!"
|
||||
else
|
||||
print_pass "Isolation maintained - c1 still cannot reach c3 (multi-homing doesn't break isolation)"
|
||||
fi
|
||||
|
||||
# Test 11: Create isolated internal network
|
||||
print_test "Test 11: Create internal network (no external access)"
|
||||
if docker network create --driver bridge --internal --subnet 10.0.10.0/24 test-isolated-net &> /dev/null; then
|
||||
print_pass "Created internal network test-isolated-net"
|
||||
docker network inspect test-isolated-net --format 'Internal: {{.Internal}}'
|
||||
else
|
||||
print_fail "Failed to create internal network"
|
||||
fi
|
||||
|
||||
# Test 12: Verify container in internal network cannot reach external internet
|
||||
print_test "Test 12: Container in internal network cannot reach external internet"
|
||||
docker run -d --name isolated-test --network test-isolated-net alpine:3.19 sleep 3600 &> /dev/null || true
|
||||
|
||||
if docker exec isolated-test ping -c 1 -W 1 8.8.8.8 &> /dev/null; then
|
||||
print_fail "Container in internal network CAN reach internet - internal flag not working!"
|
||||
else
|
||||
print_pass "Container in internal network CANNOT reach internet (isolation works)"
|
||||
print_info "This is expected behavior for --internal flag networks"
|
||||
fi
|
||||
|
||||
# Cleanup isolated test container early
|
||||
docker stop isolated-test &> /dev/null || true
|
||||
docker rm isolated-test &> /dev/null || true
|
||||
|
||||
# Test 13: Verify network IP addresses don't overlap
|
||||
print_test "Test 13: Verify network subnets are properly isolated (no IP overlap)"
|
||||
net1_subnet=$(docker network inspect test-net1 --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' 2>/dev/null || echo "")
|
||||
net2_subnet=$(docker network inspect test-net2 --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' 2>/dev/null || echo "")
|
||||
|
||||
if [[ "$net1_subnet" != "$net2_subnet" ]]; then
|
||||
print_pass "Network subnets are different (no overlap)"
|
||||
echo " test-net1: $net1_subnet"
|
||||
echo " test-net2: $net2_subnet"
|
||||
else
|
||||
print_fail "Network subnets are identical - IP overlap will occur!"
|
||||
fi
|
||||
|
||||
# Test 14: Verify containers have IPs from correct subnets
|
||||
print_test "Test 14: Verify containers have IPs from their network's subnet"
|
||||
c1_ip=$(docker inspect c1 --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 2>/dev/null || echo "")
|
||||
c3_ip=$(docker inspect c3 --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 2>/dev/null || echo "")
|
||||
|
||||
c1_in_net1=$(echo "$c1_ip" | grep -q "^10.0.1." && echo "yes" || echo "no")
|
||||
c3_in_net2=$(echo "$c3_ip" | grep -q "^10.0.2." && echo "yes" || echo "no")
|
||||
|
||||
if [[ "$c1_in_net1" == "yes" && "$c3_in_net2" == "yes" ]]; then
|
||||
print_pass "Containers have IPs from correct subnets"
|
||||
echo " c1 IP: $c1_ip (should be in 10.0.1.0/24)"
|
||||
echo " c3 IP: $c3_ip (should be in 10.0.2.0/24)"
|
||||
else
|
||||
print_fail "Container IPs don't match expected subnets"
|
||||
echo " c1 IP: $c1_ip (expected 10.0.1.x)"
|
||||
echo " c3 IP: $c3_ip (expected 10.0.2.x)"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
print_header "Test Summary"
|
||||
echo -e "Total tests run: $((pass_count + fail_count + skip_count))"
|
||||
echo -e "${GREEN}Passed: $pass_count${NC}"
|
||||
if [[ $fail_count -gt 0 ]]; then
|
||||
echo -e "${RED}Failed: $fail_count${NC}"
|
||||
fi
|
||||
if [[ $skip_count -gt 0 ]]; then
|
||||
echo -e "${YELLOW}Skipped: $skip_count${NC}"
|
||||
fi
|
||||
|
||||
# Isolation verification message
|
||||
echo -e "\n${BLUE}[*] Network Isolation Summary${NC}"
|
||||
echo -e "Same-network communication: ${GREEN}WORKS${NC} (expected)"
|
||||
echo -e "Cross-network communication: ${GREEN}BLOCKED${NC} (isolation working)"
|
||||
echo -e "DNS isolation: ${GREEN}ENFORCED${NC} (cross-network DNS blocked)"
|
||||
echo -e "Internal network isolation: ${GREEN}ENFORCED${NC} (no external access)"
|
||||
|
||||
# Exit with error code if any tests failed
|
||||
if [[ $fail_count -gt 0 ]]; then
|
||||
echo -e "\n${RED}Some tests failed. Please review the output above.${NC}"
|
||||
exit 1
|
||||
else
|
||||
echo -e "\n${GREEN}All tests passed! Network isolation is working correctly.${NC}"
|
||||
exit 0
|
||||
fi
|
||||
272
labs/lab-02-network/tests/03-inf02-compliance-test.sh
Executable file
272
labs/lab-02-network/tests/03-inf02-compliance-test.sh
Executable file
@@ -0,0 +1,272 @@
|
||||
#!/bin/bash
|
||||
# Test 03: INF-02 Compliance Verification
|
||||
# Validates INF-02 requirement: private networks must NOT expose ports on 0.0.0.0
|
||||
# Usage: bash labs/lab-02-network/tests/03-inf02-compliance-test.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Get script directory
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$TEST_DIR/../.." && pwd)"
|
||||
|
||||
# Counter helpers
|
||||
pass_count=0
|
||||
fail_count=0
|
||||
skip_count=0
|
||||
|
||||
inc_pass() { ((pass_count++)) || true; }
|
||||
inc_fail() { ((fail_count++)) || true; }
|
||||
inc_skip() { ((skip_count++)) || true; }
|
||||
|
||||
# Test helper functions
|
||||
print_header() {
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BLUE}$1${NC}"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
|
||||
}
|
||||
|
||||
print_test() {
|
||||
echo -e "\n${BLUE}[TEST]${NC} $1"
|
||||
}
|
||||
|
||||
print_pass() {
|
||||
echo -e "${GREEN}[PASS]${NC} $1"
|
||||
inc_pass
|
||||
}
|
||||
|
||||
print_fail() {
|
||||
echo -e "${RED}[FAIL]${NC} $1"
|
||||
inc_fail
|
||||
}
|
||||
|
||||
print_skip() {
|
||||
echo -e "${YELLOW}[SKIP]${NC} $1"
|
||||
inc_skip
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
# INF-02 Requirement explanation
|
||||
print_header "Lab 02 - Test 03: INF-02 Compliance Verification"
|
||||
echo -e "${BLUE}INF-02 Requirement:${NC} Private networks must NOT expose ports on 0.0.0.0"
|
||||
echo -e "${YELLOW}Allowed:${NC} 127.0.0.1 (localhost only) or no published ports"
|
||||
echo -e "${RED}Forbidden:${NC} 0.0.0.0 (exposes to all network interfaces)"
|
||||
echo -e ""
|
||||
|
||||
# Compose file path
|
||||
COMPOSE_FILE="$PROJECT_ROOT/labs/lab-02-network/docker-compose.yml"
|
||||
|
||||
# Test 1: Verify docker-compose.yml exists
|
||||
print_test "Test 1: Verify docker-compose.yml exists"
|
||||
if [[ -f "$COMPOSE_FILE" ]]; then
|
||||
print_pass "docker-compose.yml found at $COMPOSE_FILE"
|
||||
COMPOSE_EXISTS=true
|
||||
else
|
||||
print_skip "docker-compose.yml not found at $COMPOSE_FILE"
|
||||
print_info "This is expected in RED phase - file will be created in GREEN phase"
|
||||
COMPOSE_EXISTS=false
|
||||
|
||||
# Skip remaining tests if compose file doesn't exist
|
||||
print_header "Test Summary (Early Exit)"
|
||||
echo -e "Total tests: $((pass_count + fail_count + skip_count))"
|
||||
echo -e "${GREEN}Passed: $pass_count${NC}"
|
||||
echo -e "${YELLOW}Skipped: $skip_count${NC}"
|
||||
echo -e "${YELLOW}INF-02 compliance tests skipped - infrastructure not yet created${NC}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Test 2: Verify docker-compose.yml is valid YAML
|
||||
print_test "Test 2: Validate docker-compose.yml syntax"
|
||||
if docker compose -f "$COMPOSE_FILE" config &> /dev/null; then
|
||||
print_pass "docker-compose.yml is valid YAML"
|
||||
else
|
||||
print_fail "docker-compose.yml has syntax errors"
|
||||
print_info "Run 'docker compose -f docker-compose.yml config' to see errors"
|
||||
fi
|
||||
|
||||
# Test 3: Check for 0.0.0.0 port bindings (CRITICAL - must not exist)
|
||||
print_test "Test 3: Check for 0.0.0.0 port bindings (VIOLATES INF-02)"
|
||||
print_info "Searching for pattern: 0.0.0.0:PORT"
|
||||
ZERO_DETECTIONS=$(grep -n -E '0\.0\.0\.0:[0-9]+' "$COMPOSE_FILE" 2>/dev/null || echo "")
|
||||
ZERO_COUNT=$(echo "$ZERO_DETECTIONS" | grep -c "0.0.0" || true)
|
||||
|
||||
if [[ -z "$ZERO_DETECTIONS" ]]; then
|
||||
print_pass "No 0.0.0.0 port bindings found (COMPLIANT with INF-02)"
|
||||
else
|
||||
print_fail "Found $ZERO_COUNT occurrence(s) of 0.0.0.0 port bindings - INF-02 VIOLATION!"
|
||||
echo "$ZERO_DETECTIONS" | while read -r line; do
|
||||
echo -e "${RED} $line${NC}"
|
||||
done
|
||||
print_warning "0.0.0.0 exposes service on ALL network interfaces (security risk)"
|
||||
print_info "Fix: Use '127.0.0.1:PORT:CONTAINER_PORT' for localhost-only access"
|
||||
fi
|
||||
|
||||
# Test 4: Check for host:port format without explicit host (defaults to 0.0.0.0)
|
||||
print_test "Test 4: Check for implicit 0.0.0.0 bindings (e.g., '8080:80' without host)"
|
||||
print_info "Pattern: '- \"PORT:CONTAINER_PORT\"' (defaults to 0.0.0.0:PORT)"
|
||||
|
||||
# Look for port mappings without explicit host (e.g., "8080:80" instead of "127.0.0.1:8080:80")
|
||||
IMPLICIT_ZERO=$(grep -n -E '^\s*-\s*"[0-9]+:[0-9]+' "$COMPOSE_FILE" 2>/dev/null || echo "")
|
||||
IMPLICIT_ZERO_ALT=$(grep -n -E '^\s*ports:\s*$' "$COMPOSE_FILE" -A 5 | grep -E '^\s+-\s*[0-9]+:[0-9]+' || echo "")
|
||||
|
||||
if [[ -z "$IMPLICIT_ZERO" && -z "$IMPLICIT_ZERO_ALT" ]]; then
|
||||
print_pass "No implicit 0.0.0.0 port bindings found"
|
||||
else
|
||||
if [[ -n "$IMPLICIT_ZERO" ]]; then
|
||||
print_fail "Found implicit 0.0.0.0 bindings (format: 'PORT:CONTAINER')"
|
||||
echo "$IMPLICIT_ZERO" | while read -r line; do
|
||||
echo -e "${RED} $line${NC}"
|
||||
done
|
||||
fi
|
||||
if [[ -n "$IMPLICIT_ZERO_ALT" ]]; then
|
||||
print_fail "Found implicit 0.0.0.0 bindings (ports: section)"
|
||||
echo "$IMPLICIT_ZERO_ALT" | while read -r line; do
|
||||
echo -e "${RED} $line${NC}"
|
||||
done
|
||||
fi
|
||||
print_warning "Port format 'PORT:CONTAINER' defaults to 0.0.0.0:PORT"
|
||||
print_info "Fix: Use '127.0.0.1:PORT:CONTAINER_PORT' for localhost-only binding"
|
||||
fi
|
||||
|
||||
# Test 5: Verify 127.0.0.1 bindings are used for private services
|
||||
print_test "Test 5: Verify private services use 127.0.0.1 binding (localhost only)"
|
||||
LOCALHOST_BINDINGS=$(grep -n -E '127\.0\.0\.1:[0-9]+' "$COMPOSE_FILE" 2>/dev/null || echo "")
|
||||
LOCALHOST_COUNT=$(echo "$LOCALHOST_BINDINGS" | grep -c "127.0.0" || true)
|
||||
|
||||
if [[ -n "$LOCALHOST_BINDINGS" ]]; then
|
||||
print_pass "Found $LOCALHOST_COUNT service(s) using 127.0.0.1 binding (secure)"
|
||||
echo "$LOCALHOST_BINDINGS" | while read -r line; do
|
||||
echo -e "${GREEN} $line${NC}"
|
||||
done
|
||||
else
|
||||
print_skip "No 127.0.0.1 bindings found - services may have no published ports (acceptable)"
|
||||
fi
|
||||
|
||||
# Test 6: Check for services with no published ports (most secure)
|
||||
print_test "Test 6: Check for services with no published ports (fully private)"
|
||||
print_info "Services with no 'ports:' section are fully internal (most secure)"
|
||||
|
||||
# Count services
|
||||
TOTAL_SERVICES=$(docker compose -f "$COMPOSE_FILE" config --services 2>/dev/null | wc -l)
|
||||
SERVICES_WITH_PORTS=$(grep -c -E '^\s+[a-z-]+:\s*$' "$COMPOSE_FILE" -A 20 | grep -c "ports:" || echo "0")
|
||||
|
||||
if [[ $TOTAL_SERVICES -gt 0 ]]; then
|
||||
PRIVATE_SERVICES=$((TOTAL_SERVICES - SERVICES_WITH_PORTS))
|
||||
print_pass "Services analysis: $TOTAL_SERVICES total, $SERVICES_WITH_PORTS with exposed ports, $PRIVATE_SERVICES fully private"
|
||||
print_info "Services with no published ports are accessible only within Docker networks"
|
||||
else
|
||||
print_skip "Could not count services"
|
||||
fi
|
||||
|
||||
# Test 7: Verify network configuration uses custom bridge networks
|
||||
print_test "Test 7: Verify custom bridge networks are defined (not default bridge)"
|
||||
NETWORKS_SECTION=$(grep -A 10 "^networks:" "$COMPOSE_FILE" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -n "$NETWORKS_SECTION" ]]; then
|
||||
print_pass "Custom networks section found in docker-compose.yml"
|
||||
echo "$NETWORKS_SECTION" | head -5
|
||||
else
|
||||
print_skip "No custom networks defined (services use default bridge)"
|
||||
fi
|
||||
|
||||
# Test 8: Check for internal flag on private networks
|
||||
print_test "Test 8: Check for 'internal: true' on private networks"
|
||||
INTERNAL_NETWORKS=$(grep -B 5 -E 'internal:\s*true' "$COMPOSE_FILE" 2>/dev/null | grep -E '^\s+[a-z-]+:\s*$' || echo "")
|
||||
|
||||
if [[ -n "$INTERNAL_NETWORKS" ]]; then
|
||||
print_pass "Found internal networks (no external access)"
|
||||
echo "$INTERNAL_NETWORKS" | while read -r line; do
|
||||
echo -e "${GREEN} $line${NC}"
|
||||
done
|
||||
else
|
||||
print_skip "No internal networks found (acceptable - not all services need internal flag)"
|
||||
fi
|
||||
|
||||
# Test 9: Verify no host networking mode
|
||||
print_test "Test 9: Verify services don't use 'network_mode: host' (security risk)"
|
||||
HOST_NETWORK=$(grep -E 'network_mode:\s*host' "$COMPOSE_FILE" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -z "$HOST_NETWORK" ]]; then
|
||||
print_pass "No services using host networking mode"
|
||||
else
|
||||
print_fail "Found 'network_mode: host' - VIOLATES isolation principles!"
|
||||
echo "$HOST_NETWORK" | while read -r line; do
|
||||
echo -e "${RED} $line${NC}"
|
||||
done
|
||||
print_warning "Host networking bypasses Docker network isolation"
|
||||
fi
|
||||
|
||||
# Test 10: Generate INF-02 compliance report
|
||||
print_test "Test 10: Generate INF-02 compliance summary"
|
||||
|
||||
# Collect all issues
|
||||
TOTAL_ISSUES=0
|
||||
if [[ $ZERO_COUNT -gt 0 ]]; then
|
||||
((TOTAL_ISSUES += ZERO_COUNT)) || true
|
||||
fi
|
||||
if [[ -n "$IMPLICIT_ZERO" || -n "$IMPLICIT_ZERO_ALT" ]]; then
|
||||
((TOTAL_ISSUES++)) || true
|
||||
fi
|
||||
if [[ -n "$HOST_NETWORK" ]]; then
|
||||
((TOTAL_ISSUES++)) || true
|
||||
fi
|
||||
|
||||
echo -e "\n${BLUE}[*] INF-02 Compliance Report${NC}"
|
||||
echo "Compose file: $COMPOSE_FILE"
|
||||
echo "Total services: $TOTAL_SERVICES"
|
||||
echo "Services with exposed ports: $SERVICES_WITH_PORTS"
|
||||
echo "Fully private services: $PRIVATE_SERVICES"
|
||||
|
||||
if [[ $TOTAL_ISSUES -eq 0 ]]; then
|
||||
echo -e "\n${GREEN}[✓] INF-02 STATUS: COMPLIANT${NC}"
|
||||
print_pass "No security violations found"
|
||||
echo " - No 0.0.0.0 bindings"
|
||||
echo " - No implicit 0.0.0.0 bindings"
|
||||
echo " - No host networking mode"
|
||||
echo " - $PRIVATE_SERVICES services fully private"
|
||||
else
|
||||
echo -e "\n${RED}[✗] INF-02 STATUS: NON-COMPLIANT${NC}"
|
||||
print_fail "Found $TOTAL_ISSUES compliance issue(s)"
|
||||
echo " - 0.0.0.0 bindings: $ZERO_COUNT"
|
||||
echo " - Implicit bindings: $([[ -n "$IMPLICIT_ZERO" || -n "$IMPLICIT_ZERO_ALT" ]] && echo "yes" || echo "no")"
|
||||
echo " - Host networking: $([[ -n "$HOST_NETWORK" ]] && echo "yes" || echo "no")"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
print_header "Test Summary"
|
||||
echo -e "Total tests run: $((pass_count + fail_count + skip_count))"
|
||||
echo -e "${GREEN}Passed: $pass_count${NC}"
|
||||
if [[ $fail_count -gt 0 ]]; then
|
||||
echo -e "${RED}Failed: $fail_count${NC}"
|
||||
fi
|
||||
if [[ $skip_count -gt 0 ]]; then
|
||||
echo -e "${YELLOW}Skipped: $skip_count${NC}"
|
||||
fi
|
||||
|
||||
# Exit with appropriate code
|
||||
if [[ $fail_count -gt 0 ]]; then
|
||||
echo -e "\n${RED}INF-02 compliance tests FAILED${NC}"
|
||||
echo -e "Please fix the violations above before deploying to production."
|
||||
exit 1
|
||||
elif [[ $TOTAL_ISSUES -gt 0 ]]; then
|
||||
echo -e "\n${YELLOW}INF-02 compliance warnings detected${NC}"
|
||||
echo -e "Consider fixing the issues above for better security posture."
|
||||
exit 0
|
||||
else
|
||||
echo -e "\n${GREEN}All INF-02 compliance tests PASSED${NC}"
|
||||
echo -e "Infrastructure is compliant with security requirements."
|
||||
exit 0
|
||||
fi
|
||||
241
labs/lab-02-network/tests/04-verify-infrastructure.sh
Executable file
241
labs/lab-02-network/tests/04-verify-infrastructure.sh
Executable file
@@ -0,0 +1,241 @@
|
||||
#!/bin/bash
|
||||
# Test 04: Infrastructure Verification
|
||||
# Verifies that docker-compose.yml infrastructure is correctly deployed
|
||||
# Usage: bash labs/lab-02-network/tests/04-verify-infrastructure.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Get script directory
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
|
||||
|
||||
# Counter helpers
|
||||
pass_count=0
|
||||
fail_count=0
|
||||
|
||||
inc_pass() { ((pass_count++)) || true; }
|
||||
inc_fail() { ((fail_count++)) || true; }
|
||||
|
||||
# Helper functions
|
||||
print_header() {
|
||||
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║${NC} ${BOLD}$1${NC}"
|
||||
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
||||
}
|
||||
|
||||
print_test() {
|
||||
echo -e "\n${BLUE}[TEST]${NC} $1"
|
||||
}
|
||||
|
||||
print_pass() {
|
||||
echo -e " ${GREEN}[✓]${NC} $1"
|
||||
inc_pass
|
||||
}
|
||||
|
||||
print_fail() {
|
||||
echo -e " ${RED}[✗]${NC} $1"
|
||||
inc_fail
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e " ${BLUE}[i]${NC} $1"
|
||||
}
|
||||
|
||||
# Main verification
|
||||
print_header "Lab 02 Infrastructure Verification"
|
||||
|
||||
cd "$LAB_DIR"
|
||||
|
||||
# Test 1: docker-compose.yml exists
|
||||
print_test "Verifying docker-compose.yml exists"
|
||||
if [[ -f "docker-compose.yml" ]]; then
|
||||
print_pass "docker-compose.yml found"
|
||||
else
|
||||
print_fail "docker-compose.yml not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 2: docker-compose.yml is valid
|
||||
print_test "Validating docker-compose.yml syntax"
|
||||
if docker compose config &> /dev/null; then
|
||||
print_pass "docker-compose.yml has valid syntax"
|
||||
else
|
||||
print_fail "docker-compose.yml has syntax errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 3: Networks are defined
|
||||
print_test "Verifying VPC networks are defined"
|
||||
if docker compose config --format json 2>/dev/null | grep -q '"networks"'; then
|
||||
print_pass "Networks section found in compose file"
|
||||
|
||||
# Check for vpc-public
|
||||
if docker compose config --format json 2>/dev/null | grep -q '"vpc-public"'; then
|
||||
print_pass " vpc-public network defined"
|
||||
else
|
||||
print_fail " vpc-public network NOT defined"
|
||||
fi
|
||||
|
||||
# Check for vpc-private
|
||||
if docker compose config --format json 2>/dev/null | grep -q '"vpc-private"'; then
|
||||
print_pass " vpc-private network defined"
|
||||
else
|
||||
print_fail " vpc-private network NOT defined"
|
||||
fi
|
||||
else
|
||||
print_fail "No networks defined"
|
||||
fi
|
||||
|
||||
# Test 4: INF-02 compliance check
|
||||
print_test "Checking INF-02 compliance (no 0.0.0.0 bindings)"
|
||||
ZERO_BINDINGS=$(grep -c '0\.0\.0\.0:' docker-compose.yml 2>/dev/null || echo "0")
|
||||
if [[ $ZERO_BINDINGS -eq 0 ]]; then
|
||||
print_pass "No 0.0.0.0 port bindings found (INF-02 compliant)"
|
||||
else
|
||||
print_fail "Found $ZERO_BINDINGS 0.0.0.0 bindings - INF-02 VIOLATION"
|
||||
fi
|
||||
|
||||
# Test 5: Check for 127.0.0.1 bindings
|
||||
print_test "Checking for localhost-only bindings (127.0.0.1)"
|
||||
LOCALHOST_BINDINGS=$(grep -c '127\.0\.0\.1:' docker-compose.yml 2>/dev/null || echo "0")
|
||||
if [[ $LOCALHOST_BINDINGS -gt 0 ]]; then
|
||||
print_pass "Found $LOCALHOST_BINDINGS localhost-only bindings (secure)"
|
||||
else
|
||||
print_info "No 127.0.0.1 bindings - services may have no published ports"
|
||||
fi
|
||||
|
||||
# Test 6: Start services
|
||||
print_test "Starting Docker Compose services"
|
||||
if docker compose up -d &> /dev/null; then
|
||||
print_pass "Services started successfully"
|
||||
sleep 3 # Give services time to start
|
||||
else
|
||||
print_fail "Failed to start services"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 7: Verify containers are running
|
||||
print_test "Verifying containers are running"
|
||||
RUNNING_CONTAINERS=$(docker compose ps --services | wc -l)
|
||||
if [[ $RUNNING_CONTAINERS -ge 3 ]]; then
|
||||
print_pass "Services running: $RUNNING_CONTAINERS containers"
|
||||
docker compose ps
|
||||
else
|
||||
print_fail "Not enough containers running: $RUNNING_CONTAINERS (expected 3+)"
|
||||
fi
|
||||
|
||||
# Test 8: Verify network creation
|
||||
print_test "Verifying VPC networks were created"
|
||||
if docker network ls --format '{{.Name}}' | grep -q "lab02-vpc-public"; then
|
||||
print_pass " lab02-vpc-public network exists"
|
||||
else
|
||||
print_fail " lab02-vpc-public network NOT found"
|
||||
fi
|
||||
|
||||
if docker network ls --format '{{.Name}}' | grep -q "lab02-vpc-private"; then
|
||||
print_pass " lab02-vpc-private network exists"
|
||||
else
|
||||
print_fail " lab02-vpc-private network NOT found"
|
||||
fi
|
||||
|
||||
# Test 9: Verify subnet configuration
|
||||
print_test "Verifying subnet CIDR configuration"
|
||||
PUBLIC_SUBNET=$(docker network inspect lab02-vpc-public --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' 2>/dev/null)
|
||||
PRIVATE_SUBNET=$(docker network inspect lab02-vpc-private --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' 2>/dev/null)
|
||||
|
||||
if [[ "$PUBLIC_SUBNET" == "10.0.1.0/24" ]]; then
|
||||
print_pass " Public subnet: $PUBLIC_SUBNET (correct)"
|
||||
else
|
||||
print_fail " Public subnet: $PUBLIC_SUBNET (expected 10.0.1.0/24)"
|
||||
fi
|
||||
|
||||
if [[ "$PRIVATE_SUBNET" == "10.0.2.0/24" ]]; then
|
||||
print_pass " Private subnet: $PRIVATE_SUBNET (correct)"
|
||||
else
|
||||
print_fail " Private subnet: $PRIVATE_SUBNET (expected 10.0.2.0/24)"
|
||||
fi
|
||||
|
||||
# Test 10: Verify private network isolation
|
||||
print_test "Verifying private network isolation flag"
|
||||
INTERNAL_FLAG=$(docker network inspect lab02-vpc-private --format '{{.Internal}}' 2>/dev/null)
|
||||
if [[ "$INTERNAL_FLAG" == "true" ]]; then
|
||||
print_pass "Private network has internal=true flag (isolated)"
|
||||
else
|
||||
print_fail "Private network missing internal flag"
|
||||
fi
|
||||
|
||||
# Test 11: Verify container network placement
|
||||
print_test "Verifying container network placement"
|
||||
if docker inspect lab02-web --format '{{range .NetworkSettings.Networks}}{{.Network}}{{end}}' 2>/dev/null | grep -q "lab02-vpc-public"; then
|
||||
print_pass " lab02-web in vpc-public network"
|
||||
else
|
||||
print_fail " lab02-web not in vpc-public"
|
||||
fi
|
||||
|
||||
if docker inspect lab02-db --format '{{range .NetworkSettings.Networks}}{{.Network}}{{end}}' 2>/dev/null | grep -q "lab02-vpc-private"; then
|
||||
print_pass " lab02-db in vpc-private network"
|
||||
else
|
||||
print_fail " lab02-db not in vpc-private"
|
||||
fi
|
||||
|
||||
# Test 12: Verify multi-homed container
|
||||
print_test "Verifying multi-homed container (app in both networks)"
|
||||
PUBLIC_IP=$(docker inspect lab02-app --format '{{range .NetworkSettings.Networks}}{{if eq .Network "lab02-vpc-public"}}{{.IPAddress}}{{end}}{{end}}' 2>/dev/null)
|
||||
PRIVATE_IP=$(docker inspect lab02-app --format '{{range .NetworkSettings.Networks}}{{if eq .Network "lab02-vpc-private"}}{{.IPAddress}}{{end}}{{end}}' 2>/dev/null)
|
||||
|
||||
if [[ -n "$PUBLIC_IP" && -n "$PRIVATE_IP" ]]; then
|
||||
print_pass "lab02-app is multi-homed (public: $PUBLIC_IP, private: $PRIVATE_IP)"
|
||||
else
|
||||
print_fail "lab02-app not properly connected to both networks"
|
||||
fi
|
||||
|
||||
# Test 13: Verify web service accessibility
|
||||
print_test "Verifying web service is accessible from localhost"
|
||||
if curl -sf http://127.0.0.1:8080 &> /dev/null; then
|
||||
print_pass "Web service responds on http://127.0.0.1:8080"
|
||||
else
|
||||
print_fail "Web service not accessible on http://127.0.0.1:8080"
|
||||
fi
|
||||
|
||||
# Test 14: Verify database is NOT accessible from host
|
||||
print_test "Verifying database is NOT accessible from host (private)"
|
||||
if curl -sf http://127.0.0.1:5432 &> /dev/null; then
|
||||
print_fail "Database is accessible from host - PRIVATE NETWORK COMPROMISED!"
|
||||
else
|
||||
print_pass "Database is NOT accessible from host (correct - isolated)"
|
||||
fi
|
||||
|
||||
# Test 15: Verify isolation between networks
|
||||
print_test "Verifying cross-network isolation (web cannot reach db)"
|
||||
if docker exec lab02-web ping -c 1 -W 1 lab02-db &> /dev/null; then
|
||||
print_fail "Web CAN reach database - ISOLATION FAILED!"
|
||||
else
|
||||
print_pass "Web CANNOT reach database - isolation working correctly"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
print_header "Infrastructure Verification Summary"
|
||||
|
||||
echo -e "Tests run: $((pass_count + fail_count))"
|
||||
echo -e "${GREEN}Passed: $pass_count${NC}"
|
||||
if [[ $fail_count -gt 0 ]]; then
|
||||
echo -e "${RED}Failed: $fail_count${NC}"
|
||||
fi
|
||||
|
||||
if [[ $fail_count -eq 0 ]]; then
|
||||
echo -e "\n${GREEN}${BOLD}✓ ALL INFRASTRUCTURE CHECKS PASSED${NC}"
|
||||
echo -e "\nInfrastructure is correctly deployed and compliant!"
|
||||
echo -e "You can now proceed with the tutorials."
|
||||
exit 0
|
||||
else
|
||||
echo -e "\n${RED}Some infrastructure checks failed${NC}"
|
||||
echo -e "Please review the failures above."
|
||||
exit 1
|
||||
fi
|
||||
325
labs/lab-02-network/tests/99-final-verification.sh
Executable file
325
labs/lab-02-network/tests/99-final-verification.sh
Executable file
@@ -0,0 +1,325 @@
|
||||
#!/bin/bash
|
||||
# Final Verification: Lab 02 - Network & VPC
|
||||
# Comprehensive end-to-end verification for students (double-check command)
|
||||
# Usage: bash labs/lab-02-network/tests/99-final-verification.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Get script directory
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$TEST_DIR/../.." && pwd)"
|
||||
LAB_DIR="$PROJECT_ROOT/labs/lab-02-network"
|
||||
|
||||
# Counter helpers
|
||||
pass_count=0
|
||||
fail_count=0
|
||||
warn_count=0
|
||||
|
||||
inc_pass() { ((pass_count++)) || true; }
|
||||
inc_fail() { ((fail_count++)) || true; }
|
||||
inc_warn() { ((warn_count++)) || true; }
|
||||
|
||||
# Helper functions
|
||||
print_header() {
|
||||
echo -e "${CYAN}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${CYAN}║${NC} ${BOLD}$1${NC}"
|
||||
echo -e "${CYAN}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
||||
}
|
||||
|
||||
print_section() {
|
||||
echo -e "\n${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${BLUE}$1${NC}"
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
}
|
||||
|
||||
print_pass() {
|
||||
echo -e " ${GREEN}[✓]${NC} $1"
|
||||
inc_pass
|
||||
}
|
||||
|
||||
print_fail() {
|
||||
echo -e " ${RED}[✗]${NC} $1"
|
||||
inc_fail
|
||||
}
|
||||
|
||||
print_warn() {
|
||||
echo -e " ${YELLOW}[!]${NC} $1"
|
||||
inc_warn
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e " ${BLUE}[i]${NC} $1"
|
||||
}
|
||||
|
||||
# Main header
|
||||
clear
|
||||
print_header "Lab 02: Network & VPC - Final Verification"
|
||||
echo ""
|
||||
echo -e "This script verifies your entire Lab 02 implementation."
|
||||
echo -e "Run this after completing all tutorials to double-check your work."
|
||||
echo ""
|
||||
|
||||
# Verify Docker is available
|
||||
print_section "0. Environment Check"
|
||||
if ! command -v docker &> /dev/null; then
|
||||
echo -e "${RED}ERROR: Docker is not installed or not in PATH${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "Docker version: $(docker --version | cut -d' ' -f3)"
|
||||
echo -e "Docker Compose version: $(docker compose version | grep 'Docker Compose' | cut -d' ' -f4)"
|
||||
|
||||
# Test 1: Network Creation
|
||||
print_section "1. Network Creation Verification"
|
||||
|
||||
COMPOSE_FILE="$LAB_DIR/docker-compose.yml"
|
||||
if [[ ! -f "$COMPOSE_FILE" ]]; then
|
||||
print_fail "docker-compose.yml not found at $COMPOSE_FILE"
|
||||
print_info "Expected output of Tutorial 1"
|
||||
FAIL_REASON="compose_missing"
|
||||
else
|
||||
print_pass "docker-compose.yml exists"
|
||||
|
||||
# Validate compose syntax
|
||||
if docker compose -f "$COMPOSE_FILE" config &> /dev/null; then
|
||||
print_pass "docker-compose.yml has valid syntax"
|
||||
else
|
||||
print_fail "docker-compose.yml has syntax errors"
|
||||
print_info "Run: docker compose -f docker-compose.yml config"
|
||||
FAIL_REASON="compose_invalid"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test 2: Network Topology
|
||||
print_section "2. Network Topology Verification"
|
||||
|
||||
if [[ "${FAIL_REASON:-}" == "compose_missing" ]]; then
|
||||
print_warn "Skipping network tests - compose file missing"
|
||||
else
|
||||
# Check for custom networks
|
||||
NETWORKS=$(docker compose -f "$COMPOSE_FILE" config --format json 2>/dev/null | grep -o '"networks"' | wc -l || echo "0")
|
||||
if [[ $NETWORKS -gt 0 ]]; then
|
||||
print_pass "Custom networks defined in docker-compose.yml"
|
||||
|
||||
# List networks
|
||||
echo ""
|
||||
print_info "Networks defined:"
|
||||
docker compose -f "$COMPOSE_FILE" config --format json 2>/dev/null | \
|
||||
grep -A 3 '"networks"' | grep -E '^\s+"[a-z]+"' | sed 's/.*"\([^"]*\)".*/ \1/' || echo " (unable to list)"
|
||||
else
|
||||
print_warn "No custom networks found - using default bridge"
|
||||
fi
|
||||
|
||||
# Check for VPC-style naming (PARA-02 requirement)
|
||||
VPC_NAMES=$(grep -E 'vpc-|subnet-|network-' "$COMPOSE_FILE" 2>/dev/null | wc -l || echo "0")
|
||||
if [[ $VPC_NAMES -gt 0 ]]; then
|
||||
print_pass "Uses VPC-style naming convention (PARA-02 compliant)"
|
||||
else
|
||||
print_warn "VPC-style naming not found (recommended: vpc-main, subnet-public, etc.)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test 3: INF-02 Compliance
|
||||
print_section "3. INF-02 Security Compliance"
|
||||
|
||||
if [[ -f "$COMPOSE_FILE" ]]; then
|
||||
# Check for 0.0.0.0 bindings
|
||||
ZERO_BINDINGS=$(grep -c -E '0\.0\.0\.0:[0-9]+' "$COMPOSE_FILE" 2>/dev/null || echo "0")
|
||||
if [[ $ZERO_BINDINGS -eq 0 ]]; then
|
||||
print_pass "No 0.0.0.0 port bindings found (INF-02 compliant)"
|
||||
else
|
||||
print_fail "Found $ZERO_BINDINGS 0.0.0.0 port bindings - VIOLATES INF-02"
|
||||
print_info "Private networks must not expose ports on 0.0.0.0"
|
||||
FAIL_REASON="inf02_violation"
|
||||
fi
|
||||
|
||||
# Check for localhost bindings
|
||||
LOCALHOST_BINDINGS=$(grep -c -E '127\.0\.0\.1:[0-9]+' "$COMPOSE_FILE" 2>/dev/null || echo "0")
|
||||
if [[ $LOCALHOST_BINDINGS -gt 0 ]]; then
|
||||
print_pass "Found $LOCALHOST_BINDINGS service(s) with 127.0.0.1 binding (secure)"
|
||||
else
|
||||
print_info "No 127.0.0.1 bindings - services may have no published ports"
|
||||
fi
|
||||
|
||||
# Check for host networking
|
||||
HOST_NET=$(grep -c -E 'network_mode:\s*host' "$COMPOSE_FILE" 2>/dev/null || echo "0")
|
||||
if [[ $HOST_NET -eq 0 ]]; then
|
||||
print_pass "No services using host networking mode"
|
||||
else
|
||||
print_fail "Found services using 'network_mode: host' - security risk"
|
||||
FAIL_REASON="host_networking"
|
||||
fi
|
||||
else
|
||||
print_warn "Skipping INF-02 tests - compose file missing"
|
||||
fi
|
||||
|
||||
# Test 4: Service Startup
|
||||
print_section "4. Service Startup Verification"
|
||||
|
||||
if [[ -f "$COMPOSE_FILE" && "${FAIL_REASON:-}" != "compose_invalid" ]]; then
|
||||
print_info "Attempting to start services..."
|
||||
if docker compose -f "$COMPOSE_FILE" up -d &> /dev/null; then
|
||||
print_pass "Services started successfully"
|
||||
|
||||
# List running services
|
||||
echo ""
|
||||
print_info "Running services:"
|
||||
docker compose -f "$COMPOSE_FILE" ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null || \
|
||||
docker compose -f "$COMPOSE_FILE" ps
|
||||
|
||||
# Cleanup
|
||||
print_info "Stopping services..."
|
||||
docker compose -f "$COMPOSE_FILE" down &> /dev/null
|
||||
else
|
||||
print_fail "Services failed to start"
|
||||
print_info "Run: docker compose -f docker-compose.yml up"
|
||||
print_info "Check logs: docker compose -f docker-compose.yml logs"
|
||||
FAIL_REASON="services_failed"
|
||||
fi
|
||||
else
|
||||
print_warn "Skipping service tests - compose file missing or invalid"
|
||||
fi
|
||||
|
||||
# Test 5: Documentation Completeness
|
||||
print_section "5. Documentation Completeness (Diátxis Framework)"
|
||||
|
||||
DOC_COUNT=0
|
||||
DOC_FILES=(
|
||||
"$LAB_DIR/tutorial/01-create-networks.md"
|
||||
"$LAB_DIR/tutorial/02-deploy-containers.md"
|
||||
"$LAB_DIR/tutorial/03-verify-isolation.md"
|
||||
"$LAB_DIR/how-to-guides/*.md"
|
||||
"$LAB_DIR/reference/*.md"
|
||||
"$LAB_DIR/explanation/*.md"
|
||||
)
|
||||
|
||||
for file_pattern in "${DOC_FILES[@]}"; do
|
||||
count=$(ls $file_pattern 2>/dev/null | wc -l)
|
||||
DOC_COUNT=$((DOC_COUNT + count))
|
||||
done
|
||||
|
||||
if [[ $DOC_COUNT -ge 10 ]]; then
|
||||
print_pass "Documentation complete: $DOC_COUNT files found (Framework Diátaxis)"
|
||||
echo ""
|
||||
print_info "Documentation breakdown:"
|
||||
echo -e " Tutorials: $(ls "$LAB_DIR/tutorial"/*.md 2>/dev/null | wc -l)"
|
||||
echo -e " How-to Guides: $(ls "$LAB_DIR/how-to-guides"/*.md 2>/dev/null | wc -l)"
|
||||
echo -e " Reference: $(ls "$LAB_DIR/reference"/*.md 2>/dev/null | wc -l)"
|
||||
echo -e " Explanation: $(ls "$LAB_DIR/explanation"/*.md 2>/dev/null | wc -l)"
|
||||
else
|
||||
print_warn "Documentation incomplete: $DOC_COUNT files found (expected 10+)"
|
||||
print_info "Complete all tutorials and documentation"
|
||||
fi
|
||||
|
||||
# Test 6: Test Infrastructure
|
||||
print_section "6. Test Infrastructure Verification"
|
||||
|
||||
TEST_FILES=(
|
||||
"$TEST_DIR/01-network-creation-test.sh"
|
||||
"$TEST_DIR/02-isolation-verification-test.sh"
|
||||
"$TEST_DIR/03-inf02-compliance-test.sh"
|
||||
"$TEST_DIR/run-all-tests.sh"
|
||||
)
|
||||
|
||||
TESTS_FOUND=0
|
||||
for test_file in "${TEST_FILES[@]}"; do
|
||||
if [[ -f "$test_file" && -x "$test_file" ]]; then
|
||||
((TESTS_FOUND++)) || true
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $TESTS_FOUND -eq ${#TEST_FILES[@]} ]]; then
|
||||
print_pass "All test scripts present and executable"
|
||||
elif [[ $TESTS_FOUND -gt 0 ]]; then
|
||||
print_warn "Some test scripts missing: $TESTS_FOUND/${#TEST_FILES[@]} found"
|
||||
else
|
||||
print_fail "Test infrastructure not found"
|
||||
fi
|
||||
|
||||
# Final Summary
|
||||
print_section "Final Summary"
|
||||
|
||||
echo ""
|
||||
echo -e " ${BOLD}Results:${NC}"
|
||||
echo -e " ${GREEN}✓ Passed:${NC} $pass_count"
|
||||
if [[ $fail_count -gt 0 ]]; then
|
||||
echo -e " ${RED}✗ Failed:${NC} $fail_count"
|
||||
fi
|
||||
if [[ $warn_count -gt 0 ]]; then
|
||||
echo -e " ${YELLOW}! Warnings:${NC} $warn_count"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Overall verdict
|
||||
if [[ $fail_count -eq 0 && $warn_count -eq 0 ]]; then
|
||||
echo -e "${GREEN}${BOLD}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${GREEN}${BOLD}║ ✓ ALL CHECKS PASSED ║${NC}"
|
||||
echo -e "${GREEN}${BOLD}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
echo -e "${GREEN}Your Lab 02 implementation is complete and compliant!${NC}"
|
||||
echo ""
|
||||
echo -e "Next steps:"
|
||||
echo -e " 1. Review the Explanation document to understand VPC parallels"
|
||||
echo -e " 2. Proceed to Phase 4: Lab 03 - Compute & EC2"
|
||||
echo ""
|
||||
exit 0
|
||||
elif [[ $fail_count -eq 0 ]]; then
|
||||
echo -e "${YELLOW}${BOLD}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${YELLOW}${BOLD}║ ! PASSED WITH WARNINGS ║${NC}"
|
||||
echo -e "${YELLOW}${BOLD}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Implementation is functional but has warnings.${NC}"
|
||||
echo ""
|
||||
echo -e "Recommendations:"
|
||||
if [[ $VPC_NAMES -eq 0 ]]; then
|
||||
echo -e " - Consider using VPC-style naming (vpc-main, subnet-public, subnet-private)"
|
||||
fi
|
||||
if [[ $DOC_COUNT -lt 10 ]]; then
|
||||
echo -e " - Complete all documentation files (Diátxis Framework)"
|
||||
fi
|
||||
echo ""
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}${BOLD}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${RED}${BOLD}║ ✗ VERIFICATION FAILED ║${NC}"
|
||||
echo -e "${RED}${BOLD}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
echo -e "${RED}Some checks failed. Please review the issues above.${NC}"
|
||||
echo ""
|
||||
|
||||
# Provide specific guidance
|
||||
case "${FAIL_REASON:-}" in
|
||||
compose_missing)
|
||||
echo -e "${YELLOW}To fix:${NC} Complete Tutorial 1 to create docker-compose.yml"
|
||||
;;
|
||||
compose_invalid)
|
||||
echo -e "${YELLOW}To fix:${NC} Validate and fix docker-compose.yml syntax"
|
||||
echo -e " Run: docker compose -f docker-compose.yml config"
|
||||
;;
|
||||
inf02_violation)
|
||||
echo -e "${YELLOW}To fix:${NC} Replace 0.0.0.0 bindings with 127.0.0.1"
|
||||
echo -e " Change: '8080:80' → '127.0.0.1:8080:80'"
|
||||
;;
|
||||
services_failed)
|
||||
echo -e "${YELLOW}To fix:${NC} Check service logs for errors"
|
||||
echo -e " Run: docker compose -f docker-compose.yml logs"
|
||||
;;
|
||||
*)
|
||||
echo -e "${YELLOW}To fix:${NC} Review the failed items above and complete the tutorials"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo -e "After fixing, run this verification again:"
|
||||
echo -e " ${CYAN}bash labs/lab-02-network/tests/99-final-verification.sh${NC}"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
196
labs/lab-02-network/tests/quick-test.sh
Executable file
196
labs/lab-02-network/tests/quick-test.sh
Executable file
@@ -0,0 +1,196 @@
|
||||
#!/bin/bash
|
||||
# Quick Test: Fast Validation for Development
|
||||
# Runs subset of critical tests for rapid feedback during development
|
||||
# Usage: bash labs/lab-02-network/tests/quick-test.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Get script directory
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$TEST_DIR/../.." && pwd)"
|
||||
|
||||
# Counter helpers
|
||||
pass_count=0
|
||||
fail_count=0
|
||||
|
||||
inc_pass() { ((pass_count++)) || true; }
|
||||
inc_fail() { ((fail_count++)) || true; }
|
||||
|
||||
# Helper functions
|
||||
print_header() {
|
||||
echo -e "${CYAN}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${CYAN}║${NC} ${BOLD}$1${NC}"
|
||||
echo -e "${CYAN}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
||||
}
|
||||
|
||||
print_test() {
|
||||
echo -e "\n${BLUE}[TEST]${NC} $1"
|
||||
}
|
||||
|
||||
print_pass() {
|
||||
echo -e " ${GREEN}[✓]${NC} $1"
|
||||
inc_pass
|
||||
}
|
||||
|
||||
print_fail() {
|
||||
echo -e " ${RED}[✗]${NC} $1"
|
||||
inc_fail
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e " ${CYAN}[i]${NC} $1"
|
||||
}
|
||||
|
||||
# Main header
|
||||
clear
|
||||
print_header "Lab 02: Quick Test (Fast Validation)"
|
||||
echo ""
|
||||
echo -e "Running critical tests only (< 30 seconds)"
|
||||
echo -e "For full test suite, run: ${YELLOW}bash run-all-tests.sh${NC}"
|
||||
echo ""
|
||||
|
||||
# Quick Test 1: Docker availability
|
||||
print_test "Docker is available"
|
||||
if command -v docker &> /dev/null; then
|
||||
print_pass "Docker command found"
|
||||
print_info "$(docker --version)"
|
||||
else
|
||||
print_fail "Docker not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Quick Test 2: Docker Compose file exists
|
||||
print_test "docker-compose.yml exists"
|
||||
COMPOSE_FILE="$PROJECT_ROOT/labs/lab-02-network/docker-compose.yml"
|
||||
if [[ -f "$COMPOSE_FILE" ]]; then
|
||||
print_pass "docker-compose.yml found"
|
||||
else
|
||||
print_fail "docker-compose.yml not found (expected after Tutorial 1)"
|
||||
print_info "This is OK if you're starting the lab"
|
||||
fi
|
||||
|
||||
# Quick Test 3: Validate compose syntax (if file exists)
|
||||
if [[ -f "$COMPOSE_FILE" ]]; then
|
||||
print_test "docker-compose.yml has valid syntax"
|
||||
if docker compose -f "$COMPOSE_FILE" config &> /dev/null; then
|
||||
print_pass "Compose file is valid YAML"
|
||||
else
|
||||
print_fail "Compose file has syntax errors"
|
||||
print_info "Run: docker compose -f docker-compose.yml config"
|
||||
fi
|
||||
|
||||
# Quick Test 4: INF-02 compliance (no 0.0.0.0 bindings)
|
||||
print_test "INF-02 compliance (no 0.0.0.0 bindings)"
|
||||
ZERO_COUNT=$(grep -c -E '0\.0\.0\.0:[0-9]+' "$COMPOSE_FILE" 2>/dev/null || echo "0")
|
||||
if [[ $ZERO_COUNT -eq 0 ]]; then
|
||||
print_pass "No 0.0.0.0 bindings (secure)"
|
||||
else
|
||||
print_fail "Found $ZERO_COUNT 0.0.0.0 bindings (INF-02 violation)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Quick Test 5: Docker networks can be created
|
||||
print_test "Docker network creation works"
|
||||
if docker network create --driver bridge --subnet 10.0.99.0/24 quick-test-net &> /dev/null; then
|
||||
print_pass "Can create bridge network with custom subnet"
|
||||
docker network rm quick-test-net &> /dev/null
|
||||
else
|
||||
print_fail "Failed to create test network"
|
||||
fi
|
||||
|
||||
# Quick Test 6: Network isolation works
|
||||
print_test "Network isolation verification"
|
||||
# Create two networks
|
||||
if docker network create --driver bridge --subnet 10.0.98.0/24 quick-test-net1 &> /dev/null && \
|
||||
docker network create --driver bridge --subnet 10.0.97.0/24 quick-test-net2 &> /dev/null; then
|
||||
|
||||
# Create test containers
|
||||
if docker run -d --name qt-c1 --network quick-test-net1 alpine:3.19 sleep 60 &> /dev/null && \
|
||||
docker run -d --name qt-c2 --network quick-test-net2 alpine:3.19 sleep 60 &> /dev/null; then
|
||||
|
||||
# Test cross-network isolation (should fail)
|
||||
if docker exec qt-c1 ping -c 1 -W 1 qt-c2 &> /dev/null; then
|
||||
print_fail "Cross-network communication works (isolation broken!)"
|
||||
else
|
||||
print_pass "Cross-network communication blocked (isolation works)"
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
docker stop qt-c1 qt-c2 &> /dev/null
|
||||
docker rm qt-c1 qt-c2 &> /dev/null
|
||||
else
|
||||
print_fail "Failed to create test containers"
|
||||
fi
|
||||
|
||||
# Cleanup networks
|
||||
docker network rm quick-test-net1 quick-test-net2 &> /dev/null
|
||||
else
|
||||
print_fail "Failed to create test networks"
|
||||
fi
|
||||
|
||||
# Quick Test 7: Test scripts exist
|
||||
print_test "Test infrastructure present"
|
||||
TEST_COUNT=0
|
||||
if [[ -f "$TEST_DIR/01-network-creation-test.sh" ]]; then ((TEST_COUNT++)) || true; fi
|
||||
if [[ -f "$TEST_DIR/02-isolation-verification-test.sh" ]]; then ((TEST_COUNT++)) || true; fi
|
||||
if [[ -f "$TEST_DIR/03-inf02-compliance-test.sh" ]]; then ((TEST_COUNT++)) || true; fi
|
||||
|
||||
if [[ $TEST_COUNT -eq 3 ]]; then
|
||||
print_pass "All test scripts present ($TEST_COUNT/3)"
|
||||
else
|
||||
print_fail "Some test scripts missing ($TEST_COUNT/3)"
|
||||
fi
|
||||
|
||||
# Quick Test 8: Documentation exists
|
||||
print_test "Documentation files present"
|
||||
DOC_COUNT=0
|
||||
if [[ -f "$TEST_DIR/../tutorial/01-create-networks.md" ]]; then ((DOC_COUNT++)) || true; fi
|
||||
if [[ -f "$TEST_DIR/../tutorial/02-deploy-containers.md" ]]; then ((DOC_COUNT++)) || true; fi
|
||||
if [[ -f "$TEST_DIR/../tutorial/03-verify-isolation.md" ]]; then ((DOC_COUNT++)) || true; fi
|
||||
|
||||
if [[ $DOC_COUNT -ge 1 ]]; then
|
||||
print_pass "Documentation present ($DOC_COUNT tutorial files)"
|
||||
else
|
||||
print_info "No documentation yet (expected during development)"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
print_header "Quick Test Summary"
|
||||
|
||||
echo -e "Tests run: ${BOLD}$((pass_count + fail_count))${NC}"
|
||||
echo -e " ${GREEN}Passed:${NC} $pass_count"
|
||||
if [[ $fail_count -gt 0 ]]; then
|
||||
echo -e " ${RED}Failed:${NC} $fail_count"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Verdict
|
||||
if [[ $fail_count -eq 0 ]]; then
|
||||
echo -e "${GREEN}${BOLD}✓ ALL QUICK TESTS PASSED${NC}"
|
||||
echo ""
|
||||
echo -e "Quick validation successful!"
|
||||
echo ""
|
||||
echo -e "Next steps:"
|
||||
echo -e " 1. Run full test suite: ${CYAN}bash run-all-tests.sh${NC}"
|
||||
echo -e " 2. Run final verification: ${CYAN}bash 99-final-verification.sh${NC}"
|
||||
echo ""
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}${BOLD}✗ QUICK TESTS FAILED${NC}"
|
||||
echo ""
|
||||
echo -e "Some critical tests failed. Please review:"
|
||||
echo -e " 1. Check Docker is running: ${CYAN}docker ps${NC}"
|
||||
echo -e " 2. Verify compose file: ${CYAN}cd labs/lab-02-network && docker compose config${NC}"
|
||||
echo -e " 3. Run full test suite for details: ${CYAN}bash run-all-tests.sh${NC}"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
146
labs/lab-02-network/tests/run-all-tests.sh
Executable file
146
labs/lab-02-network/tests/run-all-tests.sh
Executable file
@@ -0,0 +1,146 @@
|
||||
#!/bin/bash
|
||||
# Test Orchestration: Run All Tests
|
||||
# Executes all Lab 02 test scripts with fail-fast behavior
|
||||
# Usage: bash labs/lab-02-network/tests/run-all-tests.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Get script directory
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Change to test directory
|
||||
cd "$TEST_DIR"
|
||||
|
||||
# Counter helpers
|
||||
pass_count=0
|
||||
fail_count=0
|
||||
skip_count=0
|
||||
|
||||
inc_pass() { ((pass_count++)) || true; }
|
||||
inc_fail() { ((fail_count++)) || true; }
|
||||
inc_skip() { ((skip_count++)) || true; }
|
||||
|
||||
# Print header
|
||||
print_header() {
|
||||
echo ""
|
||||
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║${NC} ${BOLD}$1${NC}"
|
||||
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Print test header
|
||||
print_test_header() {
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${BLUE}Running:${NC} $1"
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
}
|
||||
|
||||
# Main header
|
||||
print_header "Lab 02: Network & VPC - Test Suite"
|
||||
|
||||
# Test array - all test scripts in order
|
||||
declare -a tests=(
|
||||
"01-network-creation-test.sh"
|
||||
"02-isolation-verification-test.sh"
|
||||
"03-inf02-compliance-test.sh"
|
||||
)
|
||||
|
||||
# Track first failure
|
||||
first_failure=""
|
||||
|
||||
# Run each test
|
||||
for test_script in "${tests[@]}"; do
|
||||
test_path="$TEST_DIR/$test_script"
|
||||
|
||||
# Check if test file exists
|
||||
if [[ ! -f "$test_path" ]]; then
|
||||
echo -e "${YELLOW}[SKIP]${NC} $test_script not found"
|
||||
inc_skip
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check if test file is executable
|
||||
if [[ ! -x "$test_path" ]]; then
|
||||
echo -e "${YELLOW}[SKIP]${NC} $test_script not executable"
|
||||
inc_skip
|
||||
continue
|
||||
fi
|
||||
|
||||
print_test_header "$test_script"
|
||||
|
||||
# Run test and capture exit code
|
||||
if bash "$test_path"; then
|
||||
echo -e "${GREEN}[✓]${NC} $test_script passed"
|
||||
inc_pass
|
||||
echo ""
|
||||
else
|
||||
exit_code=$?
|
||||
echo -e "${RED}[✗]${NC} $test_script failed (exit code: $exit_code)"
|
||||
inc_fail
|
||||
|
||||
# Record first failure for summary
|
||||
if [[ -z "$first_failure" ]]; then
|
||||
first_failure="$test_script"
|
||||
fi
|
||||
|
||||
# Fail-fast: stop on first failure
|
||||
echo ""
|
||||
echo -e "${RED}[FATAL]${NC} Test failed. Stopping test suite (fail-fast mode)."
|
||||
echo ""
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Print summary
|
||||
print_header "Test Suite Summary"
|
||||
|
||||
echo -e "Total tests run: ${BOLD}$((pass_count + fail_count + skip_count))${NC}"
|
||||
echo -e " ${GREEN}Passed:${NC} $pass_count"
|
||||
if [[ $fail_count -gt 0 ]]; then
|
||||
echo -e " ${RED}Failed:${NC} $fail_count"
|
||||
fi
|
||||
if [[ $skip_count -gt 0 ]]; then
|
||||
echo -e " ${YELLOW}Skipped:${NC} $skip_count"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Final verdict
|
||||
if [[ $fail_count -eq 0 && $skip_count -eq 0 ]]; then
|
||||
echo -e "${GREEN}${BOLD}✓ ALL TESTS PASSED${NC}"
|
||||
echo ""
|
||||
echo -e "Next step: Run final verification"
|
||||
echo -e " ${CYAN}bash labs/lab-02-network/tests/99-final-verification.sh${NC}"
|
||||
echo ""
|
||||
exit 0
|
||||
elif [[ $fail_count -eq 0 ]]; then
|
||||
echo -e "${YELLOW}Some tests were skipped${NC}"
|
||||
echo ""
|
||||
echo -e "Note: Skipped tests are expected during RED phase (before implementation)."
|
||||
echo -e " These tests will pass after completing GREEN phase (implementation)."
|
||||
echo ""
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}${BOLD}✗ TESTS FAILED${NC}"
|
||||
echo ""
|
||||
echo -e "First failure: ${RED}$first_failure${NC}"
|
||||
echo ""
|
||||
echo -e "To debug:"
|
||||
echo -e " 1. Run the failed test directly to see detailed output:"
|
||||
echo -e " ${CYAN}bash labs/lab-02-network/tests/$first_failure${NC}"
|
||||
echo ""
|
||||
echo -e " 2. Check infrastructure setup:"
|
||||
echo -e " ${CYAN}cd labs/lab-02-network && docker compose config${NC}"
|
||||
echo ""
|
||||
echo -e " 3. Review test logs for specific failures"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user