diff --git a/cli/main.go b/cli/main.go index 72c9165e7d..8d0b36e84c 100644 --- a/cli/main.go +++ b/cli/main.go @@ -18,6 +18,7 @@ import ( "syscall" "github.com/kata-containers/runtime/pkg/katautils" + "github.com/kata-containers/runtime/pkg/rootless" "github.com/kata-containers/runtime/pkg/signals" vc "github.com/kata-containers/runtime/virtcontainers" vf "github.com/kata-containers/runtime/virtcontainers/factory" @@ -241,6 +242,9 @@ func setExternalLoggers(ctx context.Context, logger *logrus.Entry) { // Set the katautils package logger katautils.SetLogger(ctx, logger, originalLoggerLevel) + + // Set the rootless package logger + rootless.SetLogger(ctx, logger) } // beforeSubcommands is the function to perform preliminary checks @@ -337,6 +341,12 @@ func beforeSubcommands(c *cli.Context) error { kataLog.WithFields(fields).Info() + // Check if running rootlessly, and setup for rootless execution + err = rootless.SetRootless() + if err != nil { + fatal(err) + } + // make the data accessible to the sub-commands. c.App.Metadata["runtimeConfig"] = runtimeConfig c.App.Metadata["configFile"] = configFile diff --git a/pkg/rootless/rootless.go b/pkg/rootless/rootless.go new file mode 100644 index 0000000000..b9946fac7c --- /dev/null +++ b/pkg/rootless/rootless.go @@ -0,0 +1,79 @@ +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package rootless + +import ( + "bufio" + "context" + "io" + "os" + "strings" + + "github.com/sirupsen/logrus" +) + +var ( + isRootless bool + rootlessDir = os.Getenv("XDG_RUNTIME_DIR") + uidMapPath = "/proc/self/uid_map" + + rootlessLog = logrus.WithFields(logrus.Fields{ + "source": "rootless", + }) +) + +// SetLogger sets up a logger for the rootless pkg +func SetLogger(ctx context.Context, logger *logrus.Entry) { + fields := rootlessLog.Data + rootlessLog = logger.WithFields(fields) +} + +// SetRootless reads a uid_map file, compares the UID of the +// user inside the container vs on the host. If the host UID +// is not root, but the container ID is, it can be determined +// the user is running rootlessly. +func SetRootless() error { + file, err := os.Open(uidMapPath) + if err != nil { + return err + } + defer file.Close() + + buf := bufio.NewReader(file) + for { + line, _, err := buf.ReadLine() + if err != nil { + if err == io.EOF { + return nil + } + return err + } + if line == nil { + return nil + } + + // if the container id (id[0]) is 0 (root inside the container) + // has a mapping to the host id (id[1]) that is not root, then + // it can be determined that the host user is running rootless + ids := strings.Fields(string(line)) + if ids[0] == "0" && ids[1] != "0" { + rootlessLog.Infof("Running as rootless") + isRootless = true + return nil + } + } +} + +// IsRootless states whether kata is being ran with root or not +func IsRootless() bool { + return isRootless +} + +// GetRootlessDir returns the path to the location for rootless +// container and sandbox storage +func GetRootlessDir() string { + return rootlessDir +} diff --git a/pkg/rootless/rootless_test.go b/pkg/rootless/rootless_test.go new file mode 100644 index 0000000000..48df54f5c2 --- /dev/null +++ b/pkg/rootless/rootless_test.go @@ -0,0 +1,88 @@ +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package rootless + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +var uidMapPathStore = uidMapPath + +func createTestUIDMapFile(input string) error { + f, err := os.Create(uidMapPath) + if err != nil { + return err + } + defer f.Close() + + _, err = f.WriteString(input) + if err != nil { + return err + } + + return nil +} + +// TestSetRootlessRootlessUID1000 tests that isRootless is set to +// true when a host UID is not 0 +func TestSetRootlessRootlessUID1000(t *testing.T) { + assert := assert.New(t) + + // by default isRootless should be set to false initially + assert.False(IsRootless()) + + tmpDir, err := ioutil.TempDir("", "") + assert.NoError(err) + + uidMapPath = filepath.Join(tmpDir, "testUIDMapFile") + defer func() { + uidMapPath = uidMapPathStore + os.RemoveAll(tmpDir) + isRootless = false + }() + + mapping := "\t0\t1000\t5555" + err = createTestUIDMapFile(mapping) + assert.NoError(err) + + err = SetRootless() + assert.NoError(err) + + assert.True(IsRootless()) +} + +// TestSetRootlessRootUID0 tests that isRootless is not set when +// the host UID is 0 +func TestSetRootlessRootUID0(t *testing.T) { + assert := assert.New(t) + + // by default isRootless should be set to false initially + assert.False(IsRootless()) + + tmpDir, err := ioutil.TempDir("", "") + assert.NoError(err) + + uidMapPath = filepath.Join(tmpDir, "testUIDMapFile") + defer func() { + uidMapPath = uidMapPathStore + os.RemoveAll(uidMapPath) + isRootless = false + }() + + mapping := "\t0\t0\t5555" + err = createTestUIDMapFile(mapping) + assert.NoError(err) + + err = SetRootless() + assert.NoError(err) + + assert.False(IsRootless()) +}