抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

前言

这是一个关于在无公网IP 的 IPv4 环境下使用 V2Ray 实现安全的内网穿透的方案,这里用 Q&A 环节来自问自答,介绍一下背景

Q:为什么在家宽已经有了公网 IPv6 的情况下依然使用 IPv4?

A:目前的网络环境下在家宽启用 IPv6 其实存在很多的问题。
如果你的内网使用了网关透明代理,你需要关注节点是否支持 IPv6(尤其是自建的节点,有些云服务商并不会提供 IPv6)。
同时如果无论是否使用 NATv6 ,为了让客户端享受到 IPv6 代理的话,都需要将局域网里的所有设备都使用指定的出口网关,这就违背了使用 IPv6的初衷。
所以 IPv6 目前我判断的使用场景是给那些不需要走代理的游戏机使用(也就是只需要境内游戏的机子,因为境外游戏的机子你还是要走代理的),以及可以利用它在网关上部署境内回家节点。

Q:为什么在已经有了 frp/rathole 等方案之后还会去整一个如此复杂的方案来完成简单的内网穿透?

A:正如上文所说,由于 IPv6 的种种问题,目前家庭网络还是使用 IPv4 居多,但是 IPv4 想要直连的回程就比较困难了。在这种情况下,我们都会考虑使用 VPS 来中转流量。但是境内 VPS 的带宽非常昂贵,而一旦考虑使用境外服务器,过墙的事情就需要被纳入考虑范围, frp 是明文传输的,且有严重的性能问题,rathole 由于在设计之初没有考虑过这一使用场景,实际的过墙表现值得商榷。

Steps

反向代理回程

vmess 本身即支持回程,只需要按照其规则配置好 inboundsoutbounds 即可。具体可以参考白话文

为了其安全性,这里介绍一个目前看来哪怕承载大流量也不容易被墙的传输部分方案。

VMess+WebSocket+TLS

这是一个非常常见且安全的方案,其流量本质上和普通的 HTTPS 网站无异。为了实现我们的更多特殊要求,这里使用 Nginx 作为 TLS 的提供者,详见下文。vmess + websocket 的配置可以参考白话文

Nginx

首先使用 Nginx 作为前置 WebServer,由其负责TLS部分(证书买个域名然后 Certbot + Let’s Encrypt 即可),HTTPS 监听正常的443端口。Nginx 采用反向代理将特定 path 的访问请求转发至 v2ray 监听端口,不要忘了把其他 path 的访问都丢到你的假网站去 。

中转机的 Nginx 配置案例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server {
listen 443 ssl;
ssl_certificate ${path_to_cert};
ssl_certificate_key ${path_to_key};
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
server_name ${your_server_name};
location /your_path/ {
proxy_redirect off;
proxy_pass http://127.0.0.1:${your_vmess_port};
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;

# Show realip in v2ray access.log
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location / {
root ${your_fake_website};
}
}

iptables 动态端口

由于目前墙对境外 TLS 流量的单端口长期大带宽的情况比较敏感,也就是对 IP:Port 这一组合的流量有上限,因此我们只需要改变这一组合的其中一个即可。改 IP 肯定不太可能,所以需要改端口。这里使用 iptables nat 规则将高端口转发到 443 上。

相关指令示范为如下:

1
iptables -t nat -A PREROUTING -p tcp --dport 30000:40000 -j REDIRECT --to-ports 443

这样外界就可以通过访问 30000-40000 之间的高端口来做到实际访问443,且 nmap 等工具扫描高端口时并不会显示其为 http 服务,以达成欺骗墙的目的,防止主动扫描。

注意:客户端访问时也应该动态地改变访问的高端口以规避墙的检测。
同时,家庭网络中的 v2ray 服务也应当利用 crontab 之类的方式动态改变访问服务端的端口,防止长期访问同一个端口且流量过大而被墙。

内网穿透

目前我们已经可以在外通过前置代理方式连来访问内网资源(也就是连接 v2ray,再访问内网IP),这个方式也是比较安全与推荐的。但是如果有某些场景,外网的客户端不支持通过连接前置代理那么就会遇到困难(例如 Clash 等代理软件与机器同时存在的其他学校/公司 VPN 互斥)。此时我们需要中转机来提供透明代理服务。
这里可以使用 nginx 的反向代理直接将对应的服务暴露出来。为了实现这一效果,我们首先需要在服务器上运行一个能连接 v2ray 的代理客户端。在这里,使用的是 clash。

一个示例 clash 配置如下:

1
2
3
4
5
6
7
8
9
10
11
port: ${your_http_port}
socks-port: ${your_socks5_port}
allow-lan: false
mode: Rule
log-level: warning
proxies:
- ${your_proxy}
rules:
- 'IP-CIDR,${your_home_lan},${your_proxy},no-resolve' //内网网段example 192.168.100.0/24
- 'MATCH,DIRECT'

由于 nginx 不支持直接配置其反向代理走 http proxy,我们在这里需要借由 socat 来完成这一工作。注意,你需要一定的方式让 socat 保持运行。因为 socat 默认前台运行。

socat 的指令如下:

1
socat TCP4-LISTEN:${yout_socat_listen_port},reuseaddr,fork PROXY:127.0.0.1:${your_destination_ip}:${your_destination_port},proxyport=${your_clash_http_proxy_port}

完成这一工作之后,下一步则是在 nginx 上配置好你最终要反代的服务。

一个相应的 nginx 配置示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#例子中为反代一个内网http服务到外网https上
server {
listen 443 ssl;
ssl_certificate ${path_to_cert};
ssl_certificate_key ${path_to_key};
server_name ${your_server_name};

location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host-Real-IP $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-Pcol http;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection upgrade;

proxy_intercept_errors on;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:${yout_socat_listen_port};
proxy_redirect https://${your_server_name} http://${your_destination_ip}:${your_destination_port};
}
}

另外如果觉得这些暴露出来的服务敏感的话可以启用 Basic HTTP authentication。如果是大流量的服务或者会被多人使用的话则建议套上一个 cloudflare 的 CDN。

展望

由于这一套过程其实涉及大量的加解密,所以其性能表现上并不是特别优秀,只是考虑到家庭网络下上传带宽普遍不高,因此正常情况下都能承受。而且 TLS+WS 的方案也注定了延迟感人。所以这一方案主要是为那些在家庭网站中部署了媒体服务的人提供在外访问等存储方面的访问功能.

评论