2

I have a simple Python script, that creates a Docker container of choice, and then creates a TCP tunnel that forwards requests to it. I use sockets within Python to forward the data sent in between the client and Docker container.

My piece of code that is not working:

# open socket to container
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.connect((socket.gethostbyname('container IP - e.g. 172.17.0.2'), 2222))

I tried this part of the code also like this: socket.connect(('172.17.0.2', 2222)) and like it is shown in the code example. Only difference is that in my real code, I have the IP pulled from the container like so:

import docker
_DOCKER_CLIENT = docker.from_env()
c = _DOCKER_CLIENT.containers.run(
    image='linuxserver/openssh-server:latest',
    auto_remove=True,
    detach=True
)

# wait for container to start
cc = _DOCKER_CLIENT.containers.get(c.id)

# get container IP address
ip = cc.attrs['NetworkSettings']['IPAddress']

Error I get when I try to connect to the Docker container from the socket in Python:

ConnectionRefusedError: [Errno 111] Connection refused

Then, I tried to do the same thing from the Python cli in a standard shell, so I launched this in bash:

~$ python3
>>> import socket
>>> socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> socket.connect(('172.17.0.2', 2222))

----- no issues up until this point -----

>>> buffer = socket.recv(0x400)
>>> print(buffer)
SSH-2.0-OpenSSH_8.3

>>> quit()

As you can see, I can do this without issues in bash, but, when I launch the Python script as the same user, I get the Connection refused error.

The routing table is also correct:

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         _gateway        0.0.0.0         UG    600    0        0 wlp0s20f3
link-local      0.0.0.0         255.255.0.0     U     1000   0        0 virbr1
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
172.18.0.0      0.0.0.0         255.255.0.0     U     0      0        0 br-7ee23b92049c
192.168.0.0     0.0.0.0         255.255.255.0   U     600    0        0 wlp0s20f3
192.168.33.0    0.0.0.0         255.255.255.0   U     0      0        0 virbr2
192.168.100.0   0.0.0.0         255.255.255.0   U     0      0        0 virbr1
192.168.122.0   0.0.0.0         255.255.255.0   U     0      0        0 virbr0

I can also ping the container without issues.

Anyone, can you please spot the issue? Is Python working differently in cli mode than in standard interpreting mode in a script?

dodancs
  • 357
  • 1
  • 5
  • 15
  • The container-private IP address is unreachable in many common scenarios and I wouldn't try to connect to it. It'd be more reliable to ask Docker to publish ports out to the host for you, and then you can connect to the published port and the host's IP address (`localhost` if you know you're not in a container but the containers are on the same host). – David Maze Nov 08 '20 at 21:13
  • Thank you, @DavidMaze , but I need to run many of these containers. And I do not want to publish them. There is no need for it, since my Python script will be "publishing" it to the clients. Essentially, I need to run as many docker containers as there are connections to one IP on a specific port. That is why I need this solution. And nevermind that, it should be working just fine, since I can access the Docker network, and ping the container, and the routes are fine. The firewall should not be a problem as well. – dodancs Nov 08 '20 at 21:20
  • Maybe the only issue is the waiting for a container to start part... That I try to connect to it too soon. Too bad Python Docker library does not have any wait-for-start functions. – dodancs Nov 09 '20 at 06:57

1 Answers1

-1

So. I have mitigated the issue. The problem lied in me not waiting for the service inside of the container to fully boot and bind to a port.

I needed to add a waiting loop that checks whether the container's service is ready and then connect the socket.

I created a class that is used as a waiter, that gradually increases the timeout. After each sleep cycle, I check again, if I can connect to the socket.

Now it all works flawlessly.

dodancs
  • 357
  • 1
  • 5
  • 15