diff --git a/pkg/sentry/fsimpl/verity/verity.go b/pkg/sentry/fsimpl/verity/verity.go index 2f97324a8..6d6e0e77a 100644 --- a/pkg/sentry/fsimpl/verity/verity.go +++ b/pkg/sentry/fsimpl/verity/verity.go @@ -35,6 +35,7 @@ package verity import ( "bytes" + "encoding/hex" "encoding/json" "fmt" "math" @@ -105,6 +106,13 @@ var ( verityMu sync.RWMutex ) +// Mount option names for verityfs. +const ( + moptLowerPath = "lower_path" + moptRootHash = "root_hash" + moptRootName = "root_name" +) + // HashAlgorithm is a type specifying the algorithm used to hash the file // content. type HashAlgorithm int @@ -171,6 +179,9 @@ type filesystem struct { // system. alg HashAlgorithm + // opts is the string mount options passed to opts.Data. + opts string + // renameMu synchronizes renaming with non-renaming operations in order // to ensure consistent lock ordering between dentry.dirMu in different // dentries. @@ -193,9 +204,6 @@ type filesystem struct { // // +stateify savable type InternalFilesystemOptions struct { - // RootMerkleFileName is the name of the verity root Merkle tree file. - RootMerkleFileName string - // LowerName is the name of the filesystem wrapped by verity fs. LowerName string @@ -203,9 +211,6 @@ type InternalFilesystemOptions struct { // system. Alg HashAlgorithm - // RootHash is the root hash of the overall verity file system. - RootHash []byte - // AllowRuntimeEnable specifies whether the verity file system allows // enabling verification for files (i.e. building Merkle trees) during // runtime. @@ -239,28 +244,99 @@ func alertIntegrityViolation(msg string) error { // GetFilesystem implements vfs.FilesystemType.GetFilesystem. func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, source string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) { + mopts := vfs.GenericParseMountOptions(opts.Data) + var rootHash []byte + if encodedRootHash, ok := mopts[moptRootHash]; ok { + delete(mopts, moptRootHash) + hash, err := hex.DecodeString(encodedRootHash) + if err != nil { + ctx.Warningf("verity.FilesystemType.GetFilesystem: Failed to decode root hash: %v", err) + return nil, nil, syserror.EINVAL + } + rootHash = hash + } + var lowerPathname string + if path, ok := mopts[moptLowerPath]; ok { + delete(mopts, moptLowerPath) + lowerPathname = path + } + rootName := "root" + if root, ok := mopts[moptRootName]; ok { + delete(mopts, moptRootName) + rootName = root + } + + // Check for unparsed options. + if len(mopts) != 0 { + ctx.Warningf("verity.FilesystemType.GetFilesystem: unknown options: %v", mopts) + return nil, nil, syserror.EINVAL + } + + // Handle internal options. iopts, ok := opts.InternalData.(InternalFilesystemOptions) - if !ok { + if len(lowerPathname) == 0 && !ok { ctx.Warningf("verity.FilesystemType.GetFilesystem: missing verity configs") return nil, nil, syserror.EINVAL } + if len(lowerPathname) != 0 { + if ok { + ctx.Warningf("verity.FilesystemType.GetFilesystem: unexpected verity configs with specified lower path") + return nil, nil, syserror.EINVAL + } + iopts = InternalFilesystemOptions{ + AllowRuntimeEnable: len(rootHash) == 0, + Action: ErrorOnViolation, + } + } action = iopts.Action - // Mount the lower file system. The lower file system is wrapped inside - // verity, and should not be exposed or connected. - mopts := &vfs.MountOptions{ - GetFilesystemOptions: iopts.LowerGetFSOptions, - InternalMount: true, - } - mnt, err := vfsObj.MountDisconnected(ctx, creds, "", iopts.LowerName, mopts) - if err != nil { - return nil, nil, err + var lowerMount *vfs.Mount + var mountedLowerVD vfs.VirtualDentry + // Use an existing mount if lowerPath is provided. + if len(lowerPathname) != 0 { + vfsroot := vfs.RootFromContext(ctx) + if vfsroot.Ok() { + defer vfsroot.DecRef(ctx) + } + lowerPath := fspath.Parse(lowerPathname) + if !lowerPath.Absolute { + ctx.Infof("verity.FilesystemType.GetFilesystem: lower_path %q must be absolute", lowerPathname) + return nil, nil, syserror.EINVAL + } + var err error + mountedLowerVD, err = vfsObj.GetDentryAt(ctx, creds, &vfs.PathOperation{ + Root: vfsroot, + Start: vfsroot, + Path: lowerPath, + FollowFinalSymlink: true, + }, &vfs.GetDentryOptions{ + CheckSearchable: true, + }) + if err != nil { + ctx.Infof("verity.FilesystemType.GetFilesystem: failed to resolve lower_path %q: %v", lowerPathname, err) + return nil, nil, err + } + lowerMount = mountedLowerVD.Mount() + defer mountedLowerVD.DecRef(ctx) + } else { + // Mount the lower file system. The lower file system is wrapped inside + // verity, and should not be exposed or connected. + mountOpts := &vfs.MountOptions{ + GetFilesystemOptions: iopts.LowerGetFSOptions, + InternalMount: true, + } + mnt, err := vfsObj.MountDisconnected(ctx, creds, "", iopts.LowerName, mountOpts) + if err != nil { + return nil, nil, err + } + lowerMount = mnt } fs := &filesystem{ creds: creds.Fork(), alg: iopts.Alg, - lowerMount: mnt, + lowerMount: lowerMount, + opts: opts.Data, allowRuntimeEnable: iopts.AllowRuntimeEnable, } fs.vfsfs.Init(vfsObj, &fstype, fs) @@ -268,11 +344,11 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt // Construct the root dentry. d := fs.newDentry() d.refs = 1 - lowerVD := vfs.MakeVirtualDentry(mnt, mnt.Root()) + lowerVD := vfs.MakeVirtualDentry(lowerMount, lowerMount.Root()) lowerVD.IncRef() d.lowerVD = lowerVD - rootMerkleName := merkleRootPrefix + iopts.RootMerkleFileName + rootMerkleName := merkleRootPrefix + rootName lowerMerkleVD, err := vfsObj.GetDentryAt(ctx, fs.creds, &vfs.PathOperation{ Root: lowerVD, @@ -352,7 +428,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt d.mode = uint32(stat.Mode) d.uid = stat.UID d.gid = stat.GID - d.hash = make([]byte, len(iopts.RootHash)) + d.hash = make([]byte, len(rootHash)) d.childrenNames = make(map[string]struct{}) if !d.isDir() { @@ -427,7 +503,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt } d.hashMu.Lock() - copy(d.hash, iopts.RootHash) + copy(d.hash, rootHash) d.hashMu.Unlock() d.vfsd.Init(d) @@ -443,7 +519,7 @@ func (fs *filesystem) Release(ctx context.Context) { // MountOptions implements vfs.FilesystemImpl.MountOptions. func (fs *filesystem) MountOptions() string { - return "" + return fs.opts } // dentry implements vfs.DentryImpl. diff --git a/pkg/sentry/fsimpl/verity/verity_state_autogen.go b/pkg/sentry/fsimpl/verity/verity_state_autogen.go index 47bea46c9..f0c374803 100644 --- a/pkg/sentry/fsimpl/verity/verity_state_autogen.go +++ b/pkg/sentry/fsimpl/verity/verity_state_autogen.go @@ -39,6 +39,7 @@ func (fs *filesystem) StateFields() []string { "lowerMount", "rootDentry", "alg", + "opts", } } @@ -53,6 +54,7 @@ func (fs *filesystem) StateSave(stateSinkObject state.Sink) { stateSinkObject.Save(3, &fs.lowerMount) stateSinkObject.Save(4, &fs.rootDentry) stateSinkObject.Save(5, &fs.alg) + stateSinkObject.Save(6, &fs.opts) } func (fs *filesystem) afterLoad() {} @@ -65,6 +67,7 @@ func (fs *filesystem) StateLoad(stateSourceObject state.Source) { stateSourceObject.Load(3, &fs.lowerMount) stateSourceObject.Load(4, &fs.rootDentry) stateSourceObject.Load(5, &fs.alg) + stateSourceObject.Load(6, &fs.opts) } func (i *InternalFilesystemOptions) StateTypeName() string { @@ -73,10 +76,8 @@ func (i *InternalFilesystemOptions) StateTypeName() string { func (i *InternalFilesystemOptions) StateFields() []string { return []string{ - "RootMerkleFileName", "LowerName", "Alg", - "RootHash", "AllowRuntimeEnable", "LowerGetFSOptions", "Action", @@ -88,26 +89,22 @@ func (i *InternalFilesystemOptions) beforeSave() {} // +checklocksignore func (i *InternalFilesystemOptions) StateSave(stateSinkObject state.Sink) { i.beforeSave() - stateSinkObject.Save(0, &i.RootMerkleFileName) - stateSinkObject.Save(1, &i.LowerName) - stateSinkObject.Save(2, &i.Alg) - stateSinkObject.Save(3, &i.RootHash) - stateSinkObject.Save(4, &i.AllowRuntimeEnable) - stateSinkObject.Save(5, &i.LowerGetFSOptions) - stateSinkObject.Save(6, &i.Action) + stateSinkObject.Save(0, &i.LowerName) + stateSinkObject.Save(1, &i.Alg) + stateSinkObject.Save(2, &i.AllowRuntimeEnable) + stateSinkObject.Save(3, &i.LowerGetFSOptions) + stateSinkObject.Save(4, &i.Action) } func (i *InternalFilesystemOptions) afterLoad() {} // +checklocksignore func (i *InternalFilesystemOptions) StateLoad(stateSourceObject state.Source) { - stateSourceObject.Load(0, &i.RootMerkleFileName) - stateSourceObject.Load(1, &i.LowerName) - stateSourceObject.Load(2, &i.Alg) - stateSourceObject.Load(3, &i.RootHash) - stateSourceObject.Load(4, &i.AllowRuntimeEnable) - stateSourceObject.Load(5, &i.LowerGetFSOptions) - stateSourceObject.Load(6, &i.Action) + stateSourceObject.Load(0, &i.LowerName) + stateSourceObject.Load(1, &i.Alg) + stateSourceObject.Load(2, &i.AllowRuntimeEnable) + stateSourceObject.Load(3, &i.LowerGetFSOptions) + stateSourceObject.Load(4, &i.Action) } func (d *dentry) StateTypeName() string { diff --git a/runsc/boot/vfs.go b/runsc/boot/vfs.go index 9117540d5..7d8fd0483 100644 --- a/runsc/boot/vfs.go +++ b/runsc/boot/vfs.go @@ -92,7 +92,7 @@ func registerFilesystems(k *kernel.Kernel) error { }) vfsObj.MustRegisterFilesystemType(verity.Name, &verity.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{ AllowUserList: true, - AllowUserMount: false, + AllowUserMount: true, }) // Setup files in devtmpfs. @@ -483,7 +483,7 @@ func (c *containerMounter) getMountNameAndOptionsVFS2(conf *config.Config, m *mo var data []string var iopts interface{} - verityOpts, verityRequested, remainingMOpts, err := parseVerityMountOptions(m.Options) + verityData, verityOpts, verityRequested, remainingMOpts, err := parseVerityMountOptions(m.Options) if err != nil { return "", nil, false, err } @@ -555,13 +555,13 @@ func (c *containerMounter) getMountNameAndOptionsVFS2(conf *config.Config, m *mo } if verityRequested { - verityOpts.RootMerkleFileName = path.Base(m.Mount.Destination) + verityData = verityData + "root_name=" + path.Base(m.Mount.Destination) verityOpts.LowerName = fsName verityOpts.LowerGetFSOptions = opts.GetFilesystemOptions fsName = verity.Name opts = &vfs.MountOptions{ GetFilesystemOptions: vfs.GetFilesystemOptions{ - Data: strings.Join(data, ","), + Data: verityData, InternalData: verityOpts, }, InternalMount: true, @@ -582,9 +582,10 @@ func parseKeyValue(s string) (string, string, bool) { // parseAndFilterOptions scans the provided mount options for verity-related // mount options. It returns the parsed set of verity mount options, as well as // the filtered set of mount options unrelated to verity. -func parseVerityMountOptions(mopts []string) (verity.InternalFilesystemOptions, bool, []string, error) { +func parseVerityMountOptions(mopts []string) (string, verity.InternalFilesystemOptions, bool, []string, error) { nonVerity := []string{} found := false + var rootHash string verityOpts := verity.InternalFilesystemOptions{ Action: verity.PanicOnViolation, } @@ -596,13 +597,13 @@ func parseVerityMountOptions(mopts []string) (verity.InternalFilesystemOptions, k, v, ok := parseKeyValue(o) if !ok { - return verityOpts, found, nonVerity, fmt.Errorf("invalid verity mount option with no value: %q", o) + return "", verityOpts, found, nonVerity, fmt.Errorf("invalid verity mount option with no value: %q", o) } found = true switch k { case "verity.roothash": - verityOpts.RootHash = []byte(v) + rootHash = v case "verity.action": switch v { case "error": @@ -614,11 +615,12 @@ func parseVerityMountOptions(mopts []string) (verity.InternalFilesystemOptions, verityOpts.Action = verity.PanicOnViolation } default: - return verityOpts, found, nonVerity, fmt.Errorf("unknown verity mount option: %q", k) + return "", verityOpts, found, nonVerity, fmt.Errorf("unknown verity mount option: %q", k) } } - verityOpts.AllowRuntimeEnable = len(verityOpts.RootHash) == 0 - return verityOpts, found, nonVerity, nil + verityOpts.AllowRuntimeEnable = len(rootHash) == 0 + verityData := "root_hash=" + rootHash + "," + return verityData, verityOpts, found, nonVerity, nil } // mountTmpVFS2 mounts an internal tmpfs at '/tmp' if it's safe to do so. diff --git a/runsc/cmd/verity_prepare.go b/runsc/cmd/verity_prepare.go index 2197cd3f8..66128b2a3 100644 --- a/runsc/cmd/verity_prepare.go +++ b/runsc/cmd/verity_prepare.go @@ -102,5 +102,7 @@ func (c *VerityPrepare) Execute(_ context.Context, f *flag.FlagSet, args ...inte // Force no networking, it is not necessary to run the verity measure tool. conf.Network = config.NetworkNone + conf.Verity = true + return startContainerAndWait(spec, conf, cid, waitStatus) }