Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .zshrc
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
31 changes: 18 additions & 13 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -13,23 +15,22 @@ 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
build-essential \
# Utilities
jq \
nano \
vim \
unzip \
vim \
# Network tools (for security testing)
iptables \
dnsutils \
ipset \
iptables \
iproute2 \
dnsutils \
&& apt-get clean && rm -rf /var/lib/apt/lists/*

# Install git-delta
Expand All @@ -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 && \
Expand Down Expand Up @@ -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

Expand Down
3 changes: 1 addition & 2 deletions devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
28 changes: 22 additions & 6 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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)}"
}
Expand All @@ -101,19 +114,20 @@ 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

if [[ -n "$custom_mounts" ]]; then
echo "$custom_mounts" >"$temp_file"
echo "$temp_file"
else
rm -f "$temp_file"
fi
}

Expand Down Expand Up @@ -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"
Expand All @@ -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
Expand Down