Nginx配置白名单
geo指令是通过ngx_http_geo_module模块提供的。默认情况下,nginx安装时是会自动加载这个模块,除非安装时人为的手动添加--without-http_geo_module。
ngx_http_geo_module模块可以用来创建变量,其值依赖于客户端IP地址。
geo指令
语法: geo [$address] $variable { ... }
默认值: —
配置段: http
定义从指定的变量获取客户端的IP地址。默认情况下,nginx从$remote_addr变量取得客户端IP地址,但也可以从其他变量获得。
例如:
geo $remote_addr $geo {
default 0;
127.0.0.1 1;
}
geo $arg_cc_com $geo {
default 0;
127.0.0.1 1;
}
如果该变量的值不能代表一个合法的IP地址,那么nginx将使用地址"255.255.255.255"。
nginx通过CIDR或者地址段来描述地址,支持下面几个参数:
1)delete:删除指定的网络
2)default:如果客户端地址不能匹配任意一个定义的地址,nginx将使用此值。 如果使用CIDR,可以用"0.0.0.0/0"代替default。
3)include: 包含一个定义地址和值的文件,可以包含多个。
4)proxy:定义可信地址。 如果请求来自可信地址,nginx将使用其“X-Forwarded-For”头来获得地址。 相对于普通地址,可信地址是顺序检测的。
5)proxy_recursive:开启递归查找地址。 如果关闭递归查找,在客户端地址与某个可信地址匹配时,nginx将使用"X-Forwarded-For"中的最后一个地址来代替原始客户端地址。如果开启递归查找,在客户端地址与某个可信地址匹配时,nginx将使用"X-Forwarded-For"中最后一个与所有可信地址都不匹配的地址来代替原始客户端地址。
6)ranges:使用以地址段的形式定义地址,这个参数必须放在首位。为了加速装载地址库,地址应按升序定义。
geo $country {
default ZZ;
include conf/geo.conf;
delete 127.0.0.0/16;
proxy 192.168.100.0/24;
proxy 2001:0db8::/32;
127.0.0.0/24 US;
127.0.0.1/32 RU;
10.1.0.0/16 RU;
192.168.1.0/24 UK;
}
# vim conf/geo.conf //编辑conf/geo.cong文件 10.2.0.0/16 RU; 192.168.2.0/24 RU;
地址段例子:
geo $country {
ranges;
default ZZ;
127.0.0.0-127.0.0.0 US;
127.0.0.1-127.0.0.1 RU;
127.0.0.1-127.0.0.255 US;
10.1.0.0-10.1.255.255 RU;
192.168.1.0-192.168.1.255 UK;
}
geo指令主要是根据IP来对变量进行赋值的。因此geo块下只能定义IP或网络段,否则会报错。 安装完成之后,GeoIP数据库会被安装在/usr/share/GeoIP/GeoIP.dat.
yum -y install geoip-devel ls /usr/share/GeoIP/GeoIP.dat /usr/share/GeoIP/GeoIP.dat
配置openresty支持Geoip
vim /usr/local/nginx/conf/nginx.conf
....
http {
geoip_country /usr/share/GeoIP/GeoIP.dat;
map $geoip_country_code $allowed_country {
default yes;
US no; # 国家 no就是不允许哪个国际访问
}
....
server{
....
if ($allowed_country = no) { # 添加判断,如果访问国家=no,就返回404
return 404;
}
...
}
http {
geo $remote_addr $geo {
default 0; #0表示禁止访问
127.0.0.1 1; #1表示可以访问
}
...
}
server {
...
location / {
# 如果不是白名单则 显示403 禁止访问
if ( $geo = 0 ) {
return 403;
}
...
}
nginx利用geo模块做限速白名单
nginx的限速白名单需要结合geo和map指令来实现,map指令使用ngx_http_map_module模块提供的。默认情况下,nginx安装时是会自动加载这个模块,除非安装时人为手动添加--without-http_map_module。
ngx_http_map_module模块可以创建变量,这些变量的值与另外的变量值相关联。允许分类或者同时映射多个值到多个不同值并储存到一个变量中,map指令用来创建变量,但是仅在变量被接受的时候执行视图映射操作,对于处理没有引用变量的请求时,这个模块并没有性能上的缺失。
geo $limit {
default 1;
10.0.0.0/8 0;
192.168.0.0/24 0;
}
map $limit $limit_key {
0 "";
1 $binary_remote_addr;
}
limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;
server {
location / {
limit_req zone=req_zone burst=10 nodelay;
# ...
}
}
这个例子同时使用了geo和map指令。对于IP地址在白名单中的,geo块分配0值给$limit;其它所有不在白名单中的IP地址,分配1值。然后我们使用一个map去将这些值映射到某个key中,例如:
如果$limit是0,$limit_key被设置为空字符串
如果$limit是1,$limit_key被设置为客户端的IP地址的二进制格式
这个两个结合起来,对于白名单中的IP地址,$limit_key被设置为空字符串;否则,被设置为客户端的IP地址。当limit_req_zone指令的第一个参数是一个空字符串,限制不起作用,因此白名单的IP地址(在10.0.0.0/8和192.168.0.0/24子网中)没有被限制。其它所有的IP地址都被限制为5个请求每秒。
limit_req指令将限制作用在/定位中,并且允许在没有转发延迟的情况下,转发多达10个数据包。
在一个定位中包含多个limit_req指令
可以在单个定位(location)中包含多个limit_req指令。匹配给定的请求限制都会被使用,这意味着采用最严格的限制。例如,如果多于一个的指令使用了延迟,最终使用最长的延迟。类似地,如果某个指令使得请求被拒绝,即使其它的指令允许请求通过,最终还是被拒绝。
可以在白名单中的IP地址上应用某个限流来扩展之前的例子:
http {
# ...
limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=req_zone_wl:10m rate=15r/s;
server {
# ...
location / {
limit_req zone=req_zone burst=10 nodelay;
limit_req zone=req_zone_wl burst=20 nodelay;
# ...
}
}
}
在白名单上的IP地址不匹配第一个限流(req_zone),但是能匹配第二个(req_zone_wl),因此这些IP地址被限制为15个请求每秒。不在白名单上的IP地址两个限流都能匹配上,因此最严格的那个限流起作用:5个请求每秒。
配置相关的特性
日志(Logging)
默认,NGNIX记录由于限流导致的延迟或丢弃的请求的日志,如下面的例子:
2019/06/13 04:20:00 [error] 120315#0: *32086 limiting requests, excess: 1.000 by zone "mylimit", client: 192.168.1.2, server: nginx.com, request: "GET / HTTP/1.0", host: "nginx.com"
该日志记录包含的字段:
limiting requests— 日志条目记录了某个限流的标志 excess— 超过这个请求代表的配置的速率的每毫秒请求数目 zone— 定义了启用了限流的区域 client— 产生请求的客户端IP地址 server— 服务器的IP地址或主机名 request— 客户端产生的实际的HTTP请求 host— HTTP头部主机名的值
默认,NGINX日志在error级别拒绝请求,如上面例子中的[error]所示。(它在低一个级别上记录延迟的请求,因此默认是info。)用limit_req_log_level指令来改变日志级别。下面我们设置在warn级别上记录被拒绝的请求的日志:
location /login/ {
limit_req zone=mylimit burst=20 nodelay;
limit_req_log_level warn;
proxy_pass http://my_upstream;
}
发送给客户端的错误码
默认,当某个客户端超过它的限流,NGINX用503(Service Temporarily Unavailable)状态码来响应。使用limit_req_status指令设置一个不同的状态码(在下面的例子是444):
location /login/ {
limit_req zone=mylimit burst=20 nodelay;
limit_req_status 444;
}
拒绝对特定位置的所有请求
如果你想拒绝对于某个特定URL的所有请求,而不是仅仅的限制它们,可以为这个URL配置一个location块,并且在其中包含deny all指令:
location /foo.php {
deny all;
}
Nginx利用geo模块做负载均衡
本次测试的机器ip信息如下:
server1: 113.110.86.28 server2: 113.110.86.25 server3: 188.84.155.239 客户端1:113.110.86.23 客户端2:113.110.86.51 客户端3:113.110.86.19
三台server机器上都部署了nginx环境,为了测试效果,特意配置了server1和server2的9090端口的首页,如下:
[ root@localhost ~]# curl http://113.110.86.28:9090
this is server1:113.110.86.28 [root@localhost ~]# curl http://113.110.86.25:9090 this is server2:113.110.86.25
配置server3,在server3上实现利用geo模块做负载均衡的目的,server3的nginx配置如下:
[root@localhost vhosts]# cat test.conf
geo $geo {
default default;
113.110.86.19/32 uk;
113.110.86.51/32 us;
}
#这里我是单网测试,所以掩码是32位;如果是vlan,可以是24位掩码,比如:
# 113.110.86.0/24 tw;
upstream uk.server {
server 113.110.86.28:9090;
}
upstream us.server {
server 113.110.86.25:9090;
}
upstream default.server {
server 188.84.155.239:9090;
}
server {
listen 80;
server_name 188.84.155.239;
index index.html index.htm;
root /var/www/html/80;
location / {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://$geo.server$request_uri;
}
}
server {
listen 9090;
server_name 188.84.155.239;
location / {
root /var/www/html/9090;
index index.html index.htm;
}
}
访问server3的9090端口
[root@localhost vhosts]# curl http://188.84.155.239:9090 this is server3:188.84.155.239
开始测试
1)在客户端1上访问http://188.84.155.239,如下:
[root@localhost ~]# curl http://188.84.155.239 this is server3:188.84.155.239
因为客户端1的IP地址为113.110.86.23,按照上面server3中nginx的配置,它访问的很明显是server3的9090端口!
2)在客户端2上访问http://188.84.155.239,如下:
[root@localhost ~]# curl http://188.84.155.239 this is server2:113.110.86.25
按照server3的nginx配置,客户端2访问server3的80端口就会被负载到server2的9090端口上!
3)在客户端3上访问http://188.84.155.239,如下:
[root@jenkins-server ~]# curl http://188.84.155.239 this is server1:113.110.86.28
按照server3的nginx配置,客户端3访问server3的80端口就会被负载到server1的9090端口上!