Skip to content

Commit

Permalink
Modify fsverity IsSupported function for Linux
Browse files Browse the repository at this point in the history
Check that the content store root is able to store fsverity files in
addition to checking the Kernel version.

Signed-off-by: James Jenkins <James.Jenkins@ibm.com>
  • Loading branch information
Jenkins-J committed Mar 27, 2024
1 parent 244f093 commit e77afe7
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 54 deletions.
94 changes: 46 additions & 48 deletions pkg/fsverity/fsverity_linux.go
Expand Up @@ -21,30 +21,25 @@ package fsverity
import (
"fmt"
"os"
"path/filepath"
"sync"
"syscall"
"unsafe"

"github.com/containerd/containerd/v2/contrib/seccomp/kernelversion"
"github.com/containerd/containerd/v2/pkg/kernelversion"
"golang.org/x/sys/unix"
)

type fsverityEnableArg struct {
version uint32
hash_algorithm uint32
block_size uint32
salt_size uint32
salt_ptr uint64
sig_size uint32
reserved1 uint32
sig_ptr uint64
reserved2 [11]uint64
}

type fsverityDigest struct {
digest_algorithm uint16
digest_size uint16
digest [64]uint8
version uint32
hashAlgorithm uint32
blockSize uint32
saltSize uint32
saltPtr uint64
sigSize uint32
reserved1 uint32
sigPtr uint64
reserved2 [11]uint64
}

const (
Expand All @@ -53,33 +48,57 @@ const (
)

var (
once sync.Once
once sync.Once
supported bool
)

func IsSupported() bool {
once.Do(func () {
func IsSupported(rootPath string) bool {
once.Do(func() {
minKernelVersion := kernelversion.KernelVersion{Kernel: 5, Major: 4}
s, err := kernelversion.GreaterEqualThan(minKernelVersion)
if err != nil {
supported = s
return
}

integrityDir := filepath.Join(rootPath, "integrity")
if err = os.MkdirAll(integrityDir, 0755); err != nil {
supported = false
return
}

digestPath := filepath.Join(integrityDir, "supported")
digestFile, err := os.Create(digestPath)
if err != nil {
supported = false
return
}

digestFile.Close()
defer os.RemoveAll(integrityDir)

eerr := Enable(digestPath)
if eerr != nil {
supported = false
return
}
supported = s

supported = true
})
return supported
}

func IsEnabled(path string) (bool, error) {
f, err := os.Open(path)
if err != nil {
return false, fmt.Errorf("Error opening file: %s", err)
return false, fmt.Errorf("error opening file: %s", err)
}

var attr int

_, _, flagErr := unix.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(unix.FS_IOC_GETFLAGS), uintptr(unsafe.Pointer(&attr)))
if flagErr != 0 {
return false, fmt.Errorf("Error getting inode flags: %s", flagErr)
return false, fmt.Errorf("error getting inode flags: %s", flagErr)
}

if attr&unix.FS_VERITY_FL == unix.FS_VERITY_FL {
Expand All @@ -92,12 +111,12 @@ func IsEnabled(path string) (bool, error) {
func Enable(path string) error {
f, err := os.Open(path)
if err != nil {
return fmt.Errorf("Error opening file: %s\n", err.Error())
return fmt.Errorf("error opening file: %s", err.Error())
}

var args *fsverityEnableArg = &fsverityEnableArg{}
var args = &fsverityEnableArg{}
args.version = 1
args.hash_algorithm = 1
args.hashAlgorithm = 1

// fsverity block size should be the minimum between the page size
// and the file system block size
Expand All @@ -114,33 +133,12 @@ func Enable(path string) error {
blockSize = defaultBlockSize
}

args.block_size = uint32(blockSize)
args.blockSize = uint32(blockSize)

_, _, errno := unix.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(unix.FS_IOC_ENABLE_VERITY), uintptr(unsafe.Pointer(args)))
if errno != 0 {
return fmt.Errorf("Enable fsverity failed: %d\n", errno)
return fmt.Errorf("enable fsverity failed: %d", errno)
}

return nil
}

func Measure(path string) (string, error) {
var verityDigest string
f, err := os.Open(path)
if err != nil {
return verityDigest, fmt.Errorf("Error opening file: %s\n", err.Error())
}

var d *fsverityDigest = &fsverityDigest{digest_size: maxDigestSize}
_, _, errno := unix.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(unix.FS_IOC_MEASURE_VERITY), uintptr(unsafe.Pointer(d)))
if errno != 0 {
return verityDigest, fmt.Errorf("Measure fsverity failed: %d\n", errno)
}

var i uint16
for i = 0; i < (*d).digest_size; i++ {
verityDigest = fmt.Sprintf("%s%x", verityDigest, (*d).digest[i])
}

return verityDigest, nil
}
8 changes: 7 additions & 1 deletion pkg/fsverity/fsverity_other.go
Expand Up @@ -2,6 +2,12 @@

package fsverity

func IsSupported() bool {
import "fmt"

func IsSupported(rootPath string) bool {
return false
}

func Enable(_ string) error {
return fmt.Errorf("fsverity is only supported on Linux systems")
}
6 changes: 2 additions & 4 deletions plugins/content/local/store.go
Expand Up @@ -27,8 +27,8 @@ import (
"sync"
"time"

"github.com/containerd/containerd/v2/content"
"github.com/containerd/containerd/v2/filters"
"github.com/containerd/containerd/v2/core/content"
"github.com/containerd/containerd/v2/pkg/filters"
"github.com/containerd/errdefs"
"github.com/containerd/log"

Expand Down Expand Up @@ -128,8 +128,6 @@ func (s *store) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.
return nil, fmt.Errorf("calculating blob path for ReaderAt: %w", err)
}

log.G(ctx).Debugf("Getting reader for blob %v", p)

reader, err := OpenReader(p)
if err != nil {
return nil, fmt.Errorf("blob %s expected at %s: %w", desc.Digest, p, err)
Expand Down
12 changes: 11 additions & 1 deletion plugins/content/local/writer.go
Expand Up @@ -27,6 +27,7 @@ import (
"time"

"github.com/containerd/containerd/v2/core/content"
"github.com/containerd/containerd/v2/pkg/fsverity"
"github.com/containerd/errdefs"
"github.com/containerd/log"
"github.com/opencontainers/go-digest"
Expand Down Expand Up @@ -137,6 +138,16 @@ func (w *writer) Commit(ctx context.Context, size int64, expected digest.Digest,
return err
}

// Enable content blob integrity verification if supported

if integritySupported := fsverity.IsSupported(w.s.root); integritySupported {
if err := fsverity.Enable(target); err != nil {
log.G(ctx).Warnf("failed to enable integrity of blob %v: %s", target, err.Error())
}
} else {
log.G(ctx).Warnf("fsverity integrity verification is not supported")
}

// Ingest has now been made available in the content store, attempt to complete
// setting metadata but errors should only be logged and not returned since
// the content store cannot be cleanly rolled back.
Expand All @@ -151,7 +162,6 @@ func (w *writer) Commit(ctx context.Context, size int64, expected digest.Digest,
log.G(ctx).WithField("ref", w.ref).WithField("path", w.path).Error("failed to remove ingest directory")
}

log.G(ctx).Debugf("content labels: %v", base.Labels)
if w.s.ls != nil && base.Labels != nil {
if err := w.s.ls.Set(dgst, base.Labels); err != nil {
log.G(ctx).WithField("digest", dgst).Error("failed to set labels")
Expand Down

0 comments on commit e77afe7

Please sign in to comment.