mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-18 05:39:50 +08:00
234 lines
12 KiB
Docker
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"]
|