Drop capabilities not needed by Gofer
PiperOrigin-RevId: 199808391 Change-Id: Ib37a4fb6193dc85c1f93bc16769d6aa41854b9d4
This commit is contained in:
parent
5c37097e34
commit
5c51bc51e4
|
@ -5,7 +5,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
go_library(
|
go_library(
|
||||||
name = "boot",
|
name = "boot",
|
||||||
srcs = [
|
srcs = [
|
||||||
"capability.go",
|
|
||||||
"config.go",
|
"config.go",
|
||||||
"controller.go",
|
"controller.go",
|
||||||
"events.go",
|
"events.go",
|
||||||
|
@ -72,7 +71,6 @@ go_library(
|
||||||
"//runsc/boot/filter",
|
"//runsc/boot/filter",
|
||||||
"//runsc/specutils",
|
"//runsc/specutils",
|
||||||
"@com_github_opencontainers_runtime-spec//specs-go:go_default_library",
|
"@com_github_opencontainers_runtime-spec//specs-go:go_default_library",
|
||||||
"@com_github_syndtr_gocapability//capability:go_default_library",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ go_library(
|
||||||
name = "cmd",
|
name = "cmd",
|
||||||
srcs = [
|
srcs = [
|
||||||
"boot.go",
|
"boot.go",
|
||||||
|
"capability.go",
|
||||||
"checkpoint.go",
|
"checkpoint.go",
|
||||||
"cmd.go",
|
"cmd.go",
|
||||||
"create.go",
|
"create.go",
|
||||||
|
@ -39,6 +40,7 @@ go_library(
|
||||||
"//runsc/specutils",
|
"//runsc/specutils",
|
||||||
"@com_github_google_subcommands//:go_default_library",
|
"@com_github_google_subcommands//:go_default_library",
|
||||||
"@com_github_opencontainers_runtime-spec//specs-go: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",
|
"@org_golang_x_sys//unix:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,7 +16,6 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
@ -24,7 +23,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/google/subcommands"
|
"github.com/google/subcommands"
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
|
||||||
"gvisor.googlesource.com/gvisor/pkg/log"
|
"gvisor.googlesource.com/gvisor/pkg/log"
|
||||||
"gvisor.googlesource.com/gvisor/runsc/boot"
|
"gvisor.googlesource.com/gvisor/runsc/boot"
|
||||||
"gvisor.googlesource.com/gvisor/runsc/specutils"
|
"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)
|
waitStatus := args[1].(*syscall.WaitStatus)
|
||||||
|
|
||||||
if b.applyCaps {
|
if b.applyCaps {
|
||||||
setCapsAndCallSelf(conf, spec)
|
caps := spec.Process.Capabilities
|
||||||
Fatalf("setCapsAndCallSelf must never return")
|
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.
|
// Create the loader.
|
||||||
|
@ -130,32 +146,3 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{})
|
||||||
*waitStatus = syscall.WaitStatus(ws.Status())
|
*waitStatus = syscall.WaitStatus(ws.Status())
|
||||||
return subcommands.ExitSuccess
|
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{})
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package boot
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -20,53 +20,74 @@ import (
|
||||||
|
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/syndtr/gocapability/capability"
|
"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.
|
// 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())
|
setter, err := capability.NewPid2(os.Getpid())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := setter.Load(); err != nil {
|
||||||
bounding, err := capsFromNames(caps.Bounding)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
effective, err := capsFromNames(caps.Effective)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
permitted, err := capsFromNames(caps.Permitted)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
inheritable, err := capsFromNames(caps.Inheritable)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ambient, err := capsFromNames(caps.Ambient)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ptrace platform requires extra capabilities.
|
bounding, err := trimCaps(caps.Bounding, setter)
|
||||||
if conf.Platform == PlatformPtrace {
|
if err != nil {
|
||||||
bounding = append(bounding, capability.CAP_SYS_PTRACE)
|
return err
|
||||||
effective = append(effective, capability.CAP_SYS_PTRACE)
|
|
||||||
permitted = append(permitted, capability.CAP_SYS_PTRACE)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setter.Set(capability.BOUNDS, bounding...)
|
setter.Set(capability.BOUNDS, bounding...)
|
||||||
setter.Set(capability.PERMITTED, permitted...)
|
|
||||||
setter.Set(capability.INHERITABLE, inheritable...)
|
effective, err := trimCaps(caps.Effective, setter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
setter.Set(capability.EFFECTIVE, effective...)
|
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...)
|
setter.Set(capability.AMBIENT, ambient...)
|
||||||
|
|
||||||
return setter.Apply(capability.CAPS | capability.BOUNDS | capability.AMBS)
|
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) {
|
func capsFromNames(names []string) ([]capability.Cap, error) {
|
||||||
var caps []capability.Cap
|
var caps []capability.Cap
|
||||||
for _, name := range names {
|
for _, name := range names {
|
|
@ -18,9 +18,13 @@ package cmd
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"gvisor.googlesource.com/gvisor/pkg/log"
|
"gvisor.googlesource.com/gvisor/pkg/log"
|
||||||
|
"gvisor.googlesource.com/gvisor/runsc/specutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fatalf logs to stderr and exits with a failure status code.
|
// 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)
|
*i = append(*i, fd)
|
||||||
return nil
|
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")
|
||||||
|
}
|
||||||
|
|
|
@ -15,11 +15,13 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/google/subcommands"
|
"github.com/google/subcommands"
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"gvisor.googlesource.com/gvisor/pkg/log"
|
"gvisor.googlesource.com/gvisor/pkg/log"
|
||||||
"gvisor.googlesource.com/gvisor/pkg/p9"
|
"gvisor.googlesource.com/gvisor/pkg/p9"
|
||||||
"gvisor.googlesource.com/gvisor/pkg/unet"
|
"gvisor.googlesource.com/gvisor/pkg/unet"
|
||||||
|
@ -32,6 +34,7 @@ import (
|
||||||
type Gofer struct {
|
type Gofer struct {
|
||||||
bundleDir string
|
bundleDir string
|
||||||
ioFDs intFlags
|
ioFDs intFlags
|
||||||
|
applyCaps bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name implements subcommands.Command.
|
// Name implements subcommands.Command.
|
||||||
|
@ -53,6 +56,7 @@ func (*Gofer) Usage() string {
|
||||||
func (g *Gofer) SetFlags(f *flag.FlagSet) {
|
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.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.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.
|
// Execute implements subcommands.Command.
|
||||||
|
@ -66,6 +70,32 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...interface{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("error reading spec: %v", err)
|
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)
|
specutils.LogSpec(spec)
|
||||||
|
|
||||||
// Start with root mount, then add any other addition mount as needed.
|
// Start with root mount, then add any other addition mount as needed.
|
||||||
|
|
|
@ -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
|
// 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
|
// are virtualized inside the sandbox. Be paranoid and run inside an empty
|
||||||
// namespace for these.
|
// 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{
|
nss := []specs.LinuxNamespace{
|
||||||
{Type: specs.IPCNamespace},
|
{Type: specs.IPCNamespace},
|
||||||
{Type: specs.UTSNamespace},
|
{Type: specs.UTSNamespace},
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.Platform == boot.PlatformPtrace {
|
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.
|
// access to other host processes.
|
||||||
log.Infof("Sandbox will be started in the current PID namespace")
|
log.Infof("Sandbox will be started in the current PID namespace")
|
||||||
} else {
|
} 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})
|
nss = append(nss, specs.LinuxNamespace{Type: specs.PIDNamespace})
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.FileAccess == boot.FileAccessProxy {
|
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})
|
nss = append(nss, specs.LinuxNamespace{Type: specs.MountNamespace})
|
||||||
} else {
|
} else {
|
||||||
log.Infof("Sandbox will be started in the current mount namespace")
|
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)
|
log.Infof("Sandbox will be started in the container's network namespace: %+v", ns)
|
||||||
nss = append(nss, ns)
|
nss = append(nss, ns)
|
||||||
} else {
|
} 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})
|
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")
|
cmd.Args = append(cmd.Args, "--apply-caps=true")
|
||||||
|
|
||||||
} else {
|
} 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})
|
nss = append(nss, specs.LinuxNamespace{Type: specs.UserNamespace})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue