(rootfs string)
| 1187 | } |
| 1188 | |
| 1189 | func msMoveRoot(rootfs string) error { |
| 1190 | // Before we move the root and chroot we have to mask all "full" sysfs and |
| 1191 | // procfs mounts which exist on the host. This is because while the kernel |
| 1192 | // has protections against mounting procfs if it has masks, when using |
| 1193 | // chroot(2) the *host* procfs mount is still reachable in the mount |
| 1194 | // namespace and the kernel permits procfs mounts inside --no-pivot |
| 1195 | // containers. |
| 1196 | // |
| 1197 | // Users shouldn't be using --no-pivot except in exceptional circumstances, |
| 1198 | // but to avoid such a trivial security flaw we apply a best-effort |
| 1199 | // protection here. The kernel only allows a mount of a pseudo-filesystem |
| 1200 | // like procfs or sysfs if there is a *full* mount (the root of the |
| 1201 | // filesystem is mounted) without any other locked mount points covering a |
| 1202 | // subtree of the mount. |
| 1203 | // |
| 1204 | // So we try to unmount (or mount tmpfs on top of) any mountpoint which is |
| 1205 | // a full mount of either sysfs or procfs (since those are the most |
| 1206 | // concerning filesystems to us). |
| 1207 | mountinfos, err := mountinfo.GetMounts(func(info *mountinfo.Info) (skip, stop bool) { |
| 1208 | // Collect every sysfs and procfs filesystem, except for those which |
| 1209 | // are non-full mounts or are inside the rootfs of the container. |
| 1210 | if info.Root != "/" || |
| 1211 | (info.FSType != "proc" && info.FSType != "sysfs") || |
| 1212 | strings.HasPrefix(info.Mountpoint, rootfs) { |
| 1213 | skip = true |
| 1214 | } |
| 1215 | return skip, stop |
| 1216 | }) |
| 1217 | if err != nil { |
| 1218 | return err |
| 1219 | } |
| 1220 | for _, info := range mountinfos { |
| 1221 | p := info.Mountpoint |
| 1222 | // Be sure umount events are not propagated to the host. |
| 1223 | if err := mount("", p, "", unix.MS_SLAVE|unix.MS_REC, ""); err != nil { |
| 1224 | if errors.Is(err, unix.ENOENT) { |
| 1225 | // If the mountpoint doesn't exist that means that we've |
| 1226 | // already blasted away some parent directory of the mountpoint |
| 1227 | // and so we don't care about this error. |
| 1228 | continue |
| 1229 | } |
| 1230 | return err |
| 1231 | } |
| 1232 | if err := unmount(p, unix.MNT_DETACH); err != nil { |
| 1233 | if !errors.Is(err, unix.EINVAL) && !errors.Is(err, unix.EPERM) { |
| 1234 | return err |
| 1235 | } else { |
| 1236 | // If we have not privileges for umounting (e.g. rootless), then |
| 1237 | // cover the path. |
| 1238 | if err := mount("tmpfs", p, "tmpfs", 0, ""); err != nil { |
| 1239 | return err |
| 1240 | } |
| 1241 | } |
| 1242 | } |
| 1243 | } |
| 1244 | |
| 1245 | // Move the rootfs on top of "/" in our mount namespace. |
| 1246 | if err := mount(rootfs, "/", "", unix.MS_MOVE, ""); err != nil { |
no test coverage detected
searching dependent graphs…