From d26587c725d1e27aa973e411079c433ac1445956 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Tue, 7 Mar 2023 21:24:00 +0900 Subject: [PATCH] archive: disable looking up usernames and groupnames on the host These strings were resolved using the user information on the host filesystem and were decreasing reproducibility. For moby/buildkit issue 3688 Signed-off-by: Akihiro Suda --- .golangci.yml | 3 ++ archive/tar.go | 4 +- archive/tarheader/tarheader.go | 82 +++++++++++++++++++++++++++++ archive/tarheader/tarheader_unix.go | 59 +++++++++++++++++++++ 4 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 archive/tarheader/tarheader.go create mode 100644 archive/tarheader/tarheader_unix.go diff --git a/.golangci.yml b/.golangci.yml index e835ba176..e52c4ffb9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -29,6 +29,9 @@ issues: text: "blank-imports:" - path: 'contrib[\\/]fuzz[\\/]' text: "exported: func name will be used as fuzz.Fuzz" + - path: 'archive[\\/]tarheader[\\/]' + # conversion is necessary on Linux, unnecessary on macOS + text: "unnecessary conversion" linters-settings: gosec: diff --git a/archive/tar.go b/archive/tar.go index 32edd3b3d..28b623dcf 100644 --- a/archive/tar.go +++ b/archive/tar.go @@ -29,6 +29,7 @@ import ( "syscall" "time" + "github.com/containerd/containerd/archive/tarheader" "github.com/containerd/containerd/log" "github.com/containerd/containerd/pkg/epoch" "github.com/containerd/containerd/pkg/userns" @@ -586,7 +587,8 @@ func (cw *ChangeWriter) HandleChange(k fs.ChangeKind, p string, f os.FileInfo, e } } - hdr, err := tar.FileInfoHeader(f, link) + // Use FileInfoHeaderNoLookups to avoid propagating user names and group names from the host + hdr, err := tarheader.FileInfoHeaderNoLookups(f, link) if err != nil { return err } diff --git a/archive/tarheader/tarheader.go b/archive/tarheader/tarheader.go new file mode 100644 index 000000000..2f93842c1 --- /dev/null +++ b/archive/tarheader/tarheader.go @@ -0,0 +1,82 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/* + Portions from https://github.com/moby/moby/blob/v23.0.1/pkg/archive/archive.go#L419-L464 + Copyright (C) Docker/Moby authors. + Licensed under the Apache License, Version 2.0 + NOTICE: https://github.com/moby/moby/blob/v23.0.1/NOTICE +*/ + +package tarheader + +import ( + "archive/tar" + "os" +) + +// nosysFileInfo hides the system-dependent info of the wrapped FileInfo to +// prevent tar.FileInfoHeader from introspecting it and potentially calling into +// glibc. +// +// From https://github.com/moby/moby/blob/v23.0.1/pkg/archive/archive.go#L419-L434 . +type nosysFileInfo struct { + os.FileInfo +} + +func (fi nosysFileInfo) Sys() interface{} { + // A Sys value of type *tar.Header is safe as it is system-independent. + // The tar.FileInfoHeader function copies the fields into the returned + // header without performing any OS lookups. + if sys, ok := fi.FileInfo.Sys().(*tar.Header); ok { + return sys + } + return nil +} + +// sysStat, if non-nil, populates hdr from system-dependent fields of fi. +// +// From https://github.com/moby/moby/blob/v23.0.1/pkg/archive/archive.go#L436-L437 . +var sysStat func(fi os.FileInfo, hdr *tar.Header) error + +// FileInfoHeaderNoLookups creates a partially-populated tar.Header from fi. +// +// Compared to the archive/tar.FileInfoHeader function, this function is safe to +// call from a chrooted process as it does not populate fields which would +// require operating system lookups. It behaves identically to +// tar.FileInfoHeader when fi is a FileInfo value returned from +// tar.Header.FileInfo(). +// +// When fi is a FileInfo for a native file, such as returned from os.Stat() and +// os.Lstat(), the returned Header value differs from one returned from +// tar.FileInfoHeader in the following ways. The Uname and Gname fields are not +// set as OS lookups would be required to populate them. The AccessTime and +// ChangeTime fields are not currently set (not yet implemented) although that +// is subject to change. Callers which require the AccessTime or ChangeTime +// fields to be zeroed should explicitly zero them out in the returned Header +// value to avoid any compatibility issues in the future. +// +// From https://github.com/moby/moby/blob/v23.0.1/pkg/archive/archive.go#L439-L464 . +func FileInfoHeaderNoLookups(fi os.FileInfo, link string) (*tar.Header, error) { + hdr, err := tar.FileInfoHeader(nosysFileInfo{fi}, link) + if err != nil { + return nil, err + } + if sysStat != nil { + return hdr, sysStat(fi, hdr) + } + return hdr, nil +} diff --git a/archive/tarheader/tarheader_unix.go b/archive/tarheader/tarheader_unix.go new file mode 100644 index 000000000..98ad8f945 --- /dev/null +++ b/archive/tarheader/tarheader_unix.go @@ -0,0 +1,59 @@ +//go:build !windows + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/* + Portions from https://github.com/moby/moby/blob/v23.0.1/pkg/archive/archive_unix.go#L52-L70 + Copyright (C) Docker/Moby authors. + Licensed under the Apache License, Version 2.0 + NOTICE: https://github.com/moby/moby/blob/v23.0.1/NOTICE +*/ + +package tarheader + +import ( + "archive/tar" + "os" + "syscall" + + "golang.org/x/sys/unix" +) + +func init() { + sysStat = statUnix +} + +// statUnix populates hdr from system-dependent fields of fi without performing +// any OS lookups. +// From https://github.com/moby/moby/blob/v23.0.1/pkg/archive/archive_unix.go#L52-L70 +func statUnix(fi os.FileInfo, hdr *tar.Header) error { + s, ok := fi.Sys().(*syscall.Stat_t) + if !ok { + return nil + } + + hdr.Uid = int(s.Uid) + hdr.Gid = int(s.Gid) + + if s.Mode&unix.S_IFBLK != 0 || + s.Mode&unix.S_IFCHR != 0 { + hdr.Devmajor = int64(unix.Major(uint64(s.Rdev))) + hdr.Devminor = int64(unix.Minor(uint64(s.Rdev))) + } + + return nil +}