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

x/net/http/httpproxy: no_proxy no longer matches an ip suffix when used as hostname #72123

Open
nicholasngai opened this issue Mar 6, 2025 · 8 comments
Labels
BugReport Issues describing a possible bug in the Go implementation. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@nicholasngai
Copy link

nicholasngai commented Mar 6, 2025

Go version

go version go1.24.1 darwin/amd64

Output of go env in your module/workspace:

AR='ar'
CC='cc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='c++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/Users/nngai/Library/Caches/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/nngai/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/ts/hlpvmfc143v104lrvqjg58040000gn/T/go-build4286700706=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='amd64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/Users/nngai/test/go.mod'
GOMODCACHE='/Users/nngai/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/nngai/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/Cellar/go/1.24.1/libexec'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/nngai/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/Cellar/go/1.24.1/libexec/pkg/tool/darwin_amd64'
GOVCS=''
GOVERSION='go1.24.1'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

We are leveraging no_proxy in our environment which includes .1 in the list. In Go 1.24.0 and 1.23.6 and prior, this matched IP addresses such as 1.1.1.1. However, in Go 1.24.1 and 1.23.7, this hostname is no longer matched.

Here’s a simple repro:

package main

import (
	"fmt"
	"net/http"
	"os"
)

func main() {
	req, err := http.NewRequest(http.MethodGet, os.Args[1], http.NoBody)
	if err != nil {
		panic(err)
	}

	proxyUrl, err := http.ProxyFromEnvironment(req)
	if err != nil {
		panic(err)
	}
	fmt.Println("Proxy:", proxyUrl)
}

What did you see happen?

In Go 1.23.6 and 1.24.0, the behavior is as so:

$ http_proxy=http://non.existent no_proxy=.1 GOTOOLCHAIN=go1.23.6 go run main.go http://1.1.1.1/
Proxy: <nil>

$ http_proxy=http://non.existent no_proxy=.1 GOTOOLCHAIN=go1.24.0 go run main.go http://1.1.1.1/
Proxy: <nil>

In Go 1.23.7 and 1.24.1, the behavior is as so:

$ http_proxy=http://non.existent no_proxy=.1 GOTOOLCHAIN=go1.23.7 go run main.go http://1.1.1.1/
Proxy: http://non.existent

$ http_proxy=http://non.existent no_proxy=.1 GOTOOLCHAIN=go1.24.1 go run main.go http://1.1.1.1/
Proxy: http://non.existent

This is a breaking change to Go. 😞

What did you expect to see?

The same behavior that worked in Go 1.23.6 and 1.24.0 should still function in 1.23.7 and 1.24.1 according to the Go compatibility promise.

@nicholasngai
Copy link
Author

This appears to be related to #71984 and its associated CLs.

@gabyhelp gabyhelp added the BugReport Issues describing a possible bug in the Go implementation. label Mar 6, 2025
@prattmic
Copy link
Member

prattmic commented Mar 6, 2025

cc @neild @golang/security

@prattmic prattmic added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Mar 6, 2025
@prattmic prattmic added this to the Go1.25 milestone Mar 6, 2025
@seankhliao
Copy link
Member

This is for #71984, note that this was never documented to work
What's allowed is a suffix match against a domain, or a prefix (cidr) match against IP, which in general maps to control over the unmatched parts.
https://1.1.1.1 is a rare case where an ip address is a canonical name, but it doesn't change the fact that it's still an ip address that should be matched by cidr, rather than be treated as a dns hostname.

https://pkg.go.dev/golang.org/x/net/http/httpproxy#Config

@seankhliao seankhliao changed the title net/http: Go 1.23.7/1.24.1 introduces regression in no_proxy x/net/http/httpproxy: no_proxy no longer matches an ip suffix when used as hostname Mar 6, 2025
@nicholasngai
Copy link
Author

nicholasngai commented Mar 6, 2025

Sadly, there is no standardization of behavior of no_proxy matching. GitLab has a good write-up about this.

Notably, most software out there always treats IP addresses as hostnames like any other for purposes of matching, according to the table on the GitLab page. wget, Python, .NET, etc. always does string matching (no_proxy=.1 matches 1.1.1.1), and these pieces of software don’t support CIDR notation.

Would it be reasonable to support both for IPv4? IMO this is well-supported enough in the broader ecosystem among other runtimes and programming languages, and it feels like something that should be covered in the Go 1 compatibility guarantee since this used to work.

@seankhliao
Copy link
Member

Notably curl doesn't support a suffix ip address match.
There's a security carve out for go1 compat, and it can be insecure to only match the last part of an ip address, like matching only the subdomain of a dns hostname.

In your case, actually specifying the full match doesn't seem to be much more work, and should result in overall more correct behaviour across a range of inputs.

@neild
Copy link
Contributor

neild commented Mar 6, 2025

Suffix matching on IP addresses is incredibly weird. The IP hierarchy doesn't go in that direction; X.X.X.1 doesn't have any relation to Y.Y.Y.1.

Prefix matching does make some sense, and we support that.

I am curious whether any of the implementations that string match on IPs are also vulnerable to #71984, where an IPv6 address with a zone ID is improperly matched against a host pattern: "::1%.example.com" definitely should not match ".example.com".

I see several possible approaches we could take here:

  1. Say that while it makes very little sense, we used to match .1 with 127.0.0.1 and therefore we will continue to do so in the future. Put in a special case to handle this. Be careful not to match against zone IDs. Backport.
  2. More complicated version of (1): Say that while we shouldn't match .1 with 127.0.0.1, we shouldn't change our behavior in a minor release. Put in a special case to handle this for 1.23/1.24. Leave the new behavior in 1.25 and ongoing.
  3. More complicated version of (2): Put in a GODEBUG as well.
  4. Say that matching partial IPv4 addresses was a bug and leave the bugfix as-is.

@nicholasngai
Copy link
Author

nicholasngai commented Mar 6, 2025

There's a security carve out for go1 compat, and it can be insecure to only match the last part of an ip address, like matching only the subdomain of a dns hostname.

Sure! I’m not saying that there aren’t security implications to doing suffix matching on IP addresses. The fact is this is a fairly precedented behavior among the majority of software outside of Go.

I see several possible approaches we could take here:

IMO (3) would be my preferred approach. It aligns with the philosophy presented in the compatibility blog post, and this doesn’t feel like a case for not doing GODEBUG because “doing so is infeasible.”

It definitely is a security risk (I hope people aren’t generally implementing a no_proxy=.1 use case in production lol) but suffix matching IP addresses is not urgent one unlike the IPv6 zone IDs exploit. It’s probably worth removing in Go, but gingerly in a new 1.25 version rather than in 1.24.1 and 1.23.7.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BugReport Issues describing a possible bug in the Go implementation. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

5 participants