吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 536|回复: 6
上一主题 下一主题
收起左侧

[学习记录] iStoreOS 防火墙端口管理脚本+安装 LuCI页面控制

[复制链接]
跳转到指定楼层
楼主
liyu0828 发表于 2026-8-23 19:46 回帖奖励
本帖最后由 liyu0828 于 2026-8-24 15:18 编辑

这个脚本可以一键添加 / 删除 / 查看端口转发,自动同时配置 IPv4 DNAT 和 IPv6 放行

测试系统版本为:istoreos-24.10.8-2026073111-x86-64

默认ip自行修改这一行:DEFAULT_TARGET="192.168.31.196"


ssh功能
  • fwport list - 列出所有端口规则及命中计数
  • fwport add <端口> <协议> [目标IP] - 添加端口转发(IPv4+IPv6 自动配)
  • fwport del <端口> <协议> - 删除端口转发
  • fwport backup - 备份防火墙配置
  • fwport restore <文件> - 恢复配置
  • fwport restart - 重启防火墙
  • fwport test <端口> <协议> - 测试端口连通性

页面功能
  • 顶部表单:填端口、选协议、填目标 IPv4,点添加 → 自动建 IPv4+IPv6 两条规则
  • IPv4 表格:显示所有 DNAT 转发规则,每行有删除按钮
  • IPv6 表格:显示所有 wan→lan 的 IPv6 放行规则,每行有删除按钮
  • 删除时弹确认框,按端口 + 协议同时删两边的规则



创建脚本
在 iStoreOS ssh执行:
[Asm] 纯文本查看 复制代码
#!/bin/ash
# ============================================================
# luci-app-fwport v2 一键安装脚本
# 后端: /usr/sbin/fwport (IPv4 DNAT + IPv6 放行)
# 前端: LuCI 自定义视图 (网络 -> 端口转发管理)
# ============================================================

set -e

echo "=========================================="
echo "  luci-app-fwport v2 安装"
echo "  IPv4端口转发 + IPv6端口放行 统一管理"
echo "=========================================="

# ---------- 清理旧版 ----------
echo "[1/6] 清理旧版文件..."
rm -f /usr/lib/lua/luci/controller/fwport.lua 2>/dev/null || true
rm -rf /usr/lib/lua/luci/model/cbi/fwport 2>/dev/null || true
rm -f /usr/bin/fwport /bin/fwport 2>/dev/null || true
echo "  旧版已清理"

# ---------- 创建目录 ----------
echo "[2/6] 创建目录..."
mkdir -p /usr/sbin
mkdir -p /usr/lib/lua/luci/controller
mkdir -p /usr/lib/lua/luci/view/fwport
mkdir -p /etc/config
echo "  目录就绪"

# ---------- 写入后端脚本 ----------
echo "[3/6] 安装后端脚本 /usr/sbin/fwport..."
cat > /usr/sbin/fwport <<'SCRIPT_EOF'
#!/bin/ash
DEFAULT_TARGET="192.168.31.196"

if [ -f "/etc/config/fwport" ]; then
    dt=$(uci get fwport.@global[0].default_target 2>/dev/null || echo "")
    [ -n "$dt" ] && DEFAULT_TARGET="$dt"
fi

info() { echo -e "\033[0;32m[INFO]\033[0m $*"; }
warn() { echo -e "\033[1;33m[WARN]\033[0m $*"; }
error() { echo -e "\033[0;31m[ERROR]\033[0m $*"; }

