mirror of
https://github.com/fosrl/newt.git
synced 2026-02-21 20:36:38 +00:00
Simplified based on PR feedback and support checking use of "bridge" network
This commit is contained in:
132
docker/client.go
132
docker/client.go
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user