Allow all private IPv4 traffic by default in Debian initialization firewall

This commit is contained in:
kaka
2026-11-04 22:46:18 +08:00
parent 56b506e54d
commit db1bd78bec
3 changed files with 22 additions and 5 deletions

View File

@@ -56,7 +56,7 @@ Headscale 0.29.6 从预授权 key 授予节点标签,拒绝预授权注册请
所有运行服务的 Docker 模板使用 host 网络,业务服务监听 `0.0.0.0`,由 Debian nftables 控制输入端口。`host-init.sh` 安装 nftables,不自动开放端口或替换现有防火墙。通过固定 release 的 `firewall.sh --check|--apply --policy /run/platform-secrets/firewall.json` 应用已登记的主机策略;文件必须为 root 0600,策略包含准确主机名、恢复 SSH 来源、公网端口和 Tailnet 来源/端口。
规则仅管理 `inet platform_host`,INPUT 默认拒绝、同时校验 `tailscale0` 与精确源 IPv4;不清空全局规则。`--apply` 安排 180 秒自动回退,从新的 SSH transport 验证后执行 `--confirm --sha256 <apply输出摘要>` 才持久化,并建立 Docker/Tailscale 的防火墙启动依赖。通过 sudo 确认时保留 `SSH_CONNECTION` 环境变量。详细 JSON 格式、端口矩阵、Headscale 标签关系与现有集群迁移约束在 platform 的 `docs/network.md`。
规则仅管理 `inet platform_host`,INPUT 默认拒绝公网未声明来源;本机回环、RFC1918(10/8、172.16/12、192.168/16)和 tailscale0 上的完整 100.64/10 默认允许所有端口/协议;不清空全局规则。`--apply` 安排 180 秒自动回退,从新的 SSH transport 验证后执行 `--confirm --sha256 <apply输出摘要>` 才持久化,并建立 Docker/Tailscale 的防火墙启动依赖。通过 sudo 确认时保留 `SSH_CONNECTION` 环境变量。详细 JSON 格式、端口矩阵、Headscale 标签关系与现有集群迁移约束在 platform 的 `docs/network.md`。
## 独立手工升级
@@ -98,3 +98,6 @@ Caddy 同时提供 /install.sh 稳定入口与 /releases/<40位小写Git提交>/
## Headplane 准备与凭据
`headplane-prepare.sh --mode prepare|key --hostname NAME --root /srv/platform/headscale --port 3000 --project PROJECT` 在已有 Headscale 准备根目录内创建持久目录和独立 cookie,或按项目/服务标签和数据目录核对唯一健康 Headscale 容器后签发 90 天管理 key。凭据仅写 root 0600 文件,不输出值;重跑保留原值,未知或中断状态停止检查。Komodo Stack 负责服务启动,控制端回收凭据并验证登录。完整顺序见 platform 的 `docs/headscale-repeatable-install.md`。
Debian 初始化时,固定 release 的 `host-init.sh --apply` 支持 `--firewall-policy /run/platform-secrets/firewall.json --firewall-script /已校验的固定release/firewall.sh`,直接调用同一个防火墙事务。脚本不再另写一套内网规则。策略仍需明确主机名、现有 SSH 端口/公网恢复来源和公网服务端口;内网全部放行由通用 firewall.sh 自动生成。新建 SSH 验证后,用 apply 返回的准确摘要执行 firewall.sh --confirm。未传防火墙输入的旧调用仅准备包和主机名,不能宣称已完成防火墙初始化。

View File

@@ -36,6 +36,8 @@ table inet platform_host {
iifname != "tailscale0" ip saddr 100.64.0.0/10 drop
ct state invalid drop
ct state established,related accept
iifname "tailscale0" ip saddr 100.64.0.0/10 accept
iifname != "tailscale0" ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 } accept
meta l4proto icmp accept
meta l4proto ipv6-icmp accept
udp sport 67 udp dport 68 accept

View File

