Commit graph

83 commits

Author SHA1 Message Date
Ivan Bondarev
1cbee098ca
v5.15.1 - maintenance release (audit fixes, IPv6 hardening, release gate)
Maintenance release after external code and documentation audits. No new
features; hardens the v5.15.0 dual-stack IPv6 work, tightens several manage
commands, fixes correctness and robustness issues, and adds a release
preflight gate plus a docs-consistency check.

Behavior change: split tunnel + IPv6 mirrors the IPv4 intent - a custom
ALLOWED_IPS keeps that IPv4 split and adds only the tunnel ULA for IPv6
instead of forcing a full ::/0 route. A full tunnel still gets ::/0 (native
IPv6) or the ULA.

Fixed: vpn:// URI carries the server's real MTU/keepalive/DNS; stricter
native-IPv6 detection (global non-ULA address and default route) with the
host IPv6 stack re-enabled before detection; install fails fast on apt update
errors; manage modify validates Endpoint/AllowedIPs; manage add is safe
against name reuse and cleans partial artifacts; manage restore prunes stale
clients/keys and recreates the expiry dir; manage help exit codes; manage
--json keeps stdout pure JSON; log messages stop doubling percent signs.

Docs/process: subnet and OS help accuracy, list --json documented, changelog
compare-links and anchors restored, SECURITY/CONTRIBUTING refreshed, public
RELEASE_PROCESS.md, release publish gated behind a full preflight job, new
docs-consistency workflow, signing design aligned with the asset draft.

