Skip to main content

Running Claude Code in a Corporate Network Sandbox

Table of Contents
Most Claude Code setup guides assume a developer laptop with direct internet access. This is not that guide. I spent a session getting Claude Code to run inside a fully network-isolated corporate sandbox – no direct internet, all traffic through a controlled gateway, per-binary egress policies enforced at the proxy level. What started as “remove sudo from this installer” turned into a deep dive into every network call Claude Code makes and what it takes to route all of them through infrastructure you control.

Why Corporate-First Matters
#

The default Claude Code installer assumes it can reach storage.googleapis.com, api.anthropic.com, and a handful of other Anthropic endpoints freely. In a corporate environment, none of that is given:

  • Outbound traffic is proxied, TLS-terminated, and logged
  • Employees cannot install binaries with root access on shared machines
  • AI tools must route inference through an approved gateway (for cost control, data governance, and audit)
  • Self-updating software is a security problem

This article documents what it actually takes to deploy Claude Code in that environment, using OpenShell by NVIDIA as the sandbox to validate each constraint.

The Architecture
#

The target setup routes all Claude Code inference through an internal LLM gateway that proxies requests to AWS Bedrock. Claude Code talks to the gateway instead of Anthropic’s API, using Bedrock authentication.

graph LR
    subgraph Sandbox
        CC[Claude Code
~/.local/bin/claude] Node[node process] end subgraph Corporate Network Proxy[HTTPS Proxy
TLS termination] GW[LLM Gateway
:443] Mirror[Binary Mirror
:443] end subgraph Cloud Bedrock[AWS Bedrock
Claude models] end CC --> Node Node -->|CONNECT tunnel| Proxy Proxy --> GW GW --> Bedrock CC -.->|install only| Mirror

The sandbox enforces which binaries can reach which hosts. A process not listed in the policy simply cannot make outbound connections – the proxy returns 403 and logs the attempt.

Removing sudo
#

The installer originally used sudo to install the Claude Code binary to /usr/local/bin. In corporate environments, users typically do not have sudo access on shared machines. The fix: install everything under ~/.local/ and add that to PATH via the user’s shell config.

INSTALL_DIR="$HOME/.local/share/claude/versions"
mkdir -p "$INSTALL_DIR"
cp "$binary_path" "$INSTALL_DIR/$version"
chmod +x "$INSTALL_DIR/$version"
mkdir -p "$HOME/.local/bin"
ln -sf "$INSTALL_DIR/$version" "$HOME/.local/bin/claude"

The symlink layout mirrors what the official installer produces, so version tracking works correctly. PATH detection handles the right shell config file automatically (.bashrc, .zshrc, or .profile depending on what is present).

Problem 1: GCS Phone-Home
#

With sudo gone, I ran the installer in the sandbox. It failed:

Failed to fetch version from https://storage.googleapis.com/...

This came from the claude install subcommand, which contacts Anthropic’s upstream GCS bucket to resolve the version manifest – completely bypassing the internal mirror.

sequenceDiagram
    participant Script as install.sh
    participant Mirror as Internal Mirror
    participant GCS as storage.googleapis.com

    Script->>Mirror: download binary ✓
    Script->>Mirror: verify checksum ✓
    Script->>GCS: claude install (version resolve) ✗ BLOCKED
    Note over Script,GCS: Proxy denies connection.
Binary was already downloaded!

The fix: remove the claude install call entirely and place the binary manually. The installer already downloads and verifies the binary from the mirror. The claude install subcommand was redundant and had a hidden upstream dependency.

Problem 2: Linux Managed Settings
#

Corporate deployments use a managed settings file to push gateway configuration to all users. On macOS this is read from ~/Library/Application Support/ClaudeCode/managed-settings.json. On Linux, Claude Code only reads /etc/claude-code/managed-settings.json – a system path.

Without sudo, we cannot write there. The managed config was silently ignored.

flowchart TD
    A[Install completes] --> B{OS?}
    B -->|macOS| C[Read ~/Library/Application Support/ClaudeCode/managed-settings.json]
    B -->|Linux| D[Read /etc/claude-code/managed-settings.json]
    D --> E{sudo available?}
    E -->|yes| F[Write managed config]
    E -->|no - corporate install| G[File not written
Config silently ignored] G --> H[Claude Code starts
with no gateway config] H --> I[Tries api.anthropic.com
BLOCKED]

The fix: write all gateway configuration directly into ~/.claude/settings.json during install, bypassing the managed settings path entirely. The installer merges into existing settings (using jq) so it does not overwrite user preferences:

