Simplified based on PR feedback and support checking use of "bridge" network

This commit is contained in:
Jonny Booker
2025-06-14 01:26:11 +01:00
parent 5cb86f3e47
commit 6d9160ab5e
3 changed files with 89 additions and 88 deletions

View File

@@ -10,6 +10,7 @@ import (
"time"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
"github.com/fosrl/newt/logger"
)
@@ -70,18 +71,22 @@ func CheckSocket(socketPath string) bool {
}
// IsWithinHostNetwork checks if a provided target is within the host container network
func IsWithinHostNetwork(socketPath string, containerNameAsHostname bool, targetAddress string, targetPort int) (bool, error) {
func IsWithinHostNetwork(socketPath string, targetAddress string, targetPort int) (bool, error) {
// Always enforce network validation
containers, err := ListContainers(socketPath, true, containerNameAsHostname)
containers, err := ListContainers(socketPath, true)
if err != nil {
return false, fmt.Errorf("failed to list Docker containers: %s", err)
return false, err
}
// If we can find the passed hostname/ip in the networks or as the container name, it is valid and can add it
// Determine if given an IP address
var parsedTargetAddressIp = net.ParseIP(targetAddress)
// If we can find the passed hostname/IP address 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 the target address is not an IP address, use the container name
if parsedTargetAddressIp == nil {
if c.Name == targetAddress {
for _, port := range c.Ports {
if port.PublicPort == targetPort || port.PrivatePort == targetPort {
@@ -90,7 +95,7 @@ func IsWithinHostNetwork(socketPath string, containerNameAsHostname bool, target
}
}
} else {
//If the ip address matches, check the ports being mapped too
//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 {
@@ -107,12 +112,19 @@ func IsWithinHostNetwork(socketPath string, containerNameAsHostname bool, target
}
// ListContainers lists all Docker containers with their network information
func ListContainers(socketPath string, enforceNetworkValidation bool, containerNameAsHostname bool) ([]Container, error) {
func ListContainers(socketPath string, enforceNetworkValidation bool) ([]Container, error) {
// Use the provided socket path or default to standard location
if socketPath == "" {
socketPath = "/var/run/docker.sock"
}
// Used to filter down containers returned to Pangolin
containerFilters := filters.NewArgs()
// Used to determine if we will send IP addresses or hostnames to Pangolin
useContainerIpAddresses := true
hostContainerId := ""
// Create a new Docker client
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
@@ -125,16 +137,34 @@ func ListContainers(socketPath string, enforceNetworkValidation bool, containerN
if err != nil {
return nil, fmt.Errorf("failed to create Docker client: %v", err)
}
defer cli.Close()
// Get the host container
hostContainer, err := getHostContainer(ctx, cli)
if err != nil {
return nil, fmt.Errorf("failed to get host container: %v", err)
if enforceNetworkValidation && err != nil {
return nil, fmt.Errorf("network validation enforced, cannot validate due to: %v", err)
}
// We may not be able to get back host container in scenarios like running the container in network mode 'host'
if hostContainer != nil {
// We can use the host container to filter out the list of returned containers
hostContainerId = hostContainer.ID
for hostContainerNetworkName := range hostContainer.NetworkSettings.Networks {
// If we're enforcing network validation, we'll filter on the host containers networks
if enforceNetworkValidation {
containerFilters.Add("network", hostContainerNetworkName)
}
// If the container is on the docker bridge network, we will use IP addresses over hostnames
if useContainerIpAddresses && hostContainerNetworkName != "bridge" {
useContainerIpAddresses = false
}
}
}
// List containers
containers, err := cli.ContainerList(ctx, container.ListOptions{All: true})
containers, err := cli.ContainerList(ctx, container.ListOptions{All: true, Filters: containerFilters})
if err != nil {
return nil, fmt.Errorf("failed to list containers: %v", err)
}
@@ -144,6 +174,11 @@ func ListContainers(socketPath string, enforceNetworkValidation bool, containerN
// Short ID like docker ps
shortId := c.ID[:12]
// Skip host container if set
if hostContainerId != "" && c.ID == hostContainerId {
continue
}
// Get container name (remove leading slash)
name := ""
if len(c.Names) > 0 {
@@ -169,51 +204,28 @@ func ListContainers(socketPath string, enforceNetworkValidation bool, containerN
// Get network information by inspecting the container
networks := make(map[string]Network)
// 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 (%s) for network info: %v", shortId, name, err)
// Continue without network info if inspection fails
} else {
// Only containers within the host container network will be returned
isInHostContainerNetwork := 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 host container network
for _, hostContainerNetwork := range hostContainer.NetworkSettings.Networks {
if !isInHostContainerNetwork {
isInHostContainerNetwork = endpoint.NetworkID == hostContainerNetwork.NetworkID
}
}
dockerNetwork := Network{
NetworkID: endpoint.NetworkID,
EndpointID: endpoint.EndpointID,
Gateway: endpoint.Gateway,
IPPrefixLen: endpoint.IPPrefixLen,
IPv6Gateway: endpoint.IPv6Gateway,
GlobalIPv6Address: endpoint.GlobalIPv6Address,
GlobalIPv6PrefixLen: endpoint.GlobalIPv6PrefixLen,
MacAddress: endpoint.MacAddress,
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
// Extract network information from inspection
if c.NetworkSettings != nil && c.NetworkSettings.Networks != nil {
for networkName, endpoint := range c.NetworkSettings.Networks {
dockerNetwork := Network{
NetworkID: endpoint.NetworkID,
EndpointID: endpoint.EndpointID,
Gateway: endpoint.Gateway,
IPPrefixLen: endpoint.IPPrefixLen,
IPv6Gateway: endpoint.IPv6Gateway,
GlobalIPv6Address: endpoint.GlobalIPv6Address,
GlobalIPv6PrefixLen: endpoint.GlobalIPv6PrefixLen,
MacAddress: endpoint.MacAddress,
Aliases: endpoint.Aliases,
DNSNames: endpoint.DNSNames,
}
}
// Don't continue returning this container if not in the host container network(s)
if enforceNetworkValidation && !isInHostContainerNetwork {
logger.Debug("Container not found within the host container network, skipping: %s (%s)", shortId, name)
continue
// Use IPs over hostnames/containers as we're on the bridge network
if useContainerIpAddresses {
dockerNetwork.IPAddress = endpoint.IPAddress
}
networks[networkName] = dockerNetwork
}
}
@@ -228,23 +240,25 @@ func ListContainers(socketPath string, enforceNetworkValidation bool, containerN
Created: c.Created,
Networks: networks,
}
dockerContainers = append(dockerContainers, dockerContainer)
}
return dockerContainers, nil
}
// getHostContainer gets the current container for the current host if possible
func getHostContainer(dockerContext context.Context, dockerClient *client.Client) (*container.InspectResponse, error) {
// Get hostname from the os
containerHostname, err := os.Hostname()
hostContainerName, err := os.Hostname()
if err != nil {
return nil, fmt.Errorf("failed to find hostname: %v", err)
return nil, fmt.Errorf("failed to find hostname for container")
}
// Get host container from the docker socket
hostContainer, err := dockerClient.ContainerInspect(dockerContext, containerHostname)
hostContainer, err := dockerClient.ContainerInspect(dockerContext, hostContainerName)
if err != nil {
return nil, fmt.Errorf("failed to inspect host container: %v", err)
return nil, fmt.Errorf("failed to find host container")
}
return &hostContainer, nil