From 9d169f7e641aea24086890b5971f6f3be803e253 Mon Sep 17 00:00:00 2001 From: Seiry Yu <4397354+seiry@users.noreply.github.com> Date: Fri, 29 May 2026 17:18:44 +1000 Subject: [PATCH 1/6] add .gitignore to exclude mdm.xml and docker-compose.local.yml --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a7aa22 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +mdm.xml +docker-compose.local.yml From aada900afaadb736b961d788fe967d786c42ffa0 Mon Sep 17 00:00:00 2001 From: Seiry Yu <4397354+seiry@users.noreply.github.com> Date: Fri, 29 May 2026 17:40:17 +1000 Subject: [PATCH 2/6] feat: add support for endpoint overrides and generate MDM config in entrypoint script --- README.md | 43 +++++++++++++++++++++++++++++++++++++++---- docker-compose.yml | 6 ++++++ entrypoint.sh | 37 ++++++++++++++++++++++++++++++++++++- 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 641caf4..0fe667a 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,9 @@ docker run -d -p 40000:40000 --restart unless-stopped seiry/cloudflare-warp-prox docker run -d -p 40000:40000 --restart unless-stopped ghcr.io/seiry/cloudflare-warp-proxy ``` -SOCKS5 proxy server will be listening at port 40000. +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). ### docker-compose @@ -32,6 +34,9 @@ services: environment: # use your own wrap+ key or zero trust key. - LICENSE='' + # endpoint overrides, see "override endpoints" below + # - OVERRIDE_API_ENDPOINT=1.2.3.4 + # - OVERRIDE_WARP_ENDPOINT=203.0.113.0:500 logging: driver: json-file options: @@ -39,18 +44,48 @@ services: ``` +## override endpoints + +You can override the IPs the WARP client talks to by setting the environment variables +below. On startup the entrypoint writes any that are set into `/var/lib/cloudflare-warp/mdm.xml` +as the matching [MDM deployment parameters](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/). +This is mainly for Cloudflare China local-network partners or third-party network partners. + +| env var | MDM parameter | value | notes | +| --- | --- | --- | --- | +| `OVERRIDE_API_ENDPOINT` | [`override_api_endpoint`](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#override_api_endpoint) | IP, e.g. `1.2.3.4` | IP used to reach the client orchestration API | +| `OVERRIDE_WARP_ENDPOINT` | [`override_warp_endpoint`](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#override_warp_endpoint) | `IP:UDP_PORT`, e.g. `203.0.113.0:500` | IP and UDP port used to send traffic to Cloudflare's edge | + +``` +docker run -d -p 40000:40000 --restart unless-stopped \ + -e OVERRIDE_API_ENDPOINT=1.2.3.4 \ + -e OVERRIDE_WARP_ENDPOINT=203.0.113.0:500 \ + seiry/cloudflare-warp-proxy +``` + +> Remember to allow the new IP(s) through your firewall. + +If you need other MDM parameters (e.g. `organization`, service tokens), mount your own +full config instead — a mounted `mdm.xml` takes precedence over the `OVERRIDE_*` variables: + +``` +docker run -d -p 40000:40000 --restart unless-stopped \ + -v ./mdm.xml:/var/lib/cloudflare-warp/mdm.xml:ro \ + seiry/cloudflare-warp-proxy +``` + ## test ```bash -curl https://www.cloudflare.com/cdn-cgi/trace/ -x socks5h://127.1:40000 # remote dns mode +curl https://www.cloudflare.com/cdn-cgi/trace -x socks5h://127.1:40000 # remote dns mode # or -curl https://www.cloudflare.com/cdn-cgi/trace/ -x socks5://127.1:40000 # local dns mode +curl https://www.cloudflare.com/cdn-cgi/trace -x socks5://127.1:40000 # local dns mode # or -curl https://www.cloudflare.com/cdn-cgi/trace/ -x http://127.1:40000 # http mode +curl https://www.cloudflare.com/cdn-cgi/trace -x http://127.1:40000 # http mode ``` diff --git a/docker-compose.yml b/docker-compose.yml index 08ad485..30a0b51 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,12 @@ services: restart: unless-stopped environment: - LICENSE='' # use your own key, for example zero trust or warp+ + # endpoint overrides (e.g. China network partner). see README "override endpoints" + # - OVERRIDE_API_ENDPOINT=1.2.3.4 # orchestration API IP + # - OVERRIDE_WARP_ENDPOINT=203.0.113.0:500 # WARP edge IP:UDP port + # advanced: mount a full MDM config instead of using OVERRIDE_API_ENDPOINT + # volumes: + # - ./mdm.xml:/var/lib/cloudflare-warp/mdm.xml:ro logging: driver: json-file options: diff --git a/entrypoint.sh b/entrypoint.sh index 0c0383d..928615e 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,7 +1,42 @@ #!/bin/bash +MDM_FILE=/var/lib/cloudflare-warp/mdm.xml + +# Emit a / pair only when the value is non-empty. +emit_key() { # $1=key $2=value + [ -n "$2" ] && printf '\t%s\n\t%s\n' "$1" "$2" + return 0 +} + +# If a full mdm.xml is mounted, respect it as-is. Otherwise, generate one from the +# OVERRIDE_* environment variables when any is set. See: +# https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/ +if [ -f "$MDM_FILE" ]; then + >&2 echo "Using existing MDM config at $MDM_FILE" +elif [ -n "$OVERRIDE_API_ENDPOINT" ] || [ -n "$OVERRIDE_WARP_ENDPOINT" ]; then + mkdir -p "$(dirname "$MDM_FILE")" + { + echo "" + emit_key override_api_endpoint "$OVERRIDE_API_ENDPOINT" + emit_key override_warp_endpoint "$OVERRIDE_WARP_ENDPOINT" + echo "" + } > "$MDM_FILE" + >&2 echo "Generated MDM config at $MDM_FILE:" + >&2 cat "$MDM_FILE" +fi + ( -while ! warp-cli --accept-tos registration new; do +# Wait until warp-svc is online, then make sure a registration exists. Newer WARP +# 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). +while true; do + if out="$(warp-cli --accept-tos registration new 2>&1)"; then + break + fi + if echo "$out" | grep -q "Old registration is still around"; then + break + fi sleep 1 >&2 echo "Awaiting warp-svc become online..." done From 528bcbcb389ecfea13f9154389e87ec8794c693b Mon Sep 17 00:00:00 2001 From: Seiry Yu <4397354+seiry@users.noreply.github.com> Date: Fri, 29 May 2026 21:49:30 +1000 Subject: [PATCH 3/6] feat: add mdm.example.xml for endpoint configuration --- mdm.example.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 mdm.example.xml diff --git a/mdm.example.xml b/mdm.example.xml new file mode 100644 index 0000000..576142f --- /dev/null +++ b/mdm.example.xml @@ -0,0 +1,16 @@ + + override_api_endpoint + 1.1.1.1 + override_doh_endpoint + 1.1.1.1 + override_warp_endpoint + 1.1.1.1:443 + organization + your_organization + auth_client_id + your_client_id.access + auth_client_secret + your_client_secret + onboarding + + From 0f24cc1f9c978f0d2085e0d8c1dd1c636b02e903 Mon Sep 17 00:00:00 2001 From: Seiry Yu <4397354+seiry@users.noreply.github.com> Date: Fri, 29 May 2026 22:11:19 +1000 Subject: [PATCH 4/6] refactor: reorganize mdm.example.xml for improved readability --- mdm.example.xml | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/mdm.example.xml b/mdm.example.xml index 576142f..0a70b61 100644 --- a/mdm.example.xml +++ b/mdm.example.xml @@ -1,16 +1,29 @@ - override_api_endpoint - 1.1.1.1 - override_doh_endpoint - 1.1.1.1 - override_warp_endpoint - 1.1.1.1:443 - organization - your_organization - auth_client_id - your_client_id.access - auth_client_secret - your_client_secret - onboarding - + + override_api_endpoint + 1.1.1.1 + override_doh_endpoint + 1.1.1.1 + override_warp_endpoint + 1.1.1.1:443 + + + organization + your_organization + auth_client_id + your_client_id.access + auth_client_secret + your_client_secret + + + warp_tunnel_protocol + masque + + + service_mode + proxy + proxy_port + 40001 + onboarding + From c26272d868669845f80ab75048994a8fbdcdad70 Mon Sep 17 00:00:00 2001 From: Seiry Yu <4397354+seiry@users.noreply.github.com> Date: Fri, 29 May 2026 22:11:48 +1000 Subject: [PATCH 5/6] feat: update Dockerfile and entrypoint for managed deployment support; enhance README and docker-compose for Zero Trust integration --- Dockerfile | 3 +- README.md | 94 +++++++++++++++++++++++++++++++++++----------- docker-compose.yml | 8 ++-- entrypoint.sh | 71 +++++++++++++++------------------- 4 files changed, 107 insertions(+), 69 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7333a04..3dfedfe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,6 @@ ARG DEBIAN_RELEASE=bullseye ARG LICENSE='' FROM docker.io/debian:$DEBIAN_RELEASE-slim ARG DEBIAN_RELEASE -COPY entrypoint.sh / ENV DEBIAN_FRONTEND=noninteractive ENV LICENSE=${LICENSE} @@ -16,6 +15,8 @@ RUN curl https://pkg.cloudflareclient.com/pubkey.gpg | gpg --yes --dearmor --out apt update && \ apt install cloudflare-warp -y --no-install-recommends +COPY entrypoint.sh / + RUN apt remove -y curl && \ apt clean -y && \ rm -rf /var/lib/apt/lists/* && \ diff --git a/README.md b/README.md index 0fe667a..1798380 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,9 @@ services: environment: # use your own wrap+ key or zero trust key. - LICENSE='' - # endpoint overrides, see "override endpoints" below - # - OVERRIDE_API_ENDPOINT=1.2.3.4 - # - OVERRIDE_WARP_ENDPOINT=203.0.113.0:500 + # to enroll into a Zero Trust org, mount a full MDM config — see "managed deployment" below + # volumes: + # - ./mdm.xml:/var/lib/cloudflare-warp/mdm.xml:ro logging: driver: json-file options: @@ -44,35 +44,82 @@ services: ``` -## override endpoints +## managed deployment (Zero Trust) + +To join a Cloudflare Zero Trust organization — e.g. to route through a China local-network +partner or third-party network partner — mount a full [MDM config](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/) +at `/var/lib/cloudflare-warp/mdm.xml`. `warp-svc` reads it on startup and enrolls the device +using a **service token**. The same file pins the partner's endpoint overrides +(`override_warp_endpoint`, etc.) and puts the client into proxy mode. + + +### 1. create a service token with enrollment permission + +- **Access controls → Service credentials → Service Tokens → Create Service Token** — copy the + Client ID (ends in `.access`) and Client Secret (shown only once). +- **Team & Resources → Devices → Management → Device enrollment permissions → Manage → Policies + → Create policy**, set **Action = `Service Auth`** (not `Allow` — `Allow` does not work for + service tokens), Selector = your token, then add the policy to the enrollment permissions and + **Save**. + + doc: https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/#check-for-service-token + +### 2. write `mdm.xml` + +```xml + + + override_api_endpoint + 1.1.1.1 + override_doh_endpoint + 1.1.1.1 + override_warp_endpoint + 1.1.1.1:443 + + + organization + your-team-name + auth_client_id + xxxxxxxx.access + auth_client_secret + xxxxxxxxxxxxxxxx + + + warp_tunnel_protocol + masque + + + service_mode + proxy + proxy_port + 40001 + onboarding + + +``` -You can override the IPs the WARP client talks to by setting the environment variables -below. On startup the entrypoint writes any that are set into `/var/lib/cloudflare-warp/mdm.xml` -as the matching [MDM deployment parameters](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/). -This is mainly for Cloudflare China local-network partners or third-party network partners. +`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. -| env var | MDM parameter | value | notes | -| --- | --- | --- | --- | -| `OVERRIDE_API_ENDPOINT` | [`override_api_endpoint`](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#override_api_endpoint) | IP, e.g. `1.2.3.4` | IP used to reach the client orchestration API | -| `OVERRIDE_WARP_ENDPOINT` | [`override_warp_endpoint`](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#override_warp_endpoint) | `IP:UDP_PORT`, e.g. `203.0.113.0:500` | IP and UDP port used to send traffic to Cloudflare's edge | +### 3. run with the file mounted ``` docker run -d -p 40000:40000 --restart unless-stopped \ - -e OVERRIDE_API_ENDPOINT=1.2.3.4 \ - -e OVERRIDE_WARP_ENDPOINT=203.0.113.0:500 \ + -v ./mdm.xml:/var/lib/cloudflare-warp/mdm.xml:ro \ seiry/cloudflare-warp-proxy ``` -> Remember to allow the new IP(s) through your firewall. +A service-token enrolled device shows up under **My Team → Devices** with email +`non_identity@.cloudflareaccess.com`. -If you need other MDM parameters (e.g. `organization`, service tokens), mount your own -full config instead — a mounted `mdm.xml` takes precedence over the `OVERRIDE_*` variables: +### 4. (optional) set proxy mode in the dashboard instead + +Instead of `service_mode`/`proxy_port` in `mdm.xml` above, you can manage the mode centrally: +in **Settings → WARP Client → Device settings**, use a profile that targets this device (match +e.g. its `non_identity@…` email so you don't change other users) and set **Service mode = Proxy +mode, port `40001`**. -``` -docker run -d -p 40000:40000 --restart unless-stopped \ - -v ./mdm.xml:/var/lib/cloudflare-warp/mdm.xml:ro \ - seiry/cloudflare-warp-proxy -``` ## test @@ -103,3 +150,6 @@ gateway=off * new version of cloudflare warp (rust version), now only allow using `MASQUE` protocol in proxy mode. With this error message if you try to use `WireGuard` > `Connection error error=InvalidKey("Proxy mode only supports MASQUE")` + + per Cloudflare's [Set up local proxy mode](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#set-up-local-proxy-mode) docs: + > Ensure the Device tunnel protocol is set to MASQUE. diff --git a/docker-compose.yml b/docker-compose.yml index 30a0b51..3625b23 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,11 +7,9 @@ services: - 40000:40000 restart: unless-stopped environment: - - LICENSE='' # use your own key, for example zero trust or warp+ - # endpoint overrides (e.g. China network partner). see README "override endpoints" - # - OVERRIDE_API_ENDPOINT=1.2.3.4 # orchestration API IP - # - OVERRIDE_WARP_ENDPOINT=203.0.113.0:500 # WARP edge IP:UDP port - # advanced: mount a full MDM config instead of using OVERRIDE_API_ENDPOINT + - LICENSE='' # use your own key, for warp+ + # to enroll into a Zero Trust org, mount a full MDM + # config. see README "managed deployment (Zero Trust)" # volumes: # - ./mdm.xml:/var/lib/cloudflare-warp/mdm.xml:ro logging: diff --git a/entrypoint.sh b/entrypoint.sh index 928615e..931569f 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -2,49 +2,40 @@ MDM_FILE=/var/lib/cloudflare-warp/mdm.xml -# Emit a / pair only when the value is non-empty. -emit_key() { # $1=key $2=value - [ -n "$2" ] && printf '\t%s\n\t%s\n' "$1" "$2" - return 0 -} - -# If a full mdm.xml is mounted, respect it as-is. Otherwise, generate one from the -# OVERRIDE_* environment variables when any is set. See: -# https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/ -if [ -f "$MDM_FILE" ]; then - >&2 echo "Using existing MDM config at $MDM_FILE" -elif [ -n "$OVERRIDE_API_ENDPOINT" ] || [ -n "$OVERRIDE_WARP_ENDPOINT" ]; then - mkdir -p "$(dirname "$MDM_FILE")" - { - echo "" - emit_key override_api_endpoint "$OVERRIDE_API_ENDPOINT" - emit_key override_warp_endpoint "$OVERRIDE_WARP_ENDPOINT" - echo "" - } > "$MDM_FILE" - >&2 echo "Generated MDM config at $MDM_FILE:" - >&2 cat "$MDM_FILE" +( +if grep -q 'organization' "$MDM_FILE" 2>/dev/null; then + # Managed deployment: a mounted mdm.xml enrolls the device into a Zero Trust + # organization (via a service token). warp-svc auto-enrolls on startup, so we just + # wait for the daemon to come online instead of creating our own registration. + >&2 echo "mdm.xml with organization found — using managed (Zero Trust) enrollment" + until warp-cli --accept-tos status >/dev/null 2>&1; do + sleep 1 + >&2 echo "Awaiting warp-svc become online..." + done +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). + while true; do + if out="$(warp-cli --accept-tos registration new 2>&1)"; then + break + fi + echo "$out" | grep -q "Old registration is still around" && break + sleep 1 + >&2 echo "Awaiting warp-svc become online..." + done fi -( -# Wait until warp-svc is online, then make sure a registration exists. Newer WARP -# 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). -while true; do - if out="$(warp-cli --accept-tos registration new 2>&1)"; then - break - fi - if echo "$out" | grep -q "Old registration is still around"; then - break - fi - sleep 1 - >&2 echo "Awaiting warp-svc become online..." -done -warp-cli --accept-tos mode proxy -warp-cli --accept-tos proxy port 40001 +# Proxy mode. On consumer accounts we set it here. On managed (Team/Zero Trust) accounts +# 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 proxy port 40001 2>/dev/null || true if [ "$LICENSE" != "" ]; then - warp-cli --accept-tos registration license "$LICENSE" + warp-cli --accept-tos registration license "$LICENSE" 2>/dev/null || true fi warp-cli --accept-tos connect @@ -52,5 +43,3 @@ socat TCP-LISTEN:40000,fork TCP:localhost:40001 # socat is used to redirect tra ) & exec warp-svc - - From cf962b05ae5e84fdf80dd27672ff012de444ebb7 Mon Sep 17 00:00:00 2001 From: Seiry Yu <4397354+seiry@users.noreply.github.com> Date: Fri, 29 May 2026 22:20:57 +1000 Subject: [PATCH 6/6] fix: remove unnecessary LICENSE variable initialization and adjust entrypoint script to handle quotes --- Dockerfile | 2 -- README.md | 2 +- docker-compose.yml | 5 +++-- entrypoint.sh | 6 +++++- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3dfedfe..1818742 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,7 @@ ARG DEBIAN_RELEASE=bullseye -ARG LICENSE='' FROM docker.io/debian:$DEBIAN_RELEASE-slim ARG DEBIAN_RELEASE ENV DEBIAN_FRONTEND=noninteractive -ENV LICENSE=${LICENSE} RUN true && \ apt update && \ diff --git a/README.md b/README.md index 1798380..c411c07 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ services: restart: unless-stopped environment: # use your own wrap+ key or zero trust key. - - LICENSE='' + - LICENSE= # to enroll into a Zero Trust org, mount a full MDM config — see "managed deployment" below # volumes: # - ./mdm.xml:/var/lib/cloudflare-warp/mdm.xml:ro diff --git a/docker-compose.yml b/docker-compose.yml index 3625b23..54988aa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,8 +6,9 @@ services: ports: - 40000:40000 restart: unless-stopped - environment: - - LICENSE='' # use your own key, for warp+ + # environment: + # - LICENSE= # use your own key, for warp+ + # to enroll into a Zero Trust org, mount a full MDM # config. see README "managed deployment (Zero Trust)" # volumes: diff --git a/entrypoint.sh b/entrypoint.sh index 931569f..589c986 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -34,8 +34,12 @@ fi warp-cli --accept-tos mode proxy 2>/dev/null || >&2 echo "mode set by org profile, skipping local mode/port" 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" 2>/dev/null || true + warp-cli --accept-tos registration license "$LICENSE" || true fi warp-cli --accept-tos connect