Skip to content

fix: support CIDR blocks in no_proxy env variable#2876

Merged
murgatroid99 merged 5 commits intogrpc:masterfrom
melkouri:malak.elkouri/fix-grpc-js-no-proxy-cidr-support
Jan 15, 2025
Merged

fix: support CIDR blocks in no_proxy env variable#2876
murgatroid99 merged 5 commits intogrpc:masterfrom
melkouri:malak.elkouri/fix-grpc-js-no-proxy-cidr-support

Conversation

@melkouri
Copy link
Copy Markdown
Contributor

@melkouri melkouri commented Jan 6, 2025

Issue created #2878

Issue:

The grpc-js lib doesn't support having CIDR blocks in the NO_PROXY variable (like 172.16.0.0/12
192.168.0.0/16). It only checks if the address we target is in the list of IPs in the NO_PROXY. Hence, it supposes that NO_PROXY is a list of single IPs.

For example if the NO_PROXY contains 192.168.0.0/16 and the IP you're trying to reach is 192.168.0.10. This latter is NOT considered in the range of the NO_PROXY because the lib just compares the IP to the elements of the NO_PROXY list.

We also want to support the case where we whitelist domain name suffixes in the NO_PROXY (like example.com, .internal., .local. ).

What is done in this PR:

  • Parse NO_PROXY list to get the ip and the prefix length of the CIDR
  • Check if targeted ip is in the range of the CIDR.
  • Check if targeted host contains the domain suffix.

@linux-foundation-easycla
Copy link
Copy Markdown

linux-foundation-easycla bot commented Jan 6, 2025

CLA Signed

The committers listed above are authorized under a signed CLA.

@melkouri melkouri marked this pull request as ready for review January 6, 2025 17:43
@melkouri melkouri marked this pull request as draft January 7, 2025 10:27
@melkouri melkouri marked this pull request as ready for review January 7, 2025 13:06
for (const host of getNoProxyHostList()) {
const parsedCIDR = parseCIDR(host);
// host is a single IP address or a CIDR notation or a domain
if (parsedCIDR && isIpInCIDR(parsedCIDR, serverHost) || serverHost.endsWith(host)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The case where a non-IP serverHost string is passed to isIpInCIDR is not explicitly handled, making the result unclear. Either this line should avoid making that call, or isIpInCIDR should short-circuit return false if the serverHost is not an IP address, or ipToInt should return an error value if ip is not an IP address and isIpInCIDR should check for that value.

for (const host of getNoProxyHostList()) {
const parsedCIDR = parseCIDR(host);
// host is a single IP address or a CIDR notation or a domain
if (parsedCIDR && isIpInCIDR(parsedCIDR, serverHost) || serverHost.endsWith(host)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original logic uses host === serverHost to check for matches. That has been replaced here with serverHost.endsWith(host). That change is not related to supporting CIDR ranges, so it should be reverted.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original logic where we handle single IPs is handled by isIpInCIDR(parsedCIDR, serverHost) as this latter transforms single IPs to CIDR notations (here).
This serverHost.endsWith(host) was to support the case where we include domain names/suffixes in the NO_PROXY (for example NO_PROXY=example.com,.local.,.local.).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case wasn't stated in the issue, I'll update it and make sure it's well handled in the code. Thank you for raising this 🙇

@melkouri melkouri requested a review from murgatroid99 January 9, 2025 11:14
@melkouri
Copy link
Copy Markdown
Contributor Author

melkouri commented Jan 9, 2025

Thank you @murgatroid99 for your review, I made some changes accordingly to your comments. I hope it's clearer 🙂

const parsedCIDR = parseCIDR(host);
// host is a single IP or a domain name suffix
if (!parsedCIDR) {
if (host === serverHost || serverHost.includes(host)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked and I see that other implementations support suffix matching, so I am OK with keeping the endsWith check here, but includes is definitely not correct. The equality check is also redundant with the substring check, so we should not have both.

Copy link
Copy Markdown
Member

@murgatroid99 murgatroid99 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your contribution, and for being patient through all of the reviews.

@murgatroid99 murgatroid99 merged commit 34b82cb into grpc:master Jan 15, 2025
@melkouri
Copy link
Copy Markdown
Contributor Author

Thank you for the reviews!

@murgatroid99
Copy link
Copy Markdown
Member

This is now out in version 1.13.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants