feat: Add OTA auto-update and guest IPv6 via Yggdrasil
OTA: build.sh writes version/profile to firmware, generates manifest.json with SHA256 per device. parahub-autoupdate script runs nightly at 3am, fetches manifest (Yggdrasil first), verifies checksum, runs sysupgrade. sysupgrade.conf preserves /etc/parahub/, yggdrasil.conf, dropbear keys. Guest IPv6: Yggdrasil 300::/64 subnet assigned to guest via SLAAC. Separate yggdrasil firewall zone (5 zones total) with guest→yggdrasil forwarding. IPv6 exempt from tc shaping — full speed to Parahub services. IPv6 to WAN blocked. Heartbeat now reads version from file, not hardcoded. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
3
files/etc/sysupgrade.conf
Normal file
3
files/etc/sysupgrade.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
/etc/parahub/
|
||||
/etc/yggdrasil.conf
|
||||
/etc/dropbear/
|
||||
@@ -382,13 +382,12 @@ set firewall.@zone[-1].output='ACCEPT'
|
||||
set firewall.@zone[-1].forward='ACCEPT'
|
||||
add_list firewall.@zone[-1].network='private'
|
||||
|
||||
# --- Zone: guest (IPv4 only — IPv6 blocked) ---
|
||||
# --- Zone: guest (dual-stack — IPv6 for Yggdrasil services) ---
|
||||
add firewall zone
|
||||
set firewall.@zone[-1].name='guest'
|
||||
set firewall.@zone[-1].input='REJECT'
|
||||
set firewall.@zone[-1].output='ACCEPT'
|
||||
set firewall.@zone[-1].forward='REJECT'
|
||||
set firewall.@zone[-1].family='ipv4'
|
||||
add_list firewall.@zone[-1].network='guest'
|
||||
|
||||
# --- Zone: wan ---
|
||||
@@ -430,14 +429,13 @@ set firewall.@rule[-1].dest_port='67'
|
||||
set firewall.@rule[-1].target='ACCEPT'
|
||||
set firewall.@rule[-1].family='ipv4'
|
||||
|
||||
# --- Rule: guest DNS to router only (hijacked to DoH) ---
|
||||
# --- Rule: guest DNS to router (hijacked to DoH, dual-stack) ---
|
||||
add firewall rule
|
||||
set firewall.@rule[-1].name='Guest DNS'
|
||||
set firewall.@rule[-1].src='guest'
|
||||
set firewall.@rule[-1].proto='tcpudp'
|
||||
set firewall.@rule[-1].dest_port='53'
|
||||
set firewall.@rule[-1].target='ACCEPT'
|
||||
set firewall.@rule[-1].family='ipv4'
|
||||
|
||||
# --- Rule: block guest → lan (isolation) ---
|
||||
add firewall rule
|
||||
@@ -447,10 +445,20 @@ set firewall.@rule[-1].dest='lan'
|
||||
set firewall.@rule[-1].proto='all'
|
||||
set firewall.@rule[-1].target='REJECT'
|
||||
|
||||
# --- Rule: block ALL IPv6 from guest (leak prevention) ---
|
||||
# --- Rule: allow guest DHCPv6/RA (IPv6 address assignment) ---
|
||||
add firewall rule
|
||||
set firewall.@rule[-1].name='Block guest IPv6'
|
||||
set firewall.@rule[-1].name='Guest RA'
|
||||
set firewall.@rule[-1].src='guest'
|
||||
set firewall.@rule[-1].proto='icmp'
|
||||
set firewall.@rule[-1].family='ipv6'
|
||||
set firewall.@rule[-1].icmp_type='router-solicitation'
|
||||
set firewall.@rule[-1].target='ACCEPT'
|
||||
|
||||
# --- Rule: block guest IPv6 to WAN (only Yggdrasil allowed via forwarding) ---
|
||||
add firewall rule
|
||||
set firewall.@rule[-1].name='Block guest IPv6 WAN'
|
||||
set firewall.@rule[-1].src='guest'
|
||||
set firewall.@rule[-1].dest='wan'
|
||||
set firewall.@rule[-1].proto='all'
|
||||
set firewall.@rule[-1].family='ipv6'
|
||||
set firewall.@rule[-1].target='REJECT'
|
||||
@@ -608,8 +616,44 @@ set network.yggdrasil.proto='none'
|
||||
YGG_EOF
|
||||
uci commit network
|
||||
|
||||
# Add yggdrasil to LAN zone (mesh nodes trust each other)
|
||||
uci add_list firewall.@zone[0].network='yggdrasil'
|
||||
# Yggdrasil firewall zone (separate from LAN — allows guest→yggdrasil forwarding)
|
||||
uci batch <<-YGG_FW_EOF
|
||||
add firewall zone
|
||||
set firewall.@zone[-1].name='yggdrasil'
|
||||
set firewall.@zone[-1].input='REJECT'
|
||||
set firewall.@zone[-1].output='ACCEPT'
|
||||
set firewall.@zone[-1].forward='REJECT'
|
||||
add_list firewall.@zone[-1].network='yggdrasil'
|
||||
|
||||
# Allow GRE6 protocol input (tunnel endpoint)
|
||||
add firewall rule
|
||||
set firewall.@rule[-1].name='Allow GRE6 input'
|
||||
set firewall.@rule[-1].src='yggdrasil'
|
||||
set firewall.@rule[-1].proto='47'
|
||||
set firewall.@rule[-1].target='ACCEPT'
|
||||
|
||||
# Allow ICMPv6 on Yggdrasil
|
||||
add firewall rule
|
||||
set firewall.@rule[-1].name='Allow ICMPv6 Ygg'
|
||||
set firewall.@rule[-1].src='yggdrasil'
|
||||
set firewall.@rule[-1].proto='icmp'
|
||||
set firewall.@rule[-1].family='ipv6'
|
||||
set firewall.@rule[-1].target='ACCEPT'
|
||||
|
||||
# Forwardings: LAN ↔ Yggdrasil (owner full access)
|
||||
add firewall forwarding
|
||||
set firewall.@forwarding[-1].src='lan'
|
||||
set firewall.@forwarding[-1].dest='yggdrasil'
|
||||
|
||||
add firewall forwarding
|
||||
set firewall.@forwarding[-1].src='yggdrasil'
|
||||
set firewall.@forwarding[-1].dest='lan'
|
||||
|
||||
# Forwarding: guest → yggdrasil (IPv6 Parahub services at full speed)
|
||||
add firewall forwarding
|
||||
set firewall.@forwarding[-1].src='guest'
|
||||
set firewall.@forwarding[-1].dest='yggdrasil'
|
||||
YGG_FW_EOF
|
||||
uci commit firewall
|
||||
|
||||
# Enable yggdrasil service
|
||||
@@ -623,17 +667,50 @@ else
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# 12. HEARTBEAT (phone-home to Parahub cloud)
|
||||
# 12. GUEST IPv6 VIA YGGDRASIL (Bumblebee only — full-speed Parahub access)
|
||||
# ============================================================================
|
||||
|
||||
if [ "$ROLE" != "bee" ]; then
|
||||
# Extract Yggdrasil 300::/64 subnet for guest SLAAC
|
||||
YGG_SUBNET=$(yggdrasil -subnet -useconffile /etc/yggdrasil.conf 2>/dev/null)
|
||||
# YGG_SUBNET format: 300:xxxx:xxxx:xxxx::/64
|
||||
YGG_SUBNET_PREFIX="${YGG_SUBNET%%::*}" # 300:xxxx:xxxx:xxxx
|
||||
|
||||
if [ -n "$YGG_SUBNET_PREFIX" ]; then
|
||||
# Assign IPv6 from Yggdrasil subnet to guest interface
|
||||
uci set network.guest.ip6addr="${YGG_SUBNET_PREFIX}::1/64"
|
||||
uci commit network
|
||||
|
||||
# Configure RA/SLAAC for guest (odhcpd)
|
||||
uci batch <<-GUEST6_EOF
|
||||
set dhcp.guest.ra='server'
|
||||
set dhcp.guest.dhcpv6='disabled'
|
||||
set dhcp.guest.ra_management='0'
|
||||
set dhcp.guest.ra_default='1'
|
||||
set dhcp.guest.ra_slaac='1'
|
||||
GUEST6_EOF
|
||||
uci commit dhcp
|
||||
|
||||
logger -t parahub-mesh "Guest IPv6: ${YGG_SUBNET_PREFIX}::1/64 (SLAAC)"
|
||||
else
|
||||
logger -t parahub-mesh "Warning: Could not extract Yggdrasil subnet, guest IPv6 disabled"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# 13. HEARTBEAT (phone-home to Parahub cloud)
|
||||
# ============================================================================
|
||||
|
||||
chmod +x /usr/bin/parahub-heartbeat
|
||||
chmod +x /usr/bin/parahub-autoupdate
|
||||
|
||||
# Cron: every 5 minutes
|
||||
# Cron: heartbeat every 5 minutes + OTA update nightly at 3am
|
||||
echo "*/5 * * * * /usr/bin/parahub-heartbeat" >> /etc/crontabs/root
|
||||
echo "0 3 * * * /usr/bin/parahub-autoupdate" >> /etc/crontabs/root
|
||||
/etc/init.d/cron enable
|
||||
|
||||
# ============================================================================
|
||||
# 13. FINAL
|
||||
# 14. FINAL
|
||||
# ============================================================================
|
||||
|
||||
# Log completion
|
||||
@@ -651,6 +728,7 @@ else
|
||||
logger -t parahub-mesh "Yggdrasil: ${YGG_ADDR}"
|
||||
logger -t parahub-mesh "GRE tunnel: 172.16.0.2 → VPS gateway (Mullvad Portugal)"
|
||||
logger -t parahub-mesh "Kill switch: guest→vpn_tunnel only (no wan)"
|
||||
logger -t parahub-mesh "Guest IPv6: Yggdrasil SLAAC (full speed, firewall restricted)"
|
||||
logger -t parahub-mesh "bat0 gw_mode: server (gateway for Bee nodes)"
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user