From fad8095c9e6179c95bbec6f0ab5d715fb457c99d Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Mon, 2 Feb 2026 15:44:38 -0800 Subject: [PATCH 1/8] feat(devcontainer): add codex-focused secure devcontainer setup --- .devcontainer/Dockerfile | 81 +++++++++++++++----- .devcontainer/README.md | 69 ++++++++++++----- .devcontainer/devcontainer.json | 71 ++++++++++++++--- .devcontainer/init-firewall.sh | 130 ++++++++++++++++++++++++++++++++ .devcontainer/post-start.sh | 36 +++++++++ .devcontainer/post_install.py | 113 +++++++++++++++++++++++++++ 6 files changed, 451 insertions(+), 49 deletions(-) create mode 100644 .devcontainer/init-firewall.sh create mode 100644 .devcontainer/post-start.sh create mode 100644 .devcontainer/post_install.py diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index d020dfe73440..e5505d0687a8 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,27 +1,70 @@ -FROM ubuntu:24.04 +FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04 +ARG TZ ARG DEBIAN_FRONTEND=noninteractive -# enable 'universe' because musl-tools & clang live there -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - software-properties-common && \ - add-apt-repository --yes universe +ARG NODE_MAJOR=22 +ARG RUST_TOOLCHAIN=1.93.0 -# now install build deps -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - build-essential curl git ca-certificates \ - pkg-config clang musl-tools libssl-dev just && \ - rm -rf /var/lib/apt/lists/* +ENV TZ="$TZ" -# Ubuntu 24.04 ships with user 'ubuntu' already created with UID 1000. -USER ubuntu +SHELL ["/bin/bash", "-o", "pipefail", "-c"] -# install Rust + musl target as dev user -RUN curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal && \ - ~/.cargo/bin/rustup target add aarch64-unknown-linux-musl && \ - ~/.cargo/bin/rustup component add clippy rustfmt +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + curl \ + git \ + ca-certificates \ + pkg-config \ + clang \ + musl-tools \ + libssl-dev \ + libsqlite3-dev \ + just \ + python3 \ + python3-pip \ + jq \ + less \ + man-db \ + unzip \ + ripgrep \ + fzf \ + fd-find \ + zsh \ + dnsutils \ + iproute2 \ + ipset \ + iptables \ + aggregate \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* -ENV PATH="/home/ubuntu/.cargo/bin:${PATH}" +RUN curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - \ + && apt-get update \ + && apt-get install -y --no-install-recommends nodejs \ + && npm install -g corepack@latest \ + && corepack enable \ + && corepack prepare pnpm@10.28.2 --activate \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* +COPY .devcontainer/init-firewall.sh /usr/local/bin/init-firewall.sh +COPY .devcontainer/post_install.py /opt/post_install.py +COPY .devcontainer/post-start.sh /opt/post_start.sh + +RUN chmod 500 /usr/local/bin/init-firewall.sh \ + && chmod 755 /opt/post_start.sh \ + && chmod 644 /opt/post_install.py \ + && chown vscode:vscode /opt/post_install.py + +RUN install -d -m 0775 -o vscode -g vscode /commandhistory /workspace \ + && touch /commandhistory/.bash_history /commandhistory/.zsh_history \ + && chown vscode:vscode /commandhistory/.bash_history /commandhistory/.zsh_history + +USER vscode +ENV PATH="/home/vscode/.cargo/bin:${PATH}" WORKDIR /workspace + +RUN curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain "${RUST_TOOLCHAIN}" \ + && rustup component add clippy rustfmt rust-src \ + && rustup target add x86_64-unknown-linux-musl aarch64-unknown-linux-musl diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 58e4458a0660..0aea1733befc 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,30 +1,63 @@ -# Containerized Development +# Codex devcontainer -We provide the following options to facilitate Codex development in a container. This is particularly useful for verifying the Linux build when working on a macOS host. +This is a Codex-focused devcontainer setup adapted for this monorepo. -## Docker +## Core design choices -To build the Docker image locally for x64 and then run it with the repo mounted under `/workspace`: +- devcontainer schema + `init` + `updateRemoteUserUID` +- `${devcontainerId}`-scoped named volumes for per-container persistence +- read-only host `~/.gitconfig` mount with container-local `GIT_CONFIG_GLOBAL` +- explicit `workspaceMount`/`workspaceFolder` +- post-create bootstrap script (`post_install.py`) for idempotent setup -```shell -CODEX_DOCKER_IMAGE_NAME=codex-linux-dev -docker build --platform=linux/amd64 -t "$CODEX_DOCKER_IMAGE_NAME" ./.devcontainer -docker run --platform=linux/amd64 --rm -it -e CARGO_TARGET_DIR=/workspace/codex-rs/target-amd64 -v "$PWD":/workspace -w /workspace/codex-rs "$CODEX_DOCKER_IMAGE_NAME" -``` +## What is Codex-specific + +- Rust toolchain pinned to `1.93.0` with `clippy`, `rustfmt`, `rust-src` +- musl targets: `x86_64-unknown-linux-musl`, `aarch64-unknown-linux-musl` +- Node `22` + pnpm `10.28.2` +- firewall setup that allowlists domains from `OPENAI_ALLOWED_DOMAINS` +- persistent Cargo/Rustup volumes + +## Lifecycle hooks + +- `postCreateCommand`: `python3 /opt/post_install.py` + - configures history files + - fixes ownership on mounted dirs + - writes `/home/vscode/.gitconfig.local` +- `postStartCommand`: `bash /opt/post_start.sh` + - applies firewall rules through `init-firewall.sh` + - optionally adds GitHub CIDR ranges from `api.github.com/meta` + +## Firewall modes -Note that `/workspace/target` will contain the binaries built for your host platform, so we include `-e CARGO_TARGET_DIR=/workspace/codex-rs/target-amd64` in the `docker run` command so that the binaries built inside your container are written to a separate directory. +- **Strict (default)**: `CODEX_ENABLE_FIREWALL=1` (or unset) +- **Permissive**: `CODEX_ENABLE_FIREWALL=0` -For arm64, specify `--platform=linux/amd64` instead for both `docker build` and `docker run`. +Optional strict-mode enhancement: + +- `CODEX_INCLUDE_GITHUB_META_RANGES=1` (default) hydrates GitHub CIDRs into the allowlist. + +To run in permissive mode during a session: + +```bash +export CODEX_ENABLE_FIREWALL=0 +``` -Currently, the `Dockerfile` works for both x64 and arm64 Linux, though you need to run `rustup target add x86_64-unknown-linux-musl` yourself to install the musl toolchain for x64. +Then restart or rebuild the container. -## VS Code +## Persistent volumes -VS Code recognizes the `devcontainer.json` file and gives you the option to develop Codex in a container. Currently, `devcontainer.json` builds and runs the `arm64` flavor of the container. +- `/commandhistory` +- `/home/vscode/.codex` +- `/home/vscode/.config/gh` +- `/home/vscode/.cargo/registry` +- `/home/vscode/.cargo/git` +- `/home/vscode/.rustup` -From the integrated terminal in VS Code, you can build either flavor of the `arm64` build (GNU or musl): +## Local Docker smoke build -```shell -cargo build --target aarch64-unknown-linux-musl -cargo build --target aarch64-unknown-linux-gnu +```bash +docker build -f .devcontainer/Dockerfile -t codex-devcontainer-test . +docker run --rm -it --cap-add=NET_ADMIN --cap-add=NET_RAW \ + -v "$PWD":/workspace -w /workspace codex-devcontainer-test zsh ``` diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 1bed79c3ca3c..0da546ecb852 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,27 +1,74 @@ { - "name": "Codex", + "$schema": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainer.schema.json", + "name": "Codex Dev", "build": { "dockerfile": "Dockerfile", "context": "..", - "platform": "linux/arm64" + "args": { + "TZ": "${localEnv:TZ:UTC}", + "NODE_MAJOR": "22", + "RUST_TOOLCHAIN": "1.93.0" + } }, - - /* Force VS Code to run the container as arm64 in - case your host is x86 (or vice-versa). */ - "runArgs": ["--platform=linux/arm64"], - + "runArgs": [ + "--cap-add=NET_ADMIN", + "--cap-add=NET_RAW" + ], + "init": true, + "updateRemoteUserUID": true, + "remoteUser": "vscode", + "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated", + "workspaceFolder": "/workspace", + "mounts": [ + "source=codex-commandhistory-${devcontainerId},target=/commandhistory,type=volume", + "source=codex-home-${devcontainerId},target=/home/vscode/.codex,type=volume", + "source=codex-gh-${devcontainerId},target=/home/vscode/.config/gh,type=volume", + "source=codex-cargo-registry-${devcontainerId},target=/home/vscode/.cargo/registry,type=volume", + "source=codex-cargo-git-${devcontainerId},target=/home/vscode/.cargo/git,type=volume", + "source=codex-rustup-${devcontainerId},target=/home/vscode/.rustup,type=volume", + "source=${localEnv:HOME}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,readonly" + ], "containerEnv": { "RUST_BACKTRACE": "1", - "CARGO_TARGET_DIR": "${containerWorkspaceFolder}/codex-rs/target-arm64" + "CODEX_UNSAFE_ALLOW_NO_SANDBOX": "1", + "CODEX_ENABLE_FIREWALL": "1", + "CODEX_INCLUDE_GITHUB_META_RANGES": "1", + "OPENAI_ALLOWED_DOMAINS": "api.openai.com auth.openai.com github.com api.github.com codeload.github.com raw.githubusercontent.com objects.githubusercontent.com crates.io index.crates.io static.crates.io static.rust-lang.org registry.npmjs.org", + "CARGO_TARGET_DIR": "/workspace/codex-rs/target/devcontainer", + "GIT_CONFIG_GLOBAL": "/home/vscode/.gitconfig.local", + "PYTHONDONTWRITEBYTECODE": "1", + "PIP_DISABLE_PIP_VERSION_CHECK": "1" + }, + "remoteEnv": { + "OPENAI_API_KEY": "${localEnv:OPENAI_API_KEY}" }, - - "remoteUser": "ubuntu", + "postCreateCommand": "python3 /opt/post_install.py", + "postStartCommand": "bash /opt/post_start.sh", + "waitFor": "postStartCommand", "customizations": { "vscode": { "settings": { - "terminal.integrated.defaultProfile.linux": "bash" + "terminal.integrated.defaultProfile.linux": "zsh", + "terminal.integrated.profiles.linux": { + "bash": { + "path": "bash", + "icon": "terminal-bash" + }, + "zsh": { + "path": "zsh" + } + }, + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true }, - "extensions": ["rust-lang.rust-analyzer", "tamasfe.even-better-toml"] + "extensions": [ + "openai.chatgpt", + "rust-lang.rust-analyzer", + "tamasfe.even-better-toml", + "vadimcn.vscode-lldb", + "ms-azuretools.vscode-docker" + ] } } } diff --git a/.devcontainer/init-firewall.sh b/.devcontainer/init-firewall.sh new file mode 100644 index 000000000000..377fedd02846 --- /dev/null +++ b/.devcontainer/init-firewall.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' + +allowed_domains_file="/etc/codex/allowed_domains.txt" +include_github_meta_ranges="${CODEX_INCLUDE_GITHUB_META_RANGES:-1}" + +if [ -f "$allowed_domains_file" ]; then + mapfile -t allowed_domains < <(sed '/^\s*#/d;/^\s*$/d' "$allowed_domains_file") +else + allowed_domains=("api.openai.com") +fi + +if [ "${#allowed_domains[@]}" -eq 0 ]; then + echo "ERROR: No allowed domains configured" + exit 1 +fi + +# Preserve docker-managed DNS NAT rules before clearing tables. +docker_dns_rules="$(iptables-save -t nat | grep "127\\.0\\.0\\.11" || true)" + +iptables -F +iptables -X +iptables -t nat -F +iptables -t nat -X +iptables -t mangle -F +iptables -t mangle -X +ipset destroy allowed-domains 2>/dev/null || true + +if [ -n "$docker_dns_rules" ]; then + echo "Restoring Docker DNS NAT rules" + iptables -t nat -N DOCKER_OUTPUT 2>/dev/null || true + iptables -t nat -N DOCKER_POSTROUTING 2>/dev/null || true + while IFS= read -r rule; do + [ -z "$rule" ] && continue + iptables -t nat $rule + done <<< "$docker_dns_rules" +fi + +# Allow DNS resolution and localhost communication. +iptables -A OUTPUT -p udp --dport 53 -j ACCEPT +iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT +iptables -A INPUT -p udp --sport 53 -j ACCEPT +iptables -A INPUT -p tcp --sport 53 -j ACCEPT +iptables -A INPUT -i lo -j ACCEPT +iptables -A OUTPUT -o lo -j ACCEPT + +ipset create allowed-domains hash:net + +for domain in "${allowed_domains[@]}"; do + echo "Resolving $domain" + ips="$(dig +short A "$domain" | sed '/^\s*$/d')" + if [ -z "$ips" ]; then + echo "ERROR: Failed to resolve $domain" + exit 1 + fi + + while IFS= read -r ip; do + if [[ ! "$ip" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then + echo "ERROR: Invalid IPv4 address from DNS for $domain: $ip" + exit 1 + fi + ipset add allowed-domains "$ip" -exist + done <<< "$ips" +done + +if [ "$include_github_meta_ranges" = "1" ]; then + echo "Fetching GitHub meta ranges" + github_meta="$(curl -fsSL --connect-timeout 10 https://api.github.com/meta)" + + if ! echo "$github_meta" | jq -e '.web and .api and .git' >/dev/null; then + echo "ERROR: GitHub meta response missing expected fields" + exit 1 + fi + + while IFS= read -r cidr; do + [ -z "$cidr" ] && continue + if [[ "$cidr" == *:* ]]; then + # Current policy enforces IPv4-only ipset entries. + continue + fi + if [[ ! "$cidr" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}/[0-9]{1,2}$ ]]; then + echo "ERROR: Invalid GitHub CIDR range: $cidr" + exit 1 + fi + ipset add allowed-domains "$cidr" -exist + done < <(echo "$github_meta" | jq -r '((.web // []) + (.api // []) + (.git // []))[]' | sort -u) +fi + +host_ip="$(ip route | awk '/default/ {print $3; exit}')" +if [ -z "$host_ip" ]; then + echo "ERROR: Failed to detect host IP" + exit 1 +fi + +host_network="$(echo "$host_ip" | sed 's/\.[0-9]*$/.0\/24/')" +iptables -A INPUT -s "$host_network" -j ACCEPT +iptables -A OUTPUT -d "$host_network" -j ACCEPT + +iptables -P INPUT DROP +iptables -P FORWARD DROP +iptables -P OUTPUT DROP + +iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT +iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT +iptables -A OUTPUT -m set --match-set allowed-domains dst -j ACCEPT + +# Reject rather than silently drop to make policy failures obvious. +iptables -A INPUT -j REJECT --reject-with icmp-admin-prohibited +iptables -A OUTPUT -j REJECT --reject-with icmp-admin-prohibited +iptables -A FORWARD -j REJECT --reject-with icmp-admin-prohibited + +echo "Firewall configuration complete" + +if curl --connect-timeout 5 https://example.com >/dev/null 2>&1; then + echo "ERROR: Firewall verification failed - was able to reach https://example.com" + exit 1 +fi + +if ! curl --connect-timeout 5 https://api.openai.com >/dev/null 2>&1; then + echo "ERROR: Firewall verification failed - unable to reach https://api.openai.com" + exit 1 +fi + +if [ "$include_github_meta_ranges" = "1" ] && ! curl --connect-timeout 5 https://api.github.com/zen >/dev/null 2>&1; then + echo "ERROR: Firewall verification failed - unable to reach https://api.github.com" + exit 1 +fi + +echo "Firewall verification passed" diff --git a/.devcontainer/post-start.sh b/.devcontainer/post-start.sh new file mode 100644 index 000000000000..fcc42ad14543 --- /dev/null +++ b/.devcontainer/post-start.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ "${CODEX_ENABLE_FIREWALL:-1}" != "1" ]; then + echo "[devcontainer] Firewall mode: permissive (CODEX_ENABLE_FIREWALL=${CODEX_ENABLE_FIREWALL:-unset})." + exit 0 +fi + +echo "[devcontainer] Firewall mode: strict" + +domains_raw="${OPENAI_ALLOWED_DOMAINS:-api.openai.com}" +mapfile -t domains < <(printf '%s\n' "$domains_raw" | tr ', ' '\n\n' | sed '/^$/d' | sort -u) + +if [ "${#domains[@]}" -eq 0 ]; then + echo "[devcontainer] No allowed domains configured." + exit 1 +fi + +tmp_file="$(mktemp)" +for domain in "${domains[@]}"; do + if [[ ! "$domain" =~ ^[a-zA-Z0-9][a-zA-Z0-9.-]*\.[a-zA-Z]{2,}$ ]]; then + echo "[devcontainer] Invalid domain in OPENAI_ALLOWED_DOMAINS: $domain" + rm -f "$tmp_file" + exit 1 + fi + printf '%s\n' "$domain" >> "$tmp_file" +done + +sudo install -d -m 0755 /etc/codex +sudo cp "$tmp_file" /etc/codex/allowed_domains.txt +sudo chown root:root /etc/codex/allowed_domains.txt +sudo chmod 0444 /etc/codex/allowed_domains.txt +rm -f "$tmp_file" + +echo "[devcontainer] Applying firewall policy for domains: ${domains[*]}" +sudo --preserve-env=CODEX_INCLUDE_GITHUB_META_RANGES /usr/local/bin/init-firewall.sh diff --git a/.devcontainer/post_install.py b/.devcontainer/post_install.py new file mode 100644 index 000000000000..205e57ce4c9a --- /dev/null +++ b/.devcontainer/post_install.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +"""Post-install configuration for the Codex devcontainer.""" + +from __future__ import annotations + +import os +import subprocess +import sys +from pathlib import Path + + +def ensure_history_files() -> None: + command_history_dir = Path("/commandhistory") + command_history_dir.mkdir(parents=True, exist_ok=True) + + for filename in (".bash_history", ".zsh_history"): + (command_history_dir / filename).touch(exist_ok=True) + + +def fix_directory_ownership() -> None: + uid = os.getuid() + gid = os.getgid() + + paths = [ + Path.home() / ".codex", + Path.home() / ".config" / "gh", + Path.home() / ".cargo", + Path.home() / ".rustup", + Path("/commandhistory"), + ] + + for path in paths: + if not path.exists(): + continue + + stat_info = path.stat() + if stat_info.st_uid == uid and stat_info.st_gid == gid: + continue + + try: + subprocess.run( + ["sudo", "chown", "-R", f"{uid}:{gid}", str(path)], + check=True, + capture_output=True, + text=True, + ) + print(f"[post_install] fixed ownership: {path}", file=sys.stderr) + except subprocess.CalledProcessError as err: + print( + f"[post_install] warning: could not fix ownership of {path}: {err.stderr.strip()}", + file=sys.stderr, + ) + + +def setup_git_config() -> None: + home = Path.home() + host_gitconfig = home / ".gitconfig" + local_gitconfig = home / ".gitconfig.local" + gitignore_global = home / ".gitignore_global" + + gitignore_global.write_text( + """# Codex +.codex/ + +# Rust +/target/ + +# Node +node_modules/ + +# Python +__pycache__/ +*.pyc + +# Editors +.vscode/ +.idea/ + +# macOS +.DS_Store +""", + encoding="utf-8", + ) + + include_line = ( + f"[include]\n path = {host_gitconfig}\n\n" if host_gitconfig.exists() else "" + ) + + local_gitconfig.write_text( + f"""# Container-local git configuration +{include_line}[core] + excludesfile = {gitignore_global} + +[merge] + conflictstyle = diff3 + +[diff] + colorMoved = default +""", + encoding="utf-8", + ) + + +def main() -> None: + print("[post_install] configuring devcontainer...", file=sys.stderr) + ensure_history_files() + fix_directory_ownership() + setup_git_config() + print("[post_install] complete", file=sys.stderr) + + +if __name__ == "__main__": + main() From 2f8c6fcb3cd270904b77815907a6a9ee55d38b5d Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Mon, 2 Feb 2026 15:53:50 -0800 Subject: [PATCH 2/8] use rust 1.92.0 --- .devcontainer/Dockerfile | 2 +- .devcontainer/README.md | 2 +- .devcontainer/devcontainer.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index e5505d0687a8..2bfd00ce79db 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,7 +3,7 @@ FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04 ARG TZ ARG DEBIAN_FRONTEND=noninteractive ARG NODE_MAJOR=22 -ARG RUST_TOOLCHAIN=1.93.0 +ARG RUST_TOOLCHAIN=1.92.0 ENV TZ="$TZ" diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 0aea1733befc..20ed6d161308 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -12,7 +12,7 @@ This is a Codex-focused devcontainer setup adapted for this monorepo. ## What is Codex-specific -- Rust toolchain pinned to `1.93.0` with `clippy`, `rustfmt`, `rust-src` +- Rust toolchain pinned to `1.92.0` with `clippy`, `rustfmt`, `rust-src` - musl targets: `x86_64-unknown-linux-musl`, `aarch64-unknown-linux-musl` - Node `22` + pnpm `10.28.2` - firewall setup that allowlists domains from `OPENAI_ALLOWED_DOMAINS` diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 0da546ecb852..558c11439f6a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -7,7 +7,7 @@ "args": { "TZ": "${localEnv:TZ:UTC}", "NODE_MAJOR": "22", - "RUST_TOOLCHAIN": "1.93.0" + "RUST_TOOLCHAIN": "1.92.0" } }, "runArgs": [ From c0b6e2278f3e38da4fb70ad5e4d690fb07897af8 Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Mon, 2 Feb 2026 16:08:29 -0800 Subject: [PATCH 3/8] disable COREPACK_ENABLE_DOWNLOAD_PROMPT --- .devcontainer/devcontainer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 558c11439f6a..a6e5bd2b4125 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -36,6 +36,7 @@ "OPENAI_ALLOWED_DOMAINS": "api.openai.com auth.openai.com github.com api.github.com codeload.github.com raw.githubusercontent.com objects.githubusercontent.com crates.io index.crates.io static.crates.io static.rust-lang.org registry.npmjs.org", "CARGO_TARGET_DIR": "/workspace/codex-rs/target/devcontainer", "GIT_CONFIG_GLOBAL": "/home/vscode/.gitconfig.local", + "COREPACK_ENABLE_DOWNLOAD_PROMPT": "0", "PYTHONDONTWRITEBYTECODE": "1", "PIP_DISABLE_PIP_VERSION_CHECK": "1" }, From 28575cda7330f58c71a05aa5a6f4640c68d3a000 Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Mon, 2 Feb 2026 16:28:26 -0800 Subject: [PATCH 4/8] fix ipv6 bypass --- .devcontainer/README.md | 3 ++ .devcontainer/devcontainer.json | 1 + .devcontainer/init-firewall.sh | 66 ++++++++++++++++++++++++++++++--- .devcontainer/post-start.sh | 2 +- 4 files changed, 66 insertions(+), 6 deletions(-) diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 20ed6d161308..a6bfb3ff26f5 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -26,7 +26,9 @@ This is a Codex-focused devcontainer setup adapted for this monorepo. - writes `/home/vscode/.gitconfig.local` - `postStartCommand`: `bash /opt/post_start.sh` - applies firewall rules through `init-firewall.sh` + - enforces IPv6 default-deny so strict mode cannot be bypassed over IPv6 - optionally adds GitHub CIDR ranges from `api.github.com/meta` + - optionally adds Cloudflare IPv4 CIDR ranges for CDN-backed endpoints ## Firewall modes @@ -36,6 +38,7 @@ This is a Codex-focused devcontainer setup adapted for this monorepo. Optional strict-mode enhancement: - `CODEX_INCLUDE_GITHUB_META_RANGES=1` (default) hydrates GitHub CIDRs into the allowlist. +- `CODEX_INCLUDE_CLOUDFLARE_RANGES=1` (default) hydrates Cloudflare IPv4 CIDRs into the allowlist. To run in permissive mode during a session: diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a6e5bd2b4125..a3b9c2132d72 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -33,6 +33,7 @@ "CODEX_UNSAFE_ALLOW_NO_SANDBOX": "1", "CODEX_ENABLE_FIREWALL": "1", "CODEX_INCLUDE_GITHUB_META_RANGES": "1", + "CODEX_INCLUDE_CLOUDFLARE_RANGES": "1", "OPENAI_ALLOWED_DOMAINS": "api.openai.com auth.openai.com github.com api.github.com codeload.github.com raw.githubusercontent.com objects.githubusercontent.com crates.io index.crates.io static.crates.io static.rust-lang.org registry.npmjs.org", "CARGO_TARGET_DIR": "/workspace/codex-rs/target/devcontainer", "GIT_CONFIG_GLOBAL": "/home/vscode/.gitconfig.local", diff --git a/.devcontainer/init-firewall.sh b/.devcontainer/init-firewall.sh index 377fedd02846..e637e19a2e2e 100644 --- a/.devcontainer/init-firewall.sh +++ b/.devcontainer/init-firewall.sh @@ -4,6 +4,7 @@ IFS=$'\n\t' allowed_domains_file="/etc/codex/allowed_domains.txt" include_github_meta_ranges="${CODEX_INCLUDE_GITHUB_META_RANGES:-1}" +include_cloudflare_ranges="${CODEX_INCLUDE_CLOUDFLARE_RANGES:-1}" if [ -f "$allowed_domains_file" ]; then mapfile -t allowed_domains < <(sed '/^\s*#/d;/^\s*$/d' "$allowed_domains_file") @@ -16,6 +17,43 @@ if [ "${#allowed_domains[@]}" -eq 0 ]; then exit 1 fi +add_ipv4_cidr_to_allowlist() { + local source="$1" + local cidr="$2" + + if [[ ! "$cidr" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}/[0-9]{1,2}$ ]]; then + echo "ERROR: Invalid ${source} CIDR range: $cidr" + exit 1 + fi + + ipset add allowed-domains "$cidr" -exist +} + +configure_ipv6_default_deny() { + if ! command -v ip6tables >/dev/null 2>&1; then + echo "ERROR: ip6tables is required to enforce IPv6 default-deny policy" + exit 1 + fi + + ip6tables -F + ip6tables -X + ip6tables -t mangle -F + ip6tables -t mangle -X + ip6tables -t nat -F 2>/dev/null || true + ip6tables -t nat -X 2>/dev/null || true + + ip6tables -A INPUT -i lo -j ACCEPT + ip6tables -A OUTPUT -o lo -j ACCEPT + ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT + ip6tables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT + + ip6tables -P INPUT DROP + ip6tables -P FORWARD DROP + ip6tables -P OUTPUT DROP + + echo "IPv6 firewall policy configured (default-deny)" +} + # Preserve docker-managed DNS NAT rules before clearing tables. docker_dns_rules="$(iptables-save -t nat | grep "127\\.0\\.0\\.11" || true)" @@ -79,14 +117,25 @@ if [ "$include_github_meta_ranges" = "1" ]; then # Current policy enforces IPv4-only ipset entries. continue fi - if [[ ! "$cidr" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}/[0-9]{1,2}$ ]]; then - echo "ERROR: Invalid GitHub CIDR range: $cidr" - exit 1 - fi - ipset add allowed-domains "$cidr" -exist + add_ipv4_cidr_to_allowlist "GitHub" "$cidr" done < <(echo "$github_meta" | jq -r '((.web // []) + (.api // []) + (.git // []))[]' | sort -u) fi +if [ "$include_cloudflare_ranges" = "1" ]; then + echo "Fetching Cloudflare IPv4 ranges" + cloudflare_ranges="$(curl -fsSL --connect-timeout 10 https://www.cloudflare.com/ips-v4)" + + if [ -z "$(echo "$cloudflare_ranges" | sed '/^\s*#/d;/^\s*$/d')" ]; then + echo "ERROR: Cloudflare range response was empty" + exit 1 + fi + + while IFS= read -r cidr; do + [ -z "$cidr" ] && continue + add_ipv4_cidr_to_allowlist "Cloudflare" "$cidr" + done < <(echo "$cloudflare_ranges" | sed '/^\s*#/d;/^\s*$/d' | sort -u) +fi + host_ip="$(ip route | awk '/default/ {print $3; exit}')" if [ -z "$host_ip" ]; then echo "ERROR: Failed to detect host IP" @@ -110,6 +159,8 @@ iptables -A INPUT -j REJECT --reject-with icmp-admin-prohibited iptables -A OUTPUT -j REJECT --reject-with icmp-admin-prohibited iptables -A FORWARD -j REJECT --reject-with icmp-admin-prohibited +configure_ipv6_default_deny + echo "Firewall configuration complete" if curl --connect-timeout 5 https://example.com >/dev/null 2>&1; then @@ -127,4 +178,9 @@ if [ "$include_github_meta_ranges" = "1" ] && ! curl --connect-timeout 5 https:/ exit 1 fi +if curl --connect-timeout 5 -6 https://example.com >/dev/null 2>&1; then + echo "ERROR: Firewall verification failed - was able to reach https://example.com over IPv6" + exit 1 +fi + echo "Firewall verification passed" diff --git a/.devcontainer/post-start.sh b/.devcontainer/post-start.sh index fcc42ad14543..a86957a11ce8 100644 --- a/.devcontainer/post-start.sh +++ b/.devcontainer/post-start.sh @@ -33,4 +33,4 @@ sudo chmod 0444 /etc/codex/allowed_domains.txt rm -f "$tmp_file" echo "[devcontainer] Applying firewall policy for domains: ${domains[*]}" -sudo --preserve-env=CODEX_INCLUDE_GITHUB_META_RANGES /usr/local/bin/init-firewall.sh +sudo --preserve-env=CODEX_INCLUDE_GITHUB_META_RANGES,CODEX_INCLUDE_CLOUDFLARE_RANGES /usr/local/bin/init-firewall.sh From 46342774854157b149c731f87f213bfdcdb010e8 Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Mon, 2 Feb 2026 16:40:47 -0800 Subject: [PATCH 5/8] remove cloudflare ranges --- .devcontainer/README.md | 2 -- .devcontainer/devcontainer.json | 1 - .devcontainer/init-firewall.sh | 16 ---------------- .devcontainer/post-start.sh | 2 +- 4 files changed, 1 insertion(+), 20 deletions(-) diff --git a/.devcontainer/README.md b/.devcontainer/README.md index a6bfb3ff26f5..4c4a2f231666 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -28,7 +28,6 @@ This is a Codex-focused devcontainer setup adapted for this monorepo. - applies firewall rules through `init-firewall.sh` - enforces IPv6 default-deny so strict mode cannot be bypassed over IPv6 - optionally adds GitHub CIDR ranges from `api.github.com/meta` - - optionally adds Cloudflare IPv4 CIDR ranges for CDN-backed endpoints ## Firewall modes @@ -38,7 +37,6 @@ This is a Codex-focused devcontainer setup adapted for this monorepo. Optional strict-mode enhancement: - `CODEX_INCLUDE_GITHUB_META_RANGES=1` (default) hydrates GitHub CIDRs into the allowlist. -- `CODEX_INCLUDE_CLOUDFLARE_RANGES=1` (default) hydrates Cloudflare IPv4 CIDRs into the allowlist. To run in permissive mode during a session: diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a3b9c2132d72..a6e5bd2b4125 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -33,7 +33,6 @@ "CODEX_UNSAFE_ALLOW_NO_SANDBOX": "1", "CODEX_ENABLE_FIREWALL": "1", "CODEX_INCLUDE_GITHUB_META_RANGES": "1", - "CODEX_INCLUDE_CLOUDFLARE_RANGES": "1", "OPENAI_ALLOWED_DOMAINS": "api.openai.com auth.openai.com github.com api.github.com codeload.github.com raw.githubusercontent.com objects.githubusercontent.com crates.io index.crates.io static.crates.io static.rust-lang.org registry.npmjs.org", "CARGO_TARGET_DIR": "/workspace/codex-rs/target/devcontainer", "GIT_CONFIG_GLOBAL": "/home/vscode/.gitconfig.local", diff --git a/.devcontainer/init-firewall.sh b/.devcontainer/init-firewall.sh index e637e19a2e2e..92757244738d 100644 --- a/.devcontainer/init-firewall.sh +++ b/.devcontainer/init-firewall.sh @@ -4,7 +4,6 @@ IFS=$'\n\t' allowed_domains_file="/etc/codex/allowed_domains.txt" include_github_meta_ranges="${CODEX_INCLUDE_GITHUB_META_RANGES:-1}" -include_cloudflare_ranges="${CODEX_INCLUDE_CLOUDFLARE_RANGES:-1}" if [ -f "$allowed_domains_file" ]; then mapfile -t allowed_domains < <(sed '/^\s*#/d;/^\s*$/d' "$allowed_domains_file") @@ -121,21 +120,6 @@ if [ "$include_github_meta_ranges" = "1" ]; then done < <(echo "$github_meta" | jq -r '((.web // []) + (.api // []) + (.git // []))[]' | sort -u) fi -if [ "$include_cloudflare_ranges" = "1" ]; then - echo "Fetching Cloudflare IPv4 ranges" - cloudflare_ranges="$(curl -fsSL --connect-timeout 10 https://www.cloudflare.com/ips-v4)" - - if [ -z "$(echo "$cloudflare_ranges" | sed '/^\s*#/d;/^\s*$/d')" ]; then - echo "ERROR: Cloudflare range response was empty" - exit 1 - fi - - while IFS= read -r cidr; do - [ -z "$cidr" ] && continue - add_ipv4_cidr_to_allowlist "Cloudflare" "$cidr" - done < <(echo "$cloudflare_ranges" | sed '/^\s*#/d;/^\s*$/d' | sort -u) -fi - host_ip="$(ip route | awk '/default/ {print $3; exit}')" if [ -z "$host_ip" ]; then echo "ERROR: Failed to detect host IP" diff --git a/.devcontainer/post-start.sh b/.devcontainer/post-start.sh index a86957a11ce8..fcc42ad14543 100644 --- a/.devcontainer/post-start.sh +++ b/.devcontainer/post-start.sh @@ -33,4 +33,4 @@ sudo chmod 0444 /etc/codex/allowed_domains.txt rm -f "$tmp_file" echo "[devcontainer] Applying firewall policy for domains: ${domains[*]}" -sudo --preserve-env=CODEX_INCLUDE_GITHUB_META_RANGES,CODEX_INCLUDE_CLOUDFLARE_RANGES /usr/local/bin/init-firewall.sh +sudo --preserve-env=CODEX_INCLUDE_GITHUB_META_RANGES /usr/local/bin/init-firewall.sh From 58ab6db2118b4e0a5894f3148780efb019dff44a Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Mon, 2 Feb 2026 17:13:54 -0800 Subject: [PATCH 6/8] add dev first workflow --- .devcontainer/Dockerfile | 3 +- .devcontainer/README.md | 96 +++++++++++++++++++++------------ .devcontainer/devcontainer.json | 9 ++-- 3 files changed, 68 insertions(+), 40 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 2bfd00ce79db..aaff9eb2d392 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -4,6 +4,7 @@ ARG TZ ARG DEBIAN_FRONTEND=noninteractive ARG NODE_MAJOR=22 ARG RUST_TOOLCHAIN=1.92.0 +ARG CODEX_NPM_VERSION=latest ENV TZ="$TZ" @@ -42,7 +43,7 @@ RUN apt-get update \ RUN curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - \ && apt-get update \ && apt-get install -y --no-install-recommends nodejs \ - && npm install -g corepack@latest \ + && npm install -g corepack@latest "@openai/codex@${CODEX_NPM_VERSION}" \ && corepack enable \ && corepack prepare pnpm@10.28.2 --activate \ && apt-get clean \ diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 4c4a2f231666..5f736f5caddf 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,59 +1,85 @@ # Codex devcontainer -This is a Codex-focused devcontainer setup adapted for this monorepo. +Use this devcontainer when you want to run Codex inside your own project container. -## Core design choices +## Who this is for -- devcontainer schema + `init` + `updateRemoteUserUID` -- `${devcontainerId}`-scoped named volumes for per-container persistence -- read-only host `~/.gitconfig` mount with container-local `GIT_CONFIG_GLOBAL` -- explicit `workspaceMount`/`workspaceFolder` -- post-create bootstrap script (`post_install.py`) for idempotent setup +- developers using Codex on application repos +- teams that want a consistent, secure dev environment +- contributors working on this repo (also supported) -## What is Codex-specific +## Quick start -- Rust toolchain pinned to `1.92.0` with `clippy`, `rustfmt`, `rust-src` -- musl targets: `x86_64-unknown-linux-musl`, `aarch64-unknown-linux-musl` +1. Put this `.devcontainer/` folder in your project. +2. Open the project in VS Code. +3. Run **Dev Containers: Rebuild and Reopen in Container**. +4. In the container terminal, run `codex`. + +If you prefer API key auth, set `OPENAI_API_KEY` in your host environment before opening the container. + +## What you get by default + +- `codex` CLI preinstalled (`@openai/codex` via npm) - Node `22` + pnpm `10.28.2` -- firewall setup that allowlists domains from `OPENAI_ALLOWED_DOMAINS` -- persistent Cargo/Rustup volumes +- Python 3 + pip +- Rust `1.92.0` with `clippy`, `rustfmt`, `rust-src` +- musl targets: `x86_64-unknown-linux-musl`, `aarch64-unknown-linux-musl` +- common tools: git, zsh, rg, fd, fzf, jq, curl +- persistent state volumes for history, auth/config, Cargo cache, and Rustup -## Lifecycle hooks +## How to use Codex after opening the container -- `postCreateCommand`: `python3 /opt/post_install.py` - - configures history files - - fixes ownership on mounted dirs - - writes `/home/vscode/.gitconfig.local` -- `postStartCommand`: `bash /opt/post_start.sh` - - applies firewall rules through `init-firewall.sh` - - enforces IPv6 default-deny so strict mode cannot be bypassed over IPv6 - - optionally adds GitHub CIDR ranges from `api.github.com/meta` +Basic flow: -## Firewall modes +```bash +codex +``` -- **Strict (default)**: `CODEX_ENABLE_FIREWALL=1` (or unset) -- **Permissive**: `CODEX_ENABLE_FIREWALL=0` +Useful checks: -Optional strict-mode enhancement: +```bash +codex --help +which codex +``` -- `CODEX_INCLUDE_GITHUB_META_RANGES=1` (default) hydrates GitHub CIDRs into the allowlist. +Typical usage is from your project root (`/workspace`), so Codex can inspect and edit files directly. -To run in permissive mode during a session: +## Firewall and network policy + +Strict mode is the default (`CODEX_ENABLE_FIREWALL=1`): + +- outbound traffic is allowlisted by domain via `OPENAI_ALLOWED_DOMAINS` +- IPv4 is enforced with `iptables` + `ipset` +- IPv6 is explicitly default-deny via `ip6tables` (prevents bypass) + +Default allowlist includes: + +- OpenAI: `api.openai.com`, `auth.openai.com` +- GitHub: `github.com`, `api.github.com`, `codeload.github.com`, `raw.githubusercontent.com`, `objects.githubusercontent.com` +- registries: `registry.npmjs.org`, `crates.io`, `index.crates.io`, `static.crates.io`, `static.rust-lang.org`, `pypi.org`, `files.pythonhosted.org` + +You can temporarily disable strict mode: ```bash export CODEX_ENABLE_FIREWALL=0 ``` -Then restart or rebuild the container. +Then rebuild/restart the container. + +## Adding more languages or tooling -## Persistent volumes +For project-specific stacks (Go, Java, .NET, etc.), add Dev Container features in `devcontainer.json`. -- `/commandhistory` -- `/home/vscode/.codex` -- `/home/vscode/.config/gh` -- `/home/vscode/.cargo/registry` -- `/home/vscode/.cargo/git` -- `/home/vscode/.rustup` +Example: + +```json +{ + "features": { + "ghcr.io/devcontainers/features/go:1": { "version": "1.24" }, + "ghcr.io/devcontainers/features/java:1": { "version": "21" } + } +} +``` ## Local Docker smoke build diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a6e5bd2b4125..4d9ca6958dcc 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,13 +1,14 @@ { "$schema": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainer.schema.json", - "name": "Codex Dev", + "name": "Codex", "build": { "dockerfile": "Dockerfile", "context": "..", "args": { "TZ": "${localEnv:TZ:UTC}", "NODE_MAJOR": "22", - "RUST_TOOLCHAIN": "1.92.0" + "RUST_TOOLCHAIN": "1.92.0", + "CODEX_NPM_VERSION": "latest" } }, "runArgs": [ @@ -33,8 +34,8 @@ "CODEX_UNSAFE_ALLOW_NO_SANDBOX": "1", "CODEX_ENABLE_FIREWALL": "1", "CODEX_INCLUDE_GITHUB_META_RANGES": "1", - "OPENAI_ALLOWED_DOMAINS": "api.openai.com auth.openai.com github.com api.github.com codeload.github.com raw.githubusercontent.com objects.githubusercontent.com crates.io index.crates.io static.crates.io static.rust-lang.org registry.npmjs.org", - "CARGO_TARGET_DIR": "/workspace/codex-rs/target/devcontainer", + "OPENAI_ALLOWED_DOMAINS": "api.openai.com auth.openai.com github.com api.github.com codeload.github.com raw.githubusercontent.com objects.githubusercontent.com crates.io index.crates.io static.crates.io static.rust-lang.org registry.npmjs.org pypi.org files.pythonhosted.org", + "CARGO_TARGET_DIR": "/workspace/.cache/cargo-target", "GIT_CONFIG_GLOBAL": "/home/vscode/.gitconfig.local", "COREPACK_ENABLE_DOWNLOAD_PROMPT": "0", "PYTHONDONTWRITEBYTECODE": "1", From 2a4fef682c5d7806f2067abb3e6888600774cac3 Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Mon, 2 Feb 2026 22:03:17 -0800 Subject: [PATCH 7/8] feat(devcontainer): add codex-dev and secure profile variants --- .devcontainer/README.md | 95 +++++++++-------------- .devcontainer/devcontainer.codex-dev.json | 71 +++++++++++++++++ .devcontainer/devcontainer.json | 15 ++-- .devcontainer/devcontainer.secure.json | 76 ++++++++++++++++++ 4 files changed, 189 insertions(+), 68 deletions(-) create mode 100644 .devcontainer/devcontainer.codex-dev.json create mode 100644 .devcontainer/devcontainer.secure.json diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 5f736f5caddf..34bdfaac73eb 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,90 +1,69 @@ -# Codex devcontainer +# Codex devcontainer profiles -Use this devcontainer when you want to run Codex inside your own project container. +This folder now ships two profiles: -## Who this is for +- `devcontainer.codex-dev.json` (default intent: develop the Codex repo itself) +- `devcontainer.secure.json` (default intent: run Codex in a stricter, firewall-enforced project container) -- developers using Codex on application repos -- teams that want a consistent, secure dev environment -- contributors working on this repo (also supported) +`devcontainer.json` currently mirrors `devcontainer.codex-dev.json` so VS Code opens into the Codex contributor setup by default. -## Quick start +## Profile 1: Codex contributor (`devcontainer.codex-dev.json`) -1. Put this `.devcontainer/` folder in your project. -2. Open the project in VS Code. -3. Run **Dev Containers: Rebuild and Reopen in Container**. -4. In the container terminal, run `codex`. +Use this when working on this repository: -If you prefer API key auth, set `OPENAI_API_KEY` in your host environment before opening the container. +- forces `linux/arm64` (`platform` + `runArgs`) +- uses `CARGO_TARGET_DIR=${containerWorkspaceFolder}/codex-rs/target-arm64` +- keeps firewall off by default (`CODEX_ENABLE_FIREWALL=0`) for lower friction +- still includes persistent mounts and bootstrap (`post_install.py`) -## What you get by default +## Profile 2: Secure project (`devcontainer.secure.json`) -- `codex` CLI preinstalled (`@openai/codex` via npm) -- Node `22` + pnpm `10.28.2` -- Python 3 + pip -- Rust `1.92.0` with `clippy`, `rustfmt`, `rust-src` -- musl targets: `x86_64-unknown-linux-musl`, `aarch64-unknown-linux-musl` -- common tools: git, zsh, rg, fd, fzf, jq, curl -- persistent state volumes for history, auth/config, Cargo cache, and Rustup +Use this when you want stricter egress control: -## How to use Codex after opening the container +- enables firewall startup (`postStartCommand`) +- uses IPv4 allowlisting + IPv6 default-deny +- requires `NET_ADMIN` / `NET_RAW` caps +- uses project-generic Cargo target dir (`/workspace/.cache/cargo-target`) -Basic flow: +## How to switch profiles + +Option A (quick swap in repo): ```bash -codex +cp .devcontainer/devcontainer.secure.json .devcontainer/devcontainer.json ``` -Useful checks: +or ```bash -codex --help -which codex +cp .devcontainer/devcontainer.codex-dev.json .devcontainer/devcontainer.json ``` -Typical usage is from your project root (`/workspace`), so Codex can inspect and edit files directly. - -## Firewall and network policy - -Strict mode is the default (`CODEX_ENABLE_FIREWALL=1`): - -- outbound traffic is allowlisted by domain via `OPENAI_ALLOWED_DOMAINS` -- IPv4 is enforced with `iptables` + `ipset` -- IPv6 is explicitly default-deny via `ip6tables` (prevents bypass) - -Default allowlist includes: +Then run **Dev Containers: Rebuild and Reopen in Container**. -- OpenAI: `api.openai.com`, `auth.openai.com` -- GitHub: `github.com`, `api.github.com`, `codeload.github.com`, `raw.githubusercontent.com`, `objects.githubusercontent.com` -- registries: `registry.npmjs.org`, `crates.io`, `index.crates.io`, `static.crates.io`, `static.rust-lang.org`, `pypi.org`, `files.pythonhosted.org` - -You can temporarily disable strict mode: +Option B (CLI without copying): ```bash -export CODEX_ENABLE_FIREWALL=0 +devcontainer up --workspace-folder . --config .devcontainer/devcontainer.secure.json ``` -Then rebuild/restart the container. +or -## Adding more languages or tooling +```bash +devcontainer up --workspace-folder . --config .devcontainer/devcontainer.codex-dev.json +``` -For project-specific stacks (Go, Java, .NET, etc.), add Dev Container features in `devcontainer.json`. +## Using Codex after opening the container -Example: +The image preinstalls the Codex CLI. In the container terminal: -```json -{ - "features": { - "ghcr.io/devcontainers/features/go:1": { "version": "1.24" }, - "ghcr.io/devcontainers/features/java:1": { "version": "21" } - } -} +```bash +codex ``` -## Local Docker smoke build +Useful checks: ```bash -docker build -f .devcontainer/Dockerfile -t codex-devcontainer-test . -docker run --rm -it --cap-add=NET_ADMIN --cap-add=NET_RAW \ - -v "$PWD":/workspace -w /workspace codex-devcontainer-test zsh +which codex +codex --help ``` diff --git a/.devcontainer/devcontainer.codex-dev.json b/.devcontainer/devcontainer.codex-dev.json new file mode 100644 index 000000000000..c152c54fccb5 --- /dev/null +++ b/.devcontainer/devcontainer.codex-dev.json @@ -0,0 +1,71 @@ +{ + "$schema": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainer.schema.json", + "name": "Codex", + "build": { + "dockerfile": "Dockerfile", + "context": "..", + "platform": "linux/arm64", + "args": { + "TZ": "${localEnv:TZ:UTC}", + "NODE_MAJOR": "22", + "RUST_TOOLCHAIN": "1.92.0", + "CODEX_NPM_VERSION": "latest" + } + }, + "runArgs": [ + "--platform=linux/arm64" + ], + "init": true, + "updateRemoteUserUID": true, + "remoteUser": "vscode", + "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated", + "workspaceFolder": "/workspace", + "mounts": [ + "source=codex-commandhistory-${devcontainerId},target=/commandhistory,type=volume", + "source=codex-home-${devcontainerId},target=/home/vscode/.codex,type=volume", + "source=codex-gh-${devcontainerId},target=/home/vscode/.config/gh,type=volume", + "source=codex-cargo-registry-${devcontainerId},target=/home/vscode/.cargo/registry,type=volume", + "source=codex-cargo-git-${devcontainerId},target=/home/vscode/.cargo/git,type=volume", + "source=codex-rustup-${devcontainerId},target=/home/vscode/.rustup,type=volume", + "source=${localEnv:HOME}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,readonly" + ], + "containerEnv": { + "RUST_BACKTRACE": "1", + "CARGO_TARGET_DIR": "${containerWorkspaceFolder}/codex-rs/target-arm64", + "GIT_CONFIG_GLOBAL": "/home/vscode/.gitconfig.local", + "COREPACK_ENABLE_DOWNLOAD_PROMPT": "0", + "PYTHONDONTWRITEBYTECODE": "1", + "PIP_DISABLE_PIP_VERSION_CHECK": "1", + "CODEX_ENABLE_FIREWALL": "0" + }, + "remoteEnv": { + "OPENAI_API_KEY": "${localEnv:OPENAI_API_KEY}" + }, + "postCreateCommand": "python3 /opt/post_install.py", + "customizations": { + "vscode": { + "settings": { + "terminal.integrated.defaultProfile.linux": "zsh", + "terminal.integrated.profiles.linux": { + "bash": { + "path": "bash", + "icon": "terminal-bash" + }, + "zsh": { + "path": "zsh" + } + }, + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true + }, + "extensions": [ + "openai.chatgpt", + "rust-lang.rust-analyzer", + "tamasfe.even-better-toml", + "vadimcn.vscode-lldb", + "ms-azuretools.vscode-docker" + ] + } + } +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 4d9ca6958dcc..c152c54fccb5 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,6 +4,7 @@ "build": { "dockerfile": "Dockerfile", "context": "..", + "platform": "linux/arm64", "args": { "TZ": "${localEnv:TZ:UTC}", "NODE_MAJOR": "22", @@ -12,8 +13,7 @@ } }, "runArgs": [ - "--cap-add=NET_ADMIN", - "--cap-add=NET_RAW" + "--platform=linux/arm64" ], "init": true, "updateRemoteUserUID": true, @@ -31,22 +31,17 @@ ], "containerEnv": { "RUST_BACKTRACE": "1", - "CODEX_UNSAFE_ALLOW_NO_SANDBOX": "1", - "CODEX_ENABLE_FIREWALL": "1", - "CODEX_INCLUDE_GITHUB_META_RANGES": "1", - "OPENAI_ALLOWED_DOMAINS": "api.openai.com auth.openai.com github.com api.github.com codeload.github.com raw.githubusercontent.com objects.githubusercontent.com crates.io index.crates.io static.crates.io static.rust-lang.org registry.npmjs.org pypi.org files.pythonhosted.org", - "CARGO_TARGET_DIR": "/workspace/.cache/cargo-target", + "CARGO_TARGET_DIR": "${containerWorkspaceFolder}/codex-rs/target-arm64", "GIT_CONFIG_GLOBAL": "/home/vscode/.gitconfig.local", "COREPACK_ENABLE_DOWNLOAD_PROMPT": "0", "PYTHONDONTWRITEBYTECODE": "1", - "PIP_DISABLE_PIP_VERSION_CHECK": "1" + "PIP_DISABLE_PIP_VERSION_CHECK": "1", + "CODEX_ENABLE_FIREWALL": "0" }, "remoteEnv": { "OPENAI_API_KEY": "${localEnv:OPENAI_API_KEY}" }, "postCreateCommand": "python3 /opt/post_install.py", - "postStartCommand": "bash /opt/post_start.sh", - "waitFor": "postStartCommand", "customizations": { "vscode": { "settings": { diff --git a/.devcontainer/devcontainer.secure.json b/.devcontainer/devcontainer.secure.json new file mode 100644 index 000000000000..4d9ca6958dcc --- /dev/null +++ b/.devcontainer/devcontainer.secure.json @@ -0,0 +1,76 @@ +{ + "$schema": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainer.schema.json", + "name": "Codex", + "build": { + "dockerfile": "Dockerfile", + "context": "..", + "args": { + "TZ": "${localEnv:TZ:UTC}", + "NODE_MAJOR": "22", + "RUST_TOOLCHAIN": "1.92.0", + "CODEX_NPM_VERSION": "latest" + } + }, + "runArgs": [ + "--cap-add=NET_ADMIN", + "--cap-add=NET_RAW" + ], + "init": true, + "updateRemoteUserUID": true, + "remoteUser": "vscode", + "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated", + "workspaceFolder": "/workspace", + "mounts": [ + "source=codex-commandhistory-${devcontainerId},target=/commandhistory,type=volume", + "source=codex-home-${devcontainerId},target=/home/vscode/.codex,type=volume", + "source=codex-gh-${devcontainerId},target=/home/vscode/.config/gh,type=volume", + "source=codex-cargo-registry-${devcontainerId},target=/home/vscode/.cargo/registry,type=volume", + "source=codex-cargo-git-${devcontainerId},target=/home/vscode/.cargo/git,type=volume", + "source=codex-rustup-${devcontainerId},target=/home/vscode/.rustup,type=volume", + "source=${localEnv:HOME}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,readonly" + ], + "containerEnv": { + "RUST_BACKTRACE": "1", + "CODEX_UNSAFE_ALLOW_NO_SANDBOX": "1", + "CODEX_ENABLE_FIREWALL": "1", + "CODEX_INCLUDE_GITHUB_META_RANGES": "1", + "OPENAI_ALLOWED_DOMAINS": "api.openai.com auth.openai.com github.com api.github.com codeload.github.com raw.githubusercontent.com objects.githubusercontent.com crates.io index.crates.io static.crates.io static.rust-lang.org registry.npmjs.org pypi.org files.pythonhosted.org", + "CARGO_TARGET_DIR": "/workspace/.cache/cargo-target", + "GIT_CONFIG_GLOBAL": "/home/vscode/.gitconfig.local", + "COREPACK_ENABLE_DOWNLOAD_PROMPT": "0", + "PYTHONDONTWRITEBYTECODE": "1", + "PIP_DISABLE_PIP_VERSION_CHECK": "1" + }, + "remoteEnv": { + "OPENAI_API_KEY": "${localEnv:OPENAI_API_KEY}" + }, + "postCreateCommand": "python3 /opt/post_install.py", + "postStartCommand": "bash /opt/post_start.sh", + "waitFor": "postStartCommand", + "customizations": { + "vscode": { + "settings": { + "terminal.integrated.defaultProfile.linux": "zsh", + "terminal.integrated.profiles.linux": { + "bash": { + "path": "bash", + "icon": "terminal-bash" + }, + "zsh": { + "path": "zsh" + } + }, + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true + }, + "extensions": [ + "openai.chatgpt", + "rust-lang.rust-analyzer", + "tamasfe.even-better-toml", + "vadimcn.vscode-lldb", + "ms-azuretools.vscode-docker" + ] + } + } +} From 82d48531b30937359cfd5fa1033d37b91339cd61 Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Sat, 14 Mar 2026 00:05:25 -0700 Subject: [PATCH 8/8] feat(devcontainer): add separate secure customer profile --- .devcontainer/Dockerfile | 82 ++++++----------------- .devcontainer/Dockerfile.secure | 71 ++++++++++++++++++++ .devcontainer/README.md | 74 +++++++------------- .devcontainer/devcontainer.codex-dev.json | 71 -------------------- .devcontainer/devcontainer.json | 66 +++--------------- .devcontainer/devcontainer.secure.json | 4 +- 6 files changed, 128 insertions(+), 240 deletions(-) create mode 100644 .devcontainer/Dockerfile.secure delete mode 100644 .devcontainer/devcontainer.codex-dev.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index aaff9eb2d392..8c3a85977957 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,71 +1,27 @@ -FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04 +FROM ubuntu:24.04 -ARG TZ ARG DEBIAN_FRONTEND=noninteractive -ARG NODE_MAJOR=22 -ARG RUST_TOOLCHAIN=1.92.0 -ARG CODEX_NPM_VERSION=latest +# enable 'universe' because musl-tools & clang live there +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + software-properties-common && \ + add-apt-repository --yes universe -ENV TZ="$TZ" +# now install build deps +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + build-essential curl git ca-certificates \ + pkg-config libcap-dev clang musl-tools libssl-dev just && \ + rm -rf /var/lib/apt/lists/* -SHELL ["/bin/bash", "-o", "pipefail", "-c"] +# Ubuntu 24.04 ships with user 'ubuntu' already created with UID 1000. +USER ubuntu -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - build-essential \ - curl \ - git \ - ca-certificates \ - pkg-config \ - clang \ - musl-tools \ - libssl-dev \ - libsqlite3-dev \ - just \ - python3 \ - python3-pip \ - jq \ - less \ - man-db \ - unzip \ - ripgrep \ - fzf \ - fd-find \ - zsh \ - dnsutils \ - iproute2 \ - ipset \ - iptables \ - aggregate \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* +# install Rust + musl target as dev user +RUN curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal && \ + ~/.cargo/bin/rustup target add aarch64-unknown-linux-musl && \ + ~/.cargo/bin/rustup component add clippy rustfmt -RUN curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - \ - && apt-get update \ - && apt-get install -y --no-install-recommends nodejs \ - && npm install -g corepack@latest "@openai/codex@${CODEX_NPM_VERSION}" \ - && corepack enable \ - && corepack prepare pnpm@10.28.2 --activate \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* +ENV PATH="/home/ubuntu/.cargo/bin:${PATH}" -COPY .devcontainer/init-firewall.sh /usr/local/bin/init-firewall.sh -COPY .devcontainer/post_install.py /opt/post_install.py -COPY .devcontainer/post-start.sh /opt/post_start.sh - -RUN chmod 500 /usr/local/bin/init-firewall.sh \ - && chmod 755 /opt/post_start.sh \ - && chmod 644 /opt/post_install.py \ - && chown vscode:vscode /opt/post_install.py - -RUN install -d -m 0775 -o vscode -g vscode /commandhistory /workspace \ - && touch /commandhistory/.bash_history /commandhistory/.zsh_history \ - && chown vscode:vscode /commandhistory/.bash_history /commandhistory/.zsh_history - -USER vscode -ENV PATH="/home/vscode/.cargo/bin:${PATH}" WORKDIR /workspace - -RUN curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain "${RUST_TOOLCHAIN}" \ - && rustup component add clippy rustfmt rust-src \ - && rustup target add x86_64-unknown-linux-musl aarch64-unknown-linux-musl diff --git a/.devcontainer/Dockerfile.secure b/.devcontainer/Dockerfile.secure new file mode 100644 index 000000000000..aaff9eb2d392 --- /dev/null +++ b/.devcontainer/Dockerfile.secure @@ -0,0 +1,71 @@ +FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04 + +ARG TZ +ARG DEBIAN_FRONTEND=noninteractive +ARG NODE_MAJOR=22 +ARG RUST_TOOLCHAIN=1.92.0 +ARG CODEX_NPM_VERSION=latest + +ENV TZ="$TZ" + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + curl \ + git \ + ca-certificates \ + pkg-config \ + clang \ + musl-tools \ + libssl-dev \ + libsqlite3-dev \ + just \ + python3 \ + python3-pip \ + jq \ + less \ + man-db \ + unzip \ + ripgrep \ + fzf \ + fd-find \ + zsh \ + dnsutils \ + iproute2 \ + ipset \ + iptables \ + aggregate \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +RUN curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - \ + && apt-get update \ + && apt-get install -y --no-install-recommends nodejs \ + && npm install -g corepack@latest "@openai/codex@${CODEX_NPM_VERSION}" \ + && corepack enable \ + && corepack prepare pnpm@10.28.2 --activate \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +COPY .devcontainer/init-firewall.sh /usr/local/bin/init-firewall.sh +COPY .devcontainer/post_install.py /opt/post_install.py +COPY .devcontainer/post-start.sh /opt/post_start.sh + +RUN chmod 500 /usr/local/bin/init-firewall.sh \ + && chmod 755 /opt/post_start.sh \ + && chmod 644 /opt/post_install.py \ + && chown vscode:vscode /opt/post_install.py + +RUN install -d -m 0775 -o vscode -g vscode /commandhistory /workspace \ + && touch /commandhistory/.bash_history /commandhistory/.zsh_history \ + && chown vscode:vscode /commandhistory/.bash_history /commandhistory/.zsh_history + +USER vscode +ENV PATH="/home/vscode/.cargo/bin:${PATH}" +WORKDIR /workspace + +RUN curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain "${RUST_TOOLCHAIN}" \ + && rustup component add clippy rustfmt rust-src \ + && rustup target add x86_64-unknown-linux-musl aarch64-unknown-linux-musl diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 34bdfaac73eb..a5e114d37a40 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,69 +1,45 @@ -# Codex devcontainer profiles +# Containerized Development -This folder now ships two profiles: +We provide two container paths: -- `devcontainer.codex-dev.json` (default intent: develop the Codex repo itself) -- `devcontainer.secure.json` (default intent: run Codex in a stricter, firewall-enforced project container) +- `devcontainer.json` keeps the existing Codex contributor setup for working on this repository. +- `devcontainer.secure.json` adds a customer-oriented profile with stricter outbound network controls. -`devcontainer.json` currently mirrors `devcontainer.codex-dev.json` so VS Code opens into the Codex contributor setup by default. +## Codex contributor profile -## Profile 1: Codex contributor (`devcontainer.codex-dev.json`) +Use `devcontainer.json` when you are developing Codex itself. This is the same lightweight arm64 container that already exists in the repo. -Use this when working on this repository: +## Secure customer profile -- forces `linux/arm64` (`platform` + `runArgs`) -- uses `CARGO_TARGET_DIR=${containerWorkspaceFolder}/codex-rs/target-arm64` -- keeps firewall off by default (`CODEX_ENABLE_FIREWALL=0`) for lower friction -- still includes persistent mounts and bootstrap (`post_install.py`) +Use `devcontainer.secure.json` when you want a stricter runtime profile for running Codex inside a project container: -## Profile 2: Secure project (`devcontainer.secure.json`) +- installs the Codex CLI plus common build tools +- enables firewall startup with an allowlist-driven outbound policy +- blocks IPv6 by default so the allowlist cannot be bypassed over AAAA routes +- requires `NET_ADMIN` and `NET_RAW` so the firewall can be installed at startup -Use this when you want stricter egress control: +This profile keeps the stricter networking isolated to the customer path instead of changing the default Codex contributor container. -- enables firewall startup (`postStartCommand`) -- uses IPv4 allowlisting + IPv6 default-deny -- requires `NET_ADMIN` / `NET_RAW` caps -- uses project-generic Cargo target dir (`/workspace/.cache/cargo-target`) - -## How to switch profiles - -Option A (quick swap in repo): - -```bash -cp .devcontainer/devcontainer.secure.json .devcontainer/devcontainer.json -``` - -or - -```bash -cp .devcontainer/devcontainer.codex-dev.json .devcontainer/devcontainer.json -``` - -Then run **Dev Containers: Rebuild and Reopen in Container**. - -Option B (CLI without copying): +Start it from the CLI with: ```bash devcontainer up --workspace-folder . --config .devcontainer/devcontainer.secure.json ``` -or - -```bash -devcontainer up --workspace-folder . --config .devcontainer/devcontainer.codex-dev.json -``` +In VS Code, choose **Dev Containers: Open Folder in Container...** and select `.devcontainer/devcontainer.secure.json`. -## Using Codex after opening the container +## Docker -The image preinstalls the Codex CLI. In the container terminal: +To build the contributor image locally for x64 and then run it with the repo mounted under `/workspace`: -```bash -codex +```shell +CODEX_DOCKER_IMAGE_NAME=codex-linux-dev +docker build --platform=linux/amd64 -t "$CODEX_DOCKER_IMAGE_NAME" ./.devcontainer +docker run --platform=linux/amd64 --rm -it -e CARGO_TARGET_DIR=/workspace/codex-rs/target-amd64 -v "$PWD":/workspace -w /workspace/codex-rs "$CODEX_DOCKER_IMAGE_NAME" ``` -Useful checks: +Note that `/workspace/target` will contain the binaries built for your host platform, so we include `-e CARGO_TARGET_DIR=/workspace/codex-rs/target-amd64` in the `docker run` command so that the binaries built inside your container are written to a separate directory. -```bash -which codex -codex --help -``` +For arm64, specify `--platform=linux/arm64` instead for both `docker build` and `docker run`. + +Currently, the contributor `Dockerfile` works for both x64 and arm64 Linux, though you need to run `rustup target add x86_64-unknown-linux-musl` yourself to install the musl toolchain for x64. diff --git a/.devcontainer/devcontainer.codex-dev.json b/.devcontainer/devcontainer.codex-dev.json deleted file mode 100644 index c152c54fccb5..000000000000 --- a/.devcontainer/devcontainer.codex-dev.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainer.schema.json", - "name": "Codex", - "build": { - "dockerfile": "Dockerfile", - "context": "..", - "platform": "linux/arm64", - "args": { - "TZ": "${localEnv:TZ:UTC}", - "NODE_MAJOR": "22", - "RUST_TOOLCHAIN": "1.92.0", - "CODEX_NPM_VERSION": "latest" - } - }, - "runArgs": [ - "--platform=linux/arm64" - ], - "init": true, - "updateRemoteUserUID": true, - "remoteUser": "vscode", - "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated", - "workspaceFolder": "/workspace", - "mounts": [ - "source=codex-commandhistory-${devcontainerId},target=/commandhistory,type=volume", - "source=codex-home-${devcontainerId},target=/home/vscode/.codex,type=volume", - "source=codex-gh-${devcontainerId},target=/home/vscode/.config/gh,type=volume", - "source=codex-cargo-registry-${devcontainerId},target=/home/vscode/.cargo/registry,type=volume", - "source=codex-cargo-git-${devcontainerId},target=/home/vscode/.cargo/git,type=volume", - "source=codex-rustup-${devcontainerId},target=/home/vscode/.rustup,type=volume", - "source=${localEnv:HOME}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,readonly" - ], - "containerEnv": { - "RUST_BACKTRACE": "1", - "CARGO_TARGET_DIR": "${containerWorkspaceFolder}/codex-rs/target-arm64", - "GIT_CONFIG_GLOBAL": "/home/vscode/.gitconfig.local", - "COREPACK_ENABLE_DOWNLOAD_PROMPT": "0", - "PYTHONDONTWRITEBYTECODE": "1", - "PIP_DISABLE_PIP_VERSION_CHECK": "1", - "CODEX_ENABLE_FIREWALL": "0" - }, - "remoteEnv": { - "OPENAI_API_KEY": "${localEnv:OPENAI_API_KEY}" - }, - "postCreateCommand": "python3 /opt/post_install.py", - "customizations": { - "vscode": { - "settings": { - "terminal.integrated.defaultProfile.linux": "zsh", - "terminal.integrated.profiles.linux": { - "bash": { - "path": "bash", - "icon": "terminal-bash" - }, - "zsh": { - "path": "zsh" - } - }, - "files.trimTrailingWhitespace": true, - "files.insertFinalNewline": true, - "files.trimFinalNewlines": true - }, - "extensions": [ - "openai.chatgpt", - "rust-lang.rust-analyzer", - "tamasfe.even-better-toml", - "vadimcn.vscode-lldb", - "ms-azuretools.vscode-docker" - ] - } - } -} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c152c54fccb5..1bed79c3ca3c 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,71 +1,27 @@ { - "$schema": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainer.schema.json", "name": "Codex", "build": { "dockerfile": "Dockerfile", "context": "..", - "platform": "linux/arm64", - "args": { - "TZ": "${localEnv:TZ:UTC}", - "NODE_MAJOR": "22", - "RUST_TOOLCHAIN": "1.92.0", - "CODEX_NPM_VERSION": "latest" - } + "platform": "linux/arm64" }, - "runArgs": [ - "--platform=linux/arm64" - ], - "init": true, - "updateRemoteUserUID": true, - "remoteUser": "vscode", - "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated", - "workspaceFolder": "/workspace", - "mounts": [ - "source=codex-commandhistory-${devcontainerId},target=/commandhistory,type=volume", - "source=codex-home-${devcontainerId},target=/home/vscode/.codex,type=volume", - "source=codex-gh-${devcontainerId},target=/home/vscode/.config/gh,type=volume", - "source=codex-cargo-registry-${devcontainerId},target=/home/vscode/.cargo/registry,type=volume", - "source=codex-cargo-git-${devcontainerId},target=/home/vscode/.cargo/git,type=volume", - "source=codex-rustup-${devcontainerId},target=/home/vscode/.rustup,type=volume", - "source=${localEnv:HOME}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,readonly" - ], + + /* Force VS Code to run the container as arm64 in + case your host is x86 (or vice-versa). */ + "runArgs": ["--platform=linux/arm64"], + "containerEnv": { "RUST_BACKTRACE": "1", - "CARGO_TARGET_DIR": "${containerWorkspaceFolder}/codex-rs/target-arm64", - "GIT_CONFIG_GLOBAL": "/home/vscode/.gitconfig.local", - "COREPACK_ENABLE_DOWNLOAD_PROMPT": "0", - "PYTHONDONTWRITEBYTECODE": "1", - "PIP_DISABLE_PIP_VERSION_CHECK": "1", - "CODEX_ENABLE_FIREWALL": "0" - }, - "remoteEnv": { - "OPENAI_API_KEY": "${localEnv:OPENAI_API_KEY}" + "CARGO_TARGET_DIR": "${containerWorkspaceFolder}/codex-rs/target-arm64" }, - "postCreateCommand": "python3 /opt/post_install.py", + + "remoteUser": "ubuntu", "customizations": { "vscode": { "settings": { - "terminal.integrated.defaultProfile.linux": "zsh", - "terminal.integrated.profiles.linux": { - "bash": { - "path": "bash", - "icon": "terminal-bash" - }, - "zsh": { - "path": "zsh" - } - }, - "files.trimTrailingWhitespace": true, - "files.insertFinalNewline": true, - "files.trimFinalNewlines": true + "terminal.integrated.defaultProfile.linux": "bash" }, - "extensions": [ - "openai.chatgpt", - "rust-lang.rust-analyzer", - "tamasfe.even-better-toml", - "vadimcn.vscode-lldb", - "ms-azuretools.vscode-docker" - ] + "extensions": ["rust-lang.rust-analyzer", "tamasfe.even-better-toml"] } } } diff --git a/.devcontainer/devcontainer.secure.json b/.devcontainer/devcontainer.secure.json index 4d9ca6958dcc..a7f1f6c65467 100644 --- a/.devcontainer/devcontainer.secure.json +++ b/.devcontainer/devcontainer.secure.json @@ -1,8 +1,8 @@ { "$schema": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainer.schema.json", - "name": "Codex", + "name": "Codex (Secure)", "build": { - "dockerfile": "Dockerfile", + "dockerfile": "Dockerfile.secure", "context": "..", "args": { "TZ": "${localEnv:TZ:UTC}",