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>
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
--
|
||||
-- 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 ngx = ngx
|
||||
local hmac = require("resty.hmac")
|
||||
local hex_encode = require("resty.string").to_hex
|
||||
local resty_sha256 = require("resty.sha256")
|
||||
local str_strip = require("pl.stringx").strip
|
||||
local norm_path = require("pl.path").normpath
|
||||
local pairs = pairs
|
||||
local tab_concat = table.concat
|
||||
local tab_sort = table.sort
|
||||
local os = os
|
||||
|
||||
|
||||
local plugin_name = "aws-lambda"
|
||||
local plugin_version = 0.1
|
||||
local priority = -1899
|
||||
|
||||
local ALGO = "AWS4-HMAC-SHA256"
|
||||
|
||||
local function hmac256(key, msg)
|
||||
return hmac:new(key, hmac.ALGOS.SHA256):final(msg)
|
||||
end
|
||||
|
||||
local function sha256(msg)
|
||||
local hash = resty_sha256:new()
|
||||
hash:update(msg)
|
||||
local digest = hash:final()
|
||||
return hex_encode(digest)
|
||||
end
|
||||
|
||||
local function get_signature_key(key, datestamp, region, service)
|
||||
local kDate = hmac256("AWS4" .. key, datestamp)
|
||||
local kRegion = hmac256(kDate, region)
|
||||
local kService = hmac256(kRegion, service)
|
||||
local kSigning = hmac256(kService, "aws4_request")
|
||||
return kSigning
|
||||
end
|
||||
|
||||
local aws_authz_schema = {
|
||||
type = "object",
|
||||
properties = {
|
||||
-- API Key based authorization
|
||||
apikey = {type = "string"},
|
||||
-- IAM role based authorization, works via aws v4 request signing
|
||||
-- more at https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
|
||||
iam = {
|
||||
type = "object",
|
||||
properties = {
|
||||
accesskey = {
|
||||
type = "string",
|
||||
description = "access key id from from aws iam console"
|
||||
},
|
||||
secretkey = {
|
||||
type = "string",
|
||||
description = "secret access key from from aws iam console"
|
||||
},
|
||||
aws_region = {
|
||||
type = "string",
|
||||
default = "us-east-1",
|
||||
description = "the aws region that is receiving the request"
|
||||
},
|
||||
service = {
|
||||
type = "string",
|
||||
default = "execute-api",
|
||||
description = "the service that is receiving the request"
|
||||
}
|
||||
},
|
||||
required = {"accesskey", "secretkey"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
local function request_processor(conf, ctx, params)
|
||||
local headers = params.headers
|
||||
-- set authorization headers if not already set by the client
|
||||
-- we are following not to overwrite the authz keys
|
||||
if not headers["x-api-key"] then
|
||||
if conf.authorization and conf.authorization.apikey then
|
||||
headers["x-api-key"] = conf.authorization.apikey
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- performing aws v4 request signing for IAM authorization
|
||||
-- visit https://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html
|
||||
-- to look at the pseudocode in python.
|
||||
if headers["authorization"] or not conf.authorization or not conf.authorization.iam then
|
||||
return
|
||||
end
|
||||
|
||||
-- create a date for headers and the credential string
|
||||
local t = ngx.time()
|
||||
local amzdate = os.date("!%Y%m%dT%H%M%SZ", t)
|
||||
local datestamp = os.date("!%Y%m%d", t) -- Date w/o time, used in credential scope
|
||||
headers["X-Amz-Date"] = amzdate
|
||||
|
||||
-- computing canonical uri
|
||||
local canonical_uri = norm_path(params.path)
|
||||
if canonical_uri ~= "/" then
|
||||
if canonical_uri:sub(-1, -1) == "/" then
|
||||
canonical_uri = canonical_uri:sub(1, -2)
|
||||
end
|
||||
if canonical_uri:sub(1, 1) ~= "/" then
|
||||
canonical_uri = "/" .. canonical_uri
|
||||
end
|
||||
end
|
||||
|
||||
-- computing canonical query string
|
||||
local canonical_qs = {}
|
||||
local canonical_qs_i = 0
|
||||
for k, v in pairs(params.query) do
|
||||
canonical_qs_i = canonical_qs_i + 1
|
||||
canonical_qs[canonical_qs_i] = ngx.unescape_uri(k) .. "=" .. ngx.unescape_uri(v)
|
||||
end
|
||||
|
||||
tab_sort(canonical_qs)
|
||||
canonical_qs = tab_concat(canonical_qs, "&")
|
||||
|
||||
-- computing canonical and signed headers
|
||||
|
||||
local canonical_headers, signed_headers = {}, {}
|
||||
local signed_headers_i = 0
|
||||
for k, v in pairs(headers) do
|
||||
k = k:lower()
|
||||
if k ~= "connection" then
|
||||
signed_headers_i = signed_headers_i + 1
|
||||
signed_headers[signed_headers_i] = k
|
||||
-- strip starting and trailing spaces including strip multiple spaces into single space
|
||||
canonical_headers[k] = str_strip(v)
|
||||
end
|
||||
end
|
||||
tab_sort(signed_headers)
|
||||
|
||||
for i = 1, #signed_headers do
|
||||
local k = signed_headers[i]
|
||||
canonical_headers[i] = k .. ":" .. canonical_headers[k] .. "\n"
|
||||
end
|
||||
canonical_headers = tab_concat(canonical_headers, nil, 1, #signed_headers)
|
||||
signed_headers = tab_concat(signed_headers, ";")
|
||||
|
||||
-- combining elements to form the canonical request (step-1)
|
||||
local canonical_request = params.method:upper() .. "\n"
|
||||
.. canonical_uri .. "\n"
|
||||
.. (canonical_qs or "") .. "\n"
|
||||
.. canonical_headers .. "\n"
|
||||
.. signed_headers .. "\n"
|
||||
.. sha256(params.body or "")
|
||||
|
||||
-- creating the string to sign for aws signature v4 (step-2)
|
||||
local iam = conf.authorization.iam
|
||||
local credential_scope = datestamp .. "/" .. iam.aws_region .. "/"
|
||||
.. iam.service .. "/aws4_request"
|
||||
local string_to_sign = ALGO .. "\n"
|
||||
.. amzdate .. "\n"
|
||||
.. credential_scope .. "\n"
|
||||
.. sha256(canonical_request)
|
||||
|
||||
-- calculate the signature (step-3)
|
||||
local signature_key = get_signature_key(iam.secretkey, datestamp, iam.aws_region, iam.service)
|
||||
local signature = hex_encode(hmac256(signature_key, string_to_sign))
|
||||
|
||||
-- add info to the headers (step-4)
|
||||
headers["authorization"] = ALGO .. " Credential=" .. iam.accesskey
|
||||
.. "/" .. credential_scope
|
||||
.. ", SignedHeaders=" .. signed_headers
|
||||
.. ", Signature=" .. signature
|
||||
end
|
||||
|
||||
|
||||
local serverless_obj = require("apisix.plugins.serverless.generic-upstream")
|
||||
|
||||
return serverless_obj(plugin_name, plugin_version, priority, request_processor, aws_authz_schema)
|
Reference in New Issue
Block a user