cmd_list() {
    echo "===== IPv4 端口转发 (DNAT) ====="
    printf "%-20s %-8s %-15s %-25s %-8s\n" "名称" "协议" "外部端口" "目标" "状态"
    echo "---------------------------------------------------------------"
    idx=0
    while true; do
        name=$(uci get firewall.@redirect[$idx].name 2>/dev/null)
        [ -z "$name" ] && break
        proto=$(uci get firewall.@redirect[$idx].proto 2>/dev/null)
        sp=$(uci get firewall.@redirect[$idx].src_dport 2>/dev/null)
        dip=$(uci get firewall.@redirect[$idx].dest_ip 2>/dev/null)
        dp=$(uci get firewall.@redirect[$idx].dest_port 2>/dev/null)
        enabled=$(uci get firewall.@redirect[$idx].enabled 2>/dev/null || echo "1")
        [ -z "$dp" ] && dp="$sp"
        [ "$enabled" = "0" ] && status="禁用" || status="启用"
        printf "%-20s %-8s %-15s %-25s %-8s\n" "$name" "$proto" "$sp" "${dip}:${dp}" "$status"
        idx=$((idx+1))
    done
    echo ""
    echo "===== IPv6 端口放行 ====="
    printf "%-25s %-8s %-15s %-8s\n" "名称" "协议" "端口" "状态"
    echo "---------------------------------------------------"
    idx=0
    while true; do
        name=$(uci get firewall.@rule[$idx].name 2>/dev/null)
        [ -z "$name" ] && break
        fam=$(uci get firewall.@rule[$idx].family 2>/dev/null)
        src=$(uci get firewall.@rule[$idx].src 2>/dev/null)
        dst=$(uci get firewall.@rule[$idx].dest 2>/dev/null)
        proto=$(uci get firewall.@rule[$idx].proto 2>/dev/null)
        dp=$(uci get firewall.@rule[$idx].dest_port 2>/dev/null)
        enabled=$(uci get firewall.@rule[$idx].enabled 2>/dev/null || echo "1")
        if [ "$fam" = "ipv6" ] && [ "$src" = "wan" ] && [ "$dst" = "lan" ] && [ -n "$dp" ]; then
            [ "$enabled" = "0" ] && status="禁用" || status="启用"
            printf "%-25s %-8s %-15s %-8s\n" "$name" "$proto" "$dp" "$status"
        fi
        idx=$((idx+1))
    done
}

cmd_add() {
    port="$1"; proto="$2"; target="${3:-$DEFAULT_TARGET}"
    [ -z "$port" ] && { error "用法: fwport add <端口> <tcp|udp> [目标IP]"; exit 1; }
    [ -z "$proto" ] && proto="tcp"
    info "添加 $port/$proto -> $target"
    exists=0; idx=0
    while true; do
        name=$(uci get firewall.@redirect[$idx].name 2>/dev/null)
        [ -z "$name" ] && break
        sp=$(uci get firewall.@redirect[$idx].src_dport 2>/dev/null)
        pr=$(uci get firewall.@redirect[$idx].proto 2>/dev/null)
        [ "$sp" = "$port" ] && [ "$pr" = "$proto" ] && { exists=1; warn "IPv4已存在: $name"; }
        idx=$((idx+1))
    done
    if [ $exists -eq 0 ]; then
        uci add firewall redirect > /dev/null
        uci set firewall.@redirect[-1].name="Forward-${port}"
        uci set firewall.@redirect[-1].src="wan"
        uci set firewall.@redirect[-1].proto="$proto"
        uci set firewall.@redirect[-1].src_dport="$port"
        uci set firewall.@redirect[-1].dest="lan"
        uci set firewall.@redirect[-1].dest_ip="$target"
        uci set firewall.@redirect[-1].dest_port="$port"
        uci set firewall.@redirect[-1].target="DNAT"
        info "  [OK] IPv4 DNAT"
    fi
    exists=0; idx=0
    while true; do
        name=$(uci get firewall.@rule[$idx].name 2>/dev/null)
        [ -z "$name" ] && break
        fam=$(uci get firewall.@rule[$idx].family 2>/dev/null)
        dp=$(uci get firewall.@rule[$idx].dest_port 2>/dev/null)
        pr=$(uci get firewall.@rule[$idx].proto 2>/dev/null)
        [ "$fam" = "ipv6" ] && [ "$dp" = "$port" ] && [ "$pr" = "$proto" ] && { exists=1; warn "IPv6已存在: $name"; }
        idx=$((idx+1))
    done
    if [ $exists -eq 0 ]; then
        uci add firewall rule > /dev/null
        uci set firewall.@rule[-1].name="Allow-IPv6-${port}"
        uci set firewall.@rule[-1].src="wan"
        uci set firewall.@rule[-1].proto="$proto"
        uci set firewall.@rule[-1].dest="lan"
        uci set firewall.@rule[-1].dest_port="$port"
        uci set firewall.@rule[-1].family="ipv6"
        uci set firewall.@rule[-1].target="ACCEPT"
        info "  [OK] IPv6 放行"
    fi
    uci commit firewall
    /etc/init.d/firewall restart > /dev/null 2>&1
    info "完成!"
}

