Files

214 lines
5.6 KiB
Go

package iptables_helper
import (
"fmt"
"strings"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
)
type FilterRules struct {
ID uint `json:"id"`
Chain string `json:"chain"`
Protocol string `json:"protocol"`
SrcPort string `json:"srcPort"`
DstPort string `json:"dstPort"`
SrcIP string `json:"srcIP"`
DstIP string `json:"dstIP"`
Strategy string `json:"strategy"`
Description string `json:"description"`
}
func ReadFilterRulesByChain(chain string) ([]FilterRules, error) {
var rules []FilterRules
if cmd.CheckIllegal(chain) {
return rules, buserr.New("ErrCmdIllegal")
}
stdout, err := RunWithStd(FilterTab, "-nL", chain)
if err != nil {
return rules, fmt.Errorf("load filter fules by chain %s failed, %v", chain, err)
}
lines := strings.Split(stdout, "\n")
for i := 0; i < len(lines); i++ {
fields := strings.Fields(lines[i])
if len(fields) < 5 {
continue
}
strategy := strings.ToLower(fields[0])
if strategy != "accept" && strategy != "drop" && strategy != "reject" {
continue
}
itemRule := FilterRules{
Chain: chain,
Protocol: loadProtocol(fields[1]),
SrcPort: loadPort("src", fields),
DstPort: loadPort("dst", fields),
SrcIP: loadIP(fields[3]),
DstIP: loadIP(fields[4]),
Strategy: strategy,
}
rules = append(rules, itemRule)
}
return rules, nil
}
func LoadInitStatus(tab string) (bool, bool, error) {
return loadInitStatus(tab, RunWithStd, true)
}
func LoadFamilyInitStatus(family, tab string) (bool, bool, error) {
switch family {
case constant.FirewallFamilyIPv4:
return loadInitStatus(tab, RunWithStd, true)
case constant.FirewallFamilyIPv6:
return loadInitStatus(tab, RunIPv6WithStd, false)
default:
return false, false, fmt.Errorf("unsupported iptables family %q", family)
}
}
func LoadFamilyBindStatus(family string) (bool, error) {
var (
output string
err error
)
switch family {
case constant.FirewallFamilyIPv4:
output, err = RunWithStd(FilterTab, "-S", InputChain)
case constant.FirewallFamilyIPv6:
output, err = RunIPv6WithStd(FilterTab, "-S", InputChain)
default:
return false, fmt.Errorf("unsupported iptables family %q", family)
}
if err != nil {
return false, err
}
return hasBaseChainBinding(output), nil
}
func hasBaseChainBinding(output string) bool {
for _, line := range strings.Split(output, "\n") {
line = strings.TrimSpace(line)
for _, chain := range BasicChains() {
if line == fmt.Sprintf("-A %s -j %s", InputChain, chain) {
return true
}
}
}
return false
}
func loadInitStatus(tab string, runner func(string, ...string) (string, error), requireTerminalRules bool) (bool, bool, error) {
switch tab {
case "base":
filterRules, err := runner(FilterTab, "-S")
if err != nil {
return false, false, fmt.Errorf("load iptables initialization status: %w", err)
}
lines := strings.Split(filterRules, "\n")
initRules := []string{
"-N " + BasicBeforeChain,
"-N " + BasicChain,
"-N " + BasicAfterChain,
fmt.Sprintf("-A %s %s -j ACCEPT", BasicBeforeChain, strings.ReplaceAll(strings.ReplaceAll(IoRuleIn, "'", "\""), " -j ACCEPT", "")),
fmt.Sprintf("-A %s %s -j ACCEPT", BasicBeforeChain, strings.ReplaceAll(strings.ReplaceAll(EstablishedRule, "'", "\""), " -j ACCEPT", "")),
}
if requireTerminalRules {
initRules = append(initRules,
fmt.Sprintf("-A %s %s", BasicAfterChain, DropAllTcp),
fmt.Sprintf("-A %s %s", BasicAfterChain, DropAllUdp),
)
}
bindRules := []string{
fmt.Sprintf("-A %s -j %s", InputChain, BasicBeforeChain),
fmt.Sprintf("-A %s -j %s", InputChain, BasicChain),
fmt.Sprintf("-A %s -j %s", InputChain, BasicAfterChain),
}
isInit, isBind := checkWithInitAndBind(initRules, bindRules, lines)
return isInit, isBind, nil
default:
return false, false, nil
}
}
func checkWithInitAndBind(initRules, bindRules []string, lines []string) (bool, bool) {
for _, rule := range initRules {
found := false
for _, line := range lines {
if strings.TrimSpace(line) == strings.TrimSpace(rule) {
found = true
break
}
}
if !found {
if global.LOG != nil {
global.LOG.Debugf("not found init rule: %s", rule)
}
return false, false
}
}
for _, rule := range bindRules {
found := false
for _, line := range lines {
if strings.TrimSpace(line) == strings.TrimSpace(rule) {
found = true
break
}
}
if !found {
if global.LOG != nil {
global.LOG.Debugf("not found bind rule: %s", rule)
}
return true, false
}
}
return true, true
}
func loadPort(position string, portStr []string) string {
if len(portStr) < 7 {
return ""
}
var portItem string
if strings.Contains(portStr[6], "spt:") && position == "src" {
portItem = strings.ReplaceAll(portStr[6], "spt:", "")
}
if strings.Contains(portStr[6], "dpt:") && position == "dst" {
portItem = strings.ReplaceAll(portStr[6], "dpt:", "")
}
if strings.Contains(portStr[6], "spts:") && position == "src" {
portItem = strings.ReplaceAll(portStr[6], "spts:", "")
}
if strings.Contains(portStr[6], "dpts:") && position == "dst" {
portItem = strings.ReplaceAll(portStr[6], "dpts:", "")
}
portItem = strings.ReplaceAll(portItem, ":", "-")
return portItem
}
func loadIP(ipStr string) string {
if ipStr == ANYWHERE || ipStr == "0.0.0.0/0" {
return ""
}
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
}
}