SOCKS5-прокси через AmneziaWG на Synology

Контейнер поднимает VPN-туннель AmneziaWG (userspace, с обфускацией под DPI) и отдаёт наружу SOCKS5-прокси. Весь трафик, идущий через прокси, выходит в интернет через VPN. Встроенный watchdog следит за туннелем и переподключается сам, если связь падает.

Проект — это папка /volume1/docker/socks с четырьмя файлами: compose.yaml, Dockerfile, entrypoint.sh и awg0.conf (экспорт из клиента Amnezia).

compose.yaml

services: socks: build: . image: socks:latest container_name: socks restart: unless-stopped # privileged нужен, чтобы awg-quick мог записать sysctl # (иначе /proc/sys только для чтения и туннель откатывается) privileged: true # AmneziaWG (userspace) требует tun-устройства devices: - /dev/net/tun:/dev/net/tun environment: SOCKS_USER: proxy SOCKS_PASS: *ваш пароль* SOCKS_PORT: "1080" CHECK_INTERVAL: "30" FAIL_THRESHOLD: "2" ports: - "1080:1080" volumes: - ./awg0.conf:/etc/amnezia/amneziawg/awg0.conf:ro healthcheck: test: ["CMD-SHELL", "wget -qO- -T 8 https://1.1.1.1/cdn-cgi/trace | grep -q '^ip=' || exit 1"] interval: 30s timeout: 12s retries: 3 start_period: 25s

Dockerfile

# ---- Сборка amneziawg-go (userspace-реализация WireGuard с обфускацией) ---- FROM golang:alpine AS gobuild RUN apk add --no-cache git make gcc musl-dev linux-headers RUN git clone --depth=1 https://github.com/amnezia-vpn/amneziawg-go /src/awg-go WORKDIR /src/awg-go RUN go build -o /out/amneziawg-go . # ---- Сборка amneziawg-tools (awg, awg-quick) и microsocks ---- FROM alpine:3.20 AS toolsbuild RUN apk add --no-cache git make gcc musl-dev linux-headers bash RUN git clone --depth=1 https://github.com/amnezia-vpn/amneziawg-tools /src/awg-tools WORKDIR /src/awg-tools/src RUN make && make install RUN git clone --depth=1 https://github.com/rofl0r/microsocks /src/microsocks WORKDIR /src/microsocks RUN make # ---- Финальный образ ---- FROM alpine:3.20 RUN apk add --no-cache bash iproute2 ca-certificates openresolv COPY --from=gobuild /out/amneziawg-go /usr/bin/amneziawg-go COPY --from=toolsbuild /usr/bin/awg /usr/bin/awg COPY --from=toolsbuild /usr/bin/awg-quick /usr/bin/awg-quick COPY --from=toolsbuild /src/microsocks/microsocks /usr/bin/microsocks COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"]

entrypoint.sh

