V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
NGINX
NGINX Trac
3rd Party Modules
Security Advisories
CHANGES
OpenResty
ngx_lua
Tengine
在线学习资源
NGINX 开发从入门到精通
NGINX Modules
ngx_echo
ladyv2
V2EX  ›  NGINX

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

  •  
  •   ladyv2 · 2017-06-14 21:31:24 +08:00 · 6285 次点击
    这是一个创建于 2711 天前的主题,其中的信息可能已经有所发展或是发生改变。

    服务器是公网 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" "-"
    

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

    16 条回复    2018-01-04 13:29:40 +08:00
    Phant0m
        1
    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
        2
    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
        3
    ladyv2  
    OP
       2017-06-15 06:56:51 +08:00
    @Phant0m @blackjar 感谢回复
    但是不起作用的 IP,从 log 上看并没有出现 x_forwarded_for。所以我不是很明白为什么 lua 没有起作用呢
    ladyv2
        4
    ladyv2  
    OP
       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
        5
    Phant0m  
       2017-06-15 11:57:02 +08:00 via iPhone
    @ladyv2 那你完整的代码贴出来看看
    ladyv2
        6
    ladyv2  
    OP
       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
        7
    Phant0m  
       2017-07-12 15:35:58 +08:00
    @ladyv2 你代码里 存入 redis 的 key 不是 IP 啊,key 是 token = getClientIp() .. "." .. ngx.md5(url .. ua) ,直接取 IP 当然是空
    Phant0m
        8
    Phant0m  
       2017-07-12 15:42:22 +08:00
    还有最好把 IP 作为 key,如果按照你代码写的用 token,那么攻击者可以不停的更换 url 来达到攻击的目的
    ladyv2
        9
    ladyv2  
    OP
       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
        10
    Phant0m  
       2017-07-13 00:13:22 +08:00
    @ladyv2 你 27 天前的那个回复取的是 IP。。。 计数器可以用 ngx_lua 的内存字典,然后再放入 redis
    ladyv2
        11
    ladyv2  
    OP
       2017-07-13 08:15:01 +08:00
    @Phant0m 其实是一样的啊,利用 token 来屏蔽 IP
    现在的问题就是像我主楼发的一样,有的 IP 大量访问同样的 URL (几十万次),居然没被屏蔽。这就是我没搞明白的问题。。。
    Phant0m
        12
    Phant0m  
       2017-07-13 18:51:25 +08:00
    @ladyv2 日能说能说一切
    Phant0m
        13
    Phant0m  
       2017-07-13 18:52:05 +08:00
    @ladyv2 日志能说明一切
    chinaglwo
        14
    chinaglwo  
       2017-12-01 21:53:58 +08:00
    @ladyv2 解决了没有? nginx 的其他配置里面,有没有 if 判断,if 很容易导致各种各样的问题。
    chinaglwo
        15
    chinaglwo  
       2017-12-01 21:55:29 +08:00
    fastcgi_param HTTP_X_FORWARDED_FOR $remote_addr;
    这个配置是不是很多余?
    ladyv2
        16
    ladyv2  
    OP
       2018-01-04 13:29:40 +08:00
    @chinaglwo 不多于的,防止伪造 IP 啊
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1054 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 19:40 · PVG 03:40 · LAX 11:40 · JFK 14:40
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.