2023-11-16 22:50:21 +03:00
|
|
|
package docker
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net"
|
|
|
|
"strconv"
|
2023-11-22 22:21:11 +03:00
|
|
|
"strings"
|
2023-11-16 22:50:21 +03:00
|
|
|
|
2023-11-29 18:52:59 +03:00
|
|
|
"github.com/Neur0toxine/sshpoke/internal/config"
|
2023-11-16 22:50:21 +03:00
|
|
|
"github.com/Neur0toxine/sshpoke/internal/logger"
|
2023-11-18 12:36:17 +03:00
|
|
|
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
2023-11-29 18:52:59 +03:00
|
|
|
"github.com/Neur0toxine/sshpoke/pkg/smarttypes"
|
2023-11-16 22:50:21 +03:00
|
|
|
"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 {
|
2023-11-29 18:52:59 +03:00
|
|
|
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"`
|
2023-11-16 22:50:21 +03:00
|
|
|
}
|
|
|
|
|
2023-11-29 18:52:59 +03:00
|
|
|
func actorEnabled(actor events.Actor) smarttypes.BoolStr {
|
2023-11-16 22:50:21 +03:00
|
|
|
label, ok := actor.Attributes["sshpoke.enable"]
|
|
|
|
if !ok {
|
2023-12-01 22:56:38 +03:00
|
|
|
return smarttypes.AsBoolStr(false)
|
2023-11-16 22:50:21 +03:00
|
|
|
}
|
2023-12-01 22:56:38 +03:00
|
|
|
return smarttypes.BoolStr(label)
|
2023-11-29 18:52:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func populateLabelsFromConfig(labels *labelsConfig, config *config.ServiceLabels) {
|
2023-12-01 22:56:38 +03:00
|
|
|
if labels.Enable.Bool() != config.Enable.Bool() {
|
|
|
|
labels.Enable = smarttypes.AsBoolStr(config.Enable.Bool())
|
2023-11-29 18:52:59 +03:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
2023-11-16 22:50:21 +03:00
|
|
|
}
|
|
|
|
|
2023-11-29 18:52:59 +03:00
|
|
|
func dockerContainerToInternal(
|
|
|
|
container types.Container, configLabels *config.ServiceLabels, defaultServer string) (result dto.Container, ok bool) {
|
2023-11-16 22:50:21 +03:00
|
|
|
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
|
|
|
|
}
|
2023-11-29 18:52:59 +03:00
|
|
|
if configLabels != nil {
|
|
|
|
populateLabelsFromConfig(&labels, configLabels)
|
|
|
|
}
|
2023-12-01 22:56:38 +03:00
|
|
|
if !labels.Enable.Bool() {
|
2023-11-16 22:50:21 +03:00
|
|
|
logger.Sugar.Debugf("skipping container %s because sshpoke is not enabled for it", container.ID)
|
|
|
|
return result, false
|
|
|
|
}
|
|
|
|
if labels.Server == "" {
|
2023-11-29 18:52:59 +03:00
|
|
|
if defaultServer == "" {
|
|
|
|
logger.Sugar.Debugf("skipping container %s because 'sshpoke.server' is not defined", container.ID)
|
|
|
|
return result, false
|
|
|
|
}
|
|
|
|
labels.Server = defaultServer
|
2023-11-16 22:50:21 +03:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-11-18 12:36:17 +03:00
|
|
|
return dto.Container{
|
2023-11-18 21:23:29 +03:00
|
|
|
ID: container.ID,
|
2023-11-22 22:21:11 +03:00
|
|
|
Names: convertNames(container.Names),
|
2023-11-18 21:23:29 +03:00
|
|
|
IP: ip,
|
|
|
|
Port: uint16(port),
|
|
|
|
Server: labels.Server,
|
|
|
|
RemoteHost: labels.RemoteHost,
|
2023-11-16 22:50:21 +03:00
|
|
|
}, true
|
|
|
|
}
|
|
|
|
|
2023-11-22 22:21:11 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-11-16 22:50:21 +03:00
|
|
|
func getKeyVal(m map[string]*network.EndpointSettings) *network.EndpointSettings {
|
|
|
|
for _, v := range m {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|