fix(devcontainer): fix claude login hanging + replace npm with install script#27306
Closed
Convl wants to merge 2 commits intoanthropics:mainfrom
Closed
fix(devcontainer): fix claude login hanging + replace npm with install script#27306Convl wants to merge 2 commits intoanthropics:mainfrom
Convl wants to merge 2 commits intoanthropics:mainfrom
Conversation
Claude Code's OAuth callback server resolves "localhost" via getaddrinfo()
and binds to whichever address is returned first. Ubuntu containers list ::1
before 127.0.0.1 in /etc/hosts, so the server ends up on [::1]:PORT.
VS Code port forwarding connects via IPv4 (127.0.0.1) and never reaches it,
causing `claude login` to hang indefinitely after the user clicks Authorize.
Three changes to init-firewall.sh:
1. Remove ::1 entries from /etc/hosts so getaddrinfo("localhost") returns
only 127.0.0.1. This fixes the binding at the C library level, which all
Node.js runtimes (including Claude Code's bundled one) use underneath.
Note: sed -i cannot be used on /etc/hosts in Docker because it is a bind
mount that does not support atomic rename; grep + cat is used instead.
2. Add claude.ai and platform.claude.com to the firewall allowlist. After
receiving the OAuth callback, Claude Code makes outbound requests to these
domains to complete the token exchange. Without them the request is blocked
and the browser hangs waiting for a response from the local callback server.
3. Use `ipset add -exist` instead of `ipset add` to silently skip duplicate
entries. claude.ai resolves to IPs already covered by the GitHub CIDR
ranges, causing the script to crash with "Element cannot be added to the
set: it's already added" and exit before completing setup.
Fixes: #9376
…all script The npm package (@anthropic-ai/claude-code) is no longer the supported installation method. Replace it with the official install script: curl -fsSL https://claude.ai/install.sh | bash The install script drops the binary in ~/.local/bin rather than the npm global bin directory, so add /home/node/.local/bin to PATH.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Two issues make the reference devcontainer broken out of the box:
1.
claude loginhangs indefinitely after clicking AuthorizeClaude Code's OAuth callback server calls
getaddrinfo("localhost")and binds to whichever address is returned first. Ubuntu containers list::1before127.0.0.1in/etc/hosts, so the server ends up on[::1]:PORT. VS Code port forwarding connects via IPv4 (127.0.0.1) and never reaches it — the browser shows "Waiting for localhost…" forever.Setting
NODE_OPTIONS=--dns-result-order=ipv4firstdoes not fix this because Claude Code ships a self-contained binary with a bundled Node.js runtime that ignores environment-level Node.js flags.The fix is to remove
::1entries from/etc/hostsat container startup, which forcesgetaddrinfoto return only127.0.0.1forlocalhost. This works at the C library level, which all Node.js runtimes call regardless of how they are bundled.Note:
sed -icannot be used here because Docker mounts/etc/hostsas a bind mount that does not support the atomic renamesed -irelies on;grep + catis used instead.Additionally,
claude.aiandplatform.claude.commust be in the firewall allowlist. After receiving the OAuth callback, Claude Code makes outbound requests to these domains to complete the token exchange. Without them the browser hangs waiting for a response from the local callback server even after the/etc/hostsfix.Finally,
ipset addis changed toipset add -existthroughout.claude.airesolves to IPs already covered by the GitHub CIDR ranges added earlier, causing the script to crash with "Element cannot be added to the set: it's already added" and exit before the firewall is fully configured.Fixes #9376
2. Claude Code installation via npm no longer works
The npm package (
@anthropic-ai/claude-code) is no longer the supported install method. The Dockerfile is updated to use the official install script (curl -fsSL https://claude.ai/install.sh | bash), and/home/node/.local/binis added toPATHsince that is where the script installs the binary for non-root users.Changes
.devcontainer/init-firewall.sh: remove::1from/etc/hostsat startup; addclaude.aiandplatform.claude.comto allowlist; useipset add -existto handle duplicate IPs.devcontainer/Dockerfile: replacenpm install -g @anthropic-ai/claude-codewithcurl -fsSL https://claude.ai/install.sh | bash; add/home/node/.local/bintoPATH