使用国内服务器基于frp的内网穿透
前言
校内某课程和华为云平台有合作,送了100元代金券,眼看就快要过期了,不用也是浪费,于是就下单了最便宜的Flexus L服务器,能用三个月。

买的就是北京区域的服务器,虽然带宽不大,但是延迟肯定低啊,想到正好可以拿来做香橙派的内网穿透,这样哪怕在校外也可以畅快地连到内网了。
frp下载
首先到 https://github.com/fatedier/frp/releases 下载最新版的release。
压缩包解压后会发现里面有frps和frpc两个可执行文件,分别是服务端和客户端,还有对应的toml配置文件。
frp服务端部署
首先是服务端,也就是在云服务器,写服务端的配置frps.toml,我的需求是HTTP服务的内网穿透,示例如下:
bindPort = 7000
vhostHTTPPort = 8080
auth.token = "abc"
绑定端口7000用于和客户端通信,而8080端口是开放HTTP服务的端口,这两个端口都需要开放(华为云的安全组需要设置入站允许规则),这里的token是为了身份认证,保证让只有token正确的客户端才能连上。
配置写好后,使用以下命令启动服务端:./frps -c ./frps.toml
frp客户端部署
然后是客户端,对我来说就是需要被内网穿透的香橙派,写frpc.toml,示例如下:
serverAddr = "x.x.x.x"
serverPort = 7000
auth.token = "abc"
[[proxies]]
name = "web"
type = "http"
localPort = 6666
customDomains = ["www.yourdomain.com"]
这里的serverAddr和serverPort就是刚才云服务器的IP地址和设置的绑定端口,localPort是客户端的HTTP服务端口。token设置需要和服务端保持一致。
然后再启动客户端:./frpc -c ./frpc.toml

这样成功连接后,客户端这边的HTTP服务就可以在服务端的vhostHTTPPort端口被公网访问了。也就是说,访问 http://www.yourdomain.com:8080 即可访问到内网机器上的6666端口服务。
值得注意的是,这是我第一次使用国内的云服务器,刚开始我真的随手拿个域名配置了A记录,结果一访问发现:

这是因为,虽然云服务器本身是实名的,但是只要使用中国大陆节点的服务器对外提供服务,域名就必须进行ICP备案,而我并没有备案的域名。
后来我发现其实可以不要域名,只需要把customDomains参数改成服务器IP,然后直接用IP访问就可以了:
customDomains = ["x.x.x.x"]
注册成系统服务
跑通之后,可以使用systemd把服务端和服务端注册成一个服务。
对于服务端,编辑/etc/systemd/system/frps.service文件,写入:
[Unit]
# 服务名称,可自定义
Description = frp server
After = network.target syslog.target
Wants = network.target
[Service]
Type = simple
# 启动frps的命令,需修改frps的路径
ExecStart = /path/to/frps -c /path/to/frps.toml
[Install]
WantedBy = multi-user.target
然后可以用systemctl管理:
# 启动frps
sudo systemctl start frps
# 停止frps
sudo systemctl stop frps
# 重启frps
sudo systemctl restart frps
# 查看frps状态
sudo systemctl status frps
# 设置frps开机自启
sudo systemctl enable frps
客户端frpc也是类似,写入一个/etc/systemd/system/frpc.service文件就行。
配置SSL
目前虽然是通了,但是没有HTTPS的话总感觉是不太安全。由于没用域名而是直接IP访问,需要申请一个纯IP的SSL证书。
考虑到就是给自己用的,为了方便起见,我直接就使用自签证书了,CA证书的创建方法可以看这一篇,然后用CA证书分发一个IP SSL证书,步骤如下:
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/C=CN/ST=Shanghai/L=Shanghai/O=bjut/OU=IT/CN=x.x.x.x"
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650 -sha256 -extfile ip_cert.ext
以上的x.x.x.x应替换为服务器的IP地址。完成后就会得到有效期十年(3650天)的证书server.crt和私钥server.key。
之后可以套一层nginx代理来实现HTTPS,比如说配置一个/etc/nginx/conf.d/http.conf:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name x.x.x.x;
ssl_certificate /path/to/server.crt; # 证书路径
ssl_certificate_key /path/to/server.key; # 私钥路径
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
由于是自签证书,以上操作建立在CA证书被系统信任的前提上,或者是软件忽略了证书校验,比如说部署xray的XHTTP协议就可以将TLSObject的pinnedPeerCertSha256设置为证书的SHA-256指纹来强制信任。
当然了,如果不用考虑备案问题,自然是直接申请域名证书更省事。
TCP/UDP类型的内网穿透
前面所述是HTTP协议的内网穿透,如果需要做其他协议的内网穿透,参考官方示例应该在客户端做如下的proxies配置(remotePort是服务端监听对应的端口):
TCP(如SSH服务):
[[proxies]]
name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 6000
UDP(如DNS服务):
[[proxies]]
name = "dns"
type = "udp"
localIP = "127.0.0.1"
localPort = 53
remotePort = 6000