Skip to content

Commit

Permalink
Add support to use Basic Authentication with the HTTP(s) proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
An Tran committed Aug 31, 2023
1 parent 32b8f07 commit 9590e66
Show file tree
Hide file tree
Showing 4 changed files with 384 additions and 4 deletions.
8 changes: 8 additions & 0 deletions gateway/src/apicast/http_proxy.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ local resty_resolver = require 'resty.resolver'
local round_robin = require 'resty.balancer.round_robin'
local http_proxy = require 'resty.http.proxy'
local file_reader = require("resty.file").file_reader
local concat = table.concat

local _M = { }

Expand Down Expand Up @@ -156,6 +157,13 @@ end
function _M.request(upstream, proxy_uri)
local uri = upstream.uri

if not ngx.var.proxy_authorization then
if proxy_uri.user or proxy_uri.password then
local proxy_auth = "Basic " .. ngx.encode_base64(concat({ proxy_uri.user or '', proxy_uri.password or '' }, ':'))
ngx.req.set_header("Proxy-Authorization", proxy_auth)
end
end

if uri.scheme == 'http' then -- rewrite the request to use http_proxy
local err
local host = upstream:set_host_header()
Expand Down
13 changes: 9 additions & 4 deletions gateway/src/resty/http/proxy.lua
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,22 @@ local function _connect_proxy_https(httpc, request, host, port)

local uri = request.uri

