一切都是从需求来开始的。
我有一个windows机器,还有一个Linux服务器。
windows服务器需要偶尔远程桌面,linux服务器需要偶尔ssh连接,同时linux服务器上面还有网站。
但是我的公网IP在猫上,我没有猫的密码,无法直接在网络设备上面做端口转发。
为简化,本文可能尽量减少粘贴详细配置,重点是思路。
基本思路:
找到有公网IP的机器。
私网下的机器主动访问公网IP的机器进行注册。
公网的机器开放一个端口,并设置端口转发,将请求转发给私网机器。
其他客户端直接访问公网的IP加开放的那个端口,即可访问私网下的服务器。
Cloudflared方案
cloudflare是一家CDN公司,该公司也提供了一个内网穿透方案。
要用这个方案,需要把自己的域名托管到cloudflare上。好处是可以保护自己的IP地址,坏处是访问可能会变慢,因为不是直连,多加了代理。
这个方案的软件,之前应该是叫做argo-tunnel,后免费开放了,github上面有源代码,应该还是安全的。
我实际使用了下,也没有流量限制,直接代理我的NAS,进行文件上传下载,也没问题。
该方案主要操作:
1. 私网下的机器,需要安卓cloudflared二进制文件,并新建一个链路。
cloudflared tunnel create tunnel名字
2. 在cloudflare官网上面,设置域名的DNS,将记录解析到第一步创建的tunnel上。
3. 私网机器还要做下cloudflared配置文件,将公网访问下来的域名和本地的服务和端口做好对应。
ingress:
   - hostname: 对外域名
     service: https://本机域名:本机端口
4. 私网机器启动cloudflared,可以做到systemd来管理。
ExecStart=/usr/bin/cloudflared --config /etc/cloudflared/config.yml --no-autoupdate tunnel run
基本上这些就足够了,按Cloudflared的说法,除了HTTP/HTTPS,还可以代理ssh,但是实际没有测试成功。
SSH转发方案
同样的,私网的机器B主动去到公网机器A建立隧道,只是这次用的直接是ssh命令。
1. 私网机器B上面执行,
ssh -fCNR [A机器IP或省略]:[A机器的映射端口]:[B机器的IP]:[B机器需要对外开放的端口] [登陆A机器的用户名@服务器A的公网IP] -p [机器A的ssh端口]
2. 此时,如果机器B需要对外开放的端口是22,已经可以在公网机器A上面执行后面的命令的登录B。
ssh [机器B用户名]@localhost -p [机器A的映射端口]
3. 机器A上面打开端口映射,允许外部直接访问B,
ssh -fCNL [可访问IP或*]:[A机器对外端口]:[A机器的IP]:[机器A对B的映射端口] [登陆A机器的用户名@A机器的IP]
4. 外部直接访问机器A的IP,加第三部机器A的对外端口即可访问机器B开放的相关服务。
5. 第一步的机器B到机器A开放的链路可能不稳定,建议改用autossh命令,和ssh的区别是加了一个“-M 端口”参数,来让A监控B的这个ssh是否正常。
6. 改用密钥登录,同时B机器的autossh托管到systemd,如,我的指令:
ExecStart=/usr/bin/autossh -M 8888 -N -o "PubkeyAuthentication=yes" -o StrictHostKeyChecking=false" -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" -R 8899:localhost:22 ubuntu@A的ip地址 -p 22
我一开始不理解的是第一步已经指定了A的映射端口,其他客户端为啥不能直接访问这个端口就可以访问B。
转念一想,这个端口只是用来跟B来进行对接的,从机器A上面netstat也看已看到,这个端口只绑定到了127.0.0.1,虽然监听的是0.0.0.0。
所以后面才有第三步,A上面再做个本地映射,让对外开放的端口接收到的,从本地转发到这个端口,再转到B。
FRP,Ngrok,zeroTier等
没有详细研究,大体看过一些资料,简单说两句。
FRP和Ngrok,大体和ssh反代类似,而且ngrok还是不开源收费的。
zeroTier牛逼的一点,据说是可以在第一次通过公网的服务器完成客户端和服务端握手以后,后续可以P2P,不需要再让公网服务器介入。
V2RAY分流方案
这个方案也详细研究学习了下。
官方的资料,写的比较绕口,私网机器叫bridge,公网机器叫portal。
大体原理是,bridge主动往portal建立连接,portal上面有两种处理方式:
1. 使用任意门方式,实际就是本机直接inbound部分开一个端口,外界的客户端直接访问portal的ip和端口,然后portal转发给bridge。
这个方式实际是在portal上面重定向,已经修改了相应访问的最终IP和端口。
        { // 接受bridge的inbound
            "tag":"external", // 标签,路由中用到
            "port":***,// 开放端口,用于接收外部的直接访问
            "protocol":"dokodemo-door",
            "settings":{
                "address":"*.*.*.*",//要访问的内网服务器的IP。
                "port":***, //要访问的内网服务器的端口。
                "network":"tcp"
            }
        },
