Multi-proxy with identical host:port overwrites credentials in `auth_store`
Title
Multi-proxy with identical host:port overwrites credentials in auth_store
Description
Since v140.0.7339.123-2, NaiveProxy supports multiple proxies in one process:
"listen": ["...", "..."], "proxy": ["...", "..."]
However, if multiple proxies share the same upstream host and port but use different Basic Auth credentials (e.g. multi-tenant setups, multiplexing different upstreams via reverse proxies like Caddy based on Proxy-Authorization), only the credentials of the last proxy in the list are retained.
Example Configuration:
{
"listen": [
"socks://127.0.0.1:1080",
"socks://127.0.0.1:1081"
],
"proxy": [
"https://userA:[email protected]",
"https://userB:[email protected]"
]
}What Happens:
Both port 1080 and port 1081 send Proxy-Authorization for userB:passB. Connections routed through port 1080 fail or are misrouted because its credentials (userA:passA) are overwritten by userB:passB.
Root Cause Analysis
In
src/net/tools/naive/naive_config.h:std::map<url::SchemeHostPort, AuthCredentials> auth_store;In
src/net/tools/naive/naive_config.cc:auth_store[url::SchemeHostPort{GURL{proxy_url}}] = auth;url::SchemeHostPortonly represents(scheme, host, port)without user credentials. When multiple proxy URIs point to the same host/port, subsequent entries overwrite previous ones in the globalauth_store.In
src/net/tools/naive/naive_proxy_bin.cc:for (const auto& [k, v] : config.auth_store) { auto* session = context->http_transaction_factory()->GetSession(); auto* auth_cache = session->http_auth_cache(); auth_cache->Add(k, HttpAuth::AUTH_PROXY, ...); }All
URLRequestContextinstances end up populated with only the credentials of the last proxy in the list.
Proposed Solution
Since naive_proxy_bin.cc already creates an isolated URLRequestContext per proxy_chain_index (when config.proxy_chains.size() >= 2), we do not need to alter Chromium's url::SchemeHostPort.
Instead, credentials can be isolated per proxy chain by making auth_stores a std::vector:
src/net/tools/naive/naive_config.h:- std::map<url::SchemeHostPort, AuthCredentials> auth_store; + std::vector<std::map<url::SchemeHostPort, AuthCredentials>> auth_stores;src/net/tools/naive/naive_config.cc: Parse and storechain_auth_storefor each proxy chain inproxy_strs:for (const std::string& str : proxy_strs) { + std::map<url::SchemeHostPort, AuthCredentials> chain_auth_store; base::StringTokenizer proxy_uri_list(str, ","); ... - auth_store[url::SchemeHostPort{GURL{proxy_url}}] = auth; + chain_auth_store[url::SchemeHostPort{GURL{proxy_url}}] = auth; ... + auth_stores.push_back(std::move(chain_auth_store)); }src/net/tools/naive/naive_proxy_bin.cc: Populateauth_cachefromconfig.auth_stores[proxy_chain_index]:- for (const auto& [k, v] : config.auth_store) { - auto* session = context->http_transaction_factory()->GetSession(); - auto* auth_cache = session->http_auth_cache(); - auth_cache->Add(k, HttpAuth::AUTH_PROXY, ...); - } + if (proxy_chain_index < static_cast<int>(config.auth_stores.size())) { + for (const auto& [k, v] : config.auth_stores[proxy_chain_index]) { + auto* session = context->http_transaction_factory()->GetSession(); + auto* auth_cache = session->http_auth_cache(); + auth_cache->Add(k, HttpAuth::AUTH_PROXY, + /*realm=*/{}, HttpAuth::AUTH_SCHEME_BASIC, {}, + /*challenge=*/"Basic", v, /*path=*/"/"); + } + }
This cleanly decouples credentials per context and enables full multi-tenant/multi-upstream usage with identical hostnames.
Source: klzgrad/naiveproxy