-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
net: Dial is not safe to use in namespaces with dual-stack enabled #44922
Comments
CC @bradfitz @ianlancetaylor via owners. |
I'm sorry, I don't understand. Yes, it's very hard to correctly use namespaces within a single Go program. I don't understand the connection to |
The problem is that net.Dial uses go routines for the dual-stack fallback implementation, and this is not evident for the user, so you expect it to make the connection inside the namespace, but it is actually making the connection from the parent namespace. After reading all the related issues and articles, I have clear that is not easy to solve, I think that I will wrap the function and serialise the connection attempts ... |
The Go standard library, like many Go packages, uses goroutines freely as appropriate. When using any mechanism, such as namespaces, that make a goroutine special in some way, that goroutine has to be extremely careful in what it calls. It basically has to stick to trivial functions, or system calls, or cgo calls. I'm going to close this issue since I don't think there is anything we can change, but it will serve as documentation. Thanks. |
Well, it seems that the solution was easier than expected, it turns out that if a host resolves to multiple IP addresses and the FastFallback is disabled net.Dial tries the connections serially, so the solution is as simple as:
|
golang has enabled RFC 6555 Fast Fallback (aka HappyEyeballs) by default in 1.12. It means that if a host resolves to both IPv6 and IPv4, it will try to connect to any of those addresses and use the working connection. However, the implementation uses go routines to start both connections in parallel, and this has limitations when running inside a namespace, so we try to the connections serially, trying IPv4 first for keeping the same behaviour. xref golang/go#44922 Signed-off-by: Antonio Ojea <aojea@redhat.com>
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
yes
https://play.golang.org/p/2kpRWCMdZkj
What operating system and processor architecture are you using (
go env
)?Linux amd64
What did you do?
Using net.Dial inside a "namespace" is not "safe" , at least for the developer :), in dual-stack environments, it creates the connections in the parent namespace.
Golang has enabled RFC 6555 Fast Fallback (aka HappyEyeballs) in net.Dial by default since 1.12, basically, if the destination address resolves to multiple IPs, it tries to connect to them spawning different goroutines and using the working connection..
There is a lot of literature about the topic of namespaces and goroutines in golang, perfectly summarised in this article https://www.weave.works/blog/linux-namespaces-golang-followup
What did you expect to see?
I expect to be able to use net.Dial safely in all kind of environments, namespaced, single or dual-stack, or at least have some way to know that I'm not leaking connections, in this case, I was expecting to create connections inside a namespace but it turned out it was creating the connections in the parent namespace.
I've found this trying to implement port-forwarding for IPv4 and IPv6 in the main kubernetes containers runtimes:
cri-o/cri-o#4639
containerd/containerd#5145
I think that due to the adoption of golang in containers environment, and the "slow adoption of IPv6" using dual-stack (kuberntes just graduated it to beta in 1.21), it will be nice to make golang more container/dual-stack friendly or at least more predictable.
If we add this to the "net: Listen is unfriendly to multiple address families, endpoints and subflows #9334" issue, that means that it will only will open a listener in one address you practically are rolling a dice with golang when you use "localhost" as address in a dual-stack containerized environment.
The text was updated successfully, but these errors were encountered: