nginx + lua + redis 动态封ip

PHP 设置正则

<?php

namespace MainController;


use KIFCacheDisRedis;
use KIFCoreRequest;

/**
 *  涉及文件
 * /export/manager/ybyl/main/config/route.inc.php
 * /export/manager/ybyl/main/lib/Controller/QiniuAuth.class.php
 *
 */

<<<EOF
禁止ip设置

查看禁止ip列表
/usr/local/php7.4/bin/php /export/manager/ybyl/main/www/index.php -c=QiniuAuth -a=list

增加
/usr/local/php7.4/bin/php /export/manager/ybyl/main/www/index.php -c=QiniuAuth -a=add -i=2408:84e1:60:*:*:*:*:*

删除
/usr/local/php7.4/bin/php /export/manager/ybyl/main/www/index.php -c=QiniuAuth -a=del -i=IP1,IP2

清除所有配置
/usr/local/php7.4/bin/php /export/manager/ybyl/main/www/index.php -c=QiniuAuth -a=clearAll

EOF;

class QiniuAuth extends Controller
{
    protected $objDisRedis;
    protected $bannedIpKey = 'bannedips';
    protected $bannedIpPreg = 'ip_blacklist_preg';

    public function __construct()
    {
        $this->objDisRedis = new DisRedis();
    }

    /**
     *
     * /?c=qiniuAuth
     * 七牛cdn鉴权
     * 'HTTP_CIP' => '106.3.135.101',
     * 'HTTP_CIP' => '2408:84e1:60:4979:2d11:d97e:a7c0:b0d7',
     * //  'HTTP_AUTHFROM' => 'qiniu',
     *
     */
    public function doDefault()
    {
        $userip = $_SERVER['HTTP_CIP'];
        $preg = $this->objDisRedis->get($this->bannedIpPreg);
        if (!$userip || $preg && preg_match($preg, $userip)) {
            header("HTTP/1.0 401 Unauthorized");
            header("Status: 401 Unauthorized");
            exit();
        } else {
            header("HTTP/1.0 200 OK");
            header("Status: 200 OK");
            exit();
        }
    }