cmd_del() {
    port="$1"; proto="$2"
    [ -z "$port" ] && { error "用法: fwport del <端口> <tcp|udp>"; exit 1; }
    [ -z "$proto" ] && proto="tcp"
    info "删除 $port/$proto"
    deleted=0; idx=0
    while true; do
        name=$(uci get firewall.@redirect[$idx].name 2>/dev/null)
        [ -z "$name" ] && break
        sp=$(uci get firewall.@redirect[$idx].src_dport 2>/dev/null)
        pr=$(uci get firewall.@redirect[$idx].proto 2>/dev/null)
        if [ "$sp" = "$port" ] && [ "$pr" = "$proto" ]; then
            uci delete firewall.@redirect[$idx]
            info "  [OK] 删除IPv4: $name"
            deleted=1
            continue
        fi
        idx=$((idx+1))
    done
    idx=0
    while true; do
        name=$(uci get firewall.@rule[$idx].name 2>/dev/null)
        [ -z "$name" ] && break
        fam=$(uci get firewall.@rule[$idx].family 2>/dev/null)
        dp=$(uci get firewall.@rule[$idx].dest_port 2>/dev/null)
        pr=$(uci get firewall.@rule[$idx].proto 2>/dev/null)
        if [ "$fam" = "ipv6" ] && [ "$dp" = "$port" ] && [ "$pr" = "$proto" ]; then
            uci delete firewall.@rule[$idx]
            info "  [OK] 删除IPv6: $name"
            deleted=1
            continue
        fi
        idx=$((idx+1))
    done
    [ $deleted -eq 0 ] && { warn "未找到匹配规则"; return; }
    uci commit firewall
    /etc/init.d/firewall restart > /dev/null 2>&1
    info "完成!"
}

usage() {
    echo "iStoreOS 防火墙端口管理工具"
    echo ""
    echo "用法: fwport <命令> [参数]"
    echo ""
    echo "命令:"
    echo "  list                    列出所有规则"
    echo "  add <端口> <协议> [IP]  添加端口 (默认目标: $DEFAULT_TARGET)"
    echo "  del <端口> <协议>       删除端口"
    echo "  backup                  备份配置"
    echo "  restart                 重启防火墙"
}

case "$1" in
    list)    cmd_list ;;
    add)     shift; cmd_add "$@" ;;
    del)     shift; cmd_del "$@" ;;
    backup)  cp /etc/config/firewall /root/firewall-$(date +%Y%m%d-%H%M%S).bak; info "已备份到 /root/" ;;
    restart) /etc/init.d/firewall restart; info "已重启" ;;
    *)       usage ;;
esac
SCRIPT_EOF

chmod +x /usr/sbin/fwport
ln -sf /usr/sbin/fwport /bin/fwport
echo "  后端脚本已安装"

# ---------- 写入配置文件 ----------
echo "[4/6] 安装配置文件..."
if [ ! -f "/etc/config/fwport" ]; then
    cat > /etc/config/fwport <<'CONF_EOF'

config global
        option default_target '192.168.31.196'
CONF_EOF
    echo "  配置文件已创建: /etc/config/fwport"
else
    echo "  配置文件已存在,跳过"
fi

