Alain's Blog

  1. 首页
  2. NAS
  3. 正文

群晖在Frp内网穿透下获取真实IP,以及自动切换内外网

2022年10月11日 5174点热度 4人点赞 10条评论

群晖在Frp内网穿透下获取真实IP,以及自动切换内外网

转载请注明出处,本文仅用于学习交流,不对之处,恳请指正,部分图片摘取网络,如有侵权请联系

群晖在内网穿透工具下,如果使用的是HTTPS,或者TCP的方式,则无法正确获取用户的真实IP,这将影响群晖基于IP的功能,如防火墙拉黑暴力破解的IP等。那么我们可以考虑通过Proxy Protocol的方式来解决该问题。

前言

我们在将内网的NAS发布到公网的方式一般有使用公网IP,或者通过内网穿透等方式。我使用的是Frp(高性能反向代理应用)这个工具。今天刚好跟朋友林陈陈的小站闲聊的时候:提到了群晖在内网穿透的情况下,如何获取真实IP的问题。

刚好我之前有写过群晖WebDAV使用,并在穿透下获取真实IP,于是便想顺便将群晖网页服务如何获取真实IP的方式也记录下来。

需求

  1. 群晖默认通过Header中的X-Forwarded-For来获取用户的真实IP,而Frp的HTTP类型也默认开启该功能。获取用户真实 IP。所以如果你使用的是HTTP类型来访问你的NAS,则将Frp的流量指向对应的NAS IP地址即可

  2. 如果你使用的是TCP或者HTTPS的方式来访问你的NAS,则需要使用Proxy Protocol协议来获取用户的真实IP。刚好群晖使用的是Nginx作为网页服务器,Nginx支持Proxy Protocol协议。

  3. 随着现在IPv6的普及,我们大多数宽带都拥有IPv6的公网IP,那么我们再通过IPv6直连NAS的时候,也应该获取到对应的真实IP。

  4. 附加需求:当我们回到局域网的时候,应自动切换到局域网的IP进行访问,以保证我们的最佳访问速度。

测试环境

  1. OpenWrt:Frpc插件 0.42.0 版本
  2. Debian11:Frps服务端 0.42.0 版本
  3. 群晖:DS3617xs 6.2.3版本

方案

随便画,将就看...

群晖在Frp内网穿透下获取真实IP方案

Frpc 配置

本文着重于如何使用Proxy Protocol协议

Frpc 代码配置

  1. 如果你使用的是独立的Frpc客户端,则你可以通过以下代码配置来开启Proxy Protocol

    # 中括号内填入你的服务的唯一标识
    [alainlam.cn]
    # 类型可以是http,https,tcp等
    type=https
    # 自定义域名,需要将你的域名解析到对应的FRPS服务器IP
    custom_domains=alainlam.cn
    # 局域网内NAS服务器的IP
    local_ip=192.168.1.1
    # 为了满足公网IPv6以及局域网的IPv4也能够获取正确的真实IP
    # 我们可以将Nginx监听的Proxy Protocol协议放在其他端口上
    local_port=55001
    # 开启Proxy Protocol V2版本
    proxy_protocol_version=v2
    # 启用加密
    use_encryption=true
    # 启用压缩
    use_compression=true
  2. 重启Frpc客户端

    systemctl restart frpc.service

OpenWrt中配置Frpc

kuoruan/openwrt-frp

OpenWrt支持Frpc插件,我们可以使用该插件来配置Frpc。

  1. 在服务中,找到Frp内网穿透

  2. 在基础配置中写入你的服务器配置信息

    OpenWrt Frpc 服务器配置信息

  3. 在服务列表中新增一项服务,并启用Proxy Protocol协议

    OpenWrt Frpc 使用Proxy Protocol协议

  4. 点击保存,Frpc会自动重启

