github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/config/flags.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package config
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"path/filepath"
    21  	"reflect"
    22  	"strconv"
    23  
    24  	"github.com/SagerNet/gvisor/pkg/refs"
    25  	"github.com/SagerNet/gvisor/pkg/sentry/watchdog"
    26  	"github.com/SagerNet/gvisor/pkg/sync"
    27  	"github.com/SagerNet/gvisor/runsc/flag"
    28  )
    29  
    30  var registration sync.Once
    31  
    32  // RegisterFlags registers flags used to populate Config.
    33  func RegisterFlags() {
    34  	registration.Do(func() {
    35  		// Although these flags are not part of the OCI spec, they are used by
    36  		// Docker, and thus should not be changed.
    37  		flag.String("root", "", "root directory for storage of container state.")
    38  		flag.String("log", "", "file path where internal debug information is written, default is stdout.")
    39  		flag.String("log-format", "text", "log format: text (default), json, or json-k8s.")
    40  		flag.Bool("debug", false, "enable debug logging.")
    41  
    42  		// These flags are unique to runsc, and are used to configure parts of the
    43  		// system that are not covered by the runtime spec.
    44  
    45  		// Debugging flags.
    46  		flag.String("debug-log", "", "additional location for logs. If it ends with '/', log files are created inside the directory with default names. The following variables are available: %TIMESTAMP%, %COMMAND%.")
    47  		flag.String("panic-log", "", "file path where panic reports and other Go's runtime messages are written.")
    48  		flag.String("coverage-report", "", "file path where Go coverage reports are written. Reports will only be generated if runsc is built with --collect_code_coverage and --instrumentation_filter Bazel flags.")
    49  		flag.Bool("log-packets", false, "enable network packet logging.")
    50  		flag.String("debug-log-format", "text", "log format: text (default), json, or json-k8s.")
    51  		flag.Bool("alsologtostderr", false, "send log messages to stderr.")
    52  		flag.Bool("allow-flag-override", false, "allow OCI annotations (dev.gvisor.flag.<name>) to override flags for debugging.")
    53  		flag.String("traceback", "system", "golang runtime's traceback level")
    54  
    55  		// Debugging flags: strace related
    56  		flag.Bool("strace", false, "enable strace.")
    57  		flag.String("strace-syscalls", "", "comma-separated list of syscalls to trace. If --strace is true and this list is empty, then all syscalls will be traced.")
    58  		flag.Uint("strace-log-size", 1024, "default size (in bytes) to log data argument blobs.")
    59  
    60  		// Flags that control sandbox runtime behavior.
    61  		flag.String("platform", "ptrace", "specifies which platform to use: ptrace (default), kvm.")
    62  		flag.Var(watchdogActionPtr(watchdog.LogWarning), "watchdog-action", "sets what action the watchdog takes when triggered: log (default), panic.")
    63  		flag.Int("panic-signal", -1, "register signal handling that panics. Usually set to SIGUSR2(12) to troubleshoot hangs. -1 disables it.")
    64  		flag.Bool("profile", false, "prepares the sandbox to use Golang profiler. Note that enabling profiler loosens the seccomp protection added to the sandbox (DO NOT USE IN PRODUCTION).")
    65  		flag.Bool("rootless", false, "it allows the sandbox to be started with a user that is not root. Sandbox and Gofer processes may run with same privileges as current user.")
    66  		flag.Var(leakModePtr(refs.NoLeakChecking), "ref-leak-mode", "sets reference leak check mode: disabled (default), log-names, log-traces.")
    67  		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)")
    68  		flag.Bool("oci-seccomp", false, "Enables loading OCI seccomp filters inside the sandbox.")
    69  
    70  		// Flags that control sandbox runtime behavior: FS related.
    71  		flag.Var(fileAccessTypePtr(FileAccessExclusive), "file-access", "specifies which filesystem validation to use for the root mount: exclusive (default), shared.")
    72  		flag.Var(fileAccessTypePtr(FileAccessShared), "file-access-mounts", "specifies which filesystem validation to use for volumes other than the root mount: shared (default), exclusive.")
    73  		flag.Bool("overlay", false, "wrap filesystem mounts with writable overlay. All modifications are stored in memory inside the sandbox.")
    74  		flag.Bool("verity", false, "specifies whether a verity file system will be mounted.")
    75  		flag.Bool("fsgofer-host-uds", false, "allow the gofer to mount Unix Domain Sockets.")
    76  		flag.Bool("vfs2", false, "enables VFSv2. This uses the new VFS layer that is faster than the previous one.")
    77  		flag.Bool("fuse", false, "TEST ONLY; use while FUSE in VFSv2 is landing. This allows the use of the new experimental FUSE filesystem.")
    78  		flag.Bool("cgroupfs", false, "Automatically mount cgroupfs.")
    79  
    80  		// Flags that control sandbox runtime behavior: network related.
    81  		flag.Var(networkTypePtr(NetworkSandbox), "network", "specifies which network to use: sandbox (default), host, none. Using network inside the sandbox is more secure because it's isolated from the host network.")
    82  		flag.Bool("net-raw", false, "enable raw sockets. When false, raw sockets are disabled by removing CAP_NET_RAW from containers (`runsc exec` will still be able to utilize raw sockets). Raw sockets allow malicious containers to craft packets and potentially attack the network.")
    83  		flag.Bool("gso", true, "enable hardware segmentation offload if it is supported by a network device.")
    84  		flag.Bool("software-gso", true, "enable software segmentation offload when hardware offload can't be enabled.")
    85  		flag.Bool("tx-checksum-offload", false, "enable TX checksum offload.")
    86  		flag.Bool("rx-checksum-offload", true, "enable RX checksum offload.")
    87  		flag.Var(queueingDisciplinePtr(QDiscFIFO), "qdisc", "specifies which queueing discipline to apply by default to the non loopback nics used by the sandbox.")
    88  		flag.Int("num-network-channels", 1, "number of underlying channels(FDs) to use for network link endpoints.")
    89  
    90  		// Test flags, not to be used outside tests, ever.
    91  		flag.Bool("TESTONLY-unsafe-nonroot", false, "TEST ONLY; do not ever use! This skips many security measures that isolate the host from the sandbox.")
    92  		flag.String("TESTONLY-test-name-env", "", "TEST ONLY; do not ever use! Used for automated tests to improve logging.")
    93  	})
    94  }
    95  
    96  // NewFromFlags creates a new Config with values coming from command line flags.
    97  func NewFromFlags() (*Config, error) {
    98  	conf := &Config{}
    99  
   100  	obj := reflect.ValueOf(conf).Elem()
   101  	st := obj.Type()
   102  	for i := 0; i < st.NumField(); i++ {
   103  		f := st.Field(i)
   104  		name, ok := f.Tag.Lookup("flag")
   105  		if !ok {
   106  			// No flag set for this field.
   107  			continue
   108  		}
   109  		fl := flag.CommandLine.Lookup(name)
   110  		if fl == nil {
   111  			panic(fmt.Sprintf("Flag %q not found", name))
   112  		}
   113  		x := reflect.ValueOf(flag.Get(fl.Value))
   114  		obj.Field(i).Set(x)
   115  	}
   116  
   117  	if len(conf.RootDir) == 0 {
   118  		// If not set, set default root dir to something (hopefully) user-writeable.
   119  		conf.RootDir = "/var/run/runsc"
   120  		if runtimeDir, ok := os.LookupEnv("XDG_RUNTIME_DIR"); ok {
   121  			conf.RootDir = filepath.Join(runtimeDir, "runsc")
   122  		}
   123  	}
   124  
   125  	if err := conf.validate(); err != nil {
   126  		return nil, err
   127  	}
   128  	return conf, nil
   129  }
   130  
   131  // ToFlags returns a slice of flags that correspond to the given Config.
   132  func (c *Config) ToFlags() []string {
   133  	var rv []string
   134  
   135  	obj := reflect.ValueOf(c).Elem()
   136  	st := obj.Type()
   137  	for i := 0; i < st.NumField(); i++ {
   138  		f := st.Field(i)
   139  		name, ok := f.Tag.Lookup("flag")
   140  		if !ok {
   141  			// No flag set for this field.
   142  			continue
   143  		}
   144  		val := getVal(obj.Field(i))
   145  
   146  		flag := flag.CommandLine.Lookup(name)
   147  		if flag == nil {
   148  			panic(fmt.Sprintf("Flag %q not found", name))
   149  		}
   150  		if val == flag.DefValue {
   151  			continue
   152  		}
   153  		rv = append(rv, fmt.Sprintf("--%s=%s", flag.Name, val))
   154  	}
   155  	return rv
   156  }
   157  
   158  // Override writes a new value to a flag.
   159  func (c *Config) Override(name string, value string) error {
   160  	if !c.AllowFlagOverride {
   161  		return fmt.Errorf("flag override disabled, use --allow-flag-override to enable it")
   162  	}
   163  
   164  	obj := reflect.ValueOf(c).Elem()
   165  	st := obj.Type()
   166  	for i := 0; i < st.NumField(); i++ {
   167  		f := st.Field(i)
   168  		fieldName, ok := f.Tag.Lookup("flag")
   169  		if !ok || fieldName != name {
   170  			// Not a flag field, or flag name doesn't match.
   171  			continue
   172  		}
   173  		fl := flag.CommandLine.Lookup(name)
   174  		if fl == nil {
   175  			// Flag must exist if there is a field match above.
   176  			panic(fmt.Sprintf("Flag %q not found", name))
   177  		}
   178  
   179  		// Use flag to convert the string value to the underlying flag type, using
   180  		// the same rules as the command-line for consistency.
   181  		if err := fl.Value.Set(value); err != nil {
   182  			return fmt.Errorf("error setting flag %s=%q: %w", name, value, err)
   183  		}
   184  		x := reflect.ValueOf(flag.Get(fl.Value))
   185  		obj.Field(i).Set(x)
   186  
   187  		// Validates the config again to ensure it's left in a consistent state.
   188  		return c.validate()
   189  	}
   190  	return fmt.Errorf("flag %q not found. Cannot set it to %q", name, value)
   191  }
   192  
   193  func getVal(field reflect.Value) string {
   194  	if str, ok := field.Addr().Interface().(fmt.Stringer); ok {
   195  		return str.String()
   196  	}
   197  	switch field.Kind() {
   198  	case reflect.Bool:
   199  		return strconv.FormatBool(field.Bool())
   200  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   201  		return strconv.FormatInt(field.Int(), 10)
   202  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   203  		return strconv.FormatUint(field.Uint(), 10)
   204  	case reflect.String:
   205  		return field.String()
   206  	default:
   207  		panic("unknown type " + field.Kind().String())
   208  	}
   209  }