# ---------- 写入LuCI控制器 ----------
echo "[5/6] 安装 LuCI 页面..."
cat > /usr/lib/lua/luci/controller/fwport.lua <<'CTL_EOF'
module("luci.controller.fwport", package.seeall)

function index()
    entry({"admin", "network", "fwport"}, call("action_main"), _("端口转发管理"), 90)
end

function action_main()
    local http = require "luci.http"
    local uci = require "luci.model.uci".cursor()

    if http.formvalue("add") then
        local port = http.formvalue("port")
        local proto = http.formvalue("proto") or "tcp"
        local target = http.formvalue("target")
        if port and port ~= "" then
            local cmd = "/usr/sbin/fwport add " .. port .. " " .. proto
            if target and target ~= "" then
                cmd = cmd .. " " .. target
            end
            luci.sys.call(cmd .. " >/dev/null 2>&1")
        end
        http.redirect(luci.dispatcher.build_url("admin", "network", "fwport"))
        return
    end

    if http.formvalue("del") then
        local port = http.formvalue("del_port")
        local proto = http.formvalue("del_proto") or "tcp"
        if port and port ~= "" then
            luci.sys.call("/usr/sbin/fwport del " .. port .. " " .. proto .. " >/dev/null 2>&1")
        end
        http.redirect(luci.dispatcher.build_url("admin", "network", "fwport"))
        return
    end

    local default_target = uci:get("fwport", "global", "default_target") or "192.168.31.196"

    local ipv4_rules = {}
    local idx = 0
    while true do
        local section = "@redirect[" .. idx .. "]"
        local name = uci:get("firewall", section, "name")
        if not name then break end
        local proto = uci:get("firewall", section, "proto") or "tcp"
        local src_dport = uci:get("firewall", section, "src_dport") or ""
        local dest_ip = uci:get("firewall", section, "dest_ip") or ""
        local dest_port = uci:get("firewall", section, "dest_port") or src_dport
        local enabled = uci:get("firewall", section, "enabled") or "1"
        table.insert(ipv4_rules, {
            name = name, proto = proto, src_dport = src_dport,
            dest_ip = dest_ip, dest_port = dest_port, enabled = enabled
        })
        idx = idx + 1
    end

    local ipv6_rules = {}
    idx = 0
    while true do
        local section = "@rule[" .. idx .. "]"
        local name = uci:get("firewall", section, "name")
        if not name then break end
        local family = uci:get("firewall", section, "family") or ""
        local src = uci:get("firewall", section, "src") or ""
        local dest = uci:get("firewall", section, "dest") or ""
        local dest_port = uci:get("firewall", section, "dest_port") or ""
        local proto = uci:get("firewall", section, "proto") or "tcp"
        local enabled = uci:get("firewall", section, "enabled") or "1"
        if family == "ipv6" and src == "wan" and dest == "lan" and dest_port ~= "" then
            table.insert(ipv6_rules, {
                name = name, proto = proto, dest_port = dest_port, enabled = enabled
            })
        end
        idx = idx + 1
    end

    luci.template.render("fwport/main", {
        default_target = default_target,
        ipv4_rules = ipv4_rules,
        ipv6_rules = ipv6_rules
    })
end
CTL_EOF

# ---------- 写入视图模板 ----------
cat > /usr/lib/lua/luci/view/fwport/main.htm <<'VIEW_EOF'
<%+header%>

<h2 name="content"><%:端口转发管理%></h2>

