package docker import ( "net" "strconv" "strings" "github.com/Neur0toxine/sshpoke/internal/config" "github.com/Neur0toxine/sshpoke/internal/logger" "github.com/Neur0toxine/sshpoke/pkg/dto" "github.com/Neur0toxine/sshpoke/pkg/smarttypes" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/events" "github.com/docker/docker/api/types/network" "github.com/mitchellh/mapstructure" ) type labelsConfig struct { Enable smarttypes.BoolStr `mapstructure:"sshpoke.enable"` Network string `mapstructure:"sshpoke.network"` Server string `mapstructure:"sshpoke.server"` Port string `mapstructure:"sshpoke.port"` RemoteHost string `mapstructure:"sshpoke.remote_host"` } func actorEnabled(actor events.Actor) smarttypes.BoolStr { label, ok := actor.Attributes["sshpoke.enable"] if !ok { return smarttypes.AsBoolStr(false) } return smarttypes.BoolStr(label) } func populateLabelsFromConfig(labels *labelsConfig, config *config.ServiceLabels) { if labels.Enable.Bool() != config.Enable.Bool() { labels.Enable = smarttypes.AsBoolStr(config.Enable.Bool()) } if labels.Server != config.Server { labels.Server = config.Server } if labels.Network != config.Network { labels.Network = config.Network } if labels.Port != config.Port { labels.Port = config.Port } if labels.RemoteHost != config.RemoteHost { labels.RemoteHost = config.RemoteHost } } func dockerContainerToInternal( container types.Container, configLabels *config.ServiceLabels, defaultServer string) (result dto.Container, ok bool) { var labels labelsConfig if err := mapstructure.Decode(container.Labels, &labels); err != nil { logger.Sugar.Debugf("skipping container %s because configuration is invalid: %s", container.ID, err) return result, false } if configLabels != nil { populateLabelsFromConfig(&labels, configLabels) } if !labels.Enable.Bool() { logger.Sugar.Debugf("skipping container %s because sshpoke is not enabled for it", container.ID) return result, false } if labels.Server == "" { if defaultServer == "" { logger.Sugar.Debugf("skipping container %s because 'sshpoke.server' is not defined", container.ID) return result, false } labels.Server = defaultServer } if container.NetworkSettings == nil || container.NetworkSettings.Networks == nil || len(container.NetworkSettings.Networks) == 0 { logger.Sugar.Debugf("skipping container %s because network settings are empty", container.ID) return result, false } var ip net.IP if len(container.NetworkSettings.Networks) == 1 { ip = net.ParseIP(getKeyVal(container.NetworkSettings.Networks).IPAddress) } else { if labels.Network == "" { logger.Sugar.Debugf("container %s has %d networks and 'sshpoke.network' label is not specified", container.ID, len(container.NetworkSettings.Networks)) return result, false } ntwrk, exists := container.NetworkSettings.Networks[labels.Network] if !exists { logger.Sugar.Debugf("container %s does not have network %s", container.ID, labels.Network) return result, false } ip = net.ParseIP(ntwrk.IPAddress) } port, err := strconv.Atoi(labels.Port) if err != nil { logger.Sugar.Debugf("skipping container %s because 'sshpoke.port' is not numeric", container.ID) return result, false } return dto.Container{ ID: container.ID, Names: convertNames(container.Names), IP: ip, Port: uint16(port), Server: labels.Server, RemoteHost: labels.RemoteHost, }, true } func convertNames(src []string) []string { if len(src) == 0 { return nil } dst := make([]string, len(src)) for i := 0; i < len(src); i++ { dst[i] = strings.TrimLeft(src[i], "/") } return dst } func getKeyVal(m map[string]*network.EndpointSettings) *network.EndpointSettings { for _, v := range m { return v } return nil }