Files
1Panel/agent/app/service/firewall_docker.go

642 lines
23 KiB
Go

package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/netip"
"sort"
"strings"
"sync"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
agenti18n "github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/1Panel-dev/1Panel/agent/utils/firewall/docker_guard"
"github.com/1Panel-dev/1Panel/agent/utils/firewall/filter"
"github.com/1Panel-dev/1Panel/agent/utils/firewall/lifecycle"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/system"
"github.com/docker/docker/client"
"github.com/google/uuid"
"gorm.io/gorm"
)
const (
dockerGuardComposeProjectLabel = "com.docker.compose.project"
dockerGuardComposeCreatedBy = "createdBy"
)
type dockerGuardRuntime = docker_guard.Runtime
type DockerPortGuardService struct {
policies repo.IDockerPortGuardRepo
runtime dockerGuardRuntime
runtimeForBackend func(string) dockerGuardRuntime
client func() (*client.Client, error)
version func(string) string
}
var (
dockerPortGuardServiceMu sync.Mutex
dockerPortGuardSyncMu sync.RWMutex
dockerPortGuardSyncErr error
ErrDockerGuardInvalid = docker_guard.ErrInvalidPolicy
ErrDockerUnavailable = docker.ErrUnavailable
)
type IDockerPortGuardService interface {
LoadOverview(context.Context) (dto.DockerPortGuardList, error)
Operate(context.Context, dto.DockerPortGuardOperation) error
DeletePolicies(context.Context, dto.DockerPortGuardPolicyBatchDelete) error
UpsertPolicies(context.Context, dto.DockerPortGuardPolicyBatch) error
Reconcile(context.Context) error
}
func NewIDockerPortGuardService() IDockerPortGuardService {
return newDockerPortGuardService()
}
func newDockerPortGuardService() *DockerPortGuardService {
return &DockerPortGuardService{
policies: repo.NewIDockerPortGuardRepo(),
client: docker.NewDockerClient,
version: dockerFirewallVersion,
}
}
func ReconcileDockerPortGuard(ctx context.Context) error {
if global.DB == nil {
return nil
}
return NewIDockerPortGuardService().Reconcile(ctx)
}
func ReconcileDockerPortGuardBestEffort(ctx context.Context) {
if err := ReconcileDockerPortGuard(ctx); err != nil {
global.LOG.Warnf("reconcile Docker port guard failed, err: %v", err)
}
}
func (s *DockerPortGuardService) LoadOverview(ctx context.Context) (dto.DockerPortGuardList, error) {
selectedBackend := selectedDockerFirewallBackend("")
base := s.runtimeStatus(s.guardRuntime(selectedBackend), selectedBackend)
base.Version = s.loadFirewallVersion(selectedBackend)
policies, err := s.policies.List(ctx)
if err != nil {
return dto.DockerPortGuardList{}, err
}
cli, err := s.client()
if err != nil {
base.Message = agenti18n.Get("ErrDockerFailed")
return dto.DockerPortGuardList{Base: base, Containers: []dto.DockerPortGuardContainer{}, OrphanPolicies: dockerGuardPolicyEndpoints(policies)}, nil
}
defer cli.Close()
info, err := cli.Info(ctx)
if err != nil {
base.Message = agenti18n.Get("ErrDockerFailed")
return dto.DockerPortGuardList{Base: base, Containers: []dto.DockerPortGuardContainer{}, OrphanPolicies: dockerGuardPolicyEndpoints(policies)}, nil
}
base.Backend = selectedDockerFirewallBackend(dockerFirewallBackend(info))
base = s.runtimeStatus(s.guardRuntime(base.Backend), base.Backend)
base.Version = s.loadFirewallVersion(base.Backend)
if reconcileErr := lastDockerPortGuardReconcileError(); reconcileErr != nil {
base.Message = reconcileErr.Error()
markDockerGuardReconcileFailure(&base, reconcileErr)
}
endpoints, err := discoverDockerEndpoints(ctx, cli)
if err != nil {
return dto.DockerPortGuardList{}, err
}
endpoints, orphanPolicies := matchDockerGuardPolicies(base, policies, endpoints)
sort.Slice(endpoints, func(i, j int) bool {
return guardEndpointKey(endpoints[i].Family, endpoints[i].HostIP, endpoints[i].HostPort, endpoints[i].Protocol) < guardEndpointKey(endpoints[j].Family, endpoints[j].HostIP, endpoints[j].HostPort, endpoints[j].Protocol)
})
sort.Slice(orphanPolicies, func(i, j int) bool {
return guardEndpointKey(orphanPolicies[i].Family, orphanPolicies[i].HostIP, orphanPolicies[i].HostPort, orphanPolicies[i].Protocol) < guardEndpointKey(orphanPolicies[j].Family, orphanPolicies[j].HostIP, orphanPolicies[j].HostPort, orphanPolicies[j].Protocol)
})
return dto.DockerPortGuardList{Base: base, Containers: groupDockerGuardContainers(endpoints), OrphanPolicies: orphanPolicies}, nil
}
func matchDockerGuardPolicies(
base dto.DockerPortGuardBase,
policies []model.DockerPortGuardPolicy,
endpoints []dto.DockerPortGuardEndpoint,
) ([]dto.DockerPortGuardEndpoint, []dto.DockerPortGuardEndpoint) {
byEndpoint := make(map[string]model.DockerPortGuardPolicy, len(policies))
for _, policy := range policies {
byEndpoint[guardEndpointKey(policy.Family, policy.HostIP, policy.HostPort, policy.Protocol)] = policy
}
for i := range endpoints {
key := guardEndpointKey(endpoints[i].Family, endpoints[i].HostIP, endpoints[i].HostPort, endpoints[i].Protocol)
policy, ok := byEndpoint[key]
if !ok {
continue
}
endpoints[i].PolicyUUID, endpoints[i].Mode, endpoints[i].Sources = policy.UUID, policy.Mode, docker_guard.DecodeSources(policy.Sources)
endpoints[i].Description = policy.Description
endpoints[i].Effective = (policy.Family == docker_guard.FamilyIPv4 && base.IPv4.Effective) || (policy.Family == docker_guard.FamilyIPv6 && base.IPv6.Effective)
delete(byEndpoint, key)
}
orphanPolicies := make([]dto.DockerPortGuardEndpoint, 0, len(byEndpoint))
for _, policy := range byEndpoint {
orphanPolicies = append(orphanPolicies, dto.DockerPortGuardEndpoint{
Family: policy.Family, HostIP: policy.HostIP, HostPort: policy.HostPort, Protocol: policy.Protocol,
PolicyUUID: policy.UUID, Mode: policy.Mode, Sources: docker_guard.DecodeSources(policy.Sources), Description: policy.Description,
})
}
return endpoints, orphanPolicies
}
func (s *DockerPortGuardService) Operate(ctx context.Context, request dto.DockerPortGuardOperation) error {
dockerPortGuardServiceMu.Lock()
defer dockerPortGuardServiceMu.Unlock()
switch request.Operation {
case "initialize":
runtime, backend, err := s.runtimeForDocker(ctx)
if err != nil {
return err
}
policies, err := s.runtimePolicies(ctx)
if err != nil {
return err
}
if err := runtime.Initialize(policies); err != nil {
recordDockerPortGuardReconcileError(err)
return err
}
if err := settingRepo.UpdateOrCreate(constant.FirewallDockerBackendKey, backend); err != nil {
return err
}
if err := settingRepo.UpdateOrCreate(constant.FirewallDockerPortGuardStatusKey, constant.StatusEnable); err != nil {
return err
}
recordDockerPortGuardReconcileError(nil)
return nil
case "bind":
runtime, _, err := s.runtimeForDocker(ctx)
if err != nil {
return err
}
if err := runtime.Bind(); err != nil {
return err
}
return settingRepo.UpdateOrCreate(constant.FirewallDockerPortGuardStatusKey, constant.StatusEnable)
case "unbind":
var err error
if s.runtime != nil {
err = s.runtime.Unbind()
} else {
err = errors.Join(docker_guard.NewManager().Unbind(), docker_guard.NewNftablesManager().Unbind())
}
if err != nil {
return err
}
return settingRepo.UpdateOrCreate(constant.FirewallDockerPortGuardStatusKey, constant.StatusDisable)
default:
return fmt.Errorf("unsupported Docker port guard operation: %s", request.Operation)
}
}
func (s *DockerPortGuardService) DeletePolicies(ctx context.Context, request dto.DockerPortGuardPolicyBatchDelete) error {
dockerPortGuardServiceMu.Lock()
defer dockerPortGuardServiceMu.Unlock()
uuids, err := docker_guard.NormalizePolicyUUIDs(request.UUIDs)
if err != nil {
return err
}
if err := s.policies.DeleteBatch(ctx, uuids); err != nil {
return err
}
return s.reconcileLocked(ctx)
}
func (s *DockerPortGuardService) UpsertPolicies(ctx context.Context, request dto.DockerPortGuardPolicyBatch) error {
dockerPortGuardServiceMu.Lock()
defer dockerPortGuardServiceMu.Unlock()
policies := make([]model.DockerPortGuardPolicy, 0, len(request.Endpoints))
seen := make(map[string]struct{}, len(request.Endpoints))
for _, endpoint := range request.Endpoints {
normalized, err := docker_guard.NormalizePolicy(docker_guard.Policy{
Family: endpoint.Family, HostIP: endpoint.HostIP, HostPort: endpoint.HostPort,
Protocol: endpoint.Protocol, Mode: request.Mode, Sources: request.Sources,
})
if err != nil {
return err
}
key := guardEndpointKey(normalized.Family, normalized.HostIP, normalized.HostPort, normalized.Protocol)
if _, exists := seen[key]; exists {
continue
}
seen[key] = struct{}{}
encoded, _ := json.Marshal(normalized.Sources)
policies = append(policies, model.DockerPortGuardPolicy{
UUID: uuid.NewString(), Family: normalized.Family, HostIP: normalized.HostIP,
HostPort: normalized.HostPort, Protocol: normalized.Protocol, Mode: normalized.Mode,
Sources: string(encoded), Description: strings.TrimSpace(request.Description),
})
}
if err := s.policies.UpsertBatch(ctx, policies); err != nil {
return err
}
return s.reconcileLocked(ctx)
}
func (s *DockerPortGuardService) Reconcile(ctx context.Context) error {
dockerPortGuardServiceMu.Lock()
defer dockerPortGuardServiceMu.Unlock()
return s.reconcileLocked(ctx)
}
func (s *DockerPortGuardService) loadRuleSyncCandidates(
ctx context.Context,
request dto.FirewallRuleSyncRequest,
) (string, []model.DockerPortGuardPolicy, dockerGuardRuntime, error) {
targetProvider, err := databaseRuleSyncTarget(request, "Docker")
if err != nil {
return "", nil, nil, err
}
target := string(targetProvider)
selected, err := s.selectedRuleSyncBackend(ctx)
if err != nil {
return "", nil, nil, err
}
if target != selected {
return "", nil, nil, fmt.Errorf(
"%w: selected Docker firewall backend is %s, requested target is %s",
filter.ErrProviderUnavailable, selected, target,
)
}
policies, err := s.policies.List(ctx)
if err != nil {
return "", nil, nil, err
}
return target, policies, s.guardRuntime(target), nil
}
func (s *DockerPortGuardService) selectedRuleSyncBackend(ctx context.Context) (string, error) {
if global.DB != nil {
selected, _ := settingRepo.GetValueByKey(constant.FirewallDockerBackendKey)
selected = strings.ToLower(strings.TrimSpace(selected))
if selected == constant.FirewallProviderIptables || selected == constant.FirewallProviderNftables {
return selected, nil
}
}
if s.client == nil {
return "", fmt.Errorf("%w: Docker firewall backend is unavailable", ErrDockerUnavailable)
}
cli, err := s.client()
if err != nil {
return "", fmt.Errorf("%w: %v", ErrDockerUnavailable, err)
}
defer cli.Close()
info, err := cli.Info(ctx)
if err != nil {
return "", fmt.Errorf("%w: %v", ErrDockerUnavailable, err)
}
return selectedDockerFirewallBackend(dockerFirewallBackend(info)), nil
}
func dockerGuardPoliciesFromModels(policies []model.DockerPortGuardPolicy) []docker_guard.Policy {
result := make([]docker_guard.Policy, 0, len(policies))
for _, policy := range policies {
result = append(result, dockerGuardPolicyFromModel(policy))
}
return result
}
func dockerGuardPolicyFromModel(policy model.DockerPortGuardPolicy) docker_guard.Policy {
return docker_guard.Policy{
UUID: policy.UUID, Family: policy.Family, HostIP: policy.HostIP, HostPort: policy.HostPort,
Protocol: policy.Protocol, Mode: policy.Mode, Sources: docker_guard.DecodeSources(policy.Sources),
}
}
func dockerGuardRuleSyncDTO(policy model.DockerPortGuardPolicy) *dto.DockerPortGuardEndpoint {
return &dto.DockerPortGuardEndpoint{
Family: policy.Family, HostIP: policy.HostIP, HostPort: policy.HostPort, Protocol: policy.Protocol,
PolicyUUID: policy.UUID, Mode: policy.Mode, Sources: docker_guard.DecodeSources(policy.Sources), Description: policy.Description,
}
}
func dockerGuardRuntimeRuleSyncDTO(policy docker_guard.Policy) *dto.DockerPortGuardEndpoint {
return &dto.DockerPortGuardEndpoint{
Family: policy.Family, HostIP: policy.HostIP, HostPort: policy.HostPort, Protocol: policy.Protocol,
PolicyUUID: policy.UUID, Mode: policy.Mode, Sources: append([]string(nil), policy.Sources...),
}
}
func (s *DockerPortGuardService) reconcileLocked(ctx context.Context) (err error) {
defer func() { recordDockerPortGuardReconcileError(err) }()
persistedEnabled, err := dockerPortGuardPersistedEnabled()
if err != nil {
return fmt.Errorf("load Docker port guard persisted status: %w", err)
}
initialized, err := s.anyRuntimeInitialized()
if err != nil {
return &docker_guard.FamilyError{Family: docker_guard.FamilyIPv4, Err: fmt.Errorf("inspect initialization: %w", err)}
}
if !initialized && !persistedEnabled {
return nil
}
runtime, _, err := s.runtimeForDocker(ctx)
if err != nil {
return err
}
initialized, err = runtime.Initialized(docker_guard.FamilyIPv4)
if err != nil {
return &docker_guard.FamilyError{Family: docker_guard.FamilyIPv4, Err: fmt.Errorf("inspect initialization: %w", err)}
}
if !initialized && !persistedEnabled {
return nil
}
policies, err := s.runtimePolicies(ctx)
if err != nil {
return err
}
if !initialized {
err = runtime.Initialize(policies)
} else {
err = runtime.Reconcile(policies)
}
return err
}
func dockerPortGuardPersistedEnabled() (bool, error) {
status, err := settingRepo.GetValueByKey(constant.FirewallDockerPortGuardStatusKey)
if errors.Is(err, gorm.ErrRecordNotFound) {
return false, nil
}
return status == constant.StatusEnable, err
}
func (s *DockerPortGuardService) anyRuntimeInitialized() (bool, error) {
if s.runtime != nil {
return s.runtime.Initialized(docker_guard.FamilyIPv4)
}
for _, runtime := range []dockerGuardRuntime{docker_guard.NewManager(), docker_guard.NewNftablesManager()} {
initialized, err := runtime.Initialized(docker_guard.FamilyIPv4)
if err != nil || initialized {
return initialized, err
}
}
return false, nil
}
func recordDockerPortGuardReconcileError(err error) {
dockerPortGuardSyncMu.Lock()
dockerPortGuardSyncErr = err
dockerPortGuardSyncMu.Unlock()
}
func lastDockerPortGuardReconcileError() error {
dockerPortGuardSyncMu.RLock()
defer dockerPortGuardSyncMu.RUnlock()
return dockerPortGuardSyncErr
}
func markDockerGuardReconcileFailure(base *dto.DockerPortGuardBase, err error) {
var familyErr *docker_guard.FamilyError
if errors.As(err, &familyErr) {
markDockerGuardFamilyNotEffective(base, familyErr.Family)
if familyErr.Family == docker_guard.FamilyIPv4 {
markDockerGuardFamilyNotEffective(base, docker_guard.FamilyIPv6)
}
return
}
markDockerGuardFamilyNotEffective(base, docker_guard.FamilyIPv4)
markDockerGuardFamilyNotEffective(base, docker_guard.FamilyIPv6)
}
func markDockerGuardFamilyNotEffective(base *dto.DockerPortGuardBase, family string) {
var status *dto.DockerPortGuardFamilyStatus
switch family {
case docker_guard.FamilyIPv4:
status = &base.IPv4
case docker_guard.FamilyIPv6:
status = &base.IPv6
default:
return
}
if !status.Initialized {
return
}
status.State = docker_guard.StatusNotEffective
status.Reason = docker_guard.ReasonInspectFailed
status.Effective = false
}
func (s *DockerPortGuardService) runtimePolicies(ctx context.Context) ([]docker_guard.Policy, error) {
stored, err := s.policies.List(ctx)
if err != nil {
return nil, err
}
policies := make([]docker_guard.Policy, 0, len(stored))
for _, policy := range stored {
policies = append(policies, docker_guard.Policy{UUID: policy.UUID, Family: policy.Family, HostIP: policy.HostIP, HostPort: policy.HostPort, Protocol: policy.Protocol, Mode: policy.Mode, Sources: docker_guard.DecodeSources(policy.Sources)})
}
return policies, nil
}
func (s *DockerPortGuardService) runtimeStatus(runtime dockerGuardRuntime, backend string) dto.DockerPortGuardBase {
ipv4 := runtime.Status(docker_guard.FamilyIPv4)
ipv6 := runtime.Status(docker_guard.FamilyIPv6)
return dto.DockerPortGuardBase{
Name: dockerFirewallDisplayName(backend),
Backend: backend,
IsExist: ipv4.Reason != docker_guard.ReasonCommandMissing || ipv6.Reason != docker_guard.ReasonCommandMissing,
Initialized: ipv4.Initialized || ipv6.Initialized,
Bound: ipv4.Bound || ipv6.Bound,
IPv4: dto.DockerPortGuardFamilyStatus{State: ipv4.State, Reason: ipv4.Reason, Initialized: ipv4.Initialized, Bound: ipv4.Bound, Effective: ipv4.Effective},
IPv6: dto.DockerPortGuardFamilyStatus{State: ipv6.State, Reason: ipv6.Reason, Initialized: ipv6.Initialized, Bound: ipv6.Bound, Effective: ipv6.Effective},
}
}
func (s *DockerPortGuardService) guardRuntime(backend string) dockerGuardRuntime {
if s.runtimeForBackend != nil {
return s.runtimeForBackend(backend)
}
if s.runtime != nil {
return s.runtime
}
return docker_guard.NewRuntime(backend)
}
func (s *DockerPortGuardService) runtimeForDocker(ctx context.Context) (dockerGuardRuntime, string, error) {
if s.runtime != nil {
return s.runtime, selectedDockerFirewallBackend(constant.FirewallProviderIptables), nil
}
cli, err := s.client()
if err != nil {
return nil, "", fmt.Errorf("%w: %v", ErrDockerUnavailable, err)
}
defer cli.Close()
info, err := cli.Info(ctx)
if err != nil {
return nil, "", fmt.Errorf("%w: %v", ErrDockerUnavailable, err)
}
backend := selectedDockerFirewallBackend(dockerFirewallBackend(info))
if backend != constant.FirewallProviderIptables && backend != constant.FirewallProviderNftables {
return nil, backend, fmt.Errorf("Docker firewall backend %q is not supported", backend)
}
return s.guardRuntime(backend), backend, nil
}
func dockerFirewallBackend(info system.Info) string {
if info.FirewallBackend == nil || info.FirewallBackend.Driver == "" {
return constant.FirewallProviderIptables
}
return strings.ToLower(info.FirewallBackend.Driver)
}
func dockerFirewallDisplayName(backend string) string {
switch strings.ToLower(strings.TrimSpace(backend)) {
case constant.FirewallProviderNftables:
return "nftables-docker"
default:
return "iptables-docker"
}
}
func (s *DockerPortGuardService) loadFirewallVersion(backend string) string {
if s.version == nil {
return "-"
}
return s.version(backend)
}
func dockerFirewallVersion(backend string) string {
client, err := lifecycle.NewClientFor(backend)
if err != nil {
return "-"
}
version, err := client.Version()
if err != nil || strings.TrimSpace(version) == "" {
return "-"
}
return version
}
func discoverDockerEndpoints(ctx context.Context, cli *client.Client) ([]dto.DockerPortGuardEndpoint, error) {
containers, err := cli.ContainerList(ctx, containertypes.ListOptions{All: true})
if err != nil {
return nil, err
}
endpoints := make([]dto.DockerPortGuardEndpoint, 0)
for _, item := range containers {
name := strings.TrimPrefix(firstGuardString(item.Names), "/")
compose := item.Labels[dockerGuardComposeProjectLabel]
application := ""
if created, ok := item.Labels[dockerGuardComposeCreatedBy]; ok && created == "Apps" {
application = compose
}
for _, port := range item.Ports {
if port.PublicPort == 0 || (port.Type != "tcp" && port.Type != "udp") {
continue
}
family := docker_guard.FamilyIPv4
hostIP := port.IP
if addr, err := netip.ParseAddr(hostIP); err == nil && addr.Is6() {
family = docker_guard.FamilyIPv6
} else if hostIP == "" {
hostIP = "0.0.0.0"
}
endpoints = append(endpoints, dto.DockerPortGuardEndpoint{Family: family, HostIP: hostIP, HostPort: port.PublicPort, Protocol: port.Type, ContainerID: item.ID, ContainerName: name, ContainerPort: port.PrivatePort, Compose: compose, Application: application, Sources: []string{}})
}
}
return endpoints, nil
}
func dockerGuardPolicyEndpoints(policies []model.DockerPortGuardPolicy) []dto.DockerPortGuardEndpoint {
endpoints := make([]dto.DockerPortGuardEndpoint, 0, len(policies))
for _, policy := range policies {
endpoints = append(endpoints, dto.DockerPortGuardEndpoint{
Family: policy.Family, HostIP: policy.HostIP, HostPort: policy.HostPort, Protocol: policy.Protocol,
PolicyUUID: policy.UUID, Mode: policy.Mode, Sources: docker_guard.DecodeSources(policy.Sources),
Description: policy.Description,
})
}
return endpoints
}
func groupDockerGuardContainers(endpoints []dto.DockerPortGuardEndpoint) []dto.DockerPortGuardContainer {
containers := make(map[string]*dto.DockerPortGuardContainer)
order := make([]string, 0)
for _, endpoint := range endpoints {
key := endpoint.ContainerID
if key == "" {
key = "__orphan__"
}
container, ok := containers[key]
if !ok {
container = &dto.DockerPortGuardContainer{
Key: key, Name: endpoint.ContainerName, Compose: endpoint.Compose,
Application: endpoint.Application, Endpoints: []dto.DockerPortGuardEndpoint{},
}
containers[key] = container
order = append(order, key)
}
container.Endpoints = append(container.Endpoints, endpoint)
}
sort.Slice(order, func(i, j int) bool {
return containers[order[i]].Name < containers[order[j]].Name
})
result := make([]dto.DockerPortGuardContainer, 0, len(order))
for _, key := range order {
container := containers[key]
items := make([]docker.PortRangeItem, 0, len(container.Endpoints))
for i, endpoint := range container.Endpoints {
sources := append([]string(nil), endpoint.Sources...)
sort.Strings(sources)
policyKey := fmt.Sprintf("%t|%s|%s|%t|%s", endpoint.PolicyUUID != "", endpoint.Mode, strings.Join(sources, ","), endpoint.Effective, endpoint.Description)
items = append(items, docker.PortRangeItem{
Key: endpoint.Family + "|" + endpoint.HostIP + "|" + endpoint.Protocol + "|" + policyKey,
PublicPort: endpoint.HostPort, PrivatePort: endpoint.ContainerPort,
HasPrivatePort: endpoint.ContainerPort != 0, Position: i,
})
}
container.PortGroups = make([]dto.DockerPortGuardPortGroup, 0, len(items))
for _, portRange := range docker.MergePortRanges(items) {
start := container.Endpoints[portRange.Start.Position]
address := start.HostIP
if strings.Contains(address, ":") {
address = "[" + address + "]"
}
ports := fmt.Sprintf("%d", portRange.Start.PublicPort)
if portRange.Start.PublicPort != portRange.End.PublicPort {
ports = fmt.Sprintf("%d-%d", portRange.Start.PublicPort, portRange.End.PublicPort)
}
container.PortGroups = append(container.PortGroups, dto.DockerPortGuardPortGroup{
Key: fmt.Sprintf("%s|%d-%d", portRange.Start.Key, portRange.Start.PublicPort, portRange.End.PublicPort),
Label: fmt.Sprintf("%s:%s/%s", address, ports, start.Protocol), Endpoint: start,
Endpoints: func() []dto.DockerPortGuardEndpoint {
members := make([]dto.DockerPortGuardEndpoint, 0, len(portRange.Items))
for _, item := range portRange.Items {
members = append(members, container.Endpoints[item.Position])
}
return members
}(),
})
}
result = append(result, *container)
}
return result
}
func guardEndpointKey(family, hostIP string, hostPort uint16, protocol string) string {
return fmt.Sprintf("%s|%s|%d|%s", family, hostIP, hostPort, protocol)
}
func firstGuardString(values []string) string {
if len(values) == 0 {
return ""
}
return values[0]
}