美文网首页linux&github
SSH实现内网穿透,你需要的都在这里

SSH实现内网穿透,你需要的都在这里

作者: 76becd986646 | 来源:发表于2019-07-03 21:26 被阅读9次

    SSH实现内网穿透

    背景

    1. 需求:内网服务接口暴露给外网调用(外网不能直接访问内网)
    2. 前提:拥有一台有公网ip的服务器
    环境
    1. 设备A:内网服务器,内网服务器是一台虚拟机,虚拟机上运行nginx服务器,端口是8014,后续描述中设备A简称为A

    2. 设备B:有公网ip的服务器,公网IP:149.83.24.124(举例IP,替换成你的公网服务器ip地址即可),开放的端口有80、443、8081、8024,(服务器已经关闭了防火墙,而且将80、443、8081、8024端口,加入安全组放行,允许任意ip访问这些端口),后续描述中设备B简称为B

    3. 访问者C : 可以通过任意公网访问B,但不能访问A,后续描述中访问者C简称为C

    注意:

    1. 此例中ip和端口,要替换为你本人的ip和端口
    2. 此例中是B上的8024端口,所以可以使用非root用户通过ssh建立隧道,也就是此例中的local用户,如果你想使用B的80端口,那么一定要使用root用户来建立隧道,因为非root用户只能操作1024以上得端口,切记
      在以下例子中一定要注意命令是在A上执行还是在B上执行
    实现方法(使用的是root用户,操作以下命令)
    方法一(掌握方法一即可,方法二只是一个扩展方法)

    在A的虚拟中执行命令ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124,显示如下,此例中,密码处输入的就是B设备上的local用户的密码

    [root@localhost ~]# ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124
    The authenticity of host '[149.83.24.124]:50237 ([149.83.24.124]:50237)' can't be established.
    ECDSA key fingerprint is ce:5e:6d:0a:d4:05:5f:27:21:04:9f:f6:bf:d3:38:a9.
    Are you sure you want to continue connecting (yes/no)? yes
    Warning: Permanently added '[149.83.24.124]:50237' (ECDSA) to the list of known hosts.
    local@149.83.24.124's password: 
    [root@localhost ~]#
    

    ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124
    意思是:使用local用户通过B的50237端口创建ssh隧道,将B的8024端口接收的数据转发到A的8014端口

    注意:

    1. A上的8014端口,就是指A中的nginx服务器实际listen的端口,如果你的虚拟机和宿主机之间进行了端口转发操作,也不要误以为是转发到宿主机后的端口,简单讲假如nginx的8014端口映射到宿主机的端口是8017,但是这里的ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124依然要用8014而不是8017,切记
    2. 在A中执行ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124成功之前,如果在B上的执行netstat -antupl | grep local 应该是没有记录的,因为在A中执行ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124成功后。会在B上开启一个8024端口,如下:
    [root@iZwz9466l9048v1ijne6nxZ ~]# netstat -antupl | grep local
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
    tcp        0      0 0.0.0.0:8024            0.0.0.0:*               LISTEN      8223/sshd: local              
    tcp        0      0 149.83.24.124:50237     67.59.189.84:21918      ESTABLISHED 8221/sshd: local [p 
    

    可能会出现的问题

    1. [root@localhost ~]# Warning: remote port forwarding failed for listen port 8024
      解决方法:
      查看A虚拟机中/root/.ssh/known_hosts文件
    [root@localhost ~]# cat /root/.ssh/known_hosts 
    [149.83.24.124]:50237 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXsasYTItbmlzFqfBAfFvufZX8f2JEds*Osasa2Q/swp34dMkpDcff+lUT6n+QQMN+m10JByCz2VQ8yYSxs0=
    

    删除/root/.ssh/known_hosts 文件中以[149.83.24.124]:50237开头行
    然后再查看A上刚创建的ssh隧道,kill掉,此例就是kill 5690
    [root@localhost ~]# ps aux | grep ssh
    root 5431 0.0 0.1 141864 5208 ? Ss 18:05 0:00 sshd: root@pts/0
    root 5494 0.0 0.1 85732 3876 ? Ss 18:33 0:00 /usr/sbin/sshd -D
    root 5690 0.0 0.0 83920 2024 ? Ss 20:15 0:00 ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124
    root 5697 0.0 0.0 112704 968 pts/0 S+ 20:22 0:00 grep --color=auto ssh
    然后再查看B上刚创建的ssh隧道,kill掉,此例就是kill 8221 8223(如果B上没有,就无需kill)
    [root@iZwz94nxZ ~]# ps aux | grep ssh
    root 7476 0.0 0.5 157348 6040 ? Ss 17:52 0:00 sshd: root@pts/1
    root 8215 0.0 0.4 112812 4272 ? Ss 20:15 0:00 /usr/sbin/sshd -D
    root 8221 0.0 0.5 157208 5940 ? Ss 20:16 0:00 sshd: local [priv]
    local 8223 0.0 0.2 157528 2588 ? S 20:16 0:00 sshd: local
    root 8232 0.0 0.0 112704 972 pts/1 S+ 20:27 0:00 grep --color=auto ssh
    最后再执行如下命令即可

    [root@localhost ~]# ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124
    
    1. 错误展示

    这是因为B的/etc/ssh/sshd_config文件中的GatewayPorts为注释的,修改成如下:

    GatewayPorts yes
    

    然后将已经在A和B上创建的ssh隧道kill掉,然后再如下重启sshd服务

    [root@iZwne6nxZ ~]# systemctl restart sshd.service
    
    1. bind: Cannot assign requested address(使用ipv6导致的,加上-4指定ipv4)
      [root@localhost ~]# ssh -4 -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124
    方法二(不再详细描述)

    思路:在B创建一个隧道,将B的8024端口的数据转发到B的8081端口,然后再在A中创建隧道将B中8081端口的数据转到A中的8014端口上

    1. 在B创建一个隧道,将B的8024端口的数据转发到B的8081端口
    ssh -4 -p 50237 -fCNL 8024:localhost:8081 localhost
    
    1. 在A中创建隧道将B中8081端口的数据转到A中的8014端口上
    ssh -p 50237 -fCNR 8081:localhost:8014 local@149.83.24.124
    
    ssh不稳定会断开的解决方案使用autossh替换ssh(这个是在网上看的,我本地实验发现长期不连依然是会断开,有可能是我配置不对,这个不敢妄言,我使用其他的方法解决了,后面有描述),使用方法如下(在A上操作):
    1. 安装autossh
    yum install -y autossh
    
    1. 在A中运行如下命令
    autossh -M 6677 -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124
    
    1. linux查看进程显示
    [root@localhost ~]# ps aux | grep ssh | grep -v grep
    root       946  0.0  0.1  85732  3896 ?        Ss   10:06   0:00 /usr/sbin/sshd -D
    root      4969  0.0  0.1 141864  5208 ?        Ss   14:39   0:00 sshd: root@pts/2
    root      5016  0.0  0.0   6516   472 ?        Ss   14:48   0:00 autossh -M 6677 -p 40682 -CNR  80:localhost:8014 root@112.74.61.154
    root      5017  0.0  0.1  83920  5016 ?        S    14:48   0:00 /usr/bin/ssh -L 6677:127.0.0.1:6677 -R 6677:127.0.0.1:6678 -p 40682 -CNR 80:localhost:8014 root@112.74.61.154
    

    查看进程你会发现使用autossh命令后进程会多出一个监听6677端口进程,来维护ssh远程隧道的稳定

    如果希望在本地xshell窗口关闭,但是本地虚拟机依然运行的时候隧道依然可以稳定使用,需如下操作(在A上操作):
    1. 安装screen
    yum install -y screen
    
    1. 创建screen会话,命名为wap
    screen -S wap
    
    1. 退出screen会话窗口
    ctrl+A+D
    
    1. 查看创建的screen会话(注意ls左边的那个-,千万不要忘记)
    [root@localhost ~]# screen -ls
    There is a screen on:
        5112.wap    (Detached)
    1 Socket in /var/run/screen/S-root.
    
    [root@localhost ~]#
    
    1. 进入wap会话
    [root@localhost ~]# screen -r wap
    # 创建隧道
    #-o ServerAliveInterval=60 本地 ssh 每隔60s向 server 端 sshd 发送 keep-alive 包,以保持连接
    [root@localhost ~]# ssh  -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124 -o ServerAliveInterval=60
    # 退出wap会话
    ctrl+A+D
    
    1. 关闭连接A的所有xshell窗口,隧道会依然存在

    2. 然后再使xshell连接A,使用步骤4、5就可以再次进入会话查看创建隧道的命令

    3. 假如你想释放screen会话,就先进入对应会话,执行

    exit
    
    如果希望B端口443转发到A的8014端口,需要借助nginx实现(这个方法不是很优雅,但是目前未找到更好的,如果有朋友知道,请在评论区留言):
    1. 安装nginx
    yum install -y nginx
    
    1. 开放端口443和8081(用于转发),允许客户端访问
    2. 在B上配置配置文件(要去申请对应域名的ssl证书,以支持443端口使用)
    vim /etc/nginx/conf.d/domain.conf
    server {
         listen       443 ssl;
         server_name  domian;
         charset utf-8;
         ssl_certificate /etc/nginx/ssl/domain.crt;
         ssl_certificate_key /etc/nginx/ssl/domain.key;
         location / {
         proxy_pass http://127.0.0.1:8081;
         proxy_set_header Host $host:8081;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         }
    }
    

    配置的意思是将B上443端口的数据转发到B上的8081端口

    1. 重载nginx
    systemctl reload nginx
    
    1. 在A上创建隧道监听B上的8081端口的数据,并将B上8081端口的数据转发到A上的8014端口上,命令如下
    ssh -p 50237 -fCNR 8081:localhost:8014 local@149.83.24.124 -o ServerAliveInterval=60
    
    1. C去访问B的443端口,就会被转到A的8014端口上

    能力有限,如有错误,欢迎指正,谢谢

    相关文章

      网友评论

        本文标题:SSH实现内网穿透,你需要的都在这里

        本文链接:https://www.haomeiwen.com/subject/vkrhhctx.html