mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-06-28 00:07:16 +00:00
Merge pull request #1463 from egernst/bindmount-infra
runtime: add support for readonly sandbox bindmounts
This commit is contained in:
commit
50f317dcff
@ -217,6 +217,8 @@ DEFPCIEROOTPORT := 0
|
|||||||
# Default cgroup model
|
# Default cgroup model
|
||||||
DEFSANDBOXCGROUPONLY ?= false
|
DEFSANDBOXCGROUPONLY ?= false
|
||||||
|
|
||||||
|
DEFBINDMOUNTS := []
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
FEATURE_SELINUX ?= check
|
FEATURE_SELINUX ?= check
|
||||||
|
|
||||||
@ -492,6 +494,7 @@ USER_VARS += DEFHOTPLUGVFIOONROOTBUS
|
|||||||
USER_VARS += DEFPCIEROOTPORT
|
USER_VARS += DEFPCIEROOTPORT
|
||||||
USER_VARS += DEFENTROPYSOURCE
|
USER_VARS += DEFENTROPYSOURCE
|
||||||
USER_VARS += DEFSANDBOXCGROUPONLY
|
USER_VARS += DEFSANDBOXCGROUPONLY
|
||||||
|
USER_VARS += DEFBINDMOUNTS
|
||||||
USER_VARS += FEATURE_SELINUX
|
USER_VARS += FEATURE_SELINUX
|
||||||
USER_VARS += BUILDFLAGS
|
USER_VARS += BUILDFLAGS
|
||||||
|
|
||||||
|
@ -252,6 +252,12 @@ disable_guest_seccomp=@DEFDISABLEGUESTSECCOMP@
|
|||||||
# See: https://godoc.org/github.com/kata-containers/runtime/virtcontainers#ContainerType
|
# See: https://godoc.org/github.com/kata-containers/runtime/virtcontainers#ContainerType
|
||||||
sandbox_cgroup_only=@DEFSANDBOXCGROUPONLY@
|
sandbox_cgroup_only=@DEFSANDBOXCGROUPONLY@
|
||||||
|
|
||||||
|
# If specified, sandbox_bind_mounts identifieds host paths to be mounted (ro) into the sandboxes shared path.
|
||||||
|
# This is only valid if filesystem sharing is utilized. The provided path(s) will be bindmounted into the shared fs directory.
|
||||||
|
# If defaults are utilized, these mounts should be available in the guest at `/run/kata-containers/shared/containers/sandbox-mounts`
|
||||||
|
# These will not be exposed to the container workloads, and are only provided for potential guest services.
|
||||||
|
sandbox_bind_mounts=@DEFBINDMOUNTS@
|
||||||
|
|
||||||
# Enabled experimental feature list, format: ["a", "b"].
|
# Enabled experimental feature list, format: ["a", "b"].
|
||||||
# Experimental features are features not stable enough for production,
|
# Experimental features are features not stable enough for production,
|
||||||
# they may break compatibility, and are prepared for a big version bump.
|
# they may break compatibility, and are prepared for a big version bump.
|
||||||
|
@ -511,6 +511,12 @@ disable_guest_seccomp=@DEFDISABLEGUESTSECCOMP@
|
|||||||
# See: https://godoc.org/github.com/kata-containers/runtime/virtcontainers#ContainerType
|
# See: https://godoc.org/github.com/kata-containers/runtime/virtcontainers#ContainerType
|
||||||
sandbox_cgroup_only=@DEFSANDBOXCGROUPONLY@
|
sandbox_cgroup_only=@DEFSANDBOXCGROUPONLY@
|
||||||
|
|
||||||
|
# If specified, sandbox_bind_mounts identifieds host paths to be mounted (ro) into the sandboxes shared path.
|
||||||
|
# This is only valid if filesystem sharing is utilized. The provided path(s) will be bindmounted into the shared fs directory.
|
||||||
|
# If defaults are utilized, these mounts should be available in the guest at `/run/kata-containers/shared/containers/sandbox-mounts`
|
||||||
|
# These will not be exposed to the container workloads, and are only provided for potential guest services.
|
||||||
|
sandbox_bind_mounts=@DEFBINDMOUNTS@
|
||||||
|
|
||||||
# Enabled experimental feature list, format: ["a", "b"].
|
# Enabled experimental feature list, format: ["a", "b"].
|
||||||
# Experimental features are features not stable enough for production,
|
# Experimental features are features not stable enough for production,
|
||||||
# they may break compatibility, and are prepared for a big version bump.
|
# they may break compatibility, and are prepared for a big version bump.
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
goruntime "runtime"
|
goruntime "runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -136,6 +137,7 @@ type runtime struct {
|
|||||||
DisableNewNetNs bool `toml:"disable_new_netns"`
|
DisableNewNetNs bool `toml:"disable_new_netns"`
|
||||||
DisableGuestSeccomp bool `toml:"disable_guest_seccomp"`
|
DisableGuestSeccomp bool `toml:"disable_guest_seccomp"`
|
||||||
SandboxCgroupOnly bool `toml:"sandbox_cgroup_only"`
|
SandboxCgroupOnly bool `toml:"sandbox_cgroup_only"`
|
||||||
|
SandboxBindMounts []string `toml:"sandbox_bind_mounts"`
|
||||||
Experimental []string `toml:"experimental"`
|
Experimental []string `toml:"experimental"`
|
||||||
InterNetworkModel string `toml:"internetworking_model"`
|
InterNetworkModel string `toml:"internetworking_model"`
|
||||||
EnablePprof bool `toml:"enable_pprof"`
|
EnablePprof bool `toml:"enable_pprof"`
|
||||||
@ -1158,6 +1160,11 @@ func LoadConfiguration(configPath string, ignoreLogging, builtIn bool) (resolved
|
|||||||
config.Experimental = append(config.Experimental, *feature)
|
config.Experimental = append(config.Experimental, *feature)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = validateBindMounts(tomlConf.Runtime.SandboxBindMounts); err != nil {
|
||||||
|
return "", config, err
|
||||||
|
}
|
||||||
|
config.SandboxBindMounts = tomlConf.Runtime.SandboxBindMounts
|
||||||
|
|
||||||
if err := checkConfig(config); err != nil {
|
if err := checkConfig(config); err != nil {
|
||||||
return "", config, err
|
return "", config, err
|
||||||
}
|
}
|
||||||
@ -1165,6 +1172,31 @@ func LoadConfiguration(configPath string, ignoreLogging, builtIn bool) (resolved
|
|||||||
return resolved, config, nil
|
return resolved, config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify that bind mounts exist
|
||||||
|
func validateBindMounts(mounts []string) error {
|
||||||
|
if len(mounts) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bases := make(map[string]struct{})
|
||||||
|
|
||||||
|
for _, m := range mounts {
|
||||||
|
path, err := ResolvePath(m)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("sandbox-bindmounts: Failed to resolve path: %s: %v", m, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
base := filepath.Base(path)
|
||||||
|
// check to make sure the base does not already exists.
|
||||||
|
if _, ok := bases[base]; !ok {
|
||||||
|
bases[base] = struct{}{}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("sandbox-bindmounts: File %s has base that matches already specified bindmount", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func decodeConfig(configPath string) (tomlConfig, string, error) {
|
func decodeConfig(configPath string) (tomlConfig, string, error) {
|
||||||
var (
|
var (
|
||||||
resolved string
|
resolved string
|
||||||
|
@ -1610,3 +1610,52 @@ func TestCheckFactoryConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateBindMounts(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
tmpdir1, err := ioutil.TempDir(testDir, "tmp1-")
|
||||||
|
assert.NoError(err)
|
||||||
|
defer os.RemoveAll(tmpdir1)
|
||||||
|
|
||||||
|
tmpdir2, err := ioutil.TempDir(testDir, "tmp2-")
|
||||||
|
assert.NoError(err)
|
||||||
|
defer os.RemoveAll(tmpdir2)
|
||||||
|
|
||||||
|
duplicate1 := filepath.Join(tmpdir1, "cat.txt")
|
||||||
|
duplicate2 := filepath.Join(tmpdir2, "cat.txt")
|
||||||
|
unique := filepath.Join(tmpdir1, "foobar.txt")
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(duplicate1, []byte("kibble-monster"), 0644)
|
||||||
|
assert.NoError(err)
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(duplicate2, []byte("furbag"), 0644)
|
||||||
|
assert.NoError(err)
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(unique, []byte("fuzzball"), 0644)
|
||||||
|
assert.NoError(err)
|
||||||
|
|
||||||
|
type testData struct {
|
||||||
|
name string
|
||||||
|
mounts []string
|
||||||
|
expectError bool
|
||||||
|
}
|
||||||
|
|
||||||
|
data := []testData{
|
||||||
|
{"two unique directories", []string{tmpdir1, tmpdir2}, false},
|
||||||
|
{"unique directory and two unique files", []string{tmpdir1, duplicate1, unique}, false},
|
||||||
|
{"two files with same base name", []string{duplicate1, duplicate2}, true},
|
||||||
|
{"non existent path", []string{"/this/does/not/exist"}, true},
|
||||||
|
{"non existent path with existing path", []string{unique, "/this/does/not/exist"}, true},
|
||||||
|
{"non existent path with duplicates", []string{duplicate1, duplicate2, "/this/does/not/exist"}, true},
|
||||||
|
{"no paths", []string{}, false},
|
||||||
|
}
|
||||||
|
for i, d := range data {
|
||||||
|
err := validateBindMounts(d.mounts)
|
||||||
|
if d.expectError {
|
||||||
|
assert.Error(err, "test %d (%+v)", i, d.name)
|
||||||
|
} else {
|
||||||
|
assert.NoError(err, "test %d (%+v)", i, d.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -55,6 +55,8 @@ const (
|
|||||||
// path to vfio devices
|
// path to vfio devices
|
||||||
vfioPath = "/dev/vfio/"
|
vfioPath = "/dev/vfio/"
|
||||||
|
|
||||||
|
sandboxMountsDir = "sandbox-mounts"
|
||||||
|
|
||||||
// enable debug console
|
// enable debug console
|
||||||
kernelParamDebugConsole = "agent.debug_console"
|
kernelParamDebugConsole = "agent.debug_console"
|
||||||
kernelParamDebugConsoleVPort = "agent.debug_console_vport"
|
kernelParamDebugConsoleVPort = "agent.debug_console_vport"
|
||||||
@ -379,6 +381,50 @@ func (k *kataAgent) internalConfigure(h hypervisor, id string, config interface{
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupSandboxBindMounts(sandbox *Sandbox) error {
|
||||||
|
if len(sandbox.config.SandboxBindMounts) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create subdirectory in host shared path for sandbox mounts
|
||||||
|
sandboxMountDir := filepath.Join(getMountPath(sandbox.id), sandboxMountsDir)
|
||||||
|
sandboxShareDir := filepath.Join(getSharePath(sandbox.id), sandboxMountsDir)
|
||||||
|
if err := os.MkdirAll(sandboxMountDir, DirMode); err != nil {
|
||||||
|
return fmt.Errorf("Creating sandbox shared mount directory: %v: %w", sandboxMountDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range sandbox.config.SandboxBindMounts {
|
||||||
|
mountDest := filepath.Join(sandboxMountDir, filepath.Base(m))
|
||||||
|
// bind-mount each sandbox mount that's defined into the sandbox mounts dir
|
||||||
|
if err := bindMount(context.Background(), m, mountDest, true, "private"); err != nil {
|
||||||
|
return fmt.Errorf("Mounting sandbox directory: %v to %v: %w", m, mountDest, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mountDest = filepath.Join(sandboxShareDir, filepath.Base(m))
|
||||||
|
if err := remountRo(context.Background(), mountDest); err != nil {
|
||||||
|
return fmt.Errorf("remount sandbox directory: %v to %v: %w", m, mountDest, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanupSandboxBindMounts(sandbox *Sandbox) error {
|
||||||
|
if len(sandbox.config.SandboxBindMounts) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range sandbox.config.SandboxBindMounts {
|
||||||
|
mountPath := filepath.Join(getMountPath(sandbox.id), sandboxMountsDir, filepath.Base(m))
|
||||||
|
if err := syscall.Unmount(mountPath, syscall.MNT_DETACH|UmountNoFollow); err != nil {
|
||||||
|
return fmt.Errorf("Unmounting observe directory: %v: %w", mountPath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (k *kataAgent) configure(h hypervisor, id, sharePath string, config interface{}) error {
|
func (k *kataAgent) configure(h hypervisor, id, sharePath string, config interface{}) error {
|
||||||
err := k.internalConfigure(h, id, config)
|
err := k.internalConfigure(h, id, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -441,6 +487,11 @@ func (k *kataAgent) setupSharedPath(sandbox *Sandbox) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup sandbox bindmounts, if specified:
|
||||||
|
if err := setupSandboxBindMounts(sandbox); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2099,6 +2150,10 @@ func (k *kataAgent) markDead() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (k *kataAgent) cleanup(s *Sandbox) {
|
func (k *kataAgent) cleanup(s *Sandbox) {
|
||||||
|
if err := cleanupSandboxBindMounts(s); err != nil {
|
||||||
|
k.Logger().WithError(err).Errorf("failed to cleanup observability logs bindmount")
|
||||||
|
}
|
||||||
|
|
||||||
// Unmount shared path
|
// Unmount shared path
|
||||||
path := getSharePath(s.id)
|
path := getSharePath(s.id)
|
||||||
k.Logger().WithField("path", path).Infof("cleanup agent")
|
k.Logger().WithField("path", path).Infof("cleanup agent")
|
||||||
|
@ -122,6 +122,9 @@ type RuntimeConfig struct {
|
|||||||
//Determines kata processes are managed only in sandbox cgroup
|
//Determines kata processes are managed only in sandbox cgroup
|
||||||
SandboxCgroupOnly bool
|
SandboxCgroupOnly bool
|
||||||
|
|
||||||
|
//Paths to be bindmounted RO into the guest.
|
||||||
|
SandboxBindMounts []string
|
||||||
|
|
||||||
//Experimental features enabled
|
//Experimental features enabled
|
||||||
Experimental []exp.Feature
|
Experimental []exp.Feature
|
||||||
|
|
||||||
@ -964,6 +967,7 @@ func SandboxConfig(ocispec specs.Spec, runtime RuntimeConfig, bundlePath, cid, c
|
|||||||
SystemdCgroup: systemdCgroup,
|
SystemdCgroup: systemdCgroup,
|
||||||
|
|
||||||
SandboxCgroupOnly: runtime.SandboxCgroupOnly,
|
SandboxCgroupOnly: runtime.SandboxCgroupOnly,
|
||||||
|
SandboxBindMounts: runtime.SandboxBindMounts,
|
||||||
|
|
||||||
DisableGuestSeccomp: runtime.DisableGuestSeccomp,
|
DisableGuestSeccomp: runtime.DisableGuestSeccomp,
|
||||||
|
|
||||||
|
@ -116,6 +116,9 @@ type SandboxConfig struct {
|
|||||||
|
|
||||||
DisableGuestSeccomp bool
|
DisableGuestSeccomp bool
|
||||||
|
|
||||||
|
// SandboxBindMounts - list of paths to mount into guest
|
||||||
|
SandboxBindMounts []string
|
||||||
|
|
||||||
// Experimental features enabled
|
// Experimental features enabled
|
||||||
Experimental []exp.Feature
|
Experimental []exp.Feature
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user