Skip to content

Commit

Permalink
[Proxy] Support for setting proxy for addresses (#34617)
Browse files Browse the repository at this point in the history
  • Loading branch information
eugeneo committed Oct 18, 2023
1 parent 74c1de6 commit 1a76e7c
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 46 deletions.
3 changes: 2 additions & 1 deletion build_autogenerated.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions include/grpc/impl/channel_arg_names.h
Expand Up @@ -328,6 +328,16 @@
/** Channel arg to set http proxy per channel. If set, the channel arg
* value will be preferred over the environment variable settings. */
#define GRPC_ARG_HTTP_PROXY "grpc.http_proxy"
/** Specifies an HTTP proxy to use for individual addresses.
* The proxy must be specified as an IP address, not a DNS name.
* If set, the channel arg value will be preferred over the environment
* variable settings. */
#define GRPC_ARG_ADDRESS_HTTP_PROXY "grpc.address_http_proxy"
/** Comma separated list of addresses or address ranges that are behind the
* address HTTP proxy.
*/
#define GRPC_ARG_ADDRESS_HTTP_PROXY_ENABLED_ADDRESSES \
"grpc.address_http_proxy_enabled_addresses"
/** If set to non zero, surfaces the user agent string to the server. User
agent is surfaced by default. */
#define GRPC_ARG_SURFACE_USER_AGENT "grpc.surface_user_agent"
Expand Down
113 changes: 90 additions & 23 deletions src/core/ext/filters/client_channel/http_proxy_mapper.cc
Expand Up @@ -26,7 +26,6 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "absl/status/status.h"
#include "absl/status/statusor.h"
Expand Down Expand Up @@ -58,15 +57,10 @@
namespace grpc_core {
namespace {

bool ServerInCIDRRange(absl::string_view server_host,
absl::string_view no_proxy_entry) {
auto server_address = StringToSockaddr(server_host, 0);
if (!server_address.ok()) {
return false;
}
bool ServerInCIDRRange(const grpc_resolved_address& server_address,
absl::string_view cidr_range) {
std::pair<absl::string_view, absl::string_view> possible_cidr =
absl::StrSplit(no_proxy_entry, absl::MaxSplits('/', 2),
absl::SkipEmpty());
absl::StrSplit(cidr_range, absl::MaxSplits('/', 1), absl::SkipEmpty());
if (possible_cidr.first.empty() || possible_cidr.second.empty()) {
return false;
}
Expand All @@ -77,12 +71,34 @@ bool ServerInCIDRRange(absl::string_view server_host,
uint32_t mask_bits = 0;
if (absl::SimpleAtoi(possible_cidr.second, &mask_bits)) {
grpc_sockaddr_mask_bits(&*proxy_address, mask_bits);
return grpc_sockaddr_match_subnet(&*server_address, &*proxy_address,
return grpc_sockaddr_match_subnet(&server_address, &*proxy_address,
mask_bits);
}
return false;
}

bool ExactMatchOrSubdomain(absl::string_view host_name,
absl::string_view host_name_or_domain) {
return absl::EndsWithIgnoreCase(host_name, host_name_or_domain);
}

// Parses the list of host names, addresses or subnet masks and returns true if
// the target address or host matches any value.
bool AddressIncluded(
const absl::optional<grpc_resolved_address>& target_address,
absl::string_view host_name, absl::string_view addresses_and_subnets) {
for (absl::string_view entry :
absl::StrSplit(addresses_and_subnets, ',', absl::SkipEmpty())) {
absl::string_view sanitized_entry = absl::StripAsciiWhitespace(entry);
if (ExactMatchOrSubdomain(host_name, sanitized_entry) ||
(target_address.has_value() &&
ServerInCIDRRange(*target_address, sanitized_entry))) {
return true;
}
}
return false;
}

///
/// Parses the 'https_proxy' env var (fallback on 'http_proxy') and returns the
/// proxy hostname to resolve or nullopt on error. Also sets 'user_cred' to user
Expand Down Expand Up @@ -157,6 +173,33 @@ std::string MaybeAddDefaultPort(absl::string_view target) {
return std::string(target);
}

absl::optional<std::string> GetChannelArgOrEnvVarValue(
const ChannelArgs& args, absl::string_view channel_arg,
const char* env_var) {
auto arg_value = args.GetOwnedString(channel_arg);
if (arg_value.has_value()) {
return arg_value;
}
return GetEnv(env_var);
}

absl::optional<grpc_resolved_address> GetAddressProxyServer(
const ChannelArgs& args) {
auto address_value = GetChannelArgOrEnvVarValue(
args, GRPC_ARG_ADDRESS_HTTP_PROXY, HttpProxyMapper::kAddressProxyEnvVar);
if (!address_value.has_value()) {
return absl::nullopt;
}
auto address = StringToSockaddr(*address_value);
if (!address.ok()) {
gpr_log(GPR_ERROR, "cannot parse value of '%s' env var. Error: %s",
HttpProxyMapper::kAddressProxyEnvVar,
address.status().ToString().c_str());
return absl::nullopt;
}
return *address;
}

} // namespace

absl::optional<std::string> HttpProxyMapper::MapName(
Expand Down Expand Up @@ -191,7 +234,6 @@ absl::optional<std::string> HttpProxyMapper::MapName(
no_proxy_str = GetEnv("no_proxy");
}
if (no_proxy_str.has_value()) {
bool use_proxy = true;
std::string server_host;
std::string server_port;
if (!SplitHostPort(absl::StripPrefix(uri->path(), "/"), &server_host,
Expand All @@ -201,19 +243,15 @@ absl::optional<std::string> HttpProxyMapper::MapName(
"host '%s'",
std::string(server_uri).c_str());
} else {
std::vector<absl::string_view> no_proxy_hosts =
absl::StrSplit(*no_proxy_str, ',', absl::SkipEmpty());
for (const auto& no_proxy_entry : no_proxy_hosts) {
auto entry = absl::StripAsciiWhitespace(no_proxy_entry);
if (absl::EndsWithIgnoreCase(server_host, entry) ||
ServerInCIDRRange(server_host, entry)) {
gpr_log(GPR_INFO, "not using proxy for host in no_proxy list '%s'",
std::string(server_uri).c_str());
use_proxy = false;
break;
}
auto address = StringToSockaddr(server_host, 0);
if (AddressIncluded(address.ok()
? absl::optional<grpc_resolved_address>(*address)
: absl::nullopt,
server_host, *no_proxy_str)) {
gpr_log(GPR_INFO, "not using proxy for host in no_proxy list '%s'",
std::string(server_uri).c_str());
return absl::nullopt;
}
if (!use_proxy) return absl::nullopt;
}
}
*args = args->Set(GRPC_ARG_HTTP_CONNECT_SERVER,
Expand All @@ -229,6 +267,35 @@ absl::optional<std::string> HttpProxyMapper::MapName(
return name_to_resolve;
}

absl::optional<grpc_resolved_address> HttpProxyMapper::MapAddress(
const grpc_resolved_address& address, ChannelArgs* args) {
auto proxy_address = GetAddressProxyServer(*args);
if (!proxy_address.has_value()) {
return absl::nullopt;
}
auto address_string = grpc_sockaddr_to_string(&address, true);
if (!address_string.ok()) {
gpr_log(GPR_ERROR, "Unable to convert address to string: %s",
std::string(address_string.status().message()).c_str());
return absl::nullopt;
}
std::string host_name, port;
if (!SplitHostPort(*address_string, &host_name, &port)) {
gpr_log(GPR_ERROR, "Address %s cannot be split in host and port",
address_string->c_str());
return absl::nullopt;
}
auto enabled_addresses = GetChannelArgOrEnvVarValue(
*args, GRPC_ARG_ADDRESS_HTTP_PROXY_ENABLED_ADDRESSES,
kAddressProxyEnabledAddressesEnvVar);
if (!enabled_addresses.has_value() ||
!AddressIncluded(address, host_name, *enabled_addresses)) {
return absl::nullopt;
}
*args = args->Set(GRPC_ARG_HTTP_CONNECT_SERVER, *address_string);
return proxy_address;
}

void RegisterHttpProxyMapper(CoreConfiguration::Builder* builder) {
builder->proxy_mapper_registry()->Register(
true /* at_start */,
Expand Down
9 changes: 5 additions & 4 deletions src/core/ext/filters/client_channel/http_proxy_mapper.h
Expand Up @@ -35,14 +35,15 @@ namespace grpc_core {

class HttpProxyMapper : public ProxyMapperInterface {
public:
static constexpr char const* kAddressProxyEnvVar = "GRPC_ADDRESS_HTTP_PROXY";
static constexpr char const* kAddressProxyEnabledAddressesEnvVar =
"GRPC_ADDRESS_HTTP_PROXY_ENABLED_ADDRESSES";

absl::optional<std::string> MapName(absl::string_view server_uri,
ChannelArgs* args) override;

absl::optional<grpc_resolved_address> MapAddress(
const grpc_resolved_address& /*address*/,
ChannelArgs* /*args*/) override {
return absl::nullopt;
}
const grpc_resolved_address& address, ChannelArgs* args) override;
};

void RegisterHttpProxyMapper(CoreConfiguration::Builder* builder);
Expand Down
1 change: 1 addition & 0 deletions test/core/client_channel/BUILD
Expand Up @@ -43,6 +43,7 @@ grpc_cc_test(
"//:grpc",
"//src/core:channel_args",
"//test/core/util:grpc_test_util",
"//test/core/util:scoped_env_var",
],
)

Expand Down

0 comments on commit 1a76e7c

Please sign in to comment.