From dd4d306a4ed60632f7803a8a2c227a57b9f3544b Mon Sep 17 00:00:00 2001 From: dev Date: Sat, 12 Sep 2026 17:40:42 +0300 Subject: [PATCH] feat: cloudflared container with optional WireGuard egress - Dockerfile.cloudflared builds cloudflared (official static binary) on Alpine with wireguard-tools --- .gitignore | 1 + Dockerfile.cloudflared | 12 ++++++++++++ README.md | 21 ++++++++++++++++++++- docker-compose.yml | 21 +++++++++++++-------- start-cloudflared.sh | 20 ++++++++++++++++++++ 5 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 Dockerfile.cloudflared create mode 100755 start-cloudflared.sh diff --git a/.gitignore b/.gitignore index c6450e6..314667d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.key *.crt certs/ +wg/ # --- Run-time data / uploads --- uploads/ diff --git a/Dockerfile.cloudflared b/Dockerfile.cloudflared new file mode 100644 index 0000000..bf25e02 --- /dev/null +++ b/Dockerfile.cloudflared @@ -0,0 +1,12 @@ +# Сборка контейнера cloudflared с опциональным WireGuard. +# Официальный образ cloudflare/cloudflared — distroless (без оболочки/apk), поэтому: +# 1) берём из него статичный бинарник cloudflared; +# 2) саму сборку делаем на Alpine, где ставим wireguard-tools. +# Если в wg/wg0.conf нет конфига — VPN не поднимается, туннель запускается как обычно. +FROM cloudflare/cloudflared:latest AS cf + +FROM alpine:3.20 +RUN apk add --no-cache wireguard-tools iproute2 +COPY --from=cf /usr/local/bin/cloudflared /usr/bin/cloudflared +COPY start-cloudflared.sh /start-cloudflared.sh +ENTRYPOINT ["/bin/sh", "/start-cloudflared.sh"] diff --git a/README.md b/README.md index 9cf4ce4..8599d17 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,26 @@ docker exec whatido-tailscale-1 tailscale funnel status docker compose logs cloudflared | grep trycloud ``` -Адрес меняется при каждом перезапуске контейнера `cloudflared`. Для постоянного адреса используйте именованный туннель (замените `command` сервиса `cloudflared` в `docker-compose.yml` на `["tunnel", "run", "--token", "${CLOUDFLARE_TUNNEL_TOKEN}"]`, токен — из Cloudflare Zero Trust **Networks → Tunnels**) или схему через Tailscale выше. +### Опциональный WireGuard для эгресса Cloudflare + +`cloudflared` собирается из `Dockerfile.cloudflared`, в который добавлена поддержка **опционального WireGuard** — полезно, когда Cloudflare не работает с прямого IP хоста, и исход туннеля нужно пустить через VPN. + +- Положите рабочий конфиг провайдера в `wg/wg0.conf` (папка `wg/` монтируется в контейнер как `/etc/wireguard`). Пример-заглушка уже лежит в `wg/wg0.conf` — замените значения на свои. +- Если `wg/wg0.conf` есть → контейнер **сначала поднимает WireGuard, дожидается handshake и только потом запускает туннель** (весь эгресс Cloudflare идёт через VPN). +- Если `wg0.conf` нет или папка пустая → VPN не включается, туннель стартует сразу, как в базовой схеме. + +Проверка: + +```bash +docker compose logs -f cloudflared # в идеале сначала "WireGuard is up", затем URL +docker compose exec cloudflared wg show # есть handshake — VPN поднят +``` + +Примечания: +- `wg/wg0.conf` содержит приватный ключ, поэтому папка `wg/` добавлена в `.gitignore` (в репозиторий не попадёт). +- При полном туннеле (`AllowedIPs = 0.0.0.0/0`) существующий маршрут docker-сети сохраняется, поэтому `cloudflared` по-прежнему достаёт `app` локально, а наружу уходит через wg0. Если вдруг `app` станет недоступен из-за VPN — переключите `CLOUDFLARE_TUNNEL_URL` в `.env` на docker-шлюз (`http://:3003`). + +Адрес Quick Tunnel меняется при каждом перезапуске контейнера `cloudflared`. Для постоянного адреса используйте именованный туннель (для этого в `start-cloudflared.sh` замените последнюю команду на `exec cloudflared --no-autoupdate tunnel run --token "$CLOUDFLARE_TUNNEL_TOKEN"` и задайте токен; токен — из Cloudflare Zero Trust **Networks → Tunnels**) или схему через Tailscale выше. ## Структура данных diff --git a/docker-compose.yml b/docker-compose.yml index d85af44..5367315 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -68,22 +68,27 @@ services: # Публикация наружу через Cloudflare Tunnel (cloudflared), Quick Tunnel: # случайный публичный URL *.trycloudflare.com, который печатается в логах: # docker compose logs -f cloudflared - # ВАЖНО: официальный образ cloudflare/cloudflared НЕ содержит /bin/sh, - # поэтому логика передаётся аргументами в command (entrypoint образа = cloudflared), - # а не скриптом. - # Для именованного туннеля на свой домен замените command ниже на: - # command: ["tunnel", "run", "--token", "${CLOUDFLARE_TUNNEL_TOKEN}"] - # (токен получается в Cloudflare Zero Trust -> Networks -> Tunnels). + # Сборка из Dockerfile.cloudflared добавляет опциональный WireGuard: + # если в wg/wg0.conf лежит конфиг — контейнер сначала поднимает VPN и + # только потом запускает туннель (исход Cloudflare через VPN). Без конфига + # туннель стартует сразу, как в базовой схеме. cloudflared: - image: cloudflare/cloudflared:latest + build: + context: . + dockerfile: Dockerfile.cloudflared restart: unless-stopped + cap_add: + - NET_ADMIN + volumes: + - ./wg:/etc/wireguard:ro environment: TZ: Europe/Moscow - command: ["tunnel", "--url", "${CLOUDFLARE_TUNNEL_URL:-http://app:3003}"] + CLOUDFLARE_TUNNEL_URL: ${CLOUDFLARE_TUNNEL_URL:-http://app:3003} depends_on: - app + text-corrector: image: ghcr.io/ggml-org/llama.cpp:server container_name: text-corrector diff --git a/start-cloudflared.sh b/start-cloudflared.sh new file mode 100755 index 0000000..236c6ee --- /dev/null +++ b/start-cloudflared.sh @@ -0,0 +1,20 @@ +#!/bin/sh +set -e + +# Опциональный WireGuard: если в wg/wg0.conf лежит конфиг — поднимаем VPN +# и только после установки handshake запускаем туннель. Без конфига — +# cloudflared стартует сразу, как в базовой схеме Quick Tunnel. +if [ -f /etc/wireguard/wg0.conf ]; then + echo "WireGuard config found, bringing up wg0..." + wg-quick up wg0 + echo "Waiting for WireGuard handshake..." + # Формат `wg show wg0 latest-handshakes`: " "; 0 = handshake ещё не было. + until wg show wg0 latest-handshakes | awk '{ if ($2 != 0) found=1 } END { exit !found }'; do + sleep 2 + done + echo "WireGuard is up, starting Cloudflare tunnel through VPN..." +else + echo "No WireGuard config, starting Cloudflare tunnel directly..." +fi + +exec cloudflared --no-autoupdate tunnel --url "$CLOUDFLARE_TUNNEL_URL"