2026-01-02 13:52:08 +02:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
|
COMPOSE_FILE="$ROOT_DIR/docker-compose.yml"
|
2026-01-10 20:12:23 +00:00
|
|
|
EXTRA_COMPOSE_FILE="$ROOT_DIR/docker-compose.extra.yml"
|
2026-01-30 03:15:10 +01:00
|
|
|
IMAGE_NAME="${OPENCLAW_IMAGE:-openclaw:local}"
|
|
|
|
|
EXTRA_MOUNTS="${OPENCLAW_EXTRA_MOUNTS:-}"
|
|
|
|
|
HOME_VOLUME_NAME="${OPENCLAW_HOME_VOLUME:-}"
|
feat(docker): add opt-in sandbox support for Docker deployments (#29974)
* feat(docker): add opt-in sandbox support for Docker deployments
Enable Docker-based sandbox isolation via OPENCLAW_SANDBOX=1 env var
in docker-setup.sh. This is a prerequisite for agents.defaults.sandbox
to function in any Docker deployment (self-hosted, Hostinger, DigitalOcean).
Changes:
- Dockerfile: add OPENCLAW_INSTALL_DOCKER_CLI build arg (~50MB, opt-in)
- docker-compose.yml: add commented-out docker.sock mount with docs
- docker-setup.sh: auto-detect Docker socket, inject mount, detect GID,
build sandbox image, configure sandbox defaults, add group_add
All changes are opt-in. Zero impact on existing deployments.
Usage: OPENCLAW_SANDBOX=1 ./docker-setup.sh
Closes #29933
Related: #7575, #7827, #28401, #10361, #12505, #28326
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address code review feedback on sandbox support
- Persist OPENCLAW_SANDBOX, DOCKER_GID, OPENCLAW_INSTALL_DOCKER_CLI
to .env via upsert_env so group_add survives re-runs
- Show config set errors instead of swallowing them silently;
report partial failure when sandbox config is incomplete
- Warn when Dockerfile.sandbox is missing but sandbox config
is still applied (sandbox image won't exist)
- Fix non-canonical whitespace in apt sources.list entry
by using printf instead of echo with line continuation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove `local` outside function and guard sandbox behind Docker CLI check
- Remove `local` keyword from top-level `sandbox_config_ok` assignment
which caused script exit under `set -euo pipefail` (bash `local`
outside a function is an error)
- Add Docker CLI prerequisite check for pre-built (non-local) images:
runs `docker --version` inside the container and skips sandbox setup
with a clear warning if the CLI is missing
- Split sandbox block so config is only applied after prerequisites pass
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: defer docker.sock mount until sandbox prerequisites pass
Move Docker socket mounting from the early setup phase (before image
build/pull) to a dedicated compose overlay created only after:
1. Docker CLI is verified inside the container image
2. /var/run/docker.sock exists on the host
Previously the socket was mounted optimistically at startup, leaving
the host Docker daemon exposed even when sandbox setup was later
skipped due to missing Docker CLI. Now the gateway starts without
the socket, and a docker-compose.sandbox.yml overlay is generated
only when all prerequisites pass. The gateway restart at the end of
sandbox setup picks up both the socket mount and sandbox config.
Also moves group_add from write_extra_compose() into the sandbox
overlay, keeping all sandbox-specific compose configuration together.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(docker): fix sandbox docs URL in setup output
* Docker: harden sandbox setup fallback behavior
* Tests: cover docker-setup sandbox edge paths
* Docker: roll back sandbox mode on partial config failure
* Tests: assert sandbox mode rollback on partial setup
* Docs: document Docker sandbox bootstrap env controls
* Changelog: credit Docker sandbox bootstrap hardening
* Update CHANGELOG.md
* Docker: verify Docker apt signing key fingerprint
* Docker: avoid sandbox overlay deps during policy writes
* Tests: assert no-deps sandbox rollback gateway recreate
* Docs: mention OPENCLAW_INSTALL_DOCKER_CLI in Docker env vars
---------
Co-authored-by: Jakub Karwowski <jakubkarwowski@Mac.lan>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-02 08:06:10 +01:00
|
|
|
RAW_SANDBOX_SETTING="${OPENCLAW_SANDBOX:-}"
|
|
|
|
|
SANDBOX_ENABLED=""
|
|
|
|
|
DOCKER_SOCKET_PATH="${OPENCLAW_DOCKER_SOCKET:-}"
|
2026-01-02 13:52:08 +02:00
|
|
|
|
2026-02-19 10:44:38 +01:00
|
|
|
fail() {
|
|
|
|
|
echo "ERROR: $*" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-02 13:52:08 +02:00
|
|
|
require_cmd() {
|
|
|
|
|
if ! command -v "$1" >/dev/null 2>&1; then
|
|
|
|
|
echo "Missing dependency: $1" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
feat(docker): add opt-in sandbox support for Docker deployments (#29974)
* feat(docker): add opt-in sandbox support for Docker deployments
Enable Docker-based sandbox isolation via OPENCLAW_SANDBOX=1 env var
in docker-setup.sh. This is a prerequisite for agents.defaults.sandbox
to function in any Docker deployment (self-hosted, Hostinger, DigitalOcean).
Changes:
- Dockerfile: add OPENCLAW_INSTALL_DOCKER_CLI build arg (~50MB, opt-in)
- docker-compose.yml: add commented-out docker.sock mount with docs
- docker-setup.sh: auto-detect Docker socket, inject mount, detect GID,
build sandbox image, configure sandbox defaults, add group_add
All changes are opt-in. Zero impact on existing deployments.
Usage: OPENCLAW_SANDBOX=1 ./docker-setup.sh
Closes #29933
Related: #7575, #7827, #28401, #10361, #12505, #28326
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address code review feedback on sandbox support
- Persist OPENCLAW_SANDBOX, DOCKER_GID, OPENCLAW_INSTALL_DOCKER_CLI
to .env via upsert_env so group_add survives re-runs
- Show config set errors instead of swallowing them silently;
report partial failure when sandbox config is incomplete
- Warn when Dockerfile.sandbox is missing but sandbox config
is still applied (sandbox image won't exist)
- Fix non-canonical whitespace in apt sources.list entry
by using printf instead of echo with line continuation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove `local` outside function and guard sandbox behind Docker CLI check
- Remove `local` keyword from top-level `sandbox_config_ok` assignment
which caused script exit under `set -euo pipefail` (bash `local`
outside a function is an error)
- Add Docker CLI prerequisite check for pre-built (non-local) images:
runs `docker --version` inside the container and skips sandbox setup
with a clear warning if the CLI is missing
- Split sandbox block so config is only applied after prerequisites pass
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: defer docker.sock mount until sandbox prerequisites pass
Move Docker socket mounting from the early setup phase (before image
build/pull) to a dedicated compose overlay created only after:
1. Docker CLI is verified inside the container image
2. /var/run/docker.sock exists on the host
Previously the socket was mounted optimistically at startup, leaving
the host Docker daemon exposed even when sandbox setup was later
skipped due to missing Docker CLI. Now the gateway starts without
the socket, and a docker-compose.sandbox.yml overlay is generated
only when all prerequisites pass. The gateway restart at the end of
sandbox setup picks up both the socket mount and sandbox config.
Also moves group_add from write_extra_compose() into the sandbox
overlay, keeping all sandbox-specific compose configuration together.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(docker): fix sandbox docs URL in setup output
* Docker: harden sandbox setup fallback behavior
* Tests: cover docker-setup sandbox edge paths
* Docker: roll back sandbox mode on partial config failure
* Tests: assert sandbox mode rollback on partial setup
* Docs: document Docker sandbox bootstrap env controls
* Changelog: credit Docker sandbox bootstrap hardening
* Update CHANGELOG.md
* Docker: verify Docker apt signing key fingerprint
* Docker: avoid sandbox overlay deps during policy writes
* Tests: assert no-deps sandbox rollback gateway recreate
* Docs: mention OPENCLAW_INSTALL_DOCKER_CLI in Docker env vars
---------
Co-authored-by: Jakub Karwowski <jakubkarwowski@Mac.lan>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-02 08:06:10 +01:00
|
|
|
is_truthy_value() {
|
|
|
|
|
local raw="${1:-}"
|
|
|
|
|
raw="$(printf '%s' "$raw" | tr '[:upper:]' '[:lower:]')"
|
|
|
|
|
case "$raw" in
|
|
|
|
|
1 | true | yes | on) return 0 ;;
|
|
|
|
|
*) return 1 ;;
|
|
|
|
|
esac
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-26 05:45:57 +01:00
|
|
|
read_config_gateway_token() {
|
|
|
|
|
local config_path="$OPENCLAW_CONFIG_DIR/openclaw.json"
|
|
|
|
|
if [[ ! -f "$config_path" ]]; then
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
if command -v python3 >/dev/null 2>&1; then
|
|
|
|
|
python3 - "$config_path" <<'PY'
|
|
|
|
|
import json
|
|
|
|
|
import sys
|
|
|
|
|
|
|
|
|
|
path = sys.argv[1]
|
|
|
|
|
try:
|
|
|
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
|
|
|
cfg = json.load(f)
|
|
|
|
|
except Exception:
|
|
|
|
|
raise SystemExit(0)
|
|
|
|
|
|
|
|
|
|
gateway = cfg.get("gateway")
|
|
|
|
|
if not isinstance(gateway, dict):
|
|
|
|
|
raise SystemExit(0)
|
|
|
|
|
auth = gateway.get("auth")
|
|
|
|
|
if not isinstance(auth, dict):
|
|
|
|
|
raise SystemExit(0)
|
|
|
|
|
token = auth.get("token")
|
|
|
|
|
if isinstance(token, str):
|
|
|
|
|
token = token.strip()
|
|
|
|
|
if token:
|
|
|
|
|
print(token)
|
|
|
|
|
PY
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
if command -v node >/dev/null 2>&1; then
|
|
|
|
|
node - "$config_path" <<'NODE'
|
|
|
|
|
const fs = require("node:fs");
|
|
|
|
|
const configPath = process.argv[2];
|
|
|
|
|
try {
|
|
|
|
|
const cfg = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
|
|
|
const token = cfg?.gateway?.auth?.token;
|
|
|
|
|
if (typeof token === "string" && token.trim().length > 0) {
|
|
|
|
|
process.stdout.write(token.trim());
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
// Keep docker-setup resilient when config parsing fails.
|
|
|
|
|
}
|
|
|
|
|
NODE
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-07 23:41:35 +00:00
|
|
|
read_env_gateway_token() {
|
|
|
|
|
local env_path="$1"
|
|
|
|
|
local line=""
|
|
|
|
|
local token=""
|
|
|
|
|
if [[ ! -f "$env_path" ]]; then
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
|
|
|
line="${line%$'\r'}"
|
|
|
|
|
if [[ "$line" == OPENCLAW_GATEWAY_TOKEN=* ]]; then
|
|
|
|
|
token="${line#OPENCLAW_GATEWAY_TOKEN=}"
|
|
|
|
|
fi
|
|
|
|
|
done <"$env_path"
|
|
|
|
|
if [[ -n "$token" ]]; then
|
|
|
|
|
printf '%s' "$token"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-26 05:45:57 +01:00
|
|
|
ensure_control_ui_allowed_origins() {
|
|
|
|
|
if [[ "${OPENCLAW_GATEWAY_BIND}" == "loopback" ]]; then
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
local allowed_origin_json
|
|
|
|
|
local current_allowed_origins
|
|
|
|
|
allowed_origin_json="$(printf '["http://127.0.0.1:%s"]' "$OPENCLAW_GATEWAY_PORT")"
|
|
|
|
|
current_allowed_origins="$(
|
|
|
|
|
docker compose "${COMPOSE_ARGS[@]}" run --rm openclaw-cli \
|
|
|
|
|
config get gateway.controlUi.allowedOrigins 2>/dev/null || true
|
|
|
|
|
)"
|
|
|
|
|
current_allowed_origins="${current_allowed_origins//$'\r'/}"
|
|
|
|
|
|
|
|
|
|
if [[ -n "$current_allowed_origins" && "$current_allowed_origins" != "null" && "$current_allowed_origins" != "[]" ]]; then
|
|
|
|
|
echo "Control UI allowlist already configured; leaving gateway.controlUi.allowedOrigins unchanged."
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
docker compose "${COMPOSE_ARGS[@]}" run --rm openclaw-cli \
|
|
|
|
|
config set gateway.controlUi.allowedOrigins "$allowed_origin_json" --strict-json >/dev/null
|
|
|
|
|
echo "Set gateway.controlUi.allowedOrigins to $allowed_origin_json for non-loopback bind."
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 08:28:35 +07:00
|
|
|
sync_gateway_mode_and_bind() {
|
|
|
|
|
docker compose "${COMPOSE_ARGS[@]}" run --rm openclaw-cli \
|
|
|
|
|
config set gateway.mode local >/dev/null
|
|
|
|
|
docker compose "${COMPOSE_ARGS[@]}" run --rm openclaw-cli \
|
|
|
|
|
config set gateway.bind "$OPENCLAW_GATEWAY_BIND" >/dev/null
|
|
|
|
|
echo "Pinned gateway.mode=local and gateway.bind=$OPENCLAW_GATEWAY_BIND for Docker setup."
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-19 10:44:38 +01:00
|
|
|
contains_disallowed_chars() {
|
|
|
|
|
local value="$1"
|
|
|
|
|
[[ "$value" == *$'\n'* || "$value" == *$'\r'* || "$value" == *$'\t'* ]]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
validate_mount_path_value() {
|
|
|
|
|
local label="$1"
|
|
|
|
|
local value="$2"
|
|
|
|
|
if [[ -z "$value" ]]; then
|
|
|
|
|
fail "$label cannot be empty."
|
|
|
|
|
fi
|
|
|
|
|
if contains_disallowed_chars "$value"; then
|
|
|
|
|
fail "$label contains unsupported control characters."
|
|
|
|
|
fi
|
|
|
|
|
if [[ "$value" =~ [[:space:]] ]]; then
|
|
|
|
|
fail "$label cannot contain whitespace."
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
validate_named_volume() {
|
|
|
|
|
local value="$1"
|
|
|
|
|
if [[ ! "$value" =~ ^[A-Za-z0-9][A-Za-z0-9_.-]*$ ]]; then
|
|
|
|
|
fail "OPENCLAW_HOME_VOLUME must match [A-Za-z0-9][A-Za-z0-9_.-]* when using a named volume."
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
validate_mount_spec() {
|
|
|
|
|
local mount="$1"
|
|
|
|
|
if contains_disallowed_chars "$mount"; then
|
|
|
|
|
fail "OPENCLAW_EXTRA_MOUNTS entries cannot contain control characters."
|
|
|
|
|
fi
|
|
|
|
|
# Keep mount specs strict to avoid YAML structure injection.
|
|
|
|
|
# Expected format: source:target[:options]
|
|
|
|
|
if [[ ! "$mount" =~ ^[^[:space:],:]+:[^[:space:],:]+(:[^[:space:],:]+)?$ ]]; then
|
|
|
|
|
fail "Invalid mount format '$mount'. Expected source:target[:options] without spaces."
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-02 13:52:08 +02:00
|
|
|
require_cmd docker
|
|
|
|
|
if ! docker compose version >/dev/null 2>&1; then
|
|
|
|
|
echo "Docker Compose not available (try: docker compose version)" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
feat(docker): add opt-in sandbox support for Docker deployments (#29974)
* feat(docker): add opt-in sandbox support for Docker deployments
Enable Docker-based sandbox isolation via OPENCLAW_SANDBOX=1 env var
in docker-setup.sh. This is a prerequisite for agents.defaults.sandbox
to function in any Docker deployment (self-hosted, Hostinger, DigitalOcean).
Changes:
- Dockerfile: add OPENCLAW_INSTALL_DOCKER_CLI build arg (~50MB, opt-in)
- docker-compose.yml: add commented-out docker.sock mount with docs
- docker-setup.sh: auto-detect Docker socket, inject mount, detect GID,
build sandbox image, configure sandbox defaults, add group_add
All changes are opt-in. Zero impact on existing deployments.
Usage: OPENCLAW_SANDBOX=1 ./docker-setup.sh
Closes #29933
Related: #7575, #7827, #28401, #10361, #12505, #28326
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address code review feedback on sandbox support
- Persist OPENCLAW_SANDBOX, DOCKER_GID, OPENCLAW_INSTALL_DOCKER_CLI
to .env via upsert_env so group_add survives re-runs
- Show config set errors instead of swallowing them silently;
report partial failure when sandbox config is incomplete
- Warn when Dockerfile.sandbox is missing but sandbox config
is still applied (sandbox image won't exist)
- Fix non-canonical whitespace in apt sources.list entry
by using printf instead of echo with line continuation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove `local` outside function and guard sandbox behind Docker CLI check
- Remove `local` keyword from top-level `sandbox_config_ok` assignment
which caused script exit under `set -euo pipefail` (bash `local`
outside a function is an error)
- Add Docker CLI prerequisite check for pre-built (non-local) images:
runs `docker --version` inside the container and skips sandbox setup
with a clear warning if the CLI is missing
- Split sandbox block so config is only applied after prerequisites pass
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: defer docker.sock mount until sandbox prerequisites pass
Move Docker socket mounting from the early setup phase (before image
build/pull) to a dedicated compose overlay created only after:
1. Docker CLI is verified inside the container image
2. /var/run/docker.sock exists on the host
Previously the socket was mounted optimistically at startup, leaving
the host Docker daemon exposed even when sandbox setup was later
skipped due to missing Docker CLI. Now the gateway starts without
the socket, and a docker-compose.sandbox.yml overlay is generated
only when all prerequisites pass. The gateway restart at the end of
sandbox setup picks up both the socket mount and sandbox config.
Also moves group_add from write_extra_compose() into the sandbox
overlay, keeping all sandbox-specific compose configuration together.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(docker): fix sandbox docs URL in setup output
* Docker: harden sandbox setup fallback behavior
* Tests: cover docker-setup sandbox edge paths
* Docker: roll back sandbox mode on partial config failure
* Tests: assert sandbox mode rollback on partial setup
* Docs: document Docker sandbox bootstrap env controls
* Changelog: credit Docker sandbox bootstrap hardening
* Update CHANGELOG.md
* Docker: verify Docker apt signing key fingerprint
* Docker: avoid sandbox overlay deps during policy writes
* Tests: assert no-deps sandbox rollback gateway recreate
* Docs: mention OPENCLAW_INSTALL_DOCKER_CLI in Docker env vars
---------
Co-authored-by: Jakub Karwowski <jakubkarwowski@Mac.lan>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-02 08:06:10 +01:00
|
|
|
if [[ -z "$DOCKER_SOCKET_PATH" && "${DOCKER_HOST:-}" == unix://* ]]; then
|
|
|
|
|
DOCKER_SOCKET_PATH="${DOCKER_HOST#unix://}"
|
|
|
|
|
fi
|
|
|
|
|
if [[ -z "$DOCKER_SOCKET_PATH" ]]; then
|
|
|
|
|
DOCKER_SOCKET_PATH="/var/run/docker.sock"
|
|
|
|
|
fi
|
|
|
|
|
if is_truthy_value "$RAW_SANDBOX_SETTING"; then
|
|
|
|
|
SANDBOX_ENABLED="1"
|
|
|
|
|
fi
|
|
|
|
|
|
2026-01-30 03:15:10 +01:00
|
|
|
OPENCLAW_CONFIG_DIR="${OPENCLAW_CONFIG_DIR:-$HOME/.openclaw}"
|
|
|
|
|
OPENCLAW_WORKSPACE_DIR="${OPENCLAW_WORKSPACE_DIR:-$HOME/.openclaw/workspace}"
|
2026-01-02 13:52:08 +02:00
|
|
|
|
2026-02-19 10:44:38 +01:00
|
|
|
validate_mount_path_value "OPENCLAW_CONFIG_DIR" "$OPENCLAW_CONFIG_DIR"
|
|
|
|
|
validate_mount_path_value "OPENCLAW_WORKSPACE_DIR" "$OPENCLAW_WORKSPACE_DIR"
|
|
|
|
|
if [[ -n "$HOME_VOLUME_NAME" ]]; then
|
|
|
|
|
if [[ "$HOME_VOLUME_NAME" == *"/"* ]]; then
|
|
|
|
|
validate_mount_path_value "OPENCLAW_HOME_VOLUME" "$HOME_VOLUME_NAME"
|
|
|
|
|
else
|
|
|
|
|
validate_named_volume "$HOME_VOLUME_NAME"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
if contains_disallowed_chars "$EXTRA_MOUNTS"; then
|
|
|
|
|
fail "OPENCLAW_EXTRA_MOUNTS cannot contain control characters."
|
|
|
|
|
fi
|
feat(docker): add opt-in sandbox support for Docker deployments (#29974)
* feat(docker): add opt-in sandbox support for Docker deployments
Enable Docker-based sandbox isolation via OPENCLAW_SANDBOX=1 env var
in docker-setup.sh. This is a prerequisite for agents.defaults.sandbox
to function in any Docker deployment (self-hosted, Hostinger, DigitalOcean).
Changes:
- Dockerfile: add OPENCLAW_INSTALL_DOCKER_CLI build arg (~50MB, opt-in)
- docker-compose.yml: add commented-out docker.sock mount with docs
- docker-setup.sh: auto-detect Docker socket, inject mount, detect GID,
build sandbox image, configure sandbox defaults, add group_add
All changes are opt-in. Zero impact on existing deployments.
Usage: OPENCLAW_SANDBOX=1 ./docker-setup.sh
Closes #29933
Related: #7575, #7827, #28401, #10361, #12505, #28326
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address code review feedback on sandbox support
- Persist OPENCLAW_SANDBOX, DOCKER_GID, OPENCLAW_INSTALL_DOCKER_CLI
to .env via upsert_env so group_add survives re-runs
- Show config set errors instead of swallowing them silently;
report partial failure when sandbox config is incomplete
- Warn when Dockerfile.sandbox is missing but sandbox config
is still applied (sandbox image won't exist)
- Fix non-canonical whitespace in apt sources.list entry
by using printf instead of echo with line continuation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove `local` outside function and guard sandbox behind Docker CLI check
- Remove `local` keyword from top-level `sandbox_config_ok` assignment
which caused script exit under `set -euo pipefail` (bash `local`
outside a function is an error)
- Add Docker CLI prerequisite check for pre-built (non-local) images:
runs `docker --version` inside the container and skips sandbox setup
with a clear warning if the CLI is missing
- Split sandbox block so config is only applied after prerequisites pass
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: defer docker.sock mount until sandbox prerequisites pass
Move Docker socket mounting from the early setup phase (before image
build/pull) to a dedicated compose overlay created only after:
1. Docker CLI is verified inside the container image
2. /var/run/docker.sock exists on the host
Previously the socket was mounted optimistically at startup, leaving
the host Docker daemon exposed even when sandbox setup was later
skipped due to missing Docker CLI. Now the gateway starts without
the socket, and a docker-compose.sandbox.yml overlay is generated
only when all prerequisites pass. The gateway restart at the end of
sandbox setup picks up both the socket mount and sandbox config.
Also moves group_add from write_extra_compose() into the sandbox
overlay, keeping all sandbox-specific compose configuration together.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(docker): fix sandbox docs URL in setup output
* Docker: harden sandbox setup fallback behavior
* Tests: cover docker-setup sandbox edge paths
* Docker: roll back sandbox mode on partial config failure
* Tests: assert sandbox mode rollback on partial setup
* Docs: document Docker sandbox bootstrap env controls
* Changelog: credit Docker sandbox bootstrap hardening
* Update CHANGELOG.md
* Docker: verify Docker apt signing key fingerprint
* Docker: avoid sandbox overlay deps during policy writes
* Tests: assert no-deps sandbox rollback gateway recreate
* Docs: mention OPENCLAW_INSTALL_DOCKER_CLI in Docker env vars
---------
Co-authored-by: Jakub Karwowski <jakubkarwowski@Mac.lan>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-02 08:06:10 +01:00
|
|
|
if [[ -n "$SANDBOX_ENABLED" ]]; then
|
|
|
|
|
validate_mount_path_value "OPENCLAW_DOCKER_SOCKET" "$DOCKER_SOCKET_PATH"
|
|
|
|
|
fi
|
2026-02-19 10:44:38 +01:00
|
|
|
|
2026-01-30 03:15:10 +01:00
|
|
|
mkdir -p "$OPENCLAW_CONFIG_DIR"
|
|
|
|
|
mkdir -p "$OPENCLAW_WORKSPACE_DIR"
|
2026-03-01 22:07:34 -04:00
|
|
|
# Seed directory tree eagerly so bind mounts work even on Docker Desktop/Windows
|
|
|
|
|
# where the container (even as root) cannot create new host subdirectories.
|
2026-02-22 16:33:02 -08:00
|
|
|
mkdir -p "$OPENCLAW_CONFIG_DIR/identity"
|
2026-03-01 22:07:34 -04:00
|
|
|
mkdir -p "$OPENCLAW_CONFIG_DIR/agents/main/agent"
|
|
|
|
|
mkdir -p "$OPENCLAW_CONFIG_DIR/agents/main/sessions"
|
2026-01-02 13:52:08 +02:00
|
|
|
|
2026-01-30 03:15:10 +01:00
|
|
|
export OPENCLAW_CONFIG_DIR
|
|
|
|
|
export OPENCLAW_WORKSPACE_DIR
|
|
|
|
|
export OPENCLAW_GATEWAY_PORT="${OPENCLAW_GATEWAY_PORT:-18789}"
|
|
|
|
|
export OPENCLAW_BRIDGE_PORT="${OPENCLAW_BRIDGE_PORT:-18790}"
|
|
|
|
|
export OPENCLAW_GATEWAY_BIND="${OPENCLAW_GATEWAY_BIND:-lan}"
|
|
|
|
|
export OPENCLAW_IMAGE="$IMAGE_NAME"
|
|
|
|
|
export OPENCLAW_DOCKER_APT_PACKAGES="${OPENCLAW_DOCKER_APT_PACKAGES:-}"
|
2026-03-06 12:18:42 -05:00
|
|
|
export OPENCLAW_EXTENSIONS="${OPENCLAW_EXTENSIONS:-}"
|
2026-01-30 03:15:10 +01:00
|
|
|
export OPENCLAW_EXTRA_MOUNTS="$EXTRA_MOUNTS"
|
|
|
|
|
export OPENCLAW_HOME_VOLUME="$HOME_VOLUME_NAME"
|
2026-03-01 23:49:45 -05:00
|
|
|
export OPENCLAW_ALLOW_INSECURE_PRIVATE_WS="${OPENCLAW_ALLOW_INSECURE_PRIVATE_WS:-}"
|
feat(docker): add opt-in sandbox support for Docker deployments (#29974)
* feat(docker): add opt-in sandbox support for Docker deployments
Enable Docker-based sandbox isolation via OPENCLAW_SANDBOX=1 env var
in docker-setup.sh. This is a prerequisite for agents.defaults.sandbox
to function in any Docker deployment (self-hosted, Hostinger, DigitalOcean).
Changes:
- Dockerfile: add OPENCLAW_INSTALL_DOCKER_CLI build arg (~50MB, opt-in)
- docker-compose.yml: add commented-out docker.sock mount with docs
- docker-setup.sh: auto-detect Docker socket, inject mount, detect GID,
build sandbox image, configure sandbox defaults, add group_add
All changes are opt-in. Zero impact on existing deployments.
Usage: OPENCLAW_SANDBOX=1 ./docker-setup.sh
Closes #29933
Related: #7575, #7827, #28401, #10361, #12505, #28326
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address code review feedback on sandbox support
- Persist OPENCLAW_SANDBOX, DOCKER_GID, OPENCLAW_INSTALL_DOCKER_CLI
to .env via upsert_env so group_add survives re-runs
- Show config set errors instead of swallowing them silently;
report partial failure when sandbox config is incomplete
- Warn when Dockerfile.sandbox is missing but sandbox config
is still applied (sandbox image won't exist)
- Fix non-canonical whitespace in apt sources.list entry
by using printf instead of echo with line continuation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove `local` outside function and guard sandbox behind Docker CLI check
- Remove `local` keyword from top-level `sandbox_config_ok` assignment
which caused script exit under `set -euo pipefail` (bash `local`
outside a function is an error)
- Add Docker CLI prerequisite check for pre-built (non-local) images:
runs `docker --version` inside the container and skips sandbox setup
with a clear warning if the CLI is missing
- Split sandbox block so config is only applied after prerequisites pass
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: defer docker.sock mount until sandbox prerequisites pass
Move Docker socket mounting from the early setup phase (before image
build/pull) to a dedicated compose overlay created only after:
1. Docker CLI is verified inside the container image
2. /var/run/docker.sock exists on the host
Previously the socket was mounted optimistically at startup, leaving
the host Docker daemon exposed even when sandbox setup was later
skipped due to missing Docker CLI. Now the gateway starts without
the socket, and a docker-compose.sandbox.yml overlay is generated
only when all prerequisites pass. The gateway restart at the end of
sandbox setup picks up both the socket mount and sandbox config.
Also moves group_add from write_extra_compose() into the sandbox
overlay, keeping all sandbox-specific compose configuration together.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(docker): fix sandbox docs URL in setup output
* Docker: harden sandbox setup fallback behavior
* Tests: cover docker-setup sandbox edge paths
* Docker: roll back sandbox mode on partial config failure
* Tests: assert sandbox mode rollback on partial setup
* Docs: document Docker sandbox bootstrap env controls
* Changelog: credit Docker sandbox bootstrap hardening
* Update CHANGELOG.md
* Docker: verify Docker apt signing key fingerprint
* Docker: avoid sandbox overlay deps during policy writes
* Tests: assert no-deps sandbox rollback gateway recreate
* Docs: mention OPENCLAW_INSTALL_DOCKER_CLI in Docker env vars
---------
Co-authored-by: Jakub Karwowski <jakubkarwowski@Mac.lan>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-02 08:06:10 +01:00
|
|
|
export OPENCLAW_SANDBOX="$SANDBOX_ENABLED"
|
|
|
|
|
export OPENCLAW_DOCKER_SOCKET="$DOCKER_SOCKET_PATH"
|
|
|
|
|
|
|
|
|
|
# Detect Docker socket GID for sandbox group_add.
|
|
|
|
|
DOCKER_GID=""
|
|
|
|
|
if [[ -n "$SANDBOX_ENABLED" && -S "$DOCKER_SOCKET_PATH" ]]; then
|
|
|
|
|
DOCKER_GID="$(stat -c '%g' "$DOCKER_SOCKET_PATH" 2>/dev/null || stat -f '%g' "$DOCKER_SOCKET_PATH" 2>/dev/null || echo "")"
|
|
|
|
|
fi
|
|
|
|
|
export DOCKER_GID
|
2026-01-30 03:15:10 +01:00
|
|
|
|
|
|
|
|
if [[ -z "${OPENCLAW_GATEWAY_TOKEN:-}" ]]; then
|
2026-02-26 05:45:57 +01:00
|
|
|
EXISTING_CONFIG_TOKEN="$(read_config_gateway_token || true)"
|
|
|
|
|
if [[ -n "$EXISTING_CONFIG_TOKEN" ]]; then
|
|
|
|
|
OPENCLAW_GATEWAY_TOKEN="$EXISTING_CONFIG_TOKEN"
|
|
|
|
|
echo "Reusing gateway token from $OPENCLAW_CONFIG_DIR/openclaw.json"
|
2026-01-02 13:52:08 +02:00
|
|
|
else
|
2026-03-07 23:41:35 +00:00
|
|
|
DOTENV_GATEWAY_TOKEN="$(read_env_gateway_token "$ROOT_DIR/.env" || true)"
|
|
|
|
|
if [[ -n "$DOTENV_GATEWAY_TOKEN" ]]; then
|
|
|
|
|
OPENCLAW_GATEWAY_TOKEN="$DOTENV_GATEWAY_TOKEN"
|
|
|
|
|
echo "Reusing gateway token from $ROOT_DIR/.env"
|
|
|
|
|
elif command -v openssl >/dev/null 2>&1; then
|
|
|
|
|
OPENCLAW_GATEWAY_TOKEN="$(openssl rand -hex 32)"
|
|
|
|
|
else
|
|
|
|
|
OPENCLAW_GATEWAY_TOKEN="$(python3 - <<'PY'
|
2026-01-02 13:52:08 +02:00
|
|
|
import secrets
|
|
|
|
|
print(secrets.token_hex(32))
|
|
|
|
|
PY
|
|
|
|
|
)"
|
2026-03-07 23:41:35 +00:00
|
|
|
fi
|
2026-01-02 13:52:08 +02:00
|
|
|
fi
|
|
|
|
|
fi
|
2026-01-30 03:15:10 +01:00
|
|
|
export OPENCLAW_GATEWAY_TOKEN
|
2026-01-02 13:52:08 +02:00
|
|
|
|
2026-01-10 20:12:23 +00:00
|
|
|
COMPOSE_FILES=("$COMPOSE_FILE")
|
|
|
|
|
COMPOSE_ARGS=()
|
|
|
|
|
|
|
|
|
|
write_extra_compose() {
|
2026-01-10 22:42:57 +01:00
|
|
|
local home_volume="$1"
|
|
|
|
|
shift
|
2026-01-10 20:12:23 +00:00
|
|
|
local mount
|
2026-02-19 10:44:38 +01:00
|
|
|
local gateway_home_mount
|
|
|
|
|
local gateway_config_mount
|
|
|
|
|
local gateway_workspace_mount
|
2026-01-10 20:12:23 +00:00
|
|
|
|
|
|
|
|
cat >"$EXTRA_COMPOSE_FILE" <<'YAML'
|
|
|
|
|
services:
|
2026-01-30 03:15:10 +01:00
|
|
|
openclaw-gateway:
|
2026-01-10 20:12:23 +00:00
|
|
|
volumes:
|
|
|
|
|
YAML
|
|
|
|
|
|
|
|
|
|
if [[ -n "$home_volume" ]]; then
|
2026-02-19 10:44:38 +01:00
|
|
|
gateway_home_mount="${home_volume}:/home/node"
|
|
|
|
|
gateway_config_mount="${OPENCLAW_CONFIG_DIR}:/home/node/.openclaw"
|
|
|
|
|
gateway_workspace_mount="${OPENCLAW_WORKSPACE_DIR}:/home/node/.openclaw/workspace"
|
|
|
|
|
validate_mount_spec "$gateway_home_mount"
|
|
|
|
|
validate_mount_spec "$gateway_config_mount"
|
|
|
|
|
validate_mount_spec "$gateway_workspace_mount"
|
|
|
|
|
printf ' - %s\n' "$gateway_home_mount" >>"$EXTRA_COMPOSE_FILE"
|
|
|
|
|
printf ' - %s\n' "$gateway_config_mount" >>"$EXTRA_COMPOSE_FILE"
|
|
|
|
|
printf ' - %s\n' "$gateway_workspace_mount" >>"$EXTRA_COMPOSE_FILE"
|
2026-01-10 20:12:23 +00:00
|
|
|
fi
|
|
|
|
|
|
2026-02-11 01:55:43 +11:00
|
|
|
for mount in "$@"; do
|
2026-02-19 10:44:38 +01:00
|
|
|
validate_mount_spec "$mount"
|
2026-01-10 20:12:23 +00:00
|
|
|
printf ' - %s\n' "$mount" >>"$EXTRA_COMPOSE_FILE"
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
cat >>"$EXTRA_COMPOSE_FILE" <<'YAML'
|
2026-01-30 03:15:10 +01:00
|
|
|
openclaw-cli:
|
2026-01-10 20:12:23 +00:00
|
|
|
volumes:
|
|
|
|
|
YAML
|
|
|
|
|
|
|
|
|
|
if [[ -n "$home_volume" ]]; then
|
2026-02-19 10:44:38 +01:00
|
|
|
printf ' - %s\n' "$gateway_home_mount" >>"$EXTRA_COMPOSE_FILE"
|
|
|
|
|
printf ' - %s\n' "$gateway_config_mount" >>"$EXTRA_COMPOSE_FILE"
|
|
|
|
|
printf ' - %s\n' "$gateway_workspace_mount" >>"$EXTRA_COMPOSE_FILE"
|
2026-01-10 20:12:23 +00:00
|
|
|
fi
|
|
|
|
|
|
2026-02-11 01:55:43 +11:00
|
|
|
for mount in "$@"; do
|
2026-02-19 10:44:38 +01:00
|
|
|
validate_mount_spec "$mount"
|
2026-01-10 20:12:23 +00:00
|
|
|
printf ' - %s\n' "$mount" >>"$EXTRA_COMPOSE_FILE"
|
|
|
|
|
done
|
|
|
|
|
|
2026-01-10 22:42:57 +01:00
|
|
|
if [[ -n "$home_volume" && "$home_volume" != *"/"* ]]; then
|
2026-02-19 10:44:38 +01:00
|
|
|
validate_named_volume "$home_volume"
|
2026-01-10 20:12:23 +00:00
|
|
|
cat >>"$EXTRA_COMPOSE_FILE" <<YAML
|
|
|
|
|
volumes:
|
|
|
|
|
${home_volume}:
|
|
|
|
|
YAML
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
feat(docker): add opt-in sandbox support for Docker deployments (#29974)
* feat(docker): add opt-in sandbox support for Docker deployments
Enable Docker-based sandbox isolation via OPENCLAW_SANDBOX=1 env var
in docker-setup.sh. This is a prerequisite for agents.defaults.sandbox
to function in any Docker deployment (self-hosted, Hostinger, DigitalOcean).
Changes:
- Dockerfile: add OPENCLAW_INSTALL_DOCKER_CLI build arg (~50MB, opt-in)
- docker-compose.yml: add commented-out docker.sock mount with docs
- docker-setup.sh: auto-detect Docker socket, inject mount, detect GID,
build sandbox image, configure sandbox defaults, add group_add
All changes are opt-in. Zero impact on existing deployments.
Usage: OPENCLAW_SANDBOX=1 ./docker-setup.sh
Closes #29933
Related: #7575, #7827, #28401, #10361, #12505, #28326
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address code review feedback on sandbox support
- Persist OPENCLAW_SANDBOX, DOCKER_GID, OPENCLAW_INSTALL_DOCKER_CLI
to .env via upsert_env so group_add survives re-runs
- Show config set errors instead of swallowing them silently;
report partial failure when sandbox config is incomplete
- Warn when Dockerfile.sandbox is missing but sandbox config
is still applied (sandbox image won't exist)
- Fix non-canonical whitespace in apt sources.list entry
by using printf instead of echo with line continuation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove `local` outside function and guard sandbox behind Docker CLI check
- Remove `local` keyword from top-level `sandbox_config_ok` assignment
which caused script exit under `set -euo pipefail` (bash `local`
outside a function is an error)
- Add Docker CLI prerequisite check for pre-built (non-local) images:
runs `docker --version` inside the container and skips sandbox setup
with a clear warning if the CLI is missing
- Split sandbox block so config is only applied after prerequisites pass
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: defer docker.sock mount until sandbox prerequisites pass
Move Docker socket mounting from the early setup phase (before image
build/pull) to a dedicated compose overlay created only after:
1. Docker CLI is verified inside the container image
2. /var/run/docker.sock exists on the host
Previously the socket was mounted optimistically at startup, leaving
the host Docker daemon exposed even when sandbox setup was later
skipped due to missing Docker CLI. Now the gateway starts without
the socket, and a docker-compose.sandbox.yml overlay is generated
only when all prerequisites pass. The gateway restart at the end of
sandbox setup picks up both the socket mount and sandbox config.
Also moves group_add from write_extra_compose() into the sandbox
overlay, keeping all sandbox-specific compose configuration together.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(docker): fix sandbox docs URL in setup output
* Docker: harden sandbox setup fallback behavior
* Tests: cover docker-setup sandbox edge paths
* Docker: roll back sandbox mode on partial config failure
* Tests: assert sandbox mode rollback on partial setup
* Docs: document Docker sandbox bootstrap env controls
* Changelog: credit Docker sandbox bootstrap hardening
* Update CHANGELOG.md
* Docker: verify Docker apt signing key fingerprint
* Docker: avoid sandbox overlay deps during policy writes
* Tests: assert no-deps sandbox rollback gateway recreate
* Docs: mention OPENCLAW_INSTALL_DOCKER_CLI in Docker env vars
---------
Co-authored-by: Jakub Karwowski <jakubkarwowski@Mac.lan>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-02 08:06:10 +01:00
|
|
|
# When sandbox is requested, ensure Docker CLI build arg is set for local builds.
|
|
|
|
|
# Docker socket mount is deferred until sandbox prerequisites are verified.
|
|
|
|
|
if [[ -n "$SANDBOX_ENABLED" ]]; then
|
|
|
|
|
if [[ -z "${OPENCLAW_INSTALL_DOCKER_CLI:-}" ]]; then
|
|
|
|
|
export OPENCLAW_INSTALL_DOCKER_CLI=1
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
2026-01-10 22:42:57 +01:00
|
|
|
VALID_MOUNTS=()
|
|
|
|
|
if [[ -n "$EXTRA_MOUNTS" ]]; then
|
|
|
|
|
IFS=',' read -r -a mounts <<<"$EXTRA_MOUNTS"
|
|
|
|
|
for mount in "${mounts[@]}"; do
|
|
|
|
|
mount="${mount#"${mount%%[![:space:]]*}"}"
|
|
|
|
|
mount="${mount%"${mount##*[![:space:]]}"}"
|
|
|
|
|
if [[ -n "$mount" ]]; then
|
|
|
|
|
VALID_MOUNTS+=("$mount")
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [[ -n "$HOME_VOLUME_NAME" || ${#VALID_MOUNTS[@]} -gt 0 ]]; then
|
2026-02-11 01:55:43 +11:00
|
|
|
# Bash 3.2 + nounset treats "${array[@]}" on an empty array as unbound.
|
|
|
|
|
if [[ ${#VALID_MOUNTS[@]} -gt 0 ]]; then
|
|
|
|
|
write_extra_compose "$HOME_VOLUME_NAME" "${VALID_MOUNTS[@]}"
|
|
|
|
|
else
|
|
|
|
|
write_extra_compose "$HOME_VOLUME_NAME"
|
|
|
|
|
fi
|
2026-01-10 20:12:23 +00:00
|
|
|
COMPOSE_FILES+=("$EXTRA_COMPOSE_FILE")
|
|
|
|
|
fi
|
|
|
|
|
for compose_file in "${COMPOSE_FILES[@]}"; do
|
|
|
|
|
COMPOSE_ARGS+=("-f" "$compose_file")
|
|
|
|
|
done
|
feat(docker): add opt-in sandbox support for Docker deployments (#29974)
* feat(docker): add opt-in sandbox support for Docker deployments
Enable Docker-based sandbox isolation via OPENCLAW_SANDBOX=1 env var
in docker-setup.sh. This is a prerequisite for agents.defaults.sandbox
to function in any Docker deployment (self-hosted, Hostinger, DigitalOcean).
Changes:
- Dockerfile: add OPENCLAW_INSTALL_DOCKER_CLI build arg (~50MB, opt-in)
- docker-compose.yml: add commented-out docker.sock mount with docs
- docker-setup.sh: auto-detect Docker socket, inject mount, detect GID,
build sandbox image, configure sandbox defaults, add group_add
All changes are opt-in. Zero impact on existing deployments.
Usage: OPENCLAW_SANDBOX=1 ./docker-setup.sh
Closes #29933
Related: #7575, #7827, #28401, #10361, #12505, #28326
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address code review feedback on sandbox support
- Persist OPENCLAW_SANDBOX, DOCKER_GID, OPENCLAW_INSTALL_DOCKER_CLI
to .env via upsert_env so group_add survives re-runs
- Show config set errors instead of swallowing them silently;
report partial failure when sandbox config is incomplete
- Warn when Dockerfile.sandbox is missing but sandbox config
is still applied (sandbox image won't exist)
- Fix non-canonical whitespace in apt sources.list entry
by using printf instead of echo with line continuation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove `local` outside function and guard sandbox behind Docker CLI check
- Remove `local` keyword from top-level `sandbox_config_ok` assignment
which caused script exit under `set -euo pipefail` (bash `local`
outside a function is an error)
- Add Docker CLI prerequisite check for pre-built (non-local) images:
runs `docker --version` inside the container and skips sandbox setup
with a clear warning if the CLI is missing
- Split sandbox block so config is only applied after prerequisites pass
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: defer docker.sock mount until sandbox prerequisites pass
Move Docker socket mounting from the early setup phase (before image
build/pull) to a dedicated compose overlay created only after:
1. Docker CLI is verified inside the container image
2. /var/run/docker.sock exists on the host
Previously the socket was mounted optimistically at startup, leaving
the host Docker daemon exposed even when sandbox setup was later
skipped due to missing Docker CLI. Now the gateway starts without
the socket, and a docker-compose.sandbox.yml overlay is generated
only when all prerequisites pass. The gateway restart at the end of
sandbox setup picks up both the socket mount and sandbox config.
Also moves group_add from write_extra_compose() into the sandbox
overlay, keeping all sandbox-specific compose configuration together.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(docker): fix sandbox docs URL in setup output
* Docker: harden sandbox setup fallback behavior
* Tests: cover docker-setup sandbox edge paths
* Docker: roll back sandbox mode on partial config failure
* Tests: assert sandbox mode rollback on partial setup
* Docs: document Docker sandbox bootstrap env controls
* Changelog: credit Docker sandbox bootstrap hardening
* Update CHANGELOG.md
* Docker: verify Docker apt signing key fingerprint
* Docker: avoid sandbox overlay deps during policy writes
* Tests: assert no-deps sandbox rollback gateway recreate
* Docs: mention OPENCLAW_INSTALL_DOCKER_CLI in Docker env vars
---------
Co-authored-by: Jakub Karwowski <jakubkarwowski@Mac.lan>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-02 08:06:10 +01:00
|
|
|
# Keep a base compose arg set without sandbox overlay so rollback paths can
|
|
|
|
|
# force a known-safe gateway service definition (no docker.sock mount).
|
|
|
|
|
BASE_COMPOSE_ARGS=("${COMPOSE_ARGS[@]}")
|
2026-01-10 20:12:23 +00:00
|
|
|
COMPOSE_HINT="docker compose"
|
|
|
|
|
for compose_file in "${COMPOSE_FILES[@]}"; do
|
|
|
|
|
COMPOSE_HINT+=" -f ${compose_file}"
|
|
|
|
|
done
|
|
|
|
|
|
2026-01-02 13:52:08 +02:00
|
|
|
ENV_FILE="$ROOT_DIR/.env"
|
|
|
|
|
upsert_env() {
|
|
|
|
|
local file="$1"
|
|
|
|
|
shift
|
|
|
|
|
local -a keys=("$@")
|
|
|
|
|
local tmp
|
|
|
|
|
tmp="$(mktemp)"
|
2026-02-11 01:55:43 +11:00
|
|
|
# Use a delimited string instead of an associative array so the script
|
|
|
|
|
# works with Bash 3.2 (macOS default) which lacks `declare -A`.
|
|
|
|
|
local seen=" "
|
2026-01-02 13:52:08 +02:00
|
|
|
|
|
|
|
|
if [[ -f "$file" ]]; then
|
|
|
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
|
|
|
local key="${line%%=*}"
|
|
|
|
|
local replaced=false
|
|
|
|
|
for k in "${keys[@]}"; do
|
|
|
|
|
if [[ "$key" == "$k" ]]; then
|
2026-01-11 14:43:28 +01:00
|
|
|
printf '%s=%s\n' "$k" "${!k-}" >>"$tmp"
|
2026-02-11 01:55:43 +11:00
|
|
|
seen="$seen$k "
|
2026-01-02 13:52:08 +02:00
|
|
|
replaced=true
|
|
|
|
|
break
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
if [[ "$replaced" == false ]]; then
|
|
|
|
|
printf '%s\n' "$line" >>"$tmp"
|
|
|
|
|
fi
|
|
|
|
|
done <"$file"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
for k in "${keys[@]}"; do
|
2026-02-11 01:55:43 +11:00
|
|
|
if [[ "$seen" != *" $k "* ]]; then
|
2026-01-11 14:43:28 +01:00
|
|
|
printf '%s=%s\n' "$k" "${!k-}" >>"$tmp"
|
2026-01-02 13:52:08 +02:00
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
mv "$tmp" "$file"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
upsert_env "$ENV_FILE" \
|
2026-01-30 03:15:10 +01:00
|
|
|
OPENCLAW_CONFIG_DIR \
|
|
|
|
|
OPENCLAW_WORKSPACE_DIR \
|
|
|
|
|
OPENCLAW_GATEWAY_PORT \
|
|
|
|
|
OPENCLAW_BRIDGE_PORT \
|
|
|
|
|
OPENCLAW_GATEWAY_BIND \
|
|
|
|
|
OPENCLAW_GATEWAY_TOKEN \
|
|
|
|
|
OPENCLAW_IMAGE \
|
|
|
|
|
OPENCLAW_EXTRA_MOUNTS \
|
|
|
|
|
OPENCLAW_HOME_VOLUME \
|
2026-03-01 23:49:45 -05:00
|
|
|
OPENCLAW_DOCKER_APT_PACKAGES \
|
2026-03-06 12:18:42 -05:00
|
|
|
OPENCLAW_EXTENSIONS \
|
feat(docker): add opt-in sandbox support for Docker deployments (#29974)
* feat(docker): add opt-in sandbox support for Docker deployments
Enable Docker-based sandbox isolation via OPENCLAW_SANDBOX=1 env var
in docker-setup.sh. This is a prerequisite for agents.defaults.sandbox
to function in any Docker deployment (self-hosted, Hostinger, DigitalOcean).
Changes:
- Dockerfile: add OPENCLAW_INSTALL_DOCKER_CLI build arg (~50MB, opt-in)
- docker-compose.yml: add commented-out docker.sock mount with docs
- docker-setup.sh: auto-detect Docker socket, inject mount, detect GID,
build sandbox image, configure sandbox defaults, add group_add
All changes are opt-in. Zero impact on existing deployments.
Usage: OPENCLAW_SANDBOX=1 ./docker-setup.sh
Closes #29933
Related: #7575, #7827, #28401, #10361, #12505, #28326
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address code review feedback on sandbox support
- Persist OPENCLAW_SANDBOX, DOCKER_GID, OPENCLAW_INSTALL_DOCKER_CLI
to .env via upsert_env so group_add survives re-runs
- Show config set errors instead of swallowing them silently;
report partial failure when sandbox config is incomplete
- Warn when Dockerfile.sandbox is missing but sandbox config
is still applied (sandbox image won't exist)
- Fix non-canonical whitespace in apt sources.list entry
by using printf instead of echo with line continuation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove `local` outside function and guard sandbox behind Docker CLI check
- Remove `local` keyword from top-level `sandbox_config_ok` assignment
which caused script exit under `set -euo pipefail` (bash `local`
outside a function is an error)
- Add Docker CLI prerequisite check for pre-built (non-local) images:
runs `docker --version` inside the container and skips sandbox setup
with a clear warning if the CLI is missing
- Split sandbox block so config is only applied after prerequisites pass
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: defer docker.sock mount until sandbox prerequisites pass
Move Docker socket mounting from the early setup phase (before image
build/pull) to a dedicated compose overlay created only after:
1. Docker CLI is verified inside the container image
2. /var/run/docker.sock exists on the host
Previously the socket was mounted optimistically at startup, leaving
the host Docker daemon exposed even when sandbox setup was later
skipped due to missing Docker CLI. Now the gateway starts without
the socket, and a docker-compose.sandbox.yml overlay is generated
only when all prerequisites pass. The gateway restart at the end of
sandbox setup picks up both the socket mount and sandbox config.
Also moves group_add from write_extra_compose() into the sandbox
overlay, keeping all sandbox-specific compose configuration together.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(docker): fix sandbox docs URL in setup output
* Docker: harden sandbox setup fallback behavior
* Tests: cover docker-setup sandbox edge paths
* Docker: roll back sandbox mode on partial config failure
* Tests: assert sandbox mode rollback on partial setup
* Docs: document Docker sandbox bootstrap env controls
* Changelog: credit Docker sandbox bootstrap hardening
* Update CHANGELOG.md
* Docker: verify Docker apt signing key fingerprint
* Docker: avoid sandbox overlay deps during policy writes
* Tests: assert no-deps sandbox rollback gateway recreate
* Docs: mention OPENCLAW_INSTALL_DOCKER_CLI in Docker env vars
---------
Co-authored-by: Jakub Karwowski <jakubkarwowski@Mac.lan>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-02 08:06:10 +01:00
|
|
|
OPENCLAW_SANDBOX \
|
|
|
|
|
OPENCLAW_DOCKER_SOCKET \
|
|
|
|
|
DOCKER_GID \
|
|
|
|
|
OPENCLAW_INSTALL_DOCKER_CLI \
|
2026-03-01 23:49:45 -05:00
|
|
|
OPENCLAW_ALLOW_INSECURE_PRIVATE_WS
|
2026-01-02 13:52:08 +02:00
|
|
|
|
2026-02-03 23:27:34 +11:00
|
|
|
if [[ "$IMAGE_NAME" == "openclaw:local" ]]; then
|
2026-02-03 23:16:56 +11:00
|
|
|
echo "==> Building Docker image: $IMAGE_NAME"
|
|
|
|
|
docker build \
|
|
|
|
|
--build-arg "OPENCLAW_DOCKER_APT_PACKAGES=${OPENCLAW_DOCKER_APT_PACKAGES}" \
|
2026-03-06 12:18:42 -05:00
|
|
|
--build-arg "OPENCLAW_EXTENSIONS=${OPENCLAW_EXTENSIONS}" \
|
feat(docker): add opt-in sandbox support for Docker deployments (#29974)
* feat(docker): add opt-in sandbox support for Docker deployments
Enable Docker-based sandbox isolation via OPENCLAW_SANDBOX=1 env var
in docker-setup.sh. This is a prerequisite for agents.defaults.sandbox
to function in any Docker deployment (self-hosted, Hostinger, DigitalOcean).
Changes:
- Dockerfile: add OPENCLAW_INSTALL_DOCKER_CLI build arg (~50MB, opt-in)
- docker-compose.yml: add commented-out docker.sock mount with docs
- docker-setup.sh: auto-detect Docker socket, inject mount, detect GID,
build sandbox image, configure sandbox defaults, add group_add
All changes are opt-in. Zero impact on existing deployments.
Usage: OPENCLAW_SANDBOX=1 ./docker-setup.sh
Closes #29933
Related: #7575, #7827, #28401, #10361, #12505, #28326
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address code review feedback on sandbox support
- Persist OPENCLAW_SANDBOX, DOCKER_GID, OPENCLAW_INSTALL_DOCKER_CLI
to .env via upsert_env so group_add survives re-runs
- Show config set errors instead of swallowing them silently;
report partial failure when sandbox config is incomplete
- Warn when Dockerfile.sandbox is missing but sandbox config
is still applied (sandbox image won't exist)
- Fix non-canonical whitespace in apt sources.list entry
by using printf instead of echo with line continuation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove `local` outside function and guard sandbox behind Docker CLI check
- Remove `local` keyword from top-level `sandbox_config_ok` assignment
which caused script exit under `set -euo pipefail` (bash `local`
outside a function is an error)
- Add Docker CLI prerequisite check for pre-built (non-local) images:
runs `docker --version` inside the container and skips sandbox setup
with a clear warning if the CLI is missing
- Split sandbox block so config is only applied after prerequisites pass
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: defer docker.sock mount until sandbox prerequisites pass
Move Docker socket mounting from the early setup phase (before image
build/pull) to a dedicated compose overlay created only after:
1. Docker CLI is verified inside the container image
2. /var/run/docker.sock exists on the host
Previously the socket was mounted optimistically at startup, leaving
the host Docker daemon exposed even when sandbox setup was later
skipped due to missing Docker CLI. Now the gateway starts without
the socket, and a docker-compose.sandbox.yml overlay is generated
only when all prerequisites pass. The gateway restart at the end of
sandbox setup picks up both the socket mount and sandbox config.
Also moves group_add from write_extra_compose() into the sandbox
overlay, keeping all sandbox-specific compose configuration together.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(docker): fix sandbox docs URL in setup output
* Docker: harden sandbox setup fallback behavior
* Tests: cover docker-setup sandbox edge paths
* Docker: roll back sandbox mode on partial config failure
* Tests: assert sandbox mode rollback on partial setup
* Docs: document Docker sandbox bootstrap env controls
* Changelog: credit Docker sandbox bootstrap hardening
* Update CHANGELOG.md
* Docker: verify Docker apt signing key fingerprint
* Docker: avoid sandbox overlay deps during policy writes
* Tests: assert no-deps sandbox rollback gateway recreate
* Docs: mention OPENCLAW_INSTALL_DOCKER_CLI in Docker env vars
---------
Co-authored-by: Jakub Karwowski <jakubkarwowski@Mac.lan>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-02 08:06:10 +01:00
|
|
|
--build-arg "OPENCLAW_INSTALL_DOCKER_CLI=${OPENCLAW_INSTALL_DOCKER_CLI:-}" \
|
2026-02-03 23:16:56 +11:00
|
|
|
-t "$IMAGE_NAME" \
|
|
|
|
|
-f "$ROOT_DIR/Dockerfile" \
|
|
|
|
|
"$ROOT_DIR"
|
2026-02-04 10:43:30 +11:00
|
|
|
else
|
|
|
|
|
echo "==> Pulling Docker image: $IMAGE_NAME"
|
|
|
|
|
if ! docker pull "$IMAGE_NAME"; then
|
|
|
|
|
echo "ERROR: Failed to pull image $IMAGE_NAME. Please check the image name and your access permissions." >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
fi
|
2026-01-02 13:52:08 +02:00
|
|
|
|
2026-03-01 22:07:34 -04:00
|
|
|
# Ensure bind-mounted data directories are writable by the container's `node`
|
|
|
|
|
# user (uid 1000). Host-created dirs inherit the host user's uid which may
|
|
|
|
|
# differ, causing EACCES when the container tries to mkdir/write.
|
|
|
|
|
# Running a brief root container to chown is the portable Docker idiom --
|
|
|
|
|
# it works regardless of the host uid and doesn't require host-side root.
|
|
|
|
|
echo ""
|
|
|
|
|
echo "==> Fixing data-directory permissions"
|
|
|
|
|
# Use -xdev to restrict chown to the config-dir mount only — without it,
|
|
|
|
|
# the recursive chown would cross into the workspace bind mount and rewrite
|
|
|
|
|
# ownership of all user project files on Linux hosts.
|
|
|
|
|
# After fixing the config dir, only the OpenClaw metadata subdirectory
|
|
|
|
|
# (.openclaw/) inside the workspace gets chowned, not the user's project files.
|
|
|
|
|
docker compose "${COMPOSE_ARGS[@]}" run --rm --user root --entrypoint sh openclaw-cli -c \
|
|
|
|
|
'find /home/node/.openclaw -xdev -exec chown node:node {} +; \
|
|
|
|
|
[ -d /home/node/.openclaw/workspace/.openclaw ] && chown -R node:node /home/node/.openclaw/workspace/.openclaw || true'
|
|
|
|
|
|
2026-01-02 13:52:08 +02:00
|
|
|
echo ""
|
|
|
|
|
echo "==> Onboarding (interactive)"
|
2026-03-02 08:28:35 +07:00
|
|
|
echo "Docker setup pins Gateway mode to local."
|
|
|
|
|
echo "Gateway runtime bind comes from OPENCLAW_GATEWAY_BIND (default: lan)."
|
|
|
|
|
echo "Current runtime bind: $OPENCLAW_GATEWAY_BIND"
|
|
|
|
|
echo "Gateway token: $OPENCLAW_GATEWAY_TOKEN"
|
|
|
|
|
echo "Tailscale exposure: Off (use host-level tailnet/Tailscale setup separately)."
|
|
|
|
|
echo "Install Gateway daemon: No (managed by Docker Compose)"
|
|
|
|
|
echo ""
|
|
|
|
|
docker compose "${COMPOSE_ARGS[@]}" run --rm openclaw-cli onboard --mode local --no-install-daemon
|
|
|
|
|
|
2026-01-02 13:52:08 +02:00
|
|
|
echo ""
|
2026-03-02 08:28:35 +07:00
|
|
|
echo "==> Docker gateway defaults"
|
|
|
|
|
sync_gateway_mode_and_bind
|
2026-01-02 13:52:08 +02:00
|
|
|
|
2026-02-26 05:45:57 +01:00
|
|
|
echo ""
|
|
|
|
|
echo "==> Control UI origin allowlist"
|
|
|
|
|
ensure_control_ui_allowed_origins
|
|
|
|
|
|
2026-01-02 13:52:08 +02:00
|
|
|
echo ""
|
2026-01-08 07:48:23 +00:00
|
|
|
echo "==> Provider setup (optional)"
|
|
|
|
|
echo "WhatsApp (QR):"
|
2026-02-02 04:25:57 -08:00
|
|
|
echo " ${COMPOSE_HINT} run --rm openclaw-cli channels login"
|
2026-01-08 07:48:23 +00:00
|
|
|
echo "Telegram (bot token):"
|
2026-02-02 04:25:57 -08:00
|
|
|
echo " ${COMPOSE_HINT} run --rm openclaw-cli channels add --channel telegram --token <token>"
|
2026-01-08 07:48:23 +00:00
|
|
|
echo "Discord (bot token):"
|
2026-02-02 04:25:57 -08:00
|
|
|
echo " ${COMPOSE_HINT} run --rm openclaw-cli channels add --channel discord --token <token>"
|
|
|
|
|
echo "Docs: https://docs.openclaw.ai/channels"
|
2026-01-02 13:52:08 +02:00
|
|
|
|
|
|
|
|
echo ""
|
|
|
|
|
echo "==> Starting gateway"
|
2026-01-30 03:15:10 +01:00
|
|
|
docker compose "${COMPOSE_ARGS[@]}" up -d openclaw-gateway
|
2026-01-02 13:52:08 +02:00
|
|
|
|
feat(docker): add opt-in sandbox support for Docker deployments (#29974)
* feat(docker): add opt-in sandbox support for Docker deployments
Enable Docker-based sandbox isolation via OPENCLAW_SANDBOX=1 env var
in docker-setup.sh. This is a prerequisite for agents.defaults.sandbox
to function in any Docker deployment (self-hosted, Hostinger, DigitalOcean).
Changes:
- Dockerfile: add OPENCLAW_INSTALL_DOCKER_CLI build arg (~50MB, opt-in)
- docker-compose.yml: add commented-out docker.sock mount with docs
- docker-setup.sh: auto-detect Docker socket, inject mount, detect GID,
build sandbox image, configure sandbox defaults, add group_add
All changes are opt-in. Zero impact on existing deployments.
Usage: OPENCLAW_SANDBOX=1 ./docker-setup.sh
Closes #29933
Related: #7575, #7827, #28401, #10361, #12505, #28326
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address code review feedback on sandbox support
- Persist OPENCLAW_SANDBOX, DOCKER_GID, OPENCLAW_INSTALL_DOCKER_CLI
to .env via upsert_env so group_add survives re-runs
- Show config set errors instead of swallowing them silently;
report partial failure when sandbox config is incomplete
- Warn when Dockerfile.sandbox is missing but sandbox config
is still applied (sandbox image won't exist)
- Fix non-canonical whitespace in apt sources.list entry
by using printf instead of echo with line continuation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove `local` outside function and guard sandbox behind Docker CLI check
- Remove `local` keyword from top-level `sandbox_config_ok` assignment
which caused script exit under `set -euo pipefail` (bash `local`
outside a function is an error)
- Add Docker CLI prerequisite check for pre-built (non-local) images:
runs `docker --version` inside the container and skips sandbox setup
with a clear warning if the CLI is missing
- Split sandbox block so config is only applied after prerequisites pass
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: defer docker.sock mount until sandbox prerequisites pass
Move Docker socket mounting from the early setup phase (before image
build/pull) to a dedicated compose overlay created only after:
1. Docker CLI is verified inside the container image
2. /var/run/docker.sock exists on the host
Previously the socket was mounted optimistically at startup, leaving
the host Docker daemon exposed even when sandbox setup was later
skipped due to missing Docker CLI. Now the gateway starts without
the socket, and a docker-compose.sandbox.yml overlay is generated
only when all prerequisites pass. The gateway restart at the end of
sandbox setup picks up both the socket mount and sandbox config.
Also moves group_add from write_extra_compose() into the sandbox
overlay, keeping all sandbox-specific compose configuration together.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(docker): fix sandbox docs URL in setup output
* Docker: harden sandbox setup fallback behavior
* Tests: cover docker-setup sandbox edge paths
* Docker: roll back sandbox mode on partial config failure
* Tests: assert sandbox mode rollback on partial setup
* Docs: document Docker sandbox bootstrap env controls
* Changelog: credit Docker sandbox bootstrap hardening
* Update CHANGELOG.md
* Docker: verify Docker apt signing key fingerprint
* Docker: avoid sandbox overlay deps during policy writes
* Tests: assert no-deps sandbox rollback gateway recreate
* Docs: mention OPENCLAW_INSTALL_DOCKER_CLI in Docker env vars
---------
Co-authored-by: Jakub Karwowski <jakubkarwowski@Mac.lan>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-02 08:06:10 +01:00
|
|
|
# --- Sandbox setup (opt-in via OPENCLAW_SANDBOX=1) ---
|
|
|
|
|
if [[ -n "$SANDBOX_ENABLED" ]]; then
|
|
|
|
|
echo ""
|
|
|
|
|
echo "==> Sandbox setup"
|
|
|
|
|
|
|
|
|
|
# Build sandbox image if Dockerfile.sandbox exists.
|
|
|
|
|
if [[ -f "$ROOT_DIR/Dockerfile.sandbox" ]]; then
|
|
|
|
|
echo "Building sandbox image: openclaw-sandbox:bookworm-slim"
|
|
|
|
|
docker build \
|
|
|
|
|
-t "openclaw-sandbox:bookworm-slim" \
|
|
|
|
|
-f "$ROOT_DIR/Dockerfile.sandbox" \
|
|
|
|
|
"$ROOT_DIR"
|
|
|
|
|
else
|
|
|
|
|
echo "WARNING: Dockerfile.sandbox not found in $ROOT_DIR" >&2
|
|
|
|
|
echo " Sandbox config will be applied but no sandbox image will be built." >&2
|
|
|
|
|
echo " Agent exec may fail if the configured sandbox image does not exist." >&2
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Defense-in-depth: verify Docker CLI in the running image before enabling
|
|
|
|
|
# sandbox. This avoids claiming sandbox is enabled when the image cannot
|
|
|
|
|
# launch sandbox containers.
|
|
|
|
|
if ! docker compose "${COMPOSE_ARGS[@]}" run --rm --entrypoint docker openclaw-gateway --version >/dev/null 2>&1; then
|
|
|
|
|
echo "WARNING: Docker CLI not found inside the container image." >&2
|
|
|
|
|
echo " Sandbox requires Docker CLI. Rebuild with --build-arg OPENCLAW_INSTALL_DOCKER_CLI=1" >&2
|
|
|
|
|
echo " or use a local build (OPENCLAW_IMAGE=openclaw:local). Skipping sandbox setup." >&2
|
|
|
|
|
SANDBOX_ENABLED=""
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Apply sandbox config only if prerequisites are met.
|
|
|
|
|
if [[ -n "$SANDBOX_ENABLED" ]]; then
|
|
|
|
|
# Mount Docker socket via a dedicated compose overlay. This overlay is
|
|
|
|
|
# created only after sandbox prerequisites pass, so the socket is never
|
|
|
|
|
# exposed when sandbox cannot actually run.
|
|
|
|
|
if [[ -S "$DOCKER_SOCKET_PATH" ]]; then
|
|
|
|
|
SANDBOX_COMPOSE_FILE="$ROOT_DIR/docker-compose.sandbox.yml"
|
|
|
|
|
cat >"$SANDBOX_COMPOSE_FILE" <<YAML
|
|
|
|
|
services:
|
|
|
|
|
openclaw-gateway:
|
|
|
|
|
volumes:
|
|
|
|
|
- ${DOCKER_SOCKET_PATH}:/var/run/docker.sock
|
|
|
|
|
YAML
|
|
|
|
|
if [[ -n "${DOCKER_GID:-}" ]]; then
|
|
|
|
|
cat >>"$SANDBOX_COMPOSE_FILE" <<YAML
|
|
|
|
|
group_add:
|
|
|
|
|
- "${DOCKER_GID}"
|
|
|
|
|
YAML
|
|
|
|
|
fi
|
|
|
|
|
COMPOSE_ARGS+=("-f" "$SANDBOX_COMPOSE_FILE")
|
|
|
|
|
echo "==> Sandbox: added Docker socket mount"
|
|
|
|
|
else
|
|
|
|
|
echo "WARNING: OPENCLAW_SANDBOX enabled but Docker socket not found at $DOCKER_SOCKET_PATH." >&2
|
|
|
|
|
echo " Sandbox requires Docker socket access. Skipping sandbox setup." >&2
|
|
|
|
|
SANDBOX_ENABLED=""
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [[ -n "$SANDBOX_ENABLED" ]]; then
|
|
|
|
|
# Enable sandbox in OpenClaw config.
|
|
|
|
|
sandbox_config_ok=true
|
|
|
|
|
if ! docker compose "${COMPOSE_ARGS[@]}" run --rm --no-deps openclaw-cli \
|
|
|
|
|
config set agents.defaults.sandbox.mode "non-main" >/dev/null; then
|
|
|
|
|
echo "WARNING: Failed to set agents.defaults.sandbox.mode" >&2
|
|
|
|
|
sandbox_config_ok=false
|
|
|
|
|
fi
|
|
|
|
|
if ! docker compose "${COMPOSE_ARGS[@]}" run --rm --no-deps openclaw-cli \
|
|
|
|
|
config set agents.defaults.sandbox.scope "agent" >/dev/null; then
|
|
|
|
|
echo "WARNING: Failed to set agents.defaults.sandbox.scope" >&2
|
|
|
|
|
sandbox_config_ok=false
|
|
|
|
|
fi
|
|
|
|
|
if ! docker compose "${COMPOSE_ARGS[@]}" run --rm --no-deps openclaw-cli \
|
|
|
|
|
config set agents.defaults.sandbox.workspaceAccess "none" >/dev/null; then
|
|
|
|
|
echo "WARNING: Failed to set agents.defaults.sandbox.workspaceAccess" >&2
|
|
|
|
|
sandbox_config_ok=false
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [[ "$sandbox_config_ok" == true ]]; then
|
|
|
|
|
echo "Sandbox enabled: mode=non-main, scope=agent, workspaceAccess=none"
|
|
|
|
|
echo "Docs: https://docs.openclaw.ai/gateway/sandboxing"
|
|
|
|
|
# Restart gateway with sandbox compose overlay to pick up socket mount + config.
|
|
|
|
|
docker compose "${COMPOSE_ARGS[@]}" up -d openclaw-gateway
|
|
|
|
|
else
|
|
|
|
|
echo "WARNING: Sandbox config was partially applied. Check errors above." >&2
|
|
|
|
|
echo " Skipping gateway restart to avoid exposing Docker socket without a full sandbox policy." >&2
|
|
|
|
|
if ! docker compose "${BASE_COMPOSE_ARGS[@]}" run --rm --no-deps openclaw-cli \
|
|
|
|
|
config set agents.defaults.sandbox.mode "off" >/dev/null; then
|
|
|
|
|
echo "WARNING: Failed to roll back agents.defaults.sandbox.mode to off" >&2
|
|
|
|
|
else
|
|
|
|
|
echo "Sandbox mode rolled back to off due to partial sandbox config failure."
|
|
|
|
|
fi
|
|
|
|
|
if [[ -n "${SANDBOX_COMPOSE_FILE:-}" ]]; then
|
|
|
|
|
rm -f "$SANDBOX_COMPOSE_FILE"
|
|
|
|
|
fi
|
|
|
|
|
# Ensure gateway service definition is reset without sandbox overlay mount.
|
|
|
|
|
docker compose "${BASE_COMPOSE_ARGS[@]}" up -d --force-recreate openclaw-gateway
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
# Keep reruns deterministic: if sandbox is not active for this run, reset
|
|
|
|
|
# persisted sandbox mode so future execs do not require docker.sock by stale
|
|
|
|
|
# config alone.
|
|
|
|
|
if ! docker compose "${COMPOSE_ARGS[@]}" run --rm openclaw-cli \
|
|
|
|
|
config set agents.defaults.sandbox.mode "off" >/dev/null; then
|
|
|
|
|
echo "WARNING: Failed to reset agents.defaults.sandbox.mode to off" >&2
|
|
|
|
|
fi
|
|
|
|
|
if [[ -f "$ROOT_DIR/docker-compose.sandbox.yml" ]]; then
|
|
|
|
|
rm -f "$ROOT_DIR/docker-compose.sandbox.yml"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
2026-01-02 13:52:08 +02:00
|
|
|
echo ""
|
|
|
|
|
echo "Gateway running with host port mapping."
|
|
|
|
|
echo "Access from tailnet devices via the host's tailnet IP."
|
2026-01-30 03:15:10 +01:00
|
|
|
echo "Config: $OPENCLAW_CONFIG_DIR"
|
|
|
|
|
echo "Workspace: $OPENCLAW_WORKSPACE_DIR"
|
|
|
|
|
echo "Token: $OPENCLAW_GATEWAY_TOKEN"
|
2026-01-02 13:52:08 +02:00
|
|
|
echo ""
|
|
|
|
|
echo "Commands:"
|
2026-01-30 03:15:10 +01:00
|
|
|
echo " ${COMPOSE_HINT} logs -f openclaw-gateway"
|
2026-01-31 18:31:49 +09:00
|
|
|
echo " ${COMPOSE_HINT} exec openclaw-gateway node dist/index.js health --token \"$OPENCLAW_GATEWAY_TOKEN\""
|