Update to use docker network checking against newt networking

This commit is contained in:
Jonny Booker
2025-06-09 22:54:10 +01:00
parent 50b621f17c
commit a4d4976103
2 changed files with 128 additions and 28 deletions

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"fmt" "fmt"
"net" "net"
"os"
"strconv"
"strings" "strings"
"time" "time"
@@ -67,8 +69,44 @@ func CheckSocket(socketPath string) bool {
return true return true
} }
// IsWithinNewtNetwork checks if a provided target is within the newt network
func IsWithinNewtNetwork(socketPath string, containerNameAsHostname bool, targetAddress string, targetPort int) (bool, error) {
containers, err := ListContainers(socketPath, containerNameAsHostname)
if err != nil {
return false, fmt.Errorf("failed to list Docker containers: %s", err)
}
// If we can find the passed hostname/ip in the networks or as the container name, it is valid and can add it
for _, c := range containers {
for _, network := range c.Networks {
//If the container name matches, check the ports being mapped too
if containerNameAsHostname {
if c.Name == targetAddress {
for _, port := range c.Ports {
if port.PublicPort == targetPort || port.PrivatePort == targetPort {
return true, nil
}
}
}
} else {
//If the ip address matches, check the ports being mapped too
if network.IPAddress == targetAddress {
for _, port := range c.Ports {
if port.PublicPort == targetPort || port.PrivatePort == targetPort {
return true, nil
}
}
}
}
}
}
combinedTargetAddress := targetAddress + ":" + strconv.Itoa(targetPort)
return false, fmt.Errorf("target address not within newt network: %s", combinedTargetAddress)
}
// ListContainers lists all Docker containers with their network information // ListContainers lists all Docker containers with their network information
func ListContainers(socketPath string) ([]Container, error) { func ListContainers(socketPath string, containerNameAsHostname bool) ([]Container, error) {
// Use the provided socket path or default to standard location // Use the provided socket path or default to standard location
if socketPath == "" { if socketPath == "" {
socketPath = "/var/run/docker.sock" socketPath = "/var/run/docker.sock"
@@ -88,6 +126,12 @@ func ListContainers(socketPath string) ([]Container, error) {
} }
defer cli.Close() defer cli.Close()
// Get the newt container
newtContainer, err := getNewtContainer(ctx, cli)
if err != nil {
return nil, fmt.Errorf("failed to list containers: %v", err)
}
// List containers // List containers
containers, err := cli.ContainerList(ctx, container.ListOptions{All: true}) containers, err := cli.ContainerList(ctx, container.ListOptions{All: true})
if err != nil { if err != nil {
@@ -96,6 +140,12 @@ func ListContainers(socketPath string) ([]Container, error) {
var dockerContainers []Container var dockerContainers []Container
for _, c := range containers { for _, c := range containers {
// Get container name (remove leading slash)
name := ""
if len(c.Names) > 0 {
name = strings.TrimPrefix(c.Names[0], "/")
}
// Convert ports // Convert ports
var ports []Port var ports []Port
for _, port := range c.Ports { for _, port := range c.Ports {
@@ -112,29 +162,32 @@ func ListContainers(socketPath string) ([]Container, error) {
ports = append(ports, dockerPort) ports = append(ports, dockerPort)
} }
// Get container name (remove leading slash)
name := ""
if len(c.Names) > 0 {
name = strings.TrimPrefix(c.Names[0], "/")
}
// Get network information by inspecting the container // Get network information by inspecting the container
networks := make(map[string]Network) networks := make(map[string]Network)
// Inspect container to get detailed network information // Inspect the container to get detailed network information
containerInfo, err := cli.ContainerInspect(ctx, c.ID) containerInfo, err := cli.ContainerInspect(ctx, c.ID)
if err != nil { if err != nil {
logger.Debug("Failed to inspect container %s for network info: %v", c.ID[:12], err) logger.Debug("Failed to inspect container %s for network info: %v", c.ID[:12], err)
// Continue without network info if inspection fails // Continue without network info if inspection fails
} else { } else {
// Only containers within the newt network will be returned
isInNewtNetwork := false
// Extract network information from inspection // Extract network information from inspection
if containerInfo.NetworkSettings != nil && containerInfo.NetworkSettings.Networks != nil { if containerInfo.NetworkSettings != nil && containerInfo.NetworkSettings.Networks != nil {
for networkName, endpoint := range containerInfo.NetworkSettings.Networks { for networkName, endpoint := range containerInfo.NetworkSettings.Networks {
// Determine if the current container is in the newt network
for _, newtNetwork := range newtContainer.NetworkSettings.Networks {
if !isInNewtNetwork {
isInNewtNetwork = endpoint.NetworkID == newtNetwork.NetworkID
}
}
dockerNetwork := Network{ dockerNetwork := Network{
NetworkID: endpoint.NetworkID, NetworkID: endpoint.NetworkID,
EndpointID: endpoint.EndpointID, EndpointID: endpoint.EndpointID,
Gateway: endpoint.Gateway, Gateway: endpoint.Gateway,
IPAddress: endpoint.IPAddress,
IPPrefixLen: endpoint.IPPrefixLen, IPPrefixLen: endpoint.IPPrefixLen,
IPv6Gateway: endpoint.IPv6Gateway, IPv6Gateway: endpoint.IPv6Gateway,
GlobalIPv6Address: endpoint.GlobalIPv6Address, GlobalIPv6Address: endpoint.GlobalIPv6Address,
@@ -143,9 +196,21 @@ func ListContainers(socketPath string) ([]Container, error) {
Aliases: endpoint.Aliases, Aliases: endpoint.Aliases,
DNSNames: endpoint.DNSNames, DNSNames: endpoint.DNSNames,
} }
// Don't set the IP address if container name is used as hostname
if !containerNameAsHostname {
dockerNetwork.IPAddress = endpoint.IPAddress
}
networks[networkName] = dockerNetwork networks[networkName] = dockerNetwork
} }
} }
// Don't continue returning this container if not in the newt network(s)
if !isInNewtNetwork {
logger.Debug("container not found within the newt network, skipping: %s", name)
continue
}
} }
dockerContainer := Container{ dockerContainer := Container{
@@ -164,3 +229,19 @@ func ListContainers(socketPath string) ([]Container, error) {
return dockerContainers, nil return dockerContainers, nil
} }
func getNewtContainer(dockerContext context.Context, dockerClient *client.Client) (*container.InspectResponse, error) {
// Get newt hostname from the os
newtContainerName, err := os.Hostname()
if err != nil {
return nil, fmt.Errorf("failed to find newt hostname: %v", err)
}
// Get newt container from the docker socket
newtContainer, err := dockerClient.ContainerInspect(dockerContext, newtContainerName)
if err != nil {
return nil, fmt.Errorf("failed to find newt container: %v", err)
}
return &newtContainer, nil
}

29
main.go
View File

@@ -353,6 +353,8 @@ var (
updownScript string updownScript string
tlsPrivateKey string tlsPrivateKey string
dockerSocket string dockerSocket string
dockerContainerAsHostname string
dockerContainerAsHostnameBool bool
) )
func main() { func main() {
@@ -366,6 +368,7 @@ func main() {
updownScript = os.Getenv("UPDOWN_SCRIPT") updownScript = os.Getenv("UPDOWN_SCRIPT")
tlsPrivateKey = os.Getenv("TLS_CLIENT_CERT") tlsPrivateKey = os.Getenv("TLS_CLIENT_CERT")
dockerSocket = os.Getenv("DOCKER_SOCKET") dockerSocket = os.Getenv("DOCKER_SOCKET")
dockerContainerAsHostname = os.Getenv("DOCKER_CONTAINER_NAME_AS_HOSTNAME")
if endpoint == "" { if endpoint == "" {
flag.StringVar(&endpoint, "endpoint", "", "Endpoint of your pangolin server") flag.StringVar(&endpoint, "endpoint", "", "Endpoint of your pangolin server")
@@ -394,6 +397,9 @@ func main() {
if dockerSocket == "" { if dockerSocket == "" {
flag.StringVar(&dockerSocket, "docker-socket", "", "Path to Docker socket (typically /var/run/docker.sock)") flag.StringVar(&dockerSocket, "docker-socket", "", "Path to Docker socket (typically /var/run/docker.sock)")
} }
if dockerContainerAsHostname == "" {
flag.StringVar(&dockerContainerAsHostname, "docker-container-name-as-hostname", "false", "Use container name when hostname for networking (true or false)")
}
// do a --version check // do a --version check
version := flag.Bool("version", false, "Print the version") version := flag.Bool("version", false, "Print the version")
@@ -418,6 +424,13 @@ func main() {
logger.Fatal("Failed to parse MTU: %v", err) logger.Fatal("Failed to parse MTU: %v", err)
} }
// pase if to use hostname over ip address for network sent to pangolin
dockerContainerAsHostnameBool, err = strconv.ParseBool(dockerContainerAsHostname)
if err != nil {
logger.Info("Docker use container name cannot be parsed. Defaulting to 'false'")
dockerContainerAsHostnameBool = false
}
privateKey, err = wgtypes.GeneratePrivateKey() privateKey, err = wgtypes.GeneratePrivateKey()
if err != nil { if err != nil {
logger.Fatal("Failed to generate private key: %v", err) logger.Fatal("Failed to generate private key: %v", err)
@@ -676,7 +689,7 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub
} }
// List Docker containers // List Docker containers
containers, err := docker.ListContainers(dockerSocket) containers, err := docker.ListContainers(dockerSocket, dockerContainerAsHostnameBool)
if err != nil { if err != nil {
logger.Error("Failed to list Docker containers: %v", err) logger.Error("Failed to list Docker containers: %v", err)
return return
@@ -760,12 +773,14 @@ func updateTargets(pm *proxy.ProxyManager, action string, tunnelIP string, proto
} }
if action == "add" { if action == "add" {
target := parts[1] + ":" + parts[2] targetAddress := parts[1]
targetPort, _ := strconv.Atoi(parts[2])
combinedAddress := targetAddress + ":" + parts[2]
// Call updown script if provided // Call updown script if provided
processedTarget := target processedTarget := combinedAddress
if updownScript != "" { if updownScript != "" {
newTarget, err := executeUpdownScript(action, proto, target) newTarget, err := executeUpdownScript(action, proto, combinedAddress)
if err != nil { if err != nil {
logger.Warn("Updown script error: %v", err) logger.Warn("Updown script error: %v", err)
} else if newTarget != "" { } else if newTarget != "" {
@@ -783,8 +798,12 @@ func updateTargets(pm *proxy.ProxyManager, action string, tunnelIP string, proto
} }
// Add the new target // Add the new target
isWithinNewtNetwork, err := docker.IsWithinNewtNetwork(dockerSocket, dockerContainerAsHostnameBool, targetAddress, targetPort)
if !isWithinNewtNetwork {
logger.Error("Not adding target: %v", err)
} else {
pm.AddTarget(proto, tunnelIP, port, processedTarget) pm.AddTarget(proto, tunnelIP, port, processedTarget)
}
} else if action == "remove" { } else if action == "remove" {
logger.Info("Removing target with port %d", port) logger.Info("Removing target with port %d", port)