From 256672569dad3ede3b18ddbd9183266e83f21cc7 Mon Sep 17 00:00:00 2001 From: Guofang Date: Thu, 22 Oct 2026 16:00:27 +0800 Subject: [PATCH] Install ws2tcp-local, add entrypoint script and Claude installation handling Co-Authored-By: Claude Sonnet 5.5 --- debian13/Dockerfile | 18 +++++++++++++- debian13/build.sh | 55 +++++++++++++++++++++++++++++++++++++++++- debian13/entrypoint.sh | 34 ++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 2 deletions(-) create mode 100755 debian13/entrypoint.sh diff --git a/debian13/Dockerfile b/debian13/Dockerfile index a95ca9c..33ed892 100644 --- a/debian13/Dockerfile +++ b/debian13/Dockerfile @@ -826,4 +826,20 @@ RUN mkdir -p ${PREFIX}/share/source-stack \ > ${PREFIX}/share/source-stack/source-stack.cmake \ && rm /tmp/source-stack.cmake.in -CMD ["/bin/bash"] +RUN cargo install --locked --version 0.11.0 --root /usr/local ws2tcp-local \ + && rm -rf /root/.cargo/registry /root/.cargo/git + +ENV CLAUDE_CONFIG_DIR="/root/my-claude" +RUN bash -c 'curl -fsSL https://claude.ai/install.sh | bash' + +COPY --chmod=755 entrypoint.sh /usr/local/bin/ + +ENV PATH="/root/.local/bin:${PATH}" + +ARG CLAUDE_CODE_OAUTH_TOKEN +ENV CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN} + +ARG WS2TCP_LOCAL_BASIC_AUTH +ENV WS2TCP_LOCAL_BASIC_AUTH=${WS2TCP_LOCAL_BASIC_AUTH} + +ENTRYPOINT ["entrypoint.sh"] diff --git a/debian13/build.sh b/debian13/build.sh index b1cfe23..08c94db 100755 --- a/debian13/build.sh +++ b/debian13/build.sh @@ -1,10 +1,38 @@ #!/bin/sh +# Usage: +# ./build.sh build the image, asking for the password that unlocks the token +# ./build.sh --encrypt encrypt a token and print the value to paste into ENCRYPTED_TOKEN set -eu IMAGE_NAME="${IMAGE_NAME:-debian13-source-stack}" PREFIX="${PREFIX:-/usr/local}" SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) + +# CLAUDE_CODE_OAUTH_TOKEN encrypted with AES-256-CBC (PBKDF2, base64). Generate with --encrypt. +ENCRYPTED_TOKEN='U2FsdGVkX1/BCTASs0tR40hsT+sngp46zANCbGZnklLN1M0wSJs/DxbQ6JDg593G09A+cVtVius4hJHRzB6caOO2KwBV0/+i7yUjr/PfreZksrdkz1DnFc7oorxXfvB7azuNEkQfAE2fZg8cnYL5jy3/LKRdAm11SUm83fU8ke8=' + +OPENSSL_ARGS="enc -aes-256-cbc -pbkdf2 -iter 600000 -a -A" + +if ! command -v openssl >/dev/null 2>&1; then + echo "Error: OpenSSL is required." >&2 + exit 1 +fi + +# read_secret PROMPT VARNAME: read the input with echo on, so it stays visible while typing. +read_secret() { + printf '%s' "$1" >&2 + IFS= read -r "$2" +} + +if [ "${1:-}" = "--encrypt" ]; then + read_secret "OAuth token: " token + read_secret "Password: " BUILD_PASSWORD + export BUILD_PASSWORD + printf '%s' "$token" | openssl $OPENSSL_ARGS -pass env:BUILD_PASSWORD + echo + exit 0 +fi SOURCES_DIR="${SOURCES_DIR:-$SCRIPT_DIR/../sources}" FETCH_SCRIPT="$SCRIPT_DIR/../fetch-sources.sh" @@ -13,6 +41,28 @@ if ! command -v podman >/dev/null 2>&1; then exit 1 fi +if [ -z "$ENCRYPTED_TOKEN" ]; then + echo "Error: ENCRYPTED_TOKEN is empty; run './build.sh --encrypt' and paste the output into build.sh." >&2 + exit 1 +fi + +read_secret "Password for CLAUDE_CODE_OAUTH_TOKEN: " BUILD_PASSWORD +export BUILD_PASSWORD +if ! CLAUDE_CODE_OAUTH_TOKEN=$(printf '%s' "$ENCRYPTED_TOKEN" | openssl $OPENSSL_ARGS -d -pass env:BUILD_PASSWORD 2>/dev/null); then + echo "Error: wrong password or corrupted token." >&2 + exit 1 +fi +unset BUILD_PASSWORD + +printf 'ws2tcp-local username: ' >&2 +IFS= read -r WS2TCP_USER +read_secret "ws2tcp-local password: " WS2TCP_PASS +WS2TCP_LOCAL_BASIC_AUTH="$WS2TCP_USER:$WS2TCP_PASS" +unset WS2TCP_USER WS2TCP_PASS +export WS2TCP_LOCAL_BASIC_AUTH +# Exported so podman reads the value from the environment and it never appears in the command line. +export CLAUDE_CODE_OAUTH_TOKEN + SOURCES_DIR="$SOURCES_DIR" "$FETCH_SCRIPT" # macOS reports arm64 where Linux (and Debian multiarch) call it aarch64. @@ -20,6 +70,9 @@ ARCH=$(uname -m) [ "$ARCH" = arm64 ] && ARCH=aarch64 echo "Building image $IMAGE_NAME with podman (prefix $PREFIX) ..." -exec podman build --build-context sources="$SOURCES_DIR" \ +exec podman build \ + --build-arg CLAUDE_CODE_OAUTH_TOKEN \ + --build-arg WS2TCP_LOCAL_BASIC_AUTH \ + --build-context sources="$SOURCES_DIR" \ --build-arg PREFIX="$PREFIX" --build-arg MULTIARCH="$ARCH-linux-gnu" \ --tag "$IMAGE_NAME" "$SCRIPT_DIR" diff --git a/debian13/entrypoint.sh b/debian13/entrypoint.sh new file mode 100755 index 0000000..11b3293 --- /dev/null +++ b/debian13/entrypoint.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Start ws2tcp-local (logging to a file so it does not garble the claude TUI), point the proxy variables at it, then run claude. +set -eu + +GATEWAY="wss://wangguofang.net/tunnel" +LISTEN_HOST=127.0.0.1 +LISTEN_PORT=3128 +LOG_FILE="${WS2TCP_LOCAL_LOG:-/tmp/ws2tcp-local.log}" + +# The credentials come from WS2TCP_LOCAL_BASIC_AUTH (user:pass), set at image build time. +ws2tcp-local --gateway "$GATEWAY" --listen "$LISTEN_HOST:$LISTEN_PORT" >"$LOG_FILE" 2>&1 & +proxy_pid=$! + +for _ in $(seq 1 50); do + if (exec 3<>"/dev/tcp/$LISTEN_HOST/$LISTEN_PORT") 2>/dev/null; then + break + fi + if ! kill -0 "$proxy_pid" 2>/dev/null; then + echo "Error: ws2tcp-local exited during startup. Log ($LOG_FILE):" >&2 + cat "$LOG_FILE" >&2 + exit 1 + fi + sleep 0.2 +done + +export http_proxy="http://$LISTEN_HOST:$LISTEN_PORT" +export https_proxy="$http_proxy" +export HTTP_PROXY="$http_proxy" +export HTTPS_PROXY="$http_proxy" +export no_proxy="localhost,127.0.0.1" +export NO_PROXY="$no_proxy" + +trap 'kill "$proxy_pid" 2>/dev/null || true' EXIT +claude "$@"