From 2b46d04af9ea3f75253d36db23f1de9211d62959 Mon Sep 17 00:00:00 2001 From: Ivan Vasilev Date: Thu, 10 Sep 2026 17:51:19 +0300 Subject: [PATCH] Support Debian in the installer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit install.sh only knew Ubuntu. On Debian 13 the dependency step died on software-properties-common, which trixie no longer ships, and on linux-headers-generic, which Debian never had — so add-apt-repository and the Ubuntu headers meta package are both off the table there. The distro family now comes from /etc/os-release (derivatives via ID_LIKE) and shows up as its own line in the compatibility checklist. On Debian the amnezia PPA is wired by hand: the key is fetched from keyserver.ubuntu.com by its full fingerprint and checked after download, then a signed-by source is written on the Ubuntu series whose glibc is not newer than the Debian release's (bullseye→focal, bookworm→jammy, trixie→noble; AMNEZIA_PPA_SUITE overrides). Headers are installed as a separate step so a missing package gets precise advice instead of a generic failure. Debian's archive keeps only the latest kernel build, so a server that hasn't rebooted in a while has no headers for its running kernel; the hint names linux-image/linux-headers for the kernel's own flavour (cloud-amd64 and so on) plus a reboot. iptables joins the dependencies — PostUp calls it, and a minimal Debian 13 on nftables doesn't have it. IP forwarding moves to its own file in /etc/sysctl.d: Debian 13 no longer reads /etc/sysctl.conf at boot, so the old line would have held only until the first reboot. --- README.md | 2 +- install.sh | 157 ++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 132 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 21810ab..ce57e73 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Внутренняя связь awg-ui → awg-ctrl подписывается асимметрично (Ed25519): приватный ключ только у awg-ui, awg-ctrl проверяет подпись публичным. ## Требования -- **ОС**: Ubuntu 24.04+ (Тесты проходили на Ubuntu 24.04.4 LTS) +- **ОС**: Ubuntu 24.04+ (Тесты проходили на Ubuntu 24.04.4 LTS) или Debian 11–13 (поддержка экспериментальная: PPA amnezia подключается вручную, серия подбирается по релизу Debian и переопределяется через `AMNEZIA_PPA_SUITE`) - **Права**: root. - **Ядро**: kvm (не OpenVZ/LXC/Docker/WSL — нужен загружаемый kernel-модуль AmneziaWG). - **Заголовки ядра** под текущее ядро (для сборки DKMS-модуля). diff --git a/install.sh b/install.sh index 4c55986..10d7f92 100644 --- a/install.sh +++ b/install.sh @@ -72,13 +72,51 @@ echo -e "${BLD}Проверка совместимости:${NC}" KERNEL=$(uname -r) VIRT=$(systemd-detect-virt 2>/dev/null || echo "unknown") -echo " Ядро: $KERNEL · виртуализация: $VIRT · $(lsb_release -ds 2>/dev/null || echo unknown)" -# Считаем все три пункта, НЕ падая на первом, — чтобы показать полный чеклист +# Семейство дистрибутива решает, как ставить AmneziaWG: +# ubuntu — add-apt-repository ppa:amnezia/ppa, заголовки linux-headers-generic; +# debian — ни software-properties-common (в Debian 13 его нет), ни +# linux-headers-generic там не существует: PPA подключаем вручную +# (ключ + signed-by), заголовки — только под текущее ядро. +# Производные (Mint, Pop!_OS, Kali…) определяются через ID_LIKE. +OS_ID=""; OS_LIKE=""; OS_CODENAME=""; OS_NAME="" +if [[ -r /etc/os-release ]]; then + OS_ID=$(. /etc/os-release; echo "${ID:-}") + OS_LIKE=$(. /etc/os-release; echo "${ID_LIKE:-}") + OS_CODENAME=$(. /etc/os-release; echo "${VERSION_CODENAME:-}") + OS_NAME=$(. /etc/os-release; echo "${PRETTY_NAME:-}") +fi +if [[ "$OS_ID" == "ubuntu" || " $OS_LIKE " == *" ubuntu "* ]]; then + OS_FAMILY="ubuntu" +elif [[ "$OS_ID" == "debian" || " $OS_LIKE " == *" debian "* ]]; then + OS_FAMILY="debian" +else + OS_FAMILY="" +fi +# Вариант ядра без версии: 6.12.85+deb13-cloud-amd64 → cloud-amd64, +# 6.1.0-18-cloud-amd64 → cloud-amd64. Нужен для мета-пакетов Debian +# (linux-image-<вариант> / linux-headers-<вариант>). +KFLAVOUR=$(sed -E 's/^[0-9][^-]*-([0-9]+-)?//' <<< "$KERNEL") +# Что советовать, если заголовков под текущее ядро нет. +if [[ "$OS_FAMILY" == "debian" ]]; then + HDR_FIX="apt-get update && apt-get install -y linux-image-$KFLAVOUR linux-headers-$KFLAVOUR && reboot" +else + HDR_FIX="apt-get install -y linux-generic && reboot" +fi + +echo " Ядро: $KERNEL · виртуализация: $VIRT · ${OS_NAME:-$(lsb_release -ds 2>/dev/null || echo unknown)}" + +# Считаем все пункты, НЕ падая на первом, — чтобы показать полный чеклист # со статусом по каждому. Если хоть один не прошёл — печатаем причины и выходим # с кодом 0 (чистый выход, без вида «упало с ошибкой»). -VIRT_OK=1; HDR_OK=1; NET_OK=1 -VIRT_WHY=""; HDR_WHY=""; NET_WHY="" +OS_OK=1; VIRT_OK=1; HDR_OK=1; NET_OK=1 +OS_WHY=""; VIRT_WHY=""; HDR_WHY=""; NET_WHY="" + +# 0) Дистрибутив: установщик умеет только apt-семейство Ubuntu/Debian. +if [[ -z "$OS_FAMILY" ]]; then + OS_OK=0 + OS_WHY="дистрибутив '${OS_NAME:-${OS_ID:-неизвестно}}' не поддерживается — нужен Ubuntu или Debian" +fi # 1) Виртуализация: kernel-модуль нельзя загрузить там, где ядро общее с хостом. case "$VIRT" in @@ -98,7 +136,7 @@ elif echo "$HDR_POLICY" | grep -q 'Candidate: [^(]'; then : elif echo "$HDR_POLICY" | grep -q 'Candidate: (none)'; then HDR_OK=0 - HDR_WHY="нет заголовков под ядро $KERNEL в apt (кастомное ядро провайдера). Решение: apt-get install -y linux-generic && reboot, затем запусти install.sh заново в generic-ядре" + HDR_WHY="нет заголовков под ядро $KERNEL в apt. Решение: $HDR_FIX, затем запусти install.sh заново" fi # 3) Доступ в интернет: нужен для архива, Node и пакетов AWG. Без -f: любой @@ -117,13 +155,15 @@ done # Чеклист: ✓ — пройдено, ✗ — нет. mark() { [[ "$1" == 1 ]] && echo -e " ${GRN}✓${NC} $2" || echo -e " ${RED}✗${NC} $2"; } +mark "$OS_OK" "Дистрибутив" mark "$VIRT_OK" "Виртуализация" mark "$HDR_OK" "Заголовки ядра" mark "$NET_OK" "Доступ в интернет" -if [[ "$VIRT_OK" == 0 || "$HDR_OK" == 0 || "$NET_OK" == 0 ]]; then +if [[ "$OS_OK" == 0 || "$VIRT_OK" == 0 || "$HDR_OK" == 0 || "$NET_OK" == 0 ]]; then echo warn "Установка невозможна — не пройдены проверки:" + [[ "$OS_OK" == 0 ]] && echo -e " ${RED}•${NC} $OS_WHY" [[ "$VIRT_OK" == 0 ]] && echo -e " ${RED}•${NC} $VIRT_WHY" [[ "$HDR_OK" == 0 ]] && echo -e " ${RED}•${NC} $HDR_WHY" [[ "$NET_OK" == 0 ]] && echo -e " ${RED}•${NC} $NET_WHY" @@ -461,30 +501,89 @@ start_logging # секции «Проверка совместимости»; $KERNEL задан там же. step "1/7 AmneziaWG" +# Debian: add-apt-repository нет (software-properties-common из Debian 13 убран), +# а PPA под Debian-релизы не собирается — подключаем его вручную на Ubuntu-серию +# с glibc не новее дебиановской. Модуль приезжает DKMS-исходниками и от серии +# не зависит; серия важна только для бинарника amneziawg-tools. +# AMNEZIA_PPA_SUITE — задать серию вручную (focal/jammy/noble). +AMNEZIA_PPA_URL="https://ppa.launchpadcontent.net/amnezia/ppa/ubuntu" +AMNEZIA_PPA_FPR="75C9DD72C799870E310542E24166F2C257290828" # Launchpad PPA for Iurii Egorov +AMNEZIA_KEYRING="/etc/apt/keyrings/amnezia-ppa.gpg" +add_amnezia_ppa_debian() { + local suite="${AMNEZIA_PPA_SUITE:-}" + if [[ -z "$suite" ]]; then + case "$OS_CODENAME" in + bullseye) suite="focal" ;; + bookworm) suite="jammy" ;; + trixie) suite="noble" ;; + *) suite="noble" + warn "Debian '${OS_CODENAME:-неизвестно}' не сопоставлен с серией PPA — берём noble (задать вручную: AMNEZIA_PPA_SUITE=…)" ;; + esac + fi + echo " → подключение PPA amnezia (Debian ${OS_CODENAME:-?} → серия $suite)" + + # Ключ берём по полному отпечатку и сверяем отпечаток после загрузки — + # apt-key в Debian 12+ уже нет, а доверять ответу keyserver вслепую нельзя. + local tmp gh fprs="" + tmp=$(mktemp); gh=$(mktemp -d) + if curl -fsSL --connect-timeout 15 \ + "https://keyserver.ubuntu.com/pks/lookup?op=get&options=mr&search=0x${AMNEZIA_PPA_FPR}" \ + | gpg --dearmor > "$tmp" 2>/dev/null; then + fprs=$(GNUPGHOME="$gh" gpg --batch --show-keys --with-colons "$tmp" 2>/dev/null || true) + fi + if ! grep -q "^fpr:::::::::${AMNEZIA_PPA_FPR}:" <<< "$fprs"; then + rm -rf "$tmp" "$gh" + fail "Не удалось получить ключ PPA amnezia ($AMNEZIA_PPA_FPR) с keyserver.ubuntu.com" + fi + install -d -m 0755 /etc/apt/keyrings + install -m 0644 "$tmp" "$AMNEZIA_KEYRING" + rm -rf "$tmp" "$gh" + + echo "deb [signed-by=$AMNEZIA_KEYRING] $AMNEZIA_PPA_URL $suite main" \ + > /etc/apt/sources.list.d/amnezia-ppa.list + ok "PPA amnezia подключён: $suite, ключ $AMNEZIA_KEYRING" +} + if command -v awg &>/dev/null && command -v awg-quick &>/dev/null && modinfo amneziawg &>/dev/null; then ok "AWG уже установлен (модуль amneziawg: $(modinfo -F version amneziawg 2>/dev/null || echo present))" else echo " → apt-get update" apt-get update || fail "apt-get update упал — проверь /etc/apt/sources.list*" - echo " → установка зависимостей сборки + заголовков ядра" + # iptables — в обоих списках: его зовёт PostUp в awg1.conf, а в Debian 13 + # (nftables по умолчанию) он в минимальной системе не установлен. + if [[ "$OS_FAMILY" == "debian" ]]; then + DEPS=(gnupg ca-certificates curl dkms build-essential iptables) + else + DEPS=(software-properties-common python3-launchpadlib gnupg2 dkms build-essential iptables) + fi + echo " → установка зависимостей сборки" DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a \ - apt-get install -y \ - software-properties-common \ - python3-launchpadlib \ - gnupg2 \ - dkms \ - build-essential \ - "linux-headers-$KERNEL" \ - linux-headers-generic \ - || fail "Не удалось установить зависимости или заголовки ядра" + apt-get install -y "${DEPS[@]}" \ + || fail "Не удалось установить зависимости сборки: ${DEPS[*]}" + + # Заголовки — отдельным шагом: их нехватка — самая частая причина сбоя, и на + # неё нужен точный совет, а не общее «не удалось установить». Ошибку apt + # здесь не валим: решает наличие /lib/modules/$KERNEL/build ниже. + HDR_PKGS=("linux-headers-$KERNEL") + [[ "$OS_FAMILY" == "ubuntu" ]] && HDR_PKGS+=(linux-headers-generic) + echo " → установка заголовков ядра: ${HDR_PKGS[*]}" + DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a \ + apt-get install -y "${HDR_PKGS[@]}" \ + || warn "apt не смог поставить ${HDR_PKGS[*]}" if [[ ! -d "/lib/modules/$KERNEL/build" ]]; then warn "Нет /lib/modules/$KERNEL/build — заголовки под текущее ядро отсутствуют." - - warn "Часто бывает на кастомном ядре провайдера. Решение:" - warn " apt-get install -y linux-generic && reboot" - warn "и после загрузки в generic-ядро запусти install.sh заново." + if [[ "$OS_FAMILY" == "debian" ]]; then + # Debian держит в архиве только последнюю сборку ядра: если сервер + # давно не перезагружали, пакета под запущенное ядро там уже нет. + warn "Debian хранит в архиве только свежую сборку ядра — запущенное $KERNEL" + warn "уже устарело. Поставь актуальное ядро вместе с заголовками:" + else + warn "Часто бывает на кастомном ядре провайдера. Решение:" + fi + warn " $HDR_FIX" + warn "и после перезагрузки запусти install.sh заново." fail "Отсутствуют заголовки ядра $KERNEL — DKMS не соберёт модуль" fi ok "Заголовки ядра на месте: /lib/modules/$KERNEL/build" @@ -496,6 +595,8 @@ else # apt-get install кэш apt бывает в переходном состоянии и отдаёт пустоту. if grep -rqs 'amnezia/ppa' /etc/apt/sources.list /etc/apt/sources.list.d/ 2>/dev/null; then ok "Репозиторий amnezia уже настроен — add-apt-repository пропущен" + elif [[ "$OS_FAMILY" == "debian" ]]; then + add_amnezia_ppa_debian else echo " → add-apt-repository ppa:amnezia/ppa" add-apt-repository -y ppa:amnezia/ppa \ @@ -688,12 +789,16 @@ fi chmod 600 "$INTERNAL_AUTH_PRIV" "$INTERNAL_AUTH_PUB" ok "Ключи внутренней авторизации awg-ui ↔ awg-ctrl" -grep -qxF 'net.ipv4.ip_forward=1' /etc/sysctl.conf \ - || echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf -grep -qxF 'net.ipv6.conf.all.forwarding=1' /etc/sysctl.conf \ - || echo 'net.ipv6.conf.all.forwarding=1' >> /etc/sysctl.conf -sysctl -qp -ok "IP forwarding включён" +# Свой файл в /etc/sysctl.d, а не /etc/sysctl.conf: в Debian 13 systemd-sysctl +# /etc/sysctl.conf при загрузке больше не читает — forwarding включился бы +# только до первой перезагрузки, и клиенты остались бы без интернета. +SYSCTL_FILE="/etc/sysctl.d/99-awg-control.conf" +cat > "$SYSCTL_FILE" <<'SYSCTL' +net.ipv4.ip_forward=1 +net.ipv6.conf.all.forwarding=1 +SYSCTL +sysctl -q -p "$SYSCTL_FILE" +ok "IP forwarding включён ($SYSCTL_FILE)" # Параметры AmneziaWG 3.1. Значения-диапазоны взяты из дефолтов клиента # AmneziaVPN (protocolConstants.h), чтобы сервер и клиент не расходились.