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:
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
|
||||
Reference in New Issue
Block a user