用于设置临时 SSH 隧道的 Bash 脚本
Posted
技术标签:
【中文标题】用于设置临时 SSH 隧道的 Bash 脚本【英文标题】:Bash script to set up a temporary SSH tunnel 【发布时间】:2011-01-15 12:17:09 【问题描述】:在 Cygwin 上,我想要一个 Bash 脚本:
-
创建到远程服务器的 SSH 隧道。
在本地做一些使用隧道的工作。
然后关闭隧道。
关机部分让我很困惑。
目前,我有一个蹩脚的解决方案。在一个 shell 中,我运行以下命令来创建隧道:
# Create the tunnel - this works! It runs forever, until the shell is quit.
ssh -nNT -L 50000:localhost:3306 jm@sampledomain.com
然后,在另一个 shell 窗口中,我做我的工作:
# Do some mysql stuff over local port 50000 (which goes to remote port 3306)
最后,完成后,我关闭第一个 shell 窗口以终止隧道。
我想在一个脚本中完成所有这些操作,例如:
# Create tunnel
# Do work
# Kill tunnel
我如何跟踪隧道进程,以便知道要杀死哪个进程?
【问题讨论】:
我写了一个脚本来帮助做 ssh 隧道,你可以查看:github.com/gdbtek/ssh-tunneling.git 【参考方案1】:您可以使用 ssh '控制套接字' 干净地完成此操作。要与已经运行的 SSH 进程通信并获取它的 pid,将其终止等。使用“控制套接字”(-M 表示主进程,-S 表示套接字),如下所示:
$ ssh -M -S my-ctrl-socket -fnNT -L 50000:localhost:3306 jm@sampledomain.com
$ ssh -S my-ctrl-socket -O check jm@sampledomain.com
Master running (pid=3517)
$ ssh -S my-ctrl-socket -O exit jm@sampledomain.com
Exit request sent.
请注意,my-ctrl-socket 将是一个实际创建的文件。
我从a very RTFM reply on the OpenSSH mailing list 获得此信息。
【讨论】:
这是迄今为止我看到的关于该主题的最佳答案。非常感谢,应该是采纳的。我用它连接到我的 Vagrant VM 并运行 FlywayDB 更新脚本。 显然控制套接字并非在任何地方都有效。比如我在drone.io持续集成环境上得到Operation not permitted
:muxserver_listen: link mux listener ssh-ctrl-socket.wsASkszgSBlK7kqD => ssh-ctrl-socket: Operation not permitted
那么运行后my-ctrl-socket
文件会发生什么?当我在当前文件夹中执行ls -la
时,我再也看不到该文件了。
如果您在脚本中使用它,您需要等待控制套接字几秒钟才能可用。我的解决方案:while [ ! -e $ctrl_socket ]; do sleep 0.1; done
很棒的东西,比我在网上找到的所有其他东西都要优雅得多。谢谢【参考方案2】:
您可以使用 -f 选项告诉 SSH 自己在后台运行,但您不会使用 $! 获得 PID。 此外,您可以使用 -o ExitOnForwardFailure=yes 和 -f ,而不是让您的脚本在使用隧道之前休眠任意时间,SSH 将等待所有远程端口转发成功建立,然后再将其置于后台。您可以 grep ps 的输出来获取 PID。例如,您可以使用
...
ssh -Cfo ExitOnForwardFailure=yes -N -L 9999:localhost:5900 $REMOTE_HOST
PID=$(pgrep -f 'N -L 9999:')
[ "$PID" ] || exit 1
...
并确保您获得了所需的 PID
【讨论】:
你可能没有意识到,但这是一种天才。我正在寻找一种跟踪 SSH 隧道 PID 的方法,并且几乎最终使用了 systemd 服务脚本。不再:我可以使用隧道名称 grep 所需的 SSH 进程。这个想法不知何故完全跳过了我。非常感谢!【参考方案3】: 您可以告诉ssh
使用&
进入后台,而不是使用命令行标志在另一侧创建shell(只需打开隧道)(我看到您已经使用-N
执行此操作)。
用PID=$!
保存PID
做你的事
kill $PID
编辑:固定 $?到$!并添加了 &
【讨论】:
如果我的脚本在被 KILL 之前死掉了,我必须小心处理。 @jm:trap 'kill $PID' 1 2 15
将涵盖许多脚本失败的情况。
为了让它可靠地工作,我必须在创建隧道之后但在使用它之前“睡”一会儿。
@NormanRamsey 我想你的意思是trap "kill $PID"
,因为 bash 只会在双引号字符串中插入变量
@JuanCaicedo 只有在稍后重新定义 PID
变量时,这种区别才会很重要。当调用 trap
内置函数(OP 的方法)或捕获信号时(您的方法),该变量会被扩展;两种方法在这里产生相同的结果。【参考方案4】:
我更喜欢为单独的任务启动一个新的 shell,我经常使用以下命令组合:
$ sudo bash; exit
或者有时:
$ : > sensitive-temporary-data.txt; bash; rm -f sensitive-temporary-data.txt; exit
这些命令创建了一个嵌套的 shell,我可以在其中完成所有工作;当我完成时,我按下 CTRL-D 并且父 shell 也会清理并退出。您可以在 kill
部分之前轻松地将 bash;
放入您的 ssh 隧道脚本中,这样当您退出嵌套 shell 时,您的隧道将关闭:
#!/bin/bash
ssh -nNT ... &
PID=$!
bash
kill $PID
【讨论】:
非常有趣。这可能会更好地处理“陷阱”问题。一定要试试。【参考方案5】:您可以启动ssh
并以&
结尾,将其置于后台并在执行时获取其ID。然后,您只需在完成后对该 ID 执行 kill
。
【讨论】:
如果使用与号(“&”),请注意。这是一种丑陋的方法,因为您必须确定为自己建立的实际连接。它可能导致执行更多代码,而这些代码不等待实际连接完全建立。此外,如果脚本中断,连接不会自动终止。【参考方案6】:https://github.com/aronpc/remina-ssh-tunnel
#!/usr/bin/env sh
scriptname="$(basename $0)"
actionname="$1"
tunnelname=$(echo "$2" | iconv -t ascii//TRANSLIT | sed -E 's/[^a-zA-Z0-9-]+/-/g' | sed -E 's/^-+|-+$//g' | tr A-Z a-z)
remotedata="$3"
tunnelssh="$4"
if [ $# -lt 4 ]
then
echo "Usage: $scriptname start | stop LOCAL_PORT:RDP_IP:RDP_PORT SSH_NODE_IP"
exit
fi
case "$actionname" in
start)
echo "Starting tunnel to $tunnelssh"
ssh -M -S ~/.ssh/sockets/$tunnelname.control -fnNT -L $remotedata $tunnelssh
ssh -S ~/.ssh/sockets/$tunnelname.control -O check $tunnelssh
;;
stop)
echo "Stopping tunnel to $tunnelssh"
ssh -S ~/.ssh/sockets/$tunnelname.control -O exit $tunnelssh
;;
*)
echo "Did not understand your argument, please use start|stop"
;;
esac
使用示例
编辑或创建新的 remmina 服务器连接
架构
~/.ssh/rdp-tunnel.sh ACTION TUNNELNAME LOCAL_PORT:REMOTE_SERVER:REMOTE_PORT TUNNEL_PROXY
name | description |
---|---|
ACTION | start|stop |
TUNNELNAME | "string identify socket" slugify to create socket file into ~/.ssh/sockets/string-identify-socket.control |
LOCAL_PORT | the door that will be exposed locally if we use the same port for two connections it will crash |
REMOTE_SERVER | the ip of the server that you would access if you had it on the proxy server that will be used |
REMOTE_PORT | the service port that runs on the server |
TUNNEL_PROXY | the connection you are going to use as a proxy, it needs to be in your ~/.ssh/config preferably using the access keys |
我用remmina组名和连接名的组合(%g-%p)作为我的TUNNELNAME(这个要唯一,会看到socket名)
预命令
~/.ssh/rdp-tunnel.sh start "%g-%p" 63394:192.168.8.176:3389 tunnel-name-1
命令后
~/.ssh/rdp-tunnel.sh stop "%g-%p" 63394:192.168.8.176:3389 tunnel-name-1
你可以并且应该使用这个脚本来访问任何东西,我经常使用它来访问没有公共 ip 通过 1、2、3、4、5 或更多 ssh 代理的系统和服务强>
查看更多内容:
-
ssh config
ssh mach
ssh jump hosts
sshuttle python ssh
参考:
-
https://remmina.org/remmina-rdp-ssh-tunnel/
https://kgibran.wordpress.com/2019/03/13/remmina-rdp-ssh-tunnel-with-pre-and-post-scripts/
Bash script to set up a temporary SSH tunnel
https://gist.github.com/oneohthree/f528c7ae1e701ad990e6
【讨论】:
虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review 谢谢,我设法把readme.md,我不知道我有以上是关于用于设置临时 SSH 隧道的 Bash 脚本的主要内容,如果未能解决你的问题,请参考以下文章