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"
"fmt"
"net"
"os"
"strconv"
"strings"
"time"
@@ -67,8 +69,44 @@ func CheckSocket(socketPath string) bool {
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
func ListContainers(socketPath string) ([]Container, error) {
func ListContainers(socketPath string, containerNameAsHostname bool) ([]Container, error) {
// Use the provided socket path or default to standard location
if socketPath == "" {
socketPath = "/var/run/docker.sock"
@@ -88,6 +126,12 @@ func ListContainers(socketPath string) ([]Container, error) {
}
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
containers, err := cli.ContainerList(ctx, container.ListOptions{All: true})
if err != nil {
@@ -96,6 +140,12 @@ func ListContainers(socketPath string) ([]Container, error) {
var dockerContainers []Container
for _, c := range containers {
// Get container name (remove leading slash)
name := ""
if len(c.Names) > 0 {
name = strings.TrimPrefix(c.Names[0], "/")
}
// Convert ports
var ports []Port
for _, port := range c.Ports {
@@ -112,29 +162,32 @@ func ListContainers(socketPath string) ([]Container, error) {
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
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)
if err != nil {
logger.Debug("Failed to inspect container %s for network info: %v", c.ID[:12], err)
// Continue without network info if inspection fails
} else {
// Only containers within the newt network will be returned
isInNewtNetwork := false
// Extract network information from inspection
if containerInfo.NetworkSettings != nil && containerInfo.NetworkSettings.Networks != nil {
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{
NetworkID: endpoint.NetworkID,
EndpointID: endpoint.EndpointID,
Gateway: endpoint.Gateway,
IPAddress: endpoint.IPAddress,
IPPrefixLen: endpoint.IPPrefixLen,
IPv6Gateway: endpoint.IPv6Gateway,
GlobalIPv6Address: endpoint.GlobalIPv6Address,
@@ -143,9 +196,21 @@ func ListContainers(socketPath string) ([]Container, error) {
Aliases: endpoint.Aliases,
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
}
}
// 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{
@@ -164,3 +229,19 @@ func ListContainers(socketPath string) ([]Container, error) {
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
}

57
main.go
View File

@@ -341,18 +341,20 @@ func resolveDomain(domain string) (string, error) {
}
var (
endpoint string
id string
secret string
mtu string
mtuInt int
dns string
privateKey wgtypes.Key
err error
logLevel string
updownScript string
tlsPrivateKey string
dockerSocket string
endpoint string
id string
secret string
mtu string
mtuInt int
dns string
privateKey wgtypes.Key
err error
logLevel string
updownScript string
tlsPrivateKey string
dockerSocket string
dockerContainerAsHostname string
dockerContainerAsHostnameBool bool
)
func main() {
@@ -366,6 +368,7 @@ func main() {
updownScript = os.Getenv("UPDOWN_SCRIPT")
tlsPrivateKey = os.Getenv("TLS_CLIENT_CERT")
dockerSocket = os.Getenv("DOCKER_SOCKET")
dockerContainerAsHostname = os.Getenv("DOCKER_CONTAINER_NAME_AS_HOSTNAME")
if endpoint == "" {
flag.StringVar(&endpoint, "endpoint", "", "Endpoint of your pangolin server")
@@ -394,6 +397,9 @@ func main() {
if dockerSocket == "" {
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
version := flag.Bool("version", false, "Print the version")
@@ -418,6 +424,13 @@ func main() {
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()
if err != nil {
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
containers, err := docker.ListContainers(dockerSocket)
containers, err := docker.ListContainers(dockerSocket, dockerContainerAsHostnameBool)
if err != nil {
logger.Error("Failed to list Docker containers: %v", err)
return
@@ -760,12 +773,14 @@ func updateTargets(pm *proxy.ProxyManager, action string, tunnelIP string, proto
}
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
processedTarget := target
processedTarget := combinedAddress
if updownScript != "" {
newTarget, err := executeUpdownScript(action, proto, target)
newTarget, err := executeUpdownScript(action, proto, combinedAddress)
if err != nil {
logger.Warn("Updown script error: %v", err)
} else if newTarget != "" {
@@ -783,8 +798,12 @@ func updateTargets(pm *proxy.ProxyManager, action string, tunnelIP string, proto
}
// 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" {
logger.Info("Removing target with port %d", port)
@@ -849,4 +868,4 @@ func executeUpdownScript(action, proto, target string) (string, error) {
}
return target, nil
}
}