local ok, err = httpc:request({
local res, err = httpc:request({
method = 'CONNECT',
path = format('%s:%s', host, port or default_port(uri)),
headers = {
['Host'] = request.headers.host or format('%s:%s', uri.host, default_port(uri)),
['Proxy-Authorization'] = request.headers["Proxy-Authorization"] or ''
}
})
if not ok then return nil, err end
if not res then return nil, err end

ok, err = httpc:ssl_handshake(nil, uri.host, request.ssl_verify)
if not ok then return nil, err end
if res.status < 200 or res.status > 299 then
return nil, "failed to establish a tunnel through a proxy: " .. res.status
end

res, err = httpc:ssl_handshake(nil, uri.host, request.ssl_verify)
if not res then return nil, err end

return httpc
end
Expand Down
201 changes: 201 additions & 0 deletions t/apicast-policy-camel.t
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,204 @@ ETag: foobar
<<EOF
using proxy: http://127.0.0.1:$Test::Nginx::Util::PROXY_SSL_PORT,
EOF


=== TEST 5: API backend connection uses http proxy with Basic Auth
--- configuration
{
"services": [
{
"id": 42,
"backend_version": 1,
"backend_authentication_type": "service_token",
"backend_authentication_value": "token-value",
"proxy": {
"api_backend": "http://test-upstream.lvh.me:$TEST_NGINX_SERVER_PORT/",
"proxy_rules": [
{ "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 }
],
"policy_chain": [
{
"name": "apicast.policy.apicast"
},
{
"name": "apicast.policy.camel",
"configuration": {
"http_proxy": "http://foo:bar@127.0.0.1:$TEST_NGINX_HTTP_PROXY_PORT"
}
}
]
}
}
]
}
--- backend
location /transactions/authrep.xml {
content_by_lua_block {
ngx.exit(ngx.OK)
}
}
--- upstream
server_name test-upstream.lvh.me;
location / {
access_by_lua_block {
assert = require('luassert')
local proxy_auth = ngx.req.get_headers()['Proxy-Authorization']
assert.equals(proxy_auth, "Basic Zm9vOmJhcg==")
ngx.say("yay, api backend")
}
}
--- request
GET /?user_key=value
--- response_body
yay, api backend
--- error_code: 200
--- error_log env
using proxy: http://foo:bar@127.0.0.1:$TEST_NGINX_HTTP_PROXY_PORT

=== TEST 6: API backend using all_proxy with Basic Auth
--- configuration
{
"services": [
{
"id": 42,
"backend_version": 1,
"backend_authentication_type": "service_token",
"backend_authentication_value": "token-value",
"proxy": {
"api_backend": "http://test-upstream.lvh.me:$TEST_NGINX_SERVER_PORT/",
"proxy_rules": [
{ "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 }
],
"policy_chain": [
{
"name": "apicast.policy.apicast"
},
{
"name": "apicast.policy.http_proxy",
"configuration": {
"all_proxy": "http://foo:bar@127.0.0.1:$TEST_NGINX_HTTP_PROXY_PORT"
}
}
]
}
}
]
}
--- backend
location /transactions/authrep.xml {
content_by_lua_block {
ngx.exit(ngx.OK)
}
}
--- upstream
server_name test-upstream.lvh.me;
location / {
access_by_lua_block {
assert = require('luassert')
local proxy_auth = ngx.req.get_headers()['Proxy-Authorization']
assert.equals(proxy_auth, "Basic Zm9vOmJhcg==")
ngx.say("yay, api backend")
}
}
--- request
GET /?user_key=value
--- response_body
yay, api backend
--- error_code: 200
--- error_log env
using proxy: http://foo:bar@127.0.0.1:$TEST_NGINX_HTTP_PROXY_PORT


=== TEST 7: using HTTPS proxy for backend with Basic Auth.
--- ONLY
--- init eval
$Test::Nginx::Util::PROXY_SSL_PORT = Test::APIcast::get_random_port();
$Test::Nginx::Util::ENDPOINT_SSL_PORT = Test::APIcast::get_random_port();
--- configuration random_port env eval
<<EOF
{
"services": [
{
"backend_version": 1,
"proxy": {
"api_backend": "https://localhost:$Test::Nginx::Util::ENDPOINT_SSL_PORT",
"proxy_rules": [
{ "pattern": "/test", "http_method": "GET", "metric_system_name": "hits", "delta": 2 }
],
"policy_chain": [
{
"name": "apicast.policy.apicast"
},
{
"name": "apicast.policy.camel",
"configuration": {
"https_proxy": "http://foo:bar\@127.0.0.1:$Test::Nginx::Util::PROXY_SSL_PORT"
}
}
]
}
}
]
}
EOF
--- backend
location /transactions/authrep.xml {
content_by_lua_block {
ngx.exit(ngx.OK)
}
}
--- upstream eval
<<EOF
# Endpoint config
listen $Test::Nginx::Util::ENDPOINT_SSL_PORT ssl;

ssl_certificate $Test::Nginx::Util::ServRoot/html/server.crt;
ssl_certificate_key $Test::Nginx::Util::ServRoot/html/server.key;

server_name _ default_server;

location /test {
access_by_lua_block {
assert = require('luassert')
local proxy_auth = ngx.req.get_headers()['Proxy-Authorization']
assert.equals(proxy_auth, "Basic Zm9vOmJhcg==")

assert.equal('https', ngx.var.scheme)
assert.equal('$Test::Nginx::Util::ENDPOINT_SSL_PORT', ngx.var.server_port)
assert.equal('localhost', ngx.var.ssl_server_name)
assert.equal(ngx.var.request_uri, '/test?user_key=test3')

local host = ngx.req.get_headers()["Host"]
assert.equal(host, 'localhost:$Test::Nginx::Util::ENDPOINT_SSL_PORT')
ngx.say("yay, endpoint backend")

}
}
}
server {
# Proxy config
listen $Test::Nginx::Util::PROXY_SSL_PORT ssl;

ssl_certificate $Test::Nginx::Util::ServRoot/html/server.crt;
ssl_certificate_key $Test::Nginx::Util::ServRoot/html/server.key;


server_name _ default_server;

location ~ /.* {
proxy_http_version 1.1;
proxy_pass https://\$http_host;
}
EOF
--- request
GET /test?user_key=test3
--- more_headers
User-Agent: Test::APIcast::Blackbox
ETag: foobar
--- error_code: 200
--- user_files fixture=tls.pl eval
--- error_log eval
<<EOF
using proxy: http://foo:bar\@127.0.0.1:$Test::Nginx::Util::PROXY_SSL_PORT,
EOF
Loading

0 comments on commit 9590e66

Please sign in to comment.