nginx 获取真实 IP 的疑问,以及 nginx lua redis 防 CC 针对部分 IP 不起作用的问题

2017-06-14 21:31:24 +08:00
 ladyv2

服务器是公网 IP,单机所以没有用反向代理 是不是这种情况下,$remote_addr 就会始终是真实 IP 呢?

我额外设置了

fastcgi_param HTTP_X_FORWARDED_FOR  $remote_addr;

这样就能保证$remote_addr 始终是用户的真实 IP 呢?

我在这样的配置下,参考下面的文章 配置了 redis 和 lua https://blog.linuxeye.cn/453.html

然后修改了其中的 IP 部分为

function getClientIp()
   IP  = ngx.var.remote_addr
    return IP
end

然后检测 redis,发现可以正确生成黑白名单数据。但是,检查 php 的 log。发现依然会出现部分 Ip 的高频访问,这些 IP 没有被加入黑名单。比如下面这个 ip,几个小时内访问了 60W 次,基本上每秒 100 次请求。而没有被加入黑名单

61.151.186.217 - - [14/Jun/2017:03:27:49 +0800] "GET /index.php HTTP/1.1" 302 154 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36" "-"
61.151.186.217 - - [14/Jun/2017:03:27:49 +0800] "GET /index.php HTTP/1.1" 302 154 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36" "-"
61.151.186.217 - - [14/Jun/2017:03:27:49 +0800] "GET /index.php HTTP/1.1" 302 154 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36" "-"
61.151.186.217 - - [14/Jun/2017:03:27:49 +0800] "GET /index.php HTTP/1.1" 302 154 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36" "-"
61.151.186.217 - - [14/Jun/2017:03:27:49 +0800] "GET /index.php HTTP/1.1" 302 154 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36" "-"

有没有大神帮忙分析下是哪里配置有错误呢

6323 次点击
所在节点    NGINX
16 条回复
Phant0m
2017-06-14 22:00:11 +08:00
```
local function getClientIp()
local headers = ngx.req.get_headers()
if not headers['x-forwarded-for'] then
realIp = ngx.var.remote_addr or '127.0.0.1'
return realIp
end
if type(headers['x-forwarded-for']) == "table" then
realIp = headers['x-forwarded-for'][1]
else
realIp = headers['x-forwarded-for']
end
return realIp
end
```
blackjar
2017-06-14 23:40:46 +08:00
服务器设置 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
之后取 X-Forwarded-For 的第一个段值 可以拿到真实 ip
ladyv2
2017-06-15 06:56:51 +08:00
@Phant0m @blackjar 感谢回复
但是不起作用的 IP,从 log 上看并没有出现 x_forwarded_for。所以我不是很明白为什么 lua 没有起作用呢
ladyv2
2017-06-15 09:44:49 +08:00
今天看 log,又出现了

61.151.186.154 - - [15/Jun/2017:01:30:00 +0800] "GET / HTTP/1.1" 302 154 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36" "-"
61.151.186.154 - - [15/Jun/2017:01:30:00 +0800] "GET / HTTP/1.1" 302 154 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36" "-"

访问次数 61.151.186.154 次
但是看 redis,完全没有记录。。

redis /tmp/redis.sock> keys 61.151.186
(empty list or set)

非常奇怪
Phant0m
2017-06-15 11:57:02 +08:00
@ladyv2 那你完整的代码贴出来看看
ladyv2
2017-07-12 14:43:59 +08:00
@Phant0m 代码如下

local get_headers = ngx.req.get_headers
local ua = ngx.var.http_user_agent
local uri = ngx.var.request_uri
local url = ngx.var.host .. uri
local redis = require 'redis'
local red = redis.new()
local CCcount = 100
local CCseconds = 30
local blackseconds = 7200

if ua == nil then
ua = "unknown"
end

local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000) -- 1 sec

local ok, err = red:connect("localhost")
if not ok then
ngx.say("failed to connect: ", err)
return
end
local res, err = red:auth("password1")
if not res then
ngx.say("failed to authenticate: ", err)
return
end

if ok then
function getClientIp()
IP = ngx.var.remote_addr
--[[
IP = ngx.req.get_headers()["X-Real-IP"]
if IP == nil then
IP = ngx.req.get_headers()["x_forwarded_for"]
end
if IP == nil then
IP = ngx.var.remote_addr
end
if IP == nil then
IP = "unknown"
end
--]]
return IP
end
local token = getClientIp() .. "." .. ngx.md5(url .. ua)
local req = red:exists(token)
if req == 0 then
red:incr(token)
red:expire(token,CCseconds)
else
local times = tonumber(red:get(token))
if times >= CCcount then
local blackReq = red:exists("black." .. token)
if (blackReq == 0) then
red:set("black." .. token,1)
red:expire("black." .. token,blackseconds)
red:expire(token,blackseconds)
ngx.exit(503)
else
ngx.exit(503)
end
return
else
red:incr(token)
end
end
return
end
-- put it into the connection pool of size 100,
-- with 10 seconds max idle time
local ok, err = red:set_keepalive(10000, 1000)
if not ok then
return
end
Phant0m
2017-07-12 15:35:58 +08:00
@ladyv2 你代码里 存入 redis 的 key 不是 IP 啊,key 是 token = getClientIp() .. "." .. ngx.md5(url .. ua) ,直接取 IP 当然是空
Phant0m
2017-07-12 15:42:22 +08:00
还有最好把 IP 作为 key,如果按照你代码写的用 token,那么攻击者可以不停的更换 url 来达到攻击的目的
ladyv2
2017-07-12 22:47:19 +08:00
@Phant0m 不是啊。查看是不是屏蔽也是查看的这个 token 啊。查看 redis 也是有被屏蔽的

1) "black.202.141.176.9.7c879b2da20d1694c748b1a70b8d9281"
2) "black.1.180.215.60.9a394cf0c48fba4606c9757d3d8527c5"

采用这个 token 也是经过考虑的。如果单纯用 IP,很容易把一些通过代理或者共用 IP 的地址封掉。所以只能采用 IP+URL+浏览器来计算
Phant0m
2017-07-13 00:13:22 +08:00
@ladyv2 你 27 天前的那个回复取的是 IP。。。 计数器可以用 ngx_lua 的内存字典,然后再放入 redis
ladyv2
2017-07-13 08:15:01 +08:00
@Phant0m 其实是一样的啊,利用 token 来屏蔽 IP
现在的问题就是像我主楼发的一样,有的 IP 大量访问同样的 URL (几十万次),居然没被屏蔽。这就是我没搞明白的问题。。。
Phant0m
2017-07-13 18:51:25 +08:00
@ladyv2 日能说能说一切
Phant0m
2017-07-13 18:52:05 +08:00
@ladyv2 日志能说明一切
chinaglwo
2017-12-01 21:53:58 +08:00
@ladyv2 解决了没有? nginx 的其他配置里面,有没有 if 判断,if 很容易导致各种各样的问题。
chinaglwo
2017-12-01 21:55:29 +08:00
fastcgi_param HTTP_X_FORWARDED_FOR $remote_addr;
这个配置是不是很多余?
ladyv2
2018-01-04 13:29:40 +08:00
@chinaglwo 不多于的,防止伪造 IP 啊

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/368462

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX