Files
wikipedia/VPN/amneziavpn-docker-guide.md

16 KiB
Raw Permalink Blame History

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):

[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. Подготовка хоста

# Проверить 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-контейнера нестабилен.

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

Проверка тегов (актуальные на момент сборки):

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. Директория сборки

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

# 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

#!/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:
mkdir -p /root/awg-config
cp /путь/к/de.conf /root/awg-config/awg0.conf
#    ↑ имя файла = имя интерфейса (awg0)
  1. Собери образ:
cd /root/awg-builder
docker build -t amneziawg-client:latest .
  1. Запусти контейнер:
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
  1. Проверь:
docker ps --filter name=amneziawg-client   # Up, restart always
docker logs amneziawg-client               # интерфейс UP, handshake
docker exec amneziawg-client awg show      # самый наглядный статус

9. Проверка работоспособности

# Статус интерфейса и 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 шлюза:

ip addr add 192.168.2.1/32 dev eth0
# persistence: netplan / LXC interface

entrypoint.sh — расширить маршрутизацию:

# Вторая подсеть → тоже через 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-блокировками:

# Узнать свежие теги
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-сервисов.