From 31f87204fad51e09c317dc5508ecea0f6e329fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miloslav=20Trma=C4=8D?= Date: Tue, 28 Nov 2023 01:36:30 +0100 Subject: [PATCH] Don't expose local account details in oci-archive tar files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Miloslav Trmač --- oci/archive/oci_dest.go | 7 +++++- oci/archive/oci_dest_test.go | 42 +++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/oci/archive/oci_dest.go b/oci/archive/oci_dest.go index 1fb0f49e27..6ca618e351 100644 --- a/oci/archive/oci_dest.go +++ b/oci/archive/oci_dest.go @@ -13,6 +13,7 @@ import ( "github.com/containers/image/v5/internal/signature" "github.com/containers/image/v5/types" "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/idtools" digest "github.com/opencontainers/go-digest" "github.com/sirupsen/logrus" ) @@ -169,7 +170,11 @@ func (d *ociArchiveImageDestination) Commit(ctx context.Context, unparsedTopleve // tar converts the directory at src and saves it to dst func tarDirectory(src, dst string) error { // input is a stream of bytes from the archive of the directory at path - input, err := archive.Tar(src, archive.Uncompressed) + input, err := archive.TarWithOptions(src, &archive.TarOptions{ + Compression: archive.Uncompressed, + // Don’t include the data about the user account this code is running under. + ChownOpts: &idtools.IDPair{UID: 0, GID: 0}, + }) if err != nil { return fmt.Errorf("retrieving stream of bytes from %q: %w", src, err) } diff --git a/oci/archive/oci_dest_test.go b/oci/archive/oci_dest_test.go index a67112cf30..01ff1118e8 100644 --- a/oci/archive/oci_dest_test.go +++ b/oci/archive/oci_dest_test.go @@ -1,5 +1,45 @@ package archive -import "github.com/containers/image/v5/internal/private" +import ( + "archive/tar" + "io" + "os" + "path/filepath" + "testing" + + "github.com/containers/image/v5/internal/private" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) var _ private.ImageDestination = (*ociArchiveImageDestination)(nil) + +func TestTarDirectory(t *testing.T) { + srcDir := t.TempDir() + err := os.WriteFile(filepath.Join(srcDir, "regular"), []byte("contents"), 0o600) + require.NoError(t, err) + + dest := filepath.Join(t.TempDir(), "file.tar") + err = tarDirectory(srcDir, dest) + require.NoError(t, err) + + f, err := os.Open(dest) + require.NoError(t, err) + defer f.Close() + reader := tar.NewReader(f) + numItems := 0 + for { + hdr, err := reader.Next() + if err == io.EOF { + break + } + require.NoError(t, err) + // Test that the header does not expose data about the local account + assert.Equal(t, 0, hdr.Uid) + assert.Equal(t, 0, hdr.Gid) + assert.Equal(t, "", hdr.Uname) + assert.Equal(t, "", hdr.Gname) + numItems++ + } + assert.Equal(t, 1, numItems) +}