@http://127.0.0.1:9180/apisix/admin/plugin_metadata/ip-blacklist 这是一种什么方式 获取 黑名单

这是 APISIX 官方标准的 "Plugin Metadata" 机制,是章亦春设计的一种优雅方式,用于存储和管理插件的全局配置。
在 APISIX 中,/apisix/admin/plugin_metadata/{plugin-name} 接口允许你:
存储特定插件的全局配置数据
这些数据会被所有使用该插件的路由共享
避免在每个路由上重复配置
对于黑名单管理,这种方式比创建自定义资源类型更符合 APISIX 的设计理念,因为:
它使用现有的机制,不需要增加新的资源类型
数据存储在 etcd 中,保持了高可用性
这是章亦春的设计风格的体现 - 尽可能复用现有机制解决问题,而不是创建新的实现。所有通过此API更新的黑名单配置会立即对整个系统生效。

APISIX IP黑名单插件实现指南

本文档介绍如何使用Apache APISIX的Plugin Metadata机制实现IP黑名单功能。

Plugin Metadata机制简介

Plugin Metadata是APISIX中用于存储插件全局配置的机制,由章亦春设计。它允许:

  1. 为特定插件定义全局配置,所有使用该插件的路由共享这些配置
  2. 配置存储在etcd中,确保高可用性和集群一致性
  3. 支持动态更新,无需重启APISIX

访问方式:

GET/PUT/DELETE http://127.0.0.1:9180/apisix/admin/plugin_metadata/{plugin-name}

IP黑名单插件实现

1. 插件定义

插件文件:/usr/local/apisix/apisix/plugins/ip-blacklist.lua

-- IP黑名单插件
-- 基于Redis的高性能IP黑名单/白名单管理
local redis = require("resty.redis")
local core = require("apisix.core")
local plugin_name = "ip-blacklist"

local schema = {
    type = "object",
    properties = {
        redis_host = {type = "string", default = "127.0.0.1"},
        redis_port = {type = "integer", default = 6379},
        redis_password = {type = "string"},
        redis_database = {type = "integer", default = 0},
        redis_timeout = {type = "integer", default = 1000},
        blacklist_key = {type = "string", default = "ip_blacklist"},
        whitelist_key = {type = "string", default = "ip_whitelist"},
        rejected_code = {type = "integer", default = 403},
    },
    required = {"redis_host", "redis_port"}
}

-- 插件元数据配置
local metadata_schema = {
    type = "object",
    properties = {
        redis_host = {type = "string", default = "127.0.0.1"},
        redis_port = {type = "integer", default = 6379},
        redis_password = {type = "string"},
        redis_database = {type = "integer", default = 0},
        blacklist_key = {type = "string", default = "ip_blacklist"},
        whitelist_key = {type = "string", default = "ip_whitelist"},
        blacklist = {type = "array"} -- 黑名单IP列表
    }
}

local _M = {
    version = 0.1,
    priority = 2900,
    name = plugin_name,
    schema = schema,
    metadata_schema = metadata_schema
}

-- 插件逻辑实现...
-- 检查IP是否在黑名单
function _M.rewrite(conf, ctx)
    local ip = core.request.get_remote_ip(ctx)
    if not ip then
        return 500, {message = "无法获取客户端IP"}
    end
    
    -- 获取插件元数据
    local meta_data = core.plugin.plugin_metadata(plugin_name)
    if meta_data and meta_data.value and meta_data.value.blacklist then
        for _, item in ipairs(meta_data.value.blacklist) do
            -- 支持两种格式: 简单字符串或对象结构
            local blocked_ip = nil
            local expire_time = nil
            
            if type(item) == "string" then
                blocked_ip = item
            elseif type(item) == "table" and item.ip then
                blocked_ip = item.ip
                
                -- 检查过期时间
                if item.expire and item.start_time then
                    local current_time = os.time()
                    local end_time = item.start_time + item.expire
                    if current_time > end_time then
                        -- 已过期,跳过此IP
                        goto continue
                    end
                    expire_time = end_time
                end
            end
            
            -- 检查IP是否匹配
            if blocked_ip == ip then
                local msg = "IP在黑名单中"
                if expire_time then
                    msg = msg .. ",将在 " .. os.date("%Y-%m-%d %H:%M:%S", expire_time) .. " 解除屏蔽"
                end
                return conf.rejected_code, {message = msg}
            end
            
            ::continue::
        end
    end
end

return _M

2. 配置插件

在config.yaml中启用插件

文件:/usr/local/apisix/conf/config.yaml

plugins:
  - ip-blacklist

deployment:
  admin:
    admin_key_required: true
    admin_key:
      -
        name: admin
        key: edd1c9f034335f136f87ad84b625c8f1
        role: admin

设置Plugin Metadata

设置Redis连接和黑名单:

curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/ip-blacklist -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '{
    "redis_host": "127.0.0.1",
    "redis_port": 6379, 
    "redis_database": 0,
    "blacklist_key": "ip_blacklist",
    "whitelist_key": "ip_whitelist",
    "blacklist": [
        {"ip": "192.168.1.100", "expire": 3600, "start_time": 1747870000},
        {"ip": "192.168.1.101", "expire": 7200, "start_time": 1747870000}
    ]
}'

创建使用插件的路由

curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '{
    "uri": "/*",
    "plugins": {
        "ip-blacklist": {
            "rejected_code": 403
        }
    },
    "upstream": {
        "type": "roundrobin",
        "nodes": {
            "127.0.0.1:9080": 1
        }
    }
}'

3. 重新加载APISIX

apisix reload

黑名单管理API

查看黑名单配置

curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/ip-blacklist -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1"

更新黑名单

添加新的IP到黑名单:

# 先获取当前配置
META_DATA=$(curl -s http://127.0.0.1:9180/apisix/admin/plugin_metadata/ip-blacklist -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1")

# 提取现有黑名单并添加新IP
BLACKLIST=$(echo $META_DATA | grep -o '"blacklist":\[[^]]*\]' | sed 's/"blacklist"://')
NEW_BLACKLIST=$(echo $BLACKLIST | sed 's/\]/, {"ip": "10.0.0.1", "expire": 86400, "start_time": '$(date +%s)'}\]/')

# 构建新的配置
NEW_META=$(echo $META_DATA | sed "s/\"blacklist\":\[[^]]*\]/\"blacklist\":$NEW_BLACKLIST/")

# 更新配置
curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/ip-blacklist -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d "$NEW_META"

删除IP从黑名单

类似添加,但操作是从数组中移除指定IP的项。

优点与特性

  1. 高效: 黑名单检查在Lua层完成,性能好
  2. 灵活: 支持带过期时间的IP黑名单
  3. 动态: 可随时更新黑名单而无需重启服务
  4. 集成: 完全使用APISIX现有API机制
  5. 符合章亦春风格: 简洁、高效、优雅

自动化脚本示例

添加IP到黑名单的脚本add_to_blacklist.sh:

#!/bin/bash

# 参数检查
if [ -z "$1" ]; then
    echo "用法: $0 <IP地址> [过期时间(秒)]"
    exit 1
fi

IP=$1
EXPIRE=${2:-3600}  # 默认1小时
NOW=$(date +%s)

# 获取当前元数据
META_DATA=$(curl -s http://127.0.0.1:9180/apisix/admin/plugin_metadata/ip-blacklist -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1")

# 检查是否有现有黑名单
if echo "$META_DATA" | grep -q '"blacklist":'; then
    # 提取现有黑名单
    BLACKLIST=$(echo $META_DATA | grep -o '"blacklist":\[[^]]*\]' | sed 's/"blacklist"://')
    
    # 添加新IP
    if [[ $BLACKLIST == "[]" ]]; then
        NEW_BLACKLIST="[{\"ip\": \"$IP\", \"expire\": $EXPIRE, \"start_time\": $NOW}]"
    else
        NEW_BLACKLIST=$(echo $BLACKLIST | sed "s/\]/, {\"ip\": \"$IP\", \"expire\": $EXPIRE, \"start_time\": $NOW}\]/")
    fi
    
    # 更新元数据
    NEW_META=$(echo $META_DATA | sed "s/\"blacklist\":\[[^]]*\]/\"blacklist\":$NEW_BLACKLIST/")
else
    # 没有现有黑名单,创建新的
    NEW_META=$(echo $META_DATA | sed "s/{/{\"blacklist\":[{\"ip\": \"$IP\", \"expire\": $EXPIRE, \"start_time\": $NOW}],/")
fi

# 更新配置
curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/ip-blacklist -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d "$NEW_META"

echo "IP $IP 已添加到黑名单,过期时间: $EXPIRE 秒"

总结

使用APISIX的Plugin Metadata机制实现IP黑名单功能是一种优雅的解决方案,完全符合章亦春的设计理念:

  1. 使用现有机制而非创建新资源类型
  2. 配置集中存储、易于管理
  3. 高性能设计
  4. 支持动态更新

这个实现可以轻松扩展支持更复杂的黑名单规则,如CIDR匹配、地理位置屏蔽等。

posted @ 2025-05-22 07:47  昆仑葫芦  阅读(26)  评论(0)    收藏  举报