从 docker 容器访问主机的 ssh 隧道
access host's ssh tunnel from docker container
使用 ubuntu tusty,在远程机器上有一个服务 运行,我可以通过端口转发从 localhost:9999
.
通过 ssh 隧道访问它
我有一个 docker 容器 运行。我需要从容器内通过主机的隧道访问该远程服务。
我尝试使用 -L 9000:host-ip:9999
从容器到主机的隧道,然后从容器内通过 127.0.0.1:9000
访问服务无法连接。为了检查端口映射是否打开,我试过了
nc -luv -p 9999 # at host
nc -luv -p 9000 # at container
关注 this, parag. 2 但没有感知到交流,即使在做
nc -luv host-ip -p 9000
在容器处
我也尝试通过 docker run -p 9999:9000
映射端口,但这报告绑定失败,因为主机端口已在使用中(大概是从主机隧道到远程机器)。
所以我的问题是
1 - 我将如何实现连接?我是否需要设置到主机的 ssh 隧道,或者仅通过 docker 端口映射就可以实现?
2 - 测试连接是否正常的快速方法是什么?通过bash,最好。
谢谢。
我想你可以通过将 --net=host
添加到你的 docker 运行 来实现。但另见这个问题:Forward host port to docker container
通过 --net=host
或在 docker 中使用您的主机网络作为容器的网络-通过 network_mode: host
编写是一种选择,但这会产生不必要的副作用,即 (a) 您现在在您的主机系统中公开容器端口,并且 (b) 您无法再连接到那些未映射到您的主机网络的容器。
在你的情况下,一个快速和更清洁的解决方案是让你的 ssh 隧道对你的 docker 容器“可用”(例如,通过将 ssh 绑定到 docker0
桥)而不是暴露你的docker 宿主环境中的容器(如接受的答案中所建议)。
设置隧道:
为此,请检索您的 docker0
网桥正在使用的 ip:
ifconfig
你会看到这样的东西:
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
现在您需要告诉 ssh 到 bind to this ip 以侦听通过
指向端口 9000 的流量
ssh -L 172.17.0.1:9000:host-ip:9999
如果不设置 bind_address,:9000
将 only 可用于您的主机的环回接口,而不是您的 docker 个容器。
旁注:您还可以将隧道绑定到 0.0.0.0
,这将使 ssh 监听所有接口。
正在设置您的应用程序:
在您的容器化应用程序中使用相同的 docker0
ip 连接到服务器:172.17.0.1:9000
。现在,通过您的 docker0
网桥路由的流量也将到达您的 ssh 隧道 :)
例如,如果您有一个“DOT.NET 核心”应用程序需要连接到位于 :9000
的远程数据库,您的“ConnectionString”将包含 "server=172.17.0.1,9000;
。
转发多个连接:
在处理多个传出连接时(例如,一个 docker 容器需要通过隧道连接到多个远程数据库),存在几种有效的技术,但一种简单直接的方法是简单地创建多个隧道来监听流量到达不同的 docker0
个网桥端口。
在您的 ssh 隧道命令 (ssh -L [bind_address:]port:host:hostport] [user@]hostname
) 中,bind_address
的 port
部分不必匹配 host
的 hostport
因此,您可以自由选择。因此,在您的 docker 容器中,只需将流量引导至 docker0
网桥的不同端口,然后创建几个 ssh 隧道命令(一个用于您正在监听的每个端口),在这些端口拦截数据,然后转发它到您选择的不同 host
s 和 hostport
s。
在 MacOS 上(在 v19.03.2
中测试),
1) 在主机上创建隧道
ssh -i key.pem username@jump_server -L 3336:mysql_host:3306 -N
2) 从容器中,您可以使用 host.docker.internal
或 docker.for.mac.localhost
或 docker.for.mac.host.internal
来引用主机。
示例,
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
.
我想分享我的解决方案。我的情况如下:我的主机上有一个 PostgreSQL SSH 隧道,我需要堆栈中的一个容器通过它连接到数据库。
我花了几个小时试图找到解决方案 (Ubuntu + Docker 19.03) 但我失败了。我没有使用 iptables
来施展魔法,而是对 Docker 引擎本身的设置进行了修改,我想出了一个解决方案,我很震惊我之前没有想到这一点。最重要的是我不想使用 host
模式:安全第一。
我没有尝试让容器与主机通信,而是简单地向堆栈添加了另一个服务,这将创建隧道,这样其他容器就可以轻松地通信而不会受到任何攻击。
在我的 ~/.ssh/config
中配置主机后:
Host project-postgres-tunnel
HostName remote.server.host
User sshuser
Port 2200
ForwardAgent yes
TCPKeepAlive yes
ConnectTimeout 5
ServerAliveCountMax 10
ServerAliveInterval 15
并向堆栈添加服务:
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
PHP 容器开始毫无问题地通过隧道通信:
postgresql://user:password@postgres/db?serverVersion=11&charset=utf8
请记住将 public 密钥放入该主机(如果您还没有的话):
ssh-copy-id project-postgres-tunnel
我很确定无论使用何种 OS (MacOS / Linux),这都会起作用。
在我这边,Windows 子系统中的 运行 Docker Linux (WSL v1),我无法使用 docker0 连接方法. host.docker.internal
也没有解决(最新的 docker 版本)。
但是,我发现我可以直接使用我的 docker 容器中的 host-ip insider。
- 获取您的主机 IP (Windows cmd:
ipconfig
),例如192.168.0.5
- Bash 进入您的容器并测试您是否可以 ping 您的主机 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
显然,在 Windows 中,您可以使用官方主机 ip 从容器内直接连接到主机。
我的 2 美分 Ubuntu 18.04 - 一个非常简单的答案,不需要额外的隧道、额外的容器、额外的 docker 选项或暴露主机。
简单地说,在创建反向隧道时,请确保 ssh 绑定到所有接口,因为默认情况下,它仅将反向隧道的端口绑定到本地主机。例如,在 putty 中确保选项 Connection->SSH->Tunnels Remote ports do the same (SSH-2 only)
被勾选。
这或多或少等同于为隧道的远程部分指定绑定地址 0.0.0.0
(更多详细信息 here):
-R [bind_address:]port:host:hostport
但是,这对我不起作用除非我在我的 sshd 服务器配置中允许 GatewayPorts
选项。非常感谢 Stefan Seidel 的伟大 answer.
简而言之:(1) 将反向隧道绑定到 0.0.0.0,(2) 让 sshd 服务器接受此类隧道。
完成后,我可以通过 docker 网关 172.17.0.1
和绑定到主机的端口从我的 docker 容器访问我的远程服务器。
如果有人需要它(就像我一样),Windows 和 WSL 的解决方案与针对 Mac OS
提到的@prayagupd 相同
使用您喜欢的任何工具和您喜欢的任何端口(例如 3300)创建到远程服务的 SSH 隧道。
然后,您可以使用以下命令从 Docker 容器连接到隧道端口 3300 上的 MySQL 数据库:
mysql -u user -p -h host.docker.internal -P 3300
我同意@hlobit 的观点,@B12Toaster 的答案应该是公认的答案。
万一有人遇到这个问题,但 SSH 隧道的设置略有不同,这里是我的发现。在我的例子中,我没有使用 ssh -L
创建从 Docker 主机到远程机器的隧道,而是使用 [=10= 创建从远程机器到 Docker 主机的远程转发 SSH 隧道].
在此设置中,默认情况下 sshd 不允许网关端口,即在 Docker 主机上的文件 /etc/ssh/sshd_config
中,GatewayPorts no
应取消注释并设置为 GatewayPorts yes
或 GatewayPorts clientspecified
。我配置了 GatewayPorts clientspecified
并通过 ssh -L 172.17.0.1:dockerHostPort:localhost:sshClientPort user@dockerHost
配置了远程转发 SSH 隧道。改完/etc/ssh/sshd_config
(sudo systemctl restart sshd
).
记得重启sshd
您的 Docker 容器应该能够连接到 172.17.0.1:dockerHostPort
上的 Docker 主机,而这又会通过隧道返回到 SSH 客户端的 localhost:sshClientPort
.
参考文献:
重现情况和 ssh 到主机的简单示例
- 运行一个容器。使用 --network="host
docker container run --network="host" --interactive --tty --rm ubuntu bash
- 现在您可以使用 localhost
访问您的主机
现在你的主机是一台 Linux 机器,有一个 public-私钥文件可以通过 ssh 进入。因此,复制您的私钥文件的内容并在您的主机中复制密钥文件。 (不过,这只是演示,这不是复制密钥文件的好方法)
- 现在通过 ssh 连接到您的主机。使用 localhost 访问它。
ssh -i key_file.pem ec2-user@localhost
使用 ubuntu tusty,在远程机器上有一个服务 运行,我可以通过端口转发从 localhost:9999
.
我有一个 docker 容器 运行。我需要从容器内通过主机的隧道访问该远程服务。
我尝试使用 -L 9000:host-ip:9999
从容器到主机的隧道,然后从容器内通过 127.0.0.1:9000
访问服务无法连接。为了检查端口映射是否打开,我试过了
nc -luv -p 9999 # at host
nc -luv -p 9000 # at container
关注 this, parag. 2 但没有感知到交流,即使在做
nc -luv host-ip -p 9000
在容器处
我也尝试通过 docker run -p 9999:9000
映射端口,但这报告绑定失败,因为主机端口已在使用中(大概是从主机隧道到远程机器)。
所以我的问题是
1 - 我将如何实现连接?我是否需要设置到主机的 ssh 隧道,或者仅通过 docker 端口映射就可以实现?
2 - 测试连接是否正常的快速方法是什么?通过bash,最好。
谢谢。
我想你可以通过将 --net=host
添加到你的 docker 运行 来实现。但另见这个问题:Forward host port to docker container
通过 --net=host
或在 docker 中使用您的主机网络作为容器的网络-通过 network_mode: host
编写是一种选择,但这会产生不必要的副作用,即 (a) 您现在在您的主机系统中公开容器端口,并且 (b) 您无法再连接到那些未映射到您的主机网络的容器。
在你的情况下,一个快速和更清洁的解决方案是让你的 ssh 隧道对你的 docker 容器“可用”(例如,通过将 ssh 绑定到 docker0
桥)而不是暴露你的docker 宿主环境中的容器(如接受的答案中所建议)。
设置隧道:
为此,请检索您的 docker0
网桥正在使用的 ip:
ifconfig
你会看到这样的东西:
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
现在您需要告诉 ssh 到 bind to this ip 以侦听通过
指向端口 9000 的流量ssh -L 172.17.0.1:9000:host-ip:9999
如果不设置 bind_address,:9000
将 only 可用于您的主机的环回接口,而不是您的 docker 个容器。
旁注:您还可以将隧道绑定到 0.0.0.0
,这将使 ssh 监听所有接口。
正在设置您的应用程序:
在您的容器化应用程序中使用相同的 docker0
ip 连接到服务器:172.17.0.1:9000
。现在,通过您的 docker0
网桥路由的流量也将到达您的 ssh 隧道 :)
例如,如果您有一个“DOT.NET 核心”应用程序需要连接到位于 :9000
的远程数据库,您的“ConnectionString”将包含 "server=172.17.0.1,9000;
。
转发多个连接:
在处理多个传出连接时(例如,一个 docker 容器需要通过隧道连接到多个远程数据库),存在几种有效的技术,但一种简单直接的方法是简单地创建多个隧道来监听流量到达不同的 docker0
个网桥端口。
在您的 ssh 隧道命令 (ssh -L [bind_address:]port:host:hostport] [user@]hostname
) 中,bind_address
的 port
部分不必匹配 host
的 hostport
因此,您可以自由选择。因此,在您的 docker 容器中,只需将流量引导至 docker0
网桥的不同端口,然后创建几个 ssh 隧道命令(一个用于您正在监听的每个端口),在这些端口拦截数据,然后转发它到您选择的不同 host
s 和 hostport
s。
在 MacOS 上(在 v19.03.2
中测试),
1) 在主机上创建隧道
ssh -i key.pem username@jump_server -L 3336:mysql_host:3306 -N
2) 从容器中,您可以使用 host.docker.internal
或 docker.for.mac.localhost
或 docker.for.mac.host.internal
来引用主机。
示例,
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
.
我想分享我的解决方案。我的情况如下:我的主机上有一个 PostgreSQL SSH 隧道,我需要堆栈中的一个容器通过它连接到数据库。
我花了几个小时试图找到解决方案 (Ubuntu + Docker 19.03) 但我失败了。我没有使用 iptables
来施展魔法,而是对 Docker 引擎本身的设置进行了修改,我想出了一个解决方案,我很震惊我之前没有想到这一点。最重要的是我不想使用 host
模式:安全第一。
我没有尝试让容器与主机通信,而是简单地向堆栈添加了另一个服务,这将创建隧道,这样其他容器就可以轻松地通信而不会受到任何攻击。
在我的 ~/.ssh/config
中配置主机后:
Host project-postgres-tunnel
HostName remote.server.host
User sshuser
Port 2200
ForwardAgent yes
TCPKeepAlive yes
ConnectTimeout 5
ServerAliveCountMax 10
ServerAliveInterval 15
并向堆栈添加服务:
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
PHP 容器开始毫无问题地通过隧道通信:
postgresql://user:password@postgres/db?serverVersion=11&charset=utf8
请记住将 public 密钥放入该主机(如果您还没有的话):
ssh-copy-id project-postgres-tunnel
我很确定无论使用何种 OS (MacOS / Linux),这都会起作用。
在我这边,Windows 子系统中的 运行 Docker Linux (WSL v1),我无法使用 docker0 连接方法. host.docker.internal
也没有解决(最新的 docker 版本)。
但是,我发现我可以直接使用我的 docker 容器中的 host-ip insider。
- 获取您的主机 IP (Windows cmd:
ipconfig
),例如192.168.0.5
- Bash 进入您的容器并测试您是否可以 ping 您的主机 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
显然,在 Windows 中,您可以使用官方主机 ip 从容器内直接连接到主机。
我的 2 美分 Ubuntu 18.04 - 一个非常简单的答案,不需要额外的隧道、额外的容器、额外的 docker 选项或暴露主机。
简单地说,在创建反向隧道时,请确保 ssh 绑定到所有接口,因为默认情况下,它仅将反向隧道的端口绑定到本地主机。例如,在 putty 中确保选项 Connection->SSH->Tunnels Remote ports do the same (SSH-2 only)
被勾选。
这或多或少等同于为隧道的远程部分指定绑定地址 0.0.0.0
(更多详细信息 here):
-R [bind_address:]port:host:hostport
但是,这对我不起作用除非我在我的 sshd 服务器配置中允许 GatewayPorts
选项。非常感谢 Stefan Seidel 的伟大 answer.
简而言之:(1) 将反向隧道绑定到 0.0.0.0,(2) 让 sshd 服务器接受此类隧道。
完成后,我可以通过 docker 网关 172.17.0.1
和绑定到主机的端口从我的 docker 容器访问我的远程服务器。
如果有人需要它(就像我一样),Windows 和 WSL 的解决方案与针对 Mac OS
提到的@prayagupd 相同使用您喜欢的任何工具和您喜欢的任何端口(例如 3300)创建到远程服务的 SSH 隧道。
然后,您可以使用以下命令从 Docker 容器连接到隧道端口 3300 上的 MySQL 数据库:
mysql -u user -p -h host.docker.internal -P 3300
我同意@hlobit 的观点,@B12Toaster 的答案应该是公认的答案。
万一有人遇到这个问题,但 SSH 隧道的设置略有不同,这里是我的发现。在我的例子中,我没有使用 ssh -L
创建从 Docker 主机到远程机器的隧道,而是使用 [=10= 创建从远程机器到 Docker 主机的远程转发 SSH 隧道].
在此设置中,默认情况下 sshd 不允许网关端口,即在 Docker 主机上的文件 /etc/ssh/sshd_config
中,GatewayPorts no
应取消注释并设置为 GatewayPorts yes
或 GatewayPorts clientspecified
。我配置了 GatewayPorts clientspecified
并通过 ssh -L 172.17.0.1:dockerHostPort:localhost:sshClientPort user@dockerHost
配置了远程转发 SSH 隧道。改完/etc/ssh/sshd_config
(sudo systemctl restart sshd
).
您的 Docker 容器应该能够连接到 172.17.0.1:dockerHostPort
上的 Docker 主机,而这又会通过隧道返回到 SSH 客户端的 localhost:sshClientPort
.
参考文献:
重现情况和 ssh 到主机的简单示例
- 运行一个容器。使用 --network="host
docker container run --network="host" --interactive --tty --rm ubuntu bash
- 现在您可以使用 localhost
访问您的主机 现在你的主机是一台 Linux 机器,有一个 public-私钥文件可以通过 ssh 进入。因此,复制您的私钥文件的内容并在您的主机中复制密钥文件。 (不过,这只是演示,这不是复制密钥文件的好方法) - 现在通过 ssh 连接到您的主机。使用 localhost 访问它。
ssh -i key_file.pem ec2-user@localhost