130 lines
6.6 KiB
Bash
130 lines
6.6 KiB
Bash
#!/bin/sh
|
|
# aurora-vpn (B15): full-tunnel VPN for the Wi-Fi LAN. Two modes (/etc/aurora/vpn.conf VPN_MODE):
|
|
# vless = sing-box VLESS-over-WS-over-TLS via CDN, TUN singtun0 (config /etc/sing-box/config.json)
|
|
# wg = kernel WireGuard wg0 (config /etc/wireguard/wg0.conf)
|
|
# LAN clients are policy-routed into the tunnel; the board itself, USB NCM management and the tunnel's own
|
|
# outbound (to the VPN server/CDN) stay DIRECT on wwan0 (source-based rule -> no loop).
|
|
# Fail-closed: while up, LAN may leave ONLY via the tunnel; if it is down, LAN has no internet (never LTE).
|
|
# aurora-vpn up | down | status. Needs kernel 7.2.7-aurora-b15 (TUN; WireGuard for wg mode).
|
|
TBL=51820; RULE_PRI=100; S=/run/aurora-vpn; mkdir -p $S; LOG=$S/vpn.log
|
|
[ -f /etc/aurora/router.conf ] && . /etc/aurora/router.conf
|
|
[ -f /etc/aurora/vpn.conf ] && . /etc/aurora/vpn.conf
|
|
VPN_MODE=${VPN_MODE:-wg}; LAN_IF=${LAN_IF:-wlan0}; LAN_NET=${LAN_NET:-192.168.77.0/24}; LAN_ADDR=${LAN_ADDR:-192.168.77.1}
|
|
WAN_IF=${WAN_IF:-wwan0}; MGMT_IF=${MGMT_IF:-usb0}; TUN_IF=${TUN_IF:-singtun0}; TUN_MTU=${TUN_MTU:-1400}
|
|
SB=/etc/sing-box/config.json; WGC=/etc/wireguard/wg0.conf
|
|
now() { cut -d' ' -f1 /proc/uptime; }
|
|
log() { m="[$(now)] $*"; echo "$m"; echo "$m" >> $LOG; echo "<5>[aurora-vpn] $*" > /dev/kmsg 2>/dev/null; }
|
|
fail() { log "FAIL: $*"; exit 1; }
|
|
tif() { [ "$VPN_MODE" = wg ] && echo wg0 || echo "$TUN_IF"; }
|
|
|
|
firewall() {
|
|
T=$(tif); MSS=$(( ${1:-$TUN_MTU} - 40 ))
|
|
nft list table inet aurora_router >/dev/null 2>&1 && nft delete table inet aurora_router
|
|
nft -f - <<NFT || fail "nft"
|
|
table inet aurora_vpn {
|
|
chain input {
|
|
type filter hook input priority filter; policy accept;
|
|
ct state established,related accept
|
|
iifname "lo" accept
|
|
iifname "$MGMT_IF" accept
|
|
iifname "$LAN_IF" jump lan_in
|
|
iifname { "$WAN_IF", "$T" } jump wan_in
|
|
}
|
|
chain lan_in {
|
|
udp sport 68 udp dport 67 accept
|
|
ip daddr != $LAN_ADDR counter drop
|
|
udp dport 53 accept
|
|
tcp dport 22 accept comment "B17: SSH (dropbear) from Wi-Fi to the board LAN address only"
|
|
tcp dport 53 accept
|
|
icmp type echo-request limit rate 20/second accept
|
|
counter drop
|
|
}
|
|
chain wan_in {
|
|
icmp type { echo-reply, destination-unreachable, time-exceeded } accept
|
|
ct state established,related accept
|
|
counter drop
|
|
}
|
|
chain forward {
|
|
type filter hook forward priority filter; policy drop;
|
|
ct state invalid counter drop
|
|
iifname "$LAN_IF" oifname "$T" ip saddr $LAN_NET tcp flags syn tcp option maxseg size set $MSS
|
|
iifname "$LAN_IF" oifname "$T" ip saddr $LAN_NET counter accept
|
|
iifname "$T" oifname "$LAN_IF" ct state established,related counter accept
|
|
iifname "$LAN_IF" oifname "$WAN_IF" counter drop comment "fail-closed: no LTE leak"
|
|
iifname "$LAN_IF" oifname "$MGMT_IF" counter drop
|
|
counter drop
|
|
}
|
|
chain postrouting {
|
|
type nat hook postrouting priority srcnat; policy accept;
|
|
oifname "$T" ip saddr $LAN_NET counter masquerade
|
|
}
|
|
}
|
|
NFT
|
|
}
|
|
route_lan() {
|
|
T=$(tif)
|
|
ip route replace default dev "$T" table $TBL
|
|
ip rule show | grep -q "lookup $TBL" || ip rule add from "$LAN_NET" lookup $TBL priority $RULE_PRI
|
|
ip rule show | grep -q "from $LAN_NET to $LAN_NET lookup main" || ip rule add from "$LAN_NET" to "$LAN_NET" lookup main priority $((RULE_PRI-1))
|
|
echo 1 > /proc/sys/net/ipv4/ip_forward
|
|
}
|
|
unroute() {
|
|
while ip rule del from "$LAN_NET" lookup $TBL 2>/dev/null; do :; done
|
|
while ip rule del from "$LAN_NET" to "$LAN_NET" lookup main 2>/dev/null; do :; done
|
|
ip route flush table $TBL 2>/dev/null
|
|
}
|
|
case "$1" in
|
|
up)
|
|
log "=== VPN UP ($VPN_MODE)"
|
|
if [ "$VPN_MODE" = vless ]; then
|
|
[ -f $SB ] || fail "$SB missing"
|
|
command -v sing-box >/dev/null || fail "sing-box not installed"
|
|
pidof sing-box >/dev/null && { log "sing-box already running"; } || {
|
|
sing-box check -c $SB 2>>$LOG || fail "sing-box config invalid"
|
|
setsid sh -c "trap '' HUP; exec sing-box run -c $SB" >> $S/sing-box.log 2>&1 < /dev/null &
|
|
echo $! > $S/sing-box.pid
|
|
}
|
|
i=0; while [ ! -e /sys/class/net/$TUN_IF ] && [ $i -lt 100 ]; do sleep 0.1; i=$((i+1)); done
|
|
[ -e /sys/class/net/$TUN_IF ] || { tail -8 $S/sing-box.log; fail "$TUN_IF did not appear"; }
|
|
ip link set $TUN_IF mtu $TUN_MTU 2>/dev/null
|
|
else
|
|
ip link add dev wg0 type wireguard 2>/dev/null || ip link show wg0 >/dev/null 2>&1 || fail "no WireGuard"
|
|
[ -f $WGC ] || fail "$WGC missing"
|
|
A=$(sed -n 's/^Address[ ]*=[ ]*//p' $WGC | head -1); M=$(sed -n 's/^MTU[ ]*=[ ]*//p' $WGC | head -1); EP=$(sed -n 's/^Endpoint[ ]*=[ ]*//p' $WGC | head -1 | sed 's/:[0-9]*$//')
|
|
wg-quick strip wg0 2>/dev/null | wg setconf wg0 /dev/stdin || { ip link del wg0; fail "wg setconf"; }
|
|
ip addr flush dev wg0; ip addr add "$A" dev wg0; ip link set wg0 mtu "${M:-$TUN_MTU}" up
|
|
# loopback endpoint = local relay (free-turn-client on 127.0.0.1): its own uplink is board traffic -> main -> wwan0
|
|
case "$EP" in 127.*) log "endpoint $EP is local relay: no direct route";;
|
|
*) GW=$(ip route show default dev $WAN_IF | awk '{print $3; exit}'); ip route replace "$EP/32" dev $WAN_IF ${GW:+via $GW}; echo "$EP" > $S/endpoint;; esac
|
|
fi
|
|
route_lan; firewall $M
|
|
log "=== VPN UP OK mode=$VPN_MODE tun=$(tif) LAN $LAN_NET -> $(tif) (table $TBL); fail-closed on"
|
|
;;
|
|
down)
|
|
log "=== VPN DOWN"
|
|
[ "$VPN_MODE" = vless ] && { pidof sing-box >/dev/null && kill $(pidof sing-box); sleep 1; pidof sing-box >/dev/null && kill -9 $(pidof sing-box); rm -f $S/sing-box.pid; }
|
|
[ "$VPN_MODE" = wg ] && ip link del wg0 2>/dev/null
|
|
unroute
|
|
[ -f $S/endpoint ] && { ip route del "$(cat $S/endpoint)/32" dev $WAN_IF 2>/dev/null; rm -f $S/endpoint; }
|
|
nft list table inet aurora_vpn >/dev/null 2>&1 && nft delete table inet aurora_vpn
|
|
/usr/sbin/aurora-router up >/dev/null 2>&1 || log "note: run 'aurora-router up' to restore LAN->LTE"
|
|
log "=== VPN DOWN OK (router restored: $(nft list tables 2>/dev/null | tr '\n' ' '))"
|
|
;;
|
|
status)
|
|
T=$(tif); echo "kernel $(uname -r) mode $VPN_MODE"
|
|
if [ -e /sys/class/net/$T ]; then
|
|
echo "$T UP mtu=$(cat /sys/class/net/$T/mtu)"
|
|
if [ "$VPN_MODE" = vless ]; then
|
|
echo "sing-box pid=$(pidof sing-box) rss_kB=$(awk '/VmRSS/{print $2}' /proc/$(pidof sing-box 2>/dev/null)/status 2>/dev/null)"
|
|
tail -3 $S/sing-box.log 2>/dev/null | sed 's/^/ /'
|
|
else
|
|
HS=$(wg show wg0 latest-handshakes 2>/dev/null | awk '{print $2}' | head -1)
|
|
echo "handshake_age=$([ -n "$HS" ] && [ "$HS" != 0 ] && echo $(( $(date +%s)-HS ))s || echo never) transfer=$(wg show wg0 transfer 2>/dev/null | awk '{print $2"/"$3}')"
|
|
fi
|
|
echo "rules:"; ip rule show | grep -E "lookup $TBL|$LAN_NET"
|
|
echo "table $TBL:"; ip route show table $TBL
|
|
else echo "$T DOWN"; fi
|
|
nft list table inet aurora_vpn 2>/dev/null | grep -E "oifname|masquerade|drop" | sed 's/^[ \t]*//' | head
|
|
;;
|
|
*) echo "usage: $0 up|down|status"; exit 2;;
|
|
esac
|