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: delay stack-snooping syscalls beyond import time #16789
Comments
One way to connect to things is to bind the address to the socket before connect(). Any golang client that imports net/http does this [1], and so we need to allow it. [1]: golang/go#16789 Signed-off-by: Tycho Andersen <tycho.andersen@canonical.com>
This isn't the http package. This is the net package. You can see this if you change your program to just The If your seccomp policy is to explode your computer, though, Go can't help there. All Go can do is look at the return value from the system call. What is your policy? |
The policy is to send a SIGSYS (i.e. SECCOMP_RET_KILL), not to return an EPERM or something like that, which freaks golang out (naturally :). It would be nice to only do this probe if we actually invoke some IPV6 support. |
It's also testing IPv4 and figuring out whether the system can do dual-stack stuff. See comments in ipsock_posix.go: func probeIPv4Stack() bool {
s, err := socketFunc(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
switch err {
case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT:
return false
case nil:
closeFunc(s)
}
return true
}
// Should we try to use the IPv4 socket interface if we're
// only dealing with IPv4 sockets? As long as the host system
// understands IPv6, it's okay to pass IPv4 addresses to the IPv6
// interface. That simplifies our code and is most general.
// Unfortunately, we need to run on kernels built without IPv6
// support too. So probe the kernel to figure it out.
//
// probeIPv6Stack probes both basic IPv6 capability and IPv6 IPv4-
// mapping capability which is controlled by IPV6_V6ONLY socket
// option and/or kernel state "net.inet6.ip6.v6only".
// It returns two boolean values. If the first boolean value is
// true, kernel supports basic IPv6 functionality. If the second
// boolean value is true, kernel supports IPv6 IPv4-mapping.
func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { etc. I'm not sure this is worth us changing anything, since you're the first person to complain about this. It seems you can just change your configuration to SECCOMP_RET_ERRNO or whatever, telling Go "no" and letting us proceed. If we supported this configuration, we would also need to test this configuration going forward (else it would inevitably regress), and it seems like a lot of work to test for marginal benefit. Convince us otherwise? /cc @mikioh @ianlancetaylor @mdempsky for other opinions. |
I don't necessarily mind a resolution of WONTFIX here if it's too much work, as we can probably just safely add bind() to our seccomp policy in this case; I just thought I'd point it out, as it seemed a little odd. Anyway, we'll see what other people have to say, if anything. Otherwise feel free to close this. |
It's fine to propagate sync.Once(probe{IPv4,IPv6,TCP}Stack) in the exposed API of net package to delay calling socket system calls, though I have no strong opinion about some audit trail on the stack comprised of Go APIs and kernel system calls. |
I have also just encountered this issue (and I think there will be more in the future). The new Ubuntu snappy system uses seccomp extensively to ensure that a "snap" only uses the privileges that it's been given. You need to require the "network-bind" interface (as opposed to the "network" interface) in order to be able to use the bind system call (see http://snapcraft.io/docs/reference/interfaces) This init-time probe means that all Go programs that import /net, even if they're only importing it for IP address parsing, will require the "network-bind" interface. It would be more appropriate to do this probe with sync.Once only when actual access to the network stack is required. Would that result in significant performance loss? |
@rogpeppe, did you write your seccomp policy, or did it come with a snap? That is, did somebody publish a Go binary in a snap with a non-working policy? |
I think it doesn't matter what is the policy of distros, but the language standard library should fit the minimum necessary privileges good practice. In this specific case, can't something simpler be used, like reading |
The very best would be to not attempt any bind unless asked to explicitly ("do what I tell you to do, and nothing else"); also my 2c from a security perspective: software that binds when not asked to is one of the first trigger signs for further analysis 😉 which would of course turn out to be noise in the case of Go binaries. As far as I know IPv4 cannot be disabled entirely from Linux kernel (not sure about BSDs); talking about IPv6 instead, I've verified that checking
I understand why using By reading around and on the On lack of practical solutions for this, then one could just avoid probing and fail with the OS-level error when something which is not supported is being attempted. |
I think you're repeating yourself, or at least other points already made in this thread. No need to argue the point further. I think everybody agrees. The current implementation was done out of pragmatism and portability. @mikioh can elaborate further. The best help right now would be somebody working on a patch. |
@bradfitz I am asking in both comments if alternative approaches would be viable, and that has been left unanswered. I would always ask for this before attempting a patch. Specifically:
Known the above, any patch would be greatly more useful and less of a waste :) |
I don't remember how all dozen operating systems use/need the three probe variables. Whatever you do, you can't regress any behavior. Linux specific things are fine on Linux if they're reliable and not only in super recent kernel versions. But there's also Windows, Solaris, BSDs, Darwin, etc. edit: the word "can't" above was wrong when I typed it on my phone. |
In any case, most of closing this bug is doing the research on why it was done how it's currently done, and figuring out what to do instead on each operating system. I don't have the answers off hand. @mikioh might, but I don't. Ideally any patch would also document things better, since it wasn't entirely obvious to me from reading the source. I think we could probably start by doing the computation of the three supportsIP* booleans lazily, but still doing the bind-based probing on all operating systems. And then in a second change, you could change the probing on Linux to do something Linux-specific without bind. Maybe the three probe variables also need to be split into more finer-grained questions, if the variables are used for a combination of questions (client vs server related) and bind is appropriate for some questions and not others. Again, somebody needs to read the code and figure it out. |
CL https://golang.org/cl/40750 mentions this issue. |
Is the problem just the bind system call? Could we just switch the probe test from binding TCP sockets to connecting UDP sockets? |
On Fri, Apr 14, 2017 at 10:23:03AM -0700, Matthew Dempsky wrote:
Is the problem just the bind system call? Could we just switch the probe test from binding TCP sockets to connecting UDP sockets?
I think the problem is really using any syscall that the application
doesn't intend to use: if I don't intend to connect() with my
application (i.e. only bind()), then we can't use connect() to test
either, because I could write an LSM/seccomp/etc. profile that would
reasonably refuse connect().
Cheers,
Tycho
|
Please answer these questions before submitting your issue. Thanks!
go version
)?go version go1.6.2 linux/amd64
go env
)?GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/tycho/packages/go"
GORACE=""
GOROOT="/usr/lib/go-1.6"
GOTOOLDIR="/usr/lib/go-1.6/pkg/tool/linux_amd64"
GO15VENDOREXPERIMENT="1"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0"
CXX="g++"
CGO_ENABLED="1"
The full interesting part of the strace is:
I don't think golang should be doing a bind() here, since it doesn't otherwise use the socket it bind()s to. This means that clients that import net/http can't live in an environment that restricts the bind syscall (via e.g. seccomp).
The text was updated successfully, but these errors were encountered: