Kong 网关是一个开源的 API 网关,它确保只有正确的请求被接收,同时处理安全、速率限制、日志记录等更多任务。 OPA(开放策略代理)是一个开源的政策引擎,它控制着您的安全和访问决策。您可以把它看作是一个将策略执行与应用程序分离的大脑,因此您的服务不需要担心执行规则。相反,OPA 使用其 Rego 语言进行思考,跨 API、微服务甚至 Kubernetes 评估策略。它是灵活的、安全的,并且使更新策略变得容易。OPA 通过评估三个关键因素来工作:输入(如请求的真实时间数据)、数据(如用户角色的外部信息)和策略(用 Rego 编写的决定是否“允许”或“拒绝”的逻辑)。这三个组件共同使 OPA 能够保持您的 安全 游戏强大,同时保持事情的简单和一致性。
我们在寻求完成或解决什么问题?
通常情况下,OPA中的数据就像一个可靠的老朋友——静态的或缓慢变化的。它与不断变化的数据输入一起使用,以做出明智的决策。但是,想象一下一个拥有庞大微服务网络、众多用户和像PostgreSQL这样的大型数据库的系统。这个系统每秒处理大量的交易,并且需要保持速度和吞吐量,而不至于不堪重负。
在这样的系统中实现细粒度的访问控制是棘手的,但借助OPA,您可以将繁重的工作从微服务中卸载,并在网关层面处理。通过与Kong API Gateway和OPA合作,您可以同时获得一流的吞吐量和精确的访问控制。
那么,在不减慢速度的情况下如何保持用户数据的准确性呢?不断向PostgreSQL数据库获取数百万条记录既昂贵又缓慢。要同时实现准确性和速度通常需要在两者之间做出妥协。让我们通过开发一个自定义插件(在网关层面)来寻求一种实用的平衡,这个插件经常加载并在本地缓存数据供OPA在评估其策略时使用。
示例
为了演示,我在PostgreSQL中设置了示例数据,其中包括用户信息,如姓名、电子邮件和角色。当用户尝试通过特定URL访问服务时,OPA评估请求是否被允许。Rego策略检查请求URL(资源)、方法以及用户的角色,然后根据规则返回真或假。如果是真,请求被允许通过;如果是假,则拒绝访问。到目前为止,这是一个简单的设置。现在让我们来了解一下自定义插件。为了更清楚地了解其实现,请参考下面的图表。
当请求通过Kong代理时,Kong自定义插件将被触发。插件将获取所需的数据并将其与输入/查询一起传递给OPA。这个数据获取有两个部分:一个是查询Redis以找到所需的值,如果找到,将其传递给OPA;如果没有,它将进一步查询Postgres并获取数据,并在Redis中缓存后再将其传递给OPA。我们可以在下一部分运行命令时重新访问这一点并观察日志。OPA根据策略、输入和数据做出决策,如果被允许,Kong将把那个请求发送到API。采用这种方法,对Postgres的查询数量显著减少,同时确保OPA有准确的数据,并保持低延迟。
要开始构建自定义插件,我们需要一个handler.lua
文件,其中实现插件的核心逻辑,以及一个schema.lua
文件,它定义了插件配置的架构。如果您刚开始学习如何为Kong编写自定义插件,请参考这个链接获取更多信息。文档还解释了如何打包和安装插件。让我们继续了解这个插件的逻辑。
示例的第一步是在您的本地设置或任何云设置中安装OAP,Kong,Postgres和Redis。请克隆到这个仓库。
查看docker-compose.yaml文件,其中定义了部署上述四个服务的配置。观察Kong环境变量,了解如何加载自定义插件。
运行以下命令部署服务:
docker-compose build
docker-compose up
一旦我们验证容器正在运行,Kong管理器和OPA分别可以通过以下端点访问:https://localhost:8002 和 https://localhost:8181,如下所示:
使用以下命令创建一个测试服务,路由,并将我们的自定义Kong插件添加到这个路由中:
curl -X POST http://localhost:8001/config -F config=@config.yaml
定义在authopa.rego文件中的OPA策略,使用以下命令发布并更新到OPA服务:
curl -X PUT http://localhost:8181/v1/policies/mypolicyId -H "Content-Type: application/json" --data-binary @authopa.rego
此示例策略仅当用户使用GET
方法访问/demo路径并且具有"Moderator"
角色时,才授予用户请求的访问权限。可以根据需要添加其他规则,以根据不同标准定制访问控制。
opa_policy = [
{
"path": "/demo",
"method": "GET",
"allowed_roles": ["Moderator"]
}
]
现在设置已完成,但在测试之前,我们需要在Postgres中添加一些测试数据。我添加了一些示例数据(姓名、电子邮件和角色),如下所示(请参阅PostgresReadme)。
以下是一个失败和成功的请求示例:
现在,为了测试这个自定义插件的核心功能,让我们连续发送两个请求,并查看日志以了解数据检索的过程。
以下是日志:
2024/09/13 14:05:05 [error] 2535#0: *10309 [kong] redis.lua:19 [authopa] No data found in Redis for key: [email protected], client: 192.168.96.1, server: kong, request: "GET /demo HTTP/1.1", host: "localhost:8000", request_id: "ebbb8b5b57ff4601ff194907e35a3002"
2024/09/13 14:05:05 [info] 2535#0: *10309 [kong] handler.lua:25 [authopa] Fetching roles from PostgreSQL for email: [email protected], client: 192.168.96.1, server: kong, request: "GET /demo HTTP/1.1", host: "localhost:8000", request_id: "ebbb8b5b57ff4601ff194907e35a3002"
2024/09/13 14:05:05 [info] 2535#0: *10309 [kong] postgres.lua:43 [authopa] Fetched roles: Moderator, client: 192.168.96.1, server: kong, request: "GET /demo HTTP/1.1", host: "localhost:8000", request_id: "ebbb8b5b57ff4601ff194907e35a3002"
2024/09/13 14:05:05 [info] 2535#0: *10309 [kong] handler.lua:29 [authopa] Caching user roles in Redis, client: 192.168.96.1, server: kong, request: "GET /demo HTTP/1.1", host: "localhost:8000", request_id: "ebbb8b5b57ff4601ff194907e35a3002"
2024/09/13 14:05:05 [info] 2535#0: *10309 [kong] redis.lua:46 [authopa] Data successfully cached in Redis, client: 192.168.96.1, server: kong, request: "GET /demo HTTP/1.1", host: "localhost:8000", request_id: "ebbb8b5b57ff4601ff194907e35a3002"
2024/09/13 14:05:05 [info] 2535#0: *10309 [kong] opa.lua:37 [authopa] Is Allowed by OPA: true, client: 192.168.96.1, server: kong, request: "GET /demo HTTP/1.1", host: "localhost:8000", request_id: "ebbb8b5b57ff4601ff194907e35a3002"
2024/09/13 14:05:05 [info] 2535#0: *10309 client 192.168.96.1 closed keepalive connection
------------------------------------------------------------------------------------------------------------------------
2024/09/13 14:05:07 [info] 2535#0: *10320 [kong] redis.lua:23 [authopa] Redis result: {"roles":["Moderator"],"email":"[email protected]"}, client: 192.168.96.1, server: kong, request: "GET /demo HTTP/1.1", host: "localhost:8000", request_id: "75bf7a4dbe686d0f95e14621b89aba12"
2024/09/13 14:05:07 [info] 2535#0: *10320 [kong] opa.lua:37 [authopa] Is Allowed by OPA: true, client: 192.168.96.1, server: kong, request: "GET /demo HTTP/1.1", host: "localhost:8000", request_id: "75bf7a4dbe686d0f95e14621b89aba12"
日志显示,对于第一个请求,当Redis中没有数据时,数据会从Postgres中检索并缓存到Redis中,然后再发送到OPA进行评估。在随后的请求中,由于数据已在Redis中,响应将会更快。
结论
总结来说,通过将Kong Gateway与OPA结合并实现带有Redis缓存的定制插件,我们在高吞吐量环境中有效地平衡了访问控制的准确性和速度。这个插件通过在首次查询后将用户角色缓存到Redis,从而将昂贵的Postgres查询数量降到最低。在后续请求中,数据从Redis检索,显著降低了延迟,同时保持了OPA策略评估中准确和最新的用户信息。这种方法确保了细粒度访问控制在网关层面高效处理,而不会牺牲性能或安全性,使其成为在执行精确访问策略的同时扩展微服务的理想解决方案。
Source:
https://dzone.com/articles/enhanced-api-security-fine-grained-access-control