From c54948f3c1940071f1690651b533e22977481de4 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Tue, 14 Jun 2022 20:01:58 -0700 Subject: [PATCH] notify users of common reasons for failing to create a raw socket Fixes #199 PiperOrigin-RevId: 455018292 --- pkg/log/BUILD | 2 + pkg/log/rate_limited.go | 63 +++++++++++++++++++++ pkg/sentry/socket/netstack/provider.go | 6 ++ pkg/sentry/socket/netstack/provider_vfs2.go | 1 + pkg/tcpip/stack/stack.go | 4 ++ 5 files changed, 76 insertions(+) create mode 100644 pkg/log/rate_limited.go diff --git a/pkg/log/BUILD b/pkg/log/BUILD index 3ed6aba5c..7a48f92c7 100644 --- a/pkg/log/BUILD +++ b/pkg/log/BUILD @@ -9,6 +9,7 @@ go_library( "json.go", "json_k8s.go", "log.go", + "rate_limited.go", ], marshal = False, stateify = False, @@ -18,6 +19,7 @@ go_library( deps = [ "//pkg/linewriter", "//pkg/sync", + "@org_golang_x_time//rate:go_default_library", ], ) diff --git a/pkg/log/rate_limited.go b/pkg/log/rate_limited.go new file mode 100644 index 000000000..285d9b26d --- /dev/null +++ b/pkg/log/rate_limited.go @@ -0,0 +1,63 @@ +// Copyright 2022 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package log + +import ( + "time" + + "golang.org/x/time/rate" +) + +type rateLimitedLogger struct { + logger Logger + limit *rate.Limiter +} + +func (rl *rateLimitedLogger) Debugf(format string, v ...interface{}) { + if rl.limit.Allow() { + rl.logger.Debugf(format, v...) + } +} + +func (rl *rateLimitedLogger) Infof(format string, v ...interface{}) { + if rl.limit.Allow() { + rl.logger.Infof(format, v...) + } +} + +func (rl *rateLimitedLogger) Warningf(format string, v ...interface{}) { + if rl.limit.Allow() { + rl.logger.Warningf(format, v...) + } +} + +func (rl *rateLimitedLogger) IsLogging(level Level) bool { + return rl.logger.IsLogging(level) +} + +// BasicRateLimitedLogger returns a Logger that logs to the global logger no +// more than once per the provided duration. +func BasicRateLimitedLogger(every time.Duration) Logger { + return RateLimitedLogger(Log(), every) +} + +// RateLimitedLogger returns a Logger that logs to the provided logger no more +// than once per the provided duration. +func RateLimitedLogger(logger Logger, every time.Duration) Logger { + return &rateLimitedLogger{ + logger: logger, + limit: rate.NewLimiter(rate.Every(every), 1), + } +} diff --git a/pkg/sentry/socket/netstack/provider.go b/pkg/sentry/socket/netstack/provider.go index 8605ad507..e675db199 100644 --- a/pkg/sentry/socket/netstack/provider.go +++ b/pkg/sentry/socket/netstack/provider.go @@ -15,9 +15,12 @@ package netstack import ( + "time" + "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/context" + "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/sentry/fs" "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" @@ -40,6 +43,8 @@ type provider struct { netProto tcpip.NetworkProtocolNumber } +var rawMissingLogger = log.BasicRateLimitedLogger(time.Minute) + // getTransportProtocol figures out transport protocol. Currently only TCP, // UDP, and ICMP are supported. The bool return value is true when this socket // is associated with a transport protocol. This is only false for SOCK_RAW, @@ -66,6 +71,7 @@ func getTransportProtocol(ctx context.Context, stype linux.SockType, protocol in // Raw sockets require CAP_NET_RAW. creds := auth.CredentialsFromContext(ctx) if !creds.HasCapability(linux.CAP_NET_RAW) { + rawMissingLogger.Infof("A process tried to create a raw socket without CAP_NET_RAW. Should the container config enable CAP_NET_RAW?") return 0, true, syserr.ErrNotPermitted } diff --git a/pkg/sentry/socket/netstack/provider_vfs2.go b/pkg/sentry/socket/netstack/provider_vfs2.go index ba1cc79e9..f2ebb233f 100644 --- a/pkg/sentry/socket/netstack/provider_vfs2.go +++ b/pkg/sentry/socket/netstack/provider_vfs2.go @@ -86,6 +86,7 @@ func packetSocketVFS2(t *kernel.Task, epStack *Stack, stype linux.SockType, prot // Packet sockets require CAP_NET_RAW. creds := auth.CredentialsFromContext(t) if !creds.HasCapability(linux.CAP_NET_RAW) { + rawMissingLogger.Infof("A process tried to create a raw socket without CAP_NET_RAW. Should the container config enable CAP_NET_RAW?") return nil, syserr.ErrNotPermitted } diff --git a/pkg/tcpip/stack/stack.go b/pkg/tcpip/stack/stack.go index 7836934da..a741f0251 100644 --- a/pkg/tcpip/stack/stack.go +++ b/pkg/tcpip/stack/stack.go @@ -30,6 +30,7 @@ import ( "golang.org/x/time/rate" "gvisor.dev/gvisor/pkg/atomicbitops" "gvisor.dev/gvisor/pkg/buffer" + "gvisor.dev/gvisor/pkg/log" cryptorand "gvisor.dev/gvisor/pkg/rand" "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/tcpip" @@ -64,6 +65,8 @@ func (u *uniqueIDGenerator) UniqueID() uint64 { return ((*atomicbitops.Uint64)(u)).Add(1) } +var netRawMissingLogger = log.BasicRateLimitedLogger(time.Minute) + // Stack is a networking stack, with all supported protocols, NICs, and route // table. // @@ -712,6 +715,7 @@ func (s *Stack) NewEndpoint(transport tcpip.TransportProtocolNumber, network tcp // of address. func (s *Stack) NewRawEndpoint(transport tcpip.TransportProtocolNumber, network tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue, associated bool) (tcpip.Endpoint, tcpip.Error) { if s.rawFactory == nil { + netRawMissingLogger.Infof("A process tried to create a raw socket, but --net-raw was not specified. Should runsc be run with --net-raw?") return nil, &tcpip.ErrNotPermitted{} }