-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsecurity-tests.sh
More file actions
185 lines (162 loc) · 7.2 KB
/
security-tests.sh
File metadata and controls
185 lines (162 loc) · 7.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#!/bin/bash
# Security test suite - Multipass/gVisor
set -e
CONTAINER_NAME="test-sandbox"
FAILED_TESTS=0
echo "==================================="
echo "SECURITY TEST SUITE"
echo "==================================="
# Check if container exists
if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
echo "[ERROR] Container $CONTAINER_NAME not found!"
echo "Run quick-start.sh first."
exit 1
fi
# Install curl if missing
echo "[SETUP] Ensuring curl is available..."
docker exec $CONTAINER_NAME bash -c "apt-get update -qq && apt-get install -y -qq curl procps" 2>/dev/null || echo "[WARN] Could not install all tools, continuing..."
# Test 1: Container Escape Attempts
echo -e "\n[TEST 1] Container Escape Attempts"
if docker exec $CONTAINER_NAME ls -la /proc/1/root 2>&1 | grep -q "lrwxrwxrwx"; then
echo "[PASS] /proc/1/root is symlink (expected in gVisor)"
else
# gVisor may show different behavior - check it's isolated
if docker exec $CONTAINER_NAME cat /proc/1/comm 2>/dev/null | grep -q "sleep"; then
echo "[PASS] /proc/1 is container init (isolated)"
else
echo "[WARN] Unexpected /proc/1/root behavior"
fi
fi
if docker exec $CONTAINER_NAME cat /proc/kallsyms 2>&1 | grep -q "No such file"; then
echo "[PASS] kallsyms blocked (kernel symbols hidden)"
else
echo "[WARN] kallsyms accessible (may vary by gVisor version)"
fi
# Test 2: Network Isolation
echo -e "\n[TEST 2] Network Isolation (Egress Proxy)"
# Check proxy is running
if docker ps --format '{{.Names}}' | grep -q "egress-proxy"; then
echo "[PASS] Egress proxy container running"
# Test blocked host
if docker exec $CONTAINER_NAME curl -s -o /dev/null -w "%{http_code}" --max-time 5 -x http://localhost:8443 http://evil.com 2>/dev/null | grep -q "403"; then
echo "[PASS] Blocked host returns 403"
else
echo "[INFO] Testing with direct curl..."
if docker exec $CONTAINER_NAME curl -s --max-time 5 -x http://localhost:8443 http://evil.com 2>&1 | grep -q "Host not allowed\|403"; then
echo "[PASS] Blocked host rejected by Lua filter"
else
echo "[FAIL] Blocked host not rejected"
FAILED_TESTS=$((FAILED_TESTS+1))
fi
fi
# Test allowed host
HTTP_CODE=$(docker exec $CONTAINER_NAME curl -s -o /dev/null -w "%{http_code}" --max-time 10 -x http://localhost:8443 http://example.com 2>/dev/null || echo "000")
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "301" ] || [ "$HTTP_CODE" = "302" ]; then
echo "[PASS] Allowed host (example.com) accessible via proxy ($HTTP_CODE)"
else
echo "[INFO] Allowed host test returned: $HTTP_CODE (may need upstream connectivity)"
fi
else
echo "[FAIL] Egress proxy not running"
FAILED_TESTS=$((FAILED_TESTS+1))
fi
# Test 3: Filesystem Isolation
echo -e "\n[TEST 3] Filesystem Isolation"
if docker exec $CONTAINER_NAME touch /mnt/skills/testwrite 2>&1 | grep -qi "read-only\|permission denied"; then
echo "[PASS] Skills directory is read-only"
else
docker exec $CONTAINER_NAME rm -f /mnt/skills/testwrite 2>/dev/null || true
echo "[FAIL] Skills directory is writable (should be read-only)"
FAILED_TESTS=$((FAILED_TESTS+1))
fi
if docker exec $CONTAINER_NAME touch /mnt/outputs/testfile 2>/dev/null; then
docker exec $CONTAINER_NAME rm -f /mnt/outputs/testfile 2>/dev/null || true
echo "[PASS] Outputs directory is writable"
else
echo "[FAIL] Outputs directory not writable"
FAILED_TESTS=$((FAILED_TESTS+1))
fi
# Symlink test - may not work in all gVisor configs
if docker exec $CONTAINER_NAME ln -s /etc/shadow /mnt/outputs/shadow_link 2>&1 | grep -qi "operation not permitted\|input/output error\|permission denied"; then
echo "[PASS] Symlink attack blocked in outputs"
else
docker exec $CONTAINER_NAME rm -f /mnt/outputs/shadow_link 2>/dev/null || true
echo "[INFO] Symlink creation allowed (check 9P mount options)"
fi
# Test 4: Resource Limits
echo -e "\n[TEST 4] Resource Limits (cgroups)"
MEM_LIMIT=$(docker inspect $CONTAINER_NAME --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
if [ "$MEM_LIMIT" != "0" ] && [ "$MEM_LIMIT" != "<no value>" ]; then
echo "[PASS] Memory limit configured ($MEM_LIMIT bytes)"
else
echo "[FAIL] Memory limit not configured"
FAILED_TESTS=$((FAILED_TESTS+1))
fi
CPU_QUOTA=$(docker inspect $CONTAINER_NAME --format '{{.HostConfig.CpuQuota}}' 2>/dev/null || echo "0")
if [ "$CPU_QUOTA" != "0" ] && [ "$CPU_QUOTA" != "<no value>" ]; then
echo "[PASS] CPU quota configured ($CPU_QUOTA)"
else
echo "[FAIL] CPU quota not configured"
FAILED_TESTS=$((FAILED_TESTS+1))
fi
PID_LIMIT=$(docker inspect $CONTAINER_NAME --format '{{.HostConfig.PidsLimit}}' 2>/dev/null || echo "0")
if [ "$PID_LIMIT" != "0" ] && [ "$PID_LIMIT" != "<no value>" ]; then
echo "[PASS] PID limit configured ($PID_LIMIT)"
else
echo "[FAIL] PID limit not configured"
FAILED_TESTS=$((FAILED_TESTS+1))
fi
# Test 5: Process Isolation
echo -e "\n[TEST 5] Process Isolation (PID Namespace)"
HOST_PROCS=$(ps aux 2>/dev/null | wc -l)
CONTAINER_PROCS=$(docker exec $CONTAINER_NAME ps aux 2>/dev/null | wc -l)
if [ -n "$HOST_PROCS" ] && [ -n "$CONTAINER_PROCS" ] && [ "$CONTAINER_PROCS" -lt "$HOST_PROCS" ]; then
echo "[PASS] Process isolation working (container sees $CONTAINER_PROCS vs host $HOST_PROCS processes)"
else
echo "[FAIL] Process isolation may not be working"
FAILED_TESTS=$((FAILED_TESTS+1))
fi
# Test 6: Capabilities
echo -e "\n[TEST 6] Capability Dropping"
if docker exec $CONTAINER_NAME capsh --print 2>&1 | grep -qi "cap_sys_admin"; then
echo "[FAIL] CAP_SYS_ADMIN present (should be dropped)"
FAILED_TESTS=$((FAILED_TESTS+1))
else
echo "[PASS] CAP_SYS_ADMIN dropped"
fi
# Test 7: gVisor Runtime Detection
echo -e "\n[TEST 7] gVisor Runtime Detection"
KERNEL=$(docker exec $CONTAINER_NAME uname -r 2>/dev/null || echo "unknown")
PROC_VERSION=$(docker exec $CONTAINER_NAME cat /proc/version 2>/dev/null || echo "unknown")
# gVisor typically reports 4.4.0 or similar, NOT the host kernel
if echo "$KERNEL" | grep -qi "runsc\|gvisor"; then
echo "[PASS] gVisor kernel detected (explicit: $KERNEL)"
elif echo "$KERNEL" | grep -q "^4\.4\." && echo "$PROC_VERSION" | grep -qi "gvisor\|runsc"; then
echo "[PASS] gVisor kernel detected (version: $KERNEL, /proc/version confirms gVisor)"
elif [ "$KERNEL" != "$(uname -r)" ]; then
echo "[PASS] gVisor kernel detected (different from host: $KERNEL vs $(uname -r))"
else
echo "[FAIL] May not be running on gVisor (kernel: $KERNEL)"
FAILED_TESTS=$((FAILED_TESTS+1))
fi
# Test 8: Security Options
echo -e "\n[TEST 8] Security Options"
NO_NEW_PRIVS=$(docker inspect $CONTAINER_NAME --format '{{.HostConfig.SecurityOpt}}' 2>/dev/null | grep -o "no-new-privileges" || echo "")
if [ -n "$NO_NEW_PRIVS" ]; then
echo "[PASS] no-new-privileges enabled"
else
echo "[FAIL] no-new-privileges not enabled"
FAILED_TESTS=$((FAILED_TESTS+1))
fi
# Summary
echo -e "\n==================================="
echo "TEST SUMMARY"
echo "==================================="
echo "Failed tests: $FAILED_TESTS"
if [ $FAILED_TESTS -eq 0 ]; then
echo "[PASS] ALL TESTS PASSED"
exit 0
else
echo "[WARN] $FAILED_TESTS TEST(S) FAILED - Review output above"
exit 1
fi