Version bumped to 5.15.1 across all six scripts; SHA pins recomputed.
2026-06-02 14:08:36 +03:00
Ivan Bondarev
7eb5e9ebde
feat: dual-stack IPv6 inside the tunnel (v5.15.0) (#97)
Optional dual-stack IPv6 inside the AmneziaWG tunnel via --allow-ipv6-tunnel
(request #24). Off by default; without the flag behavior is identical to v5.14.x.

- server dual-stack [Interface] + ip6tables MASQUERADE
- IPv6 client allocation, dual-stack client config + vpn:// URI
- manage list/regen dual-stack aware
- native-IPv6 detection: full ::/0 route with native IPv6, tunnel-subnet-only
  without it (no black hole)
- release tooling: scripts/update-sha-pins.sh, scripts/preflight-check.sh,
  SHA-lockstep test
- bilingual docs (CHANGELOG, ADVANCED, README, INSTALL_VPS)

Tested: full bats suite plus live VPS on ARM64 / Ubuntu 26.04 (native IPv6)
and x86_64 / Debian 13 (no native IPv6); cross-host IPv4 and IPv6 confirmed
through the tunnel.
2026-06-01 17:59:22 +03:00
Ivan Bondarev
e016bb6e99
fix: detect SSH port for UFW rule, prevent lockout on non-standard port (#91)
Adds detect_ssh_ports() resolving the real SSH port from --ssh-port, sshd -T (Port + ListenAddress), ss sockets, sshd_config, then 22. Both UFW branches open every detected port; new --ssh-port flag; v5.14.5 bump + SHA256 pins + bilingual changelog. Live-tested on Ubuntu 24.04 (x86_64) and Ubuntu 26.04 (ARM64): no lockout.
2026-05-25 23:08:49 +03:00
Ivan Bondarev
4710cce748 fix: v5.14.4 - declining UFW during interactive install now continues
setup_improved_firewall returned 1 when the user answered N to the
"Enable UFW?" prompt, and the caller treats a non-zero return as fatal,
so a valid "no firewall" choice (the prompt defaults to N) stopped the
whole install. Return 0 on decline instead: the UFW rules stay staged,
the firewall is left inactive, the install proceeds, and the log shows
how to enable it later (sudo ufw enable). The --yes path is unchanged.

- install_amneziawg.sh / _en.sh: decline branch returns 0 + two warn lines
- version bump 5.14.3 -> 5.14.4 across the four versioned scripts + awg_common
- refreshed COMMON/MANAGE SHA256 pins (RU + EN)
- tests/test_v5144_ufw_optional.bats: 6 tests covering decline/accept/--yes, RU+EN
2026-05-24 20:34:19 +03:00
Ivan Bondarev
1275906d4a fix: v5.14.3 - cleanup_system stops breaking DHCP on Ubuntu 26.04 (issue #84)
@jay0x reported on a clean Ubuntu 26.04 server installed from the
subiquity ISO inside VirtualBox: after running the installer the box
lost its IP address after reboot. Root cause: cleanup_system() called
`apt-get autoremove` after `apt-get purge cloud-init`, and on subiquity
installs there are no cloud-init markers in /etc/netplan, so the
purge path activates. The autoremove then cascaded into removing
netplan-generator as a transitive cloud-init dependency, leaving the
subiquity-written 00-installer-config.yaml without anything to render
it into /run/systemd/network, so systemd-networkd booted with empty
config and DHCP did nothing.

cleanup_system() changes:
- Drop the apt-get autoremove call entirely. Orphan packages take
  ~50-200 MB; that is acceptable in exchange for not breaking the net
  stack. Users can run `apt-get autoremove --no-install-recommends`
  manually if they care.
- Snapshot the user's existing apt-mark holds via `apt-mark showhold`
  before adding our own holds, and only release the holds we placed.
  This avoids stripping a user-managed hold on something like
  linux-image-*.
- apt-mark hold netplan.io / netplan-generator / systemd-resolved /
  netcfg / ifupdown for the duration of the purge run, so transitive
  removal cannot touch them. systemd-networkd is not a standalone
  package on Ubuntu 24+ (the binary lives inside systemd), so it is
  intentionally NOT in the hold list.
- Snapshot the default route before cleanup. If after cleanup the
  route is empty but the snapshot wasn't, attempt recovery: reinstall
  netplan.io unconditionally, gate netplan-generator behind
  `apt-cache show netplan-generator` (Debian 12 does not ship that
  package and `apt install` would abort the transaction), restart
  systemd-networkd, run netplan apply, then poll the route every 1-5
  seconds for up to ~26 seconds (fixed sleeps are unreliable on slow
  VMs). On failure, last-ditch: parse the iface from the snapshot,
  bring it up with `ip link set <iface> up`, try `networkctl renew`
  with a route re-check, then `dhclient -4` if the route is still
  missing. Only after all of that fails does the installer die with
  a hint to restore the network from the console and retry with
  --no-tweaks.

check_os_version() now whitelists Ubuntu 26.04 alongside 24.04 and
25.10. Previously 26.04 fell into the warning branch with an interactive
prompt and only passed automatically when --yes was set. The release
is tested on 26.04 server in VirtualBox after this fix.

+14 bats tests in test_v5143_cleanup_no_autoremove.bats covering the
no-autoremove guarantee, hold list (without systemd-networkd), pre-
existing hold preservation, default-route recovery with apt-cache
gate, last-ditch networkctl + dhclient sequential fallback, die path
on total failure, cloud-init guard regression, and RU/EN structural
parity.

SCRIPT_VERSION and header dates bumped 5.14.2 to 5.14.3 across the
four versioned scripts. SHA256 pins for awg_common.sh and
manage_amneziawg.sh refreshed in both install scripts.

Thanks to @jay0x for the detailed repro with dpkg, journalctl, and
ls /etc/netplan/ output.
2026-05-21 18:40:42 +03:00
Ivan Bondarev
269a7fa146 fix: v5.14.2 - QR scan reliability + ARM build kernel disambiguation
generate_qr_vpnuri now passes -s 6 (module size) to qrencode so the
.vpnuri.png is large enough for an iPhone camera to read off a computer
screen, fixing error 900 ImportInvalidConfigError in AmneziaVPN on iOS
(issue #72, reporter @haritos90). The accompanying -l L and -m 4 pin
existing libqrencode defaults explicitly so future default changes can
not regress this fix. Increasing module scale does not change QR data
capacity - it makes each module physically larger so the camera can
distinguish black/white blocks reliably.

scripts/build-arm-deb.sh extracts kernel version resolution into a
_resolve_kernel_version helper. KERNEL_VERSION env is honoured when set
and validated against /lib/modules/<ver>/build. Otherwise the helper
counts /lib/modules/*/build candidates and fails explicitly when more
than one is present, instead of silently picking the first match.
External code review on 8 may flagged the original first-wins loop
as a real risk on developer hosts with multiple kernel headers. A
source guard above the main body lets bats source the script for unit
tests without triggering the build pipeline.

+18 bats tests (528 total, was 510 on v5.14.1):
- test_v5142_qr_high_density.bats (+7): capture qrencode argv via shim,
  assert -l L / -s 6 / -m 4 / -t png present, PNG produced from .vpnuri
  payload, RU/EN structural parity, RU/EN invocation line byte-identical.
- test_v5142_build_arm_deb.bats (+11): _resolve_kernel_version with
  mocked /lib/modules layouts (single/zero/many candidates; KERNEL_VERSION
  happy / disambig / missing target / empty fallback); structural checks
  for function definition, source guard, absence of inline detection loop.

SCRIPT_VERSION and header dates bumped 5.14.1 to 5.14.2 across the four
versioned scripts. SHA256 pins for awg_common.sh and manage_amneziawg.sh
refreshed in both install scripts.
2026-05-21 13:14:44 +03:00
Ivan Bondarev
f7ab696763 fix(mtu): manage regen now syncs MTU from server awg0.conf + version bump v5.14.1
Fixes Discussion #38 (E-lmedano): "After changing MTU in awg0.conf the
manage_amneziawg.sh regen does not fix this parameter in the client
config". Both render_server_config and render_client_config in awg_common.sh
had a literal `MTU = 1280` baked in, so a hand-edited server MTU was
silently dropped on the next regen.

MTU resolution order in render_client_config:
  1) MTU = N from [Interface] section of server awg0.conf (source of truth
     for a running server; user can hand-edit and regen picks it up)
  2) AWG_MTU from awgsetup_cfg.init (installer-time default, env-overridable)
  3) 1280 as last-resort fallback

render_server_config now uses ${AWG_MTU:-1280}.

Live awg0.conf parser (load_awg_params) also reads `MTU = ...` and exports
AWG_MTU, so subsequent renders see the value picked from the server.

Values outside the sane 576..9100 range are dropped at every stage and
the chain falls back to 1280. Range covers IPv4 minimum (576) through
just under jumbo (9100).

Files:
- awg_common.sh / awg_common_en.sh: new _validate_mtu helper, new
  _extract_mtu_from_server_conf helper (awk last-wins matching awg-quick
  behaviour), MTU substitution in both render functions, MTU line added
  to live-config parser case, AWG_MTU added to safe_load_config whitelist.
- install_amneziawg.sh / install_amneziawg_en.sh: AWG_MTU added to the
  safe_load_config whitelist and to the awgsetup_cfg.init writer
  (`export AWG_MTU=${AWG_MTU:-1280}`).
- tests/test_v5141_mtu_resolution.bats: +18 tests covering the extractor
  (valid / whitespace / absent / wrong section / duplicate last-wins /
  missing file / non-numeric / out-of-range), the validator (boundary
  576 and 9100, rejects 0/-1/9101/575/abc/empty), structural checks on
  both render functions, whitelist coverage in all 4 files, installer
  writer assertion, RU/EN byte-identical helper.
- tests/test_v5115_regen_multiarg.bats: replaced the v5.13.0-pinned
  SCRIPT_VERSION test with a generic "all 4 scripts agree on the
  version" check that does not need touching on every patch release.

Version + release plumbing:
- SCRIPT_VERSION 5.14.0 -> 5.14.1 in the 4 versioned scripts (RU + EN).
- COMMON_SCRIPT_SHA256 and MANAGE_SCRIPT_SHA256 refreshed in both
  installers to match the v5.14.1 helper scripts.
- README + INSTALL_VPS: install/upgrade URLs and Installer_Version badge
  bumped to v5.14.1.
- CHANGELOG entries added [5.14.1] - 2026-05-19 in RU + EN.
- CONTRIBUTING bats baseline 492 -> 510.

Quality gates:
- bash -n clean on all 6 scripts
- shellcheck -S warning clean on all 6 scripts
- Full bats suite 510/510 PASS, 0 FAIL (2 pre-existing skips)
- All 4 SHA256 pins match actual file hashes
- 2-iteration second-opinion review applied (last-wins, range
  validation, live-parser MTU read)
2026-05-19 15:47:18 +03:00
Ivan Bondarev
2a77bcfec0 feat: manage diagnose [--carrier=NAME] + observability for public IP detection
Two changes bundled in one commit because the diagnose command consumes
the IP-detection observability trail it expects.

1) MyAI-z5kc: new `manage diagnose` subcommand for self-troubleshooting.
   - 9 health checks: kernel module, service status, interface UP,
     sysctl ip_forward, BBR congestion control, UFW state + AWG port,
     peer count, AWG params snapshot, optional carrier comparison.
   - --carrier=NAME compares current Jc/Jmin/Jmax/I1 against a known
     carrier profile and prints OK/WARN/FAIL + a Fix: hint.
   - 7 confirmed carriers (matching ADVANCED.md operator matrix ✅ rows):
       beeline_msk (default preset)
       yota_msk, tele2_msk, tattelecom (mobile preset, random I1)
       tele2_krasnoyarsk, megafon_regions (mobile preset, I1=absent)
       tmobile_us (binary I1, Jc=6/Jmin=10/Jmax=50; Discussion #45)
     Megafon Moscow + MTS Moscow intentionally omitted - "🔄 testing"
     in the matrix, no fixed range to compare against yet.
   - Exit code: 1 only on FAIL or unknown carrier; WARN -> 0.
   - Bilingual RU + EN scripts in lockstep.

2) Observability for get_server_public_ip in awg_common.sh / _en.sh:
   - On successful detection: write '[ts] DEBUG: public IP detected: $ip
     (via $svc)' directly to LOG_FILE (if writable). Never to stdout -
     the function's stdout IS the IP; any extra bytes corrupt
     $(get_server_public_ip) capture and the generated client Endpoint
     line. Earlier draft used log_debug which routes to stdout via its
     awg_common.sh fallback (`echo "[DEBUG] $1"`) and silently broke
     the contract; replaced with a direct file-append guarded on
     LOG_FILE being set + writable.
   - On total failure: same trail in LOG_FILE with a failure marker.

Tests: tests/test_v5140_diagnose.bats +14 cases. Full suite 492 PASS
(478 + 14), 0 FAIL, 2 pre-existing skips. bash -n clean, shellcheck
-S warning clean on all 4 modified scripts.

Carrier matrix sourced from ADVANCED.md "Какие операторы проверены"
table - only ✅ confirmed rows; testing-only rows (🔄) deliberately
excluded so the diagnostic recommends only data we actually have.
2026-05-19 01:12:15 +03:00
Ivan Bondarev
1cec9eb706 fix(public-ip): address Codex review feedback - alphabetical order + stronger tests
Changes per Codex review of commit 073de10:

- Reorder service list alphabetically (was: checkip first as 'most reliable
  in cloud'). The original ordering biased first-wins toward AWS endpoint
  on non-AWS deployments where ifconfig.me / ipify historically work fine.
  Alphabetical order is deterministic, fair, and easier to diff.
- Rewrite the rationale comment: drop unsupported 'uptime SLA ranking'
  claim, describe what each service is good for instead.
- Strengthen RU/EN parity test: extract ordered URL list with grep and
  compare byte-identical (was: only counted URL occurrences, would have
  passed if RU and EN had different endpoints at same count).
- Switch curl mock from "${@: -1}" (SC2124) to explicit array indexing
  via local args=("$@"); url="${args[$((${#args[@]} - 1))]}".
- Drop internal beads ticket reference (MyAI-avy9) from public test file
  comment header.
- Add 2 tests asserting alphabetical order (first = api.ipify.org,
  last = ipinfo.io/ip) so future reorder needs an explicit test update.

Tests: 11 in test_v5140_public_ip_services.bats (was 9), all PASS.
2026-05-18 22:34:46 +03:00
Ivan Bondarev
073de10982 feat: extend get_server_public_ip with checkip.amazonaws.com + ifconfig.io
Add 2 services to the public IP detection cascade (was 4, now 6),
giving better coverage for AWS / GCP / OCI deployments where a single
endpoint can rate-limit or be unreachable from a VPC private subnet.
checkip.amazonaws.com is listed first because it tends to stay reachable
from cloud private subnets routed through a NAT Gateway. ifconfig.io
serves as an alternative to ifconfig.me for downtime cases.

First-wins order preserved (no behavior change when first service
responds). RU + EN parity maintained.

+ tests/test_v5140_public_ip_services.bats (+9 tests):
  structural RU/EN parity on the 6 endpoints, functional fallthrough,
  invalid IP format skip, cache short-circuit.
2026-05-18 22:21:38 +03:00
Ivan Bondarev
f1fb22c71b
v5.12.1: hotfix bundle for v5.12.0 (#77)
Three surgical fixes for issues found in the first 48 hours after v5.12.0. No new features, no architectural changes.

1. Respect AWG_SKIP_APPLY=1 in `manage add` and `manage remove`: v5.12.0 added an unconditional `ensure_amneziawg_kernel_module || die` pre-call that broke the documented offline edit-only flow on dev/CI machines without the kernel module loaded. Wrapped in `if [[ "${AWG_SKIP_APPLY:-0}" != "1" ]]`. `manage restart` is intentionally NOT gated. RU + EN mirror.

2. Cloud-headers fallback in `awg_common._install_kernel_headers` for Debian cloud kernels (`*-cloud-*` in `uname -r`): `manage repair-module` now tries `linux-headers-cloud-${arch}` before generic, matching the smart detection the installer's step 2 already had. Standard kernels — behaviour unchanged.

3. ARM prebuilt xz fix (Issue #76): `scripts/build-arm-deb.sh` switched from `xz -9` to kernel-compatible `xz --check=crc32 --lzma2=dict=1MiB`, matching mainline `scripts/Makefile.modinst`. Plus a build-time sanity gate: `xz -t` + `xz -d -c` round-trip after compression, `exit 1` on failure.

Tests: +30 new bats (387 in the matrix, was 357 in v5.12.0). All v5.12.1 fixes verified on Ubuntu 24.04 VPS — including the regression scenario `AWG_SKIP_APPLY=1 manage add` with the kernel module unloaded (the exact case that v5.12.0 broke).

SCRIPT_VERSION bumped to 5.12.1 in install / manage / awg_common (RU + EN). SHA256 pins recomputed. README URLs and version badge bumped. Bilingual CHANGELOG entries added.
2026-05-08 12:37:41 +03:00
Ivan Bondarev
af382bfbfc v5.12.0: DKMS auto-repair after kernel upgrade (#75)
Three layers of safety net for the amneziawg DKMS module after an apt
upgrade of the kernel:

- apt hook (DPkg::Post-Invoke) — rebuilds DKMS for the new kernel.
- systemd unit (Before=awg-quick@awg0.service) — guarantees the module
  at boot, ordered before the VPN service.
- manage repair-module (interactive fallback) — explicit user-facing
  recovery, allowed to apt-get install kernel-headers in this context.

Plus smart kernel-headers meta-package detection (Ubuntu flavor
aws/azure/gcp/oracle/kvm/lowlatency/raspi + Debian cloud detection),
manage pre-call wrapper for module recovery before add/remove/restart,
and clean uninstall path for all v5.12.0+ artefacts.

Backwards-compatible with v5.11.x installs — auto-repair components
deploy on the next install_amneziawg.sh run. Tests: 357/357 bats.
VPS verified on Ubuntu 24.04 amd64 + Debian 13 trixie ARM64 (DKMS
path). Known issue: prebuilt path on Debian 13 ARM64 with kernel
6.12.85-1 (2026-04-30) — fix planned in v5.12.1.
2026-05-06 23:32:03 +03:00
Ivan Bondarev
658c78cf93
v5.11.5: hotfix bundle — manage regen multi-arg + apt strict mode + S3/S4 FAQ (#73)
Three small fixes bundled in one patch release after v5.11.4:

1. manage regen multi-arg (Issue #70 from @Barmem)
   `manage regen c1 c2 c3` used to read only the first argument; the rest
   were silently dropped. add and remove already iterated `for x in
   "${ARGS[@]}"`, regen was the outlier. Now consistent across all three
   commands. Behaviour:
   - `manage regen c1 c2 c3` regenerates all three (was: only c1)
   - `manage regen <single>` unchanged
   - `manage regen` (no args) still falls back to "all clients"
   - missing/invalid name gives warn + rc=1, valid names continue
   - final summary "Processed: N of M"

2. apt strict mode at step 2 with PPA-Amnezia carve-out (PR #69 review)
   v5.11.4 downgraded `apt_update_tolerant` rc!=0 to log_warn at step 2
   so a brief Launchpad PPA outage (issue #68) would not break installs.
   Side effect: real apt errors (DNS / GPG / dpkg lock on the base
   mirror) were also masked — install continued on a stale apt-cache and
   failed later with a less actionable error. Fix: add a
   `--ppa-amnezia-tolerant` flag to apt_update_tolerant. The function
   filters out errors whose Err: lines match
   `ppa\.launchpadcontent\.net.*amnezia` only when the flag is set, so
   only step 2 opts in. raw_had_non_src_errors guard prevents silent
   crash / OOM cases (where output mentions PPA but no E:/Err:/W: lines
   were classified) from being swallowed.

3. ADVANCED FAQ: AWG 2.0 vs AWG 1.0 (S3/S4) compatibility
   New entry pointing at upstream issue
   amnezia-vpn/amneziawg-linux-kernel-module#168. Documents that
   S3>0/S4>0 (always generated by my installer) blocks handshake with
   AWG 1.0 clients. Out-of-scope concern in the standard install
   scenario (Amnezia VPN client + manage-generated client .conf), but
   surfaces under hand-edited configs / WireGuard 1.x clients /
   migrating from AWG 1.x setups.

Bundled mechanical changes:
- SCRIPT_VERSION 5.11.4 → 5.11.5 in 6 scripts
- Header date 2026-05-04 → 2026-05-05
- COMMON_SCRIPT_SHA256 / MANAGE_SCRIPT_SHA256 recomputed in both
  installers (RU pins → RU helpers; EN pins → EN helpers)

Tests: +13 new bats in tests/test_v5115_regen_multiarg.bats.
Total 325/325 PASS (was 312 in v5.11.4).
bash -n + shellcheck -S warning clean across all 6 scripts.

Refs MyAI-vnlg (Issue #70 fix), MyAI-ae13.9 (PR #69 P2 review fix),
MyAI-6x3m (S3/S4 FAQ entry).

Co-authored-by: Ivan Bondarev <ndt.ltd.pvl@gmail.com>
2026-05-05 18:29:10 +03:00
Ivan Bondarev
b918dbd8fa
v5.11.4: hotfix — vpn:// psk_key + PPA outage retry (#69)
Hotfix patch addressing two reported user bugs after v5.11.3.

Issue #67 (haritos90): generate_vpn_uri did not include psk_key field
in inner JSON, so AmneziaVPN imports of vpn:// from manage add --psk
clients silently dropped the PSK and the handshake failed. PSK was
correctly written to server [Peer] and client .conf since v5.11.1 —
only the vpn:// envelope was missing the field. Also tightened CR /
trailing-whitespace stripping for PresharedKey = and AllowedIPs = so
CRLF configs don't leak \r into the JSON.

Issue #68 (saligin / baikov): apt-get update returns 0 tolerantly
even when the PPA InRelease did not download, so a brief Launchpad
outage made apt-get install fail with "Unable to locate package".
New apt_wait_for_ppa_package helper polls apt-cache for the canonical
PPA package across 3 attempts with 30s and 60s backoff. On final
failure: friendly message pointing at issue #68.

Tests: +16 new bats (312 total). bash -n + shellcheck clean. VPS
test on Debian 13 trixie passed both fixes.
2026-05-04 12:24:53 +03:00
Ivan Bondarev
d5c85f7339
v5.11.3: UX cleanup patch — manage --yes, backup ms-suffix, ICMP+oper FAQ, --psk highlight (#64)
* feat(v5.11.3): UX cleanup patch

- manage --yes flag + AWG_YES env (skip confirm-prompt for cron/Ansible)
- backup filename ms-suffix (.123.tar.gz) — collision protection on rapid-fire
- ADVANCED FAQ: ICMP in tunnel (RU+EN, D#63)
- oper-to-I1 table extended: Tele2 Krasnoyarsk + Megafon regions (I1=absent fallback)
- README highlight for --psk: cheat sheet entry + Shadowrocket FAQ (I#62)
- release prep: SCRIPT_VERSION 5.11.2 to 5.11.3, dates 2026-04-28, SHA256 pins refreshed, URL bumps in README/ADVANCED, version badge

Tests: +33 bats (294 total, was 261)
- test_yes_flag.bats (+11)
- test_backup_collision.bats (+8)
- test_v5113_docs.bats (+14, Phase 3+4+5 parity)

Backwards-compatible: --yes opt-in, default unchanged. Backup ms-suffix
coexists with legacy filenames. Downgrade to v5.11.2 is safe.

* ci(v5.11.3): silence shellcheck SC2034 in new bats tests

Two false-positives surfaced by CI's stricter shellcheck -x mode:
- test_backup_collision.bats: replace unused loop counter 'i' with '_'
- test_yes_flag.bats: shellcheck-disable SC2034 on CLI_YES/AWG_YES
  assignments — they ARE read inside the eval-extracted confirm_action
  function body, but shellcheck cannot trace through eval

Local 'shellcheck -S warning' on .sh files alone did not catch this
because the .bats files were not in scope. Adding -x and including
the .bats glob in the local pre-commit check now matches CI.

---------

Co-authored-by: Ivan Bondarev <ndt.ltd.pvl@gmail.com>
2026-04-28 16:11:52 +03:00
Ivan Bondarev
9871f73fad feat(v5.11.2): vpn:// QR for one-tap Amnezia VPN app import
Add `<name>.vpnuri.png` - a second per-client QR code rendered from the
existing vpn:// URI - so users can one-tap-import clients into the flagship
Amnezia VPN app (Android/iOS/Desktop). The existing `<name>.png` (scan of
`.conf`) is unchanged and keeps working with WireGuard-compatible clients
(AmneziaWG Windows, wireguard-apple, wg-quick).

Code:
- awg_common.sh + _en.sh: new generate_qr_vpnuri helper with atomic write
  (tmp.\$\$ in same dir -> qrencode -> chmod 600 -> mv -f) so a failed
  encode never leaves a truncated .vpnuri.png behind.
- generate_client: hook after successful generate_vpn_uri (skipped if URI
  failed - no source to encode). generate_client and regenerate_client now
  both log_warn on URI failure for consistency.
- manage_amneziawg.sh + _en.sh: regen refreshes both QR codes together;
  remove cleans up .vpnuri.png alongside .conf/.png/.vpnuri.
- Updated lock-scope comment in regenerate_client to cover .vpnuri.png
  and document the known stale-artefact window against concurrent remove
  (peer already out of server config, user resolves on next regen/remove).

Tests:
- tests/test_qr_vpnuri.bats (+10 bats, 261 total, up from 251 on v5.11.1):
  happy path, missing .vpnuri -> error, qrencode non-zero exit -> error,
  atomic-write (stale PNG preserved on failure, no orphan .tmp.*),
  chmod 600 on Linux/Darwin, RU/EN structural parity of the helper,
  hooks in generate_client / regenerate_client / manage regen / remove.

Docs:
- ADVANCED.md + _en.md: section 'vpn:// URI import' updated with two
  import flows (QR scan for Amnezia VPN app + URI paste), callout about
  the two QR formats targeting different client families, migration note
  (manage regen <name> for pre-5.11.2 clients).
- CHANGELOG bilingual RU+EN entry [5.11.2] - 2026-04-24.

Release prep:
- SCRIPT_VERSION 5.11.2 and header date 2026-04-24 across all 6 scripts.
- COMMON_SCRIPT_SHA256 / MANAGE_SCRIPT_SHA256 in install_amneziawg{,_en}.sh
  refreshed to match the updated helpers.
- README.md + README.en.md: quick-install URLs v5.11.1 -> v5.11.2 and
  version badge updated.

Backup/restore already picks up `.vpnuri.png` via the existing *.png
glob in _backup_configs_nolock and chmod 600 *.png in restore_backup -
no changes needed there. Existing clients get the new .vpnuri.png after
one `manage regen <name>`; downgrading to v5.11.1 is safe (stale
.vpnuri.png just sits in /root/awg/ and is ignored).
2026-04-24 22:42:44 +03:00
Ivan Bondarev
e9dbd5228b fix(v5.11.1): plug CLIENT_PSK leak on regen sed error paths
Final pre-tag code audit flagged 3 return paths in regenerate_client
that skipped `unset CLIENT_PSK` after the preservation code already
exported it. If any of the three post-render sed -i calls (DNS,
PersistentKeepalive, AllowedIPs) failed, CLIENT_PSK stayed exported
in the calling shell and could bleed into the next operation.

Added `unset CLIENT_PSK` to each of the three error branches in
awg_common.sh and awg_common_en.sh.

COMMON_SCRIPT_SHA256 pins in both installers recomputed to match the
updated awg_common.sh / awg_common_en.sh.

Local validation: bash -n 6/6, shellcheck -x 0 warn, bats 251/251
PASS, SHA256 cross-check matches. No behaviour change on success
paths — only tightens the failure-mode hygiene.
2026-04-23 21:57:03 +03:00
Ivan Bondarev
7687ecae1c fix(v5.11.1): regenerate_client preserves existing PresharedKey
During Phase E VPS testing on Debian 13, `manage regen pskclient`
rebuilt the client .conf from scratch via render_client_config, which
reads CLIENT_PSK env. The env was unset, so the new conf landed
without PresharedKey while the server [Peer] still held it — the
handshake would fail on next connect.

regenerate_client now reads the existing PresharedKey from the client
.conf the same way it already preserves DNS / PersistentKeepalive /
AllowedIPs, exports it as CLIENT_PSK, and unsets it on exit to keep
it from leaking into later operations. Behaviour:

  - Client with existing PSK → regen keeps the same PSK in the rewritten
    .conf, server peer untouched, handshake continues working.
  - Client without PSK → regen does not introduce a PSK. The check is
    `[[ -n "$_psk" ]] && export ... || unset ...`.

Also recomputed COMMON_SCRIPT_SHA256 pins in both installers.

tests/test_psk_flag.bats (+2, skipped on non-Linux where flock is
unavailable):
  - regenerate_client preserves PresharedKey through regen.
  - regenerate_client: client without PSK stays without PSK.

Total 251 bats (was 249). bash -n 6/6, shellcheck -x clean.
2026-04-23 21:48:03 +03:00
Ivan Bondarev
8df9a52fd8 chore(v5.11.1): release prep — version 5.11.1, CHANGELOG, docs, URL bump, SHA pins
Bumps SCRIPT_VERSION 5.11.0 → 5.11.1 across all six scripts. Header
dates updated to 2026-04-23.

CHANGELOG.md + CHANGELOG.en.md
  Add [5.11.1] — 2026-04-23 entry (RU + EN parity). Impact-first intro
  explains this is a small UX patch for manual/non-installer setups,
  with explicit @Akh-commits credit tied to Issue #51. Three sections
  (Fixed/Added, Tests, Breaking changes).

install_amneziawg.sh + install_amneziawg_en.sh
  COMMON_SCRIPT_SHA256 / MANAGE_SCRIPT_SHA256 recomputed against the
  final helper files. AWG_BRANCH still defaults to v${SCRIPT_VERSION},
  so the installer will fetch helpers from the v5.11.1 tag once pushed.
  VPS validation can override AWG_BRANCH to v5.11.1/development to
  download from the branch pre-tag; SHA check skips when AWG_BRANCH
  does not match v${SCRIPT_VERSION}.

README.md + README.en.md + ADVANCED.md + ADVANCED.en.md
  36 URL references bumped /v5.11.0/ → /v5.11.1/. Version badge 5.11.0
  → 5.11.1. Added `--psk` flag documentation in `manage CLI` section
  (both languages) — describes behaviour, batch semantics, default-off.

awg_common.sh / awg_common_en.sh / manage_amneziawg.sh / manage_amneziawg_en.sh
  Version + date headers aligned to 5.11.1 / 2026-04-23.

Local validation: bash -n 6/6, shellcheck -x 0 warnings, bats 249/249
PASS, SHA256 cross-check matches, RU/EN parity preserved, zero AI
mentions across code + docs.

VPS test + final tag push in the next step.
2026-04-23 21:33:22 +03:00
Ivan Bondarev
fa83548609 fix(v5.11.1 Phase A): awk accepts indented PrivateKey + CLIENT_PSK hygiene
Follow-up to 12905e1. Independent code review flagged 1 blocker + 1 nit;
both addressed.

Q1 — blocker: awk pattern in _ensure_server_public_key required
PrivateKey at column 0. Hand-edited awg0.conf files may indent
key/value lines (tabs or spaces) — those were silently skipped,
resulting in an empty private key piped into `awg pubkey` and a
garbage reconstruction. Pattern widened to ^[ \t]*PrivateKey,
matching sub() updated. Also applies to the EN mirror.

Q2 — nit: CLIENT_PSK was left exported in the shell after the `add`
command completed. `unset CLIENT_PSK` added at the tail of the add
case block in both RU and EN managers so subsequent operations in
the same shell do not see a stale value.

tests/test_server_pubkey_autogen.bats (+1) — regression guard for Q1:
hand-edited indented awg0.conf is reconstructed correctly.

bats: 249 PASS (was 248, +1). shellcheck -x clean on all four files.
2026-04-23 21:24:12 +03:00
Ivan Bondarev
12905e1972 feat(v5.11.1): manage standalone UX — pubkey auto-gen, endpoint fallback, --psk
Closes the 3 remaining code items from MyAI-bylf (Issue #51 live test by
@Akh-commits, 2026-04-22). The two docs items from that list shipped in
v5.11.0; these three are the code changes that let manage work on hand-
crafted installs where /root/awg is not populated by the installer.

awg_common.sh + awg_common_en.sh

  _ensure_server_public_key (new helper)
    If $AWG_DIR/server_public.key is missing, reconstruct it from the
    [Interface] PrivateKey in awg0.conf via `awg pubkey`. Idempotent —
    returns early when the cache is already present. Called from
    generate_client, regenerate_client, generate_vpn_uri so every
    manage path that needs the server pubkey now works out of the box
    on manual installs (amneziawg-go userspace in LXC and similar).

  _try_local_ip (new helper) + endpoint chain extension
    When curl to ifconfig.me / ipify / icanhazip / ipinfo fails (LXC
    without egress, outbound firewall) we now fall back to the first
    non-loopback IPv4 on a global-scope interface via `ip -4 -o addr`.
    Emits log_warn noting that the IP may be a NAT address the user
    needs to hand-edit. New chain: arg → AWG_ENDPOINT → curl → local.

  render_client_config + add_peer_to_server + generate_client
    CLIENT_PSK env contract: "auto" → fresh `awg genpsk` per client;
    any other non-empty value → used as-is; empty/unset → no PSK (the
    default pre-v5.11.1 behaviour). The PSK is written to the server
    [Peer] AND the client [Peer] in matching order so authentication
    succeeds on both sides.

manage_amneziawg.sh + manage_amneziawg_en.sh

  --psk flag (add command only)
    Exports CLIENT_PSK="auto", which generate_client resolves to a
    fresh 32-byte PSK. In batch mode (add client1 client2 --psk) each
    client gets its own distinct PSK. Help message updated.

tests/*.bats (+18)

  test_server_pubkey_autogen.bats (+6) — no-op when cache present,
    reconstruct when missing, fail clean on missing SERVER_CONF_FILE
    or missing PrivateKey, ignore PrivateKey in [Peer] blocks, RU/EN
    parity.
  test_endpoint_fallback.bats (+5) — returns IPv4 on global-scope iface,
    fails when no global scope, skips loopback, picks first of many
    ifaces, RU/EN parity.
  test_psk_flag.bats (+7) — PresharedKey absent when CLIENT_PSK unset
    for both client conf and server [Peer], written when set, ordered
    correctly inside [Peer] blocks, auto-resolution via stubbed awg
    genpsk in generate_client, manage help mentions --psk, flag parsing
    mirror in RU/EN.

Total 248 PASS (was 230 on v5.11.0). bash -n 6/6, shellcheck -x 0 warn,
RU/EN parity preserved: install 50/50, manage 18/18, awg_common 31/31
(+2 new helpers).
2026-04-23 21:10:42 +03:00
Ivan Bondarev
cc65407341 chore(v5.11): release prep — version 5.11.0, CHANGELOG, docs URL bump, SHA256 pins
Bumps SCRIPT_VERSION 5.10.2 (and dangling 5.11.0-dev / 5.10.0 / 5.10.1) to
5.11.0 across all six scripts. Header dates updated to 2026-04-22.

CHANGELOG.md + CHANGELOG.en.md
  Add [5.11.0] — 2026-04-22 entry. Impact-first intro explaining the
  robustness theme. Sections per surface: installer (A1.2/A1.3/A4.1),
  manage/backup (A1.1/A5.1/A5.2/A5.3 + post-audit Q1-Q5), CI (A7.2),
  docs (minimum awg0.conf, #_Name marker, LXC userspace section).
  Tests section: +84 bats (230 total).

install_amneziawg.sh + install_amneziawg_en.sh
  SCRIPT_VERSION -> 5.11.0. COMMON_SCRIPT_SHA256 / MANAGE_SCRIPT_SHA256
  recomputed against the final helper files. AWG_BRANCH defaults to
  v${SCRIPT_VERSION}, so the installer will fetch helpers from the
  v5.11.0 tag once pushed; VPS validation can override AWG_BRANCH to
  v5.11/development to download from the branch before tag (SHA check
  is skipped when AWG_BRANCH is overridden, per existing design).

README.md + README.en.md + ADVANCED.md + ADVANCED.en.md
  32 URL references bumped /v5.10.2/ -> /v5.11.0/. Version badge
  bumped 5.10.2 -> 5.11.0.

awg_common.sh / awg_common_en.sh / manage_amneziawg.sh / manage_amneziawg_en.sh
  Version + date headers aligned to 5.11.0 / 2026-04-22.

Local validation: bash -n 6/6, shellcheck 0 warnings, RU/EN parity
50/18/29, bats 230/230 PASS, SHA256 cross-check matches.

VPS test + final tag push in Session D.
2026-04-22 16:12:07 +03:00
Ivan Bondarev
d5a91b194c fix(v5.11 Phase 2): address round 1 audit findings
Follow-up to the previous Phase 2 commit. Independent review flagged
3 issues + 2 nits; all addressed.

Q1 — trap RETURN global lifetime (issue)
  bash `trap ... RETURN` persists for the whole shell, not just the
  function. Without cleanup any later return in the same shell would
  re-fire _restore_cleanup with stale locals. `trap - RETURN` is now
  the second statement inside the handler (after `local _rc=$?`, which
  must come first to preserve $? — see inline comment).

Q2 — rollback trigger gate (nit)
  Rollback was firing even on errors between _backup_configs_nolock
  and systemctl stop, where the system had not been modified yet. New
  `_destructive_ops_started` flag goes 0→1 immediately after systemctl
  stop; cleanup now gates on both flags before re-extracting snapshot.

Q3 — expiry/ and cron classification (issue)
  expiry/ timestamps are Unix epoch values with no other source of
  truth; awg-expiry cron drives enforcement. Both upgraded from
  optional (log_warn) to critical (log_error + return 1) in
  _backup_configs_nolock.

Q4 — QR/URI lock scope (nit)
  Added documentation comment on regenerate_client explaining that
  generate_qr / generate_vpn_uri are called outside the config lock
  as best-effort derived artifacts — intentional tradeoff for lock
  duration vs derived-state freshness.

Q5 — modify_client flock timeout path (issue)
  flock -w 10 timeout branch leaked modify_lock_fd (return 1 without
  closing). Added `exec {modify_lock_fd}>&-` before return. All other
  error paths already release correctly.

Tests: +10 (Q1×2, Q2×2, Q3×4, Q5×2). Total 215 PASS (was 205).
All 6 scripts clean: bash -n, shellcheck 0 warnings, RU/EN parity.

Round 3 review verdict: GO for Phase 5.
2026-04-22 16:11:39 +03:00
Ivan Bondarev
8fb150163e feat(v5.11): manage/backup robustness (Phase 2 — MyAI-vsd1)
Closes audit findings A1.1, A5.1, A5.2, A5.3.

_backup_configs_nolock (A1.1):
  - critical cp failures now return 1 (was silent '|| true')
  - compgen -G pre-check distinguishes missing globs from cp errors
  - optional artifacts (QR/URI/expiry/cron) keep log_warn on failure
  - sets LAST_BACKUP_PATH on success for rollback snapshot capture

restore_backup (A5.1):
  - new module-scope helper _restore_do_rollback extracts snapshot
    back to original paths and attempts systemctl start
  - trap RETURN registered for _restore_cleanup; triggers rollback
    only when _restore_ok=0 and _rollback_snap captured
  - pre-flight validate_awg_config before systemctl start — invalid
    restored configs abort to rollback rather than starting a broken
    service
  - all destructive cp paths now just log_error + return 1; trap
    handles rollback + fd close + tmpdir cleanup

modify_client (A5.2):
  - backup cp failure → log_error + lock release + return 1
    (was log_warn, which let destructive sed -i run anyway)

regenerate_client (A5.3, awg_common.sh):
  - wrapped in .awg_config.lock (10 s timeout) to serialize with
    modify_client / remove and concurrent regens on same name
  - each sed -i (DNS, PersistentKeepalive, AllowedIPs) now checked;
    silent failures previously left conf half-updated
  - lock released before generate_qr / generate_vpn_uri

Tests: +41 (tests/test_manage_robustness.bats +22,
tests/test_restore_rollback.bats +19). Total 205 PASS (was 164).
Fixed Unicode em-dash/arrow in pre-existing test names that bats
could not parse (9 tests that were silently skipped now run).

RU/EN mirror: manage 18/18 functions, awg_common 29/29 functions.
bash -n 6/6, shellcheck 0 warnings.

VPS validation pending (Session D).
2026-04-22 16:11:17 +03:00
Ivan Bondarev
27cfc26b2f
fix: hotfix v5.10.2 — apt_update_tolerant inline + edge case propagation
v5.10.1 содержал критическую регрессию: apt_update_tolerant была определена в awg_common.sh, но вызывалась из install_amneziawg.sh до его скачивания (step 5). Результат: 'command not found' на любом fresh install.

v5.10.2:
- Inline apt_update_tolerant в install RU/EN (удалено из awg_common).
- WARN-1: rc!=0 без source-маркеров → propagate, не маскировать silent crash / OOM / SIGKILL.
- Regex future-proof: Sources([^[:alpha:]]|$).
- Date sync install_amneziawg_en.sh header.
- +9 bats (146 total). +regression guard test_install_defines_apt_tolerant.bats.

VPS live test: Ubuntu 24.04.4 PASS C1-C9. Closes #54.
2026-04-20 10:48:36 +03:00
Ivan Bondarev
811e0dcf1d
fix: apt update tolerant к source-404 mirror'ов без source packages (#47)
Closes #47
2026-04-19 22:58:55 +03:00
Ivan Bondarev
e6765202a9 release: prepare v5.10.0 — mobile presets, audit hardening, docs update
- Bump SCRIPT_VERSION to 5.10.0 in all 6 scripts
- CHANGELOG.md/en: full v5.10.0 entry (preset feature, security fixes, 131 tests)
- ADVANCED.md/en: new Presets section with carrier matrix, updated mobile FAQ
- README.md/en: version pins, mobile optimization feature bullet, SEO alt text
- SECURITY.md: 5.10.x active, 5.9.x security-only
- CONTRIBUTING.md: test baseline 98 → 131
- SHA256 hashes updated for all 4 helper scripts
2026-04-16 23:49:47 +03:00
Ivan Bondarev
cd372bcb6f fix: add protocol boundary checks to validate_awg_config
Validates AWG parameter ranges after backup restore: Jc (1-128),
Jmin/Jmax (0-1280, Jmax >= Jmin), S3 (0-64), S4 (0-32),
H1-H4 range ordering. Adds 8 boundary tests (131 total).
Updates COMMON_SCRIPT_SHA256 in both installers.
2026-04-16 23:23:10 +03:00
Ivan Bondarev
a411b663db fix: step 7 enable persistence, I1 stale leak, modify fd cleanup
step 7: ensure systemctl enable before restart on active service —
previously restart without enable could leave service disabled after
reboot (Codex round 4 MEDIUM)

load_awg_params: unset AWG_I1 before parsing live server conf —
prevents stale I1 from init file leaking when I1 is absent from
awg0.conf (Codex round 4 MEDIUM)

modify_client: move validation before flock acquisition so early
validation returns don't leak the fd. Add explicit fd close on sed
and grep error paths inside the locked section (Codex round 4 LOW)

All fixes in both RU and EN. bash -n 6/6, shellcheck 0 warnings.
2026-04-16 22:10:15 +03:00
Ivan Bondarev
9862d0d626 fix: preset live-server override, step 7 restart, efficiency cleanup
load_awg_params: skip server conf reload when CLI preset/jc/jmin/jmax
overrides are set — previously fresh generate_awg_params values were
silently overwritten by old awg0.conf values on reconfiguration runs

step 7: detect already-active service and restart instead of no-op
enable --now — ensures preset changes reach the live interface

modify_client: close flock fd on function return (was leaked)

H-ranges: replace 4x echo|sed with mapfile (eliminate subshells)

build-arm-deb: single modinfo read for both vermagic and version

All fixes in both RU and EN. bash -n 7/7, shellcheck 0 warnings.
2026-04-16 21:40:37 +03:00
Ivan Bondarev
ab4524d4a8 fix: ARM prebuilt step completion, preset rerun, dead code cleanup
ARM prebuilt: add step completion (log + request_reboot) to the early
return path after dpkg -i. Previously the function returned without
advancing the state machine, causing an infinite step-2 loop on ARM
systems with matching prebuilt packages. (Codex round 2 HIGH)

Preset rerun: regenerate AWG params when --preset, --jc, --jmin, or
--jmax CLI flags are present, even if awgsetup_cfg.init already has
AWG_Jc set. Previously these overrides were silently ignored on
rerun/reconfiguration. (Codex round 2 MEDIUM)

Dead code: remove unused AWG_COMMON_VERSION constant from
awg_common.sh and awg_common_en.sh.

Cleanup: remove no-op sed on ALLOWED_IPS in config save
(escaped commas never existed in practice).

All fixes in both RU and EN. bash -n 4/4, shellcheck 0 warnings.
2026-04-16 21:13:59 +03:00
Ivan Bondarev
233c7de825 feat: add --preset=mobile and --jc/--jmin/--jmax CLI flags
New --preset flag for obfuscation parameter presets:
- default: Jc 3-6, Jmin 40-89, Jmax Jmin+50..250 (unchanged)
- mobile: Jc=3 (fixed), Jmin 30-50, Jmax Jmin+20..80
  Optimized for mobile carriers (Tele2, Yota, Megafon, Tattelecom)
  where Jc>3 and Jmax>300 cause connection failures (Issue #42,
  Discussion #38: alkorrnd Jc=3 >95% success on Tele2)

New individual override flags (on top of any preset):
- --jc=N (1-128), --jmin=N (0-1280), --jmax=N (0-1280, >= Jmin)

Validation: Jc range, junk size limits, Jmax >= Jmin sanity check.
AWG_PRESET saved to init config for diagnostics.

18 new bats tests: default/mobile preset ranges, CLI overrides,
validation (reject bad values, unknown preset, Jmax<Jmin).
123 bats total, shellcheck 0 warnings, function parity 53/53 + 29/29.
2026-04-16 20:56:08 +03:00
Ivan Bondarev
5a989be86d fix: normalization cleanup, xz logging, endpoint tests
remove_peer_from_server: clean up temp file on cat -s normalization
failure instead of leaving it behind (awg_common RU/EN)

build-arm-deb.sh: log whether xz compression succeeded or was skipped
(previously silent || true hid the outcome)

tests: 3 new endpoint validation cases — full-length bracketed IPv6,
single-label hostname acceptance, empty brackets rejection.
105 bats total, shellcheck 0 warnings.
2026-04-16 20:28:06 +03:00
Ivan Bondarev
2851a71ae7 fix: backup accuracy, CIDR normalization, config validation, preflight
Uninstall backup: only log "backup created" when both tar and chmod
succeed — previously always logged success even on failure

validate_cidr_list: strip CR and normalize tabs before parsing CIDR
input (consistency with safe_load_config CRLF fix)

validate_awg_config: check value format, not just presence — integers
for Jc/Jmin/Jmax/S1-S4, MIN-MAX ranges for H1-H4. Catches corrupt
or malformed values that previously passed silently.

build-arm-deb.sh: add modinfo, sha256sum, awk, xz to preflight checks

All fixes applied to both RU and EN versions.
bash -n 5/5, shellcheck 0 warnings, 101/102 bats pass
(1 pre-existing flaky: rpi-headers dpkg on non-ARM dev env).
2026-04-16 20:08:27 +03:00
Ivan Bondarev
aa267ba7d4 fix: root check, cron quoting, modify validation, build-arm-deb comment
Root check: move to top of initialize_setup() before any filesystem
mutations — clearer error message on non-root invocation

Cron paths: quote AWG_DIR, CONFIG_FILE, SERVER_CONF_FILE in the
expiry cron job to prevent breakage if paths contain spaces

modify_client: validate DNS (IP charset), PersistentKeepalive (0-65535),
Endpoint and AllowedIPs (reject newlines, quotes, backslashes) before
writing to config — blocks typos and injection

build-arm-deb.sh: correct comment (default is upstream HEAD, not latest
tag)

All fixes applied to both RU and EN versions.
bash -n 7/7, shellcheck 0 warnings, 101/102 bats pass
(1 pre-existing flaky: rpi-headers dpkg on non-ARM dev env).
2026-04-16 19:51:55 +03:00
Ivan Bondarev
67dbd4dc56 fix: defense-in-depth hardening (7 audit findings)
secure_files: harden *.vpnuri alongside *.conf/*.key/*.png

set_client_expiry: validate client name before writing to expiry dir

restore_backup: harden *.png and *.vpnuri permissions after restore
(previously only *.conf and keys were re-hardened)

step3 modinfo: fail hard when vermagic cannot be read instead of
degrading to a warning — module state is not trustworthy at that point

build-arm-deb.sh:
- Detect kernel version from /lib/modules/*/build dir presence
  instead of bare ls|head (correct on multi-kernel containers)
- Assert kernel build directory exists before invoking make
- Fail on empty MODULE_VER instead of producing a bad .deb version

All fixes applied to both RU and EN script versions.
bash -n 7/7, shellcheck 0 warnings, 102/102 bats pass.
2026-04-16 15:58:11 +03:00
Ivan Bondarev
1511fc6d05 fix: harden config parser and client regen against edge cases
safe_load_config / safe_read_config_key:
- Strip UTF-8 BOM from first line of config files
- Strip trailing CR (\r) from every line (Windows line endings)
- Fixes silent value corruption when init file is edited on Windows
  and transferred back via SCP

regenerate_client:
- Escape &, \, / in sed replacement strings when restoring
  user-modified DNS, PersistentKeepalive, and AllowedIPs values
- Prevents config corruption if values contain sed metacharacters

All fixes applied to both RU and EN script versions.
4 new bats tests: CRLF, BOM, BOM+CRLF, value with equals sign.
102/102 bats pass, 0 shellcheck warnings.
2026-04-16 14:38:33 +03:00
Ivan Bondarev
e7ee0face4 release: v5.8.3 — mobile DPI fix, security hardening, integrity checks
Batch release covering the 4 preceding commits. Version bumped from
5.8.2 to 5.8.3 in all scripts, badges, and pinned install URLs.
CHANGELOG entries added for both languages with cross-reference to
the v5.8.0 main release notes.

SHA256 hashes substituted for the RELEASE_PLACEHOLDER constants in
install_amneziawg.sh and install_amneziawg_en.sh. The integrity
check added in 5cc24ea is now fully active — step 5 verifies the
downloaded awg_common.sh and manage_amneziawg.sh against hardcoded
hashes before sourcing them. Hashes computed from the finalized
files in this commit:

  RU awg_common.sh      70e0281c2192f43377e7dda7ef2766591ab9c3e7c0600c4758096bf6ddb67955
  RU manage_amneziawg.sh cb7994c811164708c71b2ce8027a91ab5af80632005d8a668913f1b9a50424d9
  EN awg_common_en.sh    ce5d9e5fb7ef3e9a056b68aa71b3f6d35dc18e32fc9f1595b88cb1e9e3cdfefe
  EN manage_amneziawg_en.sh d3481f60aff8e49a864f5a48b4e2ae8d3ed68c9a5236941d4020c57d4eb06d60

Summary of the 4 commits bundled into this release:

  33b1a43 fix: lower Jmax range for mobile carrier compatibility (#42, #38)
  3068e3e test: add bats coverage for restore_backup tar validation and key hardening
  5cc24ea feat: SHA256 integrity verification of downloaded scripts in step 5
  f40300b fix: tar pre-extraction validation in restore_backup (defense-in-depth)

Live-tested on a clean Ubuntu 24.04 LTS: all 13 validation checks
passed, including three path-traversal / symlink attack variants
rejected by restore_backup, SHA256 verification accepting the correct
hash and rejecting an incorrect one, and UFW routing cleanup on
uninstall.

Tests: 85/85 bats pass, 0 shellcheck warnings, syntax clean.
2026-04-11 02:12:13 +03:00
Ivan Bondarev
b11b9ca812 fix: Hetzner VNC, lower Jc for mobile, UFW route cleanup (#41, #38)
v5.8.2 patch release addressing three user-reported issues and one
bug found during our live testing.

rp_filter 1 → 2 (Discussion #41, z036)

z036 discovered that rp_filter=1 (strict) in our sysctl hardening
breaks routing on Hetzner and similar cloud hosters where the gateway
is in a different subnet than the VPS IP — reply packets fail the
strict reverse-path check. Symptoms: VPS periodically losing network
(once a day) and VNC console flooded with "[UFW BLOCK]" kernel
messages from Fail2Ban. Fix: rp_filter set to 2 (loose mode), which
validates source IP against any route in the table, not same-interface
only. This is the standard recommendation for VPN servers and cloud
environments. Also added kernel.printk = 3 4 1 3 to suppress
non-critical kernel messages on the VNC console.

Default Jc lowered 4-8 → 3-6 (Discussion #38, elvaleto)

After the v5.8.0 H1-H4 randomization fix, elvaleto reported that
mobile networks (Tattelecom/Letai LTE) still needed multiple attempts
to connect. Lowering Jc from the default range to 3 and I1 to <r 64>
fixed it immediately. Cellular networks have smaller MTU, higher
packet loss, and DPI closer to the subscriber — fewer junk packets
reduce handshake overhead. Changed the default Jc range from 4-8
to 3-6 as a compromise between obfuscation and mobile compatibility.
I1 range unchanged (32-256) but documented as a tunable in ADVANCED
FAQ.

UFW route delete with full rule match (MyAI-enu)

During live testing we found that ufw route delete allow in on awg0
(without "out on <nic>") fails to match the installed rule which was
created as ufw route allow in on awg0 out on <nic>. UFW requires
exact text match for delete. Fix: detect the main NIC at uninstall
time and pass the full "out on <nic>" to the delete command, with
a fallback bare delete for backwards compatibility.

Documentation

- ADVANCED.md/en parameter table: Jc range updated to 3-6
- ADVANCED.md/en FAQ: 2 new entries — mobile Jc/I1 recommendation
  (Discussion #38) and VNC/Hetzner rp_filter workaround (Discussion
  #41)

Version bumps and pinning: all scripts, badges, install URLs updated
from v5.8.1 to v5.8.2. CHANGELOG entries added for both languages.
Comparator link v5.8.1...v5.8.2.

Tests: 81/81 bats pass, 0 shellcheck warnings, syntax clean.
2026-04-10 20:20:37 +03:00
Ivan Bondarev
ebe43f9b2f fix: limit H1-H4 upper bound to INT32_MAX for windows-client compat (#40)
v5.8.1 hotfix on top of v5.8.0 following Discussion #40 from @z036.

Problem
z036 installed v5.8.0 on a fresh server, imported the generated client
config into amneziawg-windows-client (standalone Windows client, a
wireguard-windows fork with AWG patches), and the built-in config editor
underlined H2, H3 and H4 in red and refused to save edits. Screenshot
showed:

  H1 = 1091610560-1448752137    (both bounds < 2^31-1, not underlined)
  H2 = 1974284210-3471499881    (upper > 2^31-1, underlined)
  H3 = 3487968213-3702152947    (both > 2^31-1, underlined)
  H4 = 3955918242-4036259967    (both > 2^31-1, underlined)

The pattern matches signed-int32 overflow exactly.

Root cause
amnezia-vpn/amneziawg-windows-client ships a config syntax highlighter
at ui/syntax/highlighter.go. The isValidHField() function artificially
caps H1-H4 at [0, 2_147_483_647] = [0, 2^31-1] = INT32_MAX:

    func (s stringSpan) isValidHField() bool {
        ...
        return s.isValidUint(false, 0, 2_147_483_647)
    }

Upstream issue amnezia-vpn/amneziawg-windows-client#85 (open since
2026-02-20, not yet fixed): the maintainer acknowledged the bug and
promised a PR but did not ship it. iPharaon in the same thread pointed
out that the official Amnezia website documents H1-H4 as uint32
(0-4294967295), twice the size of what the client accepts, and that
configs with values above 2^31-1 work fine on the server.

Our v5.8.0 generate_awg_h_ranges produces 8 random uint32 values,
sorts them, and splits into 4 non-overlapping pairs. With a uniform
distribution in [0, 2^32-1] the chance of any single value landing
below 2^31-1 is 50%, and the chance that all 8 values land there
(so all 4 pairs are safe) is roughly 0.4%. In practice ~99.6% of
fresh v5.8.0 installs produced configs that amneziawg-windows-client
rejects. The server (amneziawg-go) accepts the full uint32 and
handshake still works, which is why nobody noticed in live testing —
we were using the cross-platform amnezia-client for verification,
not the standalone Windows client.

Carefully not-a-regression for other clients:
- amneziawg-go (Linux server): ParseUint(..., 10, 32) — full uint32,
  works
- amneziawg-windows kernel driver parser: raw-string pass-through,
  works
- amnezia-client (Qt cross-platform, Android/iOS/Desktop): regex
  ^(\d+)(-\d+)?$ validator only, no numeric cap, works
- amneziawg-windows-client (standalone): isValidUint(0, 2_147_483_647),
  capped — this is the only affected client

Fix
generate_awg_h_ranges now caps values at 2^31-1 by applying a
0x7FFFFFFF bit mask to each uint32 read from /dev/urandom, and the
fallback path uses rand_range 0 2147483647. Four non-overlapping
pairs in [0, 2^31) with a minimum width of 1000 each still give an
astronomically large key space (on the order of 2^90+ distinct
outputs), so the Discussion #38 DPI-fingerprinting protection is
unchanged.

The mask produces no distribution bias: the lower 31 bits of a
uniformly-random uint32 are themselves uniform, and we use all 31.
An alternative would be modulo 2^31, which is also unbiased at
power-of-two, but `& 2^31-1` is cheaper and clearer in bash arithmetic.

Implementation
- install_amneziawg.sh / _en: updated generate_awg_h_ranges (step-0
  inline copy) with the 0x7FFFFFFF mask and the 2147483647 fallback
- awg_common.sh / _en: same fix in the library copy used by manage
  at runtime
- Both copies have a detailed multi-line comment explaining the
  upstream #85 constraint so future maintainers know why this cap
  exists and can lift it once the upstream client is fixed

Tests
- tests/test_h_ranges.bats: the existing "all values within uint32"
  test's upper bound updated from 2^32-1 to 2^31-1, with a comment
  explaining the upstream cap
- New regression test "none of 20 runs exceeds Windows client limit
  (regression #40)": 20 generator runs × 8 values = 160 samples, every
  one asserted ≤ 2147483647. This is a stable signal against accidental
  regressions in the mask or fallback path
- bats tests/: 81/81 PASS (80 in v5.8.0 + 1 new regression case)
- shellcheck: 0 warnings
- syntax: clean across all 6 scripts

Documentation
- ADVANCED.md / _en FAQ: new entry "amneziawg-windows-client underlines
  H2-H4 in red" with root-cause explanation, upstream #85 link,
  Discussion #40 link, and three workaround options (upgrade to v5.8.1,
  manual edit to values < 2147483647, or switch to amnezia-client)
- CHANGELOG.md / _en: [5.8.1] entry with Fixed / Compatibility / Tests /
  Documentation sections, comparator link v5.8.0...v5.8.1, and a
  cross-reference note pointing readers at the v5.8.0 release notes
  for the main 5.8.x branch story

Compatibility
- Existing v5.8.0 installations keep working on the server side —
  amneziawg-go accepts the full uint32 and nothing on the wire changes
- amneziawg-windows-client users on v5.8.0 should upgrade via
  --uninstall + reinstall with v5.8.1. New H1-H4 values will land
  in the safe half of the range, and the client editor will stop
  complaining
- No breaking changes for the server, existing client .conf files,
  manage commands, or any other code path

Version bumps
- SCRIPT_VERSION 5.8.0 → 5.8.1 in install/manage RU+EN (4 files)
- AWG_COMMON_VERSION 5.8.0 → 5.8.1 in awg_common RU+EN (2 files)
- README badge 5.8.0 → 5.8.1 (RU+EN)
- All pinned install URLs in README and ADVANCED updated from
  v5.8.0 to v5.8.1 (30 URLs total)
2026-04-09 01:53:42 +03:00
Ivan Bondarev
19c028fb6a fix: revert add_peer_to_server inner flock — caused deadlock with generate_client
Live testing on Debian 13 caught a real regression introduced by the
add_peer_to_server inner flock added in 687e9d6 (round 3 LOW 3 fix).

The reasoning behind that fix was wrong: bash flock is NOT re-entrant
across different file descriptors on the same lock file. The contract
was that generate_client() opens .awg_config.lock on its own fd and
holds an exclusive lock for the duration of IP allocation + add_peer.
The inner flock then tried to open the same file on a NEW fd inside
add_peer_to_server and take an exclusive lock there — which from the
kernel's point of view is a separate process trying to lock the file
that the parent already holds. Result: 5-second timeout, error
"add_peer: failed to acquire config lock within 5s", and the function
returns 1 with the peer not added.

The bug only manifests in real flow where generate_client() actually
holds the lock — bats tests on Linux with flock available passed
because they call add_peer_to_server directly, bypassing
generate_client(). Tests on Git Bash for Windows skipped these via
require_flock so the issue went unnoticed in CI.

Symptom on the Debian 13 VPS test: step 6 of install_amneziawg.sh
failed to create the default clients my_phone and my_laptop:

  ERROR: add_peer: failed to acquire config lock within 5s
  ERROR: Rollback: removing files for 'my_phone'
  WARN: Failed to create client 'my_phone'

The server config was created and the install completed (step 6 marks
default-client failure as non-fatal warn), but the user ended up with
zero clients on the server.

Fix: revert to the original "caller must hold the lock" contract,
but with a much more explicit comment block explaining WHY an inner
flock is impossible here and exactly what the caller must do. The
single existing caller (generate_client) already does it correctly.
A new bats coverage of generate_client end-to-end (instead of testing
add_peer_to_server in isolation) would be a better long-term fix —
tracked in beads MyAI-8an alongside other test coverage gaps.

Re-entrant flock IS possible if a sub-function uses the SAME fd as
the parent (via inheritance), which would require passing the fd
explicitly as an argument. That is a larger refactor and is not
needed because the contract works correctly today.

Tests: 80/80 bats pass after the revert. Removed the require_flock
skip helper from add_peer tests (they no longer need flock since
the function does not call it).
2026-04-08 07:51:46 +03:00
Ivan Bondarev
687e9d6e46 release: rebrand 5.7.13 to 5.8.0 + audit round 3 fixes
Several consecutive audit cycles plus a parallel self-audit produced
enough breaking semantics changes in config handling and error
propagation to justify a minor version bump from 5.7.13 to 5.8.0.

Round 3 findings (this commit, on top of ece5917 and 63f1492):

HIGH 1 - uninstall no longer disables UFW globally

step_uninstall used `ufw --force disable` unconditionally. On a VPS
where UFW was already protecting SSH and other services BEFORE our
script was installed, uninstall would wipe the entire firewall posture.

Fix: step4_setup_firewall now writes a marker
$AWG_DIR/.ufw_enabled_by_installer ONLY when it had to enable UFW
itself (i.e. UFW was inactive before our installer touched it).
step_uninstall checks for the marker - if absent, our rules are
removed but UFW is left active. Backwards compat: existing installs
without the marker get safer-by-default behaviour.

MEDIUM 1 - jail.local is no longer deleted by heuristic

The old code matched any /etc/fail2ban/jail.local containing
`banaction = ufw` and removed the entire file. That heuristic was
too broad - an admin with their own jail.local using `banaction = ufw`
for unrelated jails would lose the file on uninstall.

Fix: removal of jail.local has been dropped entirely. Only our own
jail.d/amneziawg.conf is removed. If a user still has a jail.local
from very old installer versions, it is left for them to handle.

MEDIUM 2 - process-wide install lock

Two concurrent install_amneziawg.sh runs could both read the same
setup_state, race each other on apt-get and dkms, and corrupt
package state. Now initialize_setup() takes `flock -n` on
$AWG_DIR/.install.lock on fd 9 for the entire process lifetime.
A second instance dies with a clear error pointing at the lock file.
fd 9 is fixed and does not collide with update_state() which uses 200.

MEDIUM 3 - --endpoint validation

The --endpoint flag was accepted verbatim, persisted to
awgsetup_cfg.init verbatim, and injected into client.conf as
`Endpoint = ${endpoint}:${port}`. Newlines or quotes in the value
could smuggle extra directives into the generated configs.

Fix: new validate_endpoint() function in install_amneziawg.sh, called
in two places - on CLI parse (die on invalid) and on load from
CONFIG_FILE (log_warn + reset to "" so the installer falls back to
auto-detect). The validator forbids newlines, CR, single/double
quotes, backslash, space, tab, and requires one of three formats:
FQDN / IPv4 / [IPv6].

LOW 1 - manage_mktempdir + chained cleanup

manage_amneziawg.sh used `mktemp -d` directly in backup_configs()
and restore_backup(). The trap _manage_cleanup only invoked
_awg_cleanup which removed files registered via awg_mktemp, not
directories. SIGINT during backup/restore left orphan /tmp/tmp.XXXX.

Fix: new _manage_temp_dirs array and manage_mktempdir() helper.
_manage_cleanup now removes registered dirs first, then chains to
the library cleanup.

LOW 2 - check_server checks awg show exit code

The status command piped `awg show` through process substitution
without checking the exit code. If awg crashed, check could still
report "State OK" if the other (interface, service, port) checks
passed. Now the output is captured and exit code verified.

LOW 3 - add_peer_to_server takes its own inner flock

The function comment said "caller must hold flock", but the function
itself did not take the lock. The single existing caller
(generate_client) held it correctly, but the contract was fragile.
Added an inner `flock -x -w 5` on the same .awg_config.lock - bash
fd inheritance makes this re-entrant when a caller already holds
the same lock.

LOW 4 - check_expired_clients validates client name

`name=$(basename "$efile")` was used in paths and passed to
remove_peer_from_server without validation. Defense-in-depth:
EXPIRY_DIR is root-only so this was not exploitable, but a stray
file with a path-traversal-style name would have been used as-is.
Added the same regex as validate_client_name in manage_amneziawg.sh.

LOW 5 - backup file names no longer contain colons

date +%F_%T produces HH:MM:SS with colons. Works on Linux ext4 but
breaks if the backup is copied to FAT/NTFS. Replaced with
date +%F_%H-%M-%S in three places (uninstall backup, manage backup,
manage modify .bak file).

Tests

- New tests/test_validate_endpoint.bats with 14 cases (5 valid:
  FQDN, subdomain FQDN, IPv4, public IPv4, bracketed IPv6;
  9 reject: empty, newline injection, CR, single quote, double
  quote, backslash, space, tab, unbracketed IPv6).
- tests/test_peer_management.bats: two add_peer tests now require_flock
  because add_peer_to_server takes flock internally now (needed only
  on platforms without flock - Git Bash on Windows).
- Total: 80 bats tests, all PASS. 0 shellcheck warnings, syntax
  clean across all 6 .sh files.

Version bump

Reason: cumulative breaking semantics changes across rounds 1-3
(load_awg_params source-of-truth changed, apply_config error
propagation changed, generate_awg_h_ranges algorithm rewritten,
new install lock semantics, --endpoint validation tightening).
A patch bump (5.7.13) would have understated the scope.

- SCRIPT_VERSION bumped to 5.8.0 in install/manage RU+EN
- AWG_COMMON_VERSION bumped to 5.8.0 in awg_common RU+EN
- README badge bumped to 5.8.0 in README.md/en
- ADVANCED FAQ "as of v5.7.13" -> "as of v5.8.0" in RU+EN
- CHANGELOG.md/en: previous [5.7.13] section rewritten as [5.8.0]
  with the full Security/Fixed/Optimizations/Tests/Documentation
  breakdown of all four rounds. Comparator link updated:
  v5.7.12...v5.8.0.
2026-04-07 23:31:41 +03:00
Ivan Bondarev
63f1492713 fix: propagate apply_config failures + harden restored server keys
Second audit cycle on top of commit ece5917 found three real bugs in
paths the split-brain fix did not touch. All three are caller-side
regressions: apply_config now returns a reliable exit code (post-audit
commit added explicit return 0), but the callers silently ignored it.

HIGH — manage add/remove report success even if apply_config fails

manage_amneziawg.sh:790-798 (add) and :841-849 (remove) called
apply_config unconditionally then logged "Конфигурация применена".
If the apply step failed (timeout, broken awg-quick restart, flock
timeout) the user saw "OK" while the peer was only half-applied:
config file updated, live interface not.

Fix: check apply_config return code, log a specific error pointing
the user at systemctl status, set _cmd_rc=1 so the script exits
non-zero. The AWG_SKIP_APPLY=1 branch is preserved as success
because apply_config returns 0 immediately in that mode.

HIGH — check_expired_clients leaves peers on live interface on apply failure

awg_common.sh:1235-1238 removed expired peers from the server config
and the expiry/ directory, then called apply_config without checking.
If apply_config failed, the peer vanished from state files (so the
next cron run would not try to remove it again) but was still active
on the live interface — a permanent stuck-peer state until the admin
manually restarts the service.

Fix: check apply_config return, log an actionable error, return 1.
Also added an explicit return 0 at the end of the function.

MEDIUM — restore_backup preserves archive mode on server keys

manage_amneziawg.sh:295-297 used `cp -a` to copy server_private.key
and server_public.key back, which preserves the source mode. If the
backup was taken on a machine with broken umask (or the archive was
tampered with), the restored keys would keep the non-600 mode — a
real security degradation on restore.

Fix: explicit `chmod 600` after each `cp -a`, best-effort (2>/dev/null).
Mirrors the existing pattern for per-client keys ten lines above.

The audit also verified:
- Split-brain fix from ece5917: no real issues, caller graph
  consistent with the "live awg0.conf is sole source of truth" semantics.
- Edge cases: generate_awg_h_ranges optimization is safe,
  load_awg_params_from_server_conf handles CRLF and values containing
  '=' correctly.

Files
- manage_amneziawg.sh / _en.sh: apply_config return handling in add
  and remove branches, explicit chmod 600 on restored server keys
- awg_common.sh / _en.sh: apply_config return handling in
  check_expired_clients, explicit return 0
2026-04-07 23:30:08 +03:00
Ivan Bondarev
ece59174c3 fix: prevent split-brain in load_awg_params + atomic export + optimizations (#38)
Post-audit improvements on top of the v5.7.13 fix commit (20ba58d).
The audit identified one HIGH severity bug in the post-#38 code path,
plus self-review found two further improvements.

CRITICAL — split-brain prevention in load_awg_params

The post-#38 load_awg_params silently fell back to safe_load_config
when load_awg_params_from_server_conf returned 1. With a partially
corrupt awg0.conf (e.g. user accidentally removed H4 during a manual
edit) the validation loop saw AWG_* still populated from the init
file and reported success — while the live server actually ran the
new (broken) config. regen / render_client_config would then issue
clients stale J*/S*/H* from the cached init file, producing exactly
the kind of mismatch elvaleto and Klavishnik reported in #38, only
quietly.

Fix: live awg0.conf is now the SOLE source of truth for AWG protocol
parameters whenever the file exists. Atomic-parser failure on a live
config is now an explicit error with multi-line log_error and
return 1 — no fallback. The init file is used for AWG params ONLY
when awg0.conf is missing entirely (bootstrap path of first install).

This is the most important change in this commit — it closes a
regression introduced by the original #38 fix.

Atomic export in load_awg_params_from_server_conf

Self-review: previously the parser exported each AWG_* variable as
soon as it parsed the matching key. On a partially corrupt awg0.conf
that meant AWG_Jc could be updated to a new value while AWG_H4
remained at the stale init value — mixed state in the environment.
The function now accumulates values into local _Jc/_Jmin/.../_H4 and
exports them only after verifying that all 11 required fields were
found. Either everything is exported, or nothing is touched.

Optimization: single /dev/urandom read in generate_awg_h_ranges

Previously the function called rand_range 8 times (8 separate od
subprocess invocations). It now does a single
od -An -N32 -tu4 /dev/urandom read that yields all 8 uint32 values
at once, then parses them with read. Falls back to the per-value
rand_range loop if /dev/urandom is unavailable. ~8x fewer subprocess
invocations on the hot path. Mirrored in install_amneziawg.sh and
both EN scripts.

Explicit return 0 in apply_config success path

The syncconf success path ended with exec apply_fd close and returned
that command exit code implicitly. Added an explicit return 0 so
callers (manage add/remove, check_expired_clients) get a guaranteed
exit code on success. Mirrored in EN.

Tests

Three new bats cases in test_load_awg_params.bats (66 total in the
suite, up from 63):
- split-brain prevention: corrupt server_conf with init present must
  return 1 (regression for the audit finding)
- bootstrap path: server_conf missing must fall back to init and
  return 0 (sanity check that bootstrap still works)
- atomic export: corrupt server_conf must not pollute env with the
  partially parsed values
2026-04-07 23:29:19 +03:00
Ivan Bondarev
20ba58d663 fix: ТСПУ DPI fingerprint mitigation + regen reads live awg0.conf (#38)
Two bugs reported in Discussion #38 by @Klavishnik and @elvaleto: AmneziaWG
2.0 servers installed by this script stopped working over Russian mobile
networks while stock Amnezia containers kept working. Diagnosis from
@elvaleto: changing Jc/S1-S4/H1-H4/I1 manually helped, but `regen` did not
propagate the changes to client configs.

Bug 1 — static H1-H4 ranges fingerprinted by Russian DPI

`generate_awg_params` randomized Jc, Jmin, Jmax, S1-S4 and the I1 byte
count per install, but H1-H4 were hardcoded identically across every
install (100000-800000, 1000000-8000000, 10000000-80000000,
100000000-800000000). These four fixed hash ranges in every handshake
session became a perfect signature for Russian DPI.

Fix: H1-H4 are now generated from 8 random uint32 values (via
/dev/urandom), sorted and split into 4 non-overlapping low-high pairs.
Sorting guarantees low <= high and non-overlap by construction. Each
install gets a unique set with no static signature. Minimum width per
range is 1000.

The new `generate_awg_h_ranges` helper is duplicated in install*.sh (used
in step 0 before awg_common.sh is sourced) and awg_common.sh (for
testability and reuse from manage_amneziawg.sh). `rand_range` is
likewise mirrored — it already supports uint32 via /dev/urandom -tu4 -N4.

Bug 2 — regen used stale parameters from cached init file

`load_awg_params` only read AWG_* from /root/awg/awgsetup_cfg.init, which
is written once at first install. After a user manually edited
/etc/amnezia/amneziawg/awg0.conf and restarted the service, the server
ran with new values but `regen` still read the cached old ones and wrote
them into client configs. Hence @elvaleto's "ниче не дает".

Fix: new `load_awg_params_from_server_conf` parses the [Interface]
section directly from /etc/amnezia/amneziawg/awg0.conf. `load_awg_params`
now loads the init file first (for non-AWG keys: OS_ID, ALLOWED_IPS,
AWG_PORT, AWG_ENDPOINT) then overrides AWG protocol parameters with live
server config values. The server config is the source of truth, as it
should have been from day one.

Files
- install_amneziawg.sh / _en.sh: new generate_awg_h_ranges, replaced
  hardcoded H1-H4 with its output, version bump
- awg_common.sh / _en.sh: new rand_range, generate_awg_h_ranges,
  load_awg_params_from_server_conf; rewritten load_awg_params with
  live-config priority; version bump
- manage_amneziawg.sh / _en.sh: version bump only (picks up the fix
  through awg_common.sh source)
2026-04-07 09:18:03 +03:00
Ivan Bondarev
ae2c969160 chore: sync script dates to 2026-04-06 2026-04-06 09:11:02 +03:00
Ivan Bondarev
be262bd569 fix: Fail2Ban systemd backend for Debian (Discussion #39)
On Debian 12/13 rsyslog is not installed by default. SSH logs go to
systemd-journald, not /var/log/auth.log. Fail2Ban crashed without
the correct backend.

Changes:
- Add python3-systemd package on Debian
- Set backend = systemd for Debian, auto for Ubuntu
- Use unquoted heredoc (JAILEOF) for variable substitution

Bump version to 5.7.12.
2026-04-06 08:54:19 +03:00
Ivan Bondarev
68f954b6ba chore: bump version to 5.7.11 2026-03-31 19:26:39 +03:00
Ivan Bondarev
5b70e87d30 fix: regen Address corruption, grep -P portability, modify cleanup (#31)
Four bugs reported by leon-ha on Debian 12 (mawk + no PCRE grep):

1. regen corrupts Address field: awk \s not supported by mawk.
   Fix: replace \s with [ \t] in awk gsub. Also replace grep -oP
   with sed for PrivateKey extraction in regenerate_client.

2. regen loses modify values: render_client_config used hardcoded
   DNS=1.1.1.1 and PersistentKeepalive=33. Fix: read current values
   from .conf before re-rendering, restore them via sed after.

3. modify leaves .bak files: backup not deleted after success.
   Fix: rm -f "$bak" after successful sed replacement.

4. check reports port not listening: grep -qP fails without PCRE.
   Fix: replace grep -P/-qP with plain grep (space instead of \s)
   in all 6 port-checking locations.

Closes #31.
2026-03-31 19:20:06 +03:00
Ivan Bondarev
f4c614272a docs: sync dates, CHANGELOG, ADVANCED for v5.7.10
- Update script header dates to 2026-03-31
- Add unit tests + SKIP_APPLY log fix to CHANGELOG entries
- Document AWG_SKIP_APPLY and AWG_APPLY_MODE env vars in ADVANCED CLI
2026-03-31 18:51:25 +03:00