mirror of
https://github.com/trailofbits/claude-code-devcontainer
synced 2026-06-21 14:11:48 +00:00
Harden devcontainer: pin digests, guard SYS_ADMIN, fix mount filters (#20)
Address review feedback from #14–#19: - Pin base image and uv with SHA256 digests (#15) - Install fzf from GitHub releases instead of apt (#14) - Add SYS_ADMIN capability guard to prevent remount attacks (#16) - Fix mount filter to use target paths instead of source prefixes (#17) - Fix temp file leak in extract_mounts_to_file (#17) - Sort apt packages alphabetically (#18) - Remove unused Tailscale feature (#19) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,5 @@ _fzf_compgen_dir() {
|
||||
fdfind --type d --hidden --follow --exclude .git . "$1"
|
||||
}
|
||||
|
||||
# Source fzf key bindings and completion
|
||||
source ~/.fzf/key-bindings.zsh
|
||||
source ~/.fzf/completion.zsh
|
||||
# Source fzf shell integration (built-in since fzf 0.48+)
|
||||
eval "$(fzf --zsh)"
|
||||
|
||||
+19
-14
@@ -1,6 +1,8 @@
|
||||
# Claude Code Devcontainer
|
||||
# Based on Microsoft devcontainer image for better devcontainer integration
|
||||
FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04
|
||||
ARG UV_VERSION=0.10.0
|
||||
FROM ghcr.io/astral-sh/uv:${UV_VERSION}@sha256:78a7ff97cd27b7124a5f3c2aefe146170793c56a1e03321dd31a289f6d82a04f AS uv
|
||||
FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04@sha256:d94c97dd9cacf183d0a6fd12a8e87b526e9e928307674ae9c94139139c0c6eae
|
||||
|
||||
ARG TZ
|
||||
ENV TZ="$TZ"
|
||||
@@ -13,9 +15,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
bubblewrap \
|
||||
socat \
|
||||
# Modern CLI tools
|
||||
fzf \
|
||||
ripgrep \
|
||||
fd-find \
|
||||
ripgrep \
|
||||
tmux \
|
||||
zsh \
|
||||
# Build tools
|
||||
@@ -23,13 +24,13 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
# Utilities
|
||||
jq \
|
||||
nano \
|
||||
vim \
|
||||
unzip \
|
||||
vim \
|
||||
# Network tools (for security testing)
|
||||
iptables \
|
||||
ipset \
|
||||
iproute2 \
|
||||
dnsutils \
|
||||
ipset \
|
||||
iptables \
|
||||
iproute2 \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install git-delta
|
||||
@@ -40,7 +41,17 @@ RUN ARCH=$(dpkg --print-architecture) && \
|
||||
rm /tmp/git-delta.deb
|
||||
|
||||
# Install uv (Python package manager) via multi-stage copy
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
||||
COPY --from=uv /uv /usr/local/bin/uv
|
||||
|
||||
# Install fzf from GitHub releases (newer than apt, includes built-in shell integration)
|
||||
ARG FZF_VERSION=0.67.0
|
||||
RUN ARCH=$(dpkg --print-architecture) && \
|
||||
case "${ARCH}" in \
|
||||
amd64) FZF_ARCH="linux_amd64" ;; \
|
||||
arm64) FZF_ARCH="linux_arm64" ;; \
|
||||
*) echo "Unsupported architecture: ${ARCH}" && exit 1 ;; \
|
||||
esac && \
|
||||
curl -fsSL "https://github.com/junegunn/fzf/releases/download/v${FZF_VERSION}/fzf-${FZF_VERSION}-${FZF_ARCH}.tar.gz" | tar -xz -C /usr/local/bin
|
||||
|
||||
# Create directories and set ownership (combined for fewer layers)
|
||||
RUN mkdir -p /commandhistory /workspace /home/vscode/.claude /opt && \
|
||||
@@ -88,12 +99,6 @@ RUN sh -c "$(curl -fsSL https://github.com/deluan/zsh-in-docker/releases/downloa
|
||||
-p git \
|
||||
-x
|
||||
|
||||
# Download fzf shell integration (Ubuntu 24.04 package doesn't include these)
|
||||
ARG FZF_VERSION=0.44.1
|
||||
RUN mkdir -p /home/vscode/.fzf && \
|
||||
curl -fsSL "https://raw.githubusercontent.com/junegunn/fzf/${FZF_VERSION}/shell/key-bindings.zsh" -o /home/vscode/.fzf/key-bindings.zsh && \
|
||||
curl -fsSL "https://raw.githubusercontent.com/junegunn/fzf/${FZF_VERSION}/shell/completion.zsh" -o /home/vscode/.fzf/completion.zsh
|
||||
|
||||
# Copy zsh configuration
|
||||
COPY --chown=vscode:vscode .zshrc /home/vscode/.zshrc.custom
|
||||
|
||||
|
||||
+1
-2
@@ -10,8 +10,7 @@
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/github-cli:1": {},
|
||||
"ghcr.io/tailscale/codespace/tailscale:1": {}
|
||||
"ghcr.io/devcontainers/features/github-cli:1": {}
|
||||
},
|
||||
"runArgs": [
|
||||
"--cap-add=NET_ADMIN",
|
||||
|
||||
+22
-6
@@ -76,6 +76,19 @@ check_devcontainer_cli() {
|
||||
fi
|
||||
}
|
||||
|
||||
check_no_sys_admin() {
|
||||
local workspace="${1:-.}"
|
||||
local dc_json="$workspace/.devcontainer/devcontainer.json"
|
||||
[[ -f "$dc_json" ]] || return 0
|
||||
if jq -e \
|
||||
'.runArgs[]? | select(test("SYS_ADMIN"))' \
|
||||
"$dc_json" >/dev/null 2>&1; then
|
||||
log_error "SYS_ADMIN capability detected in runArgs."
|
||||
log_error "This defeats the read-only .devcontainer mount."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
get_workspace_folder() {
|
||||
echo "${1:-$(pwd)}"
|
||||
}
|
||||
@@ -101,12 +114,11 @@ extract_mounts_to_file() {
|
||||
custom_mounts=$(jq -c '
|
||||
.mounts // [] | map(
|
||||
select(
|
||||
(startswith("source=claude-code-bashhistory-") | not) and
|
||||
(startswith("source=claude-code-config-") | not) and
|
||||
(startswith("source=claude-code-gh-") | not) and
|
||||
(startswith("source=${localEnv:HOME}/.gitconfig,") | not) and
|
||||
# Security: read-only .devcontainer mount prevents container escape on rebuild
|
||||
(startswith("source=${localWorkspaceFolder}/.devcontainer,") | not)
|
||||
(contains("target=/commandhistory,") | not) and
|
||||
(contains("target=/home/vscode/.claude,") | not) and
|
||||
(contains("target=/home/vscode/.config/gh,") | not) and
|
||||
(contains("target=/home/vscode/.gitconfig,") | not) and
|
||||
(contains("target=/workspace/.devcontainer,") | not)
|
||||
)
|
||||
) | if length > 0 then . else empty end
|
||||
' "$devcontainer_json" 2>/dev/null) || true
|
||||
@@ -114,6 +126,8 @@ extract_mounts_to_file() {
|
||||
if [[ -n "$custom_mounts" ]]; then
|
||||
echo "$custom_mounts" >"$temp_file"
|
||||
echo "$temp_file"
|
||||
else
|
||||
rm -f "$temp_file"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -207,6 +221,7 @@ cmd_up() {
|
||||
workspace_folder="$(get_workspace_folder "${1:-}")"
|
||||
|
||||
check_devcontainer_cli
|
||||
check_no_sys_admin "$workspace_folder"
|
||||
log_info "Starting devcontainer in $workspace_folder..."
|
||||
|
||||
devcontainer up --workspace-folder "$workspace_folder"
|
||||
@@ -218,6 +233,7 @@ cmd_rebuild() {
|
||||
workspace_folder="$(get_workspace_folder "${1:-}")"
|
||||
|
||||
check_devcontainer_cli
|
||||
check_no_sys_admin "$workspace_folder"
|
||||
log_info "Rebuilding devcontainer in $workspace_folder..."
|
||||
|
||||
devcontainer up --workspace-folder "$workspace_folder" --remove-existing-container
|
||||
|
||||
Reference in New Issue
Block a user