0x01 前言

其实我本意并不是想区分国内外访客,只是我想使用又拍云的CDN服务分发静态文件。因此我购买了腾讯云的服务器并且完成备案,不用白不用,所以我区分了国内外访客。

因为购买了腾讯云的服务器,所以我干脆将cn域名指向该服务器,然后通过并不是很高明的技术手段区分访客,这样可以减轻香港服务器的负载,也可以尝试新技术。

其实我香港的服务器完全可以承载所有访问请求,每天的访客在200至300个IP左右,几乎是没人看的节奏,但这不是重点。

0x02 逻辑

首先是架构图:

当国内外的用户在浏览器中输入我博客的地址并会车,他们的操作系统会查询DNS以获取域名的DNS记录。

我域名的DNS托管在dnspod,而dnspod有一个免费区分国内外访客的功能,然后分别返还设定的DNS记录。而我也利用这个功能,给不同地域的用户返还cn1和hk1两台服务器的IP。

这里会有个问题,我希望国内的访客都使用enginx\.cn域名,国外访客都使用enginx\.net,但DNS只能返还DNS记录而不能跳转。

所以我还分别在两台服务器上使用nginx作301跳转,将通过其他域名进入我网站的访客都重定向到enginx\.cn或enginx\.net这两个域名。

完成跳转后服务器会返还网站源码给用户,浏览器解析源码的同时会从CDN服务器中下载静态文件。这样可以大大减少源站带宽的压力。

而cn1服务器则是hk1服务器的镜像站,通过nginx的反向代理功能实现相关服务。

0x03 dnspod

以下是分别使用国内网络与国外网络dig我域名的结果:

#国内用户
 dig ngx.hk

; <<>> DiG 9.8.3-P1 <<>> ngx.hk
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26772
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;ngx.hk.			IN	A

;; ANSWER SECTION:
ngx.hk.		600	IN	CNAME	cn1.c4.hk.
cn1.c4.hk.		600	IN	A	118.89.52.242

;; Query time: 107 msec
;; SERVER: 10.1.2.1#53(10.1.2.1)
;; WHEN: Tue Sep 12 15:27:41 2017
;; MSG SIZE  rcvd: 67

#国外用户
dig ngx.hk

; <<>> DiG 9.9.4-RedHat-9.9.4-50.el7_3.1 <<>> ngx.hk
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55912
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;ngx.hk.			IN	A

;; ANSWER SECTION:
ngx.hk.		1277	IN	CNAME	hk1.c4.hk.
hk1.c4.hk.		169	IN	A	103.15.217.210

;; Query time: 28 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Tue Sep 12 15:28:07 CST 2017
;; MSG SIZE  rcvd: 78

从上面结果可以看出,从DNS层面就区分了用户,将不同地域的用户指向了不同的服务器。以下是dnspod的配置:

enginx\.cn域名的DNS记录与.net的相类似。

0x04 301

将用户导向相应的服务器后,DNS的工作就完成了。以下都是我的域名:

ngx.hk
enginx.cn
enginx.org
enginx.uk
proj.org.cn

当国内用户通过非.cn域名访问时,cn1服务器将通过301重定向至enginx\.cn;当国外用户通过非.net域名访问时,hk1服务器将通过301重定向至enginx\.net。

而301重定向我是通过nginx实现的,因为只需要实现301重定向功能,所以配置信息较为简单。以下是enginx\.org域名跳转到enginx\.net的配置信息:

server {

    listen                  80;
    server_name             enginx.org www.enginx.org;

    return 301              https://ngx.hk$request_uri;

}

server {

    listen                  443 ssl;
    server_name             enginx.org www.enginx.org;

    ssl                     on;
    ssl_certificate         /usr/local/nginx/ssl/enginx.org.crt;
    ssl_certificate_key     /usr/local/nginx/ssl/enginx.org.key;
    ssl_buffer_size         16k;
    ssl_ciphers             ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:DES-CBC3-SHA;
    ssl_prefer_server_ciphers   on;
    ssl_protocols           TLSv1 TLSv1.1 TLSv1.2;
    ssl_session_cache       builtin:20480 shared:SSL:10m;
    ssl_session_timeout     3h;
    ssl_stapling            on;
    ssl_session_tickets     on;
    add_header              X-Content-Type-Options nosniff;
    add_header              X-XSS-Protection "1; mode=block";
    add_header              Strict-Transport-Security "max-age=31536000; preload; includeSubDomains" always;
    add_header 		    Public-Key-Pins 'pin-sha256="YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg="; pin-sha256="klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY="; max-age=2592000; includeSubDomains';

    return 301 https://ngx.hk$request_uri;

}

其他域名也类似,在这需要顾及http协议的访客。我的网站使用顶级域名作为主域名,而没有添加www,所以也需要顾及使用www的访客。

至此,所有工作均已完成。

0x05 其他

因为CDN可以设置IP+host的方式回源,所以我使用的又拍云CDN不受此规则影响:

nginx后面是apache、php-fpm与mariadb,nginx前面还有一个waf。这些内容我以后再写文章详细说明。

0x06 结语

我最近在寻找好一点的DNS服务商,我想使用CAA这个功能。但近几天了解的几家服务商都不支持根据地域区分返还DNS记录。

如果不行,那只好使用nginx通过geoip数据库区分了。