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目录
cd /usr/local/apisix/apisix/ssl/router
vi ./radixtree_port.lua #键入插件代码
修改SSL模板配置
cd /usr/local/apisix
vi ./schema_def.lua #修改_M.SSL配置
_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,
}
修改路由配置
cd /usr/local/apisix/conf
vi ./config-default.yaml #修改路由配置
删除或注释ssl: 'radixtree_sni'字段,新增ssl: 'radixtree_port'以选择路由插件
……
……
router:
http: 'radixtree_uri'
# ssl: 'radixtree_sni'
ssl: 'radixtree_port'
……
……
插件代码
-- -- 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