mirror of
https://github.com/trailofbits/claude-code-devcontainer
synced 2026-06-21 14:11:48 +00:00
Improve uv install, fix gitconfig, add security docs
- Use COPY from ghcr.io/astral-sh/uv:latest instead of curl install - Fix gitconfig bug when host config is mounted read-only - Add global gitignore for common patterns (.claude/, .DS_Store, etc.) - Use GIT_CONFIG_GLOBAL env var for container-local git settings - Add Security Model section to README explaining sandbox boundaries - Change postCreateCommand to use uv run Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+2
-3
@@ -59,9 +59,8 @@ RUN ARCH=$(dpkg --print-architecture) && \
|
||||
dpkg -i /tmp/git-delta.deb && \
|
||||
rm /tmp/git-delta.deb
|
||||
|
||||
# Install uv (Python package manager)
|
||||
ENV UV_INSTALL_DIR="/usr/local/bin"
|
||||
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
# Install uv (Python package manager) via multi-stage copy
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
||||
|
||||
ARG USERNAME=ubuntu
|
||||
|
||||
|
||||
@@ -99,6 +99,41 @@ The following data persists across container rebuilds:
|
||||
|
||||
Your host `~/.gitconfig` is mounted read-only for git identity.
|
||||
|
||||
## Security Model
|
||||
|
||||
This devcontainer provides **filesystem isolation** but not complete sandboxing.
|
||||
|
||||
### What IS sandboxed
|
||||
|
||||
- **Filesystem**: Container has its own filesystem; your host files outside the mounted workspace are inaccessible
|
||||
- **Processes**: Container processes are isolated from host processes
|
||||
- **Package installations**: npm/pip installs stay in the container
|
||||
|
||||
### What is NOT sandboxed
|
||||
|
||||
- **Network**: Full outbound network access (can make API calls, download packages, etc.)
|
||||
- **Git identity**: Your `~/.gitconfig` is mounted read-only
|
||||
- **Docker socket**: Not mounted by default, but can be added if needed
|
||||
|
||||
### The `bypassPermissions` setting
|
||||
|
||||
This container auto-configures Claude Code with `bypassPermissions` mode, which:
|
||||
|
||||
- Allows Claude to run commands without confirmation prompts
|
||||
- Is appropriate here because the container itself is the sandbox
|
||||
- Would be risky on a host machine but is safe in this isolated environment
|
||||
|
||||
### Network isolation (optional)
|
||||
|
||||
For stricter security, you can enable network restrictions using the included iptables tools:
|
||||
|
||||
```bash
|
||||
# Example: Block all outbound except specific hosts
|
||||
sudo iptables -A OUTPUT -d api.anthropic.com -j ACCEPT
|
||||
sudo iptables -A OUTPUT -d github.com -j ACCEPT
|
||||
sudo iptables -A OUTPUT -j DROP
|
||||
```
|
||||
|
||||
## Auto-Configuration
|
||||
|
||||
On container creation, `post_install.py` automatically:
|
||||
|
||||
+3
-2
@@ -50,9 +50,10 @@
|
||||
"containerEnv": {
|
||||
"NODE_OPTIONS": "--max-old-space-size=4096",
|
||||
"CLAUDE_CONFIG_DIR": "/home/ubuntu/.claude",
|
||||
"POWERLEVEL9K_DISABLE_GITSTATUS": "true"
|
||||
"POWERLEVEL9K_DISABLE_GITSTATUS": "true",
|
||||
"GIT_CONFIG_GLOBAL": "/home/ubuntu/.gitconfig.local"
|
||||
},
|
||||
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated",
|
||||
"workspaceFolder": "/workspace",
|
||||
"postCreateCommand": "python3 /opt/post_install.py"
|
||||
"postCreateCommand": "uv run /opt/post_install.py"
|
||||
}
|
||||
|
||||
+78
-2
@@ -107,8 +107,10 @@ def setup_git_config():
|
||||
"""Set up git delta as the default pager if not already configured."""
|
||||
gitconfig = Path.home() / ".gitconfig"
|
||||
|
||||
# Only add delta config if .gitconfig doesn't exist or doesn't have pager config
|
||||
if not gitconfig.exists():
|
||||
# Skip if .gitconfig exists (likely mounted from host)
|
||||
if gitconfig.exists():
|
||||
print(f"[post_install] Git config exists (mounted from host): {gitconfig}")
|
||||
else:
|
||||
config = """\
|
||||
[core]
|
||||
pager = delta
|
||||
@@ -131,6 +133,79 @@ def setup_git_config():
|
||||
print(f"[post_install] Git config created: {gitconfig}")
|
||||
|
||||
|
||||
def setup_global_gitignore():
|
||||
"""Set up global gitignore and local git config.
|
||||
|
||||
Since ~/.gitconfig is mounted read-only from host, we create a local
|
||||
config file that includes the host config and adds container-specific
|
||||
settings like core.excludesfile.
|
||||
|
||||
GIT_CONFIG_GLOBAL env var (set in devcontainer.json) points git to this
|
||||
local config as the "global" config.
|
||||
"""
|
||||
home = Path.home()
|
||||
gitignore = home / ".gitignore_global"
|
||||
local_gitconfig = home / ".gitconfig.local"
|
||||
host_gitconfig = home / ".gitconfig"
|
||||
|
||||
# Create global gitignore with common patterns
|
||||
patterns = """\
|
||||
# Claude Code
|
||||
.claude/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
._*
|
||||
|
||||
# Python
|
||||
*.pyc
|
||||
*.pyo
|
||||
__pycache__/
|
||||
*.egg-info/
|
||||
.eggs/
|
||||
*.egg
|
||||
.venv/
|
||||
venv/
|
||||
.mypy_cache/
|
||||
.ruff_cache/
|
||||
|
||||
# Node
|
||||
node_modules/
|
||||
.npm/
|
||||
|
||||
# Editors
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.idea/
|
||||
.vscode/
|
||||
*.sublime-*
|
||||
|
||||
# Misc
|
||||
*.log
|
||||
.env.local
|
||||
.env.*.local
|
||||
"""
|
||||
gitignore.write_text(patterns)
|
||||
print(f"[post_install] Global gitignore created: {gitignore}")
|
||||
|
||||
# Create local git config that includes host config and sets excludesfile
|
||||
local_config = f"""\
|
||||
# Container-local git config
|
||||
# Includes host config (mounted read-only) and adds container settings
|
||||
|
||||
[include]
|
||||
path = {host_gitconfig}
|
||||
|
||||
[core]
|
||||
excludesfile = {gitignore}
|
||||
"""
|
||||
local_gitconfig.write_text(local_config)
|
||||
print(f"[post_install] Local git config created: {local_gitconfig}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all post-install configuration."""
|
||||
print("[post_install] Starting post-install configuration...")
|
||||
@@ -139,6 +214,7 @@ def main():
|
||||
setup_tmux_config()
|
||||
fix_directory_ownership()
|
||||
setup_git_config()
|
||||
setup_global_gitignore()
|
||||
|
||||
print("[post_install] Configuration complete!")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user