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
}

55
main.go
View File

@@ -341,18 +341,20 @@ func resolveDomain(domain string) (string, error) {
} }
var ( var (
endpoint string endpoint string
id string id string
secret string secret string
mtu string mtu string
mtuInt int mtuInt int
dns string dns string
privateKey wgtypes.Key privateKey wgtypes.Key
err error err error
logLevel string logLevel string
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
pm.AddTarget(proto, tunnelIP, port, processedTarget) isWithinNewtNetwork, err := docker.IsWithinNewtNetwork(dockerSocket, dockerContainerAsHostnameBool, targetAddress, targetPort)
if !isWithinNewtNetwork {
logger.Error("Not adding target: %v", err)
} else {
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)