79130833

Date: 2024-10-27 15:01:54
Score: 1
Natty:
Report link

This problem actually has not much to do with Docker. Python's psutil reads the /proc pseudo-filesystem for each process - but for these permissions are set so only the user that's running the process can get all information. In other words: you can only see information (including sockets and therefore ports) of processes of the same user the script is running as.

There are AFAIK two ways to directly find out information about open ports, and all other tools like netstat and ss are just using these:

  1. Open an NETLINK_SOCK_DIAG socket to directly ask the kernel API for information. This is what the ss Tool is doing, performant but as there seems to be no Python library it's hard to work with.
  2. Parse e.g. the /proc/net/tcp to get the open TCP sockets, filter for rem_address 00000000:0000 to only get listening, the port is in local_address (hexadecimal). There are also Python scripts and libs doing that.

However, both ways will only get you the inodes of the file descriptors, and you can only find the process owning those by going through /proc/<process>, which runs into the same issue you already have. So unless something from there like the UID helps you, these will not help you - and Tools like netstat and ss have the same problem.

The quick-and-dirty fix is to run as root, but there are good reasons to not do that.

So it's propably better to turn this all around - the docker container is not randomly choosing a port to run, so instead of searching for that, try to have it known wherever the healthcheck is running.

Your question implies, that the check would be inside the container, but then I wonder why you are changing ports at all: multiple processes in different containers can all open the same port, and then you just map it to different ports of the host "outside".

I suppose your health check is running outside of the container - but than that's the same outside, as where the container port is mapped to the host port, so it would be better to check and parse that mapping. Or have that set in the health check the same way as it's set there.

If that's really not an option for some reason, you can use docker inspect, to check all the port mappings of your docker containers (NetworkSettings -> Ports). Python Docker SDK exposes these through attrs of Container objects (doc), so you should be able to query and parse that:

import docker
client = docker.from_env()

ports = list()
for container in client.containers.list():
    ...  # Add check whether this actually is a container you are interested in!
    for insideport, outsideports in container.attrs["NetworkSettings"]["Ports"].items():
        if outsideports is None:
            continue  # Port is not mapped to the outside
        for bind in outsideports:
            # Maybe you want to check bind["HistIp"] as well for IPv4/IPv6 or something
            ports.append(bind["HostPort"])

You could e.g. add labels to containers to check which containers you are interested in or something, otherwise this code will give you all open ports of all docker containers. (Add exception handling, note that it has to connect to the docker socket!).

Reasons:
  • Whitelisted phrase (-1.5): you can use
  • Long answer (-1):
  • Has code block (-0.5):
  • Me too answer (2.5): have the same problem
  • Filler text (0.5): 00000000
  • Low reputation (1):
Posted by: Tim