<fieldset class="cbi-section">
        <legend><%:添加端口规则%></legend>
        <div class="cbi-section-descr"><%:添加后自动创建 IPv4 DNAT 转发 + IPv6 放行两条规则%></div>
        <form method="post" action="<%=luci.dispatcher.build_url("admin", "network", "fwport")%>">
                <table class="cbi-section-table" style="width:auto">
                        <tr>
                                <td class="cbi-value-field" style="padding-right:10px">
                                        <input type="text" name="port" placeholder="<%:端口 如 8080%>" style="width:100px" required>
                                </td>
                                <td class="cbi-value-field" style="padding-right:10px">
                                        <select name="proto">
                                                <option value="tcp">TCP</option>
                                                <option value="udp">UDP</option>
                                        </select>
                                </td>
                                <td class="cbi-value-field" style="padding-right:10px">
                                        <input type="text" name="target" value="<%=default_target%>" placeholder="<%:目标IPv4%>" style="width:140px">
                                </td>
                                <td>
                                        <input type="submit" name="add" value="<%:添加%>" class="cbi-button cbi-button-apply">
                                </td>
                        </tr>
                </table>
        </form>
</fieldset>

<fieldset class="cbi-section">
        <legend><%:IPv4 端口转发 (DNAT)%></legend>
        <table class="cbi-section-table">
                <tr class="cbi-section-table-titles">
                        <th class="cbi-section-table-cell"><%:名称%></th>
                        <th class="cbi-section-table-cell"><%:协议%></th>
                        <th class="cbi-section-table-cell"><%:外部端口%></th>
                        <th class="cbi-section-table-cell"><%:目标地址%></th>
                        <th class="cbi-section-table-cell"><%:状态%></th>
                        <th class="cbi-section-table-cell"><%:操作%></th>
                </tr>
                <% local i = 0; for _, r in ipairs(ipv4_rules) do i = i + 1 %>
                <tr class="cbi-section-table-row cbi-rowstyle-<%=(i % 2 == 0 and 2 or 1)%>">
                        <td class="cbi-value-field"><%=r.name%></td>
                        <td class="cbi-value-field"><%=r.proto:upper()%></td>
                        <td class="cbi-value-field"><%=r.src_dport%></td>
                        <td class="cbi-value-field"><%=r.dest_ip%>:<%=r.dest_port%></td>
                        <td class="cbi-value-field">
                                <% if r.enabled == "0" then %>
                                        <span style="color:#cc0000"><%:已禁用%></span>
                                <% else %>
                                        <span style="color:#008800"><%:已启用%></span>
                                <% end %>
                        </td>
                        <td class="cbi-value-field">
                                <form method="post" action="<%=luci.dispatcher.build_url("admin", "network", "fwport")%>" style="display:inline;margin:0">
                                        <input type="hidden" name="del_port" value="<%=r.src_dport%>">
                                        <input type="hidden" name="del_proto" value="<%=r.proto%>">
                                        <input type="submit" name="del" value="<%:删除%>" class="cbi-button cbi-button-remove">
                                </form>
                        </td>
                </tr>
                <% end %>
                <% if #ipv4_rules == 0 then %>
                <tr><td colspan="6" class="cbi-section-table-cell" style="text-align:center;color:#999;padding:15px"><%:暂无 IPv4 转发规则%></td></tr>
                <% end %>
        </table>
</fieldset>

<fieldset class="cbi-section">
        <legend><%:IPv6 端口放行%></legend>
        <table class="cbi-section-table">
                <tr class="cbi-section-table-titles">
                        <th class="cbi-section-table-cell"><%:名称%></th>
                        <th class="cbi-section-table-cell"><%:协议%></th>
                        <th class="cbi-section-table-cell"><%:端口%></th>
                        <th class="cbi-section-table-cell"><%:状态%></th>
                        <th class="cbi-section-table-cell"><%:操作%></th>
                </tr>
                <% local j = 0; for _, r in ipairs(ipv6_rules) do j = j + 1 %>
                <tr class="cbi-section-table-row cbi-rowstyle-<%=(j % 2 == 0 and 2 or 1)%>">
                        <td class="cbi-value-field"><%=r.name%></td>
                        <td class="cbi-value-field"><%=r.proto:upper()%></td>
                        <td class="cbi-value-field"><%=r.dest_port%></td>
                        <td class="cbi-value-field">
                                <% if r.enabled == "0" then %>
                                        <span style="color:#cc0000"><%:已禁用%></span>
                                <% else %>
                                        <span style="color:#008800"><%:已启用%></span>
                                <% end %>
                        </td>
                        <td class="cbi-value-field">
                                <form method="post" action="<%=luci.dispatcher.build_url("admin", "network", "fwport")%>" style="display:inline;margin:0">
                                        <input type="hidden" name="del_port" value="<%=r.dest_port%>">
                                        <input type="hidden" name="del_proto" value="<%=r.proto%>">
                                        <input type="submit" name="del" value="<%:删除%>" class="cbi-button cbi-button-remove">
                                </form>
                        </td>
                </tr>
                <% end %>
                <% if #ipv6_rules == 0 then %>
                <tr><td colspan="5" class="cbi-section-table-cell" style="text-align:center;color:#999;padding:15px"><%:暂无 IPv6 放行规则%></td></tr>
                <% end %>
        </table>
