claude-init 1.0.33 → 1.0.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ generate git PR title and description for $ARGUMENTS
@@ -0,0 +1,126 @@
1
+ FROM node:20
2
+
3
+ ARG TZ
4
+ ENV TZ="$TZ"
5
+
6
+ ARG CLAUDE_CODE_VERSION=latest
7
+ ARG CODEX_VERSION=latest
8
+ ARG GEMINI_CLI_VERSION=latest
9
+ ARG GO_VERSION=1.22.7
10
+
11
+ # Install basic development tools and iptables/ipset
12
+ RUN apt-get update && apt-get install -y --no-install-recommends \
13
+ less \
14
+ git \
15
+ procps \
16
+ sudo \
17
+ fzf \
18
+ zsh \
19
+ man-db \
20
+ unzip \
21
+ gnupg2 \
22
+ gh \
23
+ iptables \
24
+ ipset \
25
+ iproute2 \
26
+ dnsutils \
27
+ aggregate \
28
+ jq \
29
+ nano \
30
+ vim \
31
+ && apt-get clean && rm -rf /var/lib/apt/lists/*
32
+
33
+ # Ensure default node user has access to /usr/local/share
34
+ RUN mkdir -p /usr/local/share/npm-global && \
35
+ chown -R node:node /usr/local/share
36
+
37
+ ARG USERNAME=node
38
+
39
+ # Persist bash history.
40
+ RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
41
+ && mkdir /commandhistory \
42
+ && touch /commandhistory/.bash_history \
43
+ && chown -R $USERNAME /commandhistory
44
+
45
+ # Set `DEVCONTAINER` environment variable to help with orientation
46
+ ENV DEVCONTAINER=true
47
+
48
+ # Create workspace and config directories and set permissions
49
+ RUN mkdir -p /workspace /home/node/.claude && \
50
+ chown -R node:node /workspace /home/node/.claude
51
+
52
+ WORKDIR /workspace
53
+
54
+ ARG GIT_DELTA_VERSION=0.18.2
55
+ RUN ARCH=$(dpkg --print-architecture) && \
56
+ wget "https://github.com/dandavison/delta/releases/download/${GIT_DELTA_VERSION}/git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
57
+ sudo dpkg -i "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
58
+ rm "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb"
59
+
60
+ # Install Go (golang)
61
+ USER root
62
+ RUN ARCH=$(dpkg --print-architecture) && \
63
+ case "$ARCH" in \
64
+ amd64) GO_ARCH=amd64 ;; \
65
+ arm64) GO_ARCH=arm64 ;; \
66
+ *) echo "Unsupported architecture: $ARCH" && exit 1 ;; \
67
+ esac && \
68
+ curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz" -o /tmp/go.tgz && \
69
+ rm -rf /usr/local/go && \
70
+ tar -C /usr/local -xzf /tmp/go.tgz && \
71
+ rm /tmp/go.tgz
72
+
73
+ # Configure Go environment for all users
74
+ ENV GOROOT=/usr/local/go
75
+ ENV GOPATH=/home/node/go
76
+ ENV PATH=$PATH:/usr/local/go/bin:/home/node/go/bin
77
+
78
+ # Set up non-root user
79
+ USER node
80
+
81
+ # Install gopls and delve in the node user's GOPATH
82
+ RUN go install golang.org/x/tools/gopls@latest && \
83
+ go install github.com/go-delve/delve/cmd/dlv@latest
84
+
85
+ # Install uv for Python package management and ensure it is on PATH
86
+ ENV PATH=/home/node/.local/bin:$PATH
87
+ RUN curl -LsSf https://astral.sh/uv/install.sh | sh
88
+
89
+ # Install global packages
90
+ ENV NPM_CONFIG_PREFIX=/usr/local/share/npm-global
91
+ ENV PATH=$PATH:/usr/local/share/npm-global/bin
92
+
93
+ # Set the default shell to zsh rather than sh
94
+ ENV SHELL=/bin/zsh
95
+
96
+ # Set the default editor and visual
97
+ ENV EDITOR=nano
98
+ ENV VISUAL=nano
99
+
100
+ # Default powerline10k theme
101
+ ARG ZSH_IN_DOCKER_VERSION=1.2.0
102
+ RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/v${ZSH_IN_DOCKER_VERSION}/zsh-in-docker.sh)" -- \
103
+ -p git \
104
+ -p fzf \
105
+ -a "source /usr/share/doc/fzf/examples/key-bindings.zsh" \
106
+ -a "source /usr/share/doc/fzf/examples/completion.zsh" \
107
+ -a "export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
108
+ -x
109
+
110
+ # Install claude-code
111
+ RUN npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}
112
+ # Install codex
113
+ RUN npm install -g @openai/codex@${CODEX_VERSION}
114
+ # Install gemini-cli
115
+ RUN npm install -g @google/gemini-cli@${GEMINI_CLI_VERSION}
116
+ # Install spec-kit
117
+ RUN uv tool install specify-cli --from git+https://github.com/github/spec-kit.git
118
+
119
+
120
+ # Copy and set up firewall script
121
+ COPY init-firewall.sh /usr/local/bin/
122
+ USER root
123
+ RUN chmod +x /usr/local/bin/init-firewall.sh && \
124
+ echo "node ALL=(root) NOPASSWD: /usr/local/bin/init-firewall.sh" > /etc/sudoers.d/node-firewall && \
125
+ chmod 0440 /etc/sudoers.d/node-firewall
126
+ USER node
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "claude-init",
3
+ "build": {
4
+ "dockerfile": "Dockerfile",
5
+ "args": {
6
+ "TZ": "${localEnv:TZ:America/Los_Angeles}",
7
+ "CLAUDE_CODE_VERSION": "latest",
8
+ "CODEX_VERSION": "latest",
9
+ "GEMINI_CLI_VERSION": "latest",
10
+ "GIT_DELTA_VERSION": "0.18.2",
11
+ "ZSH_IN_DOCKER_VERSION": "1.2.0",
12
+ "GO_VERSION": "1.22.7"
13
+ }
14
+ },
15
+ "runArgs": [
16
+ "--cap-add=NET_ADMIN",
17
+ "--cap-add=NET_RAW"
18
+ ],
19
+ "customizations": {
20
+ "vscode": {
21
+ "extensions": [
22
+ "dbaeumer.vscode-eslint",
23
+ "esbenp.prettier-vscode",
24
+ "eamodio.gitlens",
25
+ "anthropic.claude-code",
26
+ "golang.Go"
27
+ ],
28
+ "settings": {
29
+ "editor.formatOnSave": true,
30
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
31
+ "editor.codeActionsOnSave": {
32
+ "source.fixAll.eslint": "explicit"
33
+ },
34
+ "terminal.integrated.defaultProfile.linux": "zsh",
35
+ "terminal.integrated.profiles.linux": {
36
+ "bash": {
37
+ "path": "bash",
38
+ "icon": "terminal-bash"
39
+ },
40
+ "zsh": {
41
+ "path": "zsh"
42
+ }
43
+ }
44
+ }
45
+ }
46
+ },
47
+ "remoteUser": "node",
48
+ "mounts": [
49
+ "source=claude-code-bashhistory-${devcontainerId},target=/commandhistory,type=volume",
50
+ "source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume"
51
+ ],
52
+ "containerEnv": {
53
+ "NODE_OPTIONS": "--max-old-space-size=4096",
54
+ "CLAUDE_CONFIG_DIR": "/home/node/.claude",
55
+ "POWERLEVEL9K_DISABLE_GITSTATUS": "true"
56
+ },
57
+ "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated",
58
+ "workspaceFolder": "/workspace",
59
+ "postStartCommand": "sudo /usr/local/bin/init-firewall.sh",
60
+ "waitFor": "postStartCommand"
61
+ }
@@ -0,0 +1,155 @@
1
+ #!/bin/bash
2
+ set -euo pipefail # Exit on error, undefined vars, and pipeline failures
3
+ IFS=$'\n\t' # Stricter word splitting
4
+
5
+ # 1. Extract Docker DNS info BEFORE any flushing
6
+ DOCKER_DNS_RULES=$(iptables-save -t nat | grep "127\.0\.0\.11" || true)
7
+
8
+ # Flush existing rules and delete existing ipsets
9
+ iptables -F
10
+ iptables -X
11
+ iptables -t nat -F
12
+ iptables -t nat -X
13
+ iptables -t mangle -F
14
+ iptables -t mangle -X
15
+ ipset destroy allowed-domains 2>/dev/null || true
16
+
17
+ # 2. Selectively restore ONLY internal Docker DNS resolution
18
+ if [ -n "$DOCKER_DNS_RULES" ]; then
19
+ echo "Restoring Docker DNS rules..."
20
+ iptables -t nat -N DOCKER_OUTPUT 2>/dev/null || true
21
+ iptables -t nat -N DOCKER_POSTROUTING 2>/dev/null || true
22
+ echo "$DOCKER_DNS_RULES" | xargs -L 1 iptables -t nat
23
+ else
24
+ echo "No Docker DNS rules to restore"
25
+ fi
26
+
27
+ # First allow DNS and localhost before any restrictions
28
+ # Allow outbound DNS
29
+ iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
30
+ # Allow inbound DNS responses
31
+ iptables -A INPUT -p udp --sport 53 -j ACCEPT
32
+ # Allow outbound SSH
33
+ iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT
34
+ # Allow inbound SSH responses
35
+ iptables -A INPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
36
+ # Allow localhost
37
+ iptables -A INPUT -i lo -j ACCEPT
38
+ iptables -A OUTPUT -o lo -j ACCEPT
39
+
40
+ # Create ipset with CIDR support (idempotent)
41
+ ipset create -exist allowed-domains hash:net
42
+
43
+ # Fetch GitHub meta information and aggregate + add their IP ranges
44
+ echo "Fetching GitHub IP ranges..."
45
+ gh_ranges=$(curl -s https://api.github.com/meta)
46
+ if [ -z "$gh_ranges" ]; then
47
+ echo "ERROR: Failed to fetch GitHub IP ranges"
48
+ exit 1
49
+ fi
50
+
51
+ if ! echo "$gh_ranges" | jq -e '.web and .api and .git' >/dev/null; then
52
+ echo "ERROR: GitHub API response missing required fields"
53
+ exit 1
54
+ fi
55
+
56
+ echo "Processing GitHub IPs..."
57
+ while read -r cidr; do
58
+ if [[ ! "$cidr" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$ ]]; then
59
+ echo "ERROR: Invalid CIDR range from GitHub meta: $cidr"
60
+ exit 1
61
+ fi
62
+ echo "Adding GitHub range $cidr"
63
+ ipset add -exist allowed-domains "$cidr"
64
+ done < <(echo "$gh_ranges" | jq -r '(.web + .api + .git)[]' | aggregate -q)
65
+
66
+ # Resolve and add other allowed domains
67
+ # Include Go module infrastructure so `go mod tidy` works inside the container.
68
+ # - proxy.golang.org: primary module proxy
69
+ # - sum.golang.org: checksum database
70
+ # - storage.googleapis.com: backing store used by the proxy
71
+ # - golang.org/go.dev/go.googlesource.com: occasional fallbacks/tools
72
+ for domain in \
73
+ "registry.npmjs.org" \
74
+ "api.anthropic.com" \
75
+ "sentry.io" \
76
+ "statsig.anthropic.com" \
77
+ "statsig.com" \
78
+ "marketplace.visualstudio.com" \
79
+ "vscode.blob.core.windows.net" \
80
+ "update.code.visualstudio.com" \
81
+ "proxy.golang.org" \
82
+ "sum.golang.org" \
83
+ "storage.googleapis.com" \
84
+ "golang.org" \
85
+ "go.dev" \
86
+ "go.googlesource.com" \
87
+ "modernc.org" \
88
+ "pkg.go.dev" \
89
+ "api.telegram.org"; do
90
+ echo "Resolving $domain..."
91
+ ips=$(dig +noall +answer A "$domain" | awk '$4 == "A" {print $5}')
92
+ if [ -z "$ips" ]; then
93
+ echo "ERROR: Failed to resolve $domain"
94
+ exit 1
95
+ fi
96
+
97
+ while read -r ip; do
98
+ if [[ ! "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
99
+ echo "ERROR: Invalid IP from DNS for $domain: $ip"
100
+ exit 1
101
+ fi
102
+ echo "Adding $ip for $domain"
103
+ ipset add -exist allowed-domains "$ip"
104
+ done < <(echo "$ips")
105
+ done
106
+
107
+ # Add specific IP addresses, configure to add your own
108
+ echo "Adding specific allowed IPs..."
109
+ ipset add -exist allowed-domains "8.8.8.8"
110
+
111
+ # Get host IP from default route
112
+ HOST_IP=$(ip route | grep default | cut -d" " -f3)
113
+ if [ -z "$HOST_IP" ]; then
114
+ echo "ERROR: Failed to detect host IP"
115
+ exit 1
116
+ fi
117
+
118
+ HOST_NETWORK=$(echo "$HOST_IP" | sed "s/\.[0-9]*$/.0\/24/")
119
+ echo "Host network detected as: $HOST_NETWORK"
120
+
121
+ # Set up remaining iptables rules
122
+ iptables -A INPUT -s "$HOST_NETWORK" -j ACCEPT
123
+ iptables -A OUTPUT -d "$HOST_NETWORK" -j ACCEPT
124
+
125
+ # Set default policies to DROP first
126
+ iptables -P INPUT DROP
127
+ iptables -P FORWARD DROP
128
+ iptables -P OUTPUT DROP
129
+
130
+ # First allow established connections for already approved traffic
131
+ iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
132
+ iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
133
+
134
+ # Then allow only specific outbound traffic to allowed domains
135
+ iptables -A OUTPUT -m set --match-set allowed-domains dst -j ACCEPT
136
+
137
+ # Explicitly REJECT all other outbound traffic for immediate feedback
138
+ iptables -A OUTPUT -j REJECT --reject-with icmp-admin-prohibited
139
+
140
+ echo "Firewall configuration complete"
141
+ echo "Verifying firewall rules..."
142
+ if curl --connect-timeout 5 https://example.com >/dev/null 2>&1; then
143
+ echo "ERROR: Firewall verification failed - was able to reach https://example.com"
144
+ exit 1
145
+ else
146
+ echo "Firewall verification passed - unable to reach https://example.com as expected"
147
+ fi
148
+
149
+ # Verify GitHub API access
150
+ if ! curl --connect-timeout 5 https://api.github.com/zen >/dev/null 2>&1; then
151
+ echo "ERROR: Firewall verification failed - unable to reach https://api.github.com"
152
+ exit 1
153
+ else
154
+ echo "Firewall verification passed - able to reach https://api.github.com as expected"
155
+ fi
@@ -0,0 +1,124 @@
1
+ FROM node:20
2
+
3
+ ARG TZ
4
+ ENV TZ="$TZ"
5
+
6
+ ARG CLAUDE_CODE_VERSION=latest
7
+ ARG CODEX_VERSION=latest
8
+ ARG GEMINI_CLI_VERSION=latest
9
+
10
+ # Install basic development tools and iptables/ipset
11
+ RUN apt-get update && apt-get install -y --no-install-recommends \
12
+ less \
13
+ git \
14
+ procps \
15
+ sudo \
16
+ fzf \
17
+ zsh \
18
+ man-db \
19
+ unzip \
20
+ gnupg2 \
21
+ gh \
22
+ iptables \
23
+ ipset \
24
+ iproute2 \
25
+ dnsutils \
26
+ aggregate \
27
+ jq \
28
+ nano \
29
+ vim \
30
+ build-essential \
31
+ pkg-config \
32
+ cmake \
33
+ clang \
34
+ libclang-dev \
35
+ protobuf-compiler \
36
+ libssl-dev \
37
+ libsnappy-dev \
38
+ zlib1g-dev \
39
+ libbz2-dev \
40
+ liblz4-dev \
41
+ libzstd-dev \
42
+ curl \
43
+ && apt-get clean && rm -rf /var/lib/apt/lists/*
44
+
45
+ # Ensure default node user has access to /usr/local/share
46
+ RUN mkdir -p /usr/local/share/npm-global && \
47
+ chown -R node:node /usr/local/share
48
+
49
+ ARG USERNAME=node
50
+
51
+ # Persist bash history.
52
+ RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
53
+ && mkdir /commandhistory \
54
+ && touch /commandhistory/.bash_history \
55
+ && chown -R $USERNAME /commandhistory
56
+
57
+ # Set `DEVCONTAINER` environment variable to help with orientation
58
+ ENV DEVCONTAINER=true
59
+
60
+ # Create workspace and config directories and set permissions
61
+ RUN mkdir -p /workspace /home/node/.claude && \
62
+ chown -R node:node /workspace /home/node/.claude
63
+
64
+ WORKDIR /workspace
65
+
66
+ ARG GIT_DELTA_VERSION=0.18.2
67
+ RUN ARCH=$(dpkg --print-architecture) && \
68
+ wget "https://github.com/dandavison/delta/releases/download/${GIT_DELTA_VERSION}/git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
69
+ sudo dpkg -i "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
70
+ rm "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb"
71
+
72
+ # Set up non-root user
73
+ USER node
74
+
75
+ # Install uv for Python package management and ensure it is on PATH
76
+ ENV PATH=/home/node/.local/bin:$PATH
77
+ RUN curl -LsSf https://astral.sh/uv/install.sh | sh
78
+
79
+ # Install Rust toolchain for node user
80
+ ENV CARGO_HOME=/home/node/.cargo
81
+ ENV RUSTUP_HOME=/home/node/.rustup
82
+ ENV PATH=/home/node/.cargo/bin:$PATH
83
+ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
84
+ && rustup default stable \
85
+ && rustup component add rustfmt clippy
86
+
87
+ # Install global packages
88
+ ENV NPM_CONFIG_PREFIX=/usr/local/share/npm-global
89
+ ENV PATH=$PATH:/usr/local/share/npm-global/bin
90
+
91
+ # Set the default shell to zsh rather than sh
92
+ ENV SHELL=/bin/zsh
93
+
94
+ # Set the default editor and visual
95
+ ENV EDITOR=nano
96
+ ENV VISUAL=nano
97
+
98
+ # Default powerline10k theme
99
+ ARG ZSH_IN_DOCKER_VERSION=1.2.0
100
+ RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/v${ZSH_IN_DOCKER_VERSION}/zsh-in-docker.sh)" -- \
101
+ -p git \
102
+ -p fzf \
103
+ -a "source /usr/share/doc/fzf/examples/key-bindings.zsh" \
104
+ -a "source /usr/share/doc/fzf/examples/completion.zsh" \
105
+ -a "export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
106
+ -x
107
+
108
+ # Install claude-code
109
+ RUN npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}
110
+ # Install codex
111
+ RUN npm install -g @openai/codex@${CODEX_VERSION}
112
+ # Install gemini-cli
113
+ RUN npm install -g @google/gemini-cli@${GEMINI_CLI_VERSION}
114
+ # Install spec-kit
115
+ RUN uv tool install specify-cli --from git+https://github.com/github/spec-kit.git
116
+
117
+
118
+ # Copy and set up firewall script
119
+ COPY init-firewall.sh /usr/local/bin/
120
+ USER root
121
+ RUN chmod +x /usr/local/bin/init-firewall.sh && \
122
+ echo "node ALL=(root) NOPASSWD: /usr/local/bin/init-firewall.sh" > /etc/sudoers.d/node-firewall && \
123
+ chmod 0440 /etc/sudoers.d/node-firewall
124
+ USER node
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "claude-init",
3
+ "build": {
4
+ "dockerfile": "Dockerfile",
5
+ "args": {
6
+ "TZ": "${localEnv:TZ:America/Los_Angeles}",
7
+ "CLAUDE_CODE_VERSION": "latest",
8
+ "CODEX_VERSION": "latest",
9
+ "GEMINI_CLI_VERSION": "latest",
10
+ "GIT_DELTA_VERSION": "0.18.2",
11
+ "ZSH_IN_DOCKER_VERSION": "1.2.0"
12
+ }
13
+ },
14
+ "runArgs": [
15
+ "--cap-add=NET_ADMIN",
16
+ "--cap-add=NET_RAW"
17
+ ],
18
+ "customizations": {
19
+ "vscode": {
20
+ "extensions": [
21
+ "dbaeumer.vscode-eslint",
22
+ "esbenp.prettier-vscode",
23
+ "eamodio.gitlens",
24
+ "anthropic.claude-code",
25
+ "rust-lang.rust-analyzer",
26
+ "tamasfe.even-better-toml",
27
+ "vadimcn.vscode-lldb"
28
+ ],
29
+ "settings": {
30
+ "editor.formatOnSave": true,
31
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
32
+ "editor.codeActionsOnSave": {
33
+ "source.fixAll.eslint": "explicit"
34
+ },
35
+ "terminal.integrated.defaultProfile.linux": "zsh",
36
+ "terminal.integrated.profiles.linux": {
37
+ "bash": {
38
+ "path": "bash",
39
+ "icon": "terminal-bash"
40
+ },
41
+ "zsh": {
42
+ "path": "zsh"
43
+ }
44
+ }
45
+ }
46
+ }
47
+ },
48
+ "remoteUser": "node",
49
+ "mounts": [
50
+ "source=claude-code-bashhistory-${devcontainerId},target=/commandhistory,type=volume",
51
+ "source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume"
52
+ ],
53
+ "containerEnv": {
54
+ "NODE_OPTIONS": "--max-old-space-size=4096",
55
+ "CLAUDE_CONFIG_DIR": "/home/node/.claude",
56
+ "POWERLEVEL9K_DISABLE_GITSTATUS": "true"
57
+ },
58
+ "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated",
59
+ "workspaceFolder": "/workspace",
60
+ "postStartCommand": "sudo /usr/local/bin/init-firewall.sh",
61
+ "waitFor": "postStartCommand"
62
+ }
@@ -0,0 +1,139 @@
1
+ #!/bin/bash
2
+ set -euo pipefail # Exit on error, undefined vars, and pipeline failures
3
+ IFS=$'\n\t' # Stricter word splitting
4
+
5
+ # 1. Extract Docker DNS info BEFORE any flushing
6
+ DOCKER_DNS_RULES=$(iptables-save -t nat | grep "127\.0\.0\.11" || true)
7
+
8
+ # Flush existing rules and delete existing ipsets
9
+ iptables -F
10
+ iptables -X
11
+ iptables -t nat -F
12
+ iptables -t nat -X
13
+ iptables -t mangle -F
14
+ iptables -t mangle -X
15
+ ipset destroy allowed-domains 2>/dev/null || true
16
+
17
+ # 2. Selectively restore ONLY internal Docker DNS resolution
18
+ if [ -n "$DOCKER_DNS_RULES" ]; then
19
+ echo "Restoring Docker DNS rules..."
20
+ iptables -t nat -N DOCKER_OUTPUT 2>/dev/null || true
21
+ iptables -t nat -N DOCKER_POSTROUTING 2>/dev/null || true
22
+ echo "$DOCKER_DNS_RULES" | xargs -L 1 iptables -t nat
23
+ else
24
+ echo "No Docker DNS rules to restore"
25
+ fi
26
+
27
+ # First allow DNS and localhost before any restrictions
28
+ # Allow outbound DNS
29
+ iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
30
+ # Allow inbound DNS responses
31
+ iptables -A INPUT -p udp --sport 53 -j ACCEPT
32
+ # Allow outbound SSH
33
+ iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT
34
+ # Allow inbound SSH responses
35
+ iptables -A INPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
36
+ # Allow localhost
37
+ iptables -A INPUT -i lo -j ACCEPT
38
+ iptables -A OUTPUT -o lo -j ACCEPT
39
+
40
+ # Create ipset with CIDR support
41
+ ipset create allowed-domains hash:net
42
+
43
+ # Fetch GitHub meta information and aggregate + add their IP ranges
44
+ echo "Fetching GitHub IP ranges..."
45
+ gh_ranges=$(curl -s https://api.github.com/meta)
46
+ if [ -z "$gh_ranges" ]; then
47
+ echo "ERROR: Failed to fetch GitHub IP ranges"
48
+ exit 1
49
+ fi
50
+
51
+ if ! echo "$gh_ranges" | jq -e '.web and .api and .git' >/dev/null; then
52
+ echo "ERROR: GitHub API response missing required fields"
53
+ exit 1
54
+ fi
55
+
56
+ echo "Processing GitHub IPs..."
57
+ while read -r cidr; do
58
+ if [[ ! "$cidr" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$ ]]; then
59
+ echo "ERROR: Invalid CIDR range from GitHub meta: $cidr"
60
+ exit 1
61
+ fi
62
+ echo "Adding GitHub range $cidr"
63
+ ipset add allowed-domains "$cidr"
64
+ done < <(echo "$gh_ranges" | jq -r '(.web + .api + .git)[]' | aggregate -q)
65
+
66
+ # Resolve and add other allowed domains
67
+ for domain in \
68
+ "index.crates.io" \
69
+ "static.crates.io" \
70
+ "registry.npmjs.org" \
71
+ "api.anthropic.com" \
72
+ "sentry.io" \
73
+ "statsig.anthropic.com" \
74
+ "statsig.com" \
75
+ "marketplace.visualstudio.com" \
76
+ "vscode.blob.core.windows.net" \
77
+ "update.code.visualstudio.com"; do
78
+ echo "Resolving $domain..."
79
+ ips=$(dig +noall +answer A "$domain" | awk '$4 == "A" {print $5}')
80
+ if [ -z "$ips" ]; then
81
+ echo "ERROR: Failed to resolve $domain"
82
+ exit 1
83
+ fi
84
+
85
+ while read -r ip; do
86
+ if [[ ! "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
87
+ echo "ERROR: Invalid IP from DNS for $domain: $ip"
88
+ exit 1
89
+ fi
90
+ echo "Adding $ip for $domain"
91
+ ipset add allowed-domains "$ip"
92
+ done < <(echo "$ips")
93
+ done
94
+
95
+ # Get host IP from default route
96
+ HOST_IP=$(ip route | grep default | cut -d" " -f3)
97
+ if [ -z "$HOST_IP" ]; then
98
+ echo "ERROR: Failed to detect host IP"
99
+ exit 1
100
+ fi
101
+
102
+ HOST_NETWORK=$(echo "$HOST_IP" | sed "s/\.[0-9]*$/.0\/24/")
103
+ echo "Host network detected as: $HOST_NETWORK"
104
+
105
+ # Set up remaining iptables rules
106
+ iptables -A INPUT -s "$HOST_NETWORK" -j ACCEPT
107
+ iptables -A OUTPUT -d "$HOST_NETWORK" -j ACCEPT
108
+
109
+ # Set default policies to DROP first
110
+ iptables -P INPUT DROP
111
+ iptables -P FORWARD DROP
112
+ iptables -P OUTPUT DROP
113
+
114
+ # First allow established connections for already approved traffic
115
+ iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
116
+ iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
117
+
118
+ # Then allow only specific outbound traffic to allowed domains
119
+ iptables -A OUTPUT -m set --match-set allowed-domains dst -j ACCEPT
120
+
121
+ # Explicitly REJECT all other outbound traffic for immediate feedback
122
+ iptables -A OUTPUT -j REJECT --reject-with icmp-admin-prohibited
123
+
124
+ echo "Firewall configuration complete"
125
+ echo "Verifying firewall rules..."
126
+ if curl --connect-timeout 5 https://example.com >/dev/null 2>&1; then
127
+ echo "ERROR: Firewall verification failed - was able to reach https://example.com"
128
+ exit 1
129
+ else
130
+ echo "Firewall verification passed - unable to reach https://example.com as expected"
131
+ fi
132
+
133
+ # Verify GitHub API access
134
+ if ! curl --connect-timeout 5 https://api.github.com/zen >/dev/null 2>&1; then
135
+ echo "ERROR: Firewall verification failed - unable to reach https://api.github.com"
136
+ exit 1
137
+ else
138
+ echo "Firewall verification passed - able to reach https://api.github.com as expected"
139
+ fi
package/CLAUDE.md CHANGED
@@ -1 +1,5 @@
1
1
  + During you interaction with the user, if you find anything reusable in this project (e.g. version of a library, model name), especially about a fix to a mistake you made or a correction you received, you should take note in the `Lessons` section in the `CLAUDE.md` file so you will not make the same mistake again.
2
+
3
+ ## Lessons
4
+
5
+ - When adding new template directories (like `.devcontainer-rust/`, `.devcontainer-go/`) to the project, remember to also add them to the `files` array in `package.json`. Otherwise they won't be included in the published npm package and users will get "ENOENT: no such file or directory" errors when running via `npx`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-init",
3
- "version": "1.0.33",
3
+ "version": "1.0.36",
4
4
  "description": "Initialize Claude development environment with configurations and templates",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,7 +12,9 @@
12
12
  "src/",
13
13
  "CLAUDE.md",
14
14
  ".claude/",
15
- ".devcontainer/"
15
+ ".devcontainer/",
16
+ ".devcontainer-rust/",
17
+ ".devcontainer-go/"
16
18
  ],
17
19
  "scripts": {
18
20
  "test": "node --test test/*.test.js",