#!/bin/bash set -e mkdir -p /dev/net if [ ! -c /dev/net/tun ]; then mknod /dev/net/tun c 10 200 || true fi export WG_QUICK_USERSPACE_IMPLEMENTATION=amneziawg-go CONF_SRC=/etc/amnezia/amneziawg/awg0.conf CONF=/run/awg/awg0.conf mkdir -p /run/awg # --- параметры watchdog (можно переопределить через environment) --- CHECK_INTERVAL="${CHECK_INTERVAL:-30}" # период проверки, сек FAIL_THRESHOLD="${FAIL_THRESHOLD:-2}" # провалов подряд до реконнекта HEARTBEAT_EVERY="${HEARTBEAT_EVERY:-10}" # каждые N успешных проверок писать OK PROBE_URL="${PROBE_URL:-https://1.1.1.1/cdn-cgi/trace}" PROBE_TIMEOUT="${PROBE_TIMEOUT:-8}" SOCKS_USER="${SOCKS_USER:-proxy}" SOCKS_PASS="${SOCKS_PASS:-changeme}" SOCKS_PORT="${SOCKS_PORT:-1080}" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; } # Внутренняя копия конфига (исходный read-only mount не трогаем): # 1) Table/MTU форсим свои (Table=off + ручные маршруты, MTU=1360) # 2) вырезаем ТОЛЬКО ПУСТЫЕ доп. параметры (I1..I5, J1..J3, Itime); # непустые (реальная обфускация) сохраняем. grep -vE '^[[:space:]]*(Table|MTU)[[:space:]]*=' "$CONF_SRC" \ | grep -vE '^[[:space:]]*(I[1-5]|J[1-3]|Itime)[[:space:]]*=[[:space:]]*$' \ | awk '{print} /^\[Interface\]/ && !x {print "Table = off"; print "MTU = 1360"; x=1}' > "$CONF" EP_HOST=$(awk -F'[:= ]+' '/^Endpoint/{print $2}' "$CONF") # оригинальный LAN-шлюз фиксируем ДО поднятия туннеля ORIG_GW=$(ip route show default | awk '{print $3; exit}') ORIG_DEV=$(ip route show default | awk '{print $5; exit}') log "старт: default gw=$ORIG_GW dev=$ORIG_DEV endpoint=$EP_HOST" tunnel_up() { awg-quick up "$CONF" EP_IP=$(getent ahostsv4 "$EP_HOST" | awk '{print $1; exit}') if [ -n "$EP_IP" ] && [ -n "$ORIG_GW" ]; then ip route replace "$EP_IP/32" via "$ORIG_GW" dev "$ORIG_DEV" fi ip route replace default dev awg0 log "туннель поднят: endpoint $EP_HOST -> $EP_IP (via $ORIG_GW dev $ORIG_DEV), default->awg0, $(ip link show awg0 | grep -o 'mtu [0-9]*')" } tunnel_down() { awg-quick down "$CONF" >/dev/null 2>&1 || true } reconnect() { log "RECONNECT: связь потеряна, пересоздаю туннель..." tunnel_down sleep 2 tunnel_up || log "RECONNECT: awg-quick up вернул ошибку, повторю на следующем цикле" log "RECONNECT: завершено" } # health-проба: тянем cloudflare trace через туннель, парсим exit IP probe() { local out ip out=$(wget -qO- -T "$PROBE_TIMEOUT" "$PROBE_URL" 2>/dev/null) || return 1 ip=$(printf '%s\n' "$out" | awk -F= '/^ip=/{print $2}') [ -n "$ip" ] || return 1 LAST_EXIT_IP="$ip" return 0 } start_socks() { microsocks -i 0.0.0.0 -p "$SOCKS_PORT" -u "$SOCKS_USER" -P "$SOCKS_PASS" & SOCKS_PID=$! log "SOCKS5 запущен на 0.0.0.0:$SOCKS_PORT (user=$SOCKS_USER, pid=$SOCKS_PID)" } # --- первичный подъём --- tunnel_up awg show || true if probe; then log "проверка связи: OK, выход через $LAST_EXIT_IP" else log "проверка связи: не прошла сразу после старта (watchdog разберётся)" fi # дальше не роняем контейнер на мелких сбоях — реагирует watchdog set +e start_socks log "watchdog активен: интервал ${CHECK_INTERVAL}s, порог реконнекта ${FAIL_THRESHOLD}, heartbeat каждые ${HEARTBEAT_EVERY} проверок" FAILS=0 OKN=0 while true; do sleep "$CHECK_INTERVAL" if ! kill -0 "$SOCKS_PID" 2>/dev/null; then log "microsocks (pid=$SOCKS_PID) не запущен — перезапускаю" start_socks fi if probe; then if [ "$FAILS" -gt 0 ]; then log "связь ВОССТАНОВЛЕНА, выход через $LAST_EXIT_IP" fi FAILS=0 OKN=$((OKN+1)) if [ $((OKN % HEARTBEAT_EVERY)) -eq 0 ]; then log "OK: линк жив, выход через $LAST_EXIT_IP" fi else FAILS=$((FAILS+1)) HS=$(awg show awg0 latest-handshakes 2>/dev/null | awk '{print $2; exit}') NOW=$(date +%s) AGE="n/a" if [ -n "$HS" ]; then AGE=$((NOW-HS)); fi log "FAIL #${FAILS}/${FAIL_THRESHOLD}: проба не прошла (последний хендшейк ${AGE}s назад)" if [ "$FAILS" -ge "$FAIL_THRESHOLD" ]; then reconnect FAILS=0 fi fi done

awg0.conf

Это экспорт конфига из приложения Amnezia (тип соединения — AmneziaWG). Впиши сюда свои ключи. Параметры Table и MTU можно не указывать — их проставляет entrypoint.sh.

[Interface] Address = 10.8.1.3/32 DNS = 1.1.1.1, 1.0.0.1 PrivateKey = *ваш приватный ключ* Jc = 5 Jmin = 10 Jmax = 50 S1 = 32 S2 = 139 S3 = 52 S4 = 1 H1 = 322017579-1492438178 H2 = 2127325942-2133312985 H3 = 2140135011-2142808705 H4 = 2146777525-2147460943 I1 = <r 2><b 0x858000010001000000000669636c6f756403636f6d0000010001c00c000100010000105a00044d583737> I2 = I3 = I4 = I5 = [Peer] PublicKey = *публичный ключ сервера* PresharedKey = *ваш preshared ключ* AllowedIPs = 0.0.0.0/0, ::/0 Endpoint = vpn.example.com:48105 PersistentKeepalive = 25

Запуск

  1. Создай папку /volume1/docker/socks и положи в неё все четыре файла.
  2. В compose.yaml задай свои SOCKS_USER / SOCKS_PASS и при желании порт.
  3. В Container Manager создай проект из этой папки (или командой docker compose up -d --build).
  4. Прокси доступен на IP_синолоджи:1080 (SOCKS5, с логином/паролем).

Важно: после правки entrypoint.sh или Dockerfile образ нужно пересобрать — кнопка «Создать» в вебе берёт старый кэш. Удали образ socks:latest и подними проект заново, тогда изменения применятся.

Проверка, что трафик идёт через VPN (должен вернуться IP VPN-сервера, а не твоего провайдера):

curl --socks5 proxy:*ваш пароль*@IP_синолоджи:1080 https://1.1.1.1/cdn-cgi/trace