</fieldset>

<div style="margin-top:15px;color:#666;font-size:12px;line-height:1.6">
        <%:提示:添加规则会同时创建 IPv4 DNAT 转发和 IPv6 放行规则;删除按端口+协议同时删除两条。%><br>
        <%:命令行同步可用:%> <code>fwport list</code> / <code>fwport add 443 tcp</code> / <code>fwport del 443 tcp</code>
</div>

<%+footer%>
VIEW_EOF

echo "  LuCI 页面已安装"

# ---------- 清缓存重启 ----------
echo "[6/6] 清除缓存并重启服务..."
rm -f /tmp/luci-indexcache 2>/dev/null || true
rm -rf /tmp/luci-* 2>/dev/null || true
rm -rf /tmp/luci2-* 2>/dev/null || true
/etc/init.d/uhttpd restart 2>/dev/null || true

echo ""
echo "=========================================="
echo "  安装完成!"
echo "=========================================="
echo ""
echo "访问: 登录后台 -> 网络 -> 端口转发管理"
echo "如果菜单没出现,浏览器按 Ctrl+Shift+R 强制刷新"
echo ""
echo "命令行测试:"
echo "  fwport list"
echo "  fwport add 8080 tcp"
echo "  fwport del 8080 tcp"
echo ""
echo "卸载:"
echo "  rm -f /usr/sbin/fwport /bin/fwport"
echo "  rm -f /usr/lib/lua/luci/controller/fwport.lua"
echo "  rm -rf /usr/lib/lua/luci/view/fwport"
echo "  rm -f /etc/config/fwport"
echo "  rm -rf /tmp/luci-* && /etc/init.d/uhttpd restart"






安装脚本:
https://lzy9909.lanzouq.com/b0fqm1o8h
密码:52pj

注意事项

  1. 端口范围:添加范围端口(如 21115-21117)直接写fwport add 21115-21117 tcp
  2. 默认目标 IP:脚本默认转发到 192.168.31.196,要改的话编辑脚本里的DEFAULT_TARGET变量
  3. 去重:添加时会自动检查,已存在的规则不会重复添加
  4. 备份:修改前建议先fwport backup

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
leechjia + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
Tsuki0402 发表于 2026-8-23 21:10
在这里也能看到iStoreOS的教程
3#
 楼主| liyu0828 发表于 2026-8-24 09:38 |楼主
Tsuki0402 发表于 2026-8-23 21:10
在这里也能看到iStoreOS的教程

主要是我自己要用到,发个帖子方便后面看
4#
网络很鬼 发表于 2026-8-24 09:41
5#
wu20080808 发表于 2026-8-24 10:57
好东西,值得学习
6#
 楼主| liyu0828 发表于 2026-8-24 14:02 |楼主
本帖最后由 liyu0828 于 2026-8-24 14:06 编辑
网络很鬼 发表于 2026-8-24 09:41
可视化就更好了

改了下,可以直接LCUI页面管理了,在网络-端口转发管理页面
7#
steven666 发表于 2026-8-24 14:24
好东西,值得学习
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-8-25 09:52

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表