From b3625c38e3b8ea54e818e41be48f68d40007edab Mon Sep 17 00:00:00 2001 From: An Tran Date: Wed, 18 Mar 2026 14:04:45 +1000 Subject: [PATCH] fix: verify CA certificate when sending request via proxy --- CHANGELOG.md | 1 + gateway/src/apicast/http_proxy.lua | 20 +++++++------------- gateway/src/apicast/upstream.lua | 16 +++++++++------- gateway/src/resty/http/proxy.lua | 19 +++++++------------ spec/http_proxy_spec.lua | 2 +- 5 files changed, 25 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5e85fec0..f1f34c121 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Correct FAPI header to `x-fapi-interaction-id` [PR #1557](https://github.com/3scale/APIcast/pull/1557) [THREESCALE-11957](https://issues.redhat.com/browse/THREESCALE-11957) - Only validate oidc setting if authentication method is set to oidc [PR #1568](https://github.com/3scale/APIcast/pull/1568) [THREESCALE-11441](https://issues.redhat.com/browse/THREESCALE-11441) +- Server certificate is never verified in API request for https backend via proxy [PR #1573](https://github.com/3scale/APIcast/pull/1568) [THREESCALE-11944](https://redhat.atlassian.net/browse/THREESCALE-11944) ### Added - Update APIcast schema manifest [PR #1550](https://github.com/3scale/APIcast/pull/1550) diff --git a/gateway/src/apicast/http_proxy.lua b/gateway/src/apicast/http_proxy.lua index 0481b2a9c..c7684b861 100644 --- a/gateway/src/apicast/http_proxy.lua +++ b/gateway/src/apicast/http_proxy.lua @@ -44,10 +44,10 @@ local function resolve_servers(uri) return resolver:get_servers(uri.host, uri) end -local function forward_https_request(proxy_uri, uri, proxy_opts) +local function forward_https_request(proxy_uri, uri, options) local body, err local sock - local opts = proxy_opts or {} + local opts = options or {} local req_method = ngx_get_method() local encoding = ngx.req.get_headers()["Transfer-Encoding"] local is_chunked = encoding and encoding:lower() == "chunked" @@ -143,7 +143,7 @@ local function forward_https_request(proxy_uri, uri, proxy_opts) path = format('%s%s%s', ngx.var.uri, ngx.var.is_args, ngx.var.query_string or ''), body = body, proxy_uri = proxy_uri, - proxy_options = opts + options = opts } local httpc, err = http_proxy.new(request) @@ -189,7 +189,7 @@ function _M.find(upstream) return get_proxy_uri(upstream.uri) end -function _M.request(upstream, proxy_uri) +function _M.request(upstream, proxy_uri, options) local uri = upstream.uri local proxy_auth @@ -220,15 +220,9 @@ function _M.request(upstream, proxy_uri) return elseif uri.scheme == 'https' then upstream:rewrite_request() - local proxy_opts = { - proxy_auth = proxy_auth, - skip_https_connect = upstream.skip_https_connect, - request_unbuffered = upstream.request_unbuffered, - upstream_connection_opts = upstream.upstream_connection_opts, - upstream_ssl = upstream.upstream_ssl - } - - forward_https_request(proxy_uri, uri, proxy_opts) + options.proxy_auth = proxy_auth + + forward_https_request(proxy_uri, uri, options) return ngx.exit(ngx.OK) -- terminate phase else ngx.log(ngx.ERR, 'could not connect to proxy: ', proxy_uri, ' err: ', 'invalid request scheme') diff --git a/gateway/src/apicast/upstream.lua b/gateway/src/apicast/upstream.lua index ef2097dc9..a551b0772 100644 --- a/gateway/src/apicast/upstream.lua +++ b/gateway/src/apicast/upstream.lua @@ -231,14 +231,16 @@ function _M:call(context) self:set_skip_https_connect_on_proxy(); end - self.request_unbuffered = context.request_unbuffered - self.upstream_connection_opts = context.upstream_connection_opts - self.upstream_ssl = { - ssl_verify = context.upstream_verify, - ssl_client_cert = context.upstream_certificate, - ssl_client_priv_key = context.upstream_key + local options = { + request_unbuffered = context.request_unbuffered, + upstream_connection_opts = context.upstream_connection_opts, + upstream_ssl = { + ssl_verify = context.upstream_verify, + ssl_client_cert = context.upstream_certificate, + ssl_client_priv_key = context.upstream_key + } } - http_proxy.request(self, proxy_uri) + http_proxy.request(self, proxy_uri, options) else local err = self:rewrite_request() if err then diff --git a/gateway/src/resty/http/proxy.lua b/gateway/src/resty/http/proxy.lua index e02132c10..2cb065118 100644 --- a/gateway/src/resty/http/proxy.lua +++ b/gateway/src/resty/http/proxy.lua @@ -32,7 +32,7 @@ end local function connect(request) request = request or { } local httpc = http.new() - local proxy_options = request.proxy_options or {} + local proxy_options = request.options or {} if proxy_options.upstream_connection_opts then local con_opts = request.proxy_options.upstream_connection_opts @@ -54,10 +54,6 @@ local function connect(request) local port = default_port(uri) local skip_https_connect = proxy_options.skip_https_connect - -- set ssl_verify: lua-resty-http set ssl_verify to true by default if scheme is https, whereas - -- openresty treat nil as false, so we need to explicitly set ssl_verify to false if nil - local ssl_verify = request.options and request.options.ssl and request.options.ssl.verify or false - -- We need to set proxy_opts to an empty table here otherwise, lua-resty-http will fallback -- to the global proxy options local options = { @@ -68,10 +64,12 @@ local function connect(request) } if scheme == 'https' then options.ssl_server_name = host - options.ssl_verify = ssl_verify - if proxy_options.upstream_ssl then - options.ssl_client_cert = proxy_options.upstream_ssl.ssl_client_cert - options.ssl_client_priv_key = proxy_options.upstream_ssl.ssl_client_priv_key + if proxy_options.ssl then + -- set ssl_verify: lua-resty-http set ssl_verify to true by default if scheme is https, whereas + -- openresty treat nil as false, so we need to explicitly set ssl_verify to false if nil + options.ssl_verify = proxy_options.ssl.verify or false + options.ssl_client_cert = proxy_options.ssl.client_cert + options.ssl_client_priv_key = proxy_options.ssl.client_priv_key end end @@ -114,9 +112,6 @@ local function connect(request) ngx.log(ngx.DEBUG, 'targeting server ', host, ':', port) - local ok, err = httpc:ssl_handshake(nil, host, request.ssl_verify) - if not ok then return nil, err end - return httpc elseif scheme == 'https' then options.proxy_opts = { diff --git a/spec/http_proxy_spec.lua b/spec/http_proxy_spec.lua index a4c5b677f..78d988c1b 100644 --- a/spec/http_proxy_spec.lua +++ b/spec/http_proxy_spec.lua @@ -44,7 +44,7 @@ describe('http_proxy', function() it('terminates phase', function() local http_proxy = require('apicast.http_proxy') - http_proxy.request(upstream, proxy_uri) + http_proxy.request(upstream, proxy_uri, {}) assert.spy(ngx.exit).was_called_with(ngx.OK) end) end)