Skip to content

当我们源站只在一个地区,但 边缘用户/CDN 直接回源因为公网不确定因素导致回源不稳定的时候,这时我们需要考虑如何让 “源站” 更贴近用户;

于是可以在异地设置一个边缘节点,然后通过专线或相较公网更稳定的方式回源,于是有了如下设置

upstream source_server {
    # 可选ip_hash,保障客户端原进原出
    ip_hash;
    server 10.20.30.40:80;
    server 172.17.50.60:80;
    keepalive 30;
}

server {
    listen    80;
    listen    443 ssl http2;
    server_name ~^.*\.nestealin\.com$;
    charset utf-8;
    access_log  logs/$host.access.main.log main;
    error_log  logs/all.nestealin.com.error.crit.log crit;

    ssl_certificate      /data/keys/server.cer;
    ssl_certificate_key  /data/keys/server.key;
    ssl_session_cache shared:SSL:30m;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ecdh_curve X25519:P-256:P-384;
    ssl_ciphers TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305:EECDH+CHACHA20:EECDH+AES128;

    location / {
        proxy_next_upstream error timeout http_503 http_504 http_502;
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $clientRealIp;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-Port $remote_port;
        proxy_connect_timeout 120s;
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_pass http://source_server;
    }

}
upstream source_server {
    # 可选ip_hash,保障客户端原进原出
    ip_hash;
    server 10.20.30.40:80;
    server 172.17.50.60:80;
    keepalive 30;
}

server {
    listen    80;
    listen    443 ssl http2;
    server_name ~^.*\.nestealin\.com$;
    charset utf-8;
    access_log  logs/$host.access.main.log main;
    error_log  logs/all.nestealin.com.error.crit.log crit;

    ssl_certificate      /data/keys/server.cer;
    ssl_certificate_key  /data/keys/server.key;
    ssl_session_cache shared:SSL:30m;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ecdh_curve X25519:P-256:P-384;
    ssl_ciphers TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305:EECDH+CHACHA20:EECDH+AES128;

    location / {
        proxy_next_upstream error timeout http_503 http_504 http_502;
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $clientRealIp;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-Port $remote_port;
        proxy_connect_timeout 120s;
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_pass http://source_server;
    }

}

先在CDN节点上修改Hosts,目的就是告知CDN节点从那里去获取网站数据,也就是回源地址,修改如下:

vi /etc/hosts
192.168.1.100	www.xxx.com
vi /etc/hosts
192.168.1.100	www.xxx.com

格式是"网站所在服务器IP 需要使用CDN域名"

智能解析,DNSPOD

如果你还需要更细分的,按地区的(细分到华南、华北、西南等等)DNS,可以试试华为云DNS

反向代理配置

反向代理通俗点你把它理解成CDN节点就行了,这里用4台服务器作为解释,

  • 源站:192.168.1.100,就是网站数据真实存放的地方
  • CDN1:192.168.1.101(电信节点)
  • CDN2:192.168.1.102(联通节点)
  • CDN3:192.168.1.103(移动节点)

假如我需要对www.xxx.me搭建CDN节点,数据放在192.168.1.100,需要先修改hosts指向,告知CDN节点从那里去获取网站数据,也就是回源地址,需要在CDN1/CDN2/CDN3做如下修改:

vi /etc/hosts
192.168.1.100   www.xxx.me
vi /etc/hosts
192.168.1.100   www.xxx.me
  • 配置
proxy_cache_path /data/wwwroot/caches/www.xiaoz.me levels=1:2 keys_zone=xiaoz:50m inactive=30m max_size=50m;
server {
    listen 443 ssl http2;
    ssl_certificate /data/ssl/www.xiaoz.me/www_xiaoz_me.crt;
    ssl_certificate_key /data/ssl/www.xiaoz.me/www_xiaoz_me.key;
    ssl_session_timeout 1d;
    ssl_session_cache builtin:1000 shared:SSL:10m;
    ssl_dhparam /data/ssl/dhparam.pem;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
    ssl_prefer_server_ciphers on;

    ssl_stapling on;
    ssl_stapling_verify on;

    server_name www.xxx.me;
    access_log /data/wwwlogs/xiaoz.me_nginx.log combined;

    charset utf-8,gbk;
        location / {
        proxy_set_header Accept-Encoding "";
           proxy_pass https://www.xxx.me;
           proxy_redirect off;
           proxy_set_header X-Real-IP $remote_addr;
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
           proxy_cache xiaoz;
           proxy_cache_valid  200 304  30m;
           proxy_cache_valid  301 24h;
           proxy_cache_valid  500 502 503 504 0s;
           proxy_cache_valid any 1s;
           proxy_cache_min_uses 1;
           expires 12h;
    }
}
server {
    listen 80 default_server;
    return 301 https://$host$request_uri;
}
proxy_cache_path /data/wwwroot/caches/www.xiaoz.me levels=1:2 keys_zone=xiaoz:50m inactive=30m max_size=50m;
server {
    listen 443 ssl http2;
    ssl_certificate /data/ssl/www.xiaoz.me/www_xiaoz_me.crt;
    ssl_certificate_key /data/ssl/www.xiaoz.me/www_xiaoz_me.key;
    ssl_session_timeout 1d;
    ssl_session_cache builtin:1000 shared:SSL:10m;
    ssl_dhparam /data/ssl/dhparam.pem;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
    ssl_prefer_server_ciphers on;

    ssl_stapling on;
    ssl_stapling_verify on;

    server_name www.xxx.me;
    access_log /data/wwwlogs/xiaoz.me_nginx.log combined;

    charset utf-8,gbk;
        location / {
        proxy_set_header Accept-Encoding "";
           proxy_pass https://www.xxx.me;
           proxy_redirect off;
           proxy_set_header X-Real-IP $remote_addr;
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
           proxy_cache xiaoz;
           proxy_cache_valid  200 304  30m;
           proxy_cache_valid  301 24h;
           proxy_cache_valid  500 502 503 504 0s;
           proxy_cache_valid any 1s;
           proxy_cache_min_uses 1;
           expires 12h;
    }
}
server {
    listen 80 default_server;
    return 301 https://$host$request_uri;
}

