diff --git a/drivers/overlay/composefs_notsupported.go b/drivers/overlay/composefs_notsupported.go index 273ef4699d..75eae0affb 100644 --- a/drivers/overlay/composefs_notsupported.go +++ b/drivers/overlay/composefs_notsupported.go @@ -18,3 +18,7 @@ func generateComposeFsBlob(toc []byte, destFile string) error { func mountErofsBlob(blobFile, mountPoint string) error { return fmt.Errorf("composefs is not supported") } + +func enableVerityRecursive(path string) error { + return fmt.Errorf("composefs is not supported") +} diff --git a/drivers/overlay/composefs_supported.go b/drivers/overlay/composefs_supported.go index 7ecde856d1..c36c43b69b 100644 --- a/drivers/overlay/composefs_supported.go +++ b/drivers/overlay/composefs_supported.go @@ -5,12 +5,18 @@ package overlay import ( "bytes" + "errors" "fmt" + "io/fs" "os" "os/exec" + "path/filepath" "sync" + "syscall" + "unsafe" "github.com/containers/storage/pkg/loopback" + "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -32,6 +38,43 @@ func composeFsSupported() bool { return err == nil } +func enableVerity(description string, fd int) error { + enableArg := unix.FsverityEnableArg{ + Version: 1, + Hash_algorithm: unix.FS_VERITY_HASH_ALG_SHA256, + Block_size: 4096, + } + + _, _, e1 := syscall.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.FS_IOC_ENABLE_VERITY), uintptr(unsafe.Pointer(&enableArg))) + if e1 != 0 && !errors.Is(e1, unix.EEXIST) { + return fmt.Errorf("failed to enable verity for %q: %w", description, e1) + } + return nil +} + +func enableVerityRecursive(path string) error { + walkFn := func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if !d.Type().IsRegular() { + return nil + } + + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + + if err := enableVerity(path, int(f.Fd())); err != nil { + return err + } + return nil + } + return filepath.WalkDir(path, walkFn) +} + func generateComposeFsBlob(toc []byte, destFile string) error { writerJson, err := getComposeFsHelper() if err != nil { @@ -44,14 +87,35 @@ func generateComposeFsBlob(toc []byte, destFile string) error { } outFd := os.NewFile(uintptr(fd), "outFd") - defer outFd.Close() - cmd := exec.Command(writerJson, "--format=erofs", "--out=/proc/self/fd/3", "/proc/self/fd/0") - cmd.ExtraFiles = []*os.File{outFd} - cmd.Stdin = bytes.NewReader(toc) - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - return fmt.Errorf("failed to convert json to erofs: %w", err) + fd, err = unix.Open(fmt.Sprintf("/proc/self/fd/%d", outFd.Fd()), unix.O_RDONLY|unix.O_CLOEXEC, 0) + if err != nil { + outFd.Close() + return fmt.Errorf("failed to dup output file: %w", err) + } + newFd := os.NewFile(uintptr(fd), "newFd") + defer newFd.Close() + + err = func() error { + // a scope to close outFd before setting fsverity on the read-only fd. + defer outFd.Close() + + cmd := exec.Command(writerJson, "--format=erofs", "--out=/proc/self/fd/3", "/proc/self/fd/0") + cmd.ExtraFiles = []*os.File{outFd} + cmd.Stderr = os.Stderr + cmd.Stdin = bytes.NewReader(toc) + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to convert json to erofs: %w", err) + } + return nil + }() + if err != nil { + return err } + + if err := enableVerity("manifest file", int(newFd.Fd())); err != nil && !errors.Is(err, unix.ENOTSUP) && !errors.Is(err, unix.ENOTTY) { + logrus.Warningf("%s", err) + } + return nil } diff --git a/drivers/overlay/overlay.go b/drivers/overlay/overlay.go index a4e6b7d3ca..3170f09645 100644 --- a/drivers/overlay/overlay.go +++ b/drivers/overlay/overlay.go @@ -2064,6 +2064,11 @@ func (d *Driver) ApplyDiffFromStagingDirectory(id, parent, stagingDirectory stri } if d.useComposeFs() { + // FIXME: move this logic into the differ so we don't have to open + // the file twice. + if err := enableVerityRecursive(stagingDirectory); err != nil && !errors.Is(err, unix.ENOTSUP) && !errors.Is(err, unix.ENOTTY) { + logrus.Warningf("%s", err) + } toc := diffOutput.BigData[zstdChunkedManifest] if err := generateComposeFsBlob(toc, d.getErofsBlob(id)); err != nil { return err