Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: support CIDR blocks in no_proxy env variable #2876

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions doc/environment_variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ can be set.
checked in order, and the first one that has a value is used.

* no_grpc_proxy, no_proxy
A comma separated list of hostnames to connect to without using a proxy even
if a proxy is set. These variables are checked in order, and the first one
A comma separated list of hostnames, IP addresses,
or CIDR blocks to connect to without using a proxy even
if a proxy is set, for example: no_proxy=example.com,192.168.0.1,192.168.0.0/16.
These variables are checked in order, and the first one
that has a value is used.

* GRPC_SSL_CIPHER_SUITES
Expand Down Expand Up @@ -66,4 +68,4 @@ can be set.
* GRPC_NODE_USE_ALTERNATIVE_RESOLVER
Allows changing dns resolve behavior and parse DNS server authority as described in https://github.com/grpc/grpc/blob/master/doc/naming.md
- true - use alternative resolver
- false - use default resolver (default)
- false - use default resolver (default)
64 changes: 56 additions & 8 deletions packages/grpc-js/src/http_proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import { log } from './logging';
import { LogVerbosity } from './constants';
import { Socket } from 'net';
import { isIPv4, Socket } from 'net';
import * as http from 'http';
import * as logging from './logging';
import {
Expand Down Expand Up @@ -119,6 +119,58 @@ function getNoProxyHostList(): string[] {
}
}

interface CIDRNotation {
ip: number;
prefixLength: number;
}

/*
* The groups correspond to CIDR parts as follows:
* 1. ip
* 2. prefixLength
*/

export function parseCIDR(cidrString: string): CIDRNotation | null {
const splitRange = cidrString.split('/');
if (splitRange.length !== 2) {
return null;
}
const prefixLength = parseInt(splitRange[1], 10);
if (!isIPv4(splitRange[0]) || Number.isNaN(prefixLength) || prefixLength < 0 || prefixLength > 32) {
return null;
}
return {
ip: ipToInt(splitRange[0]),
prefixLength: prefixLength
};
}

function ipToInt(ip: string) {
return ip.split(".").reduce((acc, octet) => (acc << 8) + parseInt(octet, 10), 0);
}

function isIpInCIDR(cidr: CIDRNotation, serverHost: string) {
const ip = cidr.ip;
const mask = -1 << (32 - cidr.prefixLength);
const hostIP = ipToInt(serverHost);

return (hostIP & mask) === (ip & mask);
}

function hostMatchesNoProxyList(serverHost: string): boolean {
for (const host of getNoProxyHostList()) {
const parsedCIDR = parseCIDR(host);
// host is a CIDR and serverHost is an IP address
if (isIPv4(serverHost) && parsedCIDR && isIpInCIDR(parsedCIDR, serverHost)) {
return true;
} else if (serverHost.endsWith(host)) {
// host is a single IP or a domain name suffix
return true;
}
}
return false;
}

export interface ProxyMapResult {
target: GrpcUri;
extraOptions: ChannelOptions;
Expand Down Expand Up @@ -147,13 +199,9 @@ export function mapProxyName(
return noProxyResult;
}
const serverHost = hostPort.host;
for (const host of getNoProxyHostList()) {
if (host === serverHost) {
trace(
'Not using proxy for target in no_proxy list: ' + uriToString(target)
);
return noProxyResult;
}
if (hostMatchesNoProxyList(serverHost)) {
trace('Not using proxy for target in no_proxy list: ' + uriToString(target));
return noProxyResult;
}
const extraOptions: ChannelOptions = {
'grpc.http_connect_target': uriToString(target),
Expand Down