@@ -26,6 +26,7 @@ import (
2626 "strings"
2727 "time"
2828
29+ "github.com/containerd/containerd/pkg/userns"
2930 "golang.org/x/sys/unix"
3031)
3132
@@ -114,12 +115,56 @@ func (m *Mount) mount(target string) (err error) {
114115
115116 const broflags = unix .MS_BIND | unix .MS_RDONLY
116117 if oflags & broflags == broflags {
118+ // Preserve CL_UNPRIVILEGED "locked" flags of the
119+ // bind mount target when we remount to make the bind readonly.
120+ // This is necessary to ensure that
121+ // bind-mounting "with options" will not fail with user namespaces, due to
122+ // kernel restrictions that require user namespace mounts to preserve
123+ // CL_UNPRIVILEGED locked flags.
124+ var unprivFlags int
125+ if userns .RunningInUserNS () {
126+ unprivFlags , err = getUnprivilegedMountFlags (target )
127+ if err != nil {
128+ return err
129+ }
130+ }
117131 // Remount the bind to apply read only.
118- return unix .Mount ("" , target , "" , uintptr (oflags | unix .MS_REMOUNT ), "" )
132+ return unix .Mount ("" , target , "" , uintptr (oflags | unprivFlags | unix .MS_REMOUNT ), "" )
119133 }
120134 return nil
121135}
122136
137+ // Get the set of mount flags that are set on the mount that contains the given
138+ // path and are locked by CL_UNPRIVILEGED.
139+ //
140+ // From https://github.com/moby/moby/blob/v23.0.1/daemon/oci_linux.go#L430-L460
141+ func getUnprivilegedMountFlags (path string ) (int , error ) {
142+ var statfs unix.Statfs_t
143+ if err := unix .Statfs (path , & statfs ); err != nil {
144+ return 0 , err
145+ }
146+
147+ // The set of keys come from https://github.com/torvalds/linux/blob/v4.13/fs/namespace.c#L1034-L1048.
148+ unprivilegedFlags := []int {
149+ unix .MS_RDONLY ,
150+ unix .MS_NODEV ,
151+ unix .MS_NOEXEC ,
152+ unix .MS_NOSUID ,
153+ unix .MS_NOATIME ,
154+ unix .MS_RELATIME ,
155+ unix .MS_NODIRATIME ,
156+ }
157+
158+ var flags int
159+ for flag := range unprivilegedFlags {
160+ if int (statfs .Flags )& flag == flag {
161+ flags |= flag
162+ }
163+ }
164+
165+ return flags , nil
166+ }
167+
123168// Unmount the provided mount path with the flags
124169func Unmount (target string , flags int ) error {
125170 if err := unmount (target , flags ); err != nil && err != unix .EINVAL {
0 commit comments