2. 使用其他代理方式,需要客户端通过正常的代理链接到portal,然后:
如果没有分流到其他代理需求的话,直接客户端的全部访问流量转发给bridge处理,这种方式portal不需要配置额外的dokoodemo-door的inbound,或者额外配置分流route。
如果有分流正常代理流量的需求,portal根据客户端访问的一个虚拟的ip和端口来路由给bridge。这种情况下,需要portal额外配置route段,以及bridge要配置outbound段和route段,来让访问相应虚拟ip和端口的流量从bridge重定向出去。
portal的route段:
{
        "type": "field",
        "inboundTag": [
          "portalin"
        ],
        "ip": "111.111.111.111",  // 指定一个用来进行内网穿透的ip
        "port": "5001-5100",  // 指定一个进行内网穿透的端口范围
        "outboundTag": "portal"  // 对应内网穿透连接
      },
bridge的outbound部分:
    {
      "tag": "bridgeout3",  // 内网设备3
      "protocol": "freedom",
      "settings": {
        "redirect": "192.168.1.120:80"  // 内网设备3的内网地址与端口
      }
    },
bridge的route部分:
      {
        "type": "field",
        "inboundTag": [
          "bridge"
        ],
        "port": "5003",  // 为内网设备3分配访问端口,须在portal分配的端口范围中
        "outboundTag": "bridgeout3"  //对应内网设备3
      }
方案一比较灵活,不需要外界的客户端挂代理,或者强制让客户端上面的应用走代理。方案二更为安全,因为不用客户端直接访问服务器的真实IP。
另外,因为一般V2RAY还有其他代理需求,所以还需要考虑一些分流方案,让客户端访问portal以后,portal争取处理访问私网服务和正常的代理需求。
具体到其他基础配置。
1. bridge和portal上面都要配置reverse段,重点是domain要一致,随意编一个不存在的,防止冲突。
"reverse":{   // 这是 bridge的反向代理设置,必须有下面的 bridges 对象
    "bridges":[
        {
            "tag":"bridge", // 关于 A 的反向代理标签,在路由中会用到
            "domain":"ubuntu.com" // A 和 B 反向代理通信的域名,可以自己取一个,可以不是自己购买的域名,但必须跟下面 B 中的 reverse 配置的域名一致
        }
    ]
}
 "reverse":{  //这是portal的反向代理设置,必须有下面的 portals 对象
        "portals":[
            {
                "tag":"portal22",
                "domain":"ubuntu.com"        // 必须和上面 A 设定的域名一样
            }
        ]
    },
2. bridge配置outbound,需要两个,一个是主动到portal发起的,一个是收到请求后,往外转发的。
 "outbounds": [
        {   //主动连接portal使用
            "tag":"tunnel1",
            "protocol": "vless",
            "settings": {
                "vnext": [
                    {
                        "address": "。。。。", // 换成你的域名或服务器 IP(发起请求时无需解析域名了)
                        "port": 443,
                        "users": [
                            {
                                "id": "。。。。", // 填写你的 UUID
                                "encryption": "none",
                                "level": 0
                            }
                        ]
                    }
                ]
            },
            "streamSettings": {}
        },
        {  // 另一个 outbound,最终连接内网机器
            "protocol": "freedom",
            "settings": {},
            "tag": "out"
3. portal上面配置inbound,如上文沟通,两种处理方式。
4. bridge上面配置route。
{  // 配置 bridge 主动连接 portal 的路由规则
    "type":"field",
    "inboundTag":[
        "bridge"
    ],
    "domain":[
        "full:ubuntu.com"
    ],
    "outboundTag":"tunnel"
},
{  // 反向连接访问内部机器
    "type":"field",
    "inboundTag":[
        "bridge"
    ],
    "outboundTag":"out"
}
5. portal上面配置route,让客户端的流量和内网机器主动过来建立链接的流量都发给reverse。
            {  //路由规则,接收 C 请求后发给 A
                "type":"field",
                "inboundTag":[
                    "external_ubuntu"
                ],
                "outboundTag":"portal22"
            },
            {  //路由规则,让 B 能够识别这是 A 主动发起的反向代理连接
                "type":"field",
                "inboundTag":[
                    "tunnel"
                ],
                "domain":[
                    "full:ubuntu.com"
                ],
                "outboundTag":"portal22"
            }
从上面的配置来看,其实难点就是分流,因为如果不需要分流,直接让相应流量都发到reverse就可以了。需要解决的是两个问题:
1. 路由拟合,让相应流量,转发给reverse的链路。两个解决方案
    1.1 要么是通过dokodemo-door,直接开放相应的端口,这样路由里面就可以选定从dokodemo-door进来的这部分流量。
    1.2 要么是通过让客户端访问某个特殊的端口,路由里面选访问某个端口的流量来分流。
2. 修改访问的目标地址为真实地址。
    2.1 一般是在bridge的outbound里面来setting直接重定向到目标地址就行了,
“`”redirect”: “127.0.0.1:80″“`
    2.2 如果是dekodemo-door方式,也可以在portal的inbound的setting里面直接写目的地址。
    2.3 或者两个都配置,配置一致。