diff --git a/ap/nm-no-auto-default.conf b/ap/nm-no-auto-default.conf new file mode 100644 index 0000000..1720ead --- /dev/null +++ b/ap/nm-no-auto-default.conf @@ -0,0 +1,5 @@ +# NetworkManager must never auto-create "Wired connection" DHCP profiles on ports +# that netplan / ./deploy.sh --apply-network has not handed to it yet; otherwise it +# races systemd-networkd's DHCP on the Starlink port. +[main] +no-auto-default=* diff --git a/deploy.sh b/deploy.sh index d84515b..c96e436 100755 --- a/deploy.sh +++ b/deploy.sh @@ -13,9 +13,12 @@ # ./deploy.sh --wan-plan [conf] # what the NetworkManager WAN provisioning would do (read-only, no root) # ./deploy.sh render # preview a templated file on stdout # ./deploy.sh --redact # filter stdin, masking secret values (used by --diff; for tests) -# Idempotent. Netplan (wlan0 + Starlink NIC = NM-managed WANs; eth0 is a LAN -# port on br0) is NOT deployed here — reference copy in ap/50-van-wan.yaml, -# applied once manually (apply flaps uplinks). We do check it for drift below. +# VANLINK_NM_DIR= # test override: where existing NM *.nmconnection files are looked up (default /etc/NetworkManager/system-connections) +# Idempotent. Netplan (the NM-managed WANs; eth0 is a LAN port on br0) is never +# changed by a routine deploy (applying it flaps the uplinks). ENABLE_NM_WAN=0: +# applied manually from the reference copy in ap/50-van-wan.yaml, and checked for +# drift below. ENABLE_NM_WAN=1: a missing/differing WAN netplan is only warned +# about; it is applied by `./deploy.sh --apply-network` alone. set -euo pipefail cd "$(dirname "$(readlink -f "$0")")" @@ -98,6 +101,19 @@ nm_wan_state_netplan() { # missing | differs | matches (the installed WAN netpl else echo differs; fi } +cellular_profile_exists() { # true if an NM profile with id=$CELLULAR_CONN_NAME exists (any file name, or known to nmcli) + local d=${VANLINK_NM_DIR:-/etc/NetworkManager/system-connections} f names + for f in "$d"/*.nmconnection; do + [ -f "$f" ] || continue + if grep -Fx "id=${CELLULAR_CONN_NAME}" "$f" >/dev/null 2>&1; then return 0; fi + done + if command -v nmcli >/dev/null 2>&1; then + names=$(nmcli -g NAME connection show 2>/dev/null) || names= + if printf '%s\n' "$names" | grep -Fx "${CELLULAR_CONN_NAME}" >/dev/null 2>&1; then return 0; fi + fi + return 1 +} + check_rendered() { # check_rendered — fail on a leftover @TOKEN@ local left left=$(grep -oE "$TOKEN_RE" "$2" | sort -u | tr '\n' ' ') || true @@ -315,7 +331,7 @@ case "${1:-}" in if [ -f /etc/netplan/10-dhcp-all-interfaces.yaml ]; then echo " armbian: /etc/netplan/10-dhcp-all-interfaces.yaml present -> would move aside"; else echo " armbian: /etc/netplan/10-dhcp-all-interfaces.yaml absent"; fi if [ -z "${CELLULAR_APN:-}" ]; then echo " cellular profile: skipped (no CELLULAR_APN)" - elif [ -e "/etc/NetworkManager/system-connections/${CELLULAR_CONN_NAME}.nmconnection" ]; then + elif cellular_profile_exists; then echo " cellular profile: ${CELLULAR_CONN_NAME} apn=${CELLULAR_APN} exists" else echo " cellular profile: ${CELLULAR_CONN_NAME} apn=${CELLULAR_APN} would create" @@ -643,6 +659,8 @@ install -D -m0644 power/10-vanlink-watchdog.conf /etc/systemd/system.conf.d/10-v if [ "$ENABLE_NM_WAN" = 1 ]; then echo "== networkmanager WANs ==" + # In place before the package starts NM: no auto-created "Wired connection" profiles. + install -D -m0644 ap/nm-no-auto-default.conf /etc/NetworkManager/conf.d/00-van-no-auto-default.conf if ! dpkg -s network-manager >/dev/null 2>&1; then # Installed only now: the package starts NM, which manages Wi-Fi/gsm devices by # default, so van-ap-unmanaged.conf (AP radios, LAN ports) must already be in place. @@ -650,7 +668,8 @@ if [ "$ENABLE_NM_WAN" = 1 ]; then DEBIAN_FRONTEND=noninteractive apt-get install -y network-manager \ || { echo "deploy: apt-get install network-manager failed" >&2; exit 1; } fi - systemctl enable --now NetworkManager ModemManager >/dev/null 2>&1 || true + systemctl enable --now NetworkManager ModemManager >/dev/null 2>&1 \ + || warn "failed to enable/start NetworkManager/ModemManager (check: systemctl status NetworkManager ModemManager)" nmcli general reload conf 2>/dev/null || true for nm_if in "$WIFI_5G_IFACE" "$WIFI_2G_IFACE" "$LAN_IFACE" ${ONBOARD_LAN_IFACE:-}; do [ -e "/sys/class/net/$nm_if" ] || continue @@ -660,7 +679,7 @@ if [ "$ENABLE_NM_WAN" = 1 ]; then done if [ -n "${CELLULAR_APN:-}" ]; then nm_keyfile="/etc/NetworkManager/system-connections/${CELLULAR_CONN_NAME}.nmconnection" - if [ -e "$nm_keyfile" ]; then + if cellular_profile_exists; then echo " cellular connection ${CELLULAR_CONN_NAME} exists — left unchanged" elif [ -z "${CELLULAR_UUID:-}" ]; then warn "CELLULAR_UUID is empty — not creating the cellular keyfile $nm_keyfile" @@ -670,8 +689,9 @@ if [ "$ENABLE_NM_WAN" = 1 ]; then echo " created cellular connection ${CELLULAR_CONN_NAME} (apn ${CELLULAR_APN}) — no modem needed" fi fi - if [ "$(nm_wan_state_netplan)" != matches ]; then - warn "WAN netplan is not provisioned/up to date: run ./deploy.sh --apply-network (backs up /etc/netplan and rolls back automatically if the uplink does not come back)" + nm_np_state=$(nm_wan_state_netplan) + if [ "$nm_np_state" != matches ]; then + warn "WAN netplan is $nm_np_state (not provisioned/up to date): run ./deploy.sh --apply-network (backs up /etc/netplan and rolls back automatically if the uplink does not come back)" fi fi diff --git a/tests/check-templates.sh b/tests/check-templates.sh index 84c5711..ca66156 100755 --- a/tests/check-templates.sh +++ b/tests/check-templates.sh @@ -242,4 +242,19 @@ u2=$(VANLINK_CONF=hosts/wan.conf ./deploy.sh render ap/cellular.nmconnection | g [ -n "$u1" ] && [ "$u1" != "uuid=" ] && [ "$u1" = "$u2" ] && ok "cellular uuid non-empty and stable across hosts" || bad "cellular uuid: '$u1' vs '$u2'" [ "$(grep -v '^[[:space:]]*#' deploy.sh | grep -c uuidgen)" = 0 ] && ok "deploy.sh does not call uuidgen" || bad "deploy.sh calls uuidgen" +# 17b. an existing cellular profile is found by id, whatever its file name +nmd=$(mktemp -d); nme=$(mktemp -d) +printf '[connection]\nid=Koodo\ntype=gsm\n' > "$nmd/weird-file-name.nmconnection" +p=$(VANLINK_NM_DIR=$nmd VANLINK_CONF=hosts/van.conf ./deploy.sh --wan-plan 2>&1) +echo "$p" | grep -q 'cellular profile: Koodo apn=sp.koodo.com exists' && ok "wan-plan: profile found under another file name" || bad "wan-plan: profile under other file name not found" +if command -v nmcli >/dev/null 2>&1 && nmcli -g NAME connection show 2>/dev/null | grep -Fxq Koodo; then + ok "wan-plan would-create assertion skipped (this machine's NM lists Koodo)" +else + p=$(VANLINK_NM_DIR=$nme VANLINK_CONF=hosts/van.conf ./deploy.sh --wan-plan 2>&1) + echo "$p" | grep -q 'cellular profile: Koodo apn=sp.koodo.com would create' && ok "wan-plan: empty NM dir -> would create" || bad "wan-plan: empty NM dir should say would create" +fi +rm -rf "$nmd" "$nme" +# 17c. NM must not auto-create "Wired connection" profiles +[ "$(grep -v '^[[:space:]]*#' ap/nm-no-auto-default.conf 2>/dev/null | grep -v '^[[:space:]]*$')" = "$(printf '[main]\nno-auto-default=*')" ] && ok "nm-no-auto-default.conf is [main] + no-auto-default=*" || bad "ap/nm-no-auto-default.conf missing or wrong" + exit "$fail"