mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-06-28 16:27:50 +00:00
rootless: add rootless logic
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: #1874 Signed-off-by: Gabi Beyer <gabrielle.n.beyer@intel.com> Co-developed-by: Marco Vedovati <mvedovati@suse.com> Signed-off-by: Marco Vedovati <mvedovati@suse.com>
This commit is contained in:
parent
801a9a8fd0
commit
2d8b278c09
@ -18,6 +18,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/kata-containers/runtime/pkg/katautils"
|
"github.com/kata-containers/runtime/pkg/katautils"
|
||||||
|
"github.com/kata-containers/runtime/pkg/rootless"
|
||||||
"github.com/kata-containers/runtime/pkg/signals"
|
"github.com/kata-containers/runtime/pkg/signals"
|
||||||
vc "github.com/kata-containers/runtime/virtcontainers"
|
vc "github.com/kata-containers/runtime/virtcontainers"
|
||||||
vf "github.com/kata-containers/runtime/virtcontainers/factory"
|
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
|
// Set the katautils package logger
|
||||||
katautils.SetLogger(ctx, logger, originalLoggerLevel)
|
katautils.SetLogger(ctx, logger, originalLoggerLevel)
|
||||||
|
|
||||||
|
// Set the rootless package logger
|
||||||
|
rootless.SetLogger(ctx, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
// beforeSubcommands is the function to perform preliminary checks
|
// beforeSubcommands is the function to perform preliminary checks
|
||||||
|
123
pkg/rootless/rootless.go
Normal file
123
pkg/rootless/rootless.go
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
// Copyright (c) 2019 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
package rootless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
|
||||||
|
var parseError = errors.Errorf("Failed to parse uid map file %s", uidMapPath)
|
||||||
|
// 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))
|
||||||
|
// do some sanity checks
|
||||||
|
if len(ids) != 3 {
|
||||||
|
return parseError
|
||||||
|
}
|
||||||
|
userNSUid, err := strconv.ParseUint(ids[0], 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return parseError
|
||||||
|
}
|
||||||
|
hostUID, err := strconv.ParseUint(ids[1], 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return parseError
|
||||||
|
}
|
||||||
|
rangeUID, err := strconv.ParseUint(ids[1], 10, 0)
|
||||||
|
if err != nil || rangeUID == 0 {
|
||||||
|
return parseError
|
||||||
|
}
|
||||||
|
|
||||||
|
if userNSUid == 0 && hostUID != 0 {
|
||||||
|
rootlessLog.Info("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.WithError(err).Error("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
|
||||||
|
}
|
167
pkg/rootless/rootless_test.go
Normal file
167
pkg/rootless/rootless_test.go
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
// Copyright (c) 2019 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
package rootless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type uidMapping struct {
|
||||||
|
userNSUID int
|
||||||
|
hostUID int
|
||||||
|
rangeUID int
|
||||||
|
}
|
||||||
|
|
||||||
|
type testScenario struct {
|
||||||
|
isRootless bool
|
||||||
|
uidMap []uidMapping
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func uidMapString(userNSUID, hostUID, rangeUID int) string {
|
||||||
|
return fmt.Sprintf("\t%d\t%d\t%d", userNSUID, hostUID, rangeUID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testWithUIDMapContent(content string, expectedRootless bool, t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
// Create a test-specific message that is added to each assert
|
||||||
|
// call. It will be displayed if any assert test fails.
|
||||||
|
msg := fmt.Sprintf("isRootless[%t]: %s", expectedRootless, content)
|
||||||
|
|
||||||
|
tmpDir, err := ioutil.TempDir("", "")
|
||||||
|
assert.NoError(err)
|
||||||
|
|
||||||
|
uidMapPath = filepath.Join(tmpDir, "testUIDMapFile")
|
||||||
|
defer func() {
|
||||||
|
uidMapPath = uidMapPathStore
|
||||||
|
os.RemoveAll(uidMapPath)
|
||||||
|
os.RemoveAll(tmpDir)
|
||||||
|
isRootless = false
|
||||||
|
initRootless = false
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = createTestUIDMapFile(content)
|
||||||
|
assert.NoError(err, msg)
|
||||||
|
|
||||||
|
// make call to IsRootless, this should also call
|
||||||
|
// SetRootless
|
||||||
|
assert.Equal(expectedRootless, IsRootless(), msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsRootless(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
// by default isRootless should be set to false initially
|
||||||
|
assert.False(isRootless)
|
||||||
|
|
||||||
|
allScenarios := []testScenario{
|
||||||
|
//"User NS UID is not root UID"
|
||||||
|
{
|
||||||
|
isRootless: false,
|
||||||
|
uidMap: []uidMapping{
|
||||||
|
{1, 0, 1},
|
||||||
|
{1, 0, 1000},
|
||||||
|
|
||||||
|
{1, 1000, 1},
|
||||||
|
{1, 1000, 1000},
|
||||||
|
|
||||||
|
{1000, 1000, 1},
|
||||||
|
{1000, 1000, 1000},
|
||||||
|
|
||||||
|
{1000, 1000, 5555},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
//"Host NS UID is root UID"
|
||||||
|
{
|
||||||
|
isRootless: false,
|
||||||
|
uidMap: []uidMapping{
|
||||||
|
{0, 0, 1},
|
||||||
|
{0, 0, 1000},
|
||||||
|
|
||||||
|
{1, 0, 1},
|
||||||
|
{1, 0, 1000},
|
||||||
|
|
||||||
|
{1000, 0, 0},
|
||||||
|
{1000, 0, 1},
|
||||||
|
{1000, 0, 1000},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
//"UID range is zero"
|
||||||
|
{
|
||||||
|
isRootless: false,
|
||||||
|
uidMap: []uidMapping{
|
||||||
|
{0, 0, 0},
|
||||||
|
{1, 0, 0},
|
||||||
|
{1, 1000, 0},
|
||||||
|
{1000, 1000, 0},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
//"Negative UIDs"
|
||||||
|
{
|
||||||
|
isRootless: false,
|
||||||
|
uidMap: []uidMapping{
|
||||||
|
{-1, 0, 0},
|
||||||
|
{-1, 0, 1},
|
||||||
|
{-1, 0, 1000},
|
||||||
|
|
||||||
|
{0, -1, 0},
|
||||||
|
{0, -1, 1},
|
||||||
|
{0, -1, 1000},
|
||||||
|
|
||||||
|
{1000, 1000, -1},
|
||||||
|
{1000, 1000, -1},
|
||||||
|
{1000, 1000, -1000},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
//"User NS UID is root UID, host UID is not root UID"
|
||||||
|
{
|
||||||
|
isRootless: true,
|
||||||
|
uidMap: []uidMapping{
|
||||||
|
{0, 1, 1},
|
||||||
|
{0, 1000, 1},
|
||||||
|
{0, 1000, 5555},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the tests
|
||||||
|
for _, scenario := range allScenarios {
|
||||||
|
for _, uidMap := range scenario.uidMap {
|
||||||
|
mapping := uidMapString(uidMap.userNSUID, uidMap.hostUID, uidMap.rangeUID)
|
||||||
|
testWithUIDMapContent(mapping, scenario.isRootless, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testWithUIDMapContent("", false, t)
|
||||||
|
|
||||||
|
testWithUIDMapContent("This is not a mapping", false, t)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user