V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Recommended Services
Amazon Web Services
LeanCloud
New Relic
ClearDB
52txr
V2EX  ›  云计算

希望用户只能通过 Cloudflare CDN 访问网站,你们怎么配置 Nginx 的?

  •  
  •   52txr · 19 天前 · 3476 次点击

    我希望用户只能通过 Cloudflare CDN 访问网站,并且直接访问源站时会返回 403 Forbidden 。

    在 Nginx 配置文件中,我的配置大概如下:

    server {
        listen 80;
        server_name example.com;  # 替换为你的域名
    
        location / {
            # 允许 Cloudflare 的 IPv4 段
            allow 103.21.244.0/22;
            allow 103.22.200.0/22;
            allow 103.31.4.0/22;
            allow 104.16.0.0/12;
            allow 108.162.192.0/18;
            allow 131.0.72.0/22;
            allow 141.101.64.0/18;
            allow 162.158.0.0/15;
            allow 172.64.0.0/13;
            allow 173.245.48.0/20;
            allow 188.114.96.0/20;
            allow 190.93.240.0/20;
            allow 197.234.240.0/22;
            allow 198.41.128.0/17;
    
            # 允许 Cloudflare 的 IPv6 段(如果启用 IPv6 )
            allow 2400:cb00::/32;
            allow 2606:4700::/32;
            allow 2803:f800::/32;
            allow 2405:b500::/32;
            allow 2405:8100::/32;
            allow 2a06:98c0::/29;
            allow 2c0f:f248::/32;
    
            # 拒绝所有其他 IP
            deny all;
    
            # 正常的请求处理
            try_files $uri $uri/ =404;
        }
    }
    
    

    按理说我觉得使用直接配置后使用访问网站,应该正常显示。但是实际上是 403.

    我是参考下面几个链接配置的:

    [已完成] 怎样 只允许指定 IP 和域名访问网站: https://www.bt.cn/bbs/thread-72524-1-1.html

    Nginx 设置只允许来自 Cloudflare CDN 的 IP 访问的方法 宝塔 NGINX 网站只允许 CF IP 访问方法: https://bnxb.com/nginx/27638.html

    33 条回复    2024-12-11 12:35:14 +08:00
    julyclyde
        1
    julyclyde  
       19 天前
    那你看看 nginx 的日志,这个请求是从哪儿来的
    yaocf
        2
    yaocf  
       19 天前
    ```shell
    #!/bin/sh
    set -o pipefail

    export _ipv4Range=`curl --retry 10 --retry-delay 5 -s https://www.cloudflare-cn.com/ips-v4`
    if [[ $? -ne 0 ]]; then
    export _ipv4Range=`curl --retry 10 --retry-delay 5 -s https://www.cloudflare.com/ips-v4`
    if [[ $? -ne 0 ]]; then
    exit 1
    fi
    fi

    export _ipv6Range=`curl --retry 10 --retry-delay 5 -s https://www.cloudflare-cn.com/ips-v6`
    if [[ $? -ne 0 ]]; then
    export _ipv6Range=`curl --retry 10 --retry-delay 5 -s https://www.cloudflare.com/ips-v6`
    if [[ $? -ne 0 ]]; then
    exit 1
    fi
    fi

    #nginx real_ip
    _tmpConf="/tmp/._$(date '+%Y-%m-%d-%H-%M')-$(cat /proc/sys/kernel/random/uuid).txt"
    _target='/etc/nginx/conf.d/common/cloudflare/_real_ip_from-ip-set.conf'
    {
    echo "# Generated by ${0}"
    echo "# At $(date)"
    echo "# IPV4"
    for i in ${_ipv4Range} ;do echo "set_real_ip_from ${i};" ;done
    echo ""
    echo "# IPV6"
    for i in ${_ipv6Range} ;do echo "set_real_ip_from ${i};" ;done
    echo ""
    } |tee ${_tmpConf}
    if [[ $? -eq 0 ]]; then
    mv "${_tmpConf}" "${_target}"

    nginx -t
    if [[ $? -eq 0 ]]; then
    echo "Nginx 配置正常,重载配置!"
    nginx -s reload
    else
    /app/scripts/bin/gotify.sh --title "${_target} 文件更新成功,但 Nginx 配置有误" "请修复后手动重载 Nginx !\n""nginx -s reload"
    fi
    fi

    #nginx allow-ip-set
    #sleep 1
    _tmpConf="/tmp/._$(date '+%Y-%m-%d-%H-%M')-$(cat /proc/sys/kernel/random/uuid).txt"
    _target='/etc/nginx/conf.d/common/cloudflare/_allow-ip-set.conf'
    {
    echo "# Generated by ${0}"
    echo "# At $(date)"
    echo "# IPV4"
    for i in ${_ipv4Range} ;do echo "allow ${i};" ;done
    echo ""
    echo "# IPV6"
    for i in ${_ipv6Range} ;do echo "allow ${i};" ;done
    echo ""
    } |tee ${_tmpConf}
    if [[ $? -eq 0 ]]; then
    mv "${_tmpConf}" "${_target}"

    nginx -t
    if [[ $? -eq 0 ]]; then
    echo "Nginx 配置正常,重载配置!"
    nginx -s reload
    else
    /app/scripts/bin/gotify.sh --title "${_target} 文件更新成功,但 Nginx 配置有误" "请修复后手动重载 Nginx !\n""nginx -s reload"
    fi
    fi

    ```

    我这边用来更新 allow ip 配置文件的脚本
    herozzm
        3
    herozzm  
       19 天前
    不能这样设置吧,从 cf 过来的是客户端的 ip 噢
    我是设置了一个很长域名,仅给 cf 回源用,不泄露
    xfelix
        4
    xfelix  
       19 天前 via iPhone   ❤️ 1
    不理解为什么要这样设置 nginx 。直接设
    防火墙 iptable 之类只允许 cf ip 段不就行了。
    zuotun
        5
    zuotun  
       19 天前   ❤️ 1
    不是非要返回 403 的话,防火墙开白名单,用 CF 的 Origin 证书开 Full Strict 模式,这样配完后是绝对不能够直接访问源站的。
    我的理解是 Nginx 拿到的是客户端的真实 IP ,否则日志里岂不全是 CF 的了,这要怎么溯源?
    Ipsum
        7
    Ipsum  
       19 天前
    Ipt 直接 drop 不行吗?
    realpg
        8
    realpg  
       19 天前
    网站只跑在 ipv6 上即可,不在 ipv4 上
    XIU2
        9
    XIU2  
       19 天前
    直接在防火墙里限制 80 443 端口只允许 Cloudflare CDN 的 IP 访问即可,一直都是这样干的,而且效率更高。
    realpg
        10
    realpg  
       19 天前   ❤️ 1
    如果你的服务器是有一个路由过去的哪怕/64 块
    只要让本站虚拟主机只监听其中一个独立的 ipv6 地址
    不要用省略号,自己构造一个块内随机的 ipv6 单播地址

    然后 cf 不解析 A 记录,只解析唯一 AAAA 记录只从 ipv6 回源
    ipv6 网络下除非你其他方式泄露,根本没人找到的你这个源站
    没有扫描 没有探测 也不会有人能直接连上 除了你自己

    没必要搞那些 nginx rules 性能极差
    julyclyde
        11
    julyclyde  
       19 天前
    @zuotun 这种事还能猜啊?难道不是看看日志吗?
    263
        12
    263  
       19 天前
    你的域名( example.com )如果只解析到了 Cloudflare CDN 的 cname ,你只需要限制默认主机名。

    如果有内网访问需求,server_name 需要加上自己的内网主机名或者 ip 。

    下面的配置:

    server {
    listen 80 default_server;
    server_name _;
    return 444;
    }

    server {
    listen 443 default_server;
    server_name _;
    ssl_certificate /etc/nginx/ssl/xxx.com.pem;
    ssl_certificate_key /etc/nginx/ssl/xxx.com.key;
    return 444;
    }
    cnt2ex
        13
    cnt2ex  
       19 天前   ❤️ 3
    我记得 set_real_ip_from 就没法和 allow 一起使用。如果使用了 set_real_ip_from ,ip 会被改成客户端的 IP ,从而导致 allow 看到的 IP 也是客户端的 IP ,然后就会 403 。

    所以我都是在 nftable 配置 80 、443 端口只允许 cf 的 ip ,然后 nginx 里配置 set_real_ip_from
    wheat0r
        14
    wheat0r  
       19 天前
    Cloudflare Tunnel
    yqs112358
        15
    yqs112358  
       19 天前
    那直接用 Cloudflare Tunnel 不是更好(
    ck65
        16
    ck65  
       19 天前 via iPhone
    我的办法简单粗暴,源站简单弄个 key ,没有 key 就 4xx 掉,甚至静态的 key 都可以只要复杂一点。CF 入口弄个 worker 转发请求,带上 key 即可。
    swiftg
        17
    swiftg  
       19 天前 via iPhone
    你要设置一个没有匹配到域名时候的默认 server ,通过 ip 直接访问,或者 hostname 匹配不到任何 server 的时候用

    而且,你应该是直接在防火墙里配置源 ip ,而不是 nginx 里
    00oo00
        18
    00oo00  
       19 天前 via Android
    tls 双向认证
    JensenQian
        19
    JensenQian  
       19 天前
    cf tunnel 隧道
    NekoNeko666
        20
    NekoNeko666  
       18 天前 via iPhone
    CloudFlare tunnel 然后拒绝任何 http ,https 端口访问,tunnel 直接和 CF 链接

    或者,用 ufw ,允许 ssh ,只允许 CF ip 段的 http 和 https

    sudo ufw allow ssh

    # Allow HTTP (port 80) for Cloudflare IPv4 ranges
    sudo ufw allow from 173.245.48.0/20 to any port 80
    sudo ufw allow from 103.21.244.0/22 to any port 80
    sudo ufw allow from 103.22.200.0/22 to any port 80
    sudo ufw allow from 103.31.4.0/22 to any port 80
    sudo ufw allow from 141.101.64.0/18 to any port 80
    sudo ufw allow from 108.162.192.0/18 to any port 80
    sudo ufw allow from 190.93.240.0/20 to any port 80
    sudo ufw allow from 188.114.96.0/20 to any port 80
    sudo ufw allow from 197.234.240.0/22 to any port 80
    sudo ufw allow from 198.41.128.0/17 to any port 80
    sudo ufw allow from 162.158.0.0/15 to any port 80
    sudo ufw allow from 104.16.0.0/12 to any port 80
    sudo ufw allow from 172.64.0.0/13 to any port 80
    sudo ufw allow from 131.0.72.0/22 to any port 80

    # Allow HTTPS (port 443) for Cloudflare IPv4 ranges
    sudo ufw allow from 173.245.48.0/20 to any port 443
    sudo ufw allow from 103.21.244.0/22 to any port 443
    sudo ufw allow from 103.22.200.0/22 to any port 443
    sudo ufw allow from 103.31.4.0/22 to any port 443
    sudo ufw allow from 141.101.64.0/18 to any port 443
    sudo ufw allow from 108.162.192.0/18 to any port 443
    sudo ufw allow from 190.93.240.0/20 to any port 443
    sudo ufw allow from 188.114.96.0/20 to any port 443
    sudo ufw allow from 197.234.240.0/22 to any port 443
    sudo ufw allow from 198.41.128.0/17 to any port 443
    sudo ufw allow from 162.158.0.0/15 to any port 443
    sudo ufw allow from 104.16.0.0/12 to any port 443
    sudo ufw allow from 172.64.0.0/13 to any port 443
    sudo ufw allow from 131.0.72.0/22 to any port 443

    # Allow HTTP (port 80) for Cloudflare IPv6 ranges
    sudo ufw allow from 2400:cb00::/32 to any port 80
    sudo ufw allow from 2606:4700::/32 to any port 80
    sudo ufw allow from 2803:f800::/32 to any port 80
    sudo ufw allow from 2405:b500::/32 to any port 80
    sudo ufw allow from 2405:8100::/32 to any port 80
    sudo ufw allow from 2a06:98c0::/29 to any port 80
    sudo ufw allow from 2c0f:f248::/32 to any port 80

    # Allow HTTPS (port 443) for Cloudflare IPv6 ranges
    sudo ufw allow from 2400:cb00::/32 to any port 443
    sudo ufw allow from 2606:4700::/32 to any port 443
    sudo ufw allow from 2803:f800::/32 to any port 443
    sudo ufw allow from 2405:b500::/32 to any port 443
    sudo ufw allow from 2405:8100::/32 to any port 443
    sudo ufw allow from 2a06:98c0::/29 to any port 443
    sudo ufw allow from 2c0f:f248::/32 to any port 443

    sudo ufw deny 80
    sudo ufw deny 443

    sudo ufw enable

    或者 iptables

    或者,用你云服务商的防火墙设置,允许 cf 的 ip 段
    NekoNeko666
        21
    NekoNeko666  
       18 天前 via iPhone
    Citrus
        22
    Citrus  
       18 天前 via iPhone
    大概率是 set_real_ip_from 的问题。刚好我也研究了这个问题。
    set_real_ip_from 执行阶段在 allow deny 之前,所以 allow deny 拿到的实际是 set_real_ip_from 处理后的真实客户端 IP ,而不是 CF 的 IP 。
    但是,set_real_ip_from 其实会保留原始 IP ,在变量 $realip_remote_addr 中。所以我们可以利用这个变量曲线救国。

    先使用 geo 模块,给原始 IP 打标:
    geo $realip_remote_addr $is_cf {
    default 0;
    # Cloudflare IPv4 Allow
    173.245.48.0/20 1;
    103.21.244.0/22 1;
    103.22.200.0/22 1;
    103.31.4.0/22 1;
    141.101.64.0/18 1;
    108.162.192.0/18 1;
    190.93.240.0/20 1;
    188.114.96.0/20 1;
    197.234.240.0/22 1;
    198.41.128.0/17 1;
    162.158.0.0/15 1;
    104.16.0.0/13 1;
    104.24.0.0/14 1;
    172.64.0.0/13 1;
    131.0.72.0/22 1;

    # Cloudflare IPv6 Allow
    2400:cb00::/32 1;
    2606:4700::/32 1;
    2803:f800::/32 1;
    2405:b500::/32 1;
    2405:8100::/32 1;
    2a06:98c0::/29 1;
    2c0f:f248::/32 1;
    }

    然后在需要的地方,直接根据标记断连:
    if ($is_cf = 0) {
    return 444;
    }
    Hydrogen404
        23
    Hydrogen404  
       18 天前
    用 cf 代理了 DNS 后,通过域名访问有可能不走 CDN 吗?我的 Nginx 直接拒绝 IP 直接访问,只允许通过域名访问。这样不知道是否可行。
    Citrus
        24
    Citrus  
       18 天前 via iPhone
    @Hydrogen404 不可行,可能会被扫描出来。
    MFWT
        25
    MFWT  
       18 天前 via Android   ❤️ 1
    CF 回源支持 TLS 双向认证,没有客户端证书(只有 CF 有)访问会直接 400 ,再配合 iptables 锁回源 IP ,就很好了
    keengrass
        26
    keengrass  
       18 天前
    学习了
    beyondsoft
        27
    beyondsoft  
       18 天前
    进入 cloudflare , SSL/TLS 选项 生成 源服务器证书, nginx 关闭 80 端口, 仅开启 ssl 配置专属证书, 拒绝掉证书验证失败的请求就行了
    c15412
        28
    c15412  
       18 天前 via Android
    快进到 cf 的 ip 被墙,因为使用 cloudflare 导致用户无法访问
    cnurbansnail
        29
    cnurbansnail  
       17 天前
    直接通过 ip 访问 nginx 默认站点的 nginx 直接拒绝握手,可以过滤掉很多爬虫和扫描器

    除非域名 ip 同时泄露,客户端可以通过 ip:端口 + sni 绑定访问到你的站点
    一般泄露的可能性比较小,再加上某个人要专门搞你。
    0x5c0f
        30
    0x5c0f  
       17 天前
    简单一点的就是,cf 我记得是有自签名证书的, 给站点加一个 cf 的自签证书, 拒绝掉 http 的协议请求, 然后开启 http2 和 hsts ,基本就可以了
    v2ov
        31
    v2ov  
       17 天前
    14 楼正解 Cloudflare Tunnel 即可解决
    kingstou
        33
    kingstou  
       15 天前
    创建
    /www/allow-cloudflare-only.conf
    内容
    # IPv4
    allow 173.245.48.0/20;
    allow 103.21.244.0/22;
    allow 103.22.200.0/22;
    allow 103.31.4.0/22;
    allow 141.101.64.0/18;
    allow 108.162.192.0/18;
    allow 190.93.240.0/20;
    allow 188.114.96.0/20;
    allow 197.234.240.0/22;
    allow 198.41.128.0/17;
    allow 162.158.0.0/15;
    allow 104.16.0.0/13;
    allow 104.24.0.0/14;
    allow 172.64.0.0/13;
    allow 131.0.72.0/22;
    # IPv6
    allow 2400:cb00::/32;
    allow 2606:4700::/32;
    allow 2803:f800::/32;
    allow 2405:b500::/32;
    allow 2405:8100::/32;
    allow 2a06:98c0::/29;
    allow 2c0f:f248::/32;

    deny all; # deny all remaining ips
    在 nginx 引用
    include /www/allow-cloudflare-only.conf
    这样就不会报 403 了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1248 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 18:01 · PVG 02:01 · LAX 10:01 · JFK 13:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.