diff --git a/README.md b/README.md index e3f78c8840c..6582d99c10d 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,8 @@ Major: - [On-demand image pulling (lazy-pulling) using Stargz Snapshotter](./docs/stargz.md): `nerdctl --snapshotter=stargz run IMAGE` . - [Image encryption and decryption using ocicrypt (imgcrypt)](./docs/ocicrypt.md): `nerdctl image (encrypt|decrypt) SRC DST` - [P2P image distribution using IPFS](./docs/ipfs.md): `nerdctl run ipfs://CID` +- Recursive read-only (RRO) bind-mount: `nerdctl run -v /mnt:/mnt:rro` (make children such as `/mnt/usb` to be read-only, too). + Requires kernel >= 5.12, and crun >= 1.4 or runc >= 1.1 (PR [#3272](https://github.com/opencontainers/runc/pull/3272)). Minor: - Namespacing: `nerdctl --namespace= ps` . @@ -339,7 +341,13 @@ Runtime flags: - :whale: `--sysctl`: Sysctl options, e.g \"net.ipv4.ip_forward=1\" Volume flags: -- :whale: :blue_square: `-v, --volume`: Bind mount a volume +- :whale: :blue_square: `-v, --volume :[:]`: Bind mount a volume, e.g., `-v /mnt:/mnt:rro,rprivate` + - :whale: option `rw` : Read/Write (when writable) + - :whale: option `ro` : Non-recursive read-only + - :nerd_face: option `rro`: Recursive read-only. Should be used in conjunction with `rprivate`. e.g., `-v /mnt:/mnt:rro,rprivate` makes children such as `/mnt/usb` to be read-only, too. + Requires kernel >= 5.12, and crun >= 1.4 or runc >= 1.1 (PR [#3272](https://github.com/opencontainers/runc/pull/3272)). With older runc, `rro` just works as `ro`. + - :whale: option `shared`, `slave`, `private`: Non-recursive "shared" / "slave" / "private" propagation + - :whale: option `rshared`, `rslave`, `rprivate`: Recursive "shared" / "slave" / "private" propagation - :whale: `--tmpfs`: Mount a tmpfs directory Rootfs flags: diff --git a/pkg/mountutil/mountutil_linux.go b/pkg/mountutil/mountutil_linux.go index b6a5fbd9e1e..449d03a7034 100644 --- a/pkg/mountutil/mountutil_linux.go +++ b/pkg/mountutil/mountutil_linux.go @@ -94,9 +94,7 @@ func parseVolumeOptionsWithMountInfo(vType, src, optsRaw string, getMountInfoFun ) for _, opt := range strings.Split(optsRaw, ",") { switch opt { - case "rw": - writeModeRawOpts = append(writeModeRawOpts, opt) - case "ro": + case "rw", "ro", "rro": writeModeRawOpts = append(writeModeRawOpts, opt) case "private", "rprivate", "shared", "rshared", "slave", "rslave": propagationRawOpts = append(propagationRawOpts, opt) @@ -112,9 +110,24 @@ func parseVolumeOptionsWithMountInfo(vType, src, optsRaw string, getMountInfoFun if len(writeModeRawOpts) > 1 { return nil, nil, fmt.Errorf("duplicated read/write volume option: %+v", writeModeRawOpts) - } else if len(writeModeRawOpts) > 0 && writeModeRawOpts[0] == "ro" { - opts = append(opts, "ro") - } // No need to return option when "rw" + } else if len(writeModeRawOpts) > 0 { + switch writeModeRawOpts[0] { + case "ro": + opts = append(opts, "ro") + case "rro": + // Mount option "rro" is supported since crun v1.4 / runc v1.1 (https://github.com/opencontainers/runc/pull/3272), with kernel >= 5.12. + // Older version of runc just ignores "rro", so we have to add "ro" too, to our best effort. + opts = append(opts, "ro", "rro") + if len(propagationRawOpts) != 1 || propagationRawOpts[0] != "rprivate" { + logrus.Warn("Mount option \"rro\" should be used in conjunction with \"rprivate\"") + } + case "rw": + // NOP + default: + // NOTREACHED + return nil, nil, fmt.Errorf("unexpected writeModeRawOpts[0]=%q", writeModeRawOpts[0]) + } + } if len(propagationRawOpts) > 1 { return nil, nil, fmt.Errorf("duplicated volume propagation option: %+v", propagationRawOpts)