用于设置临时SSH隧道的Bash脚本

时间:2010-02-10 23:16:04

标签: bash ssh

在CYGWIN上,我想要一个BASH脚本:

  1. 创建到远程服务器的SSH隧道。
  2. 在本地做一些使用隧道的工作。
  3. 然后关闭隧道。
  4. “关闭部分”让我感到困惑。

    目前,我有一个蹩脚的解决方案。在一个shell中,我运行以下命令来创建一个隧道。

    # Create the tunnel - this works! It runs forever, until 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窗口以杀死隧道。

    我想在一个脚本中执行此操作,例如:     #创建隧道     # 做工作     #杀死隧道

    如何跟踪隧道过程,所以我知道要杀死哪一个?

7 个答案:

答案 0 :(得分:290)

您可以使用ssh'控制套接字'干净利落地完成此操作。要与已经运行的SSH进程通信并获取它的pid,请将其删除等。使用'control socket'(-M代表master,-S代表socket),如下所示:

$ 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获得了此信息。

答案 1 :(得分:19)

您可以使用-f选项告诉SSH背景本身,但不会使用$!获取PID。 此外,在使用隧道之前,您可以使用-o ExitOnForwardFailure = yes而不是让脚本在任意时间内休眠,并且在将自身置于后台之前,SSH将等待所有远程端口转发成功建立。您可以grep ps的输出来获取PID。例如,您可以使用

...
ssh -Cfo ExitOnForwardFailure=yes -NL 9999:localhost:5900 $REMOTE_HOST
PID=$(pgrep -f 'NL 9999:')
[ "$PID" ] || exit 1
...

并且非常确定您获得了所需的PID

答案 2 :(得分:18)

  • 您可以告诉ssh使用&进入后台,而不是使用命令行标记在另一侧创建一个shell(只是打开隧道)(我看到您已经使用{ {1}})。
  • 使用-N
  • 保存PID
  • 做你的东西
  • PID=$!
编辑:修正了$?到$!并添加了&

答案 3 :(得分:4)

我更喜欢为单独的任务启动一个新的shell,我经常使用以下命令组合:

  $ sudo bash; exit

或有时:

  $ : > sensitive-temporary-data.txt; bash; rm -f sensitive-temporary-data.txt; exit

这些命令创建了一个嵌套的shell,我可以完成所有工作;当我完成后我按下CTRL-D并且父shell清理并退出。您可以轻松地将bash;放入kill部分之前的ssh隧道脚本中,这样当您退出嵌套shell时,您的隧道将被关闭:

#!/bin/bash
ssh -nNT ... &
PID=$!
bash
kill $PID

答案 4 :(得分:2)

您可以在结尾处ssh启动&,将其置于后台并在执行时获取其ID。然后,当你完成时,你只需要做{id} kill

答案 5 :(得分:2)

另一个可能的选择 - 如果你可以安装expect包,你应该能够编写整个脚本。这里有一些很好的例子:http://en.wikipedia.org/wiki/Expect

答案 6 :(得分:-1)

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

<头>
姓名 描述
行动 开始|停止
隧道名称 "字符串识别套接字" slugify 创建套接字文件到 ~/.ssh/sockets/string-identify-socket.control
LOCAL_PORT 如果我们为两个连接使用相同的端口,将会在本地暴露的门会崩溃
REMOTE_SERVER 如果您在将要使用的代理服务器上拥有它,您将访问的服务器的 IP
REMOTE_PORT 运行在服务器上的服务端口
TUNNEL_PROXY 您将用作代理的连接,它需要在您的 ~/.ssh/config 中,最好使用访问密钥

我使用remmina组名和连接名的组合(% g-% p)作为我的TUNNELNAME(这需要唯一,它会看到套接字名称)

前置命令

~/.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

image

你可以而且应该使用这个脚本来访问任何东西,我经常用它来访问没有公共 IP 的系统和服务,这些系统和服务通过 1、2、3、4、5 或更多的 ssh 代理

查看更多:

  1. ssh config
  2. ssh mach
  3. ssh jump hosts
  4. sshuttle python ssh

参考:

  1. https://remmina.org/remmina-rdp-ssh-tunnel/
  2. https://kgibran.wordpress.com/2019/03/13/remmina-rdp-ssh-tunnel-with-pre-and-post-scripts/
  3. Bash script to set up a temporary SSH tunnel
  4. https://gist.github.com/oneohthree/f528c7ae1e701ad990e6