Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions agent/app/dto/firewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type ForwardRuleOperate struct {
Operation string `json:"operation" validate:"required,oneof=add remove"`
Num string `json:"num"`
Protocol string `json:"protocol" validate:"required,oneof=tcp udp tcp/udp"`
Interface string `json:"interface"`
Port string `json:"port" validate:"required"`
TargetIP string `json:"targetIP"`
TargetPort string `json:"targetPort" validate:"required"`
Expand Down
1 change: 1 addition & 0 deletions agent/app/model/firewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ type Forward struct {
Port string `gorm:"not null" json:"port"`
TargetIP string `gorm:"not null" json:"targetIP"`
TargetPort string `gorm:"not null" json:"targetPort"`
Interface string `json:"interface"`
}
7 changes: 5 additions & 2 deletions agent/app/service/firewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,8 @@ func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
if reqRule.Port == rule.Port &&
reqRule.TargetPort == rule.TargetPort &&
reqRule.TargetIP == rule.TargetIP &&
proto == rule.Protocol {
proto == rule.Protocol &&
reqRule.Interface == rule.Interface {
shouldKeep = false
break
}
Expand All @@ -353,7 +354,8 @@ func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
if reqRule.Port == rule.Port &&
reqRule.TargetPort == rule.TargetPort &&
reqRule.TargetIP == rule.TargetIP &&
proto == rule.Protocol {
proto == rule.Protocol &&
reqRule.Interface == rule.Interface {
return buserr.New("ErrRecordExist")
}
}
Expand Down Expand Up @@ -383,6 +385,7 @@ func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
Port: r.Port,
TargetIP: r.TargetIP,
TargetPort: r.TargetPort,
Interface: r.Interface,
}, r.Operation); err != nil {
if req.ForceDelete {
global.LOG.Error(err)
Expand Down
2 changes: 1 addition & 1 deletion agent/init/migration/migrations/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
)

var AddTable = &gormigrate.Migration{
ID: "20250902-add-table",
ID: "20250930-add-table",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(
&model.AppDetail{},
Expand Down
4 changes: 4 additions & 0 deletions agent/utils/firewall/client/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type FireInfo struct {
Num string `json:"num"`
TargetIP string `json:"targetIP"`
TargetPort string `json:"targetPort"`
Interface string `json:"interface"`

UsedStatus string `json:"usedStatus"`
Description string `json:"description"`
Expand All @@ -21,12 +22,15 @@ type Forward struct {
Port string `json:"port"`
TargetIP string `json:"targetIP"`
TargetPort string `json:"targetPort"`
Interface string `json:"interface"`
}

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"`
Expand Down
62 changes: 32 additions & 30 deletions agent/utils/firewall/client/iptables.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const (
const NatChain = "1PANEL"

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

type Iptables struct {
Expand Down Expand Up @@ -92,7 +92,7 @@ func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) {
if len(chain) == 0 {
chain = append(chain, PreRoutingChain)
}
stdout, err := iptables.outf(NatTab, "-nL %s --line", chain[0])
stdout, err := iptables.outf(NatTab, "-nvL %s --line-numbers", chain[0])
if err != nil {
return nil, err
}
Expand All @@ -104,35 +104,35 @@ func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) {
})
if natListRegex.MatchString(line) {
match := natListRegex.FindStringSubmatch(line)
if !strings.Contains(match[9], ":") {
match[9] = fmt.Sprintf(":%s", match[9])
if !strings.Contains(match[13], ":") {
match[13] = fmt.Sprintf(":%s", match[13])
}
forwardList = append(forwardList, IptablesNatInfo{
Num: match[1],
Target: match[2],
Protocol: match[7],
Opt: match[4],
Source: match[5],
Destination: match[6],
SrcPort: match[8],
DestPort: match[9],
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],
})
}
}

return forwardList, nil
}

func (iptables *Iptables) NatAdd(protocol, srcPort, dest, destPort string, save bool) error {
func (iptables *Iptables) NatAdd(protocol, srcPort, dest, destPort, iface string, save bool) error {
if dest != "" && dest != "127.0.0.1" && dest != "localhost" {
if err := iptables.runf(NatTab, fmt.Sprintf(
"-A %s -p %s --dport %s -j DNAT --to-destination %s:%s",
PreRoutingChain,
protocol,
srcPort,
dest,
destPort,
)); err != nil {
iptablesArg := fmt.Sprintf("-A %s", PreRoutingChain)
if iface != "" {
iptablesArg += fmt.Sprintf(" -i %s", iface)
}
iptablesArg += fmt.Sprintf(" -p %s --dport %s -j DNAT --to-destination %s:%s", protocol, srcPort, dest, destPort)
if err := iptables.runf(NatTab, iptablesArg); err != nil {
return err
}

Expand Down Expand Up @@ -166,13 +166,12 @@ func (iptables *Iptables) NatAdd(protocol, srcPort, dest, destPort string, save
return err
}
} else {
if err := iptables.runf(NatTab, fmt.Sprintf(
"-A %s -p %s --dport %s -j REDIRECT --to-port %s",
PreRoutingChain,
protocol,
srcPort,
destPort,
)); err != nil {
iptablesArg := fmt.Sprintf("-A %s", PreRoutingChain)
if iface != "" {
iptablesArg += fmt.Sprintf(" -i %s", iface)
}
iptablesArg += fmt.Sprintf(" -p %s --dport %s -j REDIRECT --to-port %s", protocol, srcPort, destPort)
if err := iptables.runf(NatTab, iptablesArg); err != nil {
return err
}
}
Expand All @@ -183,12 +182,13 @@ func (iptables *Iptables) NatAdd(protocol, srcPort, dest, destPort string, save
Port: srcPort,
TargetIP: dest,
TargetPort: destPort,
Interface: iface,
}).Error
}
return nil
}

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

global.DB.Where(
"protocol = ? AND port = ? AND target_ip = ? AND target_port = ?",
"protocol = ? AND port = ? AND target_ip = ? AND target_port = ? AND (interface = ? OR (interface IS NULL AND ? = ''))",
protocol,
srcPort,
dest,
destPort,
iface,
iface,
).Delete(&model.Forward{})
return nil
}
Expand All @@ -249,7 +251,7 @@ func (iptables *Iptables) Reload() error {
var rules []model.Forward
global.DB.Find(&rules)
for _, forward := range rules {
if err := iptables.NatAdd(forward.Protocol, forward.Port, forward.TargetIP, forward.TargetPort, false); err != nil {
if err := iptables.NatAdd(forward.Protocol, forward.Port, forward.TargetIP, forward.TargetPort, forward.Interface, false); err != nil {
return err
}
}
Expand Down
5 changes: 3 additions & 2 deletions agent/utils/firewall/client/ufw.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ func (f *Ufw) ListForward() ([]FireInfo, error) {
list = append(list, FireInfo{
Num: rule.Num,
Protocol: rule.Protocol,
Interface: rule.InIface,
Port: rule.SrcPort,
TargetIP: dest[0],
TargetPort: dest[1],
Expand Down Expand Up @@ -241,9 +242,9 @@ func (f *Ufw) PortForward(info Forward, operation string) error {
}

if operation == "add" {
err = iptables.NatAdd(info.Protocol, info.Port, info.TargetIP, info.TargetPort, true)
err = iptables.NatAdd(info.Protocol, info.Port, info.TargetIP, info.TargetPort, info.Interface, true)
} else {
err = iptables.NatRemove(info.Num, info.Protocol, info.Port, info.TargetIP, info.TargetPort)
err = iptables.NatRemove(info.Num, info.Protocol, info.Port, info.TargetIP, info.TargetPort, info.Interface)
}
if err != nil {
return fmt.Errorf("%s port forward failed, err: %s", operation, err)
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/api/interface/dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export namespace Dashboard {
title: string;
detail: string;
recommend: number;
isShow: boolean ;
isShow: boolean;
router: string;
}
export interface AppLauncher {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/api/interface/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export namespace Host {
port: string;
targetIP: string;
targetPort: string;
interface: string;
}
export interface RuleIP {
operation: string;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/api/interface/setting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ export namespace Setting {
id: number;
name: string;
addr: string;
description: string;
description: string;
systemVersion: string;
securityEntrance: string;
cpuUsedPercent: number;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/api/modules/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const operateFire = (operation: string, withDockerRestart: boolean) => {
export const operatePortRule = (params: Host.RulePort) => {
return http.post<Host.RulePort>(`/hosts/firewall/port`, params, TimeoutEnum.T_40S);
};
export const operateForwardRule = (params: { rules: Host.RuleForward[]; forceDelete: boolean }) => {
export const operateForwardRule = (params: { rules: Host.RuleForward[]; forceDelete?: boolean }) => {
return http.post<Host.RulePort>(`/hosts/firewall/forward`, params, TimeoutEnum.T_40S);
};
export const operateIPRule = (params: Host.RuleIP) => {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lang/modules/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2831,6 +2831,7 @@ const message = {
forwardHelper1: 'If you want to forward to the local port, the destination IP should be set to "127.0.0.1".',
forwardHelper2: 'Leave the destination IP blank to forward to the local port.',
forwardHelper3: 'Only support IPv4 port forwarding.',
forwardInboundInterface: 'Forward Inbound Network Interface',
},
runtime: {
runtime: 'Runtime',
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lang/modules/es-es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2797,6 +2797,7 @@ const message = {
forwardHelper1: 'Si quieres reenviar al puerto local, la IP de destino debe ser "127.0.0.1".',
forwardHelper2: 'Deja en blanco la IP de destino para reenviar al puerto local.',
forwardHelper3: 'Solo se admite redirección de puertos IPv4.',
forwardInboundInterface: 'Interfaz de Red de Entrada para Reenvío',
},
runtime: {
runtime: 'Runtime',
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lang/modules/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2745,6 +2745,7 @@ const message = {
forwardHelper1: 'ローカルポートに転送する場合は、宛先IPを「127.0.0.1」に設定する必要があります。',
forwardHelper2: '宛先IPを空白のままにして、ローカルポートに転送します。',
forwardHelper3: 'IPv4ポート転送のみをサポートします。',
forwardInboundInterface: '転送入站ネットワークインターフェース',
},
runtime: {
runtime: 'ランタイム',
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lang/modules/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2696,6 +2696,7 @@ const message = {
forwardHelper1: "로컬 포트로 전달하려면, 대상 IP 를 '127.0.0.1'로 설정해야 합니다.",
forwardHelper2: '대상 IP 를 비워두면 로컬 포트로 전달됩니다.',
forwardHelper3: 'IPv4 포트 전달만 지원됩니다.',
forwardInboundInterface: '포워딩 인바운드 네트워크 인터페이스',
},
runtime: {
runtime: '실행 환경',
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lang/modules/ms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2806,6 +2806,7 @@ const message = {
forwardHelper1: 'Jika anda ingin memajukan ke port tempatan, IP sasaran harus ditetapkan kepada "127.0.0.1".',
forwardHelper2: 'Biarkan IP sasaran kosong untuk memajukan ke port tempatan.',
forwardHelper3: 'Hanya menyokong pemajuan port IPv4.',
forwardInboundInterface: 'Antara Muka Rangkaian Masukan Penerusan',
},
runtime: {
runtime: 'Runtime',
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lang/modules/pt-br.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2811,6 +2811,7 @@ const message = {
'Se você deseja redirecionar para a porta local, o IP de destino deve ser definido como "127.0.0.1".',
forwardHelper2: 'Deixe o IP de destino em branco para redirecionar para a porta local.',
forwardHelper3: 'Somente suporta redirecionamento de porta IPv4.',
forwardInboundInterface: 'Interface de Rede de Entrada para Encaminhamento',
},
runtime: {
runtime: 'Runtime',
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lang/modules/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2806,6 +2806,7 @@ const message = {
'Если вы хотите перенаправить на локальный порт, целевой IP должен быть установлен как "127.0.0.1".',
forwardHelper2: 'Оставьте целевой IP пустым для перенаправления на локальный порт.',
forwardHelper3: 'Поддерживается только переадресация портов IPv4.',
forwardInboundInterface: '转发入站Сетевой интерфейс для пересылки входящего трафика网卡',
},
runtime: {
runtime: 'Среда выполнения',
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lang/modules/tr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2868,6 +2868,7 @@ const message = {
forwardHelper1: 'Yerel porta yönlendirmek istiyorsanız, hedef IP "127.0.0.1" olarak ayarlanmalıdır.',
forwardHelper2: 'Yerel porta yönlendirmek için hedef IP’yi boş bırakın.',
forwardHelper3: 'Yalnızca IPv4 port yönlendirmesini destekler.',
forwardInboundInterface: 'İletme Gelen Ağ Arayüzü',
},
runtime: {
runtime: 'Çalışma Zamanı',
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lang/modules/zh-Hant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2634,6 +2634,7 @@ const message = {
forwardHelper1: '如果是本機埠轉發,目標 IP 為:127.0.0.1',
forwardHelper2: '如果目標 IP 不填寫,預設為本機埠轉發',
forwardHelper3: '目前僅支援 IPv4 的埠轉發',
forwardInboundInterface: '轉發入站網路介面',
},
runtime: {
runtime: '執行環境',
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lang/modules/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2626,6 +2626,7 @@ const message = {
forwardHelper1: '如果是本机端口转发,目标IP为:127.0.0.1',
forwardHelper2: '如果目标IP不填写,则默认为本机端口转发',
forwardHelper3: '当前仅支持 IPv4 的端口转发',
forwardInboundInterface: '转发入站网卡',
},
runtime: {
runtime: '运行环境',
Expand Down
23 changes: 22 additions & 1 deletion frontend/src/views/host/firewall/forward/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@
<el-table-column :label="$t('firewall.sourcePort')" :min-width="70" prop="port" />
<el-table-column :min-width="80" :label="$t('firewall.targetIP')" prop="targetIP" />
<el-table-column :label="$t('firewall.targetPort')" :min-width="70" prop="targetPort" />
<template v-if="fireName === 'ufw'">
<el-table-column
:label="$t('firewall.forwardInboundInterface')"
:min-width="70"
prop="interface"
>
<template #default="{ row }">
<span>
{{ row.interface === '' ? $t('commons.table.all') : row.interface }}
</span>
</template>
</el-table-column>
</template>
<fu-table-operations
width="200px"
:buttons="buttons"
Expand Down Expand Up @@ -125,7 +138,13 @@ const search = async () => {
await searchFireRule(params)
.then((res) => {
loading.value = false;
data.value = res.data.items || [];
data.value =
res.data.items?.map((item) => {
return {
...item,
interface: item.interface === '*' ? '' : item.interface,
};
}) || [];
paginationConfig.total = res.data.total;
})
.catch(() => {
Expand All @@ -141,11 +160,13 @@ const onOpenDialog = async (
port: '8080',
targetIP: '',
targetPort: '',
interface: '',
},
) => {
let params = {
title,
rowData: { ...rowData },
fireName: fireName.value,
};
dialogRef.value!.acceptParams(params);
};
Expand Down
Loading
Loading