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

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

  •  
  •   52txr · 4 天前 · 2986 次点击

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

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

    没必要搞那些 nginx rules 性能极差
    julyclyde
        11
    julyclyde  
       4 天前
    @zuotun 这种事还能猜啊?难道不是看看日志吗?
    263
        12
    263  
       4 天前
    你的域名( 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  
       4 天前   ❤️ 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  
       4 天前
    Cloudflare Tunnel
    yqs112358
        15
    yqs112358  
       3 天前
    那直接用 Cloudflare Tunnel 不是更好(
    ck65
        16
    ck65  
       3 天前 via iPhone
    我的办法简单粗暴,源站简单弄个 key ,没有 key 就 4xx 掉,甚至静态的 key 都可以只要复杂一点。CF 入口弄个 worker 转发请求,带上 key 即可。
    swiftg
        17
    swiftg  
       3 天前 via iPhone
    你要设置一个没有匹配到域名时候的默认 server ,通过 ip 直接访问,或者 hostname 匹配不到任何 server 的时候用

    而且,你应该是直接在防火墙里配置源 ip ,而不是 nginx 里
    00oo00
        18
    00oo00  
       3 天前 via Android
    tls 双向认证
    JensenQian
        19
    JensenQian  
       3 天前
    cf tunnel 隧道
    NekoNeko666
        20
    NekoNeko666  
       3 天前 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  
       3 天前 via iPhone
    Citrus
        22
    Citrus  
       3 天前 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  
       3 天前
    用 cf 代理了 DNS 后,通过域名访问有可能不走 CDN 吗?我的 Nginx 直接拒绝 IP 直接访问,只允许通过域名访问。这样不知道是否可行。
    Citrus
        24
    Citrus  
       3 天前 via iPhone
    @Hydrogen404 不可行,可能会被扫描出来。
    MFWT
        25
    MFWT  
       3 天前 via Android   ❤️ 1
    CF 回源支持 TLS 双向认证,没有客户端证书(只有 CF 有)访问会直接 400 ,再配合 iptables 锁回源 IP ,就很好了
    keengrass
        26
    keengrass  
       3 天前
    学习了
    beyondsoft
        27
    beyondsoft  
       3 天前
    进入 cloudflare , SSL/TLS 选项 生成 源服务器证书, nginx 关闭 80 端口, 仅开启 ssl 配置专属证书, 拒绝掉证书验证失败的请求就行了
    c15412
        28
    c15412  
       3 天前 via Android
    快进到 cf 的 ip 被墙,因为使用 cloudflare 导致用户无法访问
    cnurbansnail
        29
    cnurbansnail  
       2 天前
    直接通过 ip 访问 nginx 默认站点的 nginx 直接拒绝握手,可以过滤掉很多爬虫和扫描器

    除非域名 ip 同时泄露,客户端可以通过 ip:端口 + sni 绑定访问到你的站点
    一般泄露的可能性比较小,再加上某个人要专门搞你。
    0x5c0f
        30
    0x5c0f  
       2 天前
    简单一点的就是,cf 我记得是有自签名证书的, 给站点加一个 cf 的自签证书, 拒绝掉 http 的协议请求, 然后开启 http2 和 hsts ,基本就可以了
    v2ov
        31
    v2ov  
       2 天前
    14 楼正解 Cloudflare Tunnel 即可解决
    kingstou
        33
    kingstou  
       7 小时 59 分钟前
    创建
    /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   ·   实用小工具   ·   2887 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 12:34 · PVG 20:34 · LAX 04:34 · JFK 07:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.