Skip to content

Commit

Permalink
rootless: add rootless logic
Browse files Browse the repository at this point in the history
Add the ability to check whether kata is running rootlessly or
not. Add the setup of the rootless directory located in the dir
/run/user/<UID> directory.

Fixes: kata-containers#1874

Signed-off-by: Gabi Beyer <[email protected]>
  • Loading branch information
Gabi Beyer committed Aug 16, 2019
1 parent de4582e commit ab566d7
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 0 deletions.
4 changes: 4 additions & 0 deletions cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
103 changes: 103 additions & 0 deletions pkg/rootless/rootless.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright (c) 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//

package rootless

import (
"bufio"
"context"
"io"
"os"
"strings"
"sync"

"github.com/sirupsen/logrus"
)

var (
// initRootless states whether the isRootless variable
// has been set yet
initRootless bool

// isRootless states whether execution is rootless or not
isRootless bool

// lock for the initRootless and isRootless variables
rLock sync.Mutex

// XDG_RUNTIME_DIR defines the base directory relative to
// which user-specific non-essential runtime files are stored.
rootlessDir = os.Getenv("XDG_RUNTIME_DIR")

// uidMapPath defines the location of the uid_map file to
// determine whether a user is root or not
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 {
initRootless = true
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 {
rLock.Lock()
if !initRootless {
err := setRootless()
if err != nil {
rootlessLog.Errorf("Unable to determine if running rootless")
}
}
rLock.Unlock()
return isRootless
}

// GetRootlessDir returns the path to the location for rootless
// container and sandbox storage
func GetRootlessDir() string {
return rootlessDir
}
86 changes: 86 additions & 0 deletions pkg/rootless/rootless_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// 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
}

// TestIstRootlessRootlessUID1000 tests that isRootless is set to
// true when a host UID is not 0
func TestIsRootlessRootlessUID1000(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)

// make call to IsRootless, this should also call
// SetRootless
assert.True(IsRootless())
}

// TestIsRootlessRootUID0 tests that isRootless is not set when
// the host UID is 0
func TestIsRootlessRootUID0(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)

// make call to IsRootless, this should also call
// SetRootless
assert.False(IsRootless())
}

0 comments on commit ab566d7

Please sign in to comment.