Socks5 over AmneziaWG
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
Запуск
- Создай папку /volume1/docker/socks и положи в неё все четыре файла.
- В compose.yaml задай свои SOCKS_USER / SOCKS_PASS и при желании порт.
- В Container Manager создай проект из этой папки (или командой docker compose up -d --build).
- Прокси доступен на IP_синолоджи:1080 (SOCKS5, с логином/паролем).
Важно: после правки entrypoint.sh или Dockerfile образ нужно пересобрать — кнопка «Создать» в вебе берёт старый кэш. Удали образ socks:latest и подними проект заново, тогда изменения применятся.
Проверка, что трафик идёт через VPN (должен вернуться IP VPN-сервера, а не твоего провайдера):
curl --socks5 proxy:*ваш пароль*@IP_синолоджи:1080 https://1.1.1.1/cdn-cgi/trace