Skip to content

Commit d0bb916

Browse files
AnxysUaenlive
andauthored
feat: Add inbound interface restriction option for ufw forward rules (#10345) (#10549)
* 修复https防窜站关闭时修改默认站点报错的问题 * feat: Add inbound interface restriction option for ufw forward rules (#10345) --------- Co-authored-by: live <[email protected]>
1 parent 72a0e02 commit d0bb916

File tree

23 files changed

+109
-40
lines changed

23 files changed

+109
-40
lines changed

agent/app/dto/firewall.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type ForwardRuleOperate struct {
3737
Operation string `json:"operation" validate:"required,oneof=add remove"`
3838
Num string `json:"num"`
3939
Protocol string `json:"protocol" validate:"required,oneof=tcp udp tcp/udp"`
40+
Interface string `json:"interface"`
4041
Port string `json:"port" validate:"required"`
4142
TargetIP string `json:"targetIP"`
4243
TargetPort string `json:"targetPort" validate:"required"`

agent/app/model/firewall.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ type Forward struct {
1818
Port string `gorm:"not null" json:"port"`
1919
TargetIP string `gorm:"not null" json:"targetIP"`
2020
TargetPort string `gorm:"not null" json:"targetPort"`
21+
Interface string `json:"interface"`
2122
}

agent/app/service/firewall.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,8 @@ func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
329329
if reqRule.Port == rule.Port &&
330330
reqRule.TargetPort == rule.TargetPort &&
331331
reqRule.TargetIP == rule.TargetIP &&
332-
proto == rule.Protocol {
332+
proto == rule.Protocol &&
333+
reqRule.Interface == rule.Interface {
333334
shouldKeep = false
334335
break
335336
}
@@ -353,7 +354,8 @@ func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
353354
if reqRule.Port == rule.Port &&
354355
reqRule.TargetPort == rule.TargetPort &&
355356
reqRule.TargetIP == rule.TargetIP &&
356-
proto == rule.Protocol {
357+
proto == rule.Protocol &&
358+
reqRule.Interface == rule.Interface {
357359
return buserr.New("ErrRecordExist")
358360
}
359361
}
@@ -383,6 +385,7 @@ func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
383385
Port: r.Port,
384386
TargetIP: r.TargetIP,
385387
TargetPort: r.TargetPort,
388+
Interface: r.Interface,
386389
}, r.Operation); err != nil {
387390
if req.ForceDelete {
388391
global.LOG.Error(err)

agent/init/migration/migrations/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
)
2727

2828
var AddTable = &gormigrate.Migration{
29-
ID: "20250902-add-table",
29+
ID: "20250930-add-table",
3030
Migrate: func(tx *gorm.DB) error {
3131
return tx.AutoMigrate(
3232
&model.AppDetail{},

agent/utils/firewall/client/info.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ type FireInfo struct {
1010
Num string `json:"num"`
1111
TargetIP string `json:"targetIP"`
1212
TargetPort string `json:"targetPort"`
13+
Interface string `json:"interface"`
1314

1415
UsedStatus string `json:"usedStatus"`
1516
Description string `json:"description"`
@@ -21,12 +22,15 @@ type Forward struct {
2122
Port string `json:"port"`
2223
TargetIP string `json:"targetIP"`
2324
TargetPort string `json:"targetPort"`
25+
Interface string `json:"interface"`
2426
}
2527

2628
type IptablesNatInfo struct {
2729
Num string `json:"num"`
2830
Target string `json:"target"`
2931
Protocol string `json:"protocol"`
32+
InIface string `json:"inIface"`
33+
OutIface string `json:"outIface"`
3034
Opt string `json:"opt"`
3135
Source string `json:"source"`
3236
Destination string `json:"destination"`

agent/utils/firewall/client/iptables.go

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const (
2525
const NatChain = "1PANEL"
2626

2727
var (
28-
natListRegex = regexp.MustCompile(`^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$`)
28+
natListRegex = regexp.MustCompile(`^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$`)
2929
)
3030

3131
type Iptables struct {
@@ -92,7 +92,7 @@ func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) {
9292
if len(chain) == 0 {
9393
chain = append(chain, PreRoutingChain)
9494
}
95-
stdout, err := iptables.outf(NatTab, "-nL %s --line", chain[0])
95+
stdout, err := iptables.outf(NatTab, "-nvL %s --line-numbers", chain[0])
9696
if err != nil {
9797
return nil, err
9898
}
@@ -104,35 +104,35 @@ func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) {
104104
})
105105
if natListRegex.MatchString(line) {
106106
match := natListRegex.FindStringSubmatch(line)
107-
if !strings.Contains(match[9], ":") {
108-
match[9] = fmt.Sprintf(":%s", match[9])
107+
if !strings.Contains(match[13], ":") {
108+
match[13] = fmt.Sprintf(":%s", match[13])
109109
}
110110
forwardList = append(forwardList, IptablesNatInfo{
111111
Num: match[1],
112-
Target: match[2],
113-
Protocol: match[7],
114-
Opt: match[4],
115-
Source: match[5],
116-
Destination: match[6],
117-
SrcPort: match[8],
118-
DestPort: match[9],
112+
Target: match[4],
113+
Protocol: match[11],
114+
InIface: match[7],
115+
OutIface: match[8],
116+
Opt: match[6],
117+
Source: match[9],
118+
Destination: match[10],
119+
SrcPort: match[12],
120+
DestPort: match[13],
119121
})
120122
}
121123
}
122124

123125
return forwardList, nil
124126
}
125127

126-
func (iptables *Iptables) NatAdd(protocol, srcPort, dest, destPort string, save bool) error {
128+
func (iptables *Iptables) NatAdd(protocol, srcPort, dest, destPort, iface string, save bool) error {
127129
if dest != "" && dest != "127.0.0.1" && dest != "localhost" {
128-
if err := iptables.runf(NatTab, fmt.Sprintf(
129-
"-A %s -p %s --dport %s -j DNAT --to-destination %s:%s",
130-
PreRoutingChain,
131-
protocol,
132-
srcPort,
133-
dest,
134-
destPort,
135-
)); err != nil {
130+
iptablesArg := fmt.Sprintf("-A %s", PreRoutingChain)
131+
if iface != "" {
132+
iptablesArg += fmt.Sprintf(" -i %s", iface)
133+
}
134+
iptablesArg += fmt.Sprintf(" -p %s --dport %s -j DNAT --to-destination %s:%s", protocol, srcPort, dest, destPort)
135+
if err := iptables.runf(NatTab, iptablesArg); err != nil {
136136
return err
137137
}
138138

@@ -166,13 +166,12 @@ func (iptables *Iptables) NatAdd(protocol, srcPort, dest, destPort string, save
166166
return err
167167
}
168168
} else {
169-
if err := iptables.runf(NatTab, fmt.Sprintf(
170-
"-A %s -p %s --dport %s -j REDIRECT --to-port %s",
171-
PreRoutingChain,
172-
protocol,
173-
srcPort,
174-
destPort,
175-
)); err != nil {
169+
iptablesArg := fmt.Sprintf("-A %s", PreRoutingChain)
170+
if iface != "" {
171+
iptablesArg += fmt.Sprintf(" -i %s", iface)
172+
}
173+
iptablesArg += fmt.Sprintf(" -p %s --dport %s -j REDIRECT --to-port %s", protocol, srcPort, destPort)
174+
if err := iptables.runf(NatTab, iptablesArg); err != nil {
176175
return err
177176
}
178177
}
@@ -183,12 +182,13 @@ func (iptables *Iptables) NatAdd(protocol, srcPort, dest, destPort string, save
183182
Port: srcPort,
184183
TargetIP: dest,
185184
TargetPort: destPort,
185+
Interface: iface,
186186
}).Error
187187
}
188188
return nil
189189
}
190190

191-
func (iptables *Iptables) NatRemove(num string, protocol, srcPort, dest, destPort string) error {
191+
func (iptables *Iptables) NatRemove(num string, protocol, srcPort, dest, destPort, iface string) error {
192192
if err := iptables.runf(NatTab, "-D %s %s", PreRoutingChain, num); err != nil {
193193
return err
194194
}
@@ -226,11 +226,13 @@ func (iptables *Iptables) NatRemove(num string, protocol, srcPort, dest, destPor
226226
}
227227

228228
global.DB.Where(
229-
"protocol = ? AND port = ? AND target_ip = ? AND target_port = ?",
229+
"protocol = ? AND port = ? AND target_ip = ? AND target_port = ? AND (interface = ? OR (interface IS NULL AND ? = ''))",
230230
protocol,
231231
srcPort,
232232
dest,
233233
destPort,
234+
iface,
235+
iface,
234236
).Delete(&model.Forward{})
235237
return nil
236238
}
@@ -249,7 +251,7 @@ func (iptables *Iptables) Reload() error {
249251
var rules []model.Forward
250252
global.DB.Find(&rules)
251253
for _, forward := range rules {
252-
if err := iptables.NatAdd(forward.Protocol, forward.Port, forward.TargetIP, forward.TargetPort, false); err != nil {
254+
if err := iptables.NatAdd(forward.Protocol, forward.Port, forward.TargetIP, forward.TargetPort, forward.Interface, false); err != nil {
253255
return err
254256
}
255257
}

agent/utils/firewall/client/ufw.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ func (f *Ufw) ListForward() ([]FireInfo, error) {
124124
list = append(list, FireInfo{
125125
Num: rule.Num,
126126
Protocol: rule.Protocol,
127+
Interface: rule.InIface,
127128
Port: rule.SrcPort,
128129
TargetIP: dest[0],
129130
TargetPort: dest[1],
@@ -241,9 +242,9 @@ func (f *Ufw) PortForward(info Forward, operation string) error {
241242
}
242243

243244
if operation == "add" {
244-
err = iptables.NatAdd(info.Protocol, info.Port, info.TargetIP, info.TargetPort, true)
245+
err = iptables.NatAdd(info.Protocol, info.Port, info.TargetIP, info.TargetPort, info.Interface, true)
245246
} else {
246-
err = iptables.NatRemove(info.Num, info.Protocol, info.Port, info.TargetIP, info.TargetPort)
247+
err = iptables.NatRemove(info.Num, info.Protocol, info.Port, info.TargetIP, info.TargetPort, info.Interface)
247248
}
248249
if err != nil {
249250
return fmt.Errorf("%s port forward failed, err: %s", operation, err)

frontend/src/api/interface/dashboard.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export namespace Dashboard {
1515
title: string;
1616
detail: string;
1717
recommend: number;
18-
isShow: boolean ;
18+
isShow: boolean;
1919
router: string;
2020
}
2121
export interface AppLauncher {

frontend/src/api/interface/host.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export namespace Host {
112112
port: string;
113113
targetIP: string;
114114
targetPort: string;
115+
interface: string;
115116
}
116117
export interface RuleIP {
117118
operation: string;

frontend/src/api/interface/setting.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ export namespace Setting {
246246
id: number;
247247
name: string;
248248
addr: string;
249-
description: string;
249+
description: string;
250250
systemVersion: string;
251251
securityEntrance: string;
252252
cpuUsedPercent: number;

0 commit comments

Comments
 (0)