diff --git a/pkg/test/testutil/testutil.go b/pkg/test/testutil/testutil.go index 5db023969..6b97ff185 100644 --- a/pkg/test/testutil/testutil.go +++ b/pkg/test/testutil/testutil.go @@ -56,6 +56,10 @@ var ( runscPath = flag.String("runsc", os.Getenv("RUNTIME"), "path to runsc binary") // Note: flag overlay is already taken by runsc. isRunningWithOverlay = flag.Bool("test-overlay", BoolFromEnv("TEST_OVERLAY", false), "whether test is running with --overlay2") + + // TestEnvSupportsRawSockets indicates whether a test sandbox can + // create raw sockets. + TestEnvSupportsRawSockets = true ) // StringFromEnv returns the value of the named environment variable, or `def` if unset/empty. diff --git a/runsc/boot/BUILD b/runsc/boot/BUILD index 16784d8cf..91cdcd6e8 100644 --- a/runsc/boot/BUILD +++ b/runsc/boot/BUILD @@ -117,6 +117,7 @@ go_library( "//runsc/specutils", "//runsc/specutils/seccomp", "@com_github_opencontainers_runtime_spec//specs-go:go_default_library", + "@com_github_syndtr_gocapability//capability:go_default_library", "@org_golang_google_protobuf//proto:go_default_library", "@org_golang_x_sys//unix:go_default_library", ], @@ -143,6 +144,7 @@ go_test( "//runsc/flag", "//runsc/fsgofer", "@com_github_opencontainers_runtime_spec//specs-go:go_default_library", + "@com_github_syndtr_gocapability//capability:go_default_library", "@org_golang_x_sys//unix:go_default_library", ], ) diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index f597f336d..fd0e2192a 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -24,6 +24,7 @@ import ( gtime "time" specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/syndtr/gocapability/capability" "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/bpf" @@ -1116,6 +1117,12 @@ func newRootNetworkNamespace(conf *config.Config, clock tcpip.Clock, uniqueID st // Run(). switch conf.Network { case config.NetworkHost: + // If configured for raw socket support with host network + // stack, make sure that we have CAP_NET_RAW the host, + // otherwise we can't make raw sockets. + if conf.EnableRaw && !specutils.HasCapabilities(capability.CAP_NET_RAW) { + return nil, fmt.Errorf("configuring network=host with raw sockets requires CAP_NET_RAW capability") + } // No network namespacing support for hostinet yet, hence creator is nil. return inet.NewRootNamespace(hostinet.NewStack(), nil), nil diff --git a/runsc/boot/loader_test.go b/runsc/boot/loader_test.go index fe0633ce3..a87a30928 100644 --- a/runsc/boot/loader_test.go +++ b/runsc/boot/loader_test.go @@ -18,10 +18,12 @@ import ( "fmt" "math/rand" "os" + "strings" "testing" "time" specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/syndtr/gocapability/capability" "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/control/server" "gvisor.dev/gvisor/pkg/fspath" @@ -103,12 +105,12 @@ func startGofer(root string) (int, func(), error) { return sandboxEnd, cleanup, nil } -func createLoader(spec *specs.Spec) (*Loader, func(), error) { - fd, err := server.CreateSocket(fmt.Sprintf("\x00loader-test.%010d", rand.Int())[:10]) +func createLoader(conf *config.Config, spec *specs.Spec) (*Loader, func(), error) { + sock := fmt.Sprintf("\x00loader-test.%010d", rand.Int()) + fd, err := server.CreateSocket(sock) if err != nil { return nil, nil, err } - conf := testConfig() sandEnd, cleanup, err := startGofer(spec.Root.Path) if err != nil { return nil, nil, err @@ -143,7 +145,7 @@ func createLoader(spec *specs.Spec) (*Loader, func(), error) { // TestRun runs a simple application in a sandbox and checks that it succeeds. func TestRun(t *testing.T) { - l, cleanup, err := createLoader(testSpec()) + l, cleanup, err := createLoader(testConfig(), testSpec()) if err != nil { t.Fatalf("error creating loader: %v", err) } @@ -181,7 +183,7 @@ func TestRun(t *testing.T) { // TestStartSignal tests that the controller Start message will cause // WaitForStartSignal to return. func TestStartSignal(t *testing.T) { - l, cleanup, err := createLoader(testSpec()) + l, cleanup, err := createLoader(testConfig(), testSpec()) if err != nil { t.Fatalf("error creating loader: %v", err) } @@ -228,6 +230,53 @@ func TestStartSignal(t *testing.T) { } } +// Test that network=host with raw sockets enabled requires CAP_NET_RAW on the +// host. +func TestHostnetWithRawSockets(t *testing.T) { + // Drop CAP_NET_RAW from effective capabilities, if we have it. + pid := os.Getpid() + caps, err := capability.NewPid2(os.Getpid()) + if err != nil { + t.Fatalf("error getting capabilities for pid %d: %v", pid, err) + } + if err := caps.Load(); err != nil { + t.Fatalf("error loading capabilities: %v", err) + } + if caps.Get(capability.EFFECTIVE, capability.CAP_NET_RAW) { + caps.Unset(capability.EFFECTIVE, capability.CAP_NET_RAW) + if err := caps.Apply(capability.EFFECTIVE); err != nil { + t.Fatalf("error applying capabilities") + } + // Be nice and add it back when we are done. + defer func() { + caps.Set(capability.EFFECTIVE, capability.CAP_NET_RAW) + if err := caps.Apply(capability.EFFECTIVE); err != nil { + t.Fatalf("error restoring capabilities") + } + }() + } + + // Configure host network with raw sockets. + conf := testConfig() + conf.Network = config.NetworkHost + conf.EnableRaw = true + + // Creating loader should fail. + l, err := New(Args{ + ID: "should-fail", + Spec: testSpec(), + Conf: conf, + }) + if err == nil { + l.Destroy() + t.Fatalf("expected loader.New() to fail but it did not") + } + // Error message must be about CAP_NET_RAW. + if !strings.Contains(err.Error(), "CAP_NET_RAW") { + t.Errorf("expected error to contain CAP_NET_RAW but got %q", err) + } +} + type CreateMountTestcase struct { name string // Spec that will be used to create the mount manager. Note @@ -410,7 +459,7 @@ func TestCreateMountNamespace(t *testing.T) { spec.Root = tc.spec.Root t.Logf("Using root: %q", spec.Root.Path) - l, loaderCleanup, err := createLoader(spec) + l, loaderCleanup, err := createLoader(testConfig(), spec) if err != nil { t.Fatalf("failed to create loader: %v", err) } diff --git a/test/runner/main.go b/test/runner/main.go index a9ae64688..9c41b0dd6 100644 --- a/test/runner/main.go +++ b/test/runner/main.go @@ -223,13 +223,18 @@ func runRunsc(tc *gtest.TestCase, spec *specs.Spec) error { "-log-format=text", "-TESTONLY-unsafe-nonroot=true", "-TESTONLY-allow-packet-endpoint-write=true", - "-net-raw=true", fmt.Sprintf("-panic-signal=%d", unix.SIGTERM), fmt.Sprintf("-iouring=%t", *ioUring), "-watchdog-action=panic", "-platform", *platform, "-file-access", *fileAccess, } + + if *network == "host" && !testutil.TestEnvSupportsRawSockets { + log.Warningf("Testing with network=host but test environment does not support raw sockets. Raw socket support will be disabled.") + } else { + args = append(args, "-net-raw") + } if *overlay { args = append(args, "-overlay2=all:dir=/tmp") }