Frp 是一款开源的反向代理工具,能够帮助我们反向代理出在内网的服务。

Github 地址:点此前往

当然最近 Github 因为某些原因被 GFW Gank 了,需要一些特殊手段上去。

服务器配置

将下载好的 frp 解压,frps文件放在 /usr/bin 下,systemd下的 frps .service文件放在 /etc/systemd 下,配置文件 frps.ini 放在 /etc/frp 下,/etc/frp 目录需要手工建立。

frps.ini 配置文件参考,具体配置参见官方说明

[common]
bind_port = 7000
bind_udp_port = 7001
vhost_http_port = 7080
vhost_https_port = 7443
subdomain_host = xxx.xxx.xxx
dashboard_addr = 127.0.0.1
dashboard_port = 7500
dashboard_user = xxxxxx
dashboard_pwd = xxxxxx

如果不需要使用 Nginx 作为访问前端,vhost_http_port 绑定 80端口,vhost_https_port 绑定443端口就好。

客户端配置

客户端配置和服务器差不多,但是对应的就是 frpc,frpc.ini,frpc.service 这三个文件放在相应的位置

frpc.ini 配置文件参考,具体配置参见官方说明

[common]
server_addr = xxx.xxx.xxx.xxx
server_port = 7000

[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 2020

[wordpress]
type = https
local_port = 443
subdomain = www

[nextcloud]
type = https
local_port = 443
subdomain = nextcloud

该配置仅供参考。

SSL证书

关于SSL证书的配置就不多说了,相关教程网上一大把

Https 使用 Nginx 反代

这里是踩坑最多的地方,查询了谷歌后发现相关信息极少,如果没有必须使用 Nginx 的需求,可以直接使用 frp 绑定80和443端口即可。

如果必须使用 Nginx 作为前端(比如反向代理服务器上还有其他网站),这时你会发现常规 https 访问就会直接返回502了。

样例Nginx配置

server {
    server_name *.testtest.com;
    listen 443 ssl http2;
    ssl_certificate /etc/ssl/testtest.com/fullchain.pem;
    ssl_certificate_key /etc/ssl/testtest.com/privkey.pem;

    location / {
        proxy_pass https://127.0.0.1:7443;
        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_set_header X-Forwarded-Proto $scheme;
    }
}

这么看很合理的一份配置,就是在访问的时候出现502,并且在客户端上的http服务中看不到访问日志。

在反向代理服务器上的 Nginx 发现错误日志:

2020/02/26 02:57:13 [error] 16513#16513: *133785 peer closed connection in SSL handshake while SSL handshaking to upstream, client: xxx.xxx.xxx.xxx, server: *.xxx.xxx.xxx, request: "GET / HTTP/2.0", upstream: "https://127.0.0.1:7443/", host: "xxx.xxx.xxx"

然而我们通过这个报错并没有办法查到什么有用的信息。最后在#671中发现了解决方案。

推测是 frp 在反向代理https的时候,因为 Nginx 已经处理过了 https 的握手信息,所以, Frp 无法获取到 SNI 信息,导致发送请求的时候就是不完整的https请求。因为如果改成http或者tcp反向代理都可以成功,顾认为是 Frp 机制的问题,他访问的域名变成127.0.0.1,没有 SNI 信息。所以在上面那份配置的情况下,可以通过域名+端口号成功访问。

所以修改后的配置如下:

server {
        ssl_protocols TLSv1.2 TLSv1.3;

        listen 443 ssl http2;

        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

        server_name *.xxx.xxx.xxx;

        location / {
                resolver         127.0.0.53;
                proxy_pass       https://$host:7443;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header Host $host;
                proxy_ssl_server_name on;
        }

        ssl_certificate /etc/letsencrypt/live/xxx.xxx.xxx/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/xxx.xxx.xxx/privkey.pem;
}

server {
        listen 80;
        server_name *.xxx.xxx.xxx;
        return 301 https://$server_name$request_uri;
}

相应的,需要设置DNS解析相关域名到本地。

可以使用 dnsmasq 或者直接修改 /etc/hosts

127.0.0.1    localhost

# The following lines are desirable for IPv6 capable hosts
::1          localhost ip6-localhost ip6-loopback
ff02::1      ip6-allnodes
ff02::2      ip6-allrouters

# map for frp https with ngxin frontend
127.0.0.1    xxx.xxx.xxx

总结

虽然说这样配置很烦,但是这样可以使用泛域名的方式访问,就是需要使用 dnsmasq。修改 /etc/hosts 不能泛域名。

Frp 很强大,但是没有什么好办法和 Nginx 配合使用,不得不说这应该是设计之初的问题了。在 Chrome 很快就要禁止 http 链接的情况下,https肯定是要上的,就是这样配置太不友好了。

分类: Linux运维

0 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注