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
|
force_ipv4_dns: false
|
||||||
|
|
||||||
# Environment variables to pass to the gateway (semicolon-separated key=value pairs)
|
# Environment variables to pass to the gateway (semicolon-separated key=value pairs)
|
||||||
# Example: "SERVICE_API=123;SERVICE2_API=1231"
|
# Format: "KEY1=value1;KEY2=value2" (semicolon-separated, no spaces around '=')
|
||||||
# These will be exported as environment variables when running the gateway
|
# 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: ""
|
gateway_env_vars: ""
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ from pathlib import Path
|
|||||||
|
|
||||||
CONFIG_PATH = Path(os.environ.get("OPENCLAW_CONFIG_PATH", "/config/.openclaw/openclaw.json"))
|
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():
|
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:
|
if '=' not in pair:
|
||||||
print(f"WARN: Invalid env var format '{pair}' (missing '=')")
|
print(f"WARN: Invalid env var format '{pair}' (missing '=')")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
key, value = pair.split('=', 1)
|
key, value = pair.split('=', 1)
|
||||||
key = key.strip()
|
key = key.strip()
|
||||||
value = value.strip()
|
value = value.strip()
|
||||||
|
|
||||||
|
# Validate key format
|
||||||
if not key:
|
if not key:
|
||||||
print(f"WARN: Invalid env var key (empty)")
|
print(f"WARN: Invalid env var key (empty)")
|
||||||
continue
|
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
|
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:
|
except Exception as e:
|
||||||
print(f"ERROR: Failed to parse environment variables: {e}")
|
print(f"ERROR: Failed to parse environment variables: {e}")
|
||||||
return False
|
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:
|
if new_env_vars != current_env_vars:
|
||||||
gateway["env"] = new_env_vars
|
gateway["env"] = new_env_vars
|
||||||
if 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:
|
else:
|
||||||
changes.append("env: cleared")
|
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
|
# These are user-defined variables that should be available to the gateway process
|
||||||
if [ -n "$GW_ENV_VARS" ]; then
|
if [ -n "$GW_ENV_VARS" ]; then
|
||||||
echo "INFO: Setting gateway environment variables from add-on config..."
|
echo "INFO: Setting gateway environment variables from add-on config..."
|
||||||
IFS=';' read -ra ENV_VARS <<< "$GW_ENV_VARS"
|
env_count=0
|
||||||
for var in "${ENV_VARS[@]}"; do
|
max_env_vars=50
|
||||||
var=$(echo "$var" | xargs) # trim whitespace
|
max_var_name_size=255
|
||||||
if [ -z "$var" ]; then
|
|
||||||
|
while IFS='=' read -r key value; do
|
||||||
|
# Skip empty lines
|
||||||
|
if [ -z "$key" ]; then
|
||||||
continue
|
continue
|
||||||
fi
|
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
|
continue
|
||||||
fi
|
fi
|
||||||
export "$var"
|
|
||||||
# Extract key for logging (don't log values for security)
|
# Enforce max variable name length
|
||||||
key="${var%%=*}"
|
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"
|
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
|
fi
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user