diff options
Diffstat (limited to 'runsc/cmd')
-rw-r--r-- | runsc/cmd/BUILD | 2 | ||||
-rw-r--r-- | runsc/cmd/do.go | 108 | ||||
-rw-r--r-- | runsc/cmd/gofer.go | 6 | ||||
-rw-r--r-- | runsc/cmd/mitigate.go | 49 | ||||
-rw-r--r-- | runsc/cmd/mitigate_extras.go | 33 | ||||
-rw-r--r-- | runsc/cmd/mitigate_test.go | 7 | ||||
-rw-r--r-- | runsc/cmd/symbolize.go | 6 | ||||
-rw-r--r-- | runsc/cmd/verity_prepare.go | 108 |
8 files changed, 231 insertions, 88 deletions
diff --git a/runsc/cmd/BUILD b/runsc/cmd/BUILD index 2c3b4058b..39c8ff603 100644 --- a/runsc/cmd/BUILD +++ b/runsc/cmd/BUILD @@ -23,6 +23,7 @@ go_library( "kill.go", "list.go", "mitigate.go", + "mitigate_extras.go", "path.go", "pause.go", "ps.go", @@ -35,6 +36,7 @@ go_library( "statefile.go", "symbolize.go", "syscalls.go", + "verity_prepare.go", "wait.go", ], visibility = [ diff --git a/runsc/cmd/do.go b/runsc/cmd/do.go index 455c57692..5485db149 100644 --- a/runsc/cmd/do.go +++ b/runsc/cmd/do.go @@ -126,9 +126,8 @@ func (c *Do) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) su Hostname: hostname, } - specutils.LogSpec(spec) - cid := fmt.Sprintf("runsc-%06d", rand.Int31n(1000000)) + if conf.Network == config.NetworkNone { addNamespace(spec, specs.LinuxNamespace{Type: specs.NetworkNamespace}) @@ -154,55 +153,7 @@ func (c *Do) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) su } } - out, err := json.Marshal(spec) - if err != nil { - return Errorf("Error to marshal spec: %v", err) - } - tmpDir, err := ioutil.TempDir("", "runsc-do") - if err != nil { - return Errorf("Error to create tmp dir: %v", err) - } - defer os.RemoveAll(tmpDir) - - log.Infof("Changing configuration RootDir to %q", tmpDir) - conf.RootDir = tmpDir - - cfgPath := filepath.Join(tmpDir, "config.json") - if err := ioutil.WriteFile(cfgPath, out, 0755); err != nil { - return Errorf("Error write spec: %v", err) - } - - containerArgs := container.Args{ - ID: cid, - Spec: spec, - BundleDir: tmpDir, - Attached: true, - } - ct, err := container.New(conf, containerArgs) - if err != nil { - return Errorf("creating container: %v", err) - } - defer ct.Destroy() - - if err := ct.Start(conf); err != nil { - return Errorf("starting container: %v", err) - } - - // Forward signals to init in the container. Thus if we get SIGINT from - // ^C, the container gracefully exit, and we can clean up. - // - // N.B. There is a still a window before this where a signal may kill - // this process, skipping cleanup. - stopForwarding := ct.ForwardSignals(0 /* pid */, false /* fgProcess */) - defer stopForwarding() - - ws, err := ct.Wait() - if err != nil { - return Errorf("waiting for container: %v", err) - } - - *waitStatus = ws - return subcommands.ExitSuccess + return startContainerAndWait(spec, conf, cid, waitStatus) } func addNamespace(spec *specs.Spec, ns specs.LinuxNamespace) { @@ -397,3 +348,58 @@ func calculatePeerIP(ip string) (string, error) { } return fmt.Sprintf("%s.%s.%s.%d", parts[0], parts[1], parts[2], n), nil } + +func startContainerAndWait(spec *specs.Spec, conf *config.Config, cid string, waitStatus *unix.WaitStatus) subcommands.ExitStatus { + specutils.LogSpec(spec) + + out, err := json.Marshal(spec) + if err != nil { + return Errorf("Error to marshal spec: %v", err) + } + tmpDir, err := ioutil.TempDir("", "runsc-do") + if err != nil { + return Errorf("Error to create tmp dir: %v", err) + } + defer os.RemoveAll(tmpDir) + + log.Infof("Changing configuration RootDir to %q", tmpDir) + conf.RootDir = tmpDir + + cfgPath := filepath.Join(tmpDir, "config.json") + if err := ioutil.WriteFile(cfgPath, out, 0755); err != nil { + return Errorf("Error write spec: %v", err) + } + + containerArgs := container.Args{ + ID: cid, + Spec: spec, + BundleDir: tmpDir, + Attached: true, + } + + ct, err := container.New(conf, containerArgs) + if err != nil { + return Errorf("creating container: %v", err) + } + defer ct.Destroy() + + if err := ct.Start(conf); err != nil { + return Errorf("starting container: %v", err) + } + + // Forward signals to init in the container. Thus if we get SIGINT from + // ^C, the container gracefully exit, and we can clean up. + // + // N.B. There is a still a window before this where a signal may kill + // this process, skipping cleanup. + stopForwarding := ct.ForwardSignals(0 /* pid */, false /* fgProcess */) + defer stopForwarding() + + ws, err := ct.Wait() + if err != nil { + return Errorf("waiting for container: %v", err) + } + + *waitStatus = ws + return subcommands.ExitSuccess +} diff --git a/runsc/cmd/gofer.go b/runsc/cmd/gofer.go index 4cb0164dd..6a755ecb6 100644 --- a/runsc/cmd/gofer.go +++ b/runsc/cmd/gofer.go @@ -176,7 +176,7 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) mountIdx := 1 // first one is the root for _, m := range spec.Mounts { - if specutils.Is9PMount(m) { + if specutils.Is9PMount(m, conf.VFS2) { cfg := fsgofer.Config{ ROMount: isReadonlyMount(m.Options) || conf.Overlay, HostUDS: conf.FSGoferHostUDS, @@ -350,7 +350,7 @@ func setupRootFS(spec *specs.Spec, conf *config.Config) error { // creates directories as needed. func setupMounts(conf *config.Config, mounts []specs.Mount, root string) error { for _, m := range mounts { - if m.Type != "bind" || !specutils.IsVFS1SupportedDevMount(m) { + if !specutils.Is9PMount(m, conf.VFS2) { continue } @@ -390,7 +390,7 @@ func setupMounts(conf *config.Config, mounts []specs.Mount, root string) error { func resolveMounts(conf *config.Config, mounts []specs.Mount, root string) ([]specs.Mount, error) { cleanMounts := make([]specs.Mount, 0, len(mounts)) for _, m := range mounts { - if m.Type != "bind" || !specutils.IsVFS1SupportedDevMount(m) { + if !specutils.Is9PMount(m, conf.VFS2) { cleanMounts = append(cleanMounts, m) continue } diff --git a/runsc/cmd/mitigate.go b/runsc/cmd/mitigate.go index fddf0e0dd..d37ab80ba 100644 --- a/runsc/cmd/mitigate.go +++ b/runsc/cmd/mitigate.go @@ -40,8 +40,8 @@ type Mitigate struct { reverse bool // Path to file to read to create CPUSet. path string - // Callback to check if a given thread is vulnerable. - vulnerable func(other mitigate.Thread) bool + // Extra data for post mitigate operations. + data string } // Name implements subcommands.command.name. @@ -54,19 +54,20 @@ func (*Mitigate) Synopsis() string { return "mitigate mitigates the underlying system against side channel attacks" } -// Usage implments Usage for cmd.Mitigate. +// Usage implements Usage for cmd.Mitigate. func (m Mitigate) Usage() string { - return `mitigate [flags] + return fmt.Sprintf(`mitigate [flags] mitigate mitigates a system to the "MDS" vulnerability by implementing a manual shutdown of SMT. The command checks /proc/cpuinfo for cpus having the MDS vulnerability, and if found, shutdown all but one CPU per hyperthread pair via /sys/devices/system/cpu/cpu{N}/online. CPUs can be restored by writing "2" to each file in /sys/devices/system/cpu/cpu{N}/online or performing a system reboot. -The command can be reversed with --reverse, which reads the total CPUs from /sys/devices/system/cpu/possible and enables all with /sys/devices/system/cpu/cpu{N}/online.` +The command can be reversed with --reverse, which reads the total CPUs from /sys/devices/system/cpu/possible and enables all with /sys/devices/system/cpu/cpu{N}/online.%s`, m.usage()) } // SetFlags sets flags for the command Mitigate. func (m *Mitigate) SetFlags(f *flag.FlagSet) { f.BoolVar(&m.dryRun, "dryrun", false, "run the command without changing system") f.BoolVar(&m.reverse, "reverse", false, "reverse mitigate by enabling all CPUs") + m.setFlags(f) } // Execute implements subcommands.Command.Execute. @@ -81,13 +82,17 @@ func (m *Mitigate) Execute(_ context.Context, f *flag.FlagSet, args ...interface m.path = allPossibleCPUs } - m.vulnerable = func(other mitigate.Thread) bool { - return other.IsVulnerable() + set, err := m.doExecute() + if err != nil { + return Errorf("Execute failed: %v", err) + } + + if m.data == "" { + return subcommands.ExitSuccess } - if _, err := m.doExecute(); err != nil { - log.Warningf("Execute failed: %v", err) - return subcommands.ExitFailure + if err = m.postMitigate(set); err != nil { + return Errorf("Post Mitigate failed: %v", err) } return subcommands.ExitSuccess @@ -98,32 +103,26 @@ func (m *Mitigate) doExecute() (mitigate.CPUSet, error) { if m.dryRun { log.Infof("Running with DryRun. No cpu settings will be changed.") } + data, err := ioutil.ReadFile(m.path) + if err != nil { + return nil, fmt.Errorf("failed to read %s: %w", m.path, err) + } if m.reverse { - data, err := ioutil.ReadFile(m.path) - if err != nil { - return nil, fmt.Errorf("failed to read %s: %v", m.path, err) - } - set, err := m.doReverse(data) if err != nil { - return nil, fmt.Errorf("reverse operation failed: %v", err) + return nil, fmt.Errorf("reverse operation failed: %w", err) } return set, nil } - - data, err := ioutil.ReadFile(m.path) - if err != nil { - return nil, fmt.Errorf("failed to read %s: %v", m.path, err) - } set, err := m.doMitigate(data) if err != nil { - return nil, fmt.Errorf("mitigate operation failed: %v", err) + return nil, fmt.Errorf("mitigate operation failed: %w", err) } return set, nil } func (m *Mitigate) doMitigate(data []byte) (mitigate.CPUSet, error) { - set, err := mitigate.NewCPUSet(data, m.vulnerable) + set, err := mitigate.NewCPUSet(data) if err != nil { return nil, err } @@ -139,7 +138,7 @@ func (m *Mitigate) doMitigate(data []byte) (mitigate.CPUSet, error) { continue } if err := t.Disable(); err != nil { - return nil, fmt.Errorf("error disabling thread: %s err: %v", t, err) + return nil, fmt.Errorf("error disabling thread: %s err: %w", t, err) } } log.Infof("Shutdown successful.") @@ -164,7 +163,7 @@ func (m *Mitigate) doReverse(data []byte) (mitigate.CPUSet, error) { continue } if err := t.Enable(); err != nil { - return nil, fmt.Errorf("error enabling thread: %s err: %v", t, err) + return nil, fmt.Errorf("error enabling thread: %s err: %w", t, err) } } log.Infof("Enable successful.") diff --git a/runsc/cmd/mitigate_extras.go b/runsc/cmd/mitigate_extras.go new file mode 100644 index 000000000..2cb2833f0 --- /dev/null +++ b/runsc/cmd/mitigate_extras.go @@ -0,0 +1,33 @@ +// 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. + +package cmd + +import ( + "gvisor.dev/gvisor/runsc/flag" + "gvisor.dev/gvisor/runsc/mitigate" +) + +// usage returns any extra bits of the usage string. +func (m *Mitigate) usage() string { + return "" +} + +// setFlags sets extra flags for the command Mitigate. +func (m *Mitigate) setFlags(f *flag.FlagSet) {} + +// postMitigate handles any postMitigate actions. +func (m *Mitigate) postMitigate(_ mitigate.CPUSet) error { + return nil +} diff --git a/runsc/cmd/mitigate_test.go b/runsc/cmd/mitigate_test.go index 163fece42..5a76667e3 100644 --- a/runsc/cmd/mitigate_test.go +++ b/runsc/cmd/mitigate_test.go @@ -21,7 +21,6 @@ import ( "strings" "testing" - "gvisor.dev/gvisor/runsc/mitigate" "gvisor.dev/gvisor/runsc/mitigate/mock" ) @@ -84,9 +83,6 @@ power management::84 t.Run(tc.name, func(t *testing.T) { m := &Mitigate{ dryRun: true, - vulnerable: func(other mitigate.Thread) bool { - return other.IsVulnerable() - }, } m.doExecuteTest(t, "Mitigate", tc.mitigateData, tc.mitigateCPU, tc.mitigateError) @@ -104,9 +100,6 @@ func TestExecuteSmoke(t *testing.T) { m := &Mitigate{ dryRun: true, - vulnerable: func(other mitigate.Thread) bool { - return other.IsVulnerable() - }, } m.doExecuteTest(t, "Mitigate", string(smokeMitigate), 0, nil) diff --git a/runsc/cmd/symbolize.go b/runsc/cmd/symbolize.go index fc0c69358..0fa4bfda1 100644 --- a/runsc/cmd/symbolize.go +++ b/runsc/cmd/symbolize.go @@ -65,13 +65,15 @@ func (c *Symbolize) Execute(_ context.Context, f *flag.FlagSet, args ...interfac f.Usage() return subcommands.ExitUsageError } - if !coverage.KcovAvailable() { + if !coverage.Available() { return Errorf("symbolize can only be used when coverage is available.") } coverage.InitCoverageData() if c.dumpAll { - coverage.WriteAllBlocks(os.Stdout) + if err := coverage.WriteAllBlocks(os.Stdout); err != nil { + return Errorf("Failed to write out blocks: %v", err) + } return subcommands.ExitSuccess } diff --git a/runsc/cmd/verity_prepare.go b/runsc/cmd/verity_prepare.go new file mode 100644 index 000000000..66128b2a3 --- /dev/null +++ b/runsc/cmd/verity_prepare.go @@ -0,0 +1,108 @@ +// 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. + +package cmd + +import ( + "context" + "fmt" + "math/rand" + "os" + + "github.com/google/subcommands" + specs "github.com/opencontainers/runtime-spec/specs-go" + "golang.org/x/sys/unix" + "gvisor.dev/gvisor/runsc/config" + "gvisor.dev/gvisor/runsc/flag" + "gvisor.dev/gvisor/runsc/specutils" +) + +// VerityPrepare implements subcommands.Commands for the "verity-prepare" +// command. It sets up a sandbox with a writable verity mount mapped to "--dir", +// and executes the verity measure tool specified by "--tool" in the sandbox. It +// is intended to prepare --dir to be mounted as a verity filesystem. +type VerityPrepare struct { + root string + tool string + dir string +} + +// Name implements subcommands.Command.Name. +func (*VerityPrepare) Name() string { + return "verity-prepare" +} + +// Synopsis implements subcommands.Command.Synopsis. +func (*VerityPrepare) Synopsis() string { + return "Generates the data structures necessary to enable verityfs on a filesystem." +} + +// Usage implements subcommands.Command.Usage. +func (*VerityPrepare) Usage() string { + return "verity-prepare --tool=<measure_tool> --dir=<path>" +} + +// SetFlags implements subcommands.Command.SetFlags. +func (c *VerityPrepare) SetFlags(f *flag.FlagSet) { + f.StringVar(&c.root, "root", "/", `path to the root directory, defaults to "/"`) + f.StringVar(&c.tool, "tool", "", "path to the verity measure_tool") + f.StringVar(&c.dir, "dir", "", "path to the directory to be hashed") +} + +// Execute implements subcommands.Command.Execute. +func (c *VerityPrepare) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { + conf := args[0].(*config.Config) + waitStatus := args[1].(*unix.WaitStatus) + + hostname, err := os.Hostname() + if err != nil { + return Errorf("Error to retrieve hostname: %v", err) + } + + // Map the entire host file system. + absRoot, err := resolvePath(c.root) + if err != nil { + return Errorf("Error resolving root: %v", err) + } + + spec := &specs.Spec{ + Root: &specs.Root{ + Path: absRoot, + }, + Process: &specs.Process{ + Cwd: absRoot, + Args: []string{c.tool, "--path", "/verityroot"}, + Env: os.Environ(), + Capabilities: specutils.AllCapabilities(), + }, + Hostname: hostname, + Mounts: []specs.Mount{ + specs.Mount{ + Source: c.dir, + Destination: "/verityroot", + Type: "bind", + Options: []string{"verity.roothash="}, + }, + }, + } + + cid := fmt.Sprintf("runsc-%06d", rand.Int31n(1000000)) + + // Force no networking, it is not necessary to run the verity measure tool. + conf.Network = config.NetworkNone + + conf.Verity = true + + return startContainerAndWait(spec, conf, cid, waitStatus) +} |