#!/bin/bash # Install (or verify) the WAN-follows-VRRP mechanism on a VyOS router. # # This exists because on 2026-09-05 the sim's failover proof was obtained from # scripts that had been hand-`sed`-ed in place: /config/vrrp-wan-health and # -reconcile differed from git by an edited VIP, so the tested behaviour was not # the committed behaviour and any reinstall would have silently reverted it. # `--check` makes that class of drift a hard failure instead of a discovery. # # It installs ONLY the mechanism -- scripts, units, drop-in, settings. It never # touches VyOS configuration: the `interfaces pppoe` node, `vif 53 disable` and # the VRRP sync-group hooks are config and belong in the config model # (labsim/sim-*.py for the sim, infra/vyos/subtrees/overrides.json for # production), not in an installer. # # vrrp-wan-install --vip 192.168.1.1 [--host vyos@10.0.1.253] # vrrp-wan-install --check [--host ...] # exits non-zero on any drift # # With no --host it operates on the local machine, so it can be scp'd to a # router and run there. set -uo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" VIP=""; HOST=""; MODE=install; PW="${VYOS_PW:-vyos}" while [ $# -gt 0 ]; do case "$1" in --vip) VIP="$2"; shift 2 ;; --host) HOST="$2"; shift 2 ;; --check) MODE=check; shift ;; *) echo "usage: $0 [--vip A.B.C.D] [--host user@ip] [--check]" >&2; exit 2 ;; esac done SSH_OPTS=(-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=8 -o PreferredAuthentications=password) run() { # run a command on the target if [ -n "$HOST" ]; then timeout 60 sshpass -p "$PW" ssh "${SSH_OPTS[@]}" "$HOST" "$@" else bash -c "$*"; fi } put() { # copy a file to the target if [ -n "$HOST" ]; then timeout 60 sshpass -p "$PW" scp "${SSH_OPTS[@]}" "$1" "$HOST:$2" >/dev/null else cp "$1" "$2"; fi } # script -> destination. take/release are hooks keepalived calls; both exec the # reconciler, so there is one code path. SCRIPTS="vrrp-wan-reconcile vrrp-wan-apply vrrp-wan-health vrrp-wan-guard vrrp-wan-take vrrp-wan-release" UNITS="vrrp-wan-reconcile.service vrrp-wan-reconcile.timer vrrp-wan-guard.service vrrp-wan-guard.timer" GATE_DIR=/etc/systemd/system/ppp@pppoe0.service.d GATE=$GATE_DIR/10-vrrp-wan-gate.conf if [ "$MODE" = check ]; then rc=0 for f in $SCRIPTS; do local_sum=$(md5sum "$HERE/$f" | cut -d' ' -f1) remote_sum=$(run "md5sum /config/$f 2>/dev/null | cut -d' ' -f1") [ "$local_sum" = "$remote_sum" ] || { echo " DRIFT /config/$f"; rc=1; } done for f in $UNITS; do local_sum=$(md5sum "$HERE/$f" | cut -d' ' -f1) remote_sum=$(run "md5sum /etc/systemd/system/$f 2>/dev/null | cut -d' ' -f1") [ "$local_sum" = "$remote_sum" ] || { echo " DRIFT /etc/systemd/system/$f"; rc=1; } done gate_sum=$(md5sum "$HERE/ppp-vrrp-gate.conf" | cut -d' ' -f1) remote_gate=$(run "md5sum $GATE 2>/dev/null | cut -d' ' -f1") [ "$gate_sum" = "$remote_gate" ] || { echo " DRIFT $GATE (a VyOS upgrade wipes /etc -- both routers would dial)"; rc=1; } run "[ -r /config/vrrp-wan.conf ]" || { echo " MISSING /config/vrrp-wan.conf"; rc=1; } for t in vrrp-wan-reconcile.timer vrrp-wan-guard.timer; do [ "$(run "systemctl is-enabled $t 2>/dev/null")" = enabled ] || { echo " NOT ENABLED $t"; rc=1; } done [ "$rc" -eq 0 ] && echo " vrrp-wan in sync" exit "$rc" fi [ -n "$VIP" ] || { echo "--vip is required to install" >&2; exit 2; } for f in $SCRIPTS; do put "$HERE/$f" "/tmp/$f" # root:vyattacfg 0775 -- vrrp-wan-apply enters config mode, which requires # membership of vyattacfg. run "sudo install -o root -g vyattacfg -m 0775 /tmp/$f /config/$f" done # Settings, with the VIP substituted. One file, read by BOTH the reconciler and # the health check -- keepalived invokes the latter with no environment at all, # so an Environment= line in the unit would be read by one and not the other. sed "s|^VRRP_WAN_VIP=.*|VRRP_WAN_VIP=${VIP}|" "$HERE/vrrp-wan.conf" > /tmp/vrrp-wan.conf.gen put /tmp/vrrp-wan.conf.gen /tmp/vrrp-wan.conf.gen run "sudo install -o root -g vyattacfg -m 0664 /tmp/vrrp-wan.conf.gen /config/vrrp-wan.conf" for f in $UNITS; do put "$HERE/$f" "/tmp/$f" run "sudo install -m 0644 /tmp/$f /etc/systemd/system/$f" done put "$HERE/ppp-vrrp-gate.conf" /tmp/ppp-vrrp-gate.conf run "sudo mkdir -p $GATE_DIR && sudo install -m 0644 /tmp/ppp-vrrp-gate.conf $GATE" run "sudo systemctl daemon-reload && sudo systemctl enable --now vrrp-wan-reconcile.timer vrrp-wan-guard.timer" >/dev/null 2>&1 echo " installed (vip=$VIP)" run "sudo /config/vrrp-wan-reconcile --status"