apisix基於端口的SSL路由

插件說明

         基於radixtree_sni路由插件,使用port字段而非sni進行路由選擇。express

         radixtree_port依賴的SSL屬性以下:apache

名字json

可選項api

類型數組

說明app

示例less

certfetch

必需ui

證書this

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
相關文章
相關標籤/搜索