我希望能够在不同的NAT之后立即SSH到许多linux设备。我无法配置它们所在的网络。但是,我很难让ssh遍历现有连接。
我可以完全控制我的客户端和设备。到目前为止的过程是这样的:在我的客户上,我首先运行
socat TCP-LISTEN:5001,pktinfo,fork EXEC:./create_socket.sh,fdin=3,fdout=4,nofork
内容./create_socket.sh
:
ssh -N -M -S "~/sockets/${SOCAT_PEERADDR}" -o "ProxyCommand=socat - FD:3!!FD:4" "root@${SOCAT_PEERADDR}"
在设备上,我正在跑步
socat TCP:my_host:4321 TCP:localhost:22
但是,FD:3!!FD:4
由于ProxyCommand是一个子进程,因此我认为没有任何结果。我也尝试设置fdin=3,fdout=3
并更改./create_socket.sh
为:
ssh -N -M -S "~/sockets/${SOCAT_PEERADDR}" -o "ProxyUseFdpass=yes" -o "ProxyCommand=echo 3" "root@${host}"
这会显示错误:
mm_receive_fd: no message header
proxy dialer did not pass back a connection
我相信这是因为应该使用来以某种方式发送fd sendmsg
,但fd并非源自子进程。我想使它尽可能简单,并且感觉可行。
您想颠倒客户端/服务器模型,并使其成为通用服务器,以通过网络边界按需并响应对传入的未经身份验证的TCP连接生成客户端,然后告诉该新生成的客户端使用该未经身份验证的TCP会话。我认为这可能具有您未曾想到的安全注意事项。如果恶意软件向您的计算机发送垃圾邮件,您的计算机将产生大量SSH实例以进行重新连接,并且这些过程在身份验证时会占用大量本地系统资源。您实际上是在尝试设置SSH,以跨网络边界自动连接到不受信任(未经验证)的远程启动的计算机。我不能强调可能对您的客户端计算机造成危险。使用错误的选项可能会暴露您拥有的所有凭据,甚至可能使恶意人员完全访问您的计算机。
还要注意的是,您要执行的方案(在多个设备之间建立隧道以在不受信任的网络边界上多路复用其他连接)正是VPN软件的目的。是的,SSH可以建立隧道。VPN软件可以更好地构建隧道。这个概念就是您要在客户端计算机上运行VPN服务器。VPN服务器将创建一个仅代表您的设备的新(虚拟)网络接口。设备将连接到VPN服务器,并被分配一个IP地址。然后,您只需从客户端计算机启动SSH到设备的VPN地址,它将通过虚拟网络接口路由并到达设备,并由其SSH守护程序服务器处理。那你不socat
或用于端口转发的SSH选项。您将获得有关VPN的所有工具和教程。我强烈建议您看一下VPN软件。
如果您真的想使用SSH,那么我强烈建议您学习有关保护SSH服务器的信息。您已声明设备跨网络边界(NAT),并且客户端系统不受保护。我不会阻止您用脚射击自己,但是在您陈述的情况下,如此壮观地拍摄非常容易。如果您处于工作环境中,则应与系统管理员联系,讨论防火墙规则,堡垒主机等类似内容。
是的,您可以按照您所说的去做。我强烈建议您谨慎。我建议,足够强烈,我不会建议任何这将与合作作为说明。我将建议一个具有相同概念但更多身份验证的变体。
首先,您已经有效地设置了自己的SSH退回服务器,但是没有任何与SSH服务器兼容的常用工具。因此,这是我要解决的第一件事:使用SSH服务器软件ssh
而不是通过客户端软件从设备启动连接来验证传入的隧道请求socat
。ssh
已经有足够的能力来创建隧道两个方向和你捆绑了它(与认证socat
,没有认证)。设备应该能够使用加密密钥进行身份验证(ssh称为这些身份)。您需要手动连接一次从设备验证并授权远程加密密钥指纹。您还需要将公共密钥文件(而不是私有密钥文件)复制到客户端计算机,并将其添加到您的authorized_keys文件中。如果需要,您可以单独寻求帮助。
第二个问题是您似乎正在使用fd3和fd4。我不知道你为什么要这么做。如果有的话,您应该使用fd0和fd1,因为它们分别是stdin
和stdout
。但是,如果您要socat
用来发起连接,则甚至不需要这样做。只需使用-
wherestdin
和stdout
意味着。它应该完全兼容-o ProxyCommand
而不指定任何文件描述符。这个答案的末尾有一个例子。
从设备端进行的调用可能看起来像这样(将其放入脚本文件中):
IDENTITY=/home/WavesAtParticles/.ssh/tunnel.id_rsa # on device
REMOTE_SOCKET=/home/WavesAtParticles/.ssh/$(hostname).sock # on client
REMOTEUSER=WavesAtParticles # on client
REMOTEHOST=remotehost # client hostname or IP address accessible from device
while true
do
echo "$(date -Is) connecting"
#
# Set up your SSH tunnel. Check stderr for known issues.
ssh \
-i "${IDENTITY}" \
-R "${REMOTE_SOCKET}:127.0.0.1:22" \
-o ExitOnForwardFailure=yes \
-o PasswordAuthentication=no \
-o IdentitiesOnly=yes \
-l "${REMOTEUSER}" \
"${REMOTEHOST}" \
"sleep inf" \
2> >(
read -r line
if echo "${line}" | grep -q "Error: remote port forwarding failed"
then
ssh \
-i "${IDENTITY}" \
-o PasswordAuthentication=no \
-o IdentitiesOnly=yes \
-l "${REMOTEUSER}" \
"${REMOTEHOST}" \
"rm ${REMOTE_SOCKET}" \
2>/dev/null # convince me this is wrong
echo "$(date -Is) removed stale socket"
fi
#
# Re-print stderr to the terminal
>&2 echo "${line}" # the stderr line we checked
>&2 cat - # and any unused stderr messages
)
echo "disconnected"
sleep 30
done
请记住,就shell脚本而言,复制和粘贴是不好的。至少,我建议您阅读man ssh
和man ssh_config
,并对照shellcheck.net检查脚本。该脚本的目的是:
ssh
使用以下参数运行:
-i "${IDENTITY}"
:指定用于认证的私钥。-R "${REMOTE_SOCKET}:127.0.0.1:22"
:指定一个连接请求转发器,它在远程端接受连接, /home/WavesAtParticles/$(hostname).sock
然后通过连接到本地将它们转发到本地127.0.0.1:22
。-o ExitOnForwardFailure=yes
:如果远程端无法建立连接转发器,则本地端应发出错误并死亡(我们将在子shell中检查此错误)。-o PasswordAuthentication=no
:不要回退到密码请求,尤其是因为本地用户不在此处输入密码时-o IdentitiesOnly=yes
:请勿使用任何默认身份或任何本地代理提供的身份。仅使用由指定的一个-i
。-l "${REMOTEUSER}"
:以指定用户身份登录。remotehost
,例如您希望设备连接到的客户端计算机。如果由于套接字过期而导致连接失败,请通过以下方法解决此问题:
有一个选项旨在使此错误处理变得多余:StreamLocalBindUnlink
。但是,该选件无法正常工作,并且已打开多年的错误。我想那是因为确实没有多少人使用ssh来转发unix域套接字。这很烦人,但并不难解决。
使用Unix域套接字应将连接性限制为可以访问套接字文件的任何人(应该只有您并且该文件root
位于${HOME}/.ssh
目录中并且目录具有正确的权限)。我不知道这对您的情况是否重要。
另一方面,如果您愿意为每个设备打开一个TCP端口,则也可以简化很多127.0.0.1
操作。但是随后,同一系统上的任何其他用户也可以连接。您应该专门侦听127.0.0.1
哪些内容将仅接受来自同一主机的连接,以防止外部计算机到达转发端口。您可以将${REMOTE_SOCKET}
变量更改为例如127.0.0.1:4567
侦听端口4567并仅接受本地连接。因此,您将失去命名套接字功能,并允许客户端计算机上的任何其他用户连接到您的设备,但获得了更为简单的隧道脚本(因为您可以删除解析stderr的全部内容来删除陈旧的套接字文件)。
只要您的设备处于联机状态(可以到达工作站的传入端口)并且正在运行该脚本,并且身份验证有效,则隧道也应该处于联机状态或联机状态。但是,丢失(和恢复)网络连接后,将需要一些时间来恢复。您可以使用ConnectTimeout
,TCPKeepAlive
和ServerAliveInterval
选项以及sleep 30
循环的一部分进行调整。您可以在tmux
会话中运行它以使其继续运行,即使您没有运行登录会话也是如此。您也可以在设备上将其作为系统服务运行,以使其联机,即使从电源故障中恢复后也是如此。
然后,可以从客户端反向连接:
ssh -o ProxyCommand='socat - unix-connect:/home/WavesAtParticles/remotehost.sock' -l WavesAtParticles .
在此调用中,您将开始ssh
。然后,它将使用来设置proxycommand socat
。它将使用其stdin / stdout并通过提供的路径上连接的AF_UNIX套接字将其中继。您需要更新所需的远程主机的路径。但是根本不需要指定文件描述符。如果ssh
抱怨:
2019/08/26 18:09:52 socat[29914] E connect(5, AF=1 "/home/WavesAtParticles/remotehost.sock", 7): Connection refused
ssh_exchange_identification: Connection closed by remote host
那么隧道当前已关闭,您应该调查remotehost
设备的连接性。
如果您将远程转发选项与TCP端口侦听而不是Unix域套接字一起使用,则通过隧道远程客户端调用将变得更加容易:ssh -p 4567 WavesAtParticles@localhost
。
同样,您正在尝试反转客户端/服务器模型,我认为使用SSH并不是一个好主意。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句