Skip to content

Commit

Permalink
feat: support client certificate verification
Browse files Browse the repository at this point in the history
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
  • Loading branch information
spacewander committed Apr 12, 2021
1 parent de80319 commit 5a9795b
Show file tree
Hide file tree
Showing 8 changed files with 380 additions and 1 deletion.
11 changes: 11 additions & 0 deletions apisix/admin/ssl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@ local function check_conf(id, conf, need_id)
end
end

if conf.client then
if not apisix_ssl.support_client_verfication() then
return nil, {error_msg = "client tls verify unsupported"}
end

local ok, err = apisix_ssl.validate(conf.client.ca, nil)
if not ok then
return nil, {error_msg = "failed to validate client_cert: " .. err}
end
end

return need_id and id or true
end

Expand Down
13 changes: 13 additions & 0 deletions apisix/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,19 @@ end

function _M.http_access_phase()
local ngx_ctx = ngx.ctx

if ngx_ctx.api_ctx and ngx_ctx.api_ctx.ssl_client_verified then
local res = ngx_var.ssl_client_verify
if res ~= "SUCCESS" then
if res == "NONE" then
core.log.error("client certificate was not present")
else
core.log.error("clent certificate verification is not passed: ", res)
end
return core.response.exit(400)
end
end

-- always fetch table from the table pool, we don't need a reused api_ctx
local api_ctx = core.tablepool.fetch("api_ctx", 0, 32)
ngx_ctx.api_ctx = api_ctx
Expand Down
12 changes: 12 additions & 0 deletions apisix/schema_def.lua
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,18 @@ _M.ssl = {
type = "array",
items = private_key_schema,
},
client = {
type = "object",
properties = {
ca = certificate_scheme,
depth = {
type = "integer",
minimum = 0,
default = 1,
},
},
required = {"ca"},
},
exptime = {
type = "integer",
minimum = 1588262400, -- 2020/5/1 0:0:0
Expand Down
10 changes: 10 additions & 0 deletions apisix/ssl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ function _M.validate(cert, key)
return nil, "failed to parse cert: " .. err
end

if key == nil then
-- sometimes we only need to validate the cert
return true
end

key = aes_decrypt_pkey(key)
if not key then
return nil, "failed to decrypt previous encrypted key"
Expand Down Expand Up @@ -152,4 +157,9 @@ function _M.fetch_pkey(sni, pkey)
end


function _M.support_client_verfication()
return ngx_ssl.verify_client ~= nil
end


return _M
21 changes: 20 additions & 1 deletion apisix/ssl/router/radixtree_sni.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ local core = require("apisix.core")
local apisix_ssl = require("apisix.ssl")
local ngx_ssl = require("ngx.ssl")
local config_util = require("apisix.core.config_util")
local ipairs = ipairs
local ipairs = ipairs
local type = type
local error = error
local str_find = core.string.find
Expand All @@ -29,6 +29,7 @@ local ssl_certificates
local radixtree_router
local radixtree_router_ver


local _M = {
version = 0.1,
server_name = ngx_ssl.server_name,
Expand Down Expand Up @@ -194,6 +195,24 @@ function _M.match_and_set(api_ctx)
end
end

if matched_ssl.value.client then
local client_cert = matched_ssl.value.client.ca
local depth = matched_ssl.value.client.depth
if apisix_ssl.support_client_verfication() then
local parsed_cert, err = apisix_ssl.fetch_cert(sni, client_cert)
if not parsed_cert then
return false, "failed to parse client cert: " .. err
end

local ok, err = ngx_ssl.verify_client(parsed_cert, depth)
if not ok then
return false, err
end

api_ctx.ssl_client_verified = true
end
end

return true
end

Expand Down
2 changes: 2 additions & 0 deletions docs/en/latest/admin-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,8 @@ Return response from etcd currently.
| key | True | Private key | https private key | |
| certs | False | An array of certificate | when you need to configure multiple certificate for the same domain, you can pass extra https certificates (excluding the one given as cert) in this field | |
| keys | False | An array of private key | https private keys. The keys should be paired with certs above | |
| client.ca | False | Certificate| set the CA certificate which will use to verify client. This feature requires OpenResty 1.19+. | |
| client.depth | False | Certificate| set the verification depth in the client certificates chain, default to 1. This feature requires OpenResty 1.19+. | |
| snis | True | Match Rules | a non-empty arrays of https SNI | |
| labels | False | Match Rules | Key/value pairs to specify attributes | {"version":"v2","build":"16","env":"production"} |
| create_time | False | Auxiliary | epoch timestamp in second, will be created automatically if missing | 1602883670 |
Expand Down
2 changes: 2 additions & 0 deletions docs/zh/latest/admin-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,8 @@ $ curl http://127.0.0.1:9080/get
| key | 必需 | 私钥 | https 证书私钥 | |
| certs | 可选 | 证书字符串数组 | 当你想给同一个域名配置多个证书时,除了第一个证书需要通过 cert 传递外,剩下的证书可以通过该参数传递上来 | |
| keys | 可选 | 私钥字符串数组 | certs 对应的证书私钥,注意要跟 certs 一一对应 | |
| client.ca | 可选 | 证书| 设置将用于客户端证书校验的 CA 证书。该特性需要 OpenResty 1.19+ | |
| client.depth | 可选 | 辅助| 设置客户端证书校验的深度,默认为 1。该特性需要 OpenResty 1.19+ | |
| snis | 必需 | 匹配规则 | 非空数组形式,可以匹配多个 SNI | |
| labels | 可选 | 匹配规则 | 标识附加属性的键值对 | {"version":"v2","build":"16","env":"production"} |
| create_time | 可选 | 辅助 | 单位为秒的 epoch 时间戳,如果不指定则自动创建 | 1602883670 |
Expand Down
Loading

0 comments on commit 5a9795b

Please sign in to comment.