zoukankan      html  css  js  c++  java
  • 使用SSH反向隧道进行内网穿透

    使用SSH反向隧道进行内网穿透

    对应的情况

    这篇文章主要介绍了如何利用SSH 反向隧道穿透NAT,并演示了如何维持一条稳定的SSH 隧道。

    假设有机器A 和B,A 有公网IP,B 位于NAT 之后并无可用的端口转发,现在想由A 主动向B 发起SSH 连接。由于B 在NAT 后端,无可用公网IP + 端口 这样一个组合,所以A 无法穿透NAT,这篇文章应对的就是这种情况。

    首先有如下约定,因为很重要所以放在前面:

    机器代号机器位置地址账户ssh/sshd 端口是否需要运行sshd
    A 位于公网 a.site usera 22
    B 位于NAT 之后 localhost userb 22
    C 位于NAT 之后 localhost userc 22

    这里默认你的系统init 程序为systemd,如果你使用其他的init 程序,如果没有特殊理由还是换到一个现代化的GNU/Linux 系统吧……

    SSH 反向隧道

    这种手段实质上是由B 向A 主动地建立一个SSH 隧道,将A 的6766 端口转发到B 的22 端口上,只要这条隧道不关闭,这个转发就是有效的。有了这个端口转发,只需要访问A 的6766 端口反向连接B 即可。

    首先在B 上建立一个SSH 隧道,将A 的6766 端口转发到B 的22 端口上:

    1
    B $ ssh -p 22 -qngfNTR 6766:localhost:22 usera@a.site

    然后在A 上利用6766 端口反向SSH 到B:

    1
    A $ ssh -p 6766 userb@localhost

    要做的事情其实就是这么简单。

    隧道的维持

    稳定性维持

    然而不幸的是SSH 连接是会超时关闭的,如果连接关闭,隧道无法维持,那么A 就无法利用反向隧道穿透B 所在的NAT 了,为此我们需要一种方案来提供一条稳定的SSH 反向隧道。

    一个最简单的方法就是autossh,这个软件会在超时之后自动重新建立SSH 隧道,这样就解决了隧道的稳定性问题,如果你使用Arch Linux,你可以这样获得它:

    1
    $ sudo pacman -S autossh

    下面在B 上做之前类似的事情,不同的是该隧道会由autossh 来维持:

    1
    B $ autossh -p 22 -M 6777 -NR 6766:localhost:22 usera@a.site

    -M 参数指定的端口用来监听隧道的状态,与端口转发无关。

    之后你可以在A 上通过6766 端口访问B 了:

    1
    A $ ssh -p 6766 userb@localhost

    隧道的自动建立

    然而这又有了另外一个问题,如果B 重启隧道就会消失。那么需要有一种手段在B 每次启动时使用autossh 来建立SSH 隧道。很自然的一个想法就是做成服务,之后会给出在systemd 下的一种解决方案。

    “打洞”

    之所以标题这么起,是因为自己觉得这件事情有点类似于UDP 打洞,即通过一台在公网的机器,让两台分别位于各自NAT 之后的机器可以建立SSH 连接。

    下面演示如何使用SSH 反向隧道,让C 连接到B。

    首先在A 上编辑sshd 的配置文件/etc/ssh/sshd_config,将GatewayPorts开关打开:

    1
    GatewayPorts yes

    然后重启sshd

    1
    A $ sudo systemctl restart sshd

    然后在B 上对之前用到的autossh 指令略加修改:

    1
    B $ autossh -p 22 -M 6777 -NR '*:6766:localhost:22' usera@a.site

    之后在C 上利用A 的6766 端口SSH 连接到B

    1
    C $ ssh -p 6766 userb@a.site

    至此你已经轻而易举的穿透了两层NAT。

    最终的解决方案

    整合一下前面提到的,最终的解决方案如下:

    首先打开A 上sshd 的GatewayPorts 开关,并重启sshd(如有需要)。

    然后在B 上新建一个用户autossh,根据权限最小化思想,B 上的autossh 服务将以autossh 用户的身份运行,以尽大可能避免出现安全问题:

    1
    2
    B $ sudo useradd -m autossh
    B $ sudo passwd autossh

    紧接着在B 上为autossh 用户创建SSH 密钥,并上传到A:

    1
    2
    3
    B $ su - autossh
    B $ ssh-keygen -t 'rsa' -C 'autossh@B'
    B $ ssh-copy-id usera@a.site

    注意该密钥不要设置密码,也就是运行ssh-keygen指令时尽管一路回车,不要输入额外的字符。

    然后在B 上创建以autossh 用户权限调用autossh 的service 文件。将下面文本写入到文件/lib/systemd/system/autossh.service,并设置权限为644:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    [Unit]
    Description=Auto SSH Tunnel
    After=network-online.target
     
    [Service]
    User=autossh
    Type=simple
    ExecStart=/bin/autossh -p 22 -M 6777 -NR '*:6766:localhost:22' usera@a.site -i /home/autossh/.ssh/id_rsa
    ExecReload=/bin/kill -HUP $MAINPID
    KillMode=process
    Restart=always
     
    [Install]
    WantedBy=multi-user.target
    WantedBy=graphical.target

    在B 上让network-online.target 生效:

    1
    B $ systemctl enable NetworkManager-wait-online

    如果你使用systemd-networkd,你需要启用的服务则应当是systemd-networkd-wait-online 。

    然后设置该服务自动启动:

    1
    B $ sudo systemctl enable autossh

    如果你愿意,在这之后可以立刻启动它:

    1
    B $ sudo systemctl start autossh

    然后你可以在A 上使用这条反向隧道穿透B 所在的NAT SSH 连接到B:

    1
    A $ ssh -p 6766 userb@localhost

    或者是在C 上直接穿透两层NAT SSH 连接到B:

    1
    C $ ssh -p 6766 userb@a.site

    如果你对SSH 足够熟悉,你可以利用这条隧道做更多的事情,例如你可以在反向连接时指定动态端口转发:

    1
    C $ ssh -p 6766 -qngfNTD 7677 userb@a.site

    假设C 是你家中的电脑,A 是你的VPS,B 是你公司的电脑。如果你这样做了,那么为浏览器设置端口为7677 的sock4 本地(localhost)代理后,你就可以在家里的浏览器上看到公司内网的网页。

  • 相关阅读:
    Python 多线程、进程
    Python网络编程 Socket编程
    Python基础7 面向对象编程进阶
    Python基础6 面向对象编程
    Python基础5 常用模块学习
    Python基础4 迭代器、装饰器、软件开发规范
    Python基础3 函数、递归、内置函数
    Python基础2 列表 字典 集合
    21-Python-多进程
    20-Python-queue队列
  • 原文地址:https://www.cnblogs.com/zafu/p/9320559.html
Copyright © 2011-2022 走看看