fix: Fix the issue where deleting rules in iptables advanced settings… (#10942)

… fails
This commit is contained in:
ssongliu 2025-11-13 14:38:47 +08:00 committed by GitHub
parent c4bd4734a5
commit 55cbf572e0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 108 additions and 97 deletions

View file

@ -727,6 +727,9 @@ func (u *FirewallService) addPortRecord(req dto.PortRuleOperate) error {
return nil
}
if len(req.Description) == 0 {
return nil
}
if err := hostRepo.SaveFirewallRecord(&model.Firewall{
Type: "port",
Chain: req.Chain,

View file

@ -64,11 +64,15 @@ func (s *IptablesService) Search(req dto.SearchPageWithType) (int64, interface{}
}
func (s *IptablesService) OperateRule(req dto.IptablesRuleOp) error {
action := "ACCEPT"
if req.Strategy == "drop" {
action = "DROP"
}
policy := iptables.FilterRules{
Protocol: req.Protocol,
SrcIP: req.SrcIP,
DstIP: req.DstIP,
Strategy: req.Strategy,
Strategy: action,
}
if req.SrcPort != 0 {
policy.SrcPort = fmt.Sprintf("%v", req.SrcPort)
@ -91,6 +95,7 @@ func (s *IptablesService) OperateRule(req dto.IptablesRuleOp) error {
return fmt.Errorf("failed to add iptables rule: %w", err)
}
if len(req.Description) != 0 {
rule := &model.Firewall{
Chain: req.Chain,
Protocol: req.Protocol,
@ -105,6 +110,7 @@ func (s *IptablesService) OperateRule(req dto.IptablesRuleOp) error {
if err := hostRepo.SaveFirewallRecord(rule); err != nil {
return fmt.Errorf("failed to save rule to database: %w", err)
}
}
case "remove":
if err := iptables.DeleteFilterRule(req.Chain, policy); err != nil {
return fmt.Errorf("failed to remove iptables rule: %w", err)

View file

@ -72,11 +72,6 @@ func (i *Iptables) ListPort() ([]FireInfo, error) {
if item.Strategy == "drop" || item.Strategy == "reject" {
item.Strategy = "drop"
}
if item.Protocol == "6" {
item.Protocol = "tcp"
} else if item.Protocol == "17" {
item.Protocol = "udp"
}
datas = append(datas, FireInfo{
Chain: item.Chain,
@ -263,30 +258,13 @@ func iptablesPortForward(info Forward, operation string) error {
if err := iptables.AddForward(info.Protocol, info.Port, info.TargetIP, info.TargetPort, info.Interface, true); err != nil {
return err
}
forwardPersistence()
return nil
}
natList, err := iptables.ListForward(iptables.Chain1PanelPreRouting)
if err != nil {
return fmt.Errorf("failed to list NAT rules: %w", err)
}
for _, nat := range natList {
if nat.Protocol == info.Protocol &&
strings.TrimPrefix(nat.SrcPort, ":") == info.Port &&
strings.TrimPrefix(nat.DestPort, ":") == info.TargetPort {
targetIP := info.TargetIP
if targetIP == "" {
targetIP = "127.0.0.1"
}
if err := iptables.DeleteForward(nat.Num, info.Protocol, info.Port, targetIP, info.TargetPort, info.Interface); err != nil {
} else {
if err := iptables.DeleteForward(info.Num, info.Protocol, info.Port, info.TargetIP, info.TargetPort, info.Interface); err != nil {
return err
}
}
forwardPersistence()
}
}
return fmt.Errorf("forward rule not found")
return nil
}
func forwardPersistence() {

View file

@ -86,7 +86,7 @@ func ReadFilterRulesByChain(chain string) ([]FilterRules, error) {
}
itemRule := FilterRules{
Chain: chain,
Protocol: fields[1],
Protocol: loadProtocol(fields[1]),
SrcPort: loadPort("src", fields),
DstPort: loadPort("dst", fields),
SrcIP: loadIP(fields[3]),
@ -135,6 +135,7 @@ func loadPort(position string, portStr []string) string {
if strings.Contains(portStr[6], "dpts:") && position == "dst" {
portItem = strings.ReplaceAll(portStr[6], "dpts:", "")
}
portItem = strings.ReplaceAll(portItem, ":", "-")
return portItem
}
@ -145,6 +146,21 @@ func loadIP(ipStr string) string {
return ipStr
}
func loadProtocol(protocol string) string {
switch protocol {
case "0":
return "all"
case "1":
return "icmp"
case "6":
return "tcp"
case "17":
return "udp"
default:
return protocol
}
}
func validateRuleSafety(rule FilterRules, chain string) error {
if strings.ToUpper(rule.Strategy) != "DROP" {
return nil

View file

@ -3,8 +3,6 @@ package iptables
import (
"fmt"
"strings"
"github.com/1Panel-dev/1Panel/agent/utils/re"
)
func AddForward(protocol, srcPort, dest, destPort, iface string, save bool) error {
@ -71,42 +69,57 @@ func ListForward(chain ...string) ([]IptablesNatInfo, error) {
if err != nil {
return nil, err
}
natListRegex := re.GetRegex(re.IptablesNatListPattern)
var forwardList []IptablesNatInfo
for _, line := range strings.Split(stdout, "\n") {
line = strings.TrimFunc(line, func(r rune) bool {
return r <= 32
})
if natListRegex.MatchString(line) {
match := natListRegex.FindStringSubmatch(line)
if !strings.Contains(match[13], ":") {
match[13] = fmt.Sprintf(":%s", match[13])
lines := strings.Split(stdout, "\n")
for i := 0; i < len(lines); i++ {
fields := strings.Fields(lines[i])
if len(fields) < 13 {
continue
}
forwardList = append(forwardList, IptablesNatInfo{
Num: match[1],
Target: match[4],
Protocol: match[11],
InIface: match[7],
OutIface: match[8],
Opt: match[6],
Source: match[9],
Destination: match[10],
SrcPort: match[12],
DestPort: match[13],
})
item := IptablesNatInfo{
Num: fields[0],
Protocol: loadProtocol(fields[4]),
InIface: fields[6],
OutIface: fields[7],
Source: fields[8],
SrcPort: loadNatSrcPort(fields[11]),
}
if len(fields) == 15 && fields[13] == "ports" {
item.DestPort = fields[14]
}
if len(fields) == 13 && strings.HasPrefix(fields[12], "to:") {
parts := strings.Split(fields[12], ":")
if len(parts) > 2 {
item.DestPort = parts[2]
item.Destination = parts[1]
}
}
if len(item.Destination) == 0 {
item.Destination = "127.0.0.1"
}
forwardList = append(forwardList, item)
}
return forwardList, nil
}
func loadNatSrcPort(portStr string) string {
var portItem string
if strings.Contains(portStr, "dpt:") {
portItem = strings.ReplaceAll(portStr, "dpt:", "")
}
if strings.Contains(portStr, "dpts:") {
portItem = strings.ReplaceAll(portStr, "dpts:", "")
}
portItem = strings.ReplaceAll(portItem, ":", "-")
return portItem
}
type IptablesNatInfo struct {
Num string `json:"num"`
Target string `json:"target"`
Protocol string `json:"protocol"`
InIface string `json:"inIface"`
OutIface string `json:"outIface"`
Opt string `json:"opt"`
Source string `json:"source"`
Destination string `json:"destination"`
SrcPort string `json:"srcPort"`

View file

@ -11,7 +11,6 @@ const (
ComposeEnvVarPattern = `\$\{([^}]+)\}`
DiskKeyValuePattern = `([A-Za-z0-9_]+)=("([^"\\]|\\.)*"|[^ \t]+)`
FirewalldForwardPattern = `^port=(\d{1,5}):proto=(.+?):toport=(\d{1,5}):toaddr=(.*)$`
IptablesNatListPattern = `^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$`
ValidatorNamePattern = `^[a-zA-Z\p{Han}]{1}[a-zA-Z0-9_\p{Han}]{0,30}$`
ValidatorIPPattern = `^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$`
DomainPattern = `^([\w\p{Han}\-\*]{1,100}\.){1,10}([\w\p{Han}\-]{1,24}|[\w\p{Han}\-]{1,24}\.[\w\p{Han}\-]{1,24})(:\d{1,5})?$`
@ -42,7 +41,6 @@ func Init() {
ComposeEnvVarPattern,
DiskKeyValuePattern,
FirewalldForwardPattern,
IptablesNatListPattern,
ValidatorNamePattern,
ValidatorIPPattern,
DomainPattern,

View file

@ -95,6 +95,7 @@ var WebUrlMap = map[string]struct{}{
"/hosts/firewall/port": {},
"/hosts/firewall/forward": {},
"/hosts/firewall/ip": {},
"/hosts/firewall/advance": {},
"/hosts/process/process": {},
"/hosts/process/network": {},
"/hosts/ssh/ssh": {},

View file

@ -280,8 +280,8 @@ export namespace Host {
export interface IptablesRules {
id: number;
protocol: string;
srcPort: number;
dstPort: number;
srcPort: string;
dstPort: string;
srcIP: string;
dstIP: string;
strategy: string;

View file

@ -157,10 +157,10 @@ const opRef = ref();
const data = ref();
const formatPort = (port?: number | null | string) => {
if (port === 0 || port === '0') {
if (port === '' || port === 0 || port === '0') {
return i18n.global.t('firewall.allPorts');
}
if (port === undefined || port === null || port === '') {
if (port === undefined || port === null) {
return '-';
}
return port;
@ -255,7 +255,7 @@ const onOpenDialog = async (title: string, rowData?: Host.IptablesFilterRuleOp)
dialogRef.value!.acceptParams(params);
};
const onDelete = async (row: Host.IptablesFilterRuleOp | null) => {
const onDelete = async (row: Host.IptablesRules | null) => {
let names = [];
let rules = [];
if (row) {
@ -263,8 +263,8 @@ const onDelete = async (row: Host.IptablesFilterRuleOp | null) => {
operation: 'remove',
id: row.id,
chain: selectedChain.value,
srcPort: row.srcPort,
dstPort: row.dstPort,
srcPort: Number(row.srcPort),
dstPort: Number(row.dstPort),
srcIP: row.srcIP === 'anywhere' ? '' : row.srcIP,
dstIP: row.dstIP === 'anywhere' ? '' : row.dstIP,
protocol: row.protocol,
@ -284,8 +284,8 @@ const onDelete = async (row: Host.IptablesFilterRuleOp | null) => {
operation: 'remove',
id: item.id,
chain: selectedChain.value,
srcPort: item.srcPort,
dstPort: item.dstPort,
srcPort: Number(item.srcPort),
dstPort: Number(item.dstPort),
srcIP: item.srcIP === 'anywhere' ? '' : item.srcIP,
dstIP: item.dstIP === 'anywhere' ? '' : item.dstIP,
protocol: item.protocol,
@ -305,7 +305,7 @@ const onDelete = async (row: Host.IptablesFilterRuleOp | null) => {
const buttons = [
{
label: i18n.global.t('commons.button.delete'),
click: (row: Host.IptablesFilterRuleOp) => {
click: (row: Host.IptablesRules) => {
onDelete(row);
},
},

View file

@ -26,7 +26,6 @@
<el-input clearable v-model.trim="dialogData.rowData!.targetPort" />
</el-form-item>
<template v-if="dialogData.fireName === 'ufw'">
<el-form-item :label="$t('firewall.forwardInboundInterface')" prop="interface">
<el-select class="w-full" v-model="dialogData.rowData!.interface">
<el-option
@ -37,7 +36,6 @@
/>
</el-select>
</el-form-item>
</template>
</el-form>
<template #footer>
<span class="dialog-footer">
@ -81,12 +79,10 @@ const acceptParams = (params: DialogProps): void => {
if (dialogData.value.title === 'edit') {
oldRule.value = deepCopy(params.rowData);
}
if (dialogData.value.fireName === 'ufw') {
getNetworkOptions().then((res) => {
interfaceOptions.value = res.data.map((item) => ({ label: item, value: item }));
dialogData.value.rowData!.interface = dialogData.value.rowData!.interface || 'all';
});
}
title.value = i18n.global.t('firewall.' + dialogData.value.title);
drawerVisible.value = true;
};