智能解析

域名智能解析是指域名解析服务器根据来访者的 IP 类型,对同一域名作出相应不同解析。例如:对 IP 来自电信的访问者,将域名解析到该域名对应 IP 地址为电信的服务器上;对 IP来自联通的访问者,将域名解析到该域名对应 IP 地址为联通的服务器上;对 IP 来自香港的访问者,将域名解析到该域名对应 IP 地址为香港的服务器上;以保证访问者不因电信线路瓶颈而造成联通、香港的访客无法访问

image-20220323175628354

cdn工作流

1029177661

如上图所示,这是一个完整的流程图:客户端访问域名时,先向 DNS 请求域名 IP,DNS 查询到 CNAME 记录(如果没有 DNS 直接回复源服务器 IP),则进一步解析 CNAME 智能解析服务器, 智能解析收到请求根据客户端来源按规则判断并回复 CDN 节点 IP,客户端此时访问域名就连接到了回复的 CDN 节点,如果 CDN 节点没有缓存,则 CDN 就会发起连接到负载均衡器(如果没有则直接连接源服务器,一般在有多个源服务器后端时才会有负载均衡器),然后负载均衡器根据规则分流到源服务器,将内容返回给 CDN 节点,CDN 节点再返回给客户端,完成整个访问流程

源站IP泄漏

防止CDN后的源站IP泄漏的几种方法

ssl_reject_handshake

Nginx 版本高于等于 1.19.4,才可以使用 ssl_reject_handshake 特性来防止 SNI 信息泄露

#新添加的443端口块,如果使用了错误的 Hostname,SSL 握手会被拒绝
server {
    listen 443 ssl default_server;
    #如果有IPv6地址需加入下面这行,否则不用下面这行
    listen [::]:443 ssl default_server;
    ssl_reject_handshake on;
}

#常规的443端口,包含正确的域名和证书。对于携带正确 Hostname 的请求,服务器会继续做后续处理
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com;
    ssl_certificate example.com.crt;
    ssl_certificate_key example.com.key;
}

上面插件只适用于 443 端口不适用其它端口
#新添加的443端口块,如果使用了错误的 Hostname,SSL 握手会被拒绝
server {
    listen 443 ssl default_server;
    #如果有IPv6地址需加入下面这行,否则不用下面这行
    listen [::]:443 ssl default_server;
    ssl_reject_handshake on;
}

#常规的443端口,包含正确的域名和证书。对于携带正确 Hostname 的请求,服务器会继续做后续处理
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com;
    ssl_certificate example.com.crt;
    ssl_certificate_key example.com.key;
}

上面插件只适用于 443 端口不适用其它端口

IP 白名单

添加cloudflare ips-v4 iptables 白名单的命令

for i in `curl https://www.cloudflare.com/ips-v4`;
    do iptables -I INPUT -p tcp -m multiport --dports http,https -s $i -j ACCEPT;
done
for i in `curl https://www.cloudflare.com/ips-v4`;
    do iptables -I INPUT -p tcp -m multiport --dports http,https -s $i -j ACCEPT;
done

添加cloudflare ips-v6 iptables 白名单的命令

for i in `curl https://www.cloudflare.com/ips-v6`;
    do ip6tables -I INPUT -p tcp -m multiport --dports http,https -s $i -j ACCEPT;
done
for i in `curl https://www.cloudflare.com/ips-v6`;
    do ip6tables -I INPUT -p tcp -m multiport --dports http,https -s $i -j ACCEPT;
done

丢弃白名单以外的 ipv4 80,443 tcp 包:

iptables -A INPUT -p tcp -m multiport --dports http,https -j DROP
iptables -A INPUT -p tcp -m multiport --dports http,https -j DROP

丢弃白名单以外的 ipv6 80,443 tcp 包

ip6tables -A INPUT -p tcp -m multiport --dports http,https -j DROP
ip6tables -A INPUT -p tcp -m multiport --dports http,https -j DROP

3.

爬虫的 HTTP 版本基本都是 1.1

#限制http版本号,只允许下面3个HTTP版本
if ($server_protocol !~* "HTTP/2.0|HTTP/3.0|SPDY/3.1") {
    return 403;
}
#限制http版本号,只允许下面3个HTTP版本
if ($server_protocol !~* "HTTP/2.0|HTTP/3.0|SPDY/3.1") {
    return 403;
}