Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release-1.27] server: allow Bidirectional mounts that contain storage root #7457

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 33 additions & 2 deletions server/container_create_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@
cgroup2RW := node.CgroupIsV2() && sb.Annotations()[crioann.Cgroup2RWAnnotation] == "true"

s.resourceStore.SetStageForResource(ctx, ctr.Name(), "container volume configuration")
containerVolumes, ociMounts, err := addOCIBindMounts(ctx, ctr, mountLabel, s.config.RuntimeConfig.BindMountPrefix, s.config.AbsentMountSourcesToReject, maybeRelabel, skipRelabel, cgroup2RW)
containerVolumes, ociMounts, err := addOCIBindMounts(ctx, ctr, mountLabel, s.config.RuntimeConfig.BindMountPrefix, s.config.AbsentMountSourcesToReject, maybeRelabel, skipRelabel, cgroup2RW, s.Config().Root)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -923,7 +923,7 @@
m.Options = append(m.Options, "rw")
}

func addOCIBindMounts(ctx context.Context, ctr ctrfactory.Container, mountLabel, bindMountPrefix string, absentMountSourcesToReject []string, maybeRelabel, skipRelabel, cgroup2RW bool) ([]oci.ContainerVolume, []rspec.Mount, error) {
func addOCIBindMounts(ctx context.Context, ctr ctrfactory.Container, mountLabel, bindMountPrefix string, absentMountSourcesToReject []string, maybeRelabel, skipRelabel, cgroup2RW bool, storageRoot string) ([]oci.ContainerVolume, []rspec.Mount, error) {
ctx, span := log.StartSpan(ctx)
defer span.End()

Expand Down Expand Up @@ -978,6 +978,12 @@
if m.HostPath == "/" && dest == "/" {
log.Warnf(ctx, "Configuration specifies mounting host root to the container root. This is dangerous (especially with privileged containers) and should be avoided.")
}

if isSubDirectoryOf(storageRoot, m.HostPath) && m.Propagation == types.MountPropagation_PROPAGATION_PRIVATE {
log.Infof(ctx, "Mount propogration for the host path %s will be set to HostToContainer as it includes the container storage root", m.HostPath)
m.Propagation = types.MountPropagation_PROPAGATION_HOST_TO_CONTAINER

Check warning on line 984 in server/container_create_linux.go

View check run for this annotation

Codecov / codecov/patch

server/container_create_linux.go#L983-L984

Added lines #L983 - L984 were not covered by tests
}

src := filepath.Join(bindMountPrefix, m.HostPath)

resolvedSrc, err := resolveSymbolicLink(bindMountPrefix, src)
Expand Down Expand Up @@ -1201,3 +1207,28 @@
Apparmor: &types.SecurityProfile{},
}
}

// isSubDirectoryOf checks if the base path contains the target path.
// It assumes that paths are Unix-style with forward slashes ("/").
// It ensures that both paths end with a "/" before comparing, so that "/var/lib" will not incorrectly match "/var/libs".

// The function returns true if the base path starts with the target path, providing a way to check if one directory is a subdirectory of another.

// Examples:

// isSubDirectoryOf("/var/lib/containers/storage", "/") returns true
// isSubDirectoryOf("/var/lib/containers/storage", "/var/lib") returns true
// isSubDirectoryOf("/var/lib/containers/storage", "/var/lib/containers") returns true
// isSubDirectoryOf("/var/lib/containers/storage", "/var/lib/containers/storage") returns true
// isSubDirectoryOf("/var/lib/containers/storage", "/var/lib/containers/storage/extra") returns false
// isSubDirectoryOf("/var/lib/containers/storage", "/va") returns false
// isSubDirectoryOf("/var/lib/containers/storage", "/var/tmp/containers") returns false
func isSubDirectoryOf(base, target string) bool {
if !strings.HasSuffix(target, "/") {
target += "/"
}
if !strings.HasSuffix(base, "/") {
base += "/"
}
return strings.HasPrefix(base, target)
}
33 changes: 29 additions & 4 deletions server/container_create_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func TestAddOCIBindsForDev(t *testing.T) {
t.Error(err)
}