@@ -27,15 +27,15 @@ check_existing_binding() {
[[ "$bound" == "$expected" ]] || { fail 'Existing bootstrap binding belongs to another hostname'; return 1; }
}
main() {
local action='' name='' packages='' public_key='' argument docker_mode=required
local action='' name='' packages='' public_key='' argument docker_mode=required firewall_policy='' firewall_script=''
while (($#)); do
case "$1" in
--help|-h) echo 'Usage: bash host-init.sh --check|--apply --hostname NAME [--docker-dir VERIFIED_DEB_DIRECTORY] [--ssh-key-file SHARED_PUBLIC_KEY] [--without-docker]'; return 0;;
--help|-h) echo 'Usage: bash host-init.sh --check|--apply --hostname NAME [--docker-dir VERIFIED_DEB_DIRECTORY] [--ssh-key-file SHARED_PUBLIC_KEY] [--without-docker] [--firewall-policy ROOT_0600_JSON --firewall-script VERIFIED_RELEASE_FIREWALL_SH]'; return 0;;
--without-docker) docker_mode=none; shift;;
--check|--apply) [[ -z "$action" ]] || return 1; action=$1; shift;;
--hostname|--docker-dir|--ssh-key-file)
--hostname|--docker-dir|--ssh-key-file|--firewall-policy|--firewall-script)
(($# >= 2)) || return 1
case "$1" in --hostname) name=$2;; --docker-dir) packages=$2;; --ssh-key-file) public_key=$2;; esac; shift 2;;
case "$1" in --hostname) name=$2;; --docker-dir) packages=$2;; --ssh-key-file) public_key=$2;; --firewall-policy) firewall_policy=$2;; --firewall-script) firewall_script=$2;; esac; shift 2;;
*) fail "Unknown argument: $1"; return 1;;
esac
done
@@ -71,6 +71,11 @@ main() {
ssh-keygen -l -f "$public_key" >/dev/null
[[ ! -L /root/.ssh && ! -L /root/.ssh/authorized_keys ]] || { fail 'Symlink in SSH destination'; return 1; }
fi
if [[ -n "$firewall_policy" || -n "$firewall_script" ]]; then
[[ "$firewall_policy" == /* && "$firewall_script" == /* && -f "$firewall_script" && ! -L "$firewall_script" && $(stat -c %u "$firewall_script") == 0 && $(stat -c %h "$firewall_script") == 1 ]] || { fail 'Use the verified root-owned release firewall script and explicit policy'; return 1; }
[[ -f "$firewall_policy" && ! -L "$firewall_policy" && $(stat -c %u "$firewall_policy") == 0 && $(stat -c %a "$firewall_policy") == 600 ]] || { fail 'Firewall policy must be a root-owned 0600 regular file'; return 1; }
bash "$firewall_script" --render --policy "$firewall_policy" >/dev/null
fi
[[ "$action" == --apply ]] || { echo 'Baseline inputs checked; no packages, hostname or SSH configuration changed.'; return 0; }
umask 077
exec 9>>/run/lock/platform-host-init.lock; flock -n 9 || { fail 'Host initialization is already running'; return 1; }
@@ -83,6 +88,13 @@ main() {
touch /root/.ssh/authorized_keys; chmod 0600 /root/.ssh/authorized_keys
if ! grep -Fqx -- "$(cat "$public_key")" /root/.ssh/authorized_keys; then printf '\n' >> /root/.ssh/authorized_keys; cat "$public_key" >> /root/.ssh/authorized_keys; fi
fi
if [[ -n "$firewall_policy" ]]; then
# Same native transaction as normal maintenance: all private sources allowed,
# public recovery inputs remain explicit; a fresh external SSH must confirm.
bash "$firewall_script" --check --policy "$firewall_policy"
bash "$firewall_script" --apply --policy "$firewall_policy"
echo 'Firewall pending: confirm its exact digest from a NEW SSH connection within 180 seconds.'
fi
if [[ "$docker_mode" == required ]]; then
systemctl enable --now docker
docker --host unix:///var/run/docker.sock info >/dev/null