Files
OpenShell/deploy/docker/sandbox/Dockerfile.base

234 lines
12 KiB
Docker

# syntax=docker/dockerfile:1.4
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
# Supervisor container for OpenShell
# Multi-stage build with cross-compilation support for multi-arch
# Stage 1: Rust builder (runs on build platform, cross-compiles for target)
FROM --platform=$BUILDPLATFORM rust:1.88-slim AS rust-builder
ARG RUST_BUILD_PROFILE=debug
ARG TARGETARCH
ARG BUILDARCH
ARG CARGO_TARGET_CACHE_SCOPE=default
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
cmake g++ make protobuf-compiler curl && rm -rf /var/lib/apt/lists/*
# Install cross-compilation toolchain, sccache, + Rust target (no-ops for native builds)
COPY deploy/docker/cross-build.sh /usr/local/bin/
RUN . cross-build.sh && install_cross_toolchain && install_sccache && add_rust_target
ARG SCCACHE_MEMCACHED_ENDPOINT
WORKDIR /build
# Copy dependency manifests first for better caching
COPY Cargo.toml Cargo.lock ./
COPY crates/navigator-cli/Cargo.toml crates/navigator-cli/Cargo.toml
COPY crates/navigator-core/Cargo.toml crates/navigator-core/Cargo.toml
COPY crates/navigator-policy/Cargo.toml crates/navigator-policy/Cargo.toml
COPY crates/navigator-providers/Cargo.toml crates/navigator-providers/Cargo.toml
COPY crates/navigator-router/Cargo.toml crates/navigator-router/Cargo.toml
COPY crates/navigator-sandbox/Cargo.toml crates/navigator-sandbox/Cargo.toml
COPY crates/navigator-server/Cargo.toml crates/navigator-server/Cargo.toml
COPY crates/navigator-bootstrap/Cargo.toml crates/navigator-bootstrap/Cargo.toml
# Create dummy source files to build dependencies
RUN mkdir -p crates/navigator-cli/src crates/navigator-core/src crates/navigator-policy/src crates/navigator-providers/src crates/navigator-router/src crates/navigator-sandbox/src crates/navigator-server/src crates/navigator-bootstrap/src && \
echo "fn main() {}" > crates/navigator-cli/src/main.rs && \
echo "fn main() {}" > crates/navigator-sandbox/src/main.rs && \
echo "fn main() {}" > crates/navigator-server/src/main.rs && \
touch crates/navigator-core/src/lib.rs && \
touch crates/navigator-policy/src/lib.rs && \
touch crates/navigator-providers/src/lib.rs && \
touch crates/navigator-router/src/lib.rs && \
touch crates/navigator-bootstrap/src/lib.rs
# Copy proto files needed for build
COPY proto/ proto/
COPY crates/navigator-sandbox/data/sandbox-policy.rego ./crates/navigator-sandbox/data/
COPY deploy/docker/sandbox/dev-sandbox-policy.yaml ./deploy/docker/sandbox/
# Build dependencies only (cached unless Cargo.toml/lock changes).
# sccache uses memcached in CI or the local disk cache mount for local dev.
# The cargo-target mount gives cargo a persistent target/ dir for incremental rebuilds.
RUN --mount=type=cache,id=cargo-registry-sandbox-${TARGETARCH},sharing=locked,target=/usr/local/cargo/registry \
--mount=type=cache,id=cargo-target-sandbox-${TARGETARCH}-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \
--mount=type=cache,id=sccache-sandbox-${TARGETARCH},sharing=locked,target=/tmp/sccache \
. cross-build.sh && cargo_cross_build -p navigator-sandbox 2>/dev/null || true
# Copy actual source code
COPY crates/ crates/
# Touch source files to ensure they're rebuilt (not the cached dummy).
# Touch build.rs and proto files to force proto code regeneration when the
# cargo target cache mount retains stale OUT_DIR artifacts from prior builds.
RUN touch crates/navigator-sandbox/src/main.rs \
crates/navigator-core/build.rs \
proto/*.proto
# Build with cross-compilation support.
# Cache mounts are keyed by TARGETARCH so parallel multi-platform builds
# don't corrupt each other's cargo registry or build artifacts.
RUN --mount=type=cache,id=cargo-registry-sandbox-${TARGETARCH},sharing=locked,target=/usr/local/cargo/registry \
--mount=type=cache,id=cargo-target-sandbox-${TARGETARCH}-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \
--mount=type=cache,id=sccache-sandbox-${TARGETARCH},sharing=locked,target=/tmp/sccache \
. cross-build.sh && \
if [ "$RUST_BUILD_PROFILE" = "release" ]; then \
cargo_cross_build --release -p navigator-sandbox; \
else \
cargo_cross_build -p navigator-sandbox; \
fi && \
mkdir -p /build/out && \
cp "$(cross_output_dir "$RUST_BUILD_PROFILE")/navigator-sandbox" /build/out/
# Stage 2: Python base (uses target platform)
# Pin to specific Python patch + Debian release for reproducible builds.
# CVE-2025-13836 (stdlib urllib Content-Length OOM) affects Python <= 3.12.13;
# update to python:3.12.14-slim-bookworm (or later) when released.
FROM python:3.12.13-slim-bookworm AS base
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /sandbox
# Install system dependencies
# iproute2 is needed for network namespace management (ip netns, veth pairs)
RUN apt-get update && apt-get install -y --no-install-recommends \
curl dnsutils iproute2 iputils-ping \
net-tools netcat-openbsd traceroute \
&& rm -rf /var/lib/apt/lists/*
# Create supervisor and sandbox users/groups
RUN groupadd -r supervisor && useradd -r -g supervisor -s /usr/sbin/nologin supervisor && \
groupadd -r sandbox && useradd -r -g sandbox -d /sandbox -s /bin/bash sandbox
# Stage 3: Python dependencies builder
FROM base AS builder
COPY --from=ghcr.io/astral-sh/uv:0.10.8 /uv /usr/local/bin/uv
# Copy project files for dependency resolution
COPY pyproject.toml uv.lock ./
# Install dependencies into a virtual environment
RUN uv venv /app/.venv && \
uv sync --frozen --no-dev --no-install-project 2>/dev/null || \
uv sync --no-dev --no-install-project 2>/dev/null || true
# Install navigator SDK dependencies and pip (uv venvs don't include pip by
# default, and sandbox users need it for `pip install` inside the sandbox).
RUN uv pip install --python /app/.venv/bin/python --no-cache-dir cloudpickle grpcio protobuf openai pip
# Stage 4: Coding agents layer
FROM base AS coding-agents
# Install Node.js 22 via NodeSource (required by coding agents).
# build-essential & python3 are needed for npm packages with native addons (node-gyp).
# Include a minimal native toolchain so npm can compile optional native
# dependencies on platforms where prebuilt artifacts are unavailable.
# Pin to a specific patch version for reproducible builds.
# CVE-2026-21637, CVE-2025-59466, CVE-2025-59465, CVE-2025-55131 affect
# Node.js <= 22.22.1. Update to 22.23.0+ when a patched release ships.
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
apt-get install -y --no-install-recommends build-essential git nodejs=22.22.1-1nodesource1 python3 nano && \
rm -rf /var/lib/apt/lists/*
# Install GitHub CLI (gh) from the official apt repository
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
-o /usr/share/keyrings/githubcli-archive-keyring.gpg && \
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
> /etc/apt/sources.list.d/github-cli.list && \
apt-get update && apt-get install -y --no-install-recommends gh && \
rm -rf /var/lib/apt/lists/*
# Install Claude CLI via native installer (npm method is deprecated)
RUN curl -fsSL https://claude.ai/install.sh | bash \
&& cp /root/.local/bin/claude /usr/local/bin/claude \
&& chmod 755 /usr/local/bin/claude
# Upgrade npm to v11 which ships tar@^7.5.9 (fixes tar@6.2.1 vulns bundled in
# npm 10: GHSA-r6q2-hw4h-h46w, GHSA-8qq5-rm4j-mr97, GHSA-83g3-92jg-28cx,
# GHSA-34x7-hfp2-rc4v, GHSA-qffp-2rhf-9h96).
RUN npm install -g npm@11.11.0
# Install OpenCode CLI and Codex CLI (OpenAI) with pinned versions for
# reproducible builds. Force-upgrade tar and @hono/node-server afterward to
# resolve transitive dependency vulnerabilities (GHSA-wc8c-qw6v-h7f6,
# GHSA-r6q2-hw4h-h46w, GHSA-qffp-2rhf-9h96, GHSA-8qq5-rm4j-mr97,
# GHSA-83g3-92jg-28cx, GHSA-34x7-hfp2-rc4v).
RUN npm install -g opencode-ai@1.2.18 @openai/codex@0.111.0 && \
npm install -g tar@7.5.11 @hono/node-server@1.19.11
# Install ai-pim-utils (NVIDIA PIM CLI tools: outlook, calendar, transcripts, etc.)
# The install script auto-detects Debian and installs via .deb package to /usr/bin.
# Skills are installed to /usr/share/ai-pim-utils/skills and symlinked to ~/.claude/skills.
# RUN curl -fsSL https://outlook-cli-80d21a.gitlab-master-pages.nvidia.com/install.sh | bash
# Stage 5: Final image
FROM coding-agents AS final
ARG RUST_BUILD_PROFILE=debug
COPY --from=builder /app/.venv /app/.venv
COPY --from=builder /usr/local/bin/uv /usr/local/bin/uv
COPY --from=rust-builder /build/out/navigator-sandbox /usr/local/bin/
# Copy openshell Python SDK into the virtual environment
COPY python/openshell/ /app/.venv/lib/python3.12/site-packages/openshell/
# Add venvs to PATH -- /sandbox/.venv (writable, user-installed packages)
# takes priority over /app/.venv (read-only, build-time packages).
# ssh.rs inherits PATH at runtime so it stays in sync with this layout.
# VIRTUAL_ENV and UV_PYTHON_INSTALL_DIR are also exported in .bashrc
# so that login shell sessions (interactive and exec) see them.
ENV PATH="/sandbox/.venv/bin:/app/.venv/bin:/usr/local/bin:/usr/bin:/bin" \
VIRTUAL_ENV="/sandbox/.venv" \
UV_PYTHON_INSTALL_DIR="/sandbox/.uv/python"
# Copy custom navigator skills into the image
# To add a skill, create a subdirectory under deploy/docker/sandbox/skills/
# with a SKILL.md file (and any supporting files). It will be installed into
# both ~/.claude/skills/ and ~/.agents/skills/ for agent discovery.
COPY deploy/docker/sandbox/skills/ /sandbox/.agents/skills/
# Bake the default sandbox policy into the image so that sandboxes without an
# explicit policy from the gateway can discover it on disk.
COPY deploy/docker/sandbox/dev-sandbox-policy.yaml /etc/navigator/policy.yaml
RUN mkdir -p /var/navigator /sandbox /var/log && \
touch /var/log/navigator.log && \
chown supervisor:supervisor /var/log/navigator.log && \
chmod 0664 /var/log/navigator.log && \
chown sandbox:sandbox /sandbox && \
# Create a writable venv that inherits all packages from the read-only
# /app/.venv. Sandbox users can `pip install` or `uv pip install` into
# this venv without touching the base image layer.
uv venv --python /app/.venv/bin/python --seed --system-site-packages /sandbox/.venv && \
chown -R sandbox:sandbox /sandbox/.venv && \
# Minimal shell init files so interactive and non-interactive shells
# get a sane PATH and prompt. Without these, bash sources nothing
# under /sandbox and tools like VS Code Remote-SSH may mis-detect
# the platform.
printf 'export PATH="/sandbox/.venv/bin:/app/.venv/bin:/usr/local/bin:/usr/bin:/bin"\nexport VIRTUAL_ENV="/sandbox/.venv"\nexport UV_PYTHON_INSTALL_DIR="/sandbox/.uv/python"\nexport PS1="\\u@\\h:\\w\\$ "\n' \
> /sandbox/.bashrc && \
printf '[ -f ~/.bashrc ] && . ~/.bashrc\n' > /sandbox/.profile && \
chown sandbox:sandbox /sandbox/.bashrc /sandbox/.profile && \
# Install agent skills into the sandbox user's home directory
# so Claude and other agents can discover them at runtime.
# Sources: ai-pim-utils (.deb) and custom navigator skills.
mkdir -p /sandbox/.claude/skills /sandbox/.agents/skills && \
for src in /usr/share/ai-pim-utils/skills /usr/share/navigator/skills; do \
[ -d "$src" ] || continue; \
find "$src" -mindepth 1 -maxdepth 1 -type d | while read -r skill; do \
ln -sf "$skill" "/sandbox/.claude/skills/$(basename "$skill")"; \
ln -sf "$skill" "/sandbox/.agents/skills/$(basename "$skill")"; \
done; \
done && \
chown -R sandbox:sandbox /sandbox/.claude /sandbox/.agents
ENTRYPOINT ["navigator-sandbox"]