mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-12-18 05:19:19 +08:00
fix: Fix the issue where deleting rules in iptables advanced settings… (#10942)
… fails
This commit is contained in:
parent
c4bd4734a5
commit
55cbf572e0
10 changed files with 108 additions and 97 deletions
|
|
@ -727,6 +727,9 @@ func (u *FirewallService) addPortRecord(req dto.PortRuleOperate) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(req.Description) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if err := hostRepo.SaveFirewallRecord(&model.Firewall{
|
if err := hostRepo.SaveFirewallRecord(&model.Firewall{
|
||||||
Type: "port",
|
Type: "port",
|
||||||
Chain: req.Chain,
|
Chain: req.Chain,
|
||||||
|
|
|
||||||
|
|
@ -64,11 +64,15 @@ func (s *IptablesService) Search(req dto.SearchPageWithType) (int64, interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *IptablesService) OperateRule(req dto.IptablesRuleOp) error {
|
func (s *IptablesService) OperateRule(req dto.IptablesRuleOp) error {
|
||||||
|
action := "ACCEPT"
|
||||||
|
if req.Strategy == "drop" {
|
||||||
|
action = "DROP"
|
||||||
|
}
|
||||||
policy := iptables.FilterRules{
|
policy := iptables.FilterRules{
|
||||||
Protocol: req.Protocol,
|
Protocol: req.Protocol,
|
||||||
SrcIP: req.SrcIP,
|
SrcIP: req.SrcIP,
|
||||||
DstIP: req.DstIP,
|
DstIP: req.DstIP,
|
||||||
Strategy: req.Strategy,
|
Strategy: action,
|
||||||
}
|
}
|
||||||
if req.SrcPort != 0 {
|
if req.SrcPort != 0 {
|
||||||
policy.SrcPort = fmt.Sprintf("%v", req.SrcPort)
|
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)
|
return fmt.Errorf("failed to add iptables rule: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(req.Description) != 0 {
|
||||||
rule := &model.Firewall{
|
rule := &model.Firewall{
|
||||||
Chain: req.Chain,
|
Chain: req.Chain,
|
||||||
Protocol: req.Protocol,
|
Protocol: req.Protocol,
|
||||||
|
|
@ -105,6 +110,7 @@ func (s *IptablesService) OperateRule(req dto.IptablesRuleOp) error {
|
||||||
if err := hostRepo.SaveFirewallRecord(rule); err != nil {
|
if err := hostRepo.SaveFirewallRecord(rule); err != nil {
|
||||||
return fmt.Errorf("failed to save rule to database: %w", err)
|
return fmt.Errorf("failed to save rule to database: %w", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
case "remove":
|
case "remove":
|
||||||
if err := iptables.DeleteFilterRule(req.Chain, policy); err != nil {
|
if err := iptables.DeleteFilterRule(req.Chain, policy); err != nil {
|
||||||
return fmt.Errorf("failed to remove iptables rule: %w", err)
|
return fmt.Errorf("failed to remove iptables rule: %w", err)
|
||||||
|
|
|
||||||
|
|
@ -72,11 +72,6 @@ func (i *Iptables) ListPort() ([]FireInfo, error) {
|
||||||
if item.Strategy == "drop" || item.Strategy == "reject" {
|
if item.Strategy == "drop" || item.Strategy == "reject" {
|
||||||
item.Strategy = "drop"
|
item.Strategy = "drop"
|
||||||
}
|
}
|
||||||
if item.Protocol == "6" {
|
|
||||||
item.Protocol = "tcp"
|
|
||||||
} else if item.Protocol == "17" {
|
|
||||||
item.Protocol = "udp"
|
|
||||||
}
|
|
||||||
|
|
||||||
datas = append(datas, FireInfo{
|
datas = append(datas, FireInfo{
|
||||||
Chain: item.Chain,
|
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 {
|
if err := iptables.AddForward(info.Protocol, info.Port, info.TargetIP, info.TargetPort, info.Interface, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
forwardPersistence()
|
} else {
|
||||||
return nil
|
if err := iptables.DeleteForward(info.Num, info.Protocol, info.Port, info.TargetIP, info.TargetPort, info.Interface); err != 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 {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
forwardPersistence()
|
forwardPersistence()
|
||||||
}
|
return nil
|
||||||
}
|
|
||||||
return fmt.Errorf("forward rule not found")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func forwardPersistence() {
|
func forwardPersistence() {
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ func ReadFilterRulesByChain(chain string) ([]FilterRules, error) {
|
||||||
}
|
}
|
||||||
itemRule := FilterRules{
|
itemRule := FilterRules{
|
||||||
Chain: chain,
|
Chain: chain,
|
||||||
Protocol: fields[1],
|
Protocol: loadProtocol(fields[1]),
|
||||||
SrcPort: loadPort("src", fields),
|
SrcPort: loadPort("src", fields),
|
||||||
DstPort: loadPort("dst", fields),
|
DstPort: loadPort("dst", fields),
|
||||||
SrcIP: loadIP(fields[3]),
|
SrcIP: loadIP(fields[3]),
|
||||||
|
|
@ -135,6 +135,7 @@ func loadPort(position string, portStr []string) string {
|
||||||
if strings.Contains(portStr[6], "dpts:") && position == "dst" {
|
if strings.Contains(portStr[6], "dpts:") && position == "dst" {
|
||||||
portItem = strings.ReplaceAll(portStr[6], "dpts:", "")
|
portItem = strings.ReplaceAll(portStr[6], "dpts:", "")
|
||||||
}
|
}
|
||||||
|
portItem = strings.ReplaceAll(portItem, ":", "-")
|
||||||
return portItem
|
return portItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,6 +146,21 @@ func loadIP(ipStr string) string {
|
||||||
return ipStr
|
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 {
|
func validateRuleSafety(rule FilterRules, chain string) error {
|
||||||
if strings.ToUpper(rule.Strategy) != "DROP" {
|
if strings.ToUpper(rule.Strategy) != "DROP" {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@ package iptables
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/re"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func AddForward(protocol, srcPort, dest, destPort, iface string, save bool) error {
|
func AddForward(protocol, srcPort, dest, destPort, iface string, save bool) error {
|
||||||
|
|
@ -71,42 +69,57 @@ func ListForward(chain ...string) ([]IptablesNatInfo, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
natListRegex := re.GetRegex(re.IptablesNatListPattern)
|
|
||||||
var forwardList []IptablesNatInfo
|
var forwardList []IptablesNatInfo
|
||||||
for _, line := range strings.Split(stdout, "\n") {
|
lines := strings.Split(stdout, "\n")
|
||||||
line = strings.TrimFunc(line, func(r rune) bool {
|
for i := 0; i < len(lines); i++ {
|
||||||
return r <= 32
|
fields := strings.Fields(lines[i])
|
||||||
})
|
if len(fields) < 13 {
|
||||||
if natListRegex.MatchString(line) {
|
continue
|
||||||
match := natListRegex.FindStringSubmatch(line)
|
|
||||||
if !strings.Contains(match[13], ":") {
|
|
||||||
match[13] = fmt.Sprintf(":%s", match[13])
|
|
||||||
}
|
}
|
||||||
forwardList = append(forwardList, IptablesNatInfo{
|
item := IptablesNatInfo{
|
||||||
Num: match[1],
|
Num: fields[0],
|
||||||
Target: match[4],
|
Protocol: loadProtocol(fields[4]),
|
||||||
Protocol: match[11],
|
InIface: fields[6],
|
||||||
InIface: match[7],
|
OutIface: fields[7],
|
||||||
OutIface: match[8],
|
Source: fields[8],
|
||||||
Opt: match[6],
|
SrcPort: loadNatSrcPort(fields[11]),
|
||||||
Source: match[9],
|
|
||||||
Destination: match[10],
|
|
||||||
SrcPort: match[12],
|
|
||||||
DestPort: match[13],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
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
|
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 {
|
type IptablesNatInfo struct {
|
||||||
Num string `json:"num"`
|
Num string `json:"num"`
|
||||||
Target string `json:"target"`
|
|
||||||
Protocol string `json:"protocol"`
|
Protocol string `json:"protocol"`
|
||||||
InIface string `json:"inIface"`
|
InIface string `json:"inIface"`
|
||||||
OutIface string `json:"outIface"`
|
OutIface string `json:"outIface"`
|
||||||
Opt string `json:"opt"`
|
|
||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
Destination string `json:"destination"`
|
Destination string `json:"destination"`
|
||||||
SrcPort string `json:"srcPort"`
|
SrcPort string `json:"srcPort"`
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ const (
|
||||||
ComposeEnvVarPattern = `\$\{([^}]+)\}`
|
ComposeEnvVarPattern = `\$\{([^}]+)\}`
|
||||||
DiskKeyValuePattern = `([A-Za-z0-9_]+)=("([^"\\]|\\.)*"|[^ \t]+)`
|
DiskKeyValuePattern = `([A-Za-z0-9_]+)=("([^"\\]|\\.)*"|[^ \t]+)`
|
||||||
FirewalldForwardPattern = `^port=(\d{1,5}):proto=(.+?):toport=(\d{1,5}):toaddr=(.*)$`
|
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}$`
|
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}$`
|
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})?$`
|
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,
|
ComposeEnvVarPattern,
|
||||||
DiskKeyValuePattern,
|
DiskKeyValuePattern,
|
||||||
FirewalldForwardPattern,
|
FirewalldForwardPattern,
|
||||||
IptablesNatListPattern,
|
|
||||||
ValidatorNamePattern,
|
ValidatorNamePattern,
|
||||||
ValidatorIPPattern,
|
ValidatorIPPattern,
|
||||||
DomainPattern,
|
DomainPattern,
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ var WebUrlMap = map[string]struct{}{
|
||||||
"/hosts/firewall/port": {},
|
"/hosts/firewall/port": {},
|
||||||
"/hosts/firewall/forward": {},
|
"/hosts/firewall/forward": {},
|
||||||
"/hosts/firewall/ip": {},
|
"/hosts/firewall/ip": {},
|
||||||
|
"/hosts/firewall/advance": {},
|
||||||
"/hosts/process/process": {},
|
"/hosts/process/process": {},
|
||||||
"/hosts/process/network": {},
|
"/hosts/process/network": {},
|
||||||
"/hosts/ssh/ssh": {},
|
"/hosts/ssh/ssh": {},
|
||||||
|
|
|
||||||
|
|
@ -280,8 +280,8 @@ export namespace Host {
|
||||||
export interface IptablesRules {
|
export interface IptablesRules {
|
||||||
id: number;
|
id: number;
|
||||||
protocol: string;
|
protocol: string;
|
||||||
srcPort: number;
|
srcPort: string;
|
||||||
dstPort: number;
|
dstPort: string;
|
||||||
srcIP: string;
|
srcIP: string;
|
||||||
dstIP: string;
|
dstIP: string;
|
||||||
strategy: string;
|
strategy: string;
|
||||||
|
|
|
||||||
|
|
@ -157,10 +157,10 @@ const opRef = ref();
|
||||||
const data = ref();
|
const data = ref();
|
||||||
|
|
||||||
const formatPort = (port?: number | null | string) => {
|
const formatPort = (port?: number | null | string) => {
|
||||||
if (port === 0 || port === '0') {
|
if (port === '' || port === 0 || port === '0') {
|
||||||
return i18n.global.t('firewall.allPorts');
|
return i18n.global.t('firewall.allPorts');
|
||||||
}
|
}
|
||||||
if (port === undefined || port === null || port === '') {
|
if (port === undefined || port === null) {
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
return port;
|
return port;
|
||||||
|
|
@ -255,7 +255,7 @@ const onOpenDialog = async (title: string, rowData?: Host.IptablesFilterRuleOp)
|
||||||
dialogRef.value!.acceptParams(params);
|
dialogRef.value!.acceptParams(params);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDelete = async (row: Host.IptablesFilterRuleOp | null) => {
|
const onDelete = async (row: Host.IptablesRules | null) => {
|
||||||
let names = [];
|
let names = [];
|
||||||
let rules = [];
|
let rules = [];
|
||||||
if (row) {
|
if (row) {
|
||||||
|
|
@ -263,8 +263,8 @@ const onDelete = async (row: Host.IptablesFilterRuleOp | null) => {
|
||||||
operation: 'remove',
|
operation: 'remove',
|
||||||
id: row.id,
|
id: row.id,
|
||||||
chain: selectedChain.value,
|
chain: selectedChain.value,
|
||||||
srcPort: row.srcPort,
|
srcPort: Number(row.srcPort),
|
||||||
dstPort: row.dstPort,
|
dstPort: Number(row.dstPort),
|
||||||
srcIP: row.srcIP === 'anywhere' ? '' : row.srcIP,
|
srcIP: row.srcIP === 'anywhere' ? '' : row.srcIP,
|
||||||
dstIP: row.dstIP === 'anywhere' ? '' : row.dstIP,
|
dstIP: row.dstIP === 'anywhere' ? '' : row.dstIP,
|
||||||
protocol: row.protocol,
|
protocol: row.protocol,
|
||||||
|
|
@ -284,8 +284,8 @@ const onDelete = async (row: Host.IptablesFilterRuleOp | null) => {
|
||||||
operation: 'remove',
|
operation: 'remove',
|
||||||
id: item.id,
|
id: item.id,
|
||||||
chain: selectedChain.value,
|
chain: selectedChain.value,
|
||||||
srcPort: item.srcPort,
|
srcPort: Number(item.srcPort),
|
||||||
dstPort: item.dstPort,
|
dstPort: Number(item.dstPort),
|
||||||
srcIP: item.srcIP === 'anywhere' ? '' : item.srcIP,
|
srcIP: item.srcIP === 'anywhere' ? '' : item.srcIP,
|
||||||
dstIP: item.dstIP === 'anywhere' ? '' : item.dstIP,
|
dstIP: item.dstIP === 'anywhere' ? '' : item.dstIP,
|
||||||
protocol: item.protocol,
|
protocol: item.protocol,
|
||||||
|
|
@ -305,7 +305,7 @@ const onDelete = async (row: Host.IptablesFilterRuleOp | null) => {
|
||||||
const buttons = [
|
const buttons = [
|
||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.delete'),
|
label: i18n.global.t('commons.button.delete'),
|
||||||
click: (row: Host.IptablesFilterRuleOp) => {
|
click: (row: Host.IptablesRules) => {
|
||||||
onDelete(row);
|
onDelete(row);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@
|
||||||
<el-input clearable v-model.trim="dialogData.rowData!.targetPort" />
|
<el-input clearable v-model.trim="dialogData.rowData!.targetPort" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<template v-if="dialogData.fireName === 'ufw'">
|
|
||||||
<el-form-item :label="$t('firewall.forwardInboundInterface')" prop="interface">
|
<el-form-item :label="$t('firewall.forwardInboundInterface')" prop="interface">
|
||||||
<el-select class="w-full" v-model="dialogData.rowData!.interface">
|
<el-select class="w-full" v-model="dialogData.rowData!.interface">
|
||||||
<el-option
|
<el-option
|
||||||
|
|
@ -37,7 +36,6 @@
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
|
|
@ -81,12 +79,10 @@ const acceptParams = (params: DialogProps): void => {
|
||||||
if (dialogData.value.title === 'edit') {
|
if (dialogData.value.title === 'edit') {
|
||||||
oldRule.value = deepCopy(params.rowData);
|
oldRule.value = deepCopy(params.rowData);
|
||||||
}
|
}
|
||||||
if (dialogData.value.fireName === 'ufw') {
|
|
||||||
getNetworkOptions().then((res) => {
|
getNetworkOptions().then((res) => {
|
||||||
interfaceOptions.value = res.data.map((item) => ({ label: item, value: item }));
|
interfaceOptions.value = res.data.map((item) => ({ label: item, value: item }));
|
||||||
dialogData.value.rowData!.interface = dialogData.value.rowData!.interface || 'all';
|
dialogData.value.rowData!.interface = dialogData.value.rowData!.interface || 'all';
|
||||||
});
|
});
|
||||||
}
|
|
||||||
title.value = i18n.global.t('firewall.' + dialogData.value.title);
|
title.value = i18n.global.t('firewall.' + dialogData.value.title);
|
||||||
drawerVisible.value = true;
|
drawerVisible.value = true;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue