cn_wumo
文章4
标签4
分类4
apisix基于端口的SSL路由

apisix基于端口的SSL路由

插件说明

基于radixtree_sni路由插件,使用port字段而非sni进行路由选择。

radixtree_port依赖的SSL属性如下:

名称 可选项 类型 描述 示例
cert 必需 证书 https 证书
key 必需 私钥 https 证书私钥
port 必需 匹配规则 需要匹配的端口
certs 可选 证书字符串数组 当你想给同一个域名配置多个证书时,除了第一个证书需要通过 cert 传递外,剩下的证书可以通过该参数传递上来
keys 可选 私钥字符串数组 certs 对应的证书私钥,注意要跟 certs 一一对应
client.ca 可选 证书 设置将用于客户端证书校验的 CA 证书。该特性需要 OpenResty 1.19+
client.depth 可选 辅助 设置客户端证书校验的深度,默认为 1。该特性需要 OpenResty 1.19+
labels 可选 匹配规则 标识附加属性的键值对 {“version”:”v2”,”build”:”16”,”env”:”production”}

安装流程

复制插件到router目录

1
2
cd /usr/local/apisix/apisix/ssl/router
vi ./radixtree_port.lua #键入插件代码

修改SSL模板配置

1
2
cd /usr/local/apisix
vi ./schema_def.lua #修改_M.SSL配置
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
_M.ssl = {
type = "object",
properties = {
id = id_schema,
cert = certificate_scheme,
key = private_key_schema,
certs = {
type = "array",
items = certificate_scheme,
},
keys = {
type = "array",
items = private_key_schema,
},
port = {
type = "integer"
}, --changed
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
},
labels = labels_def,
status = {
description = "ssl status, 1 to enable, 0 to disable",
type = "integer",
enum = {1, 0},
default = 1
},
validity_end = timestamp_def,
validity_start = timestamp_def,
create_time = timestamp_def,
update_time = timestamp_def
},
required = {"port", "key", "cert"},
additionalProperties = false,
}

修改路由配置

1
2
cd /usr/local/apisix/conf
vi ./config-default.yaml #修改路由配置

删除或注释ssl: ‘radixtree_sni’字段,新增ssl: ‘radixtree_port’以选择路由插件

1
2
3
4
5
6
7
8
……
……
router:
http: 'radixtree_uri'
# ssl: 'radixtree_sni'
ssl: 'radixtree_port'
……
……

插件代码

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local get_request = require("resty.core.base").get_request
local router_new = require("apisix.utils.router").new
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 type = type
local error = error
local str_find = core.string.find
local str_gsub = string.gsub
local ssl_certificates
local radixtree_router
local radixtree_router_ver


local _M = {
version = 0.1,
server_name = ngx_ssl.server_name,
}


local function create_router(ssl_items) --changed
local ssl_items = ssl_items or {}

local route_items = core.table.new(#ssl_items, 0)
local idx = 0

for _, ssl in config_util.iterate_values(ssl_items) do
if ssl.value ~= nil and
(ssl.value.status == nil or ssl.value.status == 1) then -- compatible with old version

local labels_port = tostring(ssl.value.port)
idx = idx + 1
route_items[idx] = {
paths = labels_port,
handler = function (api_ctx)
if not api_ctx then
return
end
api_ctx.matched_ssl = ssl
end
}
end
end

core.log.info("route items: ", core.json.delay_encode(route_items, true))
-- for testing
if #route_items > 1 then
core.log.info("we have more than 1 ssl certs now")
end
local router, err = router_new(route_items)
if not router then
return nil, err
end

return router
end


local function set_pem_ssl_key(port, cert, pkey) --changed
local r = get_request()
if r == nil then
return false, "no request found"
end

local parsed_cert, err = apisix_ssl.fetch_cert(port, cert)
if not parsed_cert then
return false, "failed to parse PEM cert: " .. err
end

local ok, err = ngx_ssl.set_cert(parsed_cert)
if not ok then
return false, "failed to set PEM cert: " .. err
end

local parsed_pkey, err = apisix_ssl.fetch_pkey(port, pkey)
if not parsed_cert then
return false, "failed to parse PEM priv key: " .. err
end

ok, err = ngx_ssl.set_priv_key(parsed_pkey)
if not ok then
return false, "failed to set PEM priv key: " .. err
end

return true
end


function _M.match_and_set(api_ctx)
local err
if not radixtree_router or
radixtree_router_ver ~= ssl_certificates.conf_version then
radixtree_router, err = create_router(ssl_certificates.values)
if not radixtree_router then
return false, "failed to create radixtree router: " .. err
end
radixtree_router_ver = ssl_certificates.conf_version
end

local port = tostring(ngx_ssl.server_port()) --changed
local ok = radixtree_router:dispatch(port, nil, api_ctx)

if not ok then
core.log.error("failed to find any SSL certificate by Port: ", port) --changed
return false
end

local matched_ssl = api_ctx.matched_ssl
core.log.info("debug - matched: ", core.json.delay_encode(matched_ssl, true))

ngx_ssl.clear_certs()

ok, err = set_pem_ssl_key(port, matched_ssl.value.cert,
matched_ssl.value.key) --changed
if not ok then
return false, err
end

-- multiple certificates support.
if matched_ssl.value.certs then
for i = 1, #matched_ssl.value.certs do
local cert = matched_ssl.value.certs[i]
local key = matched_ssl.value.keys[i]

ok, err = set_pem_ssl_key(port, cert, key) --changed
if not ok then
return false, err
end
end
end

if matched_ssl.value.client then
local ca_cert = matched_ssl.value.client.ca
local depth = matched_ssl.value.client.depth
if apisix_ssl.support_client_verification() then
local parsed_cert, err = apisix_ssl.fetch_cert(port, ca_cert) --changed
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


function _M.ssls()
if not ssl_certificates then
return nil, nil
end

return ssl_certificates.values, ssl_certificates.conf_version
end


function _M.init_worker()
local err
ssl_certificates, err = core.config.new("/ssl", {
automatic = true,
item_schema = core.schema.ssl,
checker = function (item, schema_type)
return apisix_ssl.check_ssl_conf(true, item)
end,
})
if not ssl_certificates then
error("failed to create etcd instance for fetching ssl certificates: "
.. err)
end
end


return _M