LibrePortal/containers/wireguard/scripts/wireguard_install_hooks.sh
librelad 8b5e02c760 refactor(storage): resolve every app directory through appDir
The main sweep — ~260 call sites across ~100 files move from string
concatenation on a single root to appDir/storageAppDirs/storageAppConfigs.
On a single-root install the resolved paths are identical, so this is a
no-op until a location is registered.

Enumerators were the interesting half. `for d in "$containers_dir"/*/`
appears in the menus, the registry/artifact scanners and the DNS setup —
and a shell glob cannot list a rootless 751 tree at all, which is the
same bug config_find_file.sh already documents in a comment. Routing them
through storageAppDirs (which enumerates as the owning user) fixes that
alongside the multi-root work.

Three places needed judgement rather than substitution:

db_app_scan.sh deletes database rows and port allocations for apps whose
folder is missing, and reaps "empty" app dirs. With a storage location
unmounted, every app on it looks exactly like that. Each of those
branches now gates on appStorageAvailable first — an app on an unplugged
drive is skipped with a notice, never deleted.

instance_create.sh rewrites cloned hooks so an instance touches its own
directory instead of the base app's. Its sed matched ${containers_dir}<type>,
which this sweep just replaced with $(appDir <type>) — so it would have
silently stopped redirecting, and an instance would have written to the
original's files (the adguard auth adapter case its own comment warns
about). Now matches both appDir forms, verified against bare, quoted,
unrelated-app, legacy and prose cases.

peer_shell/peer_pull streamed and extracted relative to the primary root.
Both now use the app's own root, and peer_shell keeps a single-root
fallback since it runs as a restricted SSH shell with no LibrePortal env.

Also fixes a pre-existing bug found on the way: webui_app_config.sh
tested "$containers_dir/frontend/data/last_update", one level short of the
real tree under the libreportal app dir, so the WebUI refresh trigger
after a config update has never once fired.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 04:09:51 +01:00

104 lines
3.8 KiB
Bash

#!/bin/bash
# WireGuard install hooks — host-conflict guard, tunnel subnet generation,
# WG_HOST resolution, IP forwarding setup, and a post-start restart so
# the firewall rules from wg-easy take effect cleanly.
wireguard_install_pre()
{
local app_name="$1"
((menu_number++))
echo ""
echo "---- $menu_number. Checking if $app_name can be installed."
echo ""
# Host-conflict guard: a host-level WireGuard (e.g. the angristan
# wireguard-install script — marker /etc/wireguard/params) collides
# with this container on the wg kernel module + UDP 51820. Abort.
if [[ -e /etc/wireguard/params ]]; then
isError "WireGuard is already installed on the host — this conflicts with the $app_name app."
isError "Installation is now aborting..."
dockerUninstallApp "$app_name"
return 1
fi
}
wireguard_install_post_compose()
{
local app_name="$1"
((menu_number++))
echo ""
echo "---- $menu_number. Resolving wireguard tunnel subnet (WG_DEFAULT_ADDRESS)"
echo ""
# Generate-once-and-persist tunnel subnet. Second-octet 200-250 is
# outside the docker network generator range (100-149), so the two
# subnets cannot collide. Reusing the persisted value keeps existing
# peer configs valid across reinstalls.
local _wg_compose_for_subnet="$(appDir "$app_name")/docker-compose.yml"
if [[ -z "$CFG_WIREGUARD_SUBNET" ]]; then
local _wg_second=$(( RANDOM % 51 + 200 ))
local _wg_third=$(( RANDOM % 256 ))
CFG_WIREGUARD_SUBNET="10.${_wg_second}.${_wg_third}.0"
updateConfigOption "CFG_WIREGUARD_SUBNET" "$CFG_WIREGUARD_SUBNET"
isSuccessful "Generated wireguard tunnel subnet: $CFG_WIREGUARD_SUBNET"
else
isNotice "Reusing existing wireguard tunnel subnet: $CFG_WIREGUARD_SUBNET"
fi
# wg-easy expects the address pattern `<base>.x` (literal x).
local _wg_addr_pattern="${CFG_WIREGUARD_SUBNET%.0}.x"
tagsManagerUpdateUniversalTag "$_wg_compose_for_subnet" "WIREGUARD_SUBNET_TAG" "$_wg_addr_pattern"
isSuccessful "WG_DEFAULT_ADDRESS set to $_wg_addr_pattern"
((menu_number++))
echo ""
echo "---- $menu_number. Resolving WG_HOST for peer configs"
echo ""
local wg_compose_file="$(appDir "$app_name")/docker-compose.yml"
local wg_host_value=""
local _wg_traefik_installed=0
if declare -f checkServiceInstalled >/dev/null 2>&1 && checkServiceInstalled "traefik"; then
_wg_traefik_installed=1
fi
if [[ -n "$domain_full" && $_wg_traefik_installed -eq 1 ]]; then
wg_host_value="$host_setup"
isNotice "Domain + Traefik present — peer configs will use $wg_host_value"
else
wg_host_value="${public_ip_v4:-127.0.0.1}"
if [[ -n "$domain_full" && $_wg_traefik_installed -eq 0 ]]; then
isNotice "Domain configured but Traefik not installed — falling back to IP $wg_host_value so peer configs actually resolve."
else
isNotice "No domain configured — peer configs will use IP $wg_host_value (LAN only)"
fi
fi
tagsManagerUpdateUniversalTag "$wg_compose_file" "PUBLIC_IP_TAG" "$wg_host_value"
isSuccessful "WG_HOST set to $wg_host_value"
((menu_number++))
echo ""
echo "---- $menu_number. Enabling IP forwarding"
echo ""
# Drop in /etc/sysctl.d/99-libreportal-wireguard.conf + reload — the
# whole thing runs as root through libreportal-appcfg so the manager
# never needs blanket /etc write or `sudo sysctl` itself.
local result
result=$(runAppCfg wireguard-ip-forward)
checkSuccess "Enabling IPv4 IP Forwarding (sysctl drop-in + reload)"
}
wireguard_install_post_start()
{
local app_name="$1"
((menu_number++))
echo ""
echo "---- $menu_number. Restarting $app_name after firewall changes"
echo ""
dockerComposeRestart $app_name
}