package main import ( "os" "path/filepath" "time" ) // installDeps installs the AmneziaWG stack and userspace tooling for the host // OS, then records completion in the state file. This step is now fully // decoupled from init-server: it must be run first, and init-server only // verifies the recorded flag rather than installing anything itself. func installDeps() { os := detectOS() info("Installing AmneziaWG dependencies for %s %s...", os.ID, os.Version) switch os.ID { case "ubuntu", "debian", "linuxmint": env := []string{"DEBIAN_FRONTEND=noninteractive"} runOrDie2(env, "apt-get", "update", "-qq") // Prerequisites for adding the PPA and building the kernel module. runOrDie2(env, "apt-get", "install", "-y", "software-properties-common", "python3-launchpadlib", "gnupg2", "linux-headers-"+unameRelease(), "curl") if os.ID == "linuxmint" { // Mint's official docs require "Source code repositories" to be // enabled (Software Sources → Optional Sources) before adding a // PPA; the CLI equivalent is uncommenting the deb-src lines that // Mint ships disabled by default. run("sed", "-i", "s/^# deb-src/deb-src/", "/etc/apt/sources.list.d/official-package-repositories.list") runOrDie2(env, "apt-get", "update", "-qq") } // AmneziaWG kernel module + userspace tools via the official PPA. runOrDie2(env, "add-apt-repository", "-y", "ppa:amnezia/ppa") runOrDie2(env, "apt-get", "update", "-qq") runOrDie2(env, "apt-get", "install", "-y", "amneziawg", "amneziawg-tools", "jq", "qrencode", "nftables") case "alpine": runOrDie("apk", "update") runOrDie("apk", "add", "jq", "libqrencode-tools", "nftables", "curl", "util-linux") buildAmneziawgToolsFromSource() warn("On Alpine the AmneziaWG kernel module may need to be provided") warn("separately (DKMS/akmods or a prebuilt module for your kernel)") } // Record that dependency installation completed so init-server can verify it. saveState(&State{ DepsInstalled: true, InstalledAt: time.Now().UTC().Format(time.RFC3339), OSID: os.ID, OSVersion: os.Version, }) info("Dependencies installed") info("State recorded: %s (deps_installed=true)", stateFile) info("Next step: run 'init-server' to configure and start the server") } // runOrDie2 is runEnv + abort-on-failure (env-aware variant of runOrDie). func runOrDie2(env []string, name string, args ...string) { if err := runEnv(env, name, args...); err != nil { die("%s failed: %v", name, err) } } // buildAmneziawgToolsFromSource builds and installs `awg`/`awg-quick` on an // Alpine host. Unlike Ubuntu/Debian/Mint (served by the official // ppa:amnezia/ppa), amneziawg-tools has no Alpine apk package — the container // image already builds it from source for exactly this reason (see the // tools-builder stage in Dockerfile); this mirrors that same build for a bare // Alpine host. func buildAmneziawgToolsFromSource() { info("No Alpine package exists for amneziawg-tools — building from source...") runOrDie("apk", "add", "git", "build-base", "linux-headers", "bash") dir, err := os.MkdirTemp("", "amneziawg-tools-*") if err != nil { die("Failed to create temp build dir: %v", err) } defer os.RemoveAll(dir) runOrDie("git", "clone", "--depth=1", "https://github.com/amnezia-vpn/amneziawg-tools", dir) src := filepath.Join(dir, "src") runOrDieIn(src, "make") runOrDieIn(src, "make", "install", "WITH_WGQUICK=yes", "WITH_BASHCOMPLETION=no", "WITH_SYSTEMDUNITS=no", "PREFIX=/usr") info("amneziawg-tools built and installed (awg, awg-quick)") }