406 lines
16 KiB
Markdown
406 lines
16 KiB
Markdown
# 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-сервисов. |