diff --git a/DOCS.md b/DOCS.md index eed5cf0..e9b4f57 100644 --- a/DOCS.md +++ b/DOCS.md @@ -251,6 +251,7 @@ All options are set via **Settings → Apps/Add-ons → OpenClaw Assistant → C When `gateway_auth_mode: trusted-proxy` is used, the add-on sets `gateway.auth.trustedProxy.userHeader` to `x-forwarded-user` by default. | `force_ipv4_dns` | bool | `false` | Force IPv4-first DNS ordering for Node network calls. Useful if IPv6 DNS resolves but IPv6 egress is broken (can affect Telegram API polling). | +| `gateway_env_vars` | list of `{name, value}` | `[]` | Environment variables exported to the gateway process at startup. UI format: list entries with `name` and `value` (example: `name=OPENAI_API_KEY`, `value=sk-...`). Limits: max 50 vars, key length 255, value length 10000. Reserved runtime keys are blocked (for example `PATH`, `HOME`, `NODE_OPTIONS`, `NODE_PATH`, `OPENCLAW_*`, proxy vars). Legacy string/object formats are still accepted for backward compatibility. | ### Terminal diff --git a/openclaw_assistant/CHANGELOG.md b/openclaw_assistant/CHANGELOG.md index a9533e4..b00b7c8 100644 --- a/openclaw_assistant/CHANGELOG.md +++ b/openclaw_assistant/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to the OpenClaw Assistant Home Assistant Add-on will be documented in this file. +## [0.5.52] - 2026-02-23 + +### Added +- New add-on option `gateway_env_vars` that accepts a list of `{name, value}` objects from Home Assistant UI and safely injects values into the gateway process at startup (max 50 vars, key <=255 chars, value <=10000 chars). +- Guard `gateway_env_vars` from overriding reserved runtime/proxy/`OPENCLAW_*` keys. +- Keep legacy string/object input formats for backward compatibility. + ## [0.5.51] - 2026-02-23 ### Fixed diff --git a/openclaw_assistant/config.yaml b/openclaw_assistant/config.yaml index fe24ac5..eb873c3 100644 --- a/openclaw_assistant/config.yaml +++ b/openclaw_assistant/config.yaml @@ -1,5 +1,5 @@ name: OpenClaw Assistant -version: "0.5.51" +version: "0.5.52" slug: openclaw_assistant description: Run OpenClaw Assistant (OpenClaw-compatible) as a Home Assistant add-on. url: https://github.com/techartdev/OpenClawHomeAssistant @@ -104,6 +104,19 @@ options: # minimal: suppress repetitive HA health-check and polling requests (default) nginx_log_level: minimal + # Environment variables to pass to the gateway process at startup. + # Format: list of objects with name/value fields. + # Example: + # gateway_env_vars: + # - name: OPENAI_API_KEY + # value: "sk-abc123" + # - name: LOG_LEVEL + # value: "debug" + # Reserved keys are blocked to protect runtime/security + # (e.g. PATH, HOME, NODE_OPTIONS, NODE_PATH, OPENCLAW_*, proxy vars). + # Limits: max 50 variables, max key length 255 chars, max value length 10000 chars + gateway_env_vars: [] + schema: timezone: str @@ -127,5 +140,7 @@ schema: gateway_trusted_proxies: str? enable_openai_api: bool? force_ipv4_dns: bool? + gateway_env_vars: + - name: "match(^[A-Z_][A-Z0-9_]*$)" + value: str nginx_log_level: list(full|minimal)? - diff --git a/openclaw_assistant/run.sh b/openclaw_assistant/run.sh index b6f8322..c578e52 100644 --- a/openclaw_assistant/run.sh +++ b/openclaw_assistant/run.sh @@ -55,6 +55,9 @@ GATEWAY_TRUSTED_PROXIES=$(jq -r '.gateway_trusted_proxies // empty' "$OPTIONS_FI FORCE_IPV4_DNS=$(jq -r '.force_ipv4_dns // true' "$OPTIONS_FILE") ACCESS_MODE=$(jq -r '.access_mode // "custom"' "$OPTIONS_FILE") NGINX_LOG_LEVEL=$(jq -r '.nginx_log_level // "minimal"' "$OPTIONS_FILE") +GW_ENV_VARS_TYPE=$(jq -r 'if .gateway_env_vars == null then "null" else (.gateway_env_vars | type) end' "$OPTIONS_FILE") +GW_ENV_VARS_RAW=$(jq -r '.gateway_env_vars // empty' "$OPTIONS_FILE") +GW_ENV_VARS_JSON=$(jq -c '.gateway_env_vars // []' "$OPTIONS_FILE") export TZ="$TZNAME" @@ -189,6 +192,140 @@ export PNPM_HOME="${PERSISTENT_NODE_GLOBAL}/pnpm" mkdir -p "$PNPM_HOME" export PATH="${PNPM_HOME}:${PATH}" +# Protect critical runtime variables from accidental override via gateway_env_vars. +is_reserved_gateway_env_var() { + case "$1" in + # Critical runtime paths/process vars. + HOME|PATH|PWD|OLDPWD|SHLVL|TZ|XDG_CONFIG_HOME|PNPM_HOME|NODE_PATH|NODE_OPTIONS|NODE_NO_WARNINGS) + return 0 + ;; + # Low-level injection vectors that can alter process/linker/shell behavior. + LD_*|DYLD_*|BASH_ENV|ENV|BASH_FUNC_*) + return 0 + ;; + # Proxy vars managed by add-on options. + HTTP_PROXY|HTTPS_PROXY|NO_PROXY|http_proxy|https_proxy|no_proxy) + return 0 + ;; + # Add-on internal control vars. + OPENCLAW_*) + return 0 + ;; + *) + return 1 + ;; + esac +} + +try_export_gateway_env_var() { + local key="$1" + local value="$2" + + if [ -z "$key" ]; then + return 0 + fi + + # 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)" + return 0 + fi + + # Protect critical runtime variables from accidental override. + if is_reserved_gateway_env_var "$key"; then + echo "WARN: Reserved environment variable '$key' cannot be overridden via gateway_env_vars (skip)" + return 0 + fi + + # 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)" + return 0 + fi + + # Enforce max variable value length + if [ ${#value} -gt $max_var_value_size ]; then + echo "WARN: Environment variable value too long for '$key' (max $max_var_value_size chars, skip)" + return 0 + 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)" + return 0 + fi + + export "$key=$value" + env_count=$((env_count + 1)) + echo "INFO: Exported gateway env var: $key" +} + +# Export gateway environment variables from add-on config +# These are user-defined variables that should be available to the gateway process. +# Primary format: array of {name, value} objects. +if [ "$GW_ENV_VARS_TYPE" = "array" ] || [ "$GW_ENV_VARS_TYPE" = "object" ] || { [ "$GW_ENV_VARS_TYPE" = "string" ] && [ -n "$GW_ENV_VARS_RAW" ]; }; then + env_count=0 + max_env_vars=50 + max_var_name_size=255 + max_var_value_size=10000 + + if [ "$GW_ENV_VARS_TYPE" = "array" ] && [ "$GW_ENV_VARS_JSON" != "[]" ]; then + echo "INFO: Setting gateway environment variables from list config..." + + invalid_entries_count=$(printf '%s' "$GW_ENV_VARS_JSON" | jq '[.[] | select((type != "object") or ((.name | type) != "string") or (has("value") | not))] | length') + if [ "$invalid_entries_count" -gt 0 ]; then + echo "WARN: Found $invalid_entries_count invalid gateway_env_vars entries; expected objects with 'name' and 'value' keys (skip)" + fi + + while IFS= read -r -d '' key && IFS= read -r -d '' value; do + try_export_gateway_env_var "$key" "$value" + done < <(printf '%s' "$GW_ENV_VARS_JSON" | jq -j '.[] | select((type == "object") and ((.name | type) == "string") and (has("value"))) | .name, "\u0000", (.value | tostring), "\u0000"') + elif [ "$GW_ENV_VARS_TYPE" = "object" ] && [ "$GW_ENV_VARS_JSON" != "{}" ]; then + # Backward compatibility for old map/object configuration. + echo "INFO: Setting gateway environment variables from object config (legacy format)..." + while IFS= read -r -d '' key && IFS= read -r -d '' value; do + try_export_gateway_env_var "$key" "$value" + done < <(printf '%s' "$GW_ENV_VARS_JSON" | jq -j 'to_entries[] | .key, "\u0000", (.value | tostring), "\u0000"') + elif [ "$GW_ENV_VARS_TYPE" = "string" ] && [ -n "$GW_ENV_VARS_RAW" ]; then + # Preferred for complex values: JSON object string in one line. + if printf '%s' "$GW_ENV_VARS_RAW" | jq -e 'type == "object"' >/dev/null 2>&1; then + echo "INFO: Setting gateway environment variables from JSON string config..." + while IFS= read -r -d '' key && IFS= read -r -d '' value; do + try_export_gateway_env_var "$key" "$value" + done < <(printf '%s' "$GW_ENV_VARS_RAW" | jq -j 'to_entries[] | .key, "\u0000", (.value | tostring), "\u0000"') + else + # Supported simple format: KEY=VALUE pairs separated by ';' or newlines. + echo "INFO: Setting gateway environment variables from KEY=VALUE string config..." + while IFS= read -r entry; do + entry="${entry%$'\r'}" + trimmed="$(printf '%s' "$entry" | sed -E 's/^[[:space:]]+//;s/[[:space:]]+$//')" + + # Skip empty lines and comments. + if [ -z "$trimmed" ] || [[ "$trimmed" == \#* ]]; then + continue + fi + + if [[ "$trimmed" != *"="* ]]; then + echo "WARN: Invalid gateway_env_vars entry '$trimmed' (expected KEY=VALUE, skip)" + continue + fi + + key="${trimmed%%=*}" + value="${trimmed#*=}" + key="$(printf '%s' "$key" | sed -E 's/^[[:space:]]+//;s/[[:space:]]+$//')" + + try_export_gateway_env_var "$key" "$value" + done < <(printf '%s' "$GW_ENV_VARS_RAW" | tr ';' '\n') + fi + fi + + if [ $env_count -gt 0 ]; then + echo "INFO: Successfully exported $env_count gateway environment variable(s)" + fi +elif [ "$GW_ENV_VARS_TYPE" != "null" ]; then + echo "WARN: Invalid gateway_env_vars format in add-on options (expected list, string or object), skipping" +fi + # ------------------------------------------------------------------------------ # Persist Linuxbrew/Homebrew across Docker image rebuilds # Homebrew installs to /home/linuxbrew/.linuxbrew/ which is ephemeral. diff --git a/openclaw_assistant/translations/bg.yaml b/openclaw_assistant/translations/bg.yaml index 8882de8..03ea571 100644 --- a/openclaw_assistant/translations/bg.yaml +++ b/openclaw_assistant/translations/bg.yaml @@ -95,3 +95,6 @@ configuration: options: minimal: "Минимално (потискане на HA polling шум)" full: "Пълно (логване на всички заявки)" + gateway_env_vars: + name: Променливи на средата за Gateway + description: Променливи на средата, предавани на OpenClaw gateway процеса при стартиране (списък от записи name/value в Home Assistant UI; макс. 50 променливи, ключове до 255 знака, стойности до 10000 знака). diff --git a/openclaw_assistant/translations/de.yaml b/openclaw_assistant/translations/de.yaml index b18847f..49448ad 100644 --- a/openclaw_assistant/translations/de.yaml +++ b/openclaw_assistant/translations/de.yaml @@ -95,3 +95,6 @@ configuration: options: minimal: "Minimal (HA-Polling-Rauschen unterdrücken)" full: "Vollständig (alle Anfragen protokollieren)" + gateway_env_vars: + name: Gateway-Umgebungsvariablen + description: Umgebungsvariablen, die beim Start an den OpenClaw-Gateway-Prozess übergeben werden (Liste mit name/value-Einträgen in der Home Assistant UI; max. 50 Variablen, Schlüssel bis 255 Zeichen, Werte bis 10.000 Zeichen). diff --git a/openclaw_assistant/translations/en.yaml b/openclaw_assistant/translations/en.yaml index 5921841..cd2cd70 100644 --- a/openclaw_assistant/translations/en.yaml +++ b/openclaw_assistant/translations/en.yaml @@ -95,3 +95,6 @@ configuration: options: minimal: "Minimal (suppress HA polling noise)" full: "Full (log all requests)" + gateway_env_vars: + name: Gateway Environment Variables + description: Environment variables passed to the OpenClaw gateway process at startup (list entries with name/value in Home Assistant UI; max 50 variables, keys up to 255 characters, values up to 10,000 characters). diff --git a/openclaw_assistant/translations/es.yaml b/openclaw_assistant/translations/es.yaml index be7aa70..771716f 100644 --- a/openclaw_assistant/translations/es.yaml +++ b/openclaw_assistant/translations/es.yaml @@ -95,3 +95,6 @@ configuration: options: minimal: "Mínimo (suprimir ruido de polling HA)" full: "Completo (registrar todas las solicitudes)" + gateway_env_vars: + name: Variables de entorno del Gateway + description: Variables de entorno que se pasan al proceso del gateway OpenClaw al iniciarse (lista de entradas name/value en la UI de Home Assistant; maximo 50 variables, claves de hasta 255 caracteres, valores de hasta 10 000 caracteres). diff --git a/openclaw_assistant/translations/pl.yaml b/openclaw_assistant/translations/pl.yaml index e4db64a..d7dadf0 100644 --- a/openclaw_assistant/translations/pl.yaml +++ b/openclaw_assistant/translations/pl.yaml @@ -95,3 +95,6 @@ configuration: options: minimal: "Minimalny (pominięcie szumu polling HA)" full: "Pełny (logowanie wszystkich żądań)" + gateway_env_vars: + name: Zmienne środowiskowe gateway + description: Zmienne środowiskowe przekazywane do procesu gateway OpenClaw podczas uruchamiania (lista wpisow name/value w UI Home Assistant; maks. 50 zmiennych, klucze do 255 znakow, wartosci do 10000 znakow). diff --git a/openclaw_assistant/translations/pt-BR.yaml b/openclaw_assistant/translations/pt-BR.yaml index 9d28c79..8207da3 100644 --- a/openclaw_assistant/translations/pt-BR.yaml +++ b/openclaw_assistant/translations/pt-BR.yaml @@ -95,3 +95,6 @@ configuration: options: minimal: "Mínimo (suprimir ruído de polling HA)" full: "Completo (registrar todas as requisições)" + gateway_env_vars: + name: Variáveis de Ambiente do Gateway + description: Variaveis de ambiente passadas para o processo do gateway OpenClaw na inicializacao (lista de entradas name/value na UI do Home Assistant; maximo de 50 variaveis, chaves com ate 255 caracteres, valores com ate 10.000 caracteres).