Go中CLONE_NEWNS未生效?mount namespace隔离失效排查
为什么syscall.CLONE_NEWNS在CentOS7上未按预期隔离挂载?
我来帮你排查这个问题——在CentOS 7环境下,你的代码没达到预期效果的核心原因是默认的挂载传播属性。
问题根源
当你用CLONE_NEWNS创建新的mount namespace时,它会完全复制父namespace的挂载点。而CentOS 7的根文件系统(/)默认被设置为shared传播模式,这意味着在新namespace里对挂载点的修改(比如挂载tmpfs)会反向传播到父namespace,所以外部shell能看到你在新shell里创建的文件。
简单说,CLONE_NEWNS只是创建了新的namespace,但没有阻止挂载操作的双向传播,这才导致隔离失效。
修复方案
你需要在新的mount namespace里,把根挂载点的传播模式改成private,这样新namespace内的挂载操作就不会影响外部。可以通过两种方式实现:
方法1:在Go代码中直接设置挂载属性
修改你的mnt.go,在启动bash之前,先调用syscall.Mount将根目录设为private:
package main import ( "fmt" "log" "os" "os/exec" "syscall" ) func main() { fmt.Println("Entering go program") cmd := exec.Command("/bin/bash") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin cmd.SysProcAttr = &syscall.SysProcAttr{ Cloneflags: syscall.CLONE_NEWNS, } // 在新namespace中设置根挂载为private,阻止传播 cmd.PreStart = func() error { err := syscall.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, "") if err != nil { return fmt.Errorf("failed to make root private: %w", err) } return nil } if err := cmd.Run(); err != nil { log.Fatal(err) } fmt.Println("Exiting go program") }
方法2:在新shell中手动设置
如果你不想修改代码,也可以在进入新bash后先执行:
mount --make-rprivate /
之后再挂载tmpfs,这样也能实现隔离效果。不过显然方法1更自动化,适合代码层面的需求。
验证修复效果
- 编译修改后的代码:
go build mnt.go - 运行
./mnt进入新shell,执行挂载和创建文件的操作 - 打开另一个外部shell,执行
ls /tmp/testmount,此时应该看不到任何文件,说明挂载已经被成功隔离了。
内容的提问来源于stack exchange,提问作者ashwnacharya
相关产品推荐
相关产品推荐

