From 95eb4907805b0c8650cc1bce01844162c2c84c4a Mon Sep 17 00:00:00 2001 From: Eric Mountain Date: Fri, 27 Nov 2020 15:38:26 +0100 Subject: [PATCH] Use v2 capabilities in layer archives When building images in a user-namespaced container, v3 capabilities are stored including the root UID of the creator of the user-namespace. This UID does not make sense outside the build environment however. If the image is run in a non-user-namespaced runtime, or if a user-namespaced runtime uses a different UID, the capabilities requested by the effective bit will not be honoured by `execve(2)` due to this mismatch. Instead, we convert v3 capabilities to v2, dropping the root UID on the fly. Signed-off-by: Eric Mountain --- integration/build/build_userns_linux_test.go | 11 +---------- pkg/archive/archive.go | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/integration/build/build_userns_linux_test.go b/integration/build/build_userns_linux_test.go index 5e0c4d40fb..b1ee8a90b4 100644 --- a/integration/build/build_userns_linux_test.go +++ b/integration/build/build_userns_linux_test.go @@ -126,15 +126,6 @@ func TestBuildUserNamespaceValidateCapabilitiesAreV2(t *testing.T) { _, err = stdcopy.StdCopy(actualStdout, actualStderr, logReader) assert.NilError(t, err) if strings.TrimSpace(actualStdout.String()) != "/bin/sleep cap_net_bind_service=eip" { - // Activate when fix is merged: https://github.com/moby/moby/pull/41724 - //t.Fatalf("run produced invalid output: %q, expected %q", actualStdout.String(), "/bin/sleep cap_net_bind_service=eip") - // t.Logf("run produced invalid output (expected until #41724 merges): %q, expected %q", - // actualStdout.String(), - // "/bin/sleep cap_net_bind_service=eip") - } else { - // Shouldn't happen until fix is merged: https://github.com/moby/moby/pull/41724 - t.Fatalf("run produced valid output (unexpected until #41724 merges): %q, expected %q", - actualStdout.String(), - "/bin/sleep cap_net_bind_service=eip") + t.Fatalf("run produced invalid output: %q, expected %q", actualStdout.String(), "/bin/sleep cap_net_bind_service=eip") } } diff --git a/pkg/archive/archive.go b/pkg/archive/archive.go index eeed674729..92253aea60 100644 --- a/pkg/archive/archive.go +++ b/pkg/archive/archive.go @@ -402,10 +402,24 @@ func fillGo18FileTypeBits(mode int64, fi os.FileInfo) int64 { // ReadSecurityXattrToTarHeader reads security.capability xattr from filesystem // to a tar header func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error { + const ( + // Values based on linux/include/uapi/linux/capability.h + xattrCapsSz2 = 20 + versionOffset = 3 + vfsCapRevision2 = 2 + vfsCapRevision3 = 3 + ) capability, _ := system.Lgetxattr(path, "security.capability") if capability != nil { + length := len(capability) + if capability[versionOffset] == vfsCapRevision3 { + // Convert VFS_CAP_REVISION_3 to VFS_CAP_REVISION_2 as root UID makes no + // sense outside the user namespace the archive is built in. + capability[versionOffset] = vfsCapRevision2 + length = xattrCapsSz2 + } hdr.Xattrs = make(map[string]string) - hdr.Xattrs["security.capability"] = string(capability) + hdr.Xattrs["security.capability"] = string(capability[:length]) } return nil }