    /**
     * /usr/local/php7.4/bin/php /export/manager/xx/main/www/index.php -c=QiniuAuth -a=Add -i=
     * 添加限制访问ip, 支持多个
     * 界面: 以换行符分割, 命令行: 以逗号分割
     * // hash 表
     */
    public function doAdd()
    {
        if (Request::isCLI()) {
            $opts = getopt('a:c:i:');
            $ips = $opts['i'];
            $ips = explode(',', $ips);
        } else {
            $ips = Request::p('ips');
            $ips = explode("
", $ips);
        }
        $ips = array_filter($ips);
        if (empty($ips)) {
            $this->ajax_fail_exit('no ips');
        }

        foreach ($ips as $ip) {
            $this->objDisRedis->hSet($this->bannedIpKey, $ip, time());
        }
        $setRes = $this->setBannedIpPreg();
        if(!$setRes) {
            $this->ajax_fail_exit('cache ip preg error');
        }
        $this->ajax_success_exit('success');
    }

    /**
     *
     * 删除限制访问ip, 支持多个
     * 界面: 以换行符分割, 命令行: 以逗号分割
     * // hash 表
     */
    public function doDel()
    {
        if (Request::isCLI()) {
            $opts = getopt('a:c:i:');
            $ips = $opts['i'];
            $ips = explode(',', $ips);
        } else {
            $ips = Request::p('ips');
            $ips = explode("
", $ips);
        }
        $ips = array_filter($ips);
        if (empty($ips)) {
            $this->ajax_fail_exit('no ips');
        }
        $res = $this->objDisRedis->hDel($this->bannedIpKey, $ips);
        $setRes = $this->setBannedIpPreg();
        if(!$setRes) {
            $this->ajax_fail_exit('cache ip preg error');
        }
        if ($res) {
            $this->ajax_success_exit('success');
        } else {
            $this->ajax_fail_exit('fail');
        }
    }

    /**
     * 清除所有配置
     */
    public function doClearAll() {
        $res = $this->objDisRedis->del([$this->bannedIpPreg, $this->bannedIpKey]);
        if ($res) {
            $this->ajax_success_exit('clear success');
        } else {
            $this->ajax_fail_exit('clear fail');
        }
    }

    /**
     * 列出所有设置的
     */
    public function doList()
    {
        $ips = $this->objDisRedis->hGetAll($this->bannedIpKey);
        $str = "Ip	添加时间" . PHP_EOL;
        foreach($ips as $ip => $addTime) {
            $str .= $ip . "	" . date('Y-m-d H:i:s', $addTime) . PHP_EOL;
        }
        echo $str;
        echo PHP_EOL;

        echo 'preg: ' . PHP_EOL;
        echo $this->objDisRedis->get($this->bannedIpPreg);
        echo PHP_EOL;
        echo PHP_EOL;
    }

    protected function setBannedIpPreg() {
        $ips = $this->objDisRedis->hGetAll($this->bannedIpKey);
        $bannedIps = array_keys($ips);
        $str = '';
        if(!empty($bannedIps)) {
            $str = implode('|', $bannedIps);
            $str = '(' . $str . ')';
            // for php
//            $str = str_replace('.', '.', $str);
//            $str = str_replace('*', '.*', $str);

            // for lua
            $str = str_replace('.', '.', $str);
            $str = str_replace('*', '[wW]+', $str);
        }
        return $this->objDisRedis->set($this->bannedIpPreg, $str);
    }


    public function display()
    {
        return $this->render();
    }
}

nginx 配置

worker_processes  1;
error_log logs/error.log;
events {
    worker_connections 1024;
}
http {
    lua_package_path /usr/local/openresty/nginx/lua/lua-resty-redis/lib/resty/redis.lua;
    lua_shared_dict ip_blacklist 1m;
    server {
        listen 8080;

        location / {
            default_type text/html;
            access_by_lua_file lua/ip_blacklist.lua;
            content_by_lua_block {
                ngx.say("<p>hello, world</p>")
            }

        }
    }
}

lua脚本

ip_blacklist.lua 只返回状态码, 七牛cdn鉴权用

local redis_host = "127.0.0.1"
local redis_port = 6379
local redis_db = 0;
local redis_auth = "17kb.com"

-- connection timeout for redis in ms. don't set this too high!
local redis_connection_timeout = 100

-- check a set with this key for blacklist entries
local redis_key = "ip_blacklist_preg"

-- cache lookups for this many seconds
local cache_ttl = 60

-- end configuration

local ip = ngx.var.remote_addr
local ip_blacklist = ngx.shared.ip_blacklist
local last_update_time = ip_blacklist:get("last_update_time");

if last_update_time == nil or last_update_time < ( ngx.now() - cache_ttl ) then
    local redis = require "resty.redis";
    local red = redis:new();
    red:set_timeout(redis_connect_timeout);
    local ok, err = red:connect(redis_host, redis_port);

    if not ok then
        -- ngx.log(ngx.DEBUG, "Redis connection error while retrieving ip_blacklist:");
    else
        local res, err = red:auth(redis_auth)
        if not res then
            -- ngx.log(ngx.DEBUG, "failed to authenticate: " .. err);
        else
            red:select(redis_db)
            local new_ip_blacklist_preg, err = red:get(redis_key);
            if err then
                -- ngx.log(ngx.DEBUG, "Redis read error while retrieving ip_blacklist:" .. err);
            else
                -- replace the locally stored ip_blacklist with the updated values:
                ip_blacklist:flush_all();

                ip_blacklist:set(redis_key, new_ip_blacklist_preg);

                -- update time
                ip_blacklist:set("last_update_time", ngx.now());
            end
        end
    end
end

local regex = ip_blacklist:get(redis_key)
if regex then
    local m  = ngx.re.match( ip, regex, "oj") 
    if m then
         return ngx.exit(401);
    else
        return ngx.exit(200);
    end
else
    return ngx.exit(200);
end

 

原文地址:https://www.cnblogs.com/bandbandme/p/13307385.html