refactor: improve gateway env vars security and limits
- Add max limits (50 vars, 255 char names, 10000 char values) - Validate variable names with regex - Warn about duplicate variables - Show var names in logs (not just count) - Improve bash parsing with process substitution - Better config documentation with examples
This commit is contained in:
@@ -89,8 +89,13 @@ options:
|
||||
force_ipv4_dns: false
|
||||
|
||||
# Environment variables to pass to the gateway (semicolon-separated key=value pairs)
|
||||
# Example: "SERVICE_API=123;SERVICE2_API=1231"
|
||||
# These will be exported as environment variables when running the gateway
|
||||
# Format: "KEY1=value1;KEY2=value2" (semicolon-separated, no spaces around '=')
|
||||
# Examples:
|
||||
# - API keys: "OPENAI_API_KEY=sk-abc123;ANTHROPIC_API_KEY=claude-key"
|
||||
# - Service URLs: "SERVICE_URL=https://api.example.com;LOG_LEVEL=debug"
|
||||
# - Features: "FEATURE_FLAG_X=1;FEATURE_FLAG_Y=0"
|
||||
# Limits: max 50 variables, max key length 255 chars, max value length 10000 chars
|
||||
# Note: Values may contain special characters; they are not shell-escaped
|
||||
gateway_env_vars: ""
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,11 @@ from pathlib import Path
|
||||
|
||||
CONFIG_PATH = Path(os.environ.get("OPENCLAW_CONFIG_PATH", "/config/.openclaw/openclaw.json"))
|
||||
|
||||
# Limits for environment variable handling
|
||||
MAX_ENV_VARS = 50
|
||||
MAX_ENV_VAR_NAME_SIZE = 255
|
||||
MAX_ENV_VAR_VALUE_SIZE = 10000
|
||||
|
||||
|
||||
|
||||
def read_config():
|
||||
@@ -150,13 +155,37 @@ def apply_gateway_settings(mode: str, bind_mode: str, port: int, enable_openai_a
|
||||
if '=' not in pair:
|
||||
print(f"WARN: Invalid env var format '{pair}' (missing '=')")
|
||||
continue
|
||||
|
||||
key, value = pair.split('=', 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
|
||||
# Validate key format
|
||||
if not key:
|
||||
print(f"WARN: Invalid env var key (empty)")
|
||||
continue
|
||||
if not re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', key):
|
||||
print(f"WARN: Invalid env var name '{key}' (must start with letter/underscore)")
|
||||
continue
|
||||
|
||||
# Enforce size limits
|
||||
if len(key) > MAX_ENV_VAR_NAME_SIZE:
|
||||
print(f"ERROR: Environment variable name '{key}' too long (max {MAX_ENV_VAR_NAME_SIZE} chars)")
|
||||
return False
|
||||
if len(value) > MAX_ENV_VAR_VALUE_SIZE:
|
||||
print(f"ERROR: Environment variable '{key}' value too large (max {MAX_ENV_VAR_VALUE_SIZE} chars)")
|
||||
return False
|
||||
|
||||
# Warn about duplicate keys
|
||||
if key in new_env_vars:
|
||||
print(f"WARN: Environment variable '{key}' appears multiple times; using last value")
|
||||
|
||||
new_env_vars[key] = value
|
||||
|
||||
# Enforce limit on total variables
|
||||
if len(new_env_vars) > MAX_ENV_VARS:
|
||||
print(f"ERROR: Too many environment variables ({len(new_env_vars)}), max {MAX_ENV_VARS}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"ERROR: Failed to parse environment variables: {e}")
|
||||
return False
|
||||
@@ -164,7 +193,8 @@ def apply_gateway_settings(mode: str, bind_mode: str, port: int, enable_openai_a
|
||||
if new_env_vars != current_env_vars:
|
||||
gateway["env"] = new_env_vars
|
||||
if new_env_vars:
|
||||
changes.append(f"env: {len(new_env_vars)} variables")
|
||||
var_names = ', '.join(sorted(new_env_vars.keys()))
|
||||
changes.append(f"env: {var_names}")
|
||||
else:
|
||||
changes.append("env: cleared")
|
||||
|
||||
|
||||
+35
-10
@@ -152,21 +152,46 @@ export PATH="${PNPM_HOME}:${PATH}"
|
||||
# These are user-defined variables that should be available to the gateway process
|
||||
if [ -n "$GW_ENV_VARS" ]; then
|
||||
echo "INFO: Setting gateway environment variables from add-on config..."
|
||||
IFS=';' read -ra ENV_VARS <<< "$GW_ENV_VARS"
|
||||
for var in "${ENV_VARS[@]}"; do
|
||||
var=$(echo "$var" | xargs) # trim whitespace
|
||||
if [ -z "$var" ]; then
|
||||
env_count=0
|
||||
max_env_vars=50
|
||||
max_var_name_size=255
|
||||
|
||||
while IFS='=' read -r key value; do
|
||||
# Skip empty lines
|
||||
if [ -z "$key" ]; then
|
||||
continue
|
||||
fi
|
||||
if [[ ! "$var" =~ ^[A-Za-z_][A-Za-z0-9_]*=.*$ ]]; then
|
||||
echo "WARN: Invalid environment variable format: '$var' (skipping)"
|
||||
|
||||
# Trim whitespace
|
||||
key=$(echo "$key" | xargs)
|
||||
value=$(echo "$value" | xargs)
|
||||
|
||||
# Validate variable name format
|
||||
if ! [[ "$key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
|
||||
echo "WARN: Invalid environment variable name: '$key' (must start with letter/underscore, skip)"
|
||||
continue
|
||||
fi
|
||||
export "$var"
|
||||
# Extract key for logging (don't log values for security)
|
||||
key="${var%%=*}"
|
||||
|
||||
# Enforce max variable name length
|
||||
if [ ${#key} -gt $max_var_name_size ]; then
|
||||
echo "WARN: Environment variable name too long: '$key' (max $max_var_name_size chars, skip)"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Enforce limit on number of variables
|
||||
if [ $env_count -ge $max_env_vars ]; then
|
||||
echo "WARN: Maximum environment variables limit ($max_env_vars) reached (skip)"
|
||||
continue
|
||||
fi
|
||||
|
||||
export "$key=$value"
|
||||
((env_count++))
|
||||
echo "INFO: Exported gateway env var: $key"
|
||||
done
|
||||
done < <(echo "$GW_ENV_VARS" | tr ';' '\n')
|
||||
|
||||
if [ $env_count -gt 0 ]; then
|
||||
echo "INFO: Successfully exported $env_count gateway environment variable(s)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user