Forward TCP requests from host to container on specific port - docker

I am running the container and mapping the port like so:
docker run -d --expose 4242 -p 4242:4242 42wim/matterbridge:stable --debug
I've created a firewall rule to allows TCP connections over port 4242 to my VM. When I send an http request to the public IP of my VM the connection is refused:
http://{public-ip}:4242/api/messages
Howevever if I open a shell into the container and do a curl to the path I get the expected response curl localhost:4242/api/messages
What is the correct way to map TCP requests on port 4242 from my Host to my Container? I'm running a Ubuntu VM on GCP that hosts my docker container
Update, if use docker run --network="host" I can curl from the host to the docker container with curl localhost:4242/api/messages with the expected response. Yet when I do the same curl request with the public IP the connection is still refused.
if I ss -na | grep :4242
tcp LISTEN 0 4096 127.0.0.1:4242 0.0.0.0:*
it shows it's listening. Is there additional mapping I need to do? I have validated from google firewall logs that it is allowing and forwarding TCP connections from port 4242 to the VM

Related

When to perform host-ip based port mapping like "-p host-ip:port:port"

Docker provides a way to map ports between the container and host.
As per the official documentation its also possible to mention host-ip while port mapping.
-p 192.168.1.100:8080:80 - Map TCP port 80 in the container to port 8080 on the Docker host for connections to host IP 192.168.1.100.
I tried this option to figure out what's the difference with/without the host-ip.
Using just -p 80:80
$ docker run -itd -p 80:80 nginx:alpine
$ curl localhost:80
$ curl 127.0.0.1:80
$ curl 0.0.0.0:80
$ curl 192.168.0.13:80
$ ps -ef | grep docker-proxy
16723 root 0:00 /usr/local/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 8080 -container-ip 172.17.0.1 -container-port 80
$
All the curl commands return the output.
Using host-ip like -p 192.168.0.13:80:80
$ docker run -itd -p 192.168.0.13:80:80 nginx:alpine
$ curl localhost:80
curl: (7) Failed to connect to localhost port 80: Connection refused
$ curl 127.0.0.1:80
curl: (7) Failed to connect to 127.0.0.1 port 80: Connection refused
$ curl 0.0.0.0:80
curl: (7) Failed to connect to 0.0.0.0 port 80: Connection refused
$ curl 192.168.0.13:80 # return output
$ ps -ef | grep docker-proxy
4914 root 0:00 /usr/local/bin/docker-proxy -proto tcp -host-ip 192.168.0.13 -host-port 80 -container-ip 172.17.0.2 -container-port 80
$
All the curl commands failed except 192.168.0.13:80.
Is there any there any other difference apart for the one I mentioned here.
Wondering when to use host-ip based port mapping. Any use cases?
A docker host may have multiple NICs. In the data center, this may be too segregate traffic, e.g. management, storage, and application/public. On your laptop, this may be for wireless and wired interfaces. There are also virtual NICs for things like loopback (127.0.0.1) and VPN tunnels.
When you do not specify an IP in the port publish command, by default docker will bind to all interfaces on the host. In IPv4, this is commonly notated as 0.0.0.0 which means listen on any interface (and this is why I don't connect to this address because there's no such thing as connecting to any IP). With the IP address specified, you manually specify which interface to use. Why would you want to specify this? Several reasons I can think of:
Listening on only 127.0.0.1 to prevent external access
Listening on 0.0.0.0 to explicitly bind to all IPv4 interfaces (it is possible to change docker's default behavior, so this could be necessary for some).
Listening on one physical NIC, allowing other NICs to be bound by other services on the same port.
Listening on only IPv4 interfaces if the app does not work for IPv6.
While there are lots of possible reasons, other than listening on loopback for security, these use cases are very rare and most users leave docker to listen on all interfaces.

Docker on Synology - Binding to all interfaces

I have a MariaDB docker container running on Synology DS918+ and redirects traffic from container port 3306 to external port 3333
When I see how it binds to the port, it seems different than a working example I have for another service that doesn't run on docker
Working :
ash-4.3# netstat -nao | grep 5000
tcp 0 0 0.0.0.0:5000 0.0.0.0:* LISTEN off (0.00/0/0)
tcp6 0 0 :::5000 :::* LISTEN
Not working:
ash-4.3# netstat -nao | grep 3333
tcp6 0 0 :::3333 :::* LISTEN off (0.00/0/0)
When I try to access port 3333 from my laptop to the remote machine running docker I'm able to do so, the issue is when trying to access the machine's private IP from within the machine itself, this one fails
Any help is appreciated here
To clarify, although your docker is only binding to the ipv6 interface(“:::”) not the ipv4(“0.0.0.0”), Docker forbids a loopback connection to its docker-proxy from the host. I believe this also fails in all networking modes.
If you’re connecting from container to another container, use the container name via the docker-dns and private LAN. For example, if your MariaDB container is named “maria”, I believe docker’s DNS on 127.0.0.11 offers a lookup for the name “maria” to a 172...* ipv4 to which other containers may connect if in the same 172.{subnet}../16 as your MariaDB host. Connect to “maria” in another container and the tcp magically gets to the right place.
If you’re trying to connect from the docker host to a container, this is a problem that I have resigned to proxying off my router in a hairpin NAT to the same upnp ports that I’ve exported via External Access on Synology, which feels like a poor solution but works today.

how to forward docker port 2375 from virtualbox to host os windows 10

I created a debian vm to have my docker host running on.
netstat
tcp 0 0 127.0.0.1:2375 0.0.0.0:* LISTEN 1260/dockerd
After that I setup port forwarding for port 2375 as described in many online tutorials.
Next I curl in the cmd of my windows 10 host os.
C:\Users\me>curl localhost:2375
curl: (56) Recv failure: Connection was reset
Notice that connecting to the VMs SSH port is working.
C:\Users\me>curl localhost:666
SSH-2.0-OpenSSH_7.4p1 Debian-10+deb9u1
Protocol mismatch.
Can anybody tell me what am I missing? Do I have to kinda allow port 2375 to be called from outside where the SSH port is allowed by default?
The issue is with your docker listening IP.
tcp 0 0 127.0.0.1:2375 0.0.0.0:* LISTEN 1260/dockerd
127.0.0.1 means it is only listening for connections generated from inside the VM.
You should change your docker daemon to use 0.0.0.0:2375. Then your port forwarding would work

Cannot connect to Go GRPC server running in local Docker container

I have a go grpc service. I'm developing on a mac, sierra. When running a grpc client against the service locally, all is well, but when running same client against same service in the docker container I get this error:
transport: http2Client.notifyError got notified that the client transport was broken EOF.
FATA[0000] rpc error: code = Internal desc = transport is closing
this is my docker file:
FROM golang:1.7.5
RUN mkdir -p /go/src/github.com/foo/bar
WORKDIR /go/src/github.com/foo/bar
COPY . /go/src/github.com/foo/bar
# ONBUILD RUN go-wrapper download
RUN go install
ENTRYPOINT /go/bin/bar
EXPOSE 51672
my command to build the image:
docker build -t bar .
my command to launch the docker container:
docker run -p 51672:51672 --name bar-container bar
Other info:
client program runs fine from within the docker container
connecting to a regular rest endpoint works fine (http2, grpc related?)
running the lsof command in OS X yields these results
$lsof -i | grep 51672
com.docke 984 oldDave 21u IPv4 0x72779547e3a32c89 0t0 TCP *:51672 (LISTEN)
com.docke 984 oldDave 22u IPv6 0x72779547cc0fd161 0t0 TCP localhost:51672 (LISTEN)
here's a snippet of my server code:
server := &Server{}
endpoint := "localhost:51672"
lis, err := net.Listen("tcp", endpoint)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer(grpc.Creds(creds))
pb.RegisterExpServiceServer(s, server)
// Register reflection service on gRPC server.
reflection.Register(s)
log.Info("Starting Exp server: ", endpoint)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
When you specify a hostname or IP address​ to listen on (in this case localhost which resolves to 127.0.0.1), then your server will only listen on that IP address.
Listening on localhost isn't a problem when you are outside of a Docker container. If your server only listens on 127.0.0.1:51672, then your client can easily connect to it since the connection is also made from 127.0.0.1.
When you run your server inside a Docker container, it'll only listen on 127.0.0.1:51672 as before. The 127.0.0.1 is a local loopback address and it not accessible outside the container.
When you fire up the docker container with "-p 51672:51672", it'll forward traffic heading to 127.0.0.1:51672 to the container's IP address, which in my case is 172.17.0.2.
The container gets an IP addresses within the docker0 network interface (which you can see with the "ip addr ls" command)
So, when your traffic gets forwarded to the container on 172.17.0.2:51672, there's nothing listening there and the connection attempt fails.
The fix:
The problem is with the listen endpoint:
endpoint := "localhost:51672"
To fix your problem, change it to
endpoint := ":51672"
That'll make your server listen on all it container's IP addresses.
Additional info:
When you expose ports in a Docker container, Docker will create iptables rules to do the actual forwarding. See this. You can view these rules
with:
iptables -n -L
iptables -t nat -n -L
If you use docker container to run grpc service,you should make sure grpc service and grpc client on ths same bridge
if you are using docker container, u can define network by IP and give this IP to your IP address(like: 178.20.0.5).

Docker inside Linux VM cannot connect to web application

My setup is the following:
Host: Win10
Guest: Ubuntu 15.10 (clean install, only docker and nodejs are added)
Base image: https://hub.docker.com/r/microsoft/aspnet/ 1.0.0-beta8-coreclr
Inside the guest I have installed Docker and created image (added sample webapp using yeoman to the image above). When I run the image inside container I can ping the container IP sucessfuly using the container IP from the linux (e.g. 172.17.0.2).
$sudo docker run -d -p 80:5000 --name web myapp
$sudo docker inspect --format '{{ .NetworkSettings.IPAddress }}' "web"
172.17.0.2
$ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.060 ms
1 packets transmitted, 1 received, 0% packet loss, time 999ms
$curl 172.17.0.2:80
curl: (7) Failed to connect to 172.17.0.2 port 80: Connection refused
I can also connect to the container and execute commands like ping, however from the linux machine (guest in VirtualBox, host for docker) I cannot access the web app that is hosted inside the container as seen above. I tried several approaches like mapping to the host IP addresses etc, but none of them worked. Did anyone have ideas where to start from ? Is the issue comes from that the docker is installed inside VirtualBox machine?
Thank you in advance.
Edit: Here are the logs from the container:
Could not open /etc/lsb_release. OS version will default to the empty string.
Hosting environment: Production
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.
Your command tells Docker to essentially proxy requests from port 80 of the Linux guest to port 5000 of the container. So the curl command you tried doesn't work because you're trying on port 80 on the container, while the container itself has a service listening on port 5000.
To connect to the container directly, you would use (on the Linux guest):
curl 172.17.0.2:5000
To access via the published port on the Linux guest (from your host):
curl (Linux guest IP)
Or (from the Linux guest):
curl localhost
Edit: This will also prove to be problematic:
Now listening on: http://localhost:5000
You'll want your app inside the container to bind to all interfaces (0.0.0.0) so it listens on the container's assigned IP. With localhost it won't be accessible.
You might find this example useful:
https://github.com/aspnet/Home/blob/dev/samples/1.0.0-beta8/HelloWeb/project.json
This line specifies that the app bind to all interfaces (using "*") on port 5004:
21 "kestrel": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls http://*:5004"
You'll need similar configuration.

Resources