configure_settings() {
    local key="$1"
    local config="$HOME/.claude/settings.json"
    mkdir -p "$HOME/.claude"
    chmod 700 "$HOME/.claude"

    local base="{}"
    [ -f "$config" ] && base=$(cat "$config")

    local update
    update=$(echo "$base" | jq \
        --arg base_url "https://your-llm-gateway/aws" \
        --arg region "eu-central-1" \
        --arg model "your-model-id" \
        --arg helper "$HOME/.local/bin/api-key-helper" \
        '
        .env.CLAUDE_CODE_USE_BEDROCK = "1" |
        .env.AWS_REGION = $region |
        .env.ANTHROPIC_BEDROCK_BASE_URL = $base_url |
        .env.DISABLE_AUTOUPDATER = "1" |
        .model = $model |
        .apiKeyHelper = $helper
        ')

    if [ -n "$key" ]; then
        update=$(echo "$update" | jq --arg key "$key" '.env.AWS_BEARER_TOKEN_BEDROCK = $key')
    fi

    echo "$update" > "$config"
    chmod 600 "$config"
}

DISABLE_AUTOUPDATER=1 is critical: without it, Claude Code will attempt to self-update on every startup, contacting Anthropic’s CDN and failing in the sandbox.

Writing the Network Policy
#

OpenShell uses a YAML policy file that defines which binaries can reach which hosts. Policy is hot-reloadable for network rules without restarting the sandbox.

The critical insight from debugging: policy rules are per binary, not per process. If curl is allowed to reach github.com, that applies to /usr/bin/curl specifically – not to /usr/bin/git even though git uses HTTPS internally. git-remote-https is a separate binary that handles git’s HTTPS transport and needs its own entry.

graph TD
    subgraph llm_gateway
        N1["/usr/bin/node"] --> GW["llm-gateway.example.com:443"]
        CC2["~/.local/bin/claude"] --> GW
    end

    subgraph mirror_downloads
        CU["/usr/bin/curl"] --> MIR["mirror.example.com:443"]
        CU --> S3["s3.amazonaws.com:443"]
        WG["/usr/bin/wget"] --> MIR
        BA["/bin/bash"] --> MIR
    end

    subgraph github
        GI["/usr/bin/git"] --> GH["github.com:443"]
        GR["/usr/lib/git-core/git-remote-https"] --> GH
        CU2["/usr/bin/curl"] --> RAW["raw.githubusercontent.com:443"]
    end

    subgraph blocked
        XX["everything else"] --> DENY["403 Forbidden"]
    end

Each 403 from the proxy told us exactly which binary-endpoint pair was missing. The debugging loop took about a minute per iteration: run, read the error, add the pair to the policy YAML, reload, repeat.

The S3 Redirect Problem
#

One non-obvious failure: the mirror’s download URL redirected to a pre-signed S3 URL. The policy allowed the mirror host but not S3. The download returned 403 on the redirect, not on the initial request.

CONNECT s3.eu-central-1.amazonaws.com:443 blocked: policy not matched

Fix: add S3 to the mirror policy group. The lesson: redirect chains require every intermediate host in the policy, not just the originating host. Pre-signed S3 URLs are common in corporate binary distribution.

Oh My Zsh in the Sandbox
#

With Claude Code working, the question was: can we make the shell comfortable for developers? Installing zsh-bin (static zsh) and Oh My Zsh requires three non-obvious things in a sandboxed environment:

1. Explicit PATH – the static zsh is at /sandbox/bin/zsh, which is not in the default PATH:

env PATH=/sandbox/bin:/sandbox/.local/bin:/usr/local/bin:/usr/bin:/bin \
    ZSH=/sandbox/.oh-my-zsh \
    sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended

2. Corporate CA for git – the proxy terminates TLS and re-signs with its own CA. Git does not trust it by default:

GIT_SSL_CAINFO=/etc/openshell-tls/ca-bundle.pem git clone ...

3. curl in the github policy – the OMZ install script fetches from raw.githubusercontent.com before running git clone. That host needs curl in the policy, not just the git binaries.

What the Policy Enforces
#

The final sandbox gives Claude Code sessions the following properties:

flowchart LR
    subgraph Allowed
        A1[LLM inference via gateway]
        A2[Binary install from mirror]
        A3[git clone from GitHub]
    end
    subgraph Blocked
        B1[api.anthropic.com direct]
        B2[PyPI / npm / apt]
        B3[statsig telemetry]
        B4[Arbitrary HTTP egress]
    end
    CC2[Claude Code session] --> A1
    CC2 -.-> B1
    CC2 -.-> B4

Claude Code cannot phone home to Anthropic’s API, cannot pull packages, and cannot exfiltrate data via HTTP. All inference goes through the gateway, which handles authentication, rate limiting, and audit logging.

Key Takeaways
#

Installer changes needed for corporate:

  • Skip claude install – it contacts upstream GCS regardless of your mirror. Place the binary manually.
  • Write gateway config to ~/.claude/settings.json directly. Linux has no user-level managed settings path.
  • Set DISABLE_AUTOUPDATER=1 or the binary will try to self-update on every launch.
  • Install to ~/.local/bin, not /usr/local/bin. No sudo required.

Network policy lessons:

  • Per-binary egress policies are more precise than process-level firewalls. git-remote-https is a separate binary from git and needs its own entry.
  • TLS interception requires the proxy CA to be trusted by each tool independently. Git, curl, and Python each have their own trust store.
  • Pre-signed S3 redirects require the S3 host in the policy, not just the originating download host.
  • Proxy CONNECT error messages are exact: they tell you the binary, the host, and the port. Follow the errors.

If you are working on corporate AI deployment, platform engineering, or cloud security and want to go deeper, I offer 1:1 coaching sessions. Book a session (50 EUR / 60 min) or reach out at manuel.fedele+website@gmail.com.

Related