mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-23 16:00:52 +00:00
Port forwarding no longer shares the filter client. FilterClient keeps only
filter capabilities, and forwarding gets its own adapter, service and boot
replay:
- utils/firewall/forwarding holds the provider adapters. firewalld uses native
forward-port, ufw and iptables share the NAT implementation moved out of
client/iptables/forward.go.
- service/forwarding.go owns base info, search, operate, enable and replay.
The API keeps its routes and dispatches on name/type/operate.
- init/firewall replays forwarding through that service instead of loading NAT
rule files inline.
Also adds 1PANEL_FORWARD to the IptablesOp name enum: the frontend already
sends {"name":"1PANEL_FORWARD","operate":"init-forward"} and the validator
rejected it with 400 before reaching the service. Besides that, the only
observable difference is that a forward-tab search no longer triggers the
port/address record cleanup goroutine on the side.
216 lines
6.2 KiB
Go
216 lines
6.2 KiB
Go
package iptables
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/global"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
|
)
|
|
|
|
const (
|
|
ChainInput = "INPUT"
|
|
ChainOutput = "OUTPUT"
|
|
Chain1PanelInput = "1PANEL_INPUT"
|
|
Chain1PanelOutput = "1PANEL_OUTPUT"
|
|
Chain1PanelBasicBefore = "1PANEL_BASIC_BEFORE"
|
|
Chain1PanelBasic = "1PANEL_BASIC"
|
|
Chain1PanelBasicAfter = "1PANEL_BASIC_AFTER"
|
|
)
|
|
|
|
const (
|
|
EstablishedRule = "-m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -m comment --comment \"ESTABLISHED Whitelist\""
|
|
IoRuleIn = "-i lo -j ACCEPT -m comment --comment \"Loopback Whitelist\""
|
|
DropAllTcp = "-p tcp -j DROP"
|
|
DropAllUdp = "-p udp -j DROP"
|
|
AllowSSH = "-p tcp --dport ssh -j ACCEPT"
|
|
)
|
|
|
|
const (
|
|
ACCEPT = "ACCEPT"
|
|
DROP = "DROP"
|
|
REJECT = "REJECT"
|
|
ANYWHERE = "anywhere"
|
|
)
|
|
|
|
const (
|
|
FilterTab = "filter"
|
|
NatTab = "nat"
|
|
)
|
|
|
|
func runIptables(tab string, ignoreExist1, withWait bool, ruleArgs ...string) (string, error) {
|
|
options := []cmd.Option{cmd.WithTimeout(60 * time.Second)}
|
|
if ignoreExist1 {
|
|
options = append(options, cmd.WithIgnoreExist1())
|
|
}
|
|
cmdMgr := cmd.NewCommandMgr(options...)
|
|
args := []string{"-t", tab}
|
|
if withWait {
|
|
args = append(args, "-w")
|
|
}
|
|
args = append(args, ruleArgs...)
|
|
return cmdMgr.RunWithOptionalSudoAndStdout("iptables", args...)
|
|
}
|
|
|
|
func RunWithStd(tab string, args ...string) (string, error) {
|
|
stdout, err := runIptables(tab, true, true, args...)
|
|
if err != nil {
|
|
global.LOG.Errorf("iptables command failed [table=%s, args=%s]: %v", tab, strings.Join(args, " "), err)
|
|
return stdout, err
|
|
}
|
|
return stdout, nil
|
|
}
|
|
func RunWithoutIgnore(tab string, args ...string) (string, error) {
|
|
stdout, err := runIptables(tab, false, false, args...)
|
|
if err != nil {
|
|
return stdout, err
|
|
}
|
|
return stdout, nil
|
|
}
|
|
func Run(tab string, args ...string) error {
|
|
if _, err := RunWithStd(tab, args...); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func NewChain(tab, chain string) error {
|
|
return Run(tab, "-N", chain)
|
|
}
|
|
|
|
func ClearChain(tab, chain string) error {
|
|
return Run(tab, "-F", chain)
|
|
}
|
|
|
|
func AddRule(tab, chain string, ruleArgs ...string) error {
|
|
if CheckRuleExist(tab, chain, ruleArgs...) {
|
|
return nil
|
|
}
|
|
args := append([]string{"-A", chain}, ruleArgs...)
|
|
return Run(tab, args...)
|
|
}
|
|
func DeleteRule(tab, chain string, ruleArgs ...string) error {
|
|
args := append([]string{"-D", chain}, ruleArgs...)
|
|
return Run(tab, args...)
|
|
}
|
|
|
|
func CheckChainExist(tab, chain string) (bool, error) {
|
|
stdout, err := RunWithStd(tab, "-S")
|
|
if err != nil {
|
|
global.LOG.Errorf("check chain %s from tab %s exist failed, err: %v", chain, tab, err)
|
|
return false, fmt.Errorf("check chain %s from tab %s exist failed, err: %v", chain, tab, err)
|
|
}
|
|
for _, line := range strings.Split(stdout, "\n") {
|
|
if strings.TrimSpace(line) == "-N "+chain {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
func CheckChainBind(tab, parentChain, chain string) (bool, error) {
|
|
stdout, err := RunWithStd(tab, "-S", parentChain)
|
|
if err != nil {
|
|
global.LOG.Errorf("check chain %s from tab %s is bind to %s failed, err: %v", chain, tab, parentChain, err)
|
|
return false, fmt.Errorf("check chain %s from tab %s is bind to %s failed, err: %v", chain, tab, parentChain, err)
|
|
}
|
|
for _, line := range strings.Split(stdout, "\n") {
|
|
if strings.Contains(line, "-j "+chain) {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
func CheckRuleExist(tab, chain string, ruleArgs ...string) bool {
|
|
args := append([]string{"-C", chain}, ruleArgs...)
|
|
_, err := RunWithoutIgnore(tab, args...)
|
|
return err == nil
|
|
}
|
|
|
|
func AddChain(tab, chain string) error {
|
|
exists, err := CheckChainExist(tab, chain)
|
|
if err != nil {
|
|
return fmt.Errorf("check chain %s exist from tab %s failed, err: %w", chain, tab, err)
|
|
}
|
|
if !exists {
|
|
if err := NewChain(tab, chain); err != nil {
|
|
return fmt.Errorf("add chain %s for tab %s failed, err: %w", tab, chain, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
func BindChain(tab, targetChain, chain string, position int) error {
|
|
line, err := FindChainNum(tab, targetChain, chain)
|
|
if err != nil {
|
|
return fmt.Errorf("find chain %s number from %s failed, err: %w", chain, targetChain, err)
|
|
}
|
|
if line == 0 {
|
|
if err := Run(tab, "-I", targetChain, strconv.Itoa(position), "-j", chain); err != nil {
|
|
return fmt.Errorf("bind chain %s to %s failed, err: %w", chain, targetChain, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
func UnbindChain(tab, targetChain, chain string) error {
|
|
line, err := FindChainNum(tab, targetChain, chain)
|
|
if err != nil {
|
|
return fmt.Errorf("find chain %s number from %s failed, err: %w", chain, targetChain, err)
|
|
}
|
|
if line != 0 {
|
|
return Run(tab, "-D", targetChain, strconv.Itoa(line))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func FindChainNum(tab, targetChain, chain string) (int, error) {
|
|
cmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(60*time.Second))
|
|
commandName, commandArgs := cmd.WrapWithOptionalSudo("iptables", "-w", "-t", tab, "-L", targetChain, "--line-numbers", "-n")
|
|
stdout, err := cmdMgr.RunPipe(
|
|
cmd.PipeCommand{Name: commandName, Args: commandArgs},
|
|
cmd.PipeCommand{Name: "grep", Args: []string{"-w", chain}},
|
|
)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to list rules in chain %s: %w", targetChain, err)
|
|
}
|
|
|
|
lineItem := strings.TrimSpace(stdout)
|
|
lines := strings.Split(lineItem, "\n")
|
|
for _, line := range lines {
|
|
fields := strings.Fields(line)
|
|
if len(fields) < 2 {
|
|
continue
|
|
}
|
|
if fields[1] == chain {
|
|
itemNum, err := strconv.Atoi(fields[0])
|
|
return itemNum, err
|
|
}
|
|
}
|
|
return 0, nil
|
|
}
|
|
|
|
func AddChainWithAppend(tab, parentChain, chain string) error {
|
|
exists, err := CheckChainExist(tab, chain)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to check chain %s: %w", chain, err)
|
|
}
|
|
if !exists {
|
|
if err := NewChain(tab, chain); err != nil {
|
|
return fmt.Errorf("failed to create chain %s: %w", chain, err)
|
|
}
|
|
}
|
|
isBind, err := CheckChainBind(tab, parentChain, chain)
|
|
if err != nil {
|
|
return fmt.Errorf("check chain %s bind to %s failed, err: %w", parentChain, chain, err)
|
|
}
|
|
if !isBind {
|
|
if err := AppendChain(tab, parentChain, chain); err != nil {
|
|
return fmt.Errorf("failed to append %s to %s: %w", chain, parentChain, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
func AppendChain(tab string, parentChain, chain string) error {
|
|
return Run(tab, "-A", parentChain, "-j", chain)
|
|
}
|