Files
1Panel/agent/utils/docker/port.go
ssongliu 7915230121 refactor: rebuild firewall management (#13628)
* refactor(firewall): rebuild rule management foundation

* refactor(firewall): streamline rule checks and inventory

* feat(firewall): improve native rule inventory

* refactor(firewall): refine rule management

* feat: add Docker port guard

* feat(firewall): support native nftables

* feat(firewall): add configurable firewall selection

* feat(firewall): support nftables docker port guard

* refactor(firewall): complete v2 rule management and migration

* refactor(firewall): align state and API contracts

* refactor(firewall): unify rule management operations

* feat: refine firewall v2 rules and forwarding

* fix(firewall): harden dual-stack rule management

* refactor(firewall): consolidate rule validation and persistence
2026-08-24 12:51:34 +08:00

99 lines
3.0 KiB
Go

package docker
import (
"fmt"
"sort"
"strings"
containertypes "github.com/docker/docker/api/types/container"
)
// PortRangeItem describes the dimensions shared by Docker port range displays.
// Key contains dimensions that must stay equal (for example address, protocol,
// and an optional protection policy identity).
type PortRangeItem struct {
Key string
PublicPort uint16
PrivatePort uint16
HasPrivatePort bool
Position int
}
type PortRange struct {
Start PortRangeItem
End PortRangeItem
Items []PortRangeItem
}
func MergePortRanges(items []PortRangeItem) []PortRange {
sorted := append([]PortRangeItem(nil), items...)
sort.SliceStable(sorted, func(i, j int) bool {
if sorted[i].Key != sorted[j].Key {
return sorted[i].Key < sorted[j].Key
}
if sorted[i].PublicPort != sorted[j].PublicPort {
return sorted[i].PublicPort < sorted[j].PublicPort
}
return sorted[i].PrivatePort < sorted[j].PrivatePort
})
ranges := make([]PortRange, 0, len(sorted))
for _, item := range sorted {
if len(ranges) == 0 {
ranges = append(ranges, PortRange{Start: item, End: item, Items: []PortRangeItem{item}})
continue
}
last := &ranges[len(ranges)-1]
publicContinuous := (last.End.PublicPort == 0 && item.PublicPort == 0) || item.PublicPort == last.End.PublicPort+1
privateContinuous := (!last.End.HasPrivatePort && !item.HasPrivatePort) ||
(last.End.HasPrivatePort && item.HasPrivatePort && item.PrivatePort == last.End.PrivatePort+1)
if item.Key == last.End.Key && publicContinuous && privateContinuous {
last.End = item
last.Items = append(last.Items, item)
continue
}
ranges = append(ranges, PortRange{Start: item, End: item, Items: []PortRangeItem{item}})
}
return ranges
}
func SimplifyPorts(ports []containertypes.Port) []string {
items := make([]PortRangeItem, 0, len(ports))
for i, port := range ports {
family := "ipv4"
if strings.Contains(port.IP, ":") {
family = "ipv6"
}
items = append(items, PortRangeItem{
Key: family + "|" + port.IP + "|" + port.Type,
PublicPort: port.PublicPort,
PrivatePort: port.PrivatePort,
HasPrivatePort: true,
Position: i,
})
}
result := make([]string, 0, len(items))
for _, portRange := range MergePortRanges(items) {
start, end := ports[portRange.Start.Position], ports[portRange.End.Position]
ip := ""
if start.IP != "" {
ip = start.IP + ":"
}
privatePorts := fmt.Sprintf("%d", start.PrivatePort)
if start.PrivatePort != end.PrivatePort {
privatePorts = fmt.Sprintf("%d-%d", start.PrivatePort, end.PrivatePort)
}
value := fmt.Sprintf("%s%s/%s", ip, privatePorts, start.Type)
if start.PublicPort != 0 {
publicPorts := fmt.Sprintf("%d", start.PublicPort)
if start.PublicPort != end.PublicPort {
publicPorts = fmt.Sprintf("%d-%d", start.PublicPort, end.PublicPort)
}
value = fmt.Sprintf("%s%s->%s/%s", ip, publicPorts, privatePorts, start.Type)
}
result = append(result, value)
}
return result
}