From 4b9b3e8fc9f4c215b4370595855258b76e4f3cbb Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 27 Aug 2018 16:35:27 -0400 Subject: [PATCH] Add WithAmbientCapabilities opt Closes #2516 This can be used for non-root users binding to low ports. Signed-off-by: Michael Crosby --- container_linux_test.go | 117 ++++++++++++++++++++++++++++++++++++++++ oci/spec_opts_unix.go | 12 +++++ 2 files changed, 129 insertions(+) diff --git a/container_linux_test.go b/container_linux_test.go index 8fa8133a2..0fcdd5f6f 100644 --- a/container_linux_test.go +++ b/container_linux_test.go @@ -1356,3 +1356,120 @@ func TestUIDNoGID(t *testing.T) { t.Fatalf("expected gid 0 but received %d", gid) } } + +func TestBindLowPortNonRoot(t *testing.T) { + t.Parallel() + + client, err := newClient(t, address) + if err != nil { + t.Fatal(err) + } + defer client.Close() + + var ( + image Image + ctx, cancel = testContext() + id = t.Name() + ) + defer cancel() + + image, err = client.GetImage(ctx, testImage) + if err != nil { + t.Fatal(err) + } + container, err := client.NewContainer(ctx, id, + WithNewSnapshot(id, image), + WithNewSpec(oci.WithImageConfig(image), withProcessArgs("nc", "-l", "-p", "80"), oci.WithUIDGID(1000, 1000)), + ) + if err != nil { + t.Fatal(err) + } + defer container.Delete(ctx, WithSnapshotCleanup) + + task, err := container.NewTask(ctx, empty()) + if err != nil { + t.Fatal(err) + } + defer task.Delete(ctx) + + statusC, err := task.Wait(ctx) + if err != nil { + t.Fatal(err) + } + + if err := task.Start(ctx); err != nil { + t.Fatal(err) + } + status := <-statusC + code, _, err := status.Result() + if err != nil { + t.Fatal(err) + } + if code != 1 { + t.Errorf("expected status 1 from wait but received %d", code) + } + if _, err := task.Delete(ctx); err != nil { + t.Fatal(err) + } +} + +func TestBindLowPortNonOpt(t *testing.T) { + t.Parallel() + + client, err := newClient(t, address) + if err != nil { + t.Fatal(err) + } + defer client.Close() + + var ( + image Image + ctx, cancel = testContext() + id = t.Name() + ) + defer cancel() + + image, err = client.GetImage(ctx, testImage) + if err != nil { + t.Fatal(err) + } + container, err := client.NewContainer(ctx, id, + WithNewSnapshot(id, image), + WithNewSpec(oci.WithImageConfig(image), withProcessArgs("nc", "-l", "-p", "80"), oci.WithUIDGID(1000, 1000), oci.WithAmbientCapabilities([]string{"CAP_NET_BIND_SERVICE"})), + ) + if err != nil { + t.Fatal(err) + } + defer container.Delete(ctx, WithSnapshotCleanup) + + task, err := container.NewTask(ctx, empty()) + if err != nil { + t.Fatal(err) + } + defer task.Delete(ctx) + + statusC, err := task.Wait(ctx) + if err != nil { + t.Fatal(err) + } + + if err := task.Start(ctx); err != nil { + t.Fatal(err) + } + go func() { + time.Sleep(2 * time.Second) + task.Kill(ctx, unix.SIGTERM) + }() + status := <-statusC + code, _, err := status.Result() + if err != nil { + t.Fatal(err) + } + // 128 + sigterm + if code != 143 { + t.Errorf("expected status 143 from wait but received %d", code) + } + if _, err := task.Delete(ctx); err != nil { + t.Fatal(err) + } +} diff --git a/oci/spec_opts_unix.go b/oci/spec_opts_unix.go index 6299b8a23..592da651d 100644 --- a/oci/spec_opts_unix.go +++ b/oci/spec_opts_unix.go @@ -486,6 +486,18 @@ func getAllCapabilities() []string { return caps } +// WithAmbientCapabilities set the Linux ambient capabilities for the process +// Ambient capabilities should only be set for non-root users or the caller should +// understand how these capabilities are used and set +func WithAmbientCapabilities(caps []string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setCapabilities(s) + + s.Process.Capabilities.Ambient = caps + return nil + } +} + var errNoUsersFound = errors.New("no users found") func getUIDGIDFromPath(root string, filter func(user.User) bool) (uid, gid uint32, err error) {