Files
ReachableCEO 54cc5f7308 feat(apisix): add Cloudron package
- Implements Apache APISIX packaging for Cloudron platform.
- Includes Dockerfile, CloudronManifest.json, and start.sh.
- Configured to use Cloudron's etcd addon.

🤖 Generated with Gemini CLI
Co-Authored-By: Gemini <noreply@google.com>
2025-09-04 09:42:47 -05:00

221 lines
6.3 KiB
Lua

--
-- 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 require = require
local http = require("resty.http")
local ngx = ngx
local ngx_ocsp = require("ngx.ocsp")
local ngx_ssl = require("ngx.ssl")
local radixtree_sni = require("apisix.ssl.router.radixtree_sni")
local core = require("apisix.core")
local plugin_name = "ocsp-stapling"
local ocsp_resp_cache = ngx.shared[plugin_name]
local plugin_schema = {
type = "object",
properties = {},
}
local _M = {
name = plugin_name,
schema = plugin_schema,
version = 0.1,
priority = -44,
}
function _M.check_schema(conf)
return core.schema.check(plugin_schema, conf)
end
local function fetch_ocsp_resp(der_cert_chain)
core.log.info("fetch ocsp response from remote")
local ocsp_url, err = ngx_ocsp.get_ocsp_responder_from_der_chain(der_cert_chain)
if not ocsp_url then
-- if cert not support ocsp, the report error is nil
if not err then
err = "cert not contains authority_information_access extension"
end
return nil, "failed to get ocsp url: " .. err
end
local ocsp_req, err = ngx_ocsp.create_ocsp_request(der_cert_chain)
if not ocsp_req then
return nil, "failed to create ocsp request: " .. err
end
local httpc = http.new()
local res, err = httpc:request_uri(ocsp_url, {
method = "POST",
headers = {
["Content-Type"] = "application/ocsp-request",
},
body = ocsp_req
})
if not res then
return nil, "ocsp responder query failed: " .. err
end
local http_status = res.status
if http_status ~= 200 then
return nil, "ocsp responder returns bad http status code: "
.. http_status
end
if res.body and #res.body > 0 then
return res.body, nil
end
return nil, "ocsp responder returns empty body"
end
local function set_ocsp_resp(full_chain_pem_cert, skip_verify, cache_ttl)
local der_cert_chain, err = ngx_ssl.cert_pem_to_der(full_chain_pem_cert)
if not der_cert_chain then
return false, "failed to convert certificate chain from PEM to DER: ", err
end
local ocsp_resp = ocsp_resp_cache:get(der_cert_chain)
if ocsp_resp == nil then
core.log.info("not ocsp resp cache found, fetch from ocsp responder")
ocsp_resp, err = fetch_ocsp_resp(der_cert_chain)
if ocsp_resp == nil then
return false, err
end
core.log.info("fetch ocsp resp ok, cache it")
ocsp_resp_cache:set(der_cert_chain, ocsp_resp, cache_ttl)
end
if not skip_verify then
local ok, err = ngx_ocsp.validate_ocsp_response(ocsp_resp, der_cert_chain)
if not ok then
return false, "failed to validate ocsp response: " .. err
end
end
-- set the OCSP stapling
local ok, err = ngx_ocsp.set_ocsp_status_resp(ocsp_resp)
if not ok then
return false, "failed to set ocsp status response: " .. err
end
return true
end
local original_set_cert_and_key
local function set_cert_and_key(sni, value)
if value.gm then
-- should not run with gm plugin
core.log.warn("gm plugin enabled, no need to run ocsp-stapling plugin")
return original_set_cert_and_key(sni, value)
end
if not value.ocsp_stapling then
core.log.info("no 'ocsp_stapling' field found, no need to run ocsp-stapling plugin")
return original_set_cert_and_key(sni, value)
end
if not value.ocsp_stapling.enabled then
return original_set_cert_and_key(sni, value)
end
if not ngx.ctx.tls_ext_status_req then
core.log.info("no status request required, no need to send ocsp response")
return original_set_cert_and_key(sni, value)
end
local ok, err = radixtree_sni.set_pem_ssl_key(sni, value.cert, value.key)
if not ok then
return false, err
end
local fin_pem_cert = value.cert
-- multiple certificates support.
if value.certs then
for i = 1, #value.certs do
local cert = value.certs[i]
local key = value.keys[i]
ok, err = radixtree_sni.set_pem_ssl_key(sni, cert, key)
if not ok then
return false, err
end
fin_pem_cert = cert
end
end
local ok, err = set_ocsp_resp(fin_pem_cert,
value.ocsp_stapling.skip_verify,
value.ocsp_stapling.cache_ttl)
if not ok then
core.log.error("no ocsp response send: ", err)
end
return true
end
function _M.init()
if core.schema.ssl.properties.gm ~= nil then
core.log.error("ocsp-stapling plugin should not run with gm plugin")
end
original_set_cert_and_key = radixtree_sni.set_cert_and_key
radixtree_sni.set_cert_and_key = set_cert_and_key
if core.schema.ssl.properties.ocsp_stapling ~= nil then
core.log.error("Field 'ocsp_stapling' is occupied")
end
core.schema.ssl.properties.ocsp_stapling = {
type = "object",
properties = {
enabled = {
type = "boolean",
default = false,
},
skip_verify = {
type = "boolean",
default = false,
},
cache_ttl = {
type = "integer",
minimum = 60,
default = 3600,
},
}
}
end
function _M.destroy()
radixtree_sni.set_cert_and_key = original_set_cert_and_key
core.schema.ssl.properties.ocsp_stapling = nil
ocsp_resp_cache:flush_all()
end
return _M