写一个Kong上游转发签名认证插件

场景

微服务API接口认证的方式有很多,Kong官网关于上游API认证的插件只有一个Upstream HTTP Basic Authentication,是基于基本的用户名和密码认证的。其它的都是前端认证认证插件。如果后端微服务API采用的是HMAC-SHA256签名认证,就接入不了了,怎么办?首先Google了一番,没有找到此类插件。那么问题来了,如果要用这种认证方式接入Kong,那么就要自己写插件造轮子了。

相关知识

先看看Lua的基本语法,由于是菜鸟,就去看了Lua 菜鸟教程
再熟悉学习了Kong的插件大致怎么写,大概看了以下文章:
kong插件官方文档翻译
API网关Kong学习笔记(十一):自己动手写一个插件
Kong Api网关简介(二) 插件
kong 网关插件快速开发指南
好了,看过这几篇文章后,大概了解了Kong的插件是一个怎样的套路了。
还需要了解ngx模块里面的var、ctx、req等内置变量和方法。

签名认证过程

API的签名认证用的是Python的httpsig模块,签名算法采用HMAC-SHA256。由于是相对于后端来说,Kong的转发相当于客户端请求,要完成这个插件就需要了解HMAC-SHA256签名的详细认证过程。
通过阅读和调试httpsig模块相关代码,签名的过程是以下这样的:

客户端获取自己的公私钥对
1
2
local key = conf.key
local secret = conf.secret
计算加密消息

加密的消息一般采用当前时间date

1
local date = os.date("%Y-%m-%d %H:%M:%S", os.time())
加密

将自己的密钥和加密消息,用HMAC-SHA256算法进行签名。

1
2
local signature_salt = string.format("date: %s", date)
local signature = openssl_hmac.new(secret, "sha256"):final(signature_salt)
组装和注入

datex-api-keyauthorization三个值注入到HTTP头字段HTTP header fields。其中authorization字段格式有一定的要求,必须包含公钥Signature keyId、签名算法algorithm、签名signature,类似这样的字符串

1
'authorization': u'Signature keyId="d1eaffbcb543r357",algorithm="hmac-sha256",signature="pp18H0zla/kvKZP/jAKCTkgqiVO92RRAqTsybZJao/o="'

组装authorization字段,signature的值需要对原始签名的二进制数据进行Base64编码。

1
local authorization = string.format('Signature keyId="%s",algorithm="hmac-sha256",signature="%s"', key, ngx.encode_base64(signature))

ngx.req.set_header注入相应值。其中的date字段值是签名时用的加密消息,x-api-key是自己的公钥。

1
2
3
req_set_header("date", date)
req_set_header("x-api-key", key)
req_set_header("authorization", authorization)

最后得到的HTTP头字段类似这样

1
2
3
4
5
6
7
{
'accept-encoding': 'gzip, deflate', 'accept': '*/*', \
'user-agent': 'xxxxxx/xxx', 'connection': 'keep-alive', \
'x-api-key': 'd1eaffbcb543r357', 'date': '2019-01-16 17:22:46',\
'content-type': 'application/json', \
'authorization': u'Signature keyId="d1eaffbcb543r357",algorithm="hmac-sha256",signature="pp18H0zla/kvKZP/jAKCTkgqiVO92RRAqTsybZJao/o="'
}
认证

后端微服务拿到公钥、加密消息、签名算法后,先查询公钥对应的私钥,在用一样的签名算法,计算私钥和加密消息,如果得到的结果一样,那么就认证成功,如果不一样那么就认证失败,返回相应错误。

代码

插件的核心代码handler.lua,目前只支持HMAC-SHA256签名认证,自己动手改一改就可以支持其它签名认证了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
local BasePlugin = require "kong.plugins.base_plugin"
local req_set_header = ngx.req.set_header
local openssl_hmac = require "openssl.hmac"

local UpstreamAuthSignatureHandler = BasePlugin:extend()

function UpstreamAuthSignatureHandler:new()
UpstreamAuthSignatureHandler.super.new(self, "upstream-auth-signature")
end

function UpstreamAuthSignatureHandler:access(conf)
UpstreamAuthSignatureHandler.super.access(self)

local key = conf.key
local secret = conf.secret
local date = os.date("%Y-%m-%d %H:%M:%S", os.time())
local signature_salt = string.format("date: %s", date)
local signature = openssl_hmac.new(secret, "sha256"):final(signature_salt)
local authorization = string.format('Signature keyId="%s",algorithm="hmac-sha256",signature="%s"', key, ngx.encode_base64(signature))

req_set_header("date", date)
req_set_header("x-api-key", key)
req_set_header("authorization", authorization)

end

UpstreamAuthSignatureHandler.PRIORITY = 850

return UpstreamAuthSignatureHandler

插件GitHub开源地址kong-plugin-upstream-auth-signature

----------------本文结束 感谢阅读----------------