Files
vpsmgr/scripts/20-network.sh
T
bluebluesodaandCommandCodeBot 5e87c9bed7 fix(ipv6): plumb none mode + extra prefix through host, boot and creates
- RewireAllIPv6() began with `if !IPv6Enabled() { return nil }`, so in none
  mode — the mode that carries an extra prefix — it did nothing at all: the
  boot unit and `vps ipv6-reapply` never set net.ipv6.conf.all.forwarding,
  all.proxy_ndp or <ext_if>.proxy_ndp, and without them Incus could not install
  the routed NIC's proxy_ndp entry, so every create failed
- give that branch its plumbing: forwarding, kernel proxy_ndp, the NDP rules
  and the whole-/64 routes
- ensure forwarding + proxy_ndp at create time, before the routed NIC exists,
  so a create no longer depends on a prior install / ipv6-reapply
- a clone or launch failure now calls cleanup() and removes the partial
  instance; the leftover used to hold the name, so every retry was refused with
  "already exists (orphan?)"
- the installer persists the name-free sysctls (all.proxy_ndp always,
  all.forwarding when vpsmgr relays container v6) and uninstall resets
  all.proxy_ndp again

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-09-27 14:47:16 +00:00

103 lines
5.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# 20-network.sh — sysctl + nftables basics.
set -uo pipefail
log(){ echo "[20] $*"; }
# ip_forward + BBR/fq TCP tuning + io_uring attack-surface clamp. Written every
# run (idempotent). io_uring_disabled=1 lets only init-userns CAP_SYS_ADMIN (the
# host's own root services) create io_uring; container tenants — even container
# root, which only has userns-scoped caps — get EPERM, closing the biggest
# kernel LPE attack surface for tenants. Value 1, not 2: the host stack (Incus,
# ZFS, Go panel/HAProxy) is untouched. Matches RHEL 9.3+'s shipped default.
cat > /etc/sysctl.d/99-vpsmgr.conf <<EOF
# Managed by vpsmgr — generated file, do not edit by hand.
# Changes are overwritten on the next install.
net.ipv4.ip_forward=1
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
kernel.io_uring_disabled=1
net.ipv6.conf.all.use_tempaddr = 0
net.ipv6.conf.default.use_tempaddr = 0
net.core.netdev_max_backlog = 8192
net.core.rmem_default = 262144
net.core.wmem_default = 262144
net.ipv4.tcp_rmem = 8192 262144 8388608
net.ipv4.tcp_wmem = 4096 16384 8388608
net.core.rmem_max = 8388608
net.core.wmem_max = 8388608
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_slow_start_after_idle = 0
EOF
# IPv6 pass-through. Only NAME-FREE keys are persisted here: `all.*` is
# inherited by interfaces created later, while the per-interface value for the
# EXISTING WAN interface is applied at runtime by `vps ipv6-reapply` / the panel
# (they know the real interface name — a name hard-coded here would silently
# miss ens3/enp1s0-style interfaces).
# proxy_ndp is inert without proxy entries, so it goes in unconditionally (it
# also future-proofs an extra prefix enabled later on a none-mode host).
# forwarding is NOT unconditional: on a host whose own IPv6 comes from a router
# advertisement, forwarding=1 suppresses the RA default route, so it is only set
# when vpsmgr itself relays container v6 (a base prefix, or a routed mode).
V6_ROUTED=0
case "${VPSMGR_IPV6_MODE:-}" in
pool|none) V6_ROUTED=1 ;;
esac
if [[ -n "${VPSMGR_IPV6_SUBNET:-}" || "$V6_ROUTED" == 1 ]]; then V6_FWD=1; else V6_FWD=0; fi
cat >> /etc/sysctl.d/99-vpsmgr.conf <<'EOF'
net.ipv6.conf.all.proxy_ndp=1
EOF
if [[ "$V6_FWD" == 1 ]]; then
cat >> /etc/sysctl.d/99-vpsmgr.conf <<'EOF'
net.ipv6.conf.all.forwarding=1
net.ipv6.conf.default.forwarding=1
EOF
fi
V6_NOTE=" + ipv6 proxy_ndp"
[[ "$V6_FWD" == 1 ]] && V6_NOTE="$V6_NOTE + ipv6 forwarding"
log "wrote /etc/sysctl.d/99-vpsmgr.conf (ip_forward + bbr/fq + io_uring_disabled$V6_NOTE)"
SYSCTL_ARGS=(net.ipv4.ip_forward=1 net.core.default_qdisc=fq net.ipv4.tcp_congestion_control=bbr kernel.io_uring_disabled=1 net.ipv6.conf.all.proxy_ndp=1)
[[ "$V6_FWD" == 1 ]] && SYSCTL_ARGS+=(net.ipv6.conf.all.forwarding=1)
if ! sysctl -q -w "${SYSCTL_ARGS[@]}" 2>/dev/null; then
log "warn: live sysctl apply failed (e.g. kernel too old for bbr/io_uring_disabled); config persisted and will apply on reboot"
fi
log "tcp congestion: $(sysctl -n net.ipv4.tcp_congestion_control 2>/dev/null || echo 'n/a')"
if ! dpkg -s nftables >/dev/null 2>&1; then
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq nftables
fi
log "nftables: $(nft --version 2>/dev/null | head -1)"
# --- Docker compatibility (DOCKER-USER chain) ---
# When Docker is installed on the host, it sets iptables FORWARD default policy
# to DROP. This breaks forwarded traffic for incusbr0 (both IPv4, and IPv6 if
# Docker's experimental/ipv6 is enabled). Docker provides the DOCKER-USER chain
# specifically for custom allow rules to be evaluated before Docker's own rules.
# Allow incusbr0 traffic in DOCKER-USER now, and install a systemd drop-in for
# docker.service so rules persist across Docker restarts.
apply_docker_rules(){
if command -v iptables >/dev/null 2>&1 && iptables -L DOCKER-USER -n >/dev/null 2>&1; then
iptables -C DOCKER-USER -i incusbr0 -j ACCEPT 2>/dev/null || iptables -I DOCKER-USER -i incusbr0 -j ACCEPT
iptables -C DOCKER-USER -o incusbr0 -m conntrack --ctstate RELATED,ESTABLISHED 2>/dev/null || iptables -I DOCKER-USER -o incusbr0 -m conntrack --ctstate RELATED,ESTABLISHED 2>/dev/null
fi
if command -v ip6tables >/dev/null 2>&1 && ip6tables -L DOCKER-USER -n >/dev/null 2>&1; then
ip6tables -C DOCKER-USER -i incusbr0 -j ACCEPT 2>/dev/null || ip6tables -I DOCKER-USER -i incusbr0 -j ACCEPT
ip6tables -C DOCKER-USER -o incusbr0 -m conntrack --ctstate RELATED,ESTABLISHED 2>/dev/null || ip6tables -I DOCKER-USER -o incusbr0 -m conntrack --ctstate RELATED,ESTABLISHED 2>/dev/null
fi
}
apply_docker_rules
if systemctl list-unit-files docker.service >/dev/null 2>&1 || [[ -f /lib/systemd/system/docker.service || -f /etc/systemd/system/docker.service ]]; then
log "docker detected — ensuring incusbr0 forwarding rules in docker.service.d/vpsmgr.conf"
install -d -m 0755 /etc/systemd/system/docker.service.d
cat > /etc/systemd/system/docker.service.d/vpsmgr.conf <<'EOF'
# Managed by vpsmgr — generated file, do not edit by hand.
# Keeps incusbr0 traffic allowed in DOCKER-USER across Docker restarts.
[Service]
ExecStartPost=-/bin/sh -c 'iptables -C DOCKER-USER -i incusbr0 -j ACCEPT 2>/dev/null || iptables -I DOCKER-USER -i incusbr0 -j ACCEPT; iptables -C DOCKER-USER -o incusbr0 -m conntrack --ctstate RELATED,ESTABLISHED 2>/dev/null || iptables -I DOCKER-USER -o incusbr0 -m conntrack --ctstate RELATED,ESTABLISHED 2>/dev/null; ip6tables -L DOCKER-USER -n >/dev/null 2>&1 && { ip6tables -C DOCKER-USER -i incusbr0 -j ACCEPT 2>/dev/null || ip6tables -I DOCKER-USER -i incusbr0 -j ACCEPT; ip6tables -C DOCKER-USER -o incusbr0 -m conntrack --ctstate RELATED,ESTABLISHED 2>/dev/null || ip6tables -I DOCKER-USER -o incusbr0 -m conntrack --ctstate RELATED,ESTABLISHED 2>/dev/null; } || true'
EOF
systemctl daemon-reload >/dev/null 2>&1 || true
fi
echo "[20] network ready"