feat(install): support disabling IPv4 forwarding

- add --disable-v4forward with explicit confirmation\n- skip reserved-port checks and keep Traefik stopped for IPv6-only installs\n- document dedicated-host requirements and sync config references
This commit is contained in:
bluebluesoda committed 2026-08-28 15:07:24 +00:00
1 parent 6d897cc1bb
commit 97396589f5
7 files changed
+81 -9

No files matched your search

+10
View File
@@ -21,6 +21,8 @@ A lightweight Incus container hosting panel for small VPS instances and low-memo
Ubuntu 24.04 is recommended; Ubuntu 22+ and Debian 12+ also work. Debian requires a kernel-module build and takes longer to install.
Unless you have checked the existing services and network configuration, treat the installer as a fresh, dedicated-host installer. Do not run it directly on a personal host that already serves other workloads. It installs and configures Incus, ZFS (by default), nftables, Traefik, systemd units, and a container bridge; with IPv4 inbound forwarding enabled it also requires ports `80/443`, `10000-29999`, and `30000-31999` to be available, and may disable conflicting UFW configuration. The installer does not uninstall your applications, but its ports, bridge, and firewall policy can conflict with existing services.
Recommended minimum: 1 CPU core, 1 GB RAM, 15 GB of free disk space, and root access. Both amd64 and arm64 are supported; testing has focused mainly on amd64.
```sh
@@ -33,6 +35,14 @@ sudo ./install.sh # download the latest prebuilt binary
The installer first downloads the prebuilt binary from GitHub Releases. If that fails, it falls back to a local build. `--local-build` always rebuilds from source. If `--update` cannot download the release, the existing binary is left untouched.
If the host already runs other public services and you only need IPv6 inbound access, without IPv4 SSH/port forwarding, use:
```sh
sudo ./install.sh --disable-v4forward
```
This option asks for confirmation at the beginning. Once confirmed, it writes `net.v4_forward=false`, skips vpsmgr's reserved-port check, and only needs one randomly selected panel entry port. Traefik is still installed but remains stopped. IPv4 inbound forwarding can later be restored with `vps config set net.v4_forward true`.
ZFS is the default storage backend. For disposable test machines, you can explicitly choose the `dir` backend. It does not provide ZFS quotas, snapshots, or cloning:
```sh
+10
View File
@@ -21,6 +21,8 @@
推荐使用 Ubuntu 24.04。Ubuntu 22+ / Debian 12+ 均可使用;Debian 需要编译内核模块,安装时间较长。
除非你明确检查过现有服务与网络配置,否则请把安装器视为“全新、专用主机”安装器,不要直接在正在运行其他业务的自用主机上执行。安装器会安装并配置 Incus、ZFS(默认)、nftables、Traefik、systemd 服务和容器网桥;启用 v4 入站转发时还要求 `80/443`、`10000-29999`、`30000-31999` 端口可用,并可能停用冲突的 UFW 配置。安装器不会卸载你的业务程序,但端口、网桥和防火墙策略可能与已有服务冲突。
最低建议:1 核、1 GB 内存、15 GB 可用磁盘空间,以及 root 权限。amd64 和 arm64 均支持,主要在 amd64 上测试。
**从预编译安装**
@@ -38,6 +40,14 @@ git clone --depth 1 https://github.com/bluebluesoda/vpsmgr.git && cd vpsmgr
sudo ./install.sh --local-build
```
如果这台主机已有其他公网服务,且你只需要 IPv6 入站、不需要 v4 SSH/端口转发,可以使用:
```sh
sudo ./install.sh --disable-v4forward
```
该参数会在安装开始时要求确认;确认后写入 `net.v4_forward=false`,跳过 vpsmgr 保留端口检查,只随机选择一个面板入口端口。Traefik 仍会安装但不会启动。以后可用 `vps config set net.v4_forward true` 恢复 IPv4 入站转发。
### IPv6
IPv6 直通默认关闭。交互式安装会询问是否启用,并根据网络条件提供以下模式:
+10 -2
View File
@@ -12,7 +12,7 @@ panel:
url_path: AUTO # 10-char random [a-zA-Z0-9_-] secret prefix, generated once at install
admin_url_path: AUTO # 10-char random secret prefix for the ADMIN panel; can be
# changed later or set to empty to disable the admin panel
admin_pass_hash: AUTO # bcrypt hash of the admin panel password; generated at install,
admin_pass_hash: AUTO # bcrypt hash of the admin panel password; generated at install,
# reset via `vps admin-passwd`. No admin username — password only.
net:
@@ -28,14 +28,22 @@ net:
# SSH/port-block DNAT and traefik/domains disabled
# (NAT4 outbound kept). Toggle at runtime: vps config set net.v4_forward true|false
ext_if: AUTO # detected from default route
ipv6_mode: none # fixed at install: none, prefix, or pool
ipv6_subnet: "" # optional: global prefix for IPv6 pass-through, e.g. "2602:fada:6::/64".
# /64 or shorter works (e.g. /56); a provider /80 slice like
# /64 or shorter uses the first /64; a provider /80 slice like
# "2406:da14:1dd2:a807:753a::/80" is also supported.
# empty = IPv6 disabled (default). Containers get global
# addresses via SLAAC; no NAT; set at install time.
ipv6_pool: [] # optional (pool mode): bare global IPv6 addresses or /128;
# addresses are assigned to containers until they are deleted.
incus:
image: "vpsmgr/debian-sshd"
image_fallback: "images:debian/13"
pool: vpsmgr
bridge: incusbr0
socket: "/var/lib/incus/unix.socket"
swap_ratio: 0.5 # 0 = no container swap; 0.5 = memory allowance / 2
snapshots:
limit: 1 # 0 disables new snapshots
+17 -6
View File
@@ -5,6 +5,13 @@ root-only read/write). The `VPSMGR_CONFIG` env var points the `vps` binary at
another path; the shell install/uninstall scripts always use
`/etc/vpsmgr/config.yaml`.
The installer is intended for a fresh, dedicated host unless existing services,
ports, bridges, UFW, and firewall rules have been checked. For a host that must
keep other public services and only needs IPv6 inbound access, install with
`./install.sh --disable-v4forward`; this requires an explicit confirmation,
skips vpsmgr's reserved-port check, writes `net.v4_forward=false`, and leaves
Traefik installed but stopped.
**Editing config.yaml by hand is discouraged.** The sanctioned interface is
`vps config list` / `vps config set` / `vps config help`, which validate every
change and refuse immutable fields. A raw YAML edit still works but is not
@@ -21,7 +28,7 @@ same table; `vps config list` shows the live values with this annotation.
| `panel.listen` | operator | `systemctl restart vps` | a FRESH install picks a random free port in 2000-9999 |
| `panel.cert` | operator | restart panel | TLS certificate path |
| `panel.key` | operator | restart panel | TLS private key path |
| `panel.db` | operator | restart panel | SQLite database path |
| `panel.db` | operator | re-run `vps install` | SQLite database path |
| `panel.public_ip` | operator | re-run `vps install` | NIC IPv4 used by firewall/routing; cert is regenerated |
| `panel.display_ip` | operator | restart panel | address shown to users (panel URL / SSH hints); any string without spaces (IP or domain), or empty = fall back to `public_ip` |
| `panel.session_days` | operator | restart panel | login session lifetime (days) |
@@ -34,19 +41,19 @@ same table; `vps config list` shows the live values with this annotation.
| `net.ext_if` | operator | re-run `vps install` | external NIC (auto-detected from default route) |
| `net.ipv6_subnet` | operator | re-run `vps install` | global IPv6 prefix for pass-through, e.g. `2602:fada:6::/64`; empty = disabled (does not remove IPv6 state already applied, see note below) |
| `net.ipv6_mode` | **fixed at install** | — | IPv6 allocation mode: `none` / `prefix` (/112 blocks) / `pool` (per-container address) |
| `net.ipv6_pool` | operator | applied immediately | pool-mode address list (bare global addresses or `/128`); editable via the admin panel's IPv6 Pool page |
| `net.ipv6_pool` | operator | `vps config set` needs `--apply`; admin-panel changes apply immediately | pool-mode address list (bare global addresses or `/128`); editable via the admin panel's IPv6 Pool page |
| `incus.image` | operator | next `vps add` / reinstall | container image alias |
| `incus.image_fallback` | operator | next `vps add` / reinstall | fallback remote image |
| `incus.pool` | **fixed at install** | — | storage pool |
| `incus.bridge` | **fixed at install** | — | managed bridge |
| `incus.socket` | operator | restart panel | Incus daemon Unix socket |
| `incus.swap_ratio` | operator | **applied immediately** | swap granted to each container as a multiple of its memory limit (`limits.memory.swap = limits.memory × ratio`); `0` disables container swap. Setting it re-applies the allowance to **all existing containers** (no restart) |
| `snapshots.limit` | operator | restart panel | max snapshots a user may keep per container (`0` = default `1`) |
| `snapshots.limit` | operator | restart panel | max snapshots a user may keep per container (`0` = disable new snapshots) |
### How "fixed at install" is enforced
Fields marked **fixed at install** (`net.subnet`, `net.gateway`, `incus.pool`,
`incus.bridge`, `panel.url_path`) are snapshotted into the DB settings table on
`incus.bridge`, `panel.url_path`, `net.ipv6_mode`) are snapshotted into the DB settings table on
the first `vps install`. Every later `vps install` and `vps serve` compares the
live config against that snapshot and **refuses to run** if any of them drifted
— `vps config set` also refuses them up front. The user panel path
@@ -91,7 +98,7 @@ net:
ext_if: AUTO # external NIC, auto-detected from the default route
ipv6_mode: "" # IPv6 allocation mode: none / prefix / pool (fixed at install)
ipv6_subnet: "" # optional: global prefix for IPv6 pass-through, e.g. "2602:fada:6::/64"
# (/64 or shorter, incl. provider /80 slices like
# (/64 or shorter uses the first /64; provider /80 slices like
# "2406:da14:1dd2:a807:753a::/80"); empty = disabled (default),
# but does not remove IPv6 state already applied
ipv6_pool: [] # optional (pool mode): the /128 addresses containers are
@@ -108,6 +115,9 @@ incus:
# memory limit (0 = no swap; 0.5 = a 1 GiB
# container may use 512 MiB host swap);
# applied to all containers immediately on set
snapshots:
limit: 1 # 0 disables new snapshots
```
## Port scheme (fixed)
@@ -159,7 +169,8 @@ goes away when the panel is uninstalled.
change them after first install.
- `net.ipv6_subnet` must be a **global** (non-ULA) IPv6 CIDR with an explicit
prefix length — a bare address is rejected, never silently assumed `/64`.
Valid range is `/48`..`/80` (see [ipv6.md](ipv6.md)).
The validator accepts a global prefix of `/80` or shorter; `/48`..`/80` are
the documented operational range (see [ipv6.md](ipv6.md)).
- `net.ipv6_mode` is fixed at install and immutable (like `net.subnet`):
`none` (pure IPv4), `prefix` (classic /112 blocks), `pool` (per-container
address from `net.ipv6_pool`). Switching modes would renumber containers.
+28
View File
@@ -4,19 +4,29 @@
# ./install.sh # default: download latest prebuilt release binary (fallback: local build)
# ./install.sh --local-build # force local Go compilation of the panel binary
# ./install.sh --update # force re-download of the prebuilt release binary over an existing one
# ./install.sh --disable-v4forward # install IPv6-only inbound policy; skip reserved-port checks
set -euo pipefail
cd "$(dirname "$0")"
ROOT="$PWD"
BUILD_MODE=release
DISABLE_V4FORWARD=0
for arg in "$@"; do
case "$arg" in
--local-build) BUILD_MODE=local ;;
--update) BUILD_MODE=update ;;
--disable-v4forward) DISABLE_V4FORWARD=1 ;;
esac
done
export VPSMGR_BUILD_MODE="$BUILD_MODE"
if [[ "$DISABLE_V4FORWARD" == "1" ]]; then
# This explicit installer choice overrides an adopted config and any
# VPSMGR_V4_FORWARD value inherited from the caller. 00-check.sh uses the
# separate marker to skip checks for ports that vpsmgr will not expose.
export VPSMGR_V4_FORWARD=0
export VPSMGR_DISABLE_V4FORWARD=1
fi
# Storage backend: zfs (default) or dir. dir has no quotas/snapshots/clones —
# only meant for throwaway test boxes. Never fall back automatically.
@@ -31,6 +41,24 @@ if [[ $EUID -ne 0 ]]; then
exit 1
fi
if [[ "$DISABLE_V4FORWARD" == "1" ]]; then
echo
echo "!! --disable-v4forward: install with IPv4 inbound forwarding disabled !!"
echo " Containers will have no IPv4 SSH/port DNAT; Traefik will be installed but stopped."
echo " Reserved vpsmgr port checks will be skipped. You can re-enable later with:"
echo " vps config set net.v4_forward true"
if [[ ! -t 0 ]]; then
echo "error: --disable-v4forward requires an interactive confirmation" >&2
exit 1
fi
read -r -p "Continue with IPv4 inbound disabled? [Y/n] " V4_CONFIRM
case "$V4_CONFIRM" in
''|y|Y|yes|YES) ;;
n|N|no|NO) echo "[install] aborted by user"; exit 1 ;;
*) echo "error: please answer Y or n" >&2; exit 1 ;;
esac
fi
# Local build: make it obvious WHICH branch will be compiled, and give the user
# a chance to abort — some people want a dev build but end up building stable.
if [[ "$BUILD_MODE" == "local" ]]; then
+2 -1
View File
@@ -10,6 +10,7 @@
# Supported args (passed straight through to install.sh):
# --update re-download the latest prebuilt release binary over an existing one
# --local-build force local Go compilation of the panel binary
# --disable-v4forward install with IPv4 inbound forwarding disabled
# Env:
# VPSMGR_STORAGE=zfs|dir storage backend (default zfs)
# VPSMGR_BRANCH=<branch> branch/tag to fetch (default main)
@@ -32,7 +33,7 @@ fi
ARGS=()
for a in "$@"; do
case "$a" in
--update|--local-build) ARGS+=("$a") ;;
--update|--local-build|--disable-v4forward) ARGS+=("$a") ;;
*) echo "oneclick.sh: ignoring unknown arg '$a'" >&2 ;;
esac
done
+4
View File
@@ -197,6 +197,9 @@ fi
# panel and traefik are already running and owned by vpsmgr — those listeners
# are excluded by process name. Checks TCP and UDP (the user port block is
# DNAT-ed for both).
if [[ "${VPSMGR_DISABLE_V4FORWARD:-0}" == "1" ]]; then
log "v4 forwarding disabled by installer — skipping reserved port checks"
else
port_reserved(){
local p="$1"
[[ "$p" == "80" || "$p" == "443" ]] && return 0
@@ -226,6 +229,7 @@ $CONFLICTS
Free these ports (or remove the programs above) and re-run install."
fi
log "reserved ports are free"
fi
# --- detect public ip / ext iface ---
# ext iface: prefer the IPv4 default route, then the IPv6 default route (covers