You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让挂载在mount namespace中的文件系统仅对该命名空间可见?

问题:如何让mount namespace内的挂载仅对该命名空间私有?

我通过unshare(CLONE_NEWNS)创建mount namespace,之后用mount系统调用(mountflags=0)将overlay文件系统挂载到/path/to/my/mount。但挂载后,不仅新namespace内的shell能看到,root mount namespace的普通shell也能看到该挂载,这不符合我“仅在新namespace内可见”的预期。

我的Go代码如下:

// lock this goroutine to a single OS thread because namespaces are thread-local
runtime.LockOSThread()
defer runtime.UnlockOSThread()

// switch to a new mount namespace
err := unix.Unshare(unix.CLONE_FS)
if err != nil {
    return fmt.Errorf("error entering new mount namespace: %w", err)
}

mountopts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lower, upper, work)
err = unix.Mount("overlay", target, "overlay", 0, mountopts)
if err != nil {
    return fmt.Errorf("error mounting overlay filesystem: %w", err)
}

我尝试过在unshare和mount之间加入以下代码,把新namespace的/设为私有挂载,但没效果:

// make the root filesystem in this new namespace private
err = unix.Mount("ignored", "/", "ignored", unix.MS_PRIVATE, "ignored")
if err != nil {
    return fmt.Errorf("error making root filesystem private")
}

虽然新namespace里的/确实变成了私有(查看/proc/self/mountinfo确认),但root namespace的/也变成了私有,而且目标挂载依然在两个namespace都可见。


解决方法与原因分析

核心问题:挂载传播的继承特性

调用unshare(CLONE_NEWNS)时,新的mount namespace会完整复制当前namespace的所有挂载点及其传播属性。默认情况下,多数系统的根挂载/处于shared模式——这种模式下,挂载点的变更会在共享该挂载的所有namespace之间双向传播。你之前修改根挂载属性时,因为新namespace的根挂载还和原root namespace的根挂载处于共享关系,所以修改操作同步到了root namespace,同时未彻底切断传播链,导致后续挂载依然可见。

正确操作步骤

要实现挂载仅在新namespace内可见,需在unshare后、挂载前彻底切断与原namespace的挂载传播链,具体步骤:

  1. 调用unshare(CLONE_NEWNS)创建新的mount namespace
  2. 使用MS_REC | MS_PRIVATE标志递归将根挂载/及其所有子挂载设置为私有,确保整个挂载树脱离与原namespace的共享关系
  3. 执行overlay挂载操作

修改后的Go代码示例

// lock this goroutine to a single OS thread because namespaces are thread-local
runtime.LockOSThread()
defer runtime.UnlockOSThread()

// switch to a new mount namespace
err := unix.Unshare(unix.CLONE_NEWNS)
if err != nil {
    return fmt.Errorf("error entering new mount namespace: %w", err)
}

// 递归将根挂载及其所有子挂载设为私有,彻底切断与原namespace的传播链
err = unix.Mount("ignored", "/", "ignored", unix.MS_REC|unix.MS_PRIVATE, "ignored")
if err != nil {
    return fmt.Errorf("error making root filesystem recursively private: %w", err)
}

mountopts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lower, upper, work)
err = unix.Mount("overlay", target, "overlay", 0, mountopts)
if err != nil {
    return fmt.Errorf("error mounting overlay filesystem: %w", err)
}

为何之前的操作无效?

你之前仅使用MS_PRIVATE而未加MS_REC,只会修改根挂载本身的属性,但子挂载可能仍处于shared模式;同时,修改时根挂载与原namespace的根挂载仍共享,导致修改同步到了root namespace。加上MS_REC后,会递归处理整个挂载树,彻底切断传播关系,后续挂载就不会影响到root namespace了。

内容的提问来源于stack exchange,提问作者Alex Flint

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 22:08:17