在站点加上CDN之后,通过Nginx设置只允许CDN回源节点访问的情况下,并获得访客的真实IP

ShowUNow 发布于 11 天前 91 次阅读


写在前面

众所周知,国内CDN的回源IP非常的多,拿腾讯云CDN来举例:

只看这个恐怖的滑动条,就知道回源IP超级多,实际测下来,有足足300多条。这样一个个往服务器的安全组里添加,一点也不现实。而且,很多云服务器的安全组也不支持添加这么多条安全组规则。但是如果不加限制,就可能有人通过修改hosts文件来绕过我们的CDN。于是,我们可以通过配置Nginx来实现仅允许CDN回源IP来访问我们的源站。

本文演示环境

操作系统:Debian11

Nginx 1.18.0

腾讯云EdgeOne(CDN)服务,并且回源协议为HTTPS

一、将所有CDN回源IP保存在文件中

你可以将CDN回源IP保存在名为allowed_ips.conf 的文件中。

nano /etc/nginx/allowed_ips.conf

二、填写nginx配置文件

# 用于内部转发的 upstream
upstream backend {
    server 127.0.0.1:8443;  # 内部端口,需与第二个 server 的 listen 对应
}

# HTTP 到 HTTPS 重定向
server {
    listen 80;
    server_name yourdomain.com;
    return 301 https://tingyun.top$request_uri;
}

# 第一个 server 块:检查直接连接的 IP(CDN 回源 IP)
server {
    listen 443 ssl http2;
    server_name yourdomain.com;

    # SSL 证书配置(保持与原配置一致)
    ssl_certificate /path/to/your/fullchain.pem; # 替换为你的SSL证书路径
    ssl_certificate_key /path/to/your/domain.com.key; # 替换为你的SSL证书密钥
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # 设置白名单 IP(针对 CDN 回源 IP)
    include /etc/nginx/allowed_ips.conf;  # 这里定义了 allow 和 deny 规则

    # 转发到内部 upstream
    location / {
        proxy_pass https://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;  # 传递原始 IP
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header REMOTE-HOST $remote_addr;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_http_version 1.1;
        proxy_ssl_server_name on;
    }
}

# 第二个 server 块:提取真实 IP 并处理后续逻辑
server {
    listen 8443 ssl http2;
    server_name localhost;  # 内部使用 localhost

    # SSL 证书配置(与第一个 server 相同)
    ssl_certificate /path/to/your/fullchain.pem; # 替换为你的SSL证书路径
    ssl_certificate_key /path/to/your/yourdomain.key;# 替换为你的SSL证书密钥
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # CDN 回源 IP 和真实 IP 提取
    set_real_ip_from 0.0.0.0/0;
    #real_ip_header X-Forwarded-For;  # 默认使用 X-Forwarded-For
    real_ip_recursive on;

    client_max_body_size 64M;

    # 代理到后端
    location / {
        proxy_pass https://alibackhost;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;  # 此时是真实客户端 IP
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header REMOTE-HOST $remote_addr;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_http_version 1.1;
        add_header X-Cache $upstream_cache_status;
        add_header Cache-Control no-cache;
        proxy_ssl_server_name on;
    }
}

这篇配置的逻辑就是:
1. 先对直接连接 Nginx 的 IP(例如 CDN 的回源 IP)应用 allow 和 deny 规则,这些规则在 /etc/nginx/allowed_ips.conf 中。

2. 然后提取真实客户端 IP(通过 real_ip 模块),并基于真实 IP 进行后续处理。

目前的配置中,include /etc/nginx/allowed_ips.conf include /etc/nginx/real_ip.conf 是在同一个 server 块中定义的。由于 ngx_http_realip_module 的处理阶段早于 access 阶段(allow 和 deny 的执行阶段),直接连接的 IP 会被真实 IP 替换,导致无法单独对 CDN 回源 IP 进行限制。

使用多层 server 块分离逻辑,我们可以将逻辑拆分为两个 server 块:

第二个 server 块:提取真实 IP并处理后续逻辑。

第一个 server 块:检查直接连接的 IP(CDN 回源 IP),应用 allow 和 deny。

三、 测试

我们可以在浏览器中访问站点域名,或者在Linux 命令行中使用 curl -I https://yourdomain.com :

返回200状态码,可以正常访问。

之后我们通过修改 /etc/hosts 文件,将我们的域名强制指向源站IP再次访问:

可以看到返回403错误代码,此时配置就已生效!