Add control configs

Also plumber the controls through runsc

PiperOrigin-RevId: 391594318
This commit is contained in:
Chong Cai 2021-08-18 13:11:36 -07:00 committed by gVisor bot
parent e0bf522502
commit 75b5a4f455
8 changed files with 196 additions and 13 deletions

View File

@ -1,7 +1,13 @@
load("//tools:defs.bzl", "go_library", "go_test")
load("//tools:defs.bzl", "go_library", "go_test", "proto_library")
package(licenses = ["notice"])
proto_library(
name = "control",
srcs = ["control.proto"],
visibility = ["//visibility:public"],
)
go_library(
name = "control",
srcs = [

View File

@ -0,0 +1,40 @@
// Copyright 2021 The gVisor Authors.
//
// 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.
syntax = "proto3";
package gvisor;
// ControlConfig configures the permission of controls.
message ControlConfig {
// Names for individual control URPC service objects.
// Any new service object that should be given conditional access should be
// named here and conditionally added based on presence in allowed_controls.
enum Endpoint {
UNKNOWN = 0;
EVENTS = 1;
FS = 2;
LIFECYCLE = 3;
LOGGING = 4;
PROFILE = 5;
USAGE = 6;
PROC = 7;
STATE = 8;
DEBUG = 9;
}
// allowed_controls represents which endpoints may be registered to the
// server.
repeated Endpoint allowed_controls = 1;
}

View File

@ -45,6 +45,7 @@ go_library(
"//pkg/sentry/arch",
"//pkg/sentry/arch:registers_go_proto",
"//pkg/sentry/control",
"//pkg/sentry/control:control_go_proto",
"//pkg/sentry/devices/memdev",
"//pkg/sentry/devices/ttydev",
"//pkg/sentry/devices/tundev",

View File

@ -26,6 +26,7 @@ import (
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/control"
controlpb "gvisor.dev/gvisor/pkg/sentry/control/control_go_proto"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/socket/netstack"
@ -165,15 +166,31 @@ func newController(fd int, l *Loader) (*controller, error) {
ctrl.srv.Register(net)
}
ctrl.srv.Register(&debug{})
ctrl.srv.Register(&control.Events{})
ctrl.srv.Register(&control.Logging{})
ctrl.srv.Register(&control.Lifecycle{l.k})
ctrl.srv.Register(&control.Fs{l.k})
ctrl.srv.Register(&control.Usage{l.k})
if l.root.conf.ProfileEnable {
ctrl.srv.Register(control.NewProfile(l.k))
if l.root.conf.Controls.Controls != nil {
for _, c := range l.root.conf.Controls.Controls.AllowedControls {
switch c {
case controlpb.ControlConfig_EVENTS:
ctrl.srv.Register(&control.Events{})
case controlpb.ControlConfig_FS:
ctrl.srv.Register(&control.Fs{Kernel: l.k})
case controlpb.ControlConfig_LIFECYCLE:
ctrl.srv.Register(&control.Lifecycle{Kernel: l.k})
case controlpb.ControlConfig_LOGGING:
ctrl.srv.Register(&control.Logging{})
case controlpb.ControlConfig_PROFILE:
if l.root.conf.ProfileEnable {
ctrl.srv.Register(control.NewProfile(l.k))
}
case controlpb.ControlConfig_USAGE:
ctrl.srv.Register(&control.Usage{Kernel: l.k})
case controlpb.ControlConfig_PROC:
ctrl.srv.Register(&control.Proc{Kernel: l.k})
case controlpb.ControlConfig_STATE:
ctrl.srv.Register(&control.State{Kernel: l.k})
case controlpb.ControlConfig_DEBUG:
ctrl.srv.Register(&debug{})
}
}
}
return ctrl, nil

View File

@ -11,6 +11,7 @@ go_library(
visibility = ["//:sandbox"],
deps = [
"//pkg/refs",
"//pkg/sentry/control:control_go_proto",
"//pkg/sentry/watchdog",
"//pkg/sync",
"//runsc/flag",
@ -24,5 +25,8 @@ go_test(
"config_test.go",
],
library = ":config",
deps = ["//runsc/flag"],
deps = [
"//pkg/sentry/control:control_go_proto",
"//runsc/flag",
],
)

View File

@ -19,8 +19,10 @@ package config
import (
"fmt"
"strings"
"gvisor.dev/gvisor/pkg/refs"
controlpb "gvisor.dev/gvisor/pkg/sentry/control/control_go_proto"
"gvisor.dev/gvisor/pkg/sentry/watchdog"
)
@ -135,6 +137,9 @@ type Config struct {
// ProfileEnable is set to prepare the sandbox to be profiled.
ProfileEnable bool `flag:"profile"`
// Controls defines the controls that may be enabled.
Controls controlConfig `flag:"controls"`
// RestoreFile is the path to the saved container image
RestoreFile string
@ -351,6 +356,96 @@ func (q QueueingDiscipline) String() string {
panic(fmt.Sprintf("Invalid qdisc %d", q))
}
// controlConfig represents control endpoints.
type controlConfig struct {
Controls *controlpb.ControlConfig
}
// Set implements flag.Value.
func (c *controlConfig) Set(v string) error {
controls := strings.Split(v, ",")
var controlList []controlpb.ControlConfig_Endpoint
for _, control := range controls {
switch control {
case "EVENTS":
controlList = append(controlList, controlpb.ControlConfig_EVENTS)
case "FS":
controlList = append(controlList, controlpb.ControlConfig_FS)
case "LIFECYCLE":
controlList = append(controlList, controlpb.ControlConfig_LIFECYCLE)
case "LOGGING":
controlList = append(controlList, controlpb.ControlConfig_LOGGING)
case "PROFILE":
controlList = append(controlList, controlpb.ControlConfig_PROFILE)
case "USAGE":
controlList = append(controlList, controlpb.ControlConfig_USAGE)
case "PROC":
controlList = append(controlList, controlpb.ControlConfig_PROC)
case "STATE":
controlList = append(controlList, controlpb.ControlConfig_STATE)
case "DEBUG":
controlList = append(controlList, controlpb.ControlConfig_DEBUG)
default:
return fmt.Errorf("invalid control %q", control)
}
}
c.Controls.AllowedControls = controlList
return nil
}
// Get implements flag.Value.
func (c *controlConfig) Get() interface{} {
return *c
}
// String implements flag.Value.
func (c *controlConfig) String() string {
v := ""
for _, control := range c.Controls.AllowedControls {
if len(v) > 0 {
v += ","
}
switch control {
case controlpb.ControlConfig_EVENTS:
v += "EVENTS"
case controlpb.ControlConfig_FS:
v += "FS"
case controlpb.ControlConfig_LIFECYCLE:
v += "LIFECYCLE"
case controlpb.ControlConfig_LOGGING:
v += "LOGGING"
case controlpb.ControlConfig_PROFILE:
v += "PROFILE"
case controlpb.ControlConfig_USAGE:
v += "USAGE"
case controlpb.ControlConfig_PROC:
v += "PROC"
case controlpb.ControlConfig_STATE:
v += "STATE"
case controlpb.ControlConfig_DEBUG:
v += "DEBUG"
default:
panic(fmt.Sprintf("Invalid control %d", control))
}
}
return v
}
func defaultControlConfig() *controlConfig {
c := controlConfig{}
c.Controls = &controlpb.ControlConfig{}
c.Controls.AllowedControls = append(c.Controls.AllowedControls, controlpb.ControlConfig_EVENTS)
c.Controls.AllowedControls = append(c.Controls.AllowedControls, controlpb.ControlConfig_FS)
c.Controls.AllowedControls = append(c.Controls.AllowedControls, controlpb.ControlConfig_LIFECYCLE)
c.Controls.AllowedControls = append(c.Controls.AllowedControls, controlpb.ControlConfig_LOGGING)
c.Controls.AllowedControls = append(c.Controls.AllowedControls, controlpb.ControlConfig_PROFILE)
c.Controls.AllowedControls = append(c.Controls.AllowedControls, controlpb.ControlConfig_USAGE)
c.Controls.AllowedControls = append(c.Controls.AllowedControls, controlpb.ControlConfig_PROC)
c.Controls.AllowedControls = append(c.Controls.AllowedControls, controlpb.ControlConfig_STATE)
c.Controls.AllowedControls = append(c.Controls.AllowedControls, controlpb.ControlConfig_DEBUG)
return &c
}
func leakModePtr(v refs.LeakMode) *refs.LeakMode {
return &v
}

View File

@ -18,6 +18,7 @@ import (
"strings"
"testing"
controlpb "gvisor.dev/gvisor/pkg/sentry/control/control_go_proto"
"gvisor.dev/gvisor/runsc/flag"
)
@ -59,6 +60,9 @@ func TestFromFlags(t *testing.T) {
if err := flag.CommandLine.Lookup("network").Value.Set("none"); err != nil {
t.Errorf("Flag set: %v", err)
}
if err := flag.CommandLine.Lookup("controls").Value.Set("EVENTS,FS"); err != nil {
t.Errorf("Flag set: %v", err)
}
defer func() {
if err := setDefault("root"); err != nil {
t.Errorf("Flag set: %v", err)
@ -72,6 +76,9 @@ func TestFromFlags(t *testing.T) {
if err := setDefault("network"); err != nil {
t.Errorf("Flag set: %v", err)
}
if err := setDefault("controls"); err != nil {
t.Errorf("Flag set: %v", err)
}
}()
c, err := NewFromFlags()
@ -90,6 +97,12 @@ func TestFromFlags(t *testing.T) {
if want := NetworkNone; c.Network != want {
t.Errorf("Network=%v, want: %v", c.Network, want)
}
wants := []controlpb.ControlConfig_Endpoint{controlpb.ControlConfig_EVENTS, controlpb.ControlConfig_FS}
for i, want := range wants {
if c.Controls.Controls.AllowedControls[i] != want {
t.Errorf("Controls.Controls.AllowedControls[%d]=%v, want: %v", i, c.Controls.Controls.AllowedControls[i], want)
}
}
}
func TestToFlags(t *testing.T) {
@ -101,10 +114,15 @@ func TestToFlags(t *testing.T) {
c.Debug = true
c.NumNetworkChannels = 123
c.Network = NetworkNone
c.Controls = controlConfig{
Controls: &controlpb.ControlConfig{
AllowedControls: []controlpb.ControlConfig_Endpoint{controlpb.ControlConfig_EVENTS, controlpb.ControlConfig_FS},
},
}
flags := c.ToFlags()
if len(flags) != 4 {
t.Errorf("wrong number of flags set, want: 4, got: %d: %s", len(flags), flags)
if len(flags) != 5 {
t.Errorf("wrong number of flags set, want: 5, got: %d: %s", len(flags), flags)
}
t.Logf("Flags: %s", flags)
fm := map[string]string{}
@ -117,6 +135,7 @@ func TestToFlags(t *testing.T) {
"--debug": "true",
"--num-network-channels": "123",
"--network": "none",
"--controls": "EVENTS,FS",
} {
if got, ok := fm[name]; ok {
if got != want {

View File

@ -67,6 +67,7 @@ func RegisterFlags() {
flag.Var(leakModePtr(refs.NoLeakChecking), "ref-leak-mode", "sets reference leak check mode: disabled (default), log-names, log-traces.")
flag.Bool("cpu-num-from-quota", false, "set cpu number to cpu quota (least integer greater or equal to quota value, but not less than 2)")
flag.Bool("oci-seccomp", false, "Enables loading OCI seccomp filters inside the sandbox.")
flag.Var(defaultControlConfig(), "controls", "Sentry control endpoints.")
// Flags that control sandbox runtime behavior: FS related.
flag.Var(fileAccessTypePtr(FileAccessExclusive), "file-access", "specifies which filesystem validation to use for the root mount: exclusive (default), shared.")