Skip to content

Commit

Permalink
Merge pull request #1502 from tkan145/THREESCALE-11412-lua-resty-openssl
Browse files Browse the repository at this point in the history
[THREESCALE-11412] Migrate to lua-resty-openssl
  • Loading branch information
tkan145 authored Nov 4, 2024
2 parents 938929b + 3af4e2d commit 58d15ec
Show file tree
Hide file tree
Showing 25 changed files with 108 additions and 953 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

- Added the `APICAST_HTTPS_VERIFY_CLIENT` variable to allow configuration of the `ssl_verify_client` directive. [PR #1491](https://github.com/3scale/APIcast/pull/1491) [THREESCALE-10156](https://issues.redhat.com/browse/THREESCALE-10156)
- Add `APICAST_LUA_SOCKET_KEEPALIVE_REQUESTS` to limit the number of requests a single keepalive socket can handle [PR #1496](https://github.com/3scale/APIcast/pull/1496) [THREESCALE-11321](https://issues.redhat.com/browse/THREESCALE-11321)
- Replace internal OPENSSL module with lua-resty-openssl [PR #1502](https://github.com/3scale/APIcast/pull/1502) [THREESCALE-11412](https://issues.redhat.com/browse/THREESCALE-11412)

## [3.15.0] 2024-04-04

Expand Down
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ RUN luarocks install --deps-mode=none --tree /usr/local https://luarocks.org/man
RUN luarocks install --deps-mode=none --tree /usr/local https://luarocks.org/manifests/hamish/lua-resty-iputils-0.3.0-1.src.rock
RUN luarocks install --deps-mode=none --tree /usr/local https://luarocks.org/manifests/golgote/net-url-0.9-1.src.rock
RUN luarocks install --deps-mode=none --tree /usr/local https://luarocks.org/manifests/membphis/lua-resty-ipmatcher-0.6.1-0.src.rock
RUN luarocks install --deps-mode=none --tree /usr/local https://luarocks.org/manifests/fffonion/lua-resty-openssl-1.5.1-1.src.rock

RUN yum -y remove libyaml-devel m4 openssl-devel git gcc luarocks && \
rm -rf /var/cache/yum && yum clean all -y && \
Expand Down Expand Up @@ -93,7 +94,7 @@ WORKDIR /opt/app-root/app
USER 1001

ENV LUA_CPATH "./?.so;/usr/lib64/lua/5.1/?.so;/usr/lib64/lua/5.1/loadall.so;/usr/local/lib64/lua/5.1/?.so"
ENV LUA_PATH "/usr/lib64/lua/5.1/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/*/?.lua;"
ENV LUA_PATH "/usr/lib64/lua/5.1/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/*/?.lua;;"

WORKDIR /opt/app-root
ENTRYPOINT ["container-entrypoint"]
Expand Down
35 changes: 18 additions & 17 deletions gateway/Roverfile.lock
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
argparse 0.7.1-1||production
busted 2.2.0-1||testing
date 2.2-2||production
busted 2.2.0-1|02f31a9c103a44e166617cfdb6ba1b8994a9c912|testing
date 2.2-2|8d74567cf979c1eab2c6b6ca2e3b978fa40569a2|production
dkjson 2.8-1||testing
fifo 0.2-0||development
inspect 3.1.3-0||production
jsonschema 0.8-0|c1d72d86bb3dc5b33da57d47febc47657d29ea74|testing
ldoc 1.5.0-1||development
ldoc 1.5.0-1|09f82c959c50d8c3d5a968c379b1c75de66b002d|development
liquid 0.2.0-2||production
lua-resty-env 0.4.0-1||production
lua-resty-execvp 0.1.1-1||production
lua-resty-http 0.17.1-0||production
lua-resty-ipmatcher 0.6.1-0||production
lua-resty-iputils 0.3.0-2||production
lua-resty-jit-uuid 0.0.7-2||production
lua-resty-jwt 0.2.0-0||production
lua-resty-http 0.17.1-0|4ab4269cf442ba52507aa2c718f606054452fcad|production
lua-resty-ipmatcher 0.6.1-0|62d4c44d67227e8f3fe02331c2f8b90fe0d7ccd1|production
lua-resty-iputils 0.3.0-2|6110b41eaa52efd25e56f89e34412ab95f700d57|production
lua-resty-jit-uuid 0.0.7-2|64ae38de75c9d58f330d89e140ac872771c19223|production
lua-resty-jwt 0.2.0-0|2a62ff95eae91df6bd8655080a4b9b04c61bec6b|production
lua-resty-openssl 1.5.1-1|a900c5f5897448c181dd58073e51cdeeb3fd0029|production
lua-resty-repl 0.0.6-0|3878f41b7e8f97b1c96919db19dbee9496569dda|development
lua-resty-url 0.3.5-1||production
lua-term 0.8-1||testing
lua_cliargs 3.0-2||testing
luacov 0.15.0-1||testing
luafilesystem 1.8.0-1||production,development,testing
luassert 1.9.0-1||testing
luasystem 0.4.1-1||testing
luacov 0.15.0-1|19b52ca0298c8942df82dd441d7a4a588db4c413|testing
luafilesystem 1.8.0-1|7c6e1b013caec0602ca4796df3b1d7253a2dd258|production,development,testing
luassert 1.9.0-1|8d8dc8a54cc468048a128a867f6449a6c3fdd11a|testing
luasystem 0.4.1-1|c832d2b857d4174d17247de837426d4cfc95ec2f|testing
lyaml 6.2.8-1||production
markdown 0.33-1||development
markdown 0.33-1|8c09109924b218aaecbfd4d4b1de538269c4d765|development
mediator_lua 1.1.2-0||testing
net-url 1.1-1||testing
nginx-lua-prometheus 0.20181120-3||production
penlight 1.13.1-1||production,development,testing
net-url 1.1-1|32acd84d06e16ddffc975adafce9cea26f3b2dd1|testing
nginx-lua-prometheus 0.20181120-3|379c0a4d4d6f3c5b0eb93691fc7e14fff498e1ca|production
penlight 1.13.1-1|3b42fd05d8e60998b0fc5830a397b2354a81170b|production,development,testing
router 2.1-0||production
say 1.4.1-3||testing
say 1.4.1-3|45a3057e68c52b34ab59ef167efeb2340e356661|testing
1 change: 1 addition & 0 deletions gateway/apicast-scm-1.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ dependencies = {
'nginx-lua-prometheus == 0.20181120',
'lua-resty-jit-uuid',
'lua-resty-ipmatcher',
'lua-resty-openssl'
}
build = {
type = "make",
Expand Down
2 changes: 1 addition & 1 deletion gateway/src/apicast/policy/fapi/fapi.lua
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function _M:access(context)
if self.validate_oauth2_certificate_bound_access_token then
if not context.jwt then return error(context.service or {}) end

local cert = X509.parse_pem_cert(ngx.var.ssl_client_raw_cert)
local cert = X509.new(ngx.var.ssl_client_raw_cert)

if not check_certificate(cert, context.jwt.cnf) then
ngx.log(ngx.WARN, 'fapi oauth_mtls failed for service ', context.service and context.service.id)
Expand Down
2 changes: 1 addition & 1 deletion gateway/src/apicast/policy/oauth_mtls/oauth_mtls.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ end
function _M.access(_, context)
if not context.jwt then return error(context.service or {}) end

local cert = X509.parse_pem_cert(ngx.var.ssl_client_raw_cert)
local cert = X509.new(ngx.var.ssl_client_raw_cert)

if not check_certificate(cert, context.jwt.cnf) then
return error(context.service or {})
Expand Down
20 changes: 15 additions & 5 deletions gateway/src/apicast/policy/tls_validation/tls_validation.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ local debug = ngx.config.debug

local function init_trusted_store(store, certificates)
for _,certificate in ipairs(certificates) do
local cert, err = X509.parse_pem_cert(certificate.pem_certificate) -- TODO: handle errors
local cert, err = X509.new(certificate.pem_certificate) -- TODO: handle errors

if cert then
store:add_cert(cert)
store:add(cert)

if debug then
ngx.log(ngx.DEBUG, 'adding certificate to the tls validation ', tostring(cert:subject_name()), ' SHA1: ', cert:hexdigest('SHA1'))
Expand Down Expand Up @@ -60,21 +60,31 @@ function _M:ssl_certificate()
end

function _M:access()
local cert = X509.parse_pem_cert(ngx.var.ssl_client_raw_cert)
if not cert then
local client_cert = ngx.var.ssl_client_raw_cert
if not client_cert then
ngx.status = self.error_status
ngx.say("No required TLS certificate was sent")
return ngx.exit(ngx.status)
end

local cert, err = X509.new(client_cert)
if not cert then
ngx.status = self.error_status
ngx.log(ngx.WARN, "Invalid TLS certificate, err: ", err)
ngx.say("Invalid TLS certificate")
return ngx.exit(ngx.status)
end

local store = self.x509_store
store:set_flags(store.verify_flags.X509_V_FLAG_PARTIAL_CHAIN)

-- err is printed inside validate_cert method
-- so no need capture the err here
local ok, _ = store:validate_cert(cert)
local ok, err = store:verify(cert)

if not ok then
ngx.status = self.error_status
ngx.log(ngx.INFO, "TLS certificate validation failed, err: ", err)
ngx.say("TLS certificate validation failed")
return ngx.exit(ngx.status)
end
Expand Down
6 changes: 3 additions & 3 deletions gateway/src/apicast/policy/upstream_mtls/upstream_mtls.lua
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,17 @@ local function read_ca_certificates(ca_certificates)
local valid = false
local store = X509_STORE.new()
for _,certificate in pairs(ca_certificates) do
local cert, err = X509.parse_pem_cert(certificate)
local cert, err = X509.new(certificate)
if cert then
valid = true
store:add_cert(cert)
store:add(cert)
else
ngx.log(ngx.INFO, "cannot load certificate, err: ", err)
end
end

if valid then
return store.store
return store.ctx
end
end

Expand Down
132 changes: 8 additions & 124 deletions gateway/src/resty/oidc/jwk.lua
Original file line number Diff line number Diff line change
@@ -1,127 +1,11 @@
local ipairs = ipairs

local b64 = require('ngx.base64')
local ffi = require('ffi')
local tab_new = require('resty.core.base').new_tab
local base = require('resty.openssl.base')

ffi.cdef [[
typedef struct bio_st BIO;
typedef struct bio_method_st BIO_METHOD;
BIO_METHOD *BIO_s_mem(void);
BIO * BIO_new(BIO_METHOD *type);
void BIO_vfree(BIO *a);
int BIO_read(BIO *b, void *data, int len);

size_t BIO_ctrl_pending(BIO *b);

typedef struct bignum_st BIGNUM;
typedef void FILE;

BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
BIGNUM *BN_new(void);
void BN_free(BIGNUM *a);

int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d);
RSA * RSA_new(void);

void RSA_free(RSA *rsa);

int PEM_write_RSA_PUBKEY(FILE *fp, RSA *x);
int PEM_write_bio_RSA_PUBKEY(BIO *bp, RSA *x);
]]

local C = ffi.C
local ffi_gc = ffi.gc
local ffi_assert = base.ffi_assert
local pkey = require ('resty.openssl.pkey')
local cjson = require ('cjson')

local _M = { }

_M.jwk_to_pem = { }

local function b64toBN(str)
local val, err = b64.decode_base64url(str)
if not val then return nil, err end

local bn = ffi_assert(C.BN_new())
ffi_gc(bn, C.BN_free)

ffi_assert(C.BN_bin2bn(val, #val, bn))

return bn
end

local function read_BIO(bio)
-- BIO_ctrl_pending() return the amount of pending data.
local len = C.BIO_ctrl_pending(bio)
local buf = ffi.new("char[?]", len)
ffi_assert(C.BIO_read(bio, buf, len) >= 0)
return ffi.string(buf, len)
end

local bio_mem = C.BIO_s_mem()

local function new_BIO()
local bio = ffi_assert(C.BIO_new(bio_mem))

ffi_gc(bio, C.BIO_vfree)

return bio
end

local function RSA_to_PEM(rsa)
local bio = new_BIO()

ffi_assert(C.PEM_write_bio_RSA_PUBKEY(bio, rsa), 1)

return read_BIO(bio)
end


local function RSA_new(n, e, d)
--- https://github.com/sfackler/rust-openssl/blob/2df87cfd5974da887b5cb84c81e249f485bed9f7/openssl/src/rsa.rs#L420-L437
local rsa = ffi_assert(C.RSA_new())
ffi_gc(rsa, C.RSA_free)

--[[
The n, e and d parameter values can be set by calling RSA_set0_key()
and passing the new values for n, e and d as parameters to the function.
The values n and e must be non-NULL the first time this function is called
on a given RSA object. The value d may be NULL.
]]--

ffi_assert(C.RSA_set0_key(rsa, n, e, d), 1)

--[[
Calling this function transfers the memory management of the values
to the RSA object, and therefore the values that have been passed
in should not be freed by the caller after this function has been called.
]]--
ffi_gc(n, nil)
ffi_gc(e, nil)

return rsa
end

function _M.jwk_to_pem.RSA(jwk)
local n, e, err

-- parameter n: Base64 URL encoded string representing the modulus of the RSA Key.
n, err = b64toBN(jwk.n)
if err then return nil, err end

-- parameter e: Base64 URL encoded string representing the public exponent of the RSA Key.
e, err = b64toBN(jwk.e)
if err then return nil, err end

local rsa = RSA_new(n, e)

-- jwk.rsa = rsa
jwk.pem = RSA_to_PEM(rsa)

return jwk
end

function _M.convert_keys(res, ...)
if not res then return nil, ... end
local keys = tab_new(0, #res.keys)
Expand All @@ -134,13 +18,13 @@ function _M.convert_keys(res, ...)
end

function _M.convert_jwk_to_pem(jwk)
local fun = _M.jwk_to_pem[jwk.kty]

if not fun then
return nil, 'unsupported kty'
end
local val, err = pkey.new(cjson.encode(jwk), { format = "JWK" })
if not val then
return nil, err
end
jwk.pem = val:tostring("public", "PEM")

return fun(jwk)
return jwk
end

return _M
Loading

0 comments on commit 58d15ec

Please sign in to comment.