summaryrefslogtreecommitdiffhomepage
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
parent5c37097e34a513845d77bb8b7240f0074aa1c1e9 (diff)
Drop capabilities not needed by Gofer
PiperOrigin-RevId: 199808391 Change-Id: Ib37a4fb6193dc85c1f93bc16769d6aa41854b9d4
-rw-r--r--runsc/boot/BUILD2
-rw-r--r--runsc/cmd/BUILD2
-rw-r--r--runsc/cmd/boot.go53
-rw-r--r--runsc/cmd/capability.go (renamed from runsc/boot/capability.go)59
-rw-r--r--runsc/cmd/cmd.go26
-rw-r--r--runsc/cmd/gofer.go30
-rw-r--r--runsc/sandbox/sandbox.go12
7 files changed, 124 insertions, 60 deletions
diff --git a/runsc/boot/BUILD b/runsc/boot/BUILD
index 1746df988..73893d699 100644
--- a/runsc/boot/BUILD
+++ b/runsc/boot/BUILD
@@ -5,7 +5,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "boot",
srcs = [
- "capability.go",
"config.go",
"controller.go",
"events.go",
@@ -72,7 +71,6 @@ go_library(
"//runsc/boot/filter",
"//runsc/specutils",
"@com_github_opencontainers_runtime-spec//specs-go:go_default_library",
- "@com_github_syndtr_gocapability//capability:go_default_library",
],
)
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/boot/capability.go b/runsc/cmd/capability.go
index efa28fb97..0209feb1b 100644
--- a/runsc/boot/capability.go
+++ b/runsc/cmd/capability.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package boot
+package cmd
import (
"fmt"
@@ -20,51 +20,72 @@ import (
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.
+// applyCaps applies the capabilities in the spec to the current thread.
//
// Note that it must be called with current thread locked.
-func ApplyCaps(conf *Config, caps *specs.LinuxCapabilities) error {
+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 := capsFromNames(caps.Bounding)
+ bounding, err := trimCaps(caps.Bounding, setter)
if err != nil {
return err
}
- effective, err := capsFromNames(caps.Effective)
+ setter.Set(capability.BOUNDS, bounding...)
+
+ effective, err := trimCaps(caps.Effective, setter)
if err != nil {
return err
}
- permitted, err := capsFromNames(caps.Permitted)
+ setter.Set(capability.EFFECTIVE, effective...)
+
+ permitted, err := trimCaps(caps.Permitted, setter)
if err != nil {
return err
}
- inheritable, err := capsFromNames(caps.Inheritable)
+ setter.Set(capability.PERMITTED, permitted...)
+
+ inheritable, err := trimCaps(caps.Inheritable, setter)
if err != nil {
return err
}
- ambient, err := capsFromNames(caps.Ambient)
+ setter.Set(capability.INHERITABLE, inheritable...)
+
+ ambient, err := trimCaps(caps.Ambient, setter)
if err != nil {
return err
}
+ setter.Set(capability.AMBIENT, ambient...)
- // Ptrace platform requires extra capabilities.
- if conf.Platform == PlatformPtrace {
- bounding = append(bounding, capability.CAP_SYS_PTRACE)
- effective = append(effective, capability.CAP_SYS_PTRACE)
- permitted = append(permitted, capability.CAP_SYS_PTRACE)
+ 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
}
- setter.Set(capability.BOUNDS, bounding...)
- setter.Set(capability.PERMITTED, permitted...)
- setter.Set(capability.INHERITABLE, inheritable...)
- setter.Set(capability.EFFECTIVE, effective...)
- setter.Set(capability.AMBIENT, ambient...)
- return setter.Apply(capability.CAPS | capability.BOUNDS | capability.AMBS)
+ // 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) {
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.
diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go
index 2a434cfb7..48388aa7f 100644
--- a/runsc/sandbox/sandbox.go
+++ b/runsc/sandbox/sandbox.go
@@ -295,23 +295,23 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund
// process. IPC and UTS namespaces from the host are not used as they
// are virtualized inside the sandbox. Be paranoid and run inside an empty
// namespace for these.
- log.Infof("Sandbox will be started in empty IPC and UTS namespaces")
+ log.Infof("Sandbox will be started in new IPC and UTS namespaces")
nss := []specs.LinuxNamespace{
{Type: specs.IPCNamespace},
{Type: specs.UTSNamespace},
}
if conf.Platform == boot.PlatformPtrace {
- // TODO: Also set an empty PID namespace so that we limit
+ // TODO: Also set a new PID namespace so that we limit
// access to other host processes.
log.Infof("Sandbox will be started in the current PID namespace")
} else {
- log.Infof("Sandbox will be started in empty PID namespace")
+ log.Infof("Sandbox will be started in a new PID namespace")
nss = append(nss, specs.LinuxNamespace{Type: specs.PIDNamespace})
}
if conf.FileAccess == boot.FileAccessProxy {
- log.Infof("Sandbox will be started in empty mount namespace")
+ log.Infof("Sandbox will be started in new mount namespace")
nss = append(nss, specs.LinuxNamespace{Type: specs.MountNamespace})
} else {
log.Infof("Sandbox will be started in the current mount namespace")
@@ -324,7 +324,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund
log.Infof("Sandbox will be started in the container's network namespace: %+v", ns)
nss = append(nss, ns)
} else {
- log.Infof("Sandbox will be started in empty network namespace")
+ log.Infof("Sandbox will be started in new network namespace")
nss = append(nss, specs.LinuxNamespace{Type: specs.NetworkNamespace})
}
@@ -347,7 +347,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund
cmd.Args = append(cmd.Args, "--apply-caps=true")
} else {
- log.Infof("Sandbox will be started in empty user namespace")
+ log.Infof("Sandbox will be started in new user namespace")
nss = append(nss, specs.LinuxNamespace{Type: specs.UserNamespace})
}