_, binds, err := addOCIBindMounts(context.Background(), ctr, "", "", nil, false, false, false)
_, binds, err := addOCIBindMounts(context.Background(), ctr, "", "", nil, false, false, false, "")
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -75,7 +75,7 @@ func TestAddOCIBindsForSys(t *testing.T) {
t.Error(err)
}

_, binds, err := addOCIBindMounts(context.Background(), ctr, "", "", nil, false, false, false)
_, binds, err := addOCIBindMounts(context.Background(), ctr, "", "", nil, false, false, false, "")
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -107,7 +107,7 @@ func TestAddOCIBindsCGroupRW(t *testing.T) {
}); err != nil {
t.Error(err)
}
_, _, err = addOCIBindMounts(context.Background(), ctr, "", "", nil, false, false, true)
_, _, err = addOCIBindMounts(context.Background(), ctr, "", "", nil, false, false, true, "")
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -141,7 +141,7 @@ func TestAddOCIBindsCGroupRW(t *testing.T) {
t.Error(err)
}
var hasCgroupRO bool
_, _, err = addOCIBindMounts(context.Background(), ctr, "", "", nil, false, false, false)
_, _, err = addOCIBindMounts(context.Background(), ctr, "", "", nil, false, false, false, "")
if err != nil {
t.Error(err)
}
Expand All @@ -158,3 +158,28 @@ func TestAddOCIBindsCGroupRW(t *testing.T) {
t.Error("Cgroup mount not added with RO.")
}
}

func TestIsSubDirectoryOf(t *testing.T) {
tests := []struct {
base, target string
want bool
}{
{"/var/lib/containers/storage", "/", true},
{"/var/lib/containers/storage", "/var/lib", true},
{"/var/lib/containers/storage", "/var/lib/containers", true},
{"/var/lib/containers/storage", "/var/lib/containers/storage", true},
{"/var/lib/containers/storage", "/var/lib/containers/storage/extra", false},
{"/var/lib/containers/storage", "/va", false},
{"/var/lib/containers/storage", "/var/tmp/containers", false},
}

for _, tt := range tests {
testname := tt.base + " " + tt.target
t.Run(testname, func(t *testing.T) {
ans := isSubDirectoryOf(tt.base, tt.target)
if ans != tt.want {
t.Errorf("got %v, want %v", ans, tt.want)
}
})
}
}
36 changes: 36 additions & 0 deletions test/ctr.bats
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,42 @@ function check_oci_annotation() {
! crictl create "$pod_id" "$TESTDIR/config" "$TESTDATA"/sandbox_config.json
}

@test "ctr that mounts container storage as shared should keep shared" {
# parent of `--root`, keep in sync with test/helpers.bash
PARENT_DIR="$TESTDIR"
CTR_DIR="/host"
jq --arg path "$PARENT_DIR" --arg ctr_dir "$CTR_DIR" \
' .mounts = [ {
host_path: $path,
container_path: $ctr_dir,
propagation: 2
} ]' \
"$TESTDATA"/container_redis.json > "$TESTDIR/config"

start_crio

ctr_id=$(crictl run "$TESTDIR/config" "$TESTDATA"/sandbox_config.json)
crictl exec --sync "$ctr_id" findmnt -no TARGET,PROPAGATION "$CTR_DIR" | grep shared
}

@test "ctr that mounts container storage as private should not be private" {
# parent of `--root`, keep in sync with test/helpers.bash
PARENT_DIR="$TESTDIR"
CTR_DIR="/host"
jq --arg path "$PARENT_DIR" --arg ctr_dir "$CTR_DIR" \
' .mounts = [ {
host_path: $path,
container_path: $ctr_dir,
propagation: 1
} ]' \
"$TESTDATA"/container_redis.json > "$TESTDIR/config"

start_crio

ctr_id=$(crictl run "$TESTDIR/config" "$TESTDATA"/sandbox_config.json)
crictl exec --sync "$ctr_id" findmnt -no TARGET,PROPAGATION "$CTR_DIR" | grep -v private
}

@test "ctr has containerenv" {
start_crio
pod_id=$(crictl runp "$TESTDATA"/sandbox_config.json)
Expand Down