Using ubuntu tusty, there is a service running on a remote machine, that I can access via port forwarding through an ssh tunnel from localhost:9999.
I have a docker container running. I need to access that remote service via the host's tunnel, from within the container.
I tried tunneling from the container to the host with -L 9000:host-ip:9999 , then accessing the service through 127.0.0.1:9000 from within the container fails to connect. To check wether the port mapping was on, I tried
nc -luv -p 9999 # at host
nc -luv -p 9000 # at container
following this, parag. 2 but there was no perceived communication, even when doing
nc -luv host-ip -p 9000
at the container
I also tried mapping the ports via docker run -p 9999:9000 , but this reports that the bind failed because the host port is already in use (from the host tunnel to the remote machine, presumably).
So my questions are
1 - How will I achieve the connection? Do I need to setup an ssh tunnel to the host, or can this be achieved with the docker port mapping alone?
2 - What's a quick way to test that the connection is up? Via bash, preferably.
Thanks.
Using your hosts network as network for your containers via --net=host or in docker-compose via network_mode: host is one option but this has the unwanted side effect that (a) you now expose the container ports in your host system and (b) that you cannot connect to those containers anymore that are not mapped to your host network.
In your case, a quick and cleaner solution would be to make your ssh tunnel "available" to your docker containers (e.g. by binding ssh to the docker0 bridge) instead of exposing your docker containers in your host environment (as suggested in the accepted answer).
Setting up the tunnel:
For this to work, retrieve the ip your docker0 bridge is using via:
ifconfig
you will see something like this:
docker0 Link encap:Ethernet HWaddr 03:41:4a:26:b7:31
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
Now you need to tell ssh to bind to this ip to listen for traffic directed towards port 9000 via
ssh -L 172.17.0.1:9000:host-ip:9999
Without setting the bind_address, :9000 would only be available to your host's loopback interface and not per se to your docker containers.
Side note: You could also bind your tunnel to 0.0.0.0, which will make ssh listen to all interfaces.
Setting up your application:
In your containerized application use the same docker0 ip to connect to the server: 172.17.0.1:9000. Now traffic being routed through your docker0 bridge will also reach your ssh tunnel :)
For example, if you have a "DOT.NET Core" application that needs to connect to a remote db located at :9000, your "ConnectionString" would contain "server=172.17.0.1,9000;.
Forwarding multiple connections:
When dealing with multiple outgoing connections (e.g. a docker container needs to connect to multiple remote DB's via tunnel), several valid techniques exist but an easy and straightforward way is to simply create multiple tunnels listening to traffic arriving at different docker0 bridge ports.
Within your ssh tunnel command (ssh -L [bind_address:]port:host:hostport] [user#]hostname), the port part of the bind_address does not have to match the hostport of the host and, therefore, can be freely chosen by you. So within your docker containers just channel the traffic to different ports of your docker0 bridge and then create several ssh tunnel commands (one for each port you are listening to) that intercept data at these ports and then forward it to the different hosts and hostports of your choice.
on MacOS (tested in v19.03.2),
1) create a tunnel on host
ssh -i key.pem username#jump_server -L 3336:mysql_host:3306 -N
2) from container, you can use host.docker.internal or docker.for.mac.localhost or docker.for.mac.host.internal to reference host.
example,
mysql -h host.docker.internal -P 3336 -u admin -p
note from docker-for-mac official doc
I WANT TO CONNECT FROM A CONTAINER TO A SERVICE ON THE HOST
The host has a changing IP address (or none if you have no network access).
From 18.03 onwards our recommendation is to connect to the special DNS
name host.docker.internal, which resolves to the internal IP address
used by the host. This is for development purpose and will not work in
a production environment outside of Docker Desktop for Mac.
The gateway is also reachable as gateway.docker.internal.
I think you can do it by adding --net=host to your docker run. But see also this question: Forward host port to docker container
I'd like to share my solution to this. My case was as follows: I had a PostgreSQL SSH tunnel on my host and I needed one of my containers from the stack to connect to a database through it.
I spent hours trying to find a solution (Ubuntu + Docker 19.03) and I failed. Instead of doing voodoo magic with iptables, doing modifications to the settings of the Docker engine itself I came up with a solution and was shocked I didn't thought of this earlier. The most important thing was I didn't want to use the host mode: security first.
Instead of trying to allow a container to talk to the host, I simply added another service to the stack, which would create the tunnel, so other containers could talk to easily without any hacks.
After configuring a host inside my ~/.ssh/config:
Host project-postgres-tunnel
HostName remote.server.host
User sshuser
Port 2200
ForwardAgent yes
TCPKeepAlive yes
ConnectTimeout 5
ServerAliveCountMax 10
ServerAliveInterval 15
And adding a service to the stack:
postgres:
image: cagataygurturk/docker-ssh-tunnel:0.0.1
volumes:
- $HOME/.ssh:/root/ssh:ro
environment:
TUNNEL_HOST: project-postgres-tunnel
REMOTE_HOST: localhost
LOCAL_PORT: 5432
REMOTE_PORT: 5432
# uncomment if you wish to access the tunnel on the host
#ports:
# - 5432:5432
The PHP container started talking through the tunnel without any problems:
postgresql://user:password#postgres/db?serverVersion=11&charset=utf8
Just remember to put your public key inside that host if you haven't already:
ssh-copy-id project-postgres-tunnel
I'm pretty sure this will work regardless of the OS used (MacOS / Linux).
I agree with #hlobit that #B12Toaster answer should be the accepted answer.
In case anyone hits this problem but with a slightly different setup with the SSH tunnel, here are my findings. In my case, instead of creating a tunnel from Docker host machine to remote machine using ssh -L, I was creating remote forward SSH tunnel from remote machine to Docker host machine using ssh -L.
In this setup, by default sshd does NOT allow gateway ports, i.e. in file /etc/ssh/sshd_config on Docker host, the GatewayPorts no should be uncommented and set to GatewayPorts yes or GatewayPorts clientspecified. I configured GatewayPorts clientspecified and configured the remote forward SSH tunnel by ssh -L 172.17.0.1:dockerHostPort:localhost:sshClientPort user#dockerHost. Remember to restart sshd after changing /etc/ssh/sshd_config (sudo systemctl restart sshd).
Your Docker container should be able to connect to Docker host on 172.17.0.1:dockerHostPort and this in turn gets tunnelled back to SSH client's localhost:sshClientPort.
References:
https://www.ssh.com/ssh/tunneling/example
https://docs.docker.com/network/network-tutorial-host/
https://docs.docker.com/network/host/
My 2 cents for Ubuntu 18.04 - a very simple answer, no need for extra tunnels, extra containers, extra docker options or exposing host.
Simply, when creating a reverse tunnel make sure ssh binds to all interfaces as, by default, it binds ports of the reverse tunnel to localhost only. For example, in putty make sure that option Connection->SSH->Tunnels Remote ports do the same (SSH-2 only) is ticked.
This is more or less equivalent to specifying the binding address 0.0.0.0 for the remote part of the tunnel (more details here):
-R [bind_address:]port:host:hostport
However, this did not work for me unless I allowed the GatewayPorts option in my sshd server configuration. Many thanks to Stefan Seidel for his great answer.
In short: (1) you bind the reverse tunnel to 0.0.0.0, (2) you let the sshd server to accept such tunnels.
Once this is done I can access my remote server from my docker containers via the docker gateway 172.17.0.1 and port bound to the host.
On my side, running Docker in Windows Subsystem for Linux (WSL v1), I couldn't use docker0 connection approach. host.docker.internal also doesn't resolve (latest docker version).
However, I found out I could directly use the host-ip insider my docker container.
Get your Host IP (Windows cmd: ipconfig), e.g. 192.168.0.5
Bash into your Container and test if you can ping your host ip:
- docker exec -it d6b4be5b20f7 /bin/bash
- apt-get update && apt-get install iputils-ping
- ping 192.168.0.5
PING 192.168.0.5 (192.168.0.5) 56(84) bytes of data.
64 bytes from 192.168.0.5 : icmp_seq=1 ttl=37 time=2.17 ms
64 bytes from 192.168.0.5 : icmp_seq=2 ttl=37 time=1.44 ms
64 bytes from 192.168.0.5 : icmp_seq=3 ttl=37 time=1.68 ms
Apparently, in Windows, you can directly connect from within containers to the host using the official host ip.
In case anyone needs it (like I did), solution for Windows and WSL is same as #prayagupd mentioned for Mac OS
Create an SSH tunnel to your remote service with whatever tool you prefer to whatever port you prefer, for example 3300.
Then, from Docker container you can connect to, for example, MySQL DB on tunnel port 3300 using following command:
mysql -u user -p -h host.docker.internal -P 3300
An easy example to reproduce the situation and ssh to host
Run a container. Use --network="host
docker container run --network="host" --interactive --tty --rm ubuntu bash
Now you can access your host using localhost
Now your host machine is a Linux machine that has a public-private key file to ssh into it. So copy the contents of your private key file and reproduce the key file inside your host. (However, this is just a demonstration. This is not a good way to copy key files)
Now ssh into your host. Use localhost to access it.
ssh -i key_file.pem ec2-user#localhost
Related
I am having trouble setting up an ssh tunnel on my mac machine. I have no problems setting up the tunnel on my ubuntu box. This is the command I run
ssh -nNT -L 172.18.0.1:4000:production-database-url:3306 jump-point
When I run this on my mac, I get the following error:
bind [172.18.0.1]:4000: Can't assign requested address
channel_setup_fwd_listener_tcpip: cannot listen to port: 4000 Could
not request local forwarding.
If I run without the bind_address (172.18.0.1), I am able to connect to the database via the tunnel.
If I bind to all interfaces (0.0.0.0), then tunnel is open, however, the connection to the database from inside the docker container does not work.
172.18.0.1 is the IP of docker's default bridge network gateway, not your host's IP.
You can run this command to check that.
$ docker network inspect bridge
Docker for Mac has limitations
There is no docker0 bridge on macOS (it's in the docker VM host on Mac and on Windows)
You cannot ping containers (without shaving a bunch of yaks)
Per-container IP addressing is not possible
Also note that this means the docker run option --net-host is not supported on Mac, but maybe that's a good thing
There is a workaround
These magic addresses resolve to the host's IP from within a container
docker.for.mac.localhost (deprecated)
docker.for.mac.host.internal (deprecated)
host.docker.internal
This resolves to the gateway of the host mac
gateway.docker.internal
Use the name host.docker.internal from within the container just like you would use localhost on the mac directly.
Don't worry about the bind address for the tunnel:
ssh -nNT -L 4000:production-database-url:3306 jump-point
You didn't mention which database but I take it from the port 3306 that it is MySQL.
To connect using the mysql cli from within a container, via an ssh tunnel on your host, to a remote mysql database server you can run:
mysql --host host.docker.internal [... other options go here]
I want to start an Elasticsearch container in Docker. By default I see nearly everywhere something like:
docker run -d -p 9200:9200 docker.elastic.co/elasticsearch/elasticsearch:5.5.1
Now is my question: Why are we mapping the port on our host network? I understand port mapping but I don't see the big advantage of it.
In my opinion I would always do something like this:
$ docker network create logging
20aa4c7bf2d8289d8cbd485c3e384f9371eed87204625998687c61e4bad27f14
$ docker run -d --name es --net logging docker.elastic.co/elasticsearch/elasticsearch:5.5.1
And connect to the ES by using it's name (es in this case) and deploying containers in the same network. I would think my ES is more secure in its private docker network.
I see there is an advantage for port mapping when your containers which need to connect to elasticsearch aren't in the same network. But are there other advantages or why is this always shown with port mapping?
So host access is more about accessibility. If you are running docker on local machine and you want to access the app only on that machine, then host mapping is not need.
Now if you need to access this app on a external computer other than your docker host then you need to do that port mapping.
docker run -d -p 9200:9200 docker.elastic.co/elasticsearch/elasticsearch:5.5.1
This maps the host port 9200 (left side) to 9200 inside the docker (right side). The listening interface is 0.0.0.0 which means all interfaces. And hence it is accessible to to anyone how has access to this machine.
If you want to make it more secure then you do it like below
docker run -d -p 127.0.0.1:9200:9200 docker.elastic.co/elasticsearch/elasticsearch:5.5.1
This would listen on local host only. So only you can access it on the machine. But if you need to access it from some place else then you would use a SSH tunnel
ssh -L 9200:127.0.0.1:9200 <user>#<HOSTIP>
And on that machine you can access it on 127.0.0.1:9200
Next level of security is added when you use a firewall like ufw, firewalld etc.
What you did with network command
docker network create logging
Basically creates new network and isolates other docker containers from accessing it on the host. But as long as external accessibility is concerned, you still need to map it to the host port
Hope this answers your question
I'm running a CentOS-based container on Docker for Windows and trying to connect to an http service running on port 8545 of my host environment.
I've tried this, attempting a variety of suspected host names and IP addresses:
curl http://localhost:8545
But the error message I get is "curl: (7) Failed connect to localhost:8545; Connection refused"
How should I figure out what IP Address to use? Is there anything I need to configure as far as allowing the port number to be accessed from inside the container?
Localhost is not working yet I think with Docker for Windows.
There is few things you can try. First you can add EXPOSE 'portnumber' in the dockerfile so the container will listen on this port. You can also use docker run with -p 8545:8545, it will map the port of the container and the host.
To get the Ip address of the container you can use:
docker inspect -f "{{ .NetworkSettings.Networks.nat.IPAddress }}" containername
You can access the host using its ip but localhost/127.0.0.1 won't work (they will resolve to the Linux VM that is part of docker for windows). If you use the default network settings, your host should be reachable on 10.0.75.1 from your container
I have a docker container running in a default vm in mac, I've already mapped the ports of my particular application to the ports of the vm. Once I've done that I can access my application by doing:
curl $(docker-machine ip default):9003
After that I've mapped my vm port to my localhost by doing port forwarding this way:
VBoxManage controlvm default natpf1 "9003,tcp,127.0.0.1,9003,,9003"
Once I've done that I get a response of my application from my localhost
curl localhost:9003
My question is: How can I access this application from the outside world or even from my own network? I've tried opening a port in my mac with:
pass in inet proto tcp from any to any port 9003
but still the port shows as closed with nmap. Does anyone knows what I'm missing?
You can either bind your socket to localhost if the docker-run option --net=host is used, hence the local stack, as well as NICs, are shared between the host and the container(s).
OTOH, if the docker-run option -p is used then if the socket is bound to a localhost IP address then the service cannot be accessed from outside the container.
Wait for the answer of #marc-b though, he gave it the first.
Docker (www.docker.io) looks terrific. However, after installing VirtualBox, Vagrant
... and finally Docker on a Mac, I'm finding it's not possible to access the service running in the Docker container from another computer (or from a terminal session on the Mac). The service I'm trying to access is Redis.
The problem appears to be that there's no route to the IP address assigned to the Docker container. In this case the container's IP is 172.16.42.2 while the Mac's IP is 196.168.0.3.
A couple notes:
It IS possible to access it - but only from within the VirtualBox session. This can be done using redis-cli -h 172.16.42.2 -p 6379.
I have added "config.vm.network :bridged" to the VagrantFile in an attempt to get the, but that didn't solve the problem.
The VM generated by vagrant is indeed isolated, in order to access it from your host, you can allocate a private network to it.
Instead of doing config.vm.network :bridged, try config.vm.network :private_network, ip: "192.168.50.4", It should do the trick
However, this will only allow you to access the VM itself, not the containers.
In order to do so, when running the container, you can add the -p option
ex: docker run -d -p 8989 base nc -lkp 8989
This will run a netcat listening on 8989 within a container and expose the port publicly. As it is also run with -d, the container will be in detached mode and the only output will be the container's ID
In order to expose the port, Docker do a simple NAT. In order to know the real port, you can
do docker port <ID of the container> 8989
Netcat will be available from the mac at 192.168.50.4:<result>
I just wrote a tutorial of how to use a host-only network and TCP routing to make this pretty easy. This way you don't have to map every specific port.
http://ispyker.blogspot.com/2014/04/accessing-docker-container-private.html
Important points ...
1) Add host-only network to Virtual Box
2) Tell the boot2docker VM to have an adapter on the host-only network
3) Add an IP for the new boot2docker VM host-only networking adapter
4) Route all Mac OS X traffic for the docker container subnet to that boot2docker VM host-only networking IP
Actual steps are on the blog with output so you can compare to what you see as you follow them.
I have installed tomcat from my Dockerfile and forwarded that to 6060 using vagrant`s port forwarding. These are the steps worked for me:
vagrant provision
vagrant up
vagrant ssh
box_name$ docker run -i -t -p 8080:8080 bsb_tomcat6 /bin/bash
Able to see tomcat up & running on localhost:6060, as I have done port forwarding to 6060 in my Vagrantfile
you also can define PRIVATE_NETWORK and FORWARD_DOCKER_PORTS environment variables to access your services that are running in docker containers:
$ vagrant halt
$ export PRIVATE_NETWORK=192.168.50.4
$ export FORWARD_DOCKER_PORTS=1
$ vagrant up
In my case i can access postgres from Mac using
$ telnet 192.168.50.4 49154
to find out actual application port you can use
$ sudo docker port 1854499c6547 5432
0.0.0.0:49154