VPS运维
VPS常用命令
新装VPS必用命令
root用户密码登录
root用户密钥登录
VPS重装系统脚本
Oracle DD系统后获取ipv6地址
ubuntu通过netplan设置静态ip
VPS流量监控脚本
rclone完全备份脚本
Oracle Cloud安装Alpine Linux
通过 CF Warp 为 IPv4/6 Only VPS 添加 IPv6/4 支持
流量转发脚本
ssh配置文件
rsync传输文件
阿里云盘CLi安装
Linux 设置IPv4优先
流量监控脚本
aliyunpan备份脚本
卸载snap
H3C CAStools (qemu-ga) AppArmor 安全策略文档
Debian 系统中关闭 IPv6
常用工具
speedtest-cli
OpenWrt接口配置示例
利用Cloudflare Tunnel实现内网穿透
Caddy示例配置文件
Clash Meta客户端配置模板
Caddy-dns的配置与使用
sing-box安装与配置
Openwrt中使用ddns-go加Caddy实现域名访问
NAT VPS 使用 Caddy 为内部服务提供 HTTPS 访问
哪吒面板8008端口访问白名单脚本
Docker
Docker 多平台镜像构建与推送教程(arm64 主机示例)
Docker 离线安装通用指南(基于官方 Static Binary)
清理旧Buildx builder
PVE
PVE实用命令
PVE温度显示以及去除弹窗脚本
Github
Cloudflare Pages 静态网页托管全流程指南
将mkdocs中的站点同步到本地git文件夹
本文档使用 MrDoc 发布
-
+
首页
哪吒面板8008端口访问白名单脚本
## 完整使用流程 ```bash # 1. 安装依赖(如果还没装) sudo apt update && sudo apt install -y ipset iptables iptables-persistent # 2. 初始化 sudo bash whitelist8008.sh init ``` `init`会自动检测8008是不是被docker容器映射出来的(在你这台机器上是,`nezha-dashboard`占用着),据此把规则写进`DOCKER-USER`链而不是`INPUT`链;创建IPv4白名单ipset集合,自动放行`127.0.0.1`和检测到的本机内网段(你这台是`10.0.0.0/24`);同时给IPv6配一条无条件DROP,不设白名单,任何IPv6地址都无法访问8008。 ```bash # 3. 把你的公网IP加进白名单(局域网已自动放行,这步是给公网访问用的) sudo bash whitelist8008.sh add 你的公网IP # 4. 持久化(写入磁盘 + 配置开机自动恢复) sudo bash whitelist8008.sh save # 5. 确认一切生效 sudo bash whitelist8008.sh status ``` `status`会显示ipset集合内容、IPv4/IPv6在`INPUT`和`DOCKER-USER`两条链上的规则、docker端口映射情况,以及持久化状态(是否已启用开机自动恢复、磁盘保存内容是否和当前一致)。 日常管理就这几个命令: ```bash sudo bash whitelist8008.sh add 1.2.3.4 # 新增一个公网IP sudo bash whitelist8008.sh del 1.2.3.4 # 移除一个IP bash whitelist8008.sh list # 查看当前白名单(会标注哪些是自动加入的内网段) sudo bash whitelist8008.sh save # add/del之后必须重新执行,否则重启后会丢失改动 ``` 有两点需要记住。第一,**每次`add`或`del`之后一定要重新跑`save`**,脚本会在操作完成后提示你这一点,否则磁盘上保存的白名单是旧版本,重启后新加的IP会消失。第二,如果以后想临时换种判断方式(比如8008变成宿主机原生进程而不是docker映射了),可以用环境变量强制指定:`sudo WHITELIST_MODE=input bash whitelist8008.sh init`,默认是`auto`自动判断不需要手动指定。 ## 脚本`whitelist8008.sh` ```bash #!/usr/bin/env bash # # whitelist8008.sh —— 8008 端口入站访问控制脚本(IPv4白名单 + IPv6整体阻断,适配 Docker -p 映射) # 依赖:iptables, ip6tables, ipset # # 策略: # - IPv4:白名单机制,只有加入白名单的地址才能访问,其余 DROP # - IPv6:默认整体阻断,无论什么地址一律 DROP,没有豁免/白名单 # # 适用场景:8008 端口由 docker 容器通过 `-p 8008:8008` 映射出来(例如 nezha-dashboard), # 此时流量走 FORWARD/DOCKER 链而非 INPUT 链,必须把规则写入 DOCKER-USER 链才会生效。 # # 用法: # ./whitelist8008.sh init 初始化(首次使用先执行) # ./whitelist8008.sh add <ip> [ip2 ...] 添加 IPv4 到白名单 # ./whitelist8008.sh del <ip> [ip2 ...] 从白名单删除 IPv4 # ./whitelist8008.sh list 列出当前白名单 # ./whitelist8008.sh status 查看相关规则与docker端口映射情况 # ./whitelist8008.sh save 持久化规则(重启后依然生效) # set -euo pipefail PORT=8008 SET4_NAME="whitelist_${PORT}_v4" # 说明:IPv6 不设白名单机制,直接默认全部阻断对该端口的IPv6访问。 # 如果之后需要放开某些IPv6地址,再改回白名单模式即可。 # 默认放行的局域网段:自动检测本机网卡所在的实际子网(而非整个RFC1918大段), # 例如检测到 10.0.0.227/24 时,只放行 10.0.0.0/24 这一个 /24 子网, # 不会放开 10.0.0.0/8 这种可能横跨多个客户VPC的超大范围。 # 可通过环境变量 LAN_CIDR_OVERRIDE 手动指定(如换了网卡/机房时),多个用逗号分隔。 LAN_CIDR_OVERRIDE="${LAN_CIDR_OVERRIDE:-}" # 将 ip/prefix(如 10.0.0.227/24)转换成网段(如 10.0.0.0/24),纯bash实现,无外部依赖 ip_to_network_cidr() { local ip_cidr="$1" local ip="${ip_cidr%/*}" local prefix="${ip_cidr#*/}" [[ "$prefix" =~ ^[0-9]+$ ]] || return 1 [[ "$prefix" -ge 0 && "$prefix" -le 32 ]] || return 1 IFS='.' read -r o1 o2 o3 o4 <<< "$ip" for o in "$o1" "$o2" "$o3" "$o4"; do [[ "$o" =~ ^[0-9]+$ ]] && [[ "$o" -ge 0 && "$o" -le 255 ]] || return 1 done # ip -> 32位整数 local ip_int=$(( (o1 << 24) + (o2 << 16) + (o3 << 8) + o4 )) # 子网掩码整数 local mask_int=0 if [[ "$prefix" -gt 0 ]]; then mask_int=$(( 0xFFFFFFFF << (32 - prefix) & 0xFFFFFFFF )) fi local net_int=$(( ip_int & mask_int )) local n1=$(( (net_int >> 24) & 255 )) local n2=$(( (net_int >> 16) & 255 )) local n3=$(( (net_int >> 8) & 255 )) local n4=$(( net_int & 255 )) echo "${n1}.${n2}.${n3}.${n4}/${prefix}" } detect_lan_cidrs() { if [[ -n "$LAN_CIDR_OVERRIDE" ]]; then echo "$LAN_CIDR_OVERRIDE" | tr ',' '\n' return fi # 从所有非lo网卡里提取 "inet a.b.c.d/n" 形式的地址,转换成对应的网段CIDR # 例如 10.0.0.227/24 -> 10.0.0.0/24 local cidr net while read -r cidr; do [[ -z "$cidr" ]] && continue if net="$(ip_to_network_cidr "$cidr")"; then echo "$net" fi done < <(ip -4 -o addr show scope global 2>/dev/null | awk '{print $4}') } # 模式:auto(自动判断)/ input(强制走 INPUT 链,宿主机原生进程) # / docker(强制走 DOCKER-USER 链,端口是 docker -p 映射出来的容器) MODE="${WHITELIST_MODE:-auto}" require_root() { if [[ "$(id -u)" -ne 0 ]]; then echo "请使用 root 权限运行(例如:sudo $0 $*)" >&2 exit 1 fi } require_tools() { local missing=0 for bin in ipset iptables ip6tables; do if ! command -v "$bin" >/dev/null 2>&1; then echo "未检测到 $bin,请先安装:apt update && apt install -y ipset iptables" >&2 missing=1 fi done [[ $missing -eq 0 ]] || exit 1 } detect_chain() { if [[ "$MODE" == "input" ]]; then echo "INPUT"; return fi if [[ "$MODE" == "docker" ]]; then echo "DOCKER-USER"; return fi # auto 模式:DOCKER-USER 链存在 且 docker 确实把该端口映射出来了 if iptables -L DOCKER-USER -n >/dev/null 2>&1; then if command -v docker >/dev/null 2>&1 && \ docker ps --format '{{.Ports}}' 2>/dev/null | grep -q ":${PORT}->"; then echo "DOCKER-USER"; return fi fi echo "INPUT" } # 判断字符串是否为合法 IPv4(含CIDR)。该脚本不再支持添加IPv6到白名单, # 因为IPv6访问8008端口已被默认整体阻断。 classify_ip() { local ip="$1" if [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}(/[0-9]{1,2})?$ ]]; then echo "v4"; return fi if [[ "$ip" =~ : ]]; then echo "v6"; return fi echo "" } ensure_chain_rules() { # $1 = ip4tables|ip6tables 命令名, $2 = chain, $3 = ipset集合名 local IPT="$1" chain="$2" setname="$3" if ! "$IPT" -C "$chain" -p tcp --dport "$PORT" -m set --match-set "$setname" src -j ACCEPT 2>/dev/null; then "$IPT" -I "$chain" -p tcp --dport "$PORT" -m set --match-set "$setname" src -j ACCEPT echo " [$IPT] 已添加 ACCEPT 规则(链:$chain,集合:$setname)" else echo " [$IPT] ACCEPT 规则已存在,跳过" fi if ! "$IPT" -C "$chain" -p tcp --dport "$PORT" -j DROP 2>/dev/null; then if [[ "$chain" == "DOCKER-USER" ]]; then # DOCKER-USER 链末尾默认有 RETURN,DROP 必须插在其之前 "$IPT" -I "$chain" 2 -p tcp --dport "$PORT" -j DROP else "$IPT" -A "$chain" -p tcp --dport "$PORT" -j DROP fi echo " [$IPT] 已添加 DROP 规则(链:$chain)" else echo " [$IPT] DROP 规则已存在,跳过" fi } ensure_v6_block_all() { # $1 = chain,对该链上的8008端口IPv6流量无条件DROP,不设白名单 local chain="$1" if ! ip6tables -C "$chain" -p tcp --dport "$PORT" -j DROP 2>/dev/null; then if [[ "$chain" == "DOCKER-USER" ]]; then ip6tables -I "$chain" 1 -p tcp --dport "$PORT" -j DROP else ip6tables -A "$chain" -p tcp --dport "$PORT" -j DROP fi echo " [ip6tables] 已添加 DROP 规则(链:$chain,无条件阻断所有IPv6访问)" else echo " [ip6tables] DROP 规则已存在,跳过" fi } cmd_init() { require_root require_tools local chain chain="$(detect_chain)" echo "检测到写入链:$chain" if [[ "$chain" == "DOCKER-USER" ]]; then echo "(判断依据:检测到 docker 已将 $PORT 端口映射到容器,走 INPUT 链会被绕过)" fi echo # 创建 IPv4 ipset 集合 if ! ipset list "$SET4_NAME" >/dev/null 2>&1; then ipset create "$SET4_NAME" hash:net echo "已创建 IPv4 ipset 集合:$SET4_NAME" fi echo "已添加 127.0.0.1/32(本机回环)" ipset add "$SET4_NAME" "127.0.0.1/32" -exist echo "检测本机网卡所在子网,自动放行(而非整个RFC1918大段):" local found_any=0 while read -r cidr; do [[ -z "$cidr" ]] && continue ipset add "$SET4_NAME" "$cidr" -exist echo " 已加入:$cidr" found_any=1 done < <(detect_lan_cidrs) if [[ "$found_any" -eq 0 ]]; then echo " (未检测到内网网卡地址,跳过;如果你知道实际内网段,可用" echo " sudo LAN_CIDR_OVERRIDE=10.0.0.0/24 $0 init 手动指定后重新初始化," echo " 或直接用 $0 add 10.0.0.0/24 手动添加)" fi echo echo "配置 IPv4 白名单规则(iptables):" ensure_chain_rules iptables "$chain" "$SET4_NAME" echo "配置 IPv6 规则(ip6tables,默认全部阻断,不设白名单):" ensure_v6_block_all "$chain" echo echo "初始化完成。" echo " - IPv4:已默认放行本机回环地址 + 检测到的内网子网(见上方输出)," echo " 其余公网地址需要手动执行:$0 add <你的公网IP> 才能访问。" echo " - IPv6:已默认整体阻断,无白名单机制,任何IPv6地址都无法访问 $PORT。" } cmd_add() { require_root require_tools if [[ $# -eq 0 ]]; then echo "用法:$0 add <ip> [ip2 ...]" >&2 exit 1 fi local changed=0 for ip in "$@"; do local kind kind="$(classify_ip "$ip")" case "$kind" in v4) ipset add "$SET4_NAME" "$ip" -exist echo "已添加(IPv4):$ip" changed=1 ;; v6) echo "忽略:'$ip' 是 IPv6 地址,本脚本已默认整体阻断IPv6访问,不支持单独放行" >&2 ;; *) echo "忽略:'$ip' 不是有效的 IP/CIDR 格式" >&2 ;; esac done if [[ "$changed" -eq 1 ]]; then echo "提示:白名单已更新但尚未持久化,重启后会丢失。请执行:$0 save" fi } cmd_del() { require_root require_tools if [[ $# -eq 0 ]]; then echo "用法:$0 del <ip> [ip2 ...]" >&2 exit 1 fi local changed=0 for ip in "$@"; do local kind kind="$(classify_ip "$ip")" case "$kind" in v4) if [[ "$ip" == "127.0.0.1/32" ]] || detect_lan_cidrs | grep -qx "$ip"; then echo "注意:'$ip' 是本机回环/自动检测的内网段,删除后该范围将无法访问 $PORT" >&2 fi if ipset del "$SET4_NAME" "$ip" 2>/dev/null; then echo "已删除(IPv4):$ip" changed=1 else echo "未找到或删除失败(IPv4):$ip" >&2 fi ;; v6) echo "忽略:IPv6 没有白名单机制(已默认整体阻断),无需删除" >&2 ;; *) echo "忽略:'$ip' 不是有效的 IP/CIDR 格式" >&2 ;; esac done if [[ "$changed" -eq 1 ]]; then echo "提示:白名单已更新但尚未持久化,重启后会恢复成旧内容。请执行:$0 save" fi } cmd_list() { require_tools echo "== IPv4 白名单($SET4_NAME) ==" if ipset list "$SET4_NAME" >/dev/null 2>&1; then local members members="$(ipset list "$SET4_NAME" | sed -n '/Members:/,$p' | tail -n +2)" echo "$members" echo echo "(其中以下为本机回环/内网段,由init自动检测加入,并非手动添加:)" if echo "$members" | grep -qx "127.0.0.1/32"; then echo " - 127.0.0.1/32 (本机回环)" fi while read -r cidr; do [[ -z "$cidr" ]] && continue if echo "$members" | grep -qx "$cidr"; then echo " - $cidr (内网自动检测)" fi done < <(detect_lan_cidrs) else echo "(尚未初始化)" fi echo echo "== IPv6 ==" echo "已默认整体阻断,无白名单(无论哪个IPv6地址都无法访问 $PORT)" } cmd_status() { require_tools echo "== ipset 集合(IPv4白名单) ==" ipset list "$SET4_NAME" 2>/dev/null || echo "(IPv4 集合未创建)" echo echo "== INPUT 链(IPv4) ==" iptables -L INPUT -n -v --line-numbers 2>/dev/null | grep -E "Chain INPUT|dpt:$PORT" || echo "(未找到相关规则)" echo "== INPUT 链(IPv6,应为整体DROP) ==" ip6tables -L INPUT -n -v --line-numbers 2>/dev/null | grep -E "Chain INPUT|dpt:$PORT" || echo "(未找到相关规则)" echo if iptables -L DOCKER-USER -n >/dev/null 2>&1; then echo "== DOCKER-USER 链(IPv4) ==" iptables -L DOCKER-USER -n -v --line-numbers | grep -E "Chain DOCKER-USER|dpt:$PORT" || echo "(未找到相关规则)" echo "== DOCKER-USER 链(IPv6,应为整体DROP) ==" ip6tables -L DOCKER-USER -n -v --line-numbers 2>/dev/null | grep -E "Chain DOCKER-USER|dpt:$PORT" || echo "(未找到相关规则,或 ip6tables 未启用 docker 管理)" echo echo "== docker 端口映射情况(grep $PORT) ==" docker ps --format "table {{.Names}}\t{{.Ports}}" 2>/dev/null | grep -E "NAMES|:${PORT}->" || echo "(没有容器映射该端口,或 docker 未安装/无权限)" fi echo echo "== 持久化状态 ==" local unit_file="/etc/systemd/system/ipset-restore-whitelist8008.service" local ipset_file="/etc/ipset/whitelist8008.rules" if [[ -f "$unit_file" ]] && systemctl is-enabled ipset-restore-whitelist8008.service >/dev/null 2>&1; then echo " 开机自动恢复:已启用($unit_file)" else echo " 开机自动恢复:未配置,重启后ipset白名单会丢失,请执行:$0 save" fi if [[ -f "$ipset_file" ]]; then local saved_md5 current_md5 saved_md5="$(md5sum "$ipset_file" 2>/dev/null | awk '{print $1}')" current_md5="$(ipset save "$SET4_NAME" 2>/dev/null | md5sum | awk '{print $1}')" if [[ "$saved_md5" == "$current_md5" ]]; then echo " 磁盘保存内容:与当前白名单一致" else echo " 磁盘保存内容:与当前白名单不一致(有未保存的改动),请执行:$0 save" fi else echo " 磁盘保存内容:尚未保存过,请执行:$0 save" fi } cmd_save() { require_root require_tools local ipset_dir="/etc/ipset" local ipset_file="${ipset_dir}/whitelist8008.rules" local unit_file="/etc/systemd/system/ipset-restore-whitelist8008.service" # 1) 持久化 iptables / ip6tables 规则本身 if command -v netfilter-persistent >/dev/null 2>&1; then netfilter-persistent save echo "已通过 netfilter-persistent 持久化 iptables/ip6tables 规则" else echo "未检测到 netfilter-persistent,正在尝试安装 iptables-persistent ..." if DEBIAN_FRONTEND=noninteractive apt-get install -y iptables-persistent >/tmp/iptables-persistent-install.log 2>&1; then netfilter-persistent save echo "已安装 iptables-persistent 并持久化规则" else echo "自动安装失败,请手动执行:apt install -y iptables-persistent" >&2 echo "(详细日志见 /tmp/iptables-persistent-install.log)" >&2 fi fi # 2) 持久化 ipset 白名单内容本身(重启后ipset集合会丢失,必须单独保存+开机恢复) mkdir -p "$ipset_dir" ipset save "$SET4_NAME" > "$ipset_file" echo "已保存 ipset 白名单内容到:$ipset_file" # 3) 写一个 systemd service,在 docker 启动前先把 ipset 集合恢复出来 # 避免开机时 iptables 规则引用一个还不存在的 ipset 集合而报错/失效, # 也避免 docker 先于白名单生效造成短暂的不设防窗口。 cat > "$unit_file" <<EOF [Unit] Description=Restore ipset whitelist for port ${PORT} before Docker starts DefaultDependencies=no Before=docker.service network-pre.target Wants=network-pre.target [Service] Type=oneshot ExecStartPre=/sbin/ipset create ${SET4_NAME} hash:net -exist ExecStart=/bin/bash -c '/sbin/ipset restore < ${ipset_file}' RemainAfterExit=yes [Install] WantedBy=multi-user.target sysinit.target EOF echo "已生成 systemd service:$unit_file" systemctl daemon-reload systemctl enable ipset-restore-whitelist8008.service >/tmp/systemd-enable.log 2>&1 \ && echo "已启用开机自动恢复(ipset-restore-whitelist8008.service)" \ || echo "启用失败,请检查 /tmp/systemd-enable.log" >&2 echo echo "持久化完成。现在整机重启后会按以下顺序自动恢复:" echo " 1) systemd 在网络/docker启动前执行 ipset-restore-whitelist8008.service," echo " 从 $ipset_file 恢复白名单内容到 ipset 集合" echo " 2) netfilter-persistent 恢复 iptables/ip6tables 规则(引用该 ipset 集合)" echo " 3) docker 服务启动,DOCKER-USER 链中的规则已经可以正确匹配白名单" echo echo "提示:以后每次用 add/del 修改白名单后,请重新执行一次 $0 save," echo " 这样保存到磁盘的 $ipset_file 才会同步最新内容,否则重启后会恢复成上次 save 时的旧名单。" } cmd_save_test() { # 内部自检:验证生成的 systemd unit 文件语法是否正确(不实际重启服务) require_root local unit_file="/etc/systemd/system/ipset-restore-whitelist8008.service" if [[ ! -f "$unit_file" ]]; then echo "尚未生成 $unit_file,请先执行:$0 save" >&2 exit 1 fi systemd-analyze verify "$unit_file" && echo "systemd unit 文件语法检查通过" } usage() { cat <<EOF 用法:$0 <command> [args] 命令: init 初始化(IPv4白名单机制 + IPv6默认整体阻断) add <ip> [ip2 ...] 添加 IPv4 到白名单(不支持IPv6,IPv6已整体阻断) del <ip> [ip2 ...] 删除 IPv4 白名单中的IP list 查看当前白名单状态 status 查看规则详情(含 DOCKER-USER 链与端口映射) save 持久化规则到磁盘 + 配置开机自动恢复(重启服务器后依然生效) 说明: 本脚本对 $PORT 端口采用「IPv4白名单 + IPv6全部阻断」策略: - IPv4:默认放行 127.0.0.1(回环)+ 自动检测到的本机内网子网 (例如检测到 10.0.0.227/24 则放行 10.0.0.0/24,而非整个 10.0.0.0/8),其余公网地址需手动 add 才能访问 - IPv6:无论什么地址,一律拒绝访问,没有豁免机制 如果自动检测不准确(如多网卡、换机房),可用环境变量手动指定: LAN_CIDR_OVERRIDE=10.0.0.0/24,172.16.5.0/24 持久化机制(save命令做的事): 1) 用 netfilter-persistent 保存 iptables/ip6tables 规则本身 2) 把当前 ipset 白名单内容导出到 /etc/ipset/whitelist8008.rules 3) 生成并启用一个 systemd service,在 docker 启动前自动恢复该 ipset 内容 每次 add/del 后请重新执行一次 save,否则重启后会恢复成上次保存时的旧名单。 环境变量: WHITELIST_MODE=auto|input|docker auto(默认):自动判断 8008 是否为 docker -p 映射端口 input :强制写入 INPUT 链 docker :强制写入 DOCKER-USER 链 示例: sudo $0 init sudo $0 add 203.0.113.10 sudo $0 add 203.0.113.0/24 198.51.100.5 sudo $0 del 203.0.113.10 $0 list $0 status sudo $0 save # 若自动检测的内网段不准确,手动指定后重新初始化: sudo LAN_CIDR_OVERRIDE=10.0.0.0/24 $0 init EOF } main() { local cmd="${1:-}" shift || true case "$cmd" in init) cmd_init "$@" ;; add) cmd_add "$@" ;; del) cmd_del "$@" ;; list) cmd_list "$@" ;; status) cmd_status "$@" ;; save) cmd_save "$@" ;; *) usage; exit 1 ;; esac } main "$@" ```
koalalove
2026年6月17日 22:03
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码