群晖使用Proxy Protocol协议

  1. 使用ssh连接到群晖

  2. 切换到root用户

    sudo -i
  3. 阅读群晖的nginx的配置文件

    cat /etc/nginx/nginx.conf

    我们可以在配置文件的结尾看到以下代码,故我们可以将我们的Nginx配置文件放置在/etc/nginx/conf.d/目录下,并以http.*.conf格式命名,这样群晖的Nginx会自动引用相应的配置文件,且不会被系统所覆盖修改。

    注意我这里使用的是DS3617xs 6.2.3版本,请注意查阅你的版本是否相同!

    群晖nginx.conf

  4. 编写反向代理配置,并开启Proxy Protocol协议,请根据你的具体情况以及以下注释说明进行适当调整

    server {
        # 监听IPv4,并开启Proxy Protocol协议
        listen 55001 ssl proxy_protocol;
    
        # 监听IPv6,并开启Proxy Protocol协议
        listen [::]:55001 ssl proxy_protocol;
    
        # 监听的域名,与你的Frpc服务所配置的custom_domains一致
        server_name alainlam.cn;
    
        # 排除Cloudflare CDN的IP
        # 如果你有使用到CDN的服务的话,一般NAS不会使用到此类IP
        # 仅供参考
        set_real_ip_from 173.245.48.0/20;
        set_real_ip_from 103.21.244.0/22;
        set_real_ip_from 103.22.200.0/22;
        set_real_ip_from 103.31.4.0/22;
        set_real_ip_from 141.101.64.0/18;
        set_real_ip_from 108.162.192.0/18;
        set_real_ip_from 190.93.240.0/20;
        set_real_ip_from 188.114.96.0/20;
        set_real_ip_from 197.234.240.0/22;
        set_real_ip_from 198.41.128.0/17;
        set_real_ip_from 162.158.0.0/15;
        set_real_ip_from 104.16.0.0/13;
        set_real_ip_from 104.24.0.0/14;
        set_real_ip_from 172.64.0.0/13;
        set_real_ip_from 131.0.72.0/22;
        set_real_ip_from 2400:cb00::/32;
        set_real_ip_from 2606:4700::/32;
        set_real_ip_from 2803:f800::/32;
        set_real_ip_from 2405:b500::/32;
        set_real_ip_from 2405:8100::/32;
        set_real_ip_from 2a06:98c0::/29;
        set_real_ip_from 2c0f:f248::/32;
    
        # 排除本地IP,请根据你的具体情况配置
        set_real_ip_from 192.168.1.0/24;
        set_real_ip_from 10.0.0.0/8;
    
        # 排除服务器IP
        set_real_ip_from 你运行Frps的服务器IP地址
    
        # 真实IP使用proxy_protocol协议
        real_ip_header proxy_protocol;
    
        # 开启排除IP功能
        real_ip_recursive on;
    
        proxy_headers_hash_max_size 512;
        proxy_headers_hash_bucket_size 128;
    
        # 反向代理
        location / {
            # 目标地址,群晖默认HTTPS地址为5001,请根据实际情况调整
            proxy_pass https://localhost:5001;
    
            # 兼容http
            # proxy_set_header Upgrade-Insecure-Requests 1;
    
            # 告诉后端使用ssl
            proxy_ssl_server_name on;
    
            # 客户端使用的http协议
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Scheme $scheme;
    
            # 客户端host
            proxy_set_header Host $host;
            proxy_set_header REMOTE-HOST $remote_addr;
            proxy_set_header X-Forwarded-Host $http_host;
    
            # 完整URI
            proxy_set_header X-Original-URI $request_uri;
    
            # 客户端使用的端口
            proxy_set_header X-Real-Port $proxy_protocol_port;
    
            # 多层代理IP
            proxy_set_header X-Forwarded-For $proxy_protocol_addr;
    
            # 客户端IP,群晖默认会通过X-Real-IP获取用户IP
            proxy_set_header X-Real-IP $proxy_protocol_addr;
    
            # 支持Websocket
            # 如果你使用诸如Docker bash此类的功能,则需要开启Websocket
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection Upgrade;
            proxy_connect_timeout 60s;
            proxy_read_timeout 60s;
            proxy_send_timeout 12s;
        }
    }
  5. 测试nginx配置是否正确

    nginx -t

    正确则返回

    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful
  6. 重载Nginx

    nginx -s reload
  7. 完成

局域网与公网自动切换

如果我们的电脑或者手机的网关以及DNS服务器指向是我们的主路由局域网IP地址,则我们可以在路由器中对相应的域名进行拦截。

