From 8f977ddbdf406202754cf8ef0f78b78794b97627 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Tue, 29 May 2018 16:42:47 -0400 Subject: [PATCH] Add default mount options to pass to drivers I believe we should be running container images mounted with nodev by default. This would eliminate the disk of a device sneaking into the container without being on the approved list. This would give us the same or potentially additional security over the device cgroup. It would be nice if this could be passed in on an image by image basis. So users could also specify if they want nosuid images. Signed-off-by: Daniel J Walsh --- docs/containers-storage.conf.5.md | 8 ++++---- drivers/aufs/aufs.go | 7 +++++++ drivers/btrfs/btrfs.go | 7 +++++-- drivers/devmapper/deviceset.go | 4 ++-- drivers/overlay/overlay.go | 6 ++++++ drivers/vfs/driver.go | 3 +++ drivers/windows/windows.go | 8 ++++++++ drivers/zfs/zfs.go | 10 ++++++++-- storage.conf | 6 +++--- store.go | 12 +++++------- 10 files changed, 51 insertions(+), 20 deletions(-) diff --git a/docs/containers-storage.conf.5.md b/docs/containers-storage.conf.5.md index 3c63cfe783..d100f68800 100644 --- a/docs/containers-storage.conf.5.md +++ b/docs/containers-storage.conf.5.md @@ -109,10 +109,6 @@ Specifies the min free space percent in a thin pool required for new device crea Specifies extra mkfs arguments to be used when creating the base device. -**mountopt**="" - -Specifies extra mount options used when mounting the thin devices. - **use_deferred_removal**="" Marks devicemapper block device for deferred removal. If the device is in use when its driver attempts to remove it, the driver tells the kernel to remove the device as soon as possible. Note this does not free up the disk space, use deferred deletion to fully remove the thinpool. (default: true). @@ -125,6 +121,10 @@ Marks thinpool device for deferred deletion. If the thinpool is in use when the Specifies the maximum number of retries XFS should attempt to complete IO when ENOSPC (no space) error is returned by underlying storage device. (default: 0, which means to try continuously.) +**mountopt**="" + +Default options to be used to mount container images. Suggested value "nodev". + # HISTORY May 2017, Originally compiled by Dan Walsh Format copied from crio.conf man page created by Aleksa Sarai diff --git a/drivers/aufs/aufs.go b/drivers/aufs/aufs.go index bbbe9e4af8..14d8a23a8f 100644 --- a/drivers/aufs/aufs.go +++ b/drivers/aufs/aufs.go @@ -103,6 +103,13 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, errors.Wrapf(graphdriver.ErrIncompatibleFS, "AUFS is not supported over %q", backingFs) } + for _, option := range options { + if strings.HasPrefix(option, "aufs.mountopt=") { + return nil, fmt.Errorf("aufs driver does not support mount options") + } else { + return nil, fmt.Errorf("option %s not supported", option) + } + } paths := []string{ "mnt", "diff", diff --git a/drivers/btrfs/btrfs.go b/drivers/btrfs/btrfs.go index 4b9699fdb3..28049f4ada 100644 --- a/drivers/btrfs/btrfs.go +++ b/drivers/btrfs/btrfs.go @@ -43,8 +43,9 @@ func init() { } type btrfsOptions struct { - minSpace uint64 - size uint64 + minSpace uint64 + size uint64 + mountOptions string } // Init returns a new BTRFS driver. @@ -110,6 +111,8 @@ func parseOptions(opt []string) (btrfsOptions, bool, error) { } userDiskQuota = true options.minSpace = uint64(minSpace) + case "btrfs.mountopt": + options.mountOptions = val default: return options, userDiskQuota, fmt.Errorf("Unknown option %s", key) } diff --git a/drivers/devmapper/deviceset.go b/drivers/devmapper/deviceset.go index 0fab980b9b..f2ea75ff4e 100644 --- a/drivers/devmapper/deviceset.go +++ b/drivers/devmapper/deviceset.go @@ -2664,6 +2664,8 @@ func NewDeviceSet(root string, doInit bool, options []string, uidMaps, gidMaps [ } key = strings.ToLower(key) switch key { + case "devicemapper.mountopt": + devices.mountOptions = joinMountOptions(devices.mountOptions, val) case "dm.basesize": size, err := units.RAMInBytes(val) if err != nil { @@ -2690,8 +2692,6 @@ func NewDeviceSet(root string, doInit bool, options []string, uidMaps, gidMaps [ devices.filesystem = val case "dm.mkfsarg": devices.mkfsArgs = append(devices.mkfsArgs, val) - case "dm.mountopt": - devices.mountOptions = joinMountOptions(devices.mountOptions, val) case "dm.metadatadev": devices.metadataDevice = val case "dm.datadev": diff --git a/drivers/overlay/overlay.go b/drivers/overlay/overlay.go index 6b5f6912f7..4a2460cef4 100644 --- a/drivers/overlay/overlay.go +++ b/drivers/overlay/overlay.go @@ -84,6 +84,7 @@ type overlayOptions struct { overrideKernelCheck bool imageStores []string quota quota.Quota + mountOptions string } // Driver contains information about the home directory and the list of active mounts that are created using this driver. @@ -203,6 +204,8 @@ func parseOptions(options []string) (*overlayOptions, error) { if err != nil { return nil, err } + case ".mountopt", "overlay.mountopt", "overlay2.mountopt": + o.mountOptions = val case ".size", "overlay.size", "overlay2.size": logrus.Debugf("overlay: size=%s", val) size, err := units.RAMInBytes(val) @@ -666,6 +669,9 @@ func (d *Driver) Get(id, mountLabel string) (_ string, retErr error) { if len(mountData) > pageSize { //FIXME: We need to figure out to get this to work with additional stores opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(relLowers, ":"), path.Join(id, "diff"), path.Join(id, "work")) + if d.options.mountOptions != "" { + opts = fmt.Sprintf("%s,%s", d.options.mountOptions, opts) + } mountData = label.FormatMountLabel(opts, mountLabel) if len(mountData) > pageSize { return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData)) diff --git a/drivers/vfs/driver.go b/drivers/vfs/driver.go index cee55f8d16..17a37f2264 100644 --- a/drivers/vfs/driver.go +++ b/drivers/vfs/driver.go @@ -42,6 +42,9 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap d.homes = append(d.homes, strings.Split(option[12:], ",")...) continue } + if strings.HasPrefix(option, "vfs.mountopt=") { + return nil, fmt.Errorf("vfs driver does not support mount options") + } } return graphdriver.NewNaiveDiffDriver(d, graphdriver.NewNaiveLayerIDMapUpdater(d)), nil } diff --git a/drivers/windows/windows.go b/drivers/windows/windows.go index b750715bfe..930824c942 100644 --- a/drivers/windows/windows.go +++ b/drivers/windows/windows.go @@ -86,6 +86,14 @@ type Driver struct { func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { logrus.Debugf("WindowsGraphDriver InitFilter at %s", home) + for _, option := range options { + if strings.HasPrefix(option, "windows.mountopt=") { + return nil, fmt.Errorf("windows driver does not support mount options") + } else { + return nil, fmt.Errorf("option %s not supported", option) + } + } + fsType, err := getFileSystemType(string(home[0])) if err != nil { return nil, err diff --git a/drivers/zfs/zfs.go b/drivers/zfs/zfs.go index 886ebc8bdb..bc21723a4f 100644 --- a/drivers/zfs/zfs.go +++ b/drivers/zfs/zfs.go @@ -24,8 +24,9 @@ import ( ) type zfsOptions struct { - fsName string - mountPath string + fsName string + mountPath string + mountOptions string } func init() { @@ -134,6 +135,8 @@ func parseOptions(opt []string) (zfsOptions, error) { switch key { case "zfs.fsname": options.fsName = val + case "zfs.mountopt": + options.mountOptions = val default: return options, fmt.Errorf("Unknown option %s", key) } @@ -300,6 +303,9 @@ func (d *Driver) create(id, parent string, storageOpt map[string]string) error { } if parent == "" { mountoptions := map[string]string{"mountpoint": "legacy"} + if d.options.mountOptions != "" { + mountoptions[d.options.mountOptions] = "" + } fs, err := zfs.CreateFilesystem(name, mountoptions) if err == nil { err = setQuota(name, quota) diff --git a/storage.conf b/storage.conf index 2eedc619d6..fcf24d67a3 100644 --- a/storage.conf +++ b/storage.conf @@ -28,6 +28,9 @@ size = "" # OverrideKernelCheck tells the driver to ignore kernel checks based on kernel version override_kernel_check = "false" +# mountopt specifies extra mount options used when mounting +mountopt = "nodev" + # Remap-UIDs/GIDs is the mapping from UIDs/GIDs as they should appear inside of # a container, to UIDs/GIDs as they should appear outside of the container, and # the length of the range of UIDs/GIDs. Additional mapped sets can be listed @@ -99,9 +102,6 @@ override_kernel_check = "false" # device. # mkfsarg = "" -# mountopt specifies extra mount options used when mounting the thin devices. -# mountopt = "" - # use_deferred_removal marks devicemapper block device for deferred removal. # If the thinpool is in use when the driver attempts to remove it, the driver # tells the kernel to remove it as soon as possible. Note this does not free diff --git a/store.go b/store.go index 088d9c0c5b..ee7d29acf7 100644 --- a/store.go +++ b/store.go @@ -2638,10 +2638,6 @@ type ThinpoolOptionsConfig struct { // basedevice. MkfsArg string `toml:"mkfsarg"` - // MountOpt specifies extra mount options used when mounting the thin - // devices. - MountOpt string `toml:"mountopt"` - // UseDeferredDeletion marks device for deferred deletion UseDeferredDeletion string `toml:"use_deferred_deletion"` @@ -2680,6 +2676,8 @@ type OptionsConfig struct { RemapGroup string `toml:"remap-group"` // Thinpool container options to be handed to thinpool drivers Thinpool struct{ ThinpoolOptionsConfig } `toml:"thinpool"` + // MountOpt specifies extra mount options used when mounting + MountOpt string `toml:"mountopt"` } // TOML-friendly explicit tables used for conversions. @@ -2752,9 +2750,6 @@ func init() { if config.Storage.Options.Thinpool.MkfsArg != "" { DefaultStoreOptions.GraphDriverOptions = append(DefaultStoreOptions.GraphDriverOptions, fmt.Sprintf("dm.mkfsarg=%s", config.Storage.Options.Thinpool.MkfsArg)) } - if config.Storage.Options.Thinpool.MountOpt != "" { - DefaultStoreOptions.GraphDriverOptions = append(DefaultStoreOptions.GraphDriverOptions, fmt.Sprintf("dm.mountopt=%s", config.Storage.Options.Thinpool.MountOpt)) - } if config.Storage.Options.Thinpool.UseDeferredDeletion != "" { DefaultStoreOptions.GraphDriverOptions = append(DefaultStoreOptions.GraphDriverOptions, fmt.Sprintf("dm.use_deferred_deletion=%s", config.Storage.Options.Thinpool.UseDeferredDeletion)) } @@ -2770,6 +2765,9 @@ func init() { if config.Storage.Options.Size != "" { DefaultStoreOptions.GraphDriverOptions = append(DefaultStoreOptions.GraphDriverOptions, fmt.Sprintf("%s.size=%s", config.Storage.Driver, config.Storage.Options.Size)) } + if config.Storage.Options.MountOpt != "" { + DefaultStoreOptions.GraphDriverOptions = append(DefaultStoreOptions.GraphDriverOptions, fmt.Sprintf("%s.mountopt=%s", config.Storage.Driver, config.Storage.Options.MountOpt)) + } if config.Storage.Options.OverrideKernelCheck != "" { DefaultStoreOptions.GraphDriverOptions = append(DefaultStoreOptions.GraphDriverOptions, fmt.Sprintf("%s.override_kernel_check=%s", config.Storage.Driver, config.Storage.Options.OverrideKernelCheck)) }