From 223c67106086266bc399b872fdedb4b7e06357a8 Mon Sep 17 00:00:00 2001 From: razzle Date: Thu, 28 Dec 2023 21:31:28 -0500 Subject: [PATCH] repro tarballs for components + sboms Signed-off-by: razzle --- src/pkg/layout/component.go | 2 +- src/pkg/layout/sbom.go | 7 +--- src/pkg/utils/io.go | 70 +++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 7 deletions(-) diff --git a/src/pkg/layout/component.go b/src/pkg/layout/component.go index 2309d8200c..8eef88efd8 100644 --- a/src/pkg/layout/component.go +++ b/src/pkg/layout/component.go @@ -65,7 +65,7 @@ func (c *Components) Archive(component types.ZarfComponent, cleanupTemp bool) (e if size > 0 { tb := fmt.Sprintf("%s.tar", base) message.Debugf("Archiving %q", name) - if err := archiver.Archive([]string{base}, tb); err != nil { + if err := utils.CreateReproducibleTarballFromDir(base, tb); err != nil { return err } if c.Tarballs == nil { diff --git a/src/pkg/layout/sbom.go b/src/pkg/layout/sbom.go index 8920331a52..782e38ed0f 100644 --- a/src/pkg/layout/sbom.go +++ b/src/pkg/layout/sbom.go @@ -60,14 +60,9 @@ func (s *SBOMs) Archive() (err error) { dir := s.Path tb := filepath.Join(filepath.Dir(dir), SBOMTar) - allSBOMFiles, err := filepath.Glob(filepath.Join(dir, "*")) - if err != nil { + if err := utils.CreateReproducibleTarballFromDir(dir, tb); err != nil { return err } - - if err = archiver.Archive(allSBOMFiles, tb); err != nil { - return - } s.Path = tb return os.RemoveAll(dir) } diff --git a/src/pkg/utils/io.go b/src/pkg/utils/io.go index 436e8b0155..40d6bb7712 100755 --- a/src/pkg/utils/io.go +++ b/src/pkg/utils/io.go @@ -5,6 +5,7 @@ package utils import ( + "archive/tar" "bufio" "crypto/sha256" "fmt" @@ -16,6 +17,7 @@ import ( "path/filepath" "regexp" "strings" + "time" "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/pkg/message" @@ -422,3 +424,71 @@ func SHAsMatch(path, expected string) error { } return nil } + +// CreateReproducibleTarballFromDir creates a tarball from a directory with stripped headers +func CreateReproducibleTarballFromDir(dirPath, tarballPath string) error { + // Create a new tarball for the output + outFile, err := os.Create(tarballPath) + if err != nil { + return fmt.Errorf("error creating tarball: %w", err) + } + defer outFile.Close() + + tarWriter := tar.NewWriter(outFile) + defer tarWriter.Close() + + // Walk through the directory and process each file + err = filepath.Walk(dirPath, func(filePath string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + // Create a new header + header, err := tar.FileInfoHeader(info, "") + if err != nil { + return fmt.Errorf("error creating tar header: %w", err) + } + + // Strip non-deterministic header data + header.ModTime = time.Time{} + header.AccessTime = time.Time{} + header.ChangeTime = time.Time{} + header.Uid = 0 + header.Gid = 0 + header.Uname = "" + header.Gname = "" + + // Ensure the header's name is correctly set relative to the base directory + relPath, err := filepath.Rel(dirPath, filePath) + if err != nil { + return fmt.Errorf("error getting relative path: %w", err) + } + header.Name = relPath + + // Write the header to the tarball + if err := tarWriter.WriteHeader(header); err != nil { + return fmt.Errorf("error writing header: %w", err) + } + + // If it's a file, write its content + if !info.IsDir() { + file, err := os.Open(filePath) + if err != nil { + return fmt.Errorf("error opening file: %w", err) + } + defer file.Close() + + if _, err := io.Copy(tarWriter, file); err != nil { + return fmt.Errorf("error writing file to tarball: %w", err) + } + } + + return nil + }) + + if err != nil { + return fmt.Errorf("error walking through directory: %w", err) + } + + return nil +}