• SSH端口转发


    SSH Port Forwarding

    什么是SSH端口转发?

    SSH端口转发也被称为SSH隧道,我们知道SSH是加密通讯的,所以通过SSH建立的隧道传输的内容是安全的。它通常被用来绕过防火墙等设备穿透到内网,或用于保护TCP连接等。

    SSH端口转发分为以下三种:

    1. 本地转发
    2. 远程转发
    3. 动态转发

    什么时候需要使用SSH 端口转发?

    让我们思考以下的情况,当我们使用WorkStation SSH连接到SQL Server时,我们通过SSH发送的数据是加密的,但我们通过Mysql连接到SQL Server时,它的连接则是不会被加密的,如果这是一条跨越公网的连接,这将是十分危险的。

    但如果我们时候SSH端口转发,我们则能够在过程中通过SSH进行加密,能够保护我们数据的安全。

    那么我们应该使用那种SSH端口转发方式呢?

    本地转发

    本地转发——表示本地某个端口的数据通信会被转发到目标主机的特定端口,注意,我们把执行本地转发命令的设备称为本地主机。

    在上面讲的情况,我们就可以使用本地转发的方式。在设置完成本地转发的方式之后,我们就能够通过本地与目标主机建立的SSH隧道进行传输数据,这样可以将我们传输的数据进行加密。

    让我们看看下面这个实例:

    我们通过curl命令行工具访问Web服务器。

    bash
    [root@localhost ~]# curl -I 192.168.222.128
    HTTP/1.1 403 Forbidden
    Date: Wed, 23 Nov 2022 20:04:36 GMT
    Server: Apache/2.4.37 (centos)
    Content-Location: index.html.zh-CN
    Vary: negotiate,accept-language
    TCN: choice
    Last-Modified: Fri, 14 Jun 2019 03:37:43 GMT
    ETag: "fa6-58b405e7d6fc0;5ee25c70d2749"
    Accept-Ranges: bytes
    Content-Length: 4006
    Content-Type: text/html; charset=UTF-8
    Content-Language: zh-cn

    通过上述内容,我们可以通过WireShark抓取到明文的HTTP包。

    那么我们接下来设置SSH端口转发。

    bash
    [root@localhost ~]# ssh -L 80:192.168.222.128:80 192.168.222.128
    The authenticity of host '192.168.222.128 (192.168.222.128)' can't be established.
    ECDSA key fingerprint is SHA256:gfElOdquMLiDDsfg0TQG//KU+uahlfzSjb23pQlbSxk.
    Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
    Warning: Permanently added '192.168.222.128' (ECDSA) to the list of known hosts.
    root@192.168.222.128's password: 
    Last login: Thu Nov 24 04:01:51 2022 from 192.168.222.1

    值得注意的是,这里我们设置了SSH端口转发,输入完相应的密码后,我们将会生成一个新的SSH会话连接到远程主机。

    关于设置隧道的具体参数,我们在端末会提到。

    bash
    [root@localhost ~]# netstat -tnl
    Active Internet connections (only servers)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State      
    tcp        0      0 127.0.0.1:80            0.0.0.0:*               LISTEN     
    tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN     
    tcp6       0      0 ::1:80                  :::*                    LISTEN     
    tcp6       0      0 :::22                   :::*                    LISTEN     

    从上可见,我们在本地监听了一个80端口(此时使用的是刚刚执行端口转发命令的机器)。我们再次尝试访问Web服务器。

    bash
    [root@localhost ~]# curl -I localhost
    HTTP/1.1 403 Forbidden
    Date: Wed, 23 Nov 2022 20:12:06 GMT
    Server: Apache/2.4.37 (centos)
    Content-Location: index.html.zh-CN
    Vary: negotiate,accept-language
    TCN: choice
    Last-Modified: Fri, 14 Jun 2019 03:37:43 GMT
    ETag: "fa6-58b405e7d6fc0;5ee25c70d2749"
    Accept-Ranges: bytes
    Content-Length: 4006
    Content-Type: text/html; charset=UTF-8
    Content-Language: zh-cn

    我们再次查看产生的流量。

    那么此时的流量则是经过SSH加密后的流量了。

    那么如何不产生新的SSH会话,单独建立SSH隧道呢?我们可以使用 -N 参数,这个选项将会帮助我们仅建立SSH隧道而不会打开远程Shell连接到主机。

    但是光使用这个 -N 参数也是不够的(具体为什么不够,可以自己尝试一下),我们还需要使用 -f 参数,让这个隧道在后台运行。

    那么我们目前的命令就是 ssh -f -N -L 80:192.168.222.128:80 192.168.222.128

    但是这也是有弊端的,这条命令仅仅只会监听在 127.0.0.1 这个本地环回地址上。如果想要监听在一个本地地址上我们可以写成下面这种形式。

    ssh -f -N -L 192.168.222.129:80:192.168.222.128:80 192.168.222.128 像这样我们就能够将80端口监听在自己所需要的地址上。

    如果我们需要设置端口监听在所有地址上,我们可以使用 -g 参数——开启网关模式,这样它就能够监听在所有的地址上了。

    远程转发

    介绍完本地转发后,我们再来思考一下远程转发,为了方便理解,我们来描述一个场景。

    在上述这个场景中,Web服务器由于防火墙的原因不能正常的通过外部进行访问,我们能够控制内部Web服务器与外部服务器,但却没有防火墙的操作权限。

    此时我们就可以使用远程转发,来绕过防火墙的限制,穿透到内网访问内网的资源。

    我们来通过实际实验介绍远程转发。

    远程&动态转发实验

    实验环境

    我们是用CentOS 8 作为服务器以及防火墙,通过设置三台主机构建网络环境,通过模拟防火墙分割网络,具体拓扑如下图。

    环境部署

    FW

    首先我们要设置Linux的全局转发功能,使得Linux能够转发不同网段之间的数据。

    bash
    [root@localhost ~]# vim /etc/sysctl.conf    

    在其中添加如下内容。

    bash
    net.ipv4.ip_forward = 1

    然后执行命令。

    bash
    [root@localhost ~]# sysctl -p
    net.ipv4.ip_forward = 1

    其次,设置 CentOS 8 自带的 Firewalld,我们需要将 FW 连接到 OutSide 设备的网段的网卡设置到 external 区域( external 区域自动开启 masquerade 功能 )。

    默认网卡都在 Public 区域中,我们应该先把网卡从 Public 区域中删除,然后将网卡添加到 external 区域中。

    bash
    [root@localhost ~]# firewall-cmd --remove-interface=ens160 --zone=public
    success
    [root@localhost ~]# firewall-cmd --add-interface=ens160 --zone=external
    success

    下述内容均为操作 Inside 主机。

    Inside

    然后在Web服务器Ping OutSide 主机,理论上是能够通信的。

    bash
    [root@localhost ~]# ping outside
    PING outside (192.168.244.130) 56(84) bytes of data.
    64 bytes from outside (192.168.244.130): icmp_seq=1 ttl=63 time=2.49 ms
    64 bytes from outside (192.168.244.130): icmp_seq=2 ttl=63 time=0.890 ms
    64 bytes from outside (192.168.244.130): icmp_seq=3 ttl=63 time=0.854 ms
    64 bytes from outside (192.168.244.130): icmp_seq=4 ttl=63 time=1.02 ms

    那么基础网络已经构建完毕了。我们给 Inside 服务器安装 Web 功能,设置防火墙放行指定服务。

    bash
    [root@localhost ~]# yum install -y httpd
    [root@localhost ~]# systemctl start httpd && systemctl enable httpd
    [root@localhost ~]# firewall-cmd --add-service=http --per && firewall-cmd --reload
    [root@localhost ~]# curl -I localhost
    HTTP/1.1 403 Forbidden
    Date: Fri, 25 Nov 2022 20:57:20 GMT
    Server: Apache/2.4.37 (centos)
    Content-Location: index.html.zh-CN
    Vary: negotiate,accept-language
    TCN: choice
    Last-Modified: Fri, 14 Jun 2019 03:37:43 GMT
    ETag: "fa6-58b405e7d6fc0;5ee25c70d2749"
    Accept-Ranges: bytes
    Content-Length: 4006
    Content-Type: text/html; charset=UTF-8
    Content-Language: zh-cn
    
    # 能有如上显示则成功配置

    以上就是基础环境的配置,下面开始远程转发的配置。

    远程转发实验

    我们可以从拓扑得知,FW 隔断开了两个网段,由于 FW 的 Masquerade 功能,Inside 可以访问 OutSide,而 OutSide 不能访问 Inside。如果此时 Inside 想让 OutSide 能够访问自己的Web服务就可以通过远程转发功能。

    bash
    [root@localhost ~]# ssh -f -N -R 80:192.168.222.128:80 192.168.244.130 
    root@192.168.244.130's password: 
    

    此时我们可以在OutSide机器上看到相应端口的建立。

    bash
    [root@OutSide ~]# netstat -tnl    
    Active Internet connections (only servers)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State      
    tcp        0      0 127.0.0.1:80            0.0.0.0:*               LISTEN     
    tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN     
    tcp6       0      0 ::1:80                  :::*                    LISTEN     
    tcp6       0      0 :::22                   :::*                    LISTEN     

    我们在OutSide机器上访问本地的80端口。

    bash
    [root@OutSide ~]# curl -I localhost
    HTTP/1.1 403 Forbidden
    Date: Fri, 25 Nov 2022 22:01:18 GMT
    Server: Apache/2.4.37 (centos)
    Content-Location: index.html.zh-CN
    Vary: negotiate,accept-language
    TCN: choice
    Last-Modified: Fri, 14 Jun 2019 03:37:43 GMT
    ETag: "fa6-58b405e7d6fc0;5ee25c70d2749"
    Accept-Ranges: bytes
    Content-Length: 4006
    Content-Type: text/html; charset=UTF-8
    Content-Language: zh-cn

    至此,我们的远程端口转发也搭建成功。

    具体的流量还是会经过 FW ,但是 FW 放行 SSH 端口,数据经过SSH的加密将会穿过FW到达内网,从而能够访问Inside。

    动态转发&实验

    那么我们都有了远程转发和本地转发,为什么还需要动态转发呢?

    那么我们思考这样一个问题,如果我们本地转发的目的端口为443,对端是一个HTTPS服务,在我们通过浏览器访问时,由于本地监听的地址为localhost,则会出现证书无法被验证的问题。还有假设这个Web服务器将我们重定向至另一个URL,很有可能将连接失败,例如在使用单点登录时(SSO),这种情况很可能出现问题。

    使用动态转发则能够解决这个问题,动态转发的实现是SSH通过在本地建立Socks代理,然后通过SSH转发到远程主机,然后远程主机再将SSH内的数据包转发至内网主机。

    在如上的拓扑环境中,OutSide会首先监听本地端口用作Socks代理,对于传输到Socks代理的数据,将会经过SSH的加密传输到FW,然后FW再次转发至内部Web服务器。

    我们在OutSide主机上进行操作。

    bash
    [root@Outside ~]# ssh -f -N -D 1080 192.168.244.129
    root@192.168.244.129's password: 
    [root@Outside ~]# netstat -tnl
    Active Internet connections (only servers)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State      
    tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN     
    tcp        0      0 127.0.0.1:1080          0.0.0.0:*               LISTEN     
    tcp6       0      0 :::22                   :::*                    LISTEN     
    tcp6       0      0 ::1:1080                :::*                    LISTEN  
    

    一般Socks代理都会监听在 TCP/1080 上,不过在上述命令行命令中,你可以任意指定,但是使用的端口要和设置的相匹配。

    我们在输入正确的远程主机SSH密码后,则能够建立SSH隧道。然后我们使用Curl的命令行工具进行测试。

    bash
    [root@Outside ~]# curl -I --socks5 127.0.0.1:1080 192.168.222.128
    HTTP/1.1 403 Forbidden
    Date: Sat, 26 Nov 2022 00:08:51 GMT
    Server: Apache/2.4.37 (centos)
    Content-Location: index.html.zh-CN
    Vary: negotiate,accept-language
    TCN: choice
    Last-Modified: Fri, 14 Jun 2019 03:37:43 GMT
    ETag: "fa6-58b405e7d6fc0;5ee25c70d2749"
    Accept-Ranges: bytes
    Content-Length: 4006
    Content-Type: text/html; charset=UTF-8
    Content-Language: zh-cn

    可以看到,我们虽然指定的是内网地址,但仍能通过Socks代理成功访问到了Web服务器。

    接下来我们利用WireShark查看实际的情况。首先我们来分析建立隧道时候的数据包。

    这里与SSH打开Shell所建立的过程类似。接下来看看通过Socks代理转发HTTP请求的情况。

    从上图可以得知,由于Socks地址监听在本地并不会在虚拟网卡上产生数据,我们只能抓到通过隧道传输过的HTTP请求数据。那么我们如何抓取本地网卡上的Socks包呢?

    我们通过tcpdump工具监听数据。

    bash
    [root@Outside ~]# tcpdump -i lo dst port 1080 -vv > test.pcap

    我们将 test.pcap 文件传入主机,用WireShark打开分析。我们可以看到,这里显示出的内容证明了SSH动态转发是通过Socks实现的,而且数据包为明文。

    接下来关注 FW 到 Inside 服务器的传输情况。

    可以看到,FW承担了一个代理的作用,在内网与外网间转发流量,而且仅仅使用了SSH所需要的端口。

    只要应用程序能够通过Socks代理,动态转发理论上可以转发所有的流量,就不用设置转发目的端口(本地)这一麻烦的步骤了。


    __EOF__

  • 本文作者: RichardLuo
  • 本文链接: https://www.cnblogs.com/RichardLuo/p/SSH_PORT_FORWARDING.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    2022年最新宁夏建筑八大员(材料员)模拟考试试题及答案
    springAOP 通过注解实现 日志打印
    【python学习】基础篇-常用函数-匿名函数的使用
    python+requests+pytest+allure自动化框架
    纳米体育数据足球数据接口:资料库数据包接口文档API示例②
    微服务框架 SpringCloud微服务架构 12 DockerCompose 12.2 部署微服务集群
    将可遍历对象转换为(索引,值)序列 enumerate() 函数
    Java开发学习(五)----bean的生命周期
    Java学习笔记(二十四)
    【Hadoop】学习笔记(五)
  • 原文地址:https://www.cnblogs.com/RichardLuo/p/SSH_PORT_FORWARDING.html