比如在OpenWrt中:

局域网与公网自动切换

参考

  1. 群晖WebDAV使用,并在穿透下获取真实IP
  2. 获取用户真实 IP
标签: Frp NAS 内网穿透 群晖
最后更新:2022年11月16日

Alain

看了我的文,就是我的人,点个赞再走成不成

点赞
< 上一篇

文章评论

  • 皇家元林

    看了你这篇文章,我有两个问题:
    1、listen 55001 ssl proxy_protocol; 这个proxy_protocol我一添加,网站就打不开了,去掉就好了。
    2、看你的配置文件里,还有反向代理,这是群晖直接反向代理自己吗?

    2023年10月3日
    回复
    • Alain

      @皇家元林 FRP在使用TCP或者HTTPS的时候需要使用proxy_protocol才能获取到真实ip(我用的是0.42的版本),如果打开proxy_protocol,那么没有经过proxy_protocol代理的流量确实是不能打开的。所以才会放在一个特殊的端口55001,专门给frp的流量使用。frp内网端口设置为55001,外网端口设置为5001,那么你正常访问5001就可以了。形成的访问模式就跟我上图画的一样。
      1. 在外面通过frp访问的时候走的线路是服务器的5001->frp->群晖的55001(这里会把拿到的真实ip设置到X-Forwarded-For)->群晖的5001(会从X-Forwarded-For拿到ip地址)
      2. 通过ipv6或者lan访问的时候,因为用的仍然是5001端口直连,所以群晖可以正确获取ip。

      总而言之,我们访问的仍然是5001端口,只是当我们没办法直接访问,走的是frp的时候,那么就会通过服务器的5001访问frps,frpc再去访问群晖的55001,再反向到5001。

      2023年10月3日
      回复
      • 皇家元林

        @Alain 说实话,你这逻辑,好复杂!
        当frp添加proxy_protocol_version = v2这个时候,不应该就是走proxy_protocol代理的流量吗?
        既然listen 55001 ssl proxy_protocol;后面加这个proxy_protocol有效的话,直接穿透这个端口就可以了啊。而我现在遇到的问题就是这个,后面加这个proxy_protocol无效,出现502错误。
        我刚本地测试了。

        server
        {
        listen 80;
        listen 443 ssl http2;
        listen [::]:443 ssl http2; #proxy_protocol
        listen [::]:80;
        server_name 192.168.0.108 rsshub.iyl.me;
        index index.php index.html index.htm default.php default.htm default.html;
        root /www/wwwroot/rss/;

        #SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则
        #error_page 404/404.html;
        #HTTP_TO_HTTPS_START
        #if ($server_port !~ 443){
        # rewrite ^(/.*)$ https://$host$1 permanent;
        #}
        #HTTP_TO_HTTPS_END
        ssl_certificate /www/server/panel/vhost/cert/192.168.1.60/fullchain.pem;
        ssl_certificate_key /www/server/panel/vhost/cert/192.168.1.60/privkey.pem;
        ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
        ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;
        add_header Strict-Transport-Security "max-age=31536000";
        error_page 497 https://$host$request_uri;

        #SSL-END

        #ERROR-PAGE-START 错误页配置,可以注释、删除或修改
        error_page 404 /404.html;
        error_page 502 /502.html;
        #ERROR-PAGE-END

        #PHP-INFO-START PHP引用配置,可以注释或修改
        include enable-php-81.conf;
        #PHP-INFO-END

        #REWRITE-START URL重写规则引用,修改后将导致面板设置的伪静态规则失效
        include /www/server/panel/vhost/rewrite/192.168.1.60.conf;
        #REWRITE-END

        #禁止访问的文件或目录
        location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md)
        {
        return 404;
        }

        #一键申请SSL证书验证目录相关设置
        location ~ \.well-known{
        allow all;
        }

        #禁止在证书验证目录放入敏感文件
        if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
        return 403;
        }

        location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
        {
        expires 30d;
        error_log /dev/null;
        access_log /dev/null;
        }

        location ~ .*\.(js|css)?$
        {
        expires 12h;
        error_log /dev/null;
        access_log /dev/null;
        }
        access_log /www/wwwlogs/192.168.1.60.log;
        error_log /www/wwwlogs/192.168.1.60.error.log;
        }

        server {
        # 监听IPv4并开启Proxy Protocol协议
        listen 8443 ssl proxy_protocol;
        # 监听IPv6并开启Proxy Protocol协议
        listen [::]:8443 ssl proxy_protocol;
        # 监听的域名
        server_name rsshub.iyl.me;

        #HTTP_TO_HTTPS_END
        ssl_certificate /www/server/panel/vhost/cert/192.168.1.60/fullchain.pem;
        ssl_certificate_key /www/server/panel/vhost/cert/192.168.1.60/privkey.pem;
        ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
        ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;
        add_header Strict-Transport-Security "max-age=31536000";
        error_page 497 https://$host$request_uri;

        #SSL-END
        # 真实IP使用proxy_protocol协议
        real_ip_header proxy_protocol;
        # 开启排除IP功能
        real_ip_recursive on;

        # 排除服务器IP
        set_real_ip_from 127.0.0.1;

        # 排除CDN的IP
        # 根据你的实际情况配置
        # set_real_ip_from 你.的.CDN.IPS

        proxy_headers_hash_max_size 512;
        proxy_headers_hash_bucket_size 128;

        # 反向代理
        location / {
        # 代理的目标地址
        proxy_pass https://127.0.0.1:443;
        # 客户端host
        proxy_set_header Host $host;
        # 兼容http
        proxy_set_header Upgrade-Insecure-Requests 1;
        # 告诉后端使用ssl
        proxy_ssl_server_name on;

        # 完整URI
        proxy_set_header X-Original-URI $request_uri;
        # 客户端host
        proxy_set_header X-Forwarded-Host $host;
        # 客户端IP
        proxy_set_header X-Real-IP $proxy_protocol_addr;
        # 客户端使用的http协议
        proxy_set_header X-Forwarded-Proto $scheme;
        # 客户端使用的端口
        proxy_set_header X-Real-Port $proxy_protocol_port;
        # 客户端及多层代理IP
        proxy_set_header X-Forwarded-For $proxy_protocol_addr;
        # 客户端使用的http协议
        proxy_set_header X-Scheme $scheme;

        # 支持websocket
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection Upgrade;
        proxy_connect_timeout 60s;
        proxy_read_timeout 60s;
        proxy_send_timeout 12s;
        }
        }
        应该是这样的意思把,加个端口,这个端口是frpc的监听端口,然后这个端口又反向代理到这个网站的端口。但是网站还是502错误。跟直接穿透一样的结果。

        2023年10月3日
        回复
        • Alain

          @皇家元林 确实只是穿透了55001端口,我只是跟你解释了不同场景下的流量走向。我在外,晚点看下你的配置,或者你可以通过网站底部的邮件联系我

          2023年10月3日
          回复
      • abc

        @Alain 请问有解决吗,我也遇到同样的问题

        2023年11月25日
        回复
        • Alain

          @abc 元林遇到的问题是因为启用了https2https插件导致了冲突,你可以将配置、nginx日志发我邮件,我可以帮你看一下

          2023年11月25日
          回复
        • Alain

          @abc :confused: 还有你用的名字太马虎了

          2023年11月25日
          回复
      • abc

        @Alain 我也是开启了https2https插件,请问是什么冲突(起名字太麻烦了>>>)

        2023年11月25日
        回复
      • abc

        @Alain 去掉https2https插件后正常了,请问这是为什么,https2https插件和proxy protocol有什么冲突吗

        2023年11月25日
        回复
        • Alain

          @abc 我没有深入研究过FRP的原理,有空了可以抓包看看是不是启用了https2https会导致没有向后端服务器发送代理协议头部信息

          2023年11月25日
          回复
  • 取消回复

    文章目录
    • 前言
    • 需求
    • 测试环境
    • 方案
    • Frpc 配置
      • Frpc 代码配置
      • OpenWrt中配置Frpc
    • 群晖使用Proxy Protocol协议
    • 局域网与公网自动切换
    • 参考

    COPYRIGHT © 2022 Alain's Blog. ALL RIGHTS RESERVED.

    Theme Kratos Made By Seaton Jiang