用于设置临时 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 permittedmuxserver_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 脚本的主要内容,如果未能解决你的问题,请参考以下文章

putty中隧道设置的脚本

如何在 kubernetes pod 中正确创建用于创建 SSH 隧道的 sidecar 容器

[原创]SSH 隧道转发

ssh 隧道脚本在 beanstalk 部署中永远挂起

我可以将这些 SSH 隧道命令组合成一个命令吗?

通过 SSH 隧道访问 Ezproxy