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 <eric.mountain@datadoghq.com>
(cherry picked from commit 95eb490780)
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
This commit is contained in:
Eric Mountain 2020-11-27 15:38:26 +01:00 committed by Akihiro Suda
parent 614a70a11b
commit 2a0c446866
No known key found for this signature in database
GPG Key ID: 49524C6F9F638F1A
2 changed files with 16 additions and 11 deletions

View File

@ -118,15 +118,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")
}
}

View File

@ -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
}