# AmneziaVPN в Docker: полный гайд **Цель:** поднять в Docker контейнере клиент AmneziaVPN (AmneziaWG 3.1), подключить его по пользовательскому `.conf` от Amnezia Premium и сделать его VPN-шлюзом для домашней сети. **Автор:** Hermes Agent • Среда: LXC-контейнер (Docker 29.4.1, ядро Proxmox 6.17.13-2-pve) --- ## 1. Что потребуется | Компонент | Значение | |-----------|----------| | Хост | Linux с Docker 20.10+ (тестировалось: LXC 29.4.1) | | `/dev/net/tun` | Должен существовать и быть открыт | | `ip_forward` | `1` | | `.conf` файл | Amnezia Premium / AmneziaWG 3.1 конфиг | | Версии | `amneziawg-go` v3.1.20260828, `amneziawg-tools` v3.1.20260812 | Пример `.conf` (Amnezia Premium, конфиг 3.1): ```ini [Interface] Address = 100.85.175.118/32 DNS = 100.64.0.1, 8.8.4.4 PrivateKey = fSdjHMi8tAzL2wbNxsylQW1FAIXdn2/ODPGWr569J/w= Jc = 5 Jmin = 10 Jmax = 80 H1 = 1 H2 = 2 H3 = 3 H4 = 4 HeaderProtectionKey = 8H1SggRhZ8iT8ZYZlS/RPf6SItoxb8OeXX2rNwMgc3M= RekeyAfterTime = 100-120 RekeyTimeout = 3-8 RejectAfterTime = 150-180 KeepaliveTimeout = 7-13 MaxHandshakeAttempts = 15-20 ContentPaddingAddition = 10-100 [Peer] PublicKey = FfgUO6z0plGUntTW3T8fsFsZ250GcQZEKlCgj1fkRFc= PresharedKey = +Is1eWUUQ5UXYD8E7zigG32KaThcGC/YolOVS7LeDGU= AllowedIPs = 0.0.0.0/0, ::/0 Endpoint = 78.17.149.78:8344 PersistentKeepalive = 25-35 ``` > ⚠️ **НЕ** выкладывай `.conf` ни в чат, ни на сторонние сайты. Его приватный ключ — твой доступ. Файл копируется только на сервер. --- ## 2. Подготовка хоста ```bash # Проверить Tun-устройство и форвардинг ls -la /dev/net/tun cat /proc/sys/net/ipv4/ip_forward # должно быть 1 # Включить форвардинг (если 0) echo 1 > /proc/sys/net/ipv4/ip_forward # Проверить Docker docker --version ``` **На LXC (виртуальном контейнере Proxmox)** убедись, что: - `/dev/net/tun` существует (создаётся через настройки LXC) - `net.ipv4.ip_forward=1` установлен (в Proxmox: LXC → Options → Features) --- ## 3. Скачивание репозиториев > ⚠️ Сборку ведём из **локальных** копий репозиториев (COPY в Dockerfile), так как DNS внутри build-контейнера нестабилен. ```bash mkdir -p /root/awg-src cd /root/awg-src # Правильный DNS на хосте LXC echo "nameserver 8.8.8.8" > /etc/resolv.conf git clone --depth 1 --branch v3.1.20260828 https://github.com/amnezia-vpn/amneziawg-go.git git clone --depth 1 --branch v3.1.20260812 https://github.com/amnezia-vpn/amneziawg-tools.git ``` **Проверка тегов** (актуальные на момент сборки): ```bash git ls-remote --tags https://github.com/amnezia-vpn/amneziawg-go.git | tail # v3.1.20260813, v3.1.20260814, v3.1.20260828 git ls-remote --tags https://github.com/amnezia-vpn/amneziawg-tools.git | tail # v3.0.20260805, v3.1.20260812 ``` > Найдите **последний** тег v3.1.* — они выходят часто и содержат исправления DPI-обхода. --- ## 4. Директория сборки ```bash mkdir -p /root/awg-builder cp -r /root/awg-src/amneziawg-go /root/awg-builder/ cp -r /root/awg-src/amneziawg-tools /root/awg-builder/ ``` --- ## 5. Dockerfile **`/root/awg-builder/Dockerfile`** ```dockerfile # syntax=docker/dockerfile:1 FROM golang:alpine AS go-build RUN apk add --no-cache make gcc musl-dev linux-headers COPY amneziawg-go /src/amneziawg-go RUN cd /src/amneziawg-go && make && cp amneziawg-go /go/bin/ COPY amneziawg-tools /src/amneziawg-tools RUN cd /src/amneziawg-tools/src && make && cp wg /go/bin/awg FROM alpine:3.21 RUN apk add --no-cache iptables ip6tables iproute2 bash openresolv COPY --from=go-build /go/bin/amneziawg-go /usr/bin/ COPY --from=go-build /go/bin/awg /usr/bin/ COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] ``` > **Почему `cp wg /go/bin/awg`**: в `amneziawg-tools` бинарь надёжнее назвать `awg`. Это клиентская утилита управления интерфейсом. **Healthcheck** (необязателен): добавить в Dockerfile ```HEALTHCHECK``` — можно пропустить, статус смотрим через `awg show`. --- ## 6. entrypoint.sh Скрипт: поднимает интерфейс, загружает конфиг (с фолбэком на минимальный), настраивает policy routing и NAT. **`/root/awg-builder/entrypoint.sh`** ```bash #!/bin/bash CONFIG_DIR=/config for f in "${CONFIG_DIR}"/*.conf; do [ -f "$f" ] || continue IFACE=$(basename "$f" .conf) echo "Starting AmneziaWG interface: ${IFACE}" modprobe amneziawg 2>/dev/null || true if ! ip link show "${IFACE}" 2>/dev/null | grep -q UP; then ip link add "${IFACE}" type amneziawg 2>/dev/null || { echo "[*] Using userspace amneziawg-go" amneziawg-go "${IFACE}" 2>/dev/null || true sleep 1 } fi TMPF=$(mktemp) grep -v -E '^(Address|DNS)' "$f" > "$TMPF" if ! awg setconf "${IFACE}" "$TMPF" 2>/dev/null; then echo "[!] Trying minimal config" { echo "[Interface]" grep -E '^PrivateKey' "$f" echo "ListenPort = 51820" echo "" echo "[Peer]" grep -E '^(PublicKey|PresharedKey|AllowedIPs|Endpoint|PersistentKeepalive)' "$f" } | awg setconf "${IFACE}" /dev/stdin 2>/dev/null || { echo "[!] FATAL: cannot parse config" rm -f "$TMPF" continue } fi rm -f "$TMPF" IP=$(grep ^Address "$f" | head -1 | cut -d= -f2 | tr -d ' ' | cut -d, -f1) [ -n "$IP" ] && ip addr add "$IP" dev "${IFACE}" 2>/dev/null || true ip link set "${IFACE}" up echo "[*] ${IFACE} UP: ${IP}" done # --- Policy routing: только forwarded LAN-трафик через VPN --- mkdir -p /etc/iproute2 if ! grep -q "awg-vpn" /etc/iproute2/rt_tables 2>/dev/null; then echo "100 awg-vpn" >> /etc/iproute2/rt_tables 2>/dev/null || true fi ip route add 0.0.0.0/0 dev awg0 table 100 2>/dev/null || true # Пометить forwarded LAN-трафик (не на LAN) меткой 100 iptables -t mangle -C PREROUTING -s 192.168.1.0/24 ! -d 192.168.1.0/24 -j MARK --set-mark 100 2>/dev/null || \ iptables -t mangle -A PREROUTING -s 192.168.1.0/24 ! -d 192.168.1.0/24 -j MARK --set-mark 100 2>/dev/null || true # Пакеты с меткой 100 → таблица awg-vpn (через awg0) ip rule add fwmark 100 lookup 100 priority 5000 2>/dev/null || true # NAT для LAN через туннель iptables -t nat -C POSTROUTING -s 192.168.1.0/24 -o awg0 -j MASQUERADE 2>/dev/null || \ iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o awg0 -j MASQUERADE 2>/dev/null || true echo 1 > /proc/sys/net/ipv4/ip_forward 2>/dev/null || true echo "[*] AmneziaWG client running" awg show 2>/dev/null || true sleep infinity ``` > **Как работает policy routing**: > - Локальный (исходящий) трафик **самого LXC** (SSH и т.д.) → идёт напрямую через `eth0` > - Только **forwarded** трафик из LAN → помечается меткой → направляется в таблицу `awg-vpn` → уходит через `awg0` > - Это сохраняет доступ к управлению LXC, не ломая VPN-шлюз --- ## 7. Редактирование сети под свою подсеть В скрипте выше я использовал `192.168.1.0/24` — это **основной LAN сегмент**, который пойдёт через VPN. **Если нужна отдельная VPN-подсеть** (например `192.168.2.0/24`), замени в двух местах: - `iptables -t mangle ... -s 192.168.1.0/24` → `-s 192.168.2.0/24` - `iptables -t nat ... -s 192.168.1.0/24 ... -o awg0` → `-s 192.168.2.0/24` Подробнее про отдельную подсеть см. раздел «Расширенный сценарий» ниже. --- ## 8. Запуск 1. **Создай директорию конфига** и положи туда `.conf`: ```bash mkdir -p /root/awg-config cp /путь/к/de.conf /root/awg-config/awg0.conf # ↑ имя файла = имя интерфейса (awg0) ``` 2. **Собери образ:** ```bash cd /root/awg-builder docker build -t amneziawg-client:latest . ``` 3. **Запусти контейнер:** ```bash docker run -dit \ --name=amneziawg-client \ -v /root/awg-config:/config \ --network=host \ --device=/dev/net/tun:/dev/net/tun \ --cap-add=NET_ADMIN \ --cap-add=SYS_MODULE \ --restart always \ amneziawg-client:latest ``` 4. **Проверь:** ```bash docker ps --filter name=amneziawg-client # Up, restart always docker logs amneziawg-client # интерфейс UP, handshake docker exec amneziawg-client awg show # самый наглядный статус ``` --- ## 9. Проверка работоспособности ```bash # Статус интерфейса и handshake (главное) docker exec amneziawg-client awg show # Должно быть: "latest handshake: N seconds ago" и ненулевые transfer # Внешний IP с самого LXC (через awg0): curl -s ifconfig.me # Это будет IP VPN-провайдера # Статус NAT/маршрутизации docker exec amneziawg-client iptables -t mangle -L PREROUTING -n -v docker exec amneziawg-client iptables -t nat -L POSTROUTING -n -v # Проверка forwarded трафика из LAN: # с устройства в 192.168.1.0/24, у которого шлюз = 192.168.1.147 curl -s ifconfig.me # Тоже IP VPN-провайдера ``` **Полезный признак нормальной работы:** ``` peer: FfgUO6z0plGUntTW3T8fsFsZ250GcQZEKlCgj1fkRFc= latest handshake: 42 seconds ago ← периодически обновляется transfer: 1.24 MiB received, 3.2 MiB sent ← данные идут ``` Если `latest handshake` отсутствует и `received = 0` — пакеты не доходят до сервера. Причины: файрвол роутера блокирует UDP, неверный Endpoint в `.conf`, фильтрация DPI. --- ## 10. Включение шлюза для LAN Когда туннель поднят, чтобы устройства LAN пошли через VPN, на **роутере** меняют шлюз по умолчанию или статический маршрут: **Keenetic (веб-интерфейс):** 1. `Интернет-центр → Маршрутизация → Статические маршруты` 2. Добавить: - Сеть назначения: `0.0.0.0` - Маска: `0.0.0.0` - Шлюз: `192.168.1.147` - (или конкретная VPN-подсеть, если не вся LAN) **На телефоне (временная проверка):** - Wi-Fi → IP-настройки → Статический → Маршрутизатор = `192.168.1.147` --- ## 11. Питфоллы и их решение | Проблема | Причина | Решение | |----------|---------|---------| | `Unknown device type. Falling back to userspace` | Нет модуля ядра amneziawg | Нормально; userspace (amneziawg-go) работает | | `Line unrecognized: S3=145` | Старая версия `awg`, не знает v3.1 параметры | Собрать свежую amneziawg-tools v3.1.x (п.3) | | `Address=/DNS=` line error | `awg setconf` не понимает `wg-quick` директивы | Убрать `Address`/`DNS` из `setconf` (см. entrypoint) | | `/proc/sys/...ip_forward: Read-only` | Ограничение LXC / контейнера | Включить заранее на хосте (п.2) | | Диск забит (`no space left on device`) | Сборки и образы жрут место | `docker system prune`, расширить диск | | `gcc not found` при сборке tools | Нет компилятора | `apk add gcc musl-dev` в Dockerfile | | `linux/types.h` нет | Нужны linux-headers | `apk add linux-headers` | | `cockpit`/быстрые списки GitHub таймаут | DNS внутри build-контейнера | Клонировать на хосте, `COPY` в Dockerfile | --- ## 12. Расширенный сценарий: отдельная VPN-подсеть (вариант B) Нужно, чтобы не вся LAN уходила в VPN, а только отдельные устройства в своей подсети (`192.168.2.0/24`). **На LXC добавить alias-IP шлюза:** ```bash ip addr add 192.168.2.1/32 dev eth0 # persistence: netplan / LXC interface ``` **entrypoint.sh — расширить маршрутизацию:** ```bash # Вторая подсеть → тоже через VPN iptables -t mangle -A PREROUTING -s 192.168.2.0/24 ! -d 192.168.2.0/24 -j MARK --set-mark 100 iptables -t nat -A POSTROUTING -s 192.168.2.0/24 -o awg0 -j MASQUERADE ``` **DHCP сервер для 192.168.2.0/24** (dnsmasq на LXC или Docker): ``` interface=eth0 dhcp-range=192.168.2.100,192.168.2.200,255.255.255.0,24h dhcp-option=3,192.168.2.1 dhcp-option=6,100.64.0.1,8.8.8.8 ``` **Keenetic — статический маршрут:** - Сеть: `192.168.2.0`, маска `255.255.255.0`, шлюз `192.168.1.147` **Keenetic — гостевая сеть с другой подсетью:** - Создать вторую SSID, IP `192.168.2.1/24`, DHCP `192.168.2.x` --- ## 13. Обновление версий AWG Поскольку AmneziaWG 3.x развивается быстро (новые теги), при проблемах с DPI-блокировками: ```bash # Узнать свежие теги git ls-remote --tags https://github.com/amnezia-vpn/amneziawg-go.git | tail git ls-remote --tags https://github.com/amnezia-vpn/amneziawg-tools.git | tail # Пересобрать из свежих копий cd /root/awg-src/amneziawg-go && git fetch --tags && git checkout v3.1.XXXXXXX cd /root/awg-src/amneziawg-tools && git fetch --tags && git checkout v3.1.XXXXXXX cp -r /root/awg-src/amneziawg-*/* /root/awg-builder/ docker build -t amneziawg-client:latest /root/awg-builder/ ``` --- ## 14. Безопасность и данные - **Приватный ключ** в `.conf` не попадает в логи/память ассистента — только в файл на сервере. - Не выкладывай `.conf` в чат, репозитории, скриншоты. - `.conf` читается из `/root/awg-config/awg0.conf` только контейнером. - Права: `chmod 600 /root/awg-config/awg0.conf`. --- ## Итоговая структура файлов ``` /root/awg-config/awg0.conf ← твой .conf (private) /root/awg-builder/Dockerfile /root/awg-builder/entrypoint.sh /root/awg-builder/amneziawg-go/ ← исходники (3.1.20260828) /root/awg-builder/amneziawg-tools/ ← исходники (3.1.20260812) /root/awg-src/ ← свежие клоны (для апдейтов) ``` **Контейнер:** `amneziawg-client` = полностью автономный AWG-шлюз, ставится как `--restart always`, не зависит от остальных Docker-сервисов.