diff --git a/Dockerfile b/Dockerfile index 1818742..a65d067 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,24 +1,51 @@ ARG DEBIAN_RELEASE=bullseye -FROM docker.io/debian:$DEBIAN_RELEASE-slim +FROM docker.io/debian:${DEBIAN_RELEASE}-slim + ARG DEBIAN_RELEASE ENV DEBIAN_FRONTEND=noninteractive -RUN true && \ - apt update && \ - apt install -y gnupg ca-certificates curl socat +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + gnupg \ + ca-certificates \ + curl \ + gzip \ + tar \ + jq && \ + rm -rf /var/lib/apt/lists/* -RUN curl https://pkg.cloudflareclient.com/pubkey.gpg | gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg && \ - ARCH="$(dpkg --print-architecture)" && \ - echo "deb [arch=${ARCH} signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ $DEBIAN_RELEASE main" | tee /etc/apt/sources.list.d/cloudflare-client.list && \ - apt update && \ - apt install cloudflare-warp -y --no-install-recommends +RUN ARCH="$(dpkg --print-architecture)" && \ + case "${ARCH}" in \ + amd64) GOST_ARCH="amd64" ;; \ + arm64) GOST_ARCH="arm64" ;; \ + armhf) GOST_ARCH="armv7" ;; \ + *) echo "Unsupported architecture" && exit 1 ;; \ + esac && \ + DOWNLOAD_URL=$(curl -fsSL https://api.github.com/repos/go-gost/gost/releases/latest | \ + jq -r --arg arch "linux_${GOST_ARCH}.tar.gz" '.assets[] | select(.name | endswith($arch)) | .browser_download_url' | head -n 1) && \ + if [ -z "$DOWNLOAD_URL" ]; then echo "Error: Failed to find download URL for architecture ${GOST_ARCH}"; exit 1; fi && \ + echo "Downloading GOST from: $DOWNLOAD_URL" && \ + curl -fsSL "$DOWNLOAD_URL" | tar -C /usr/local/bin -xzf - gost && \ + chmod +x /usr/local/bin/gost -COPY entrypoint.sh / +RUN curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | \ + gpg --dearmor -o /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg && \ + ARCH="$(dpkg --print-architecture)" && \ + echo "deb [arch=${ARCH} signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ ${DEBIAN_RELEASE} main" \ + > /etc/apt/sources.list.d/cloudflare-client.list + +RUN apt-get update && \ + apt-get install -y --no-install-recommends cloudflare-warp && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* -RUN apt remove -y curl && \ - apt clean -y && \ - rm -rf /var/lib/apt/lists/* && \ - chmod +x /entrypoint.sh +COPY entrypoint.sh / +RUN chmod +x /entrypoint.sh EXPOSE 40000/tcp -ENTRYPOINT [ "/entrypoint.sh" ] + +HEALTHCHECK --interval=15s --timeout=10s --start-period=30s --retries=2 \ + CMD curl -fsSL https://www.cloudflare.com/cdn-cgi/trace \ + -x socks5h://127.0.0.1:40000 >/dev/null || exit 1 + +ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/README.md b/README.md index c411c07..980c9c7 100644 --- a/README.md +++ b/README.md @@ -7,18 +7,19 @@ Docker image to run Cloudflare Warp in proxy mode. Image is rebuilt and updated ## Usage ### docker hub image -``` +```bash docker run -d -p 40000:40000 --restart unless-stopped seiry/cloudflare-warp-proxy + ``` ### or github package image -``` + +```bash docker run -d -p 40000:40000 --restart unless-stopped ghcr.io/seiry/cloudflare-warp-proxy + ``` -A **SOCKS5** proxy will be listening at port 40000 (this is WARP's proxy mode, -exposed via `socat`). The same port also accepts HTTP `CONNECT`, so you can point -either a `socks5h://` / `socks5://` or an `http://` client at it — see [test](#test). +A **multiplexed** proxy will be listening at port 40000. Under the hood, this uses `gost` to forward traffic to WARP's internal proxy. This setup allows the same port to automatically accept **both SOCKS5 and HTTP** proxy connections seamlessly. You can point a `socks5h://`, `socks5://`, or an `http://` client directly at it — see [test](#test). ### docker-compose @@ -100,14 +101,15 @@ using a **service token**. The same file pins the partner's endpoint overrides `service_mode`/`proxy_port` put the client into proxy mode locally. The Cloudflare One Client gives precedence to local settings, so this overrides the org's device profile — -keep `proxy_port` at `40001` to match the `socat` forward (`40000 → 40001`) in the container. +keep `proxy_port` at `40001` to match the `gost` forward (`40000 → 40001`) in the container. ### 3. run with the file mounted -``` +```bash docker run -d -p 40000:40000 --restart unless-stopped \ -v ./mdm.xml:/var/lib/cloudflare-warp/mdm.xml:ro \ seiry/cloudflare-warp-proxy + ``` A service-token enrolled device shows up under **My Team → Devices** with email @@ -140,7 +142,7 @@ curl https://www.cloudflare.com/cdn-cgi/trace -x http://127.1:40000 # http mode ... sni=plaintext warp=on -# 👆wrap on! +# 👆warp on! gateway=off ... ``` diff --git a/entrypoint.sh b/entrypoint.sh index 589c986..b50cb79 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -16,7 +16,7 @@ else # Consumer deployment: create a free registration. Newer clients auto-register on # startup and the registration persists across restarts, so "registration new" can # fail with "Old registration is still around" — treat that as success instead of - # looping forever (otherwise socat below never starts). + # looping forever (otherwise gost below never starts). while true; do if out="$(warp-cli --accept-tos registration new 2>&1)"; then break @@ -31,19 +31,44 @@ fi # the mode is dictated by the org device profile and these calls return "Invalid setting # for this account type" — in that case set the profile's service mode to "Proxy mode" # (port 40001) in the Zero Trust dashboard, and ignore the errors below. -warp-cli --accept-tos mode proxy 2>/dev/null || >&2 echo "mode set by org profile, skipping local mode/port" +warp-cli --accept-tos mode proxy 2>/dev/null || true warp-cli --accept-tos proxy port 40001 2>/dev/null || true # docker-compose's `- LICENSE='...'` keeps the surrounding quotes as part of the value, # which makes warp-cli reject the key — strip a single pair of leading/trailing quotes. LICENSE="${LICENSE#[\"\']}" LICENSE="${LICENSE%[\"\']}" + if [ "$LICENSE" != "" ]; then - warp-cli --accept-tos registration license "$LICENSE" || true + warp-cli --accept-tos registration license "$LICENSE" >/dev/null 2>&1 || true fi -warp-cli --accept-tos connect -socat TCP-LISTEN:40000,fork TCP:localhost:40001 # socat is used to redirect traffic from 40000 to 40001 +# Connect WARP silently +warp-cli --accept-tos connect >/dev/null 2>&1 + + +# ========================================== +# GOST MEMORY Limit +# ========================================== +export GOMEMLIMIT=50MiB +export GOGC=30 + +# Gost acts as a multiplexed proxy listening on 40000. Using the 'auto://' scheme, +# it automatically detects and supports incoming traffic for: +# - HTTP proxy +# - HTTPS proxy (CONNECT) +# - SOCKS4 +# - SOCKS5 +# and forwards that traffic directly to Cloudflare Warp's SOCKS5 on 40001. +gost -C '{"log": {"level": "warn"}}' -L "auto://:40000" -F "socks5://127.0.0.1:40001" & ) & -exec warp-svc +# ========================================== +# WARP-SVC WITH LOG FILTER +# ========================================== +warp-svc 2>&1 | grep --line-buffered -vE 'Initializing dbus connection|org\.freedesktop\.DBus\.Error\.FileNotFound' & +WARP_PID=$! + +# Ensure graceful shutdown if container is stopped +trap 'kill -TERM $WARP_PID 2>/dev/null' TERM INT +wait $WARP_PID \ No newline at end of file