Skip to content

Commit a1f3994

Browse files
dguidoclaude
andauthored
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>
1 parent 90e3280 commit a1f3994

4 files changed

Lines changed: 43 additions & 24 deletions

File tree

.zshrc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,5 @@ _fzf_compgen_dir() {
5151
fdfind --type d --hidden --follow --exclude .git . "$1"
5252
}
5353

54-
# Source fzf key bindings and completion
55-
source ~/.fzf/key-bindings.zsh
56-
source ~/.fzf/completion.zsh
54+
# Source fzf shell integration (built-in since fzf 0.48+)
55+
eval "$(fzf --zsh)"

Dockerfile

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Claude Code Devcontainer
22
# Based on Microsoft devcontainer image for better devcontainer integration
3-
FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04
3+
ARG UV_VERSION=0.10.0
4+
FROM ghcr.io/astral-sh/uv:${UV_VERSION}@sha256:78a7ff97cd27b7124a5f3c2aefe146170793c56a1e03321dd31a289f6d82a04f AS uv
5+
FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04@sha256:d94c97dd9cacf183d0a6fd12a8e87b526e9e928307674ae9c94139139c0c6eae
46

57
ARG TZ
68
ENV TZ="$TZ"
@@ -13,23 +15,22 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
1315
bubblewrap \
1416
socat \
1517
# Modern CLI tools
16-
fzf \
17-
ripgrep \
1818
fd-find \
19+
ripgrep \
1920
tmux \
2021
zsh \
2122
# Build tools
2223
build-essential \
2324
# Utilities
2425
jq \
2526
nano \
26-
vim \
2727
unzip \
28+
vim \
2829
# Network tools (for security testing)
29-
iptables \
30+
dnsutils \
3031
ipset \
32+
iptables \
3133
iproute2 \
32-
dnsutils \
3334
&& apt-get clean && rm -rf /var/lib/apt/lists/*
3435

3536
# Install git-delta
@@ -40,7 +41,17 @@ RUN ARCH=$(dpkg --print-architecture) && \
4041
rm /tmp/git-delta.deb
4142

4243
# Install uv (Python package manager) via multi-stage copy
43-
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
44+
COPY --from=uv /uv /usr/local/bin/uv
45+
46+
# Install fzf from GitHub releases (newer than apt, includes built-in shell integration)
47+
ARG FZF_VERSION=0.67.0
48+
RUN ARCH=$(dpkg --print-architecture) && \
49+
case "${ARCH}" in \
50+
amd64) FZF_ARCH="linux_amd64" ;; \
51+
arm64) FZF_ARCH="linux_arm64" ;; \
52+
*) echo "Unsupported architecture: ${ARCH}" && exit 1 ;; \
53+
esac && \
54+
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
4455

4556
# Create directories and set ownership (combined for fewer layers)
4657
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
8899
-p git \
89100
-x
90101

91-
# Download fzf shell integration (Ubuntu 24.04 package doesn't include these)
92-
ARG FZF_VERSION=0.44.1
93-
RUN mkdir -p /home/vscode/.fzf && \
94-
curl -fsSL "https://raw.githubusercontent.com/junegunn/fzf/${FZF_VERSION}/shell/key-bindings.zsh" -o /home/vscode/.fzf/key-bindings.zsh && \
95-
curl -fsSL "https://raw.githubusercontent.com/junegunn/fzf/${FZF_VERSION}/shell/completion.zsh" -o /home/vscode/.fzf/completion.zsh
96-
97102
# Copy zsh configuration
98103
COPY --chown=vscode:vscode .zshrc /home/vscode/.zshrc.custom
99104

devcontainer.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
}
1111
},
1212
"features": {
13-
"ghcr.io/devcontainers/features/github-cli:1": {},
14-
"ghcr.io/tailscale/codespace/tailscale:1": {}
13+
"ghcr.io/devcontainers/features/github-cli:1": {}
1514
},
1615
"runArgs": [
1716
"--cap-add=NET_ADMIN",

install.sh

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,19 @@ check_devcontainer_cli() {
7676
fi
7777
}
7878

79+
check_no_sys_admin() {
80+
local workspace="${1:-.}"
81+
local dc_json="$workspace/.devcontainer/devcontainer.json"
82+
[[ -f "$dc_json" ]] || return 0
83+
if jq -e \
84+
'.runArgs[]? | select(test("SYS_ADMIN"))' \
85+
"$dc_json" >/dev/null 2>&1; then
86+
log_error "SYS_ADMIN capability detected in runArgs."
87+
log_error "This defeats the read-only .devcontainer mount."
88+
exit 1
89+
fi
90+
}
91+
7992
get_workspace_folder() {
8093
echo "${1:-$(pwd)}"
8194
}
@@ -101,19 +114,20 @@ extract_mounts_to_file() {
101114
custom_mounts=$(jq -c '
102115
.mounts // [] | map(
103116
select(
104-
(startswith("source=claude-code-bashhistory-") | not) and
105-
(startswith("source=claude-code-config-") | not) and
106-
(startswith("source=claude-code-gh-") | not) and
107-
(startswith("source=${localEnv:HOME}/.gitconfig,") | not) and
108-
# Security: read-only .devcontainer mount prevents container escape on rebuild
109-
(startswith("source=${localWorkspaceFolder}/.devcontainer,") | not)
117+
(contains("target=/commandhistory,") | not) and
118+
(contains("target=/home/vscode/.claude,") | not) and
119+
(contains("target=/home/vscode/.config/gh,") | not) and
120+
(contains("target=/home/vscode/.gitconfig,") | not) and
121+
(contains("target=/workspace/.devcontainer,") | not)
110122
)
111123
) | if length > 0 then . else empty end
112124
' "$devcontainer_json" 2>/dev/null) || true
113125

114126
if [[ -n "$custom_mounts" ]]; then
115127
echo "$custom_mounts" >"$temp_file"
116128
echo "$temp_file"
129+
else
130+
rm -f "$temp_file"
117131
fi
118132
}
119133

@@ -207,6 +221,7 @@ cmd_up() {
207221
workspace_folder="$(get_workspace_folder "${1:-}")"
208222

209223
check_devcontainer_cli
224+
check_no_sys_admin "$workspace_folder"
210225
log_info "Starting devcontainer in $workspace_folder..."
211226

212227
devcontainer up --workspace-folder "$workspace_folder"
@@ -218,6 +233,7 @@ cmd_rebuild() {
218233
workspace_folder="$(get_workspace_folder "${1:-}")"
219234

220235
check_devcontainer_cli
236+
check_no_sys_admin "$workspace_folder"
221237
log_info "Rebuilding devcontainer in $workspace_folder..."
222238

223239
devcontainer up --workspace-folder "$workspace_folder" --remove-existing-container

0 commit comments

Comments
 (0)