summaryrefslogtreecommitdiffhomepage
path: root/runsc/cmd
diff options
context:
space:
mode:
authorFabricio Voznika <fvoznika@google.com>2018-06-08 09:58:29 -0700
committerShentubot <shentubot@google.com>2018-06-08 09:59:26 -0700
commit5c51bc51e43a0f1d1f06ae490b0d352d1b483766 (patch)
tree356f35ee9f4980879a0b1ae2f975fae1e041de18 /runsc/cmd
parent5c37097e34a513845d77bb8b7240f0074aa1c1e9 (diff)
Drop capabilities not needed by Gofer
PiperOrigin-RevId: 199808391 Change-Id: Ib37a4fb6193dc85c1f93bc16769d6aa41854b9d4
Diffstat (limited to 'runsc/cmd')
-rw-r--r--runsc/cmd/BUILD2
-rw-r--r--runsc/cmd/boot.go53
-rw-r--r--runsc/cmd/capability.go142
-rw-r--r--runsc/cmd/cmd.go26
-rw-r--r--runsc/cmd/gofer.go30
5 files changed, 220 insertions, 33 deletions
diff --git a/runsc/cmd/BUILD b/runsc/cmd/BUILD
index a8c84a6a3..63d8036bd 100644
--- a/runsc/cmd/BUILD
+++ b/runsc/cmd/BUILD
@@ -6,6 +6,7 @@ go_library(
name = "cmd",
srcs = [
"boot.go",
+ "capability.go",
"checkpoint.go",
"cmd.go",
"create.go",
@@ -39,6 +40,7 @@ go_library(
"//runsc/specutils",
"@com_github_google_subcommands//:go_default_library",
"@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/cmd/boot.go b/runsc/cmd/boot.go
index 3bdc2ced0..34dd8b3c0 100644
--- a/runsc/cmd/boot.go
+++ b/runsc/cmd/boot.go
@@ -16,7 +16,6 @@ package cmd
import (
"os"
- "runtime"
"runtime/debug"
"strings"
"syscall"
@@ -24,7 +23,6 @@ import (
"context"
"flag"
"github.com/google/subcommands"
- specs "github.com/opencontainers/runtime-spec/specs-go"
"gvisor.googlesource.com/gvisor/pkg/log"
"gvisor.googlesource.com/gvisor/runsc/boot"
"gvisor.googlesource.com/gvisor/runsc/specutils"
@@ -106,8 +104,26 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{})
waitStatus := args[1].(*syscall.WaitStatus)
if b.applyCaps {
- setCapsAndCallSelf(conf, spec)
- Fatalf("setCapsAndCallSelf must never return")
+ caps := spec.Process.Capabilities
+ if conf.Platform == boot.PlatformPtrace {
+ // Ptrace platform requires extra capabilities.
+ const c = "CAP_SYS_PTRACE"
+ caps.Bounding = append(caps.Bounding, c)
+ caps.Effective = append(caps.Effective, c)
+ caps.Permitted = append(caps.Permitted, c)
+ }
+
+ // Remove --apply-caps arg to call myself.
+ var args []string
+ for _, arg := range os.Args {
+ if !strings.Contains(arg, "apply-caps") {
+ args = append(args, arg)
+ }
+ }
+ if err := setCapsAndCallSelf(spec, args, caps); err != nil {
+ Fatalf("%v", err)
+ }
+ panic("setCapsAndCallSelf must never return success")
}
// Create the loader.
@@ -130,32 +146,3 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{})
*waitStatus = syscall.WaitStatus(ws.Status())
return subcommands.ExitSuccess
}
-
-// setCapsAndCallSelf sets capabilities to the current thread and then execve's
-// itself again with the same arguments except '--apply-caps' to restart the
-// whole process with the desired capabilities.
-func setCapsAndCallSelf(conf *boot.Config, spec *specs.Spec) {
- // Keep thread locked while capabilities are changed.
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
-
- if err := boot.ApplyCaps(conf, spec.Process.Capabilities); err != nil {
- Fatalf("ApplyCaps, err: %v", err)
- }
- binPath, err := specutils.BinPath()
- if err != nil {
- Fatalf("%v", err)
- }
-
- // Remove --apply-caps arg to call myself.
- var args []string
- for _, arg := range os.Args {
- if !strings.Contains(arg, "apply-caps") {
- args = append(args, arg)
- }
- }
-
- log.Infof("Execve 'boot' again, bye!")
- log.Infof("%s %v", binPath, args)
- syscall.Exec(binPath, args, []string{})
-}
diff --git a/runsc/cmd/capability.go b/runsc/cmd/capability.go
new file mode 100644
index 000000000..0209feb1b
--- /dev/null
+++ b/runsc/cmd/capability.go
@@ -0,0 +1,142 @@
+// Copyright 2018 Google Inc.
+//
+// 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 cmd
+
+import (
+ "fmt"
+ "os"
+
+ specs "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/syndtr/gocapability/capability"
+ "gvisor.googlesource.com/gvisor/pkg/log"
+)
+
+// applyCaps applies the capabilities in the spec to the current thread.
+//
+// Note that it must be called with current thread locked.
+func applyCaps(caps *specs.LinuxCapabilities) error {
+ setter, err := capability.NewPid2(os.Getpid())
+ if err != nil {
+ return err
+ }
+ if err := setter.Load(); err != nil {
+ return err
+ }
+
+ bounding, err := trimCaps(caps.Bounding, setter)
+ if err != nil {
+ return err
+ }
+ setter.Set(capability.BOUNDS, bounding...)
+
+ effective, err := trimCaps(caps.Effective, setter)
+ if err != nil {
+ return err
+ }
+ setter.Set(capability.EFFECTIVE, effective...)
+
+ permitted, err := trimCaps(caps.Permitted, setter)
+ if err != nil {
+ return err
+ }
+ setter.Set(capability.PERMITTED, permitted...)
+
+ inheritable, err := trimCaps(caps.Inheritable, setter)
+ if err != nil {
+ return err
+ }
+ setter.Set(capability.INHERITABLE, inheritable...)
+
+ ambient, err := trimCaps(caps.Ambient, setter)
+ if err != nil {
+ return err
+ }
+ setter.Set(capability.AMBIENT, ambient...)
+
+ return setter.Apply(capability.CAPS | capability.BOUNDS | capability.AMBS)
+}
+
+func trimCaps(names []string, setter capability.Capabilities) ([]capability.Cap, error) {
+ wantedCaps, err := capsFromNames(names)
+ if err != nil {
+ return nil, err
+ }
+
+ // Trim down capabilities that aren't possible to acquire.
+ var caps []capability.Cap
+ for _, c := range wantedCaps {
+ // Capability rules are more complicated than this, but this catches most
+ // problems with tests running with non-priviledged user.
+ if setter.Get(capability.PERMITTED, c) {
+ caps = append(caps, c)
+ } else {
+ log.Warningf("Capability %q is not permitted, dropping it.", c)
+ }
+ }
+ return caps, nil
+}
+
+func capsFromNames(names []string) ([]capability.Cap, error) {
+ var caps []capability.Cap
+ for _, name := range names {
+ cap, ok := capFromName[name]
+ if !ok {
+ return nil, fmt.Errorf("invalid capability %q", name)
+ }
+ caps = append(caps, cap)
+ }
+ return caps, nil
+}
+
+var capFromName = map[string]capability.Cap{
+ "CAP_CHOWN": capability.CAP_CHOWN,
+ "CAP_DAC_OVERRIDE": capability.CAP_DAC_OVERRIDE,
+ "CAP_DAC_READ_SEARCH": capability.CAP_DAC_READ_SEARCH,
+ "CAP_FOWNER": capability.CAP_FOWNER,
+ "CAP_FSETID": capability.CAP_FSETID,
+ "CAP_KILL": capability.CAP_KILL,
+ "CAP_SETGID": capability.CAP_SETGID,
+ "CAP_SETUID": capability.CAP_SETUID,
+ "CAP_SETPCAP": capability.CAP_SETPCAP,
+ "CAP_LINUX_IMMUTABLE": capability.CAP_LINUX_IMMUTABLE,
+ "CAP_NET_BIND_SERVICE": capability.CAP_NET_BIND_SERVICE,
+ "CAP_NET_BROADCAST": capability.CAP_NET_BROADCAST,
+ "CAP_NET_ADMIN": capability.CAP_NET_ADMIN,
+ "CAP_NET_RAW": capability.CAP_NET_RAW,
+ "CAP_IPC_LOCK": capability.CAP_IPC_LOCK,
+ "CAP_IPC_OWNER": capability.CAP_IPC_OWNER,
+ "CAP_SYS_MODULE": capability.CAP_SYS_MODULE,
+ "CAP_SYS_RAWIO": capability.CAP_SYS_RAWIO,
+ "CAP_SYS_CHROOT": capability.CAP_SYS_CHROOT,
+ "CAP_SYS_PTRACE": capability.CAP_SYS_PTRACE,
+ "CAP_SYS_PACCT": capability.CAP_SYS_PACCT,
+ "CAP_SYS_ADMIN": capability.CAP_SYS_ADMIN,
+ "CAP_SYS_BOOT": capability.CAP_SYS_BOOT,
+ "CAP_SYS_NICE": capability.CAP_SYS_NICE,
+ "CAP_SYS_RESOURCE": capability.CAP_SYS_RESOURCE,
+ "CAP_SYS_TIME": capability.CAP_SYS_TIME,
+ "CAP_SYS_TTY_CONFIG": capability.CAP_SYS_TTY_CONFIG,
+ "CAP_MKNOD": capability.CAP_MKNOD,
+ "CAP_LEASE": capability.CAP_LEASE,
+ "CAP_AUDIT_WRITE": capability.CAP_AUDIT_WRITE,
+ "CAP_AUDIT_CONTROL": capability.CAP_AUDIT_CONTROL,
+ "CAP_SETFCAP": capability.CAP_SETFCAP,
+ "CAP_MAC_OVERRIDE": capability.CAP_MAC_OVERRIDE,
+ "CAP_MAC_ADMIN": capability.CAP_MAC_ADMIN,
+ "CAP_SYSLOG": capability.CAP_SYSLOG,
+ "CAP_WAKE_ALARM": capability.CAP_WAKE_ALARM,
+ "CAP_BLOCK_SUSPEND": capability.CAP_BLOCK_SUSPEND,
+ "CAP_AUDIT_READ": capability.CAP_AUDIT_READ,
+}
diff --git a/runsc/cmd/cmd.go b/runsc/cmd/cmd.go
index 9f7fd6e25..940c8cd14 100644
--- a/runsc/cmd/cmd.go
+++ b/runsc/cmd/cmd.go
@@ -18,9 +18,13 @@ package cmd
import (
"fmt"
"os"
+ "runtime"
"strconv"
+ "syscall"
+ specs "github.com/opencontainers/runtime-spec/specs-go"
"gvisor.googlesource.com/gvisor/pkg/log"
+ "gvisor.googlesource.com/gvisor/runsc/specutils"
)
// Fatalf logs to stderr and exits with a failure status code.
@@ -64,3 +68,25 @@ func (i *intFlags) Set(s string) error {
*i = append(*i, fd)
return nil
}
+
+// setCapsAndCallSelf sets capabilities to the current thread and then execve's
+// itself again with the arguments specified in 'args' to restart the process
+// with the desired capabilities.
+func setCapsAndCallSelf(spec *specs.Spec, args []string, caps *specs.LinuxCapabilities) error {
+ // Keep thread locked while capabilities are changed.
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ if err := applyCaps(caps); err != nil {
+ return fmt.Errorf("applyCaps() failed: %v", err)
+ }
+ binPath, err := specutils.BinPath()
+ if err != nil {
+ return err
+ }
+
+ log.Infof("Capabilities applied: %+v", caps)
+ log.Infof("Execve %q again, bye!", binPath)
+ syscall.Exec(binPath, args, []string{})
+ panic("unreachable")
+}
diff --git a/runsc/cmd/gofer.go b/runsc/cmd/gofer.go
index 844e16dbf..39803bde5 100644
--- a/runsc/cmd/gofer.go
+++ b/runsc/cmd/gofer.go
@@ -15,11 +15,13 @@
package cmd
import (
+ "os"
"sync"
"context"
"flag"
"github.com/google/subcommands"
+ specs "github.com/opencontainers/runtime-spec/specs-go"
"gvisor.googlesource.com/gvisor/pkg/log"
"gvisor.googlesource.com/gvisor/pkg/p9"
"gvisor.googlesource.com/gvisor/pkg/unet"
@@ -32,6 +34,7 @@ import (
type Gofer struct {
bundleDir string
ioFDs intFlags
+ applyCaps bool
}
// Name implements subcommands.Command.
@@ -53,6 +56,7 @@ func (*Gofer) Usage() string {
func (g *Gofer) SetFlags(f *flag.FlagSet) {
f.StringVar(&g.bundleDir, "bundle", "", "path to the root of the bundle directory, defaults to the current directory")
f.Var(&g.ioFDs, "io-fds", "list of FDs to connect 9P servers. They must follow this order: root first, then mounts as defined in the spec")
+ f.BoolVar(&g.applyCaps, "apply-caps", true, "if true, apply capabilities to restrict what the Gofer process can do")
}
// Execute implements subcommands.Command.
@@ -66,6 +70,32 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...interface{})
if err != nil {
Fatalf("error reading spec: %v", err)
}
+
+ if g.applyCaps {
+ // Minimal set of capabilities needed by the Gofer to operate on files.
+ caps := []string{
+ "CAP_CHOWN",
+ "CAP_DAC_OVERRIDE",
+ "CAP_DAC_READ_SEARCH",
+ "CAP_FOWNER",
+ "CAP_FSETID",
+ }
+ lc := &specs.LinuxCapabilities{
+ Bounding: caps,
+ Effective: caps,
+ Permitted: caps,
+ }
+
+ // Disable caps when calling myself again.
+ // Note: minimal argument handling for the default case to keep it simple.
+ args := os.Args
+ args = append(args, "--apply-caps=false")
+ if err := setCapsAndCallSelf(spec, args, lc); err != nil {
+ Fatalf("Unable to apply caps: %v", err)
+ }
+ panic("unreachable")
+ }
+
specutils.LogSpec(spec)
// Start with root mount, then add any other addition mount as needed.