feat: Cloudflare WARP routing for AmneziaWG

Web toggles per IPv4 peer; wgcf install script on host (no Telegram/QR).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Андрей Бобырев
2026-05-14 18:00:39 +03:00
parent 448fe3b360
commit 6f6fb5f9b9
7 changed files with 922 additions and 25 deletions

252
scripts/warp-amnezia.sh Executable file
View File

@@ -0,0 +1,252 @@
#!/usr/bin/env bash
# Cloudflare WARP внутри контейнера AmneziaWG (wgcf → warp.conf → wg-quick).
# Запускать на хосте VPS от root. Без Telegram и без QR — только установка и обслуживание туннеля WARP.
# После install управление «кто выходит через WARP» — в веб-панели Amnezia Admin (раздел WARP).
set -euo pipefail
WGCF_VERSION="${WGCF_VERSION:-2.2.30}"
WGCF_BIN="${WGCF_BIN:-/root/wgcf}"
WGCF_ACCOUNT="${WGCF_ACCOUNT:-/root/wgcf-account.toml}"
WGCF_PROFILE="${WGCF_PROFILE:-/root/wgcf-profile.conf}"
usage() {
echo "Использование: $0 {install|start|stop|status|rekey} [имя_контейнера]"
echo "Переменные: AWG_CONTAINER, WARP_DIR (по умолчанию /opt/warp)"
exit 1
}
need_root() {
if [[ "${EUID:-0}" -ne 0 ]]; then
echo "Запустите от root."
exit 1
fi
}
pick_container() {
local c="${2:-${AWG_CONTAINER:-}}"
if [[ -n "$c" ]] && docker exec "$c" true 2>/dev/null; then
CONTAINER="$c"
return 0
fi
local -a found=()
while IFS= read -r n; do found+=("$n"); done < <(docker ps --format '{{.Names}}' | grep -E '^amnezia-awg2$|^amnezia-awg$' || true)
if [[ ${#found[@]} -eq 1 ]]; then
CONTAINER="${found[0]}"
return 0
fi
if [[ ${#found[@]} -gt 1 ]]; then
echo "Несколько контейнеров: ${found[*]}. Укажите вторым аргументом или AWG_CONTAINER="
exit 1
fi
echo "Не найден контейнер amnezia-awg / amnezia-awg2."
exit 1
}
load_paths() {
AWG_WARP_DIR="${WARP_DIR:-/opt/warp}"
AWG_WARP_CONF="${AWG_WARP_DIR}/warp.conf"
AWG_VPN_CONF=""
if docker exec "$CONTAINER" test -f /opt/amnezia/awg/awg0.conf 2>/dev/null; then
AWG_VPN_CONF="/opt/amnezia/awg/awg0.conf"
elif docker exec "$CONTAINER" test -f /opt/amnezia/awg/wg0.conf 2>/dev/null; then
AWG_VPN_CONF="/opt/amnezia/awg/wg0.conf"
else
for f in /opt/amnezia/awg/wg0.conf /opt/amnezia/awg/awg0.conf /etc/wireguard/wg0.conf; do
if docker exec "$CONTAINER" test -f "$f" 2>/dev/null; then
AWG_VPN_CONF="$f"
break
fi
done
fi
[[ -n "$AWG_VPN_CONF" ]] || {
echo "Не найден конфиг WireGuard/AWG в контейнере."
exit 1
}
}
install_wgcf() {
[[ -x "$WGCF_BIN" ]] && return 0
local arch wa
arch="$(uname -m)"
case "$arch" in
x86_64) wa="amd64" ;;
aarch64 | arm64) wa="arm64" ;;
armv7l) wa="armv7" ;;
*) echo "Архитектура не поддерживается: $arch"; exit 1 ;;
esac
wget -q -O "$WGCF_BIN" "https://github.com/ViRb3/wgcf/releases/download/v${WGCF_VERSION}/wgcf_${WGCF_VERSION}_linux_${wa}"
chmod +x "$WGCF_BIN"
}
ensure_account() {
if [[ ! -f "$WGCF_ACCOUNT" ]]; then
echo "Регистрация WARP (wgcf register)…"
(cd /root && yes | "$WGCF_BIN" register >/dev/null 2>&1 || true)
fi
[[ -f "$WGCF_ACCOUNT" ]] || {
echo "Не создан $WGCF_ACCOUNT"
exit 1
}
}
generate_profile() {
(cd /root && yes | "$WGCF_BIN" generate >/dev/null 2>&1 || true)
[[ -f "$WGCF_PROFILE" ]] || {
echo "Не создан $WGCF_PROFILE"
exit 1
}
}
resolve_endpoint() {
local ep
ep="$(getent ahostsv4 engage.cloudflareclient.com 2>/dev/null | awk 'NR==1{print $1}')"
[[ -n "$ep" ]] || {
echo "Не удалось резолвить engage.cloudflareclient.com"
exit 1
}
echo "$ep"
}
build_warp_conf() {
local endpoint_ip="$1"
local pk pub addr
pk="$(awk -F' = ' '/^PrivateKey = /{print $2}' "$WGCF_PROFILE")"
pub="$(awk -F' = ' '/^PublicKey = /{print $2}' "$WGCF_PROFILE")"
addr="$(awk -F' = ' '/^Address = /{print $2}' "$WGCF_PROFILE" | cut -d',' -f1)"
docker exec "$CONTAINER" sh -c "mkdir -p '$AWG_WARP_DIR'"
docker cp "$WGCF_PROFILE" "${CONTAINER}:${AWG_WARP_DIR}/wgcf-profile.conf" 2>/dev/null || true
docker exec "$CONTAINER" sh -c "cat > '$AWG_WARP_CONF' <<WARPEOF
[Interface]
PrivateKey = ${pk}
Address = ${addr}
MTU = 1280
Table = off
[Peer]
PublicKey = ${pub}
AllowedIPs = 0.0.0.0/0
Endpoint = ${endpoint_ip}:2408
PersistentKeepalive = 25
WARPEOF
chmod 600 '$AWG_WARP_CONF'"
}
warp_up() {
docker exec "$CONTAINER" sh -c "wg-quick down '$AWG_WARP_CONF' >/dev/null 2>&1 || true"
docker exec "$CONTAINER" sh -c "wg-quick up '$AWG_WARP_CONF'"
docker exec "$CONTAINER" ip addr show warp >/dev/null 2>&1 || {
echo "Интерфейс warp не поднялся."
exit 1
}
}
warp_down() {
docker exec "$CONTAINER" sh -c "wg-quick down '$AWG_WARP_CONF' 2>/dev/null || true"
}
is_installed() {
docker exec "$CONTAINER" test -f "$AWG_WARP_CONF" 2>/dev/null
}
is_running() {
docker exec "$CONTAINER" ip addr show warp >/dev/null 2>&1
}
cmd_install() {
echo "Бэкап конфигов в контейнере…"
docker exec "$CONTAINER" sh -c "
ts=\$(date +%Y%m%d-%H%M%S)
cp '$AWG_VPN_CONF' '${AWG_VPN_CONF}.bak-warp-'\$ts 2>/dev/null || true
cp /opt/amnezia/start.sh /opt/amnezia/start.sh.bak-warp-\$ts 2>/dev/null || true
true
"
install_wgcf
ensure_account
generate_profile
local ep
ep="$(resolve_endpoint)"
echo "Endpoint: $ep"
build_warp_conf "$ep"
warp_up
echo "Готово: WARP установлен. Управление клиентами — в веб-панели (раздел WARP)."
}
cmd_status() {
if is_installed; then
echo "warp.conf: есть ($AWG_WARP_CONF)"
else
echo "warp.conf: нет — выполните: $0 install"
exit 1
fi
if is_running; then
echo "Интерфейс warp: поднят"
docker exec "$CONTAINER" wg show warp 2>/dev/null || true
echo -n "Внешний IP через WARP: "
docker exec "$CONTAINER" sh -c "curl -fsS --interface warp --connect-timeout 4 https://ifconfig.me 2>/dev/null || echo '?'"
echo
else
echo "Интерфейс warp: опущен ($0 start)"
fi
}
cmd_rekey() {
is_installed || {
echo "Сначала install."
exit 1
}
warp_down || true
rm -f "$WGCF_ACCOUNT"
ensure_account
generate_profile
local ep
ep="$(resolve_endpoint)"
build_warp_conf "$ep"
warp_up
echo "Ключ WARP перевыпущен. Заново отметьте клиентов в веб-панели и примените маршрутизацию."
}
[[ "${1:-}" ]] || usage
need_root
command -v docker >/dev/null || {
echo "Нужен docker в PATH."
exit 1
}
CMD="$1"
pick_container "$@"
load_paths
case "$CMD" in
install)
if is_installed && is_running; then
echo "Уже установлен и работает."
exit 0
fi
if is_installed && ! is_running; then
echo "Конфиг есть — поднимаю интерфейс…"
warp_up
exit 0
fi
cmd_install
;;
start)
is_installed || {
echo "Нет warp.conf — сначала install."
exit 1
}
is_running && {
echo "Уже работает."
exit 0
}
warp_up
echo "WARP поднят."
;;
stop)
is_installed || exit 0
warp_down
echo "WARP остановлен."
;;
status) cmd_status ;;
rekey) cmd_rekey ;;
*) usage ;;
esac