-
Notifications
You must be signed in to change notification settings - Fork 34
Expand file tree
/
Copy pathrun_containerized.sh
More file actions
executable file
·276 lines (230 loc) · 8.67 KB
/
Copy pathrun_containerized.sh
File metadata and controls
executable file
·276 lines (230 loc) · 8.67 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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
#!/bin/bash
# run_containerized.sh - Startup script for containerized MCP Gateway
# This script should be used when running the gateway inside a Docker container.
# It performs comprehensive validation of the container environment before starting.
set -e
# Detect if stderr is a TTY and colors should be enabled
# Respects NO_COLOR and DEBUG_COLORS environment variables
USE_COLORS=false
if [ -t 2 ] && [ -z "$NO_COLOR" ] && [ "${DEBUG_COLORS:-1}" != "0" ]; then
USE_COLORS=true
fi
# Color output for better visibility (only when USE_COLORS=true)
if [ "$USE_COLORS" = true ]; then
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
else
RED=''
YELLOW=''
GREEN=''
NC=''
fi
log_info() {
echo -e "${GREEN}[INFO]${NC} $1" >&2
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1" >&2
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
}
# Validate container ID contains only hex characters (security check)
validate_container_id() {
local cid="$1"
if [ -z "$cid" ]; then
return 1
fi
# Container IDs must be 12-64 hex characters only.
# Non-hex IDs (e.g. Kubernetes pod names on ARC/containerd) are expected and
# are silently rejected here — the caller treats this as informational.
if ! echo "$cid" | grep -qE '^[a-f0-9]{12,64}$'; then
return 1
fi
return 0
}
# Detect container ID from /proc/self/cgroup
get_container_id() {
if [ -f /proc/self/cgroup ]; then
# Try to extract container ID from cgroup v1 or v2 paths
# Use grep directly on the file instead of cat | grep
local cid=$(grep -oE '[0-9a-f]{12,64}' /proc/self/cgroup | head -1)
if validate_container_id "$cid"; then
echo "$cid"
return 0
fi
fi
# Fallback: check hostname (often set to container ID)
if [ -f /.dockerenv ]; then
local host_id=$(hostname)
if validate_container_id "$host_id"; then
echo "$host_id"
return 0
fi
fi
return 1
}
# Verify we're running in a container
verify_containerized() {
if [ ! -f /.dockerenv ] && ! grep -q 'docker\|containerd' /proc/self/cgroup 2>/dev/null; then
log_error "This script should only be run inside a Docker container."
log_error "For non-containerized deployments, use run.sh instead."
exit 1
fi
log_info "Running in containerized environment"
}
# Validate required environment variables
check_required_env_vars() {
local missing_vars=()
if [ -z "$MCP_GATEWAY_PORT" ]; then
missing_vars+=("MCP_GATEWAY_PORT")
fi
if [ -z "$MCP_GATEWAY_DOMAIN" ]; then
missing_vars+=("MCP_GATEWAY_DOMAIN")
fi
# Accept MCP_GATEWAY_AGENT_ID (preferred) or MCP_GATEWAY_API_KEY (deprecated fallback)
if [ -z "$MCP_GATEWAY_AGENT_ID" ] && [ -z "$MCP_GATEWAY_API_KEY" ]; then
missing_vars+=("MCP_GATEWAY_AGENT_ID")
elif [ -z "$MCP_GATEWAY_AGENT_ID" ] && [ -n "$MCP_GATEWAY_API_KEY" ]; then
log_warn "MCP_GATEWAY_API_KEY is deprecated; please rename it to MCP_GATEWAY_AGENT_ID"
export MCP_GATEWAY_AGENT_ID="$MCP_GATEWAY_API_KEY"
fi
if [ ${#missing_vars[@]} -ne 0 ]; then
log_error "Required environment variables not set:"
for var in "${missing_vars[@]}"; do
log_error " - $var"
done
log_error ""
log_error "Set these when running the container:"
log_error " docker run -e MCP_GATEWAY_PORT=8080 -e MCP_GATEWAY_DOMAIN=localhost -e MCP_GATEWAY_AGENT_ID=your-agent-id ..."
exit 1
fi
log_info "Required environment variables are set"
}
# Detect host IP and configure host.docker.internal DNS mapping
configure_host_dns() {
log_info "Detecting host IP for container networking..."
local HOST_IP=""
# Method 1: Try to get the primary network interface IP (not loopback)
HOST_IP=$(ip -4 addr show 2>/dev/null | grep 'inet ' | awk '{print $2}' | cut -d/ -f1 | grep -v '^127\.' | head -1)
if [ -n "$HOST_IP" ]; then
log_info "Method 1 (primary interface): $HOST_IP"
fi
# Method 2: Try default gateway IP
if [ -z "$HOST_IP" ]; then
HOST_IP=$(ip route 2>/dev/null | grep default | awk '{print $3}' | head -1)
if [ -n "$HOST_IP" ]; then
log_info "Method 2 (default gateway): $HOST_IP"
fi
fi
# Method 3: Try docker0 bridge IP
if [ -z "$HOST_IP" ]; then
HOST_IP=$(ip addr show docker0 2>/dev/null | grep 'inet ' | awk '{print $2}' | cut -d/ -f1)
if [ -n "$HOST_IP" ]; then
log_info "Method 3 (docker0 bridge): $HOST_IP"
fi
fi
# Method 4: Last resort - common Docker bridge IP
if [ -z "$HOST_IP" ]; then
HOST_IP="172.17.0.1"
log_info "Method 4 (fallback): $HOST_IP"
fi
log_info "Using host IP: $HOST_IP"
# Add host.docker.internal mapping to /etc/hosts
# Check if the entry already exists to avoid duplicates
if ! grep -q "host.docker.internal" /etc/hosts 2>/dev/null; then
if { echo "$HOST_IP host.docker.internal" >> /etc/hosts; } 2>/dev/null; then
log_info "DNS mapping configured: $HOST_IP -> host.docker.internal"
else
log_warn "Cannot write to /etc/hosts (running as non-root?); host.docker.internal mapping skipped"
fi
else
log_info "host.docker.internal already exists in /etc/hosts"
fi
}
# Build command line arguments
build_command_args() {
local host="${MCP_GATEWAY_HOST:-0.0.0.0}"
local port="$MCP_GATEWAY_PORT"
local mode="${MCP_GATEWAY_MODE:---routed}"
local log_dir="${MCP_GATEWAY_LOG_DIR:-/tmp/gh-aw/mcp-logs}"
local flags="$mode --listen ${host}:${port} --config-stdin --validate-env --log-dir ${log_dir}"
# Add env file if specified and exists
if [ -n "$ENV_FILE" ] && [ -f "$ENV_FILE" ]; then
flags="$flags --env $ENV_FILE"
log_info "Using environment file: $ENV_FILE"
fi
log_info "Gateway will listen on ${host}:${port}"
log_info "Log directory: ${log_dir}"
echo "$flags"
}
# Run proxy mode — lightweight path that skips gateway-specific checks
# (no Docker socket, no stdin config, no MCP_GATEWAY_PORT/MCP_GATEWAY_DOMAIN/MCP_GATEWAY_AGENT_ID needed)
run_proxy_mode() {
log_info "Starting MCP Gateway in proxy mode..."
local log_dir="${MCP_GATEWAY_LOG_DIR:-/tmp/gh-aw/mcp-logs}"
# Build args: insert --log-dir default if not already specified
local args=("$@")
local has_log_dir=false
for arg in "$@"; do
if [ "$arg" = "--log-dir" ] || [[ "$arg" == --log-dir=* ]]; then
has_log_dir=true
break
fi
done
if [ "$has_log_dir" = false ]; then
args+=("--log-dir" "$log_dir")
fi
log_info "Command: ./awmg ${args[*]}"
exec ./awmg "${args[@]}"
}
# Main execution
main() {
log_info "Starting MCP Gateway in containerized mode..."
# If first argument is "proxy", use the lightweight proxy path.
# Proxy mode doesn't need Docker socket, stdin config, or gateway env vars.
if [ "$1" = "proxy" ]; then
run_proxy_mode "$@"
return
fi
# Auto-detect baked-in WASM guards if env var not explicitly set.
# Setting MCP_GATEWAY_WASM_GUARDS_DIR="" explicitly disables auto-detection.
if [ -z "${MCP_GATEWAY_WASM_GUARDS_DIR+set}" ] && [ -d "/guards" ]; then
export MCP_GATEWAY_WASM_GUARDS_DIR="/guards"
log_info "Auto-detected baked-in WASM guards at /guards"
fi
if [ -n "$MCP_GATEWAY_WASM_GUARDS_DIR" ]; then
log_info "MCP_GATEWAY_WASM_GUARDS_DIR=$MCP_GATEWAY_WASM_GUARDS_DIR"
else
log_info "MCP_GATEWAY_WASM_GUARDS_DIR is not set"
fi
# Verify we're in a container
verify_containerized
# Get container ID
CONTAINER_ID=$(get_container_id) || true
if [ -n "$CONTAINER_ID" ]; then
log_info "Container ID: ${CONTAINER_ID:0:12}..."
else
log_warn "Could not determine container ID"
fi
# Required deployment variables are checked before loading the config.
# The selected container runtime is validated by --validate-env after stdin
# is parsed, so Dockerless mode never probes the Docker socket.
check_required_env_vars
# Configure DNS for host.docker.internal
configure_host_dns
# Build command
FLAGS=$(build_command_args)
CMD="./awmg"
log_info "Command: $CMD $FLAGS"
log_info "Waiting for JSON configuration on stdin..."
log_info ""
log_info "IMPORTANT: Configuration must be provided via stdin."
log_info "No default configuration is used in containerized mode."
log_info ""
# Execute - stdin will be passed through
# Any extra args passed to the container (Docker CMD) are appended
exec $CMD $FLAGS "$@"
}
main "$@"