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

Ubuntu18.04运行Go实现容器代码报fork/exec权限错误排查

问题背景
  • 学习场景:跟随Liz Rice在GOTO 2018分享的《Containers From Scratch》教程,本地测试开源仓库lizrice/containers-from-scratch源码,理解容器底层实现原理
  • 测试环境:Ubuntu 18.04
  • 测试代码main1.go内容如下:
package main

import (
    "fmt"
    "os"
    "os/exec"
    "syscall"
)

// go run main.go run <cmd> <args>
func main() {
    switch os.Args[1] {
    case "run":
        run()
    case "child":
        child()
    default:
        panic("help")
    }
}

func run() {
    fmt.Printf("In <Run> Running %v \n", os.Args[2:])

    cmd := exec.Command("/proc/self/exe", append([]string{"child"}, os.Args[2:]...)...)
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.SysProcAttr = &syscall.SysProcAttr{
    Cloneflags:   syscall.CLONE_NEWUTS,
    }

    must(cmd.Run())
}

func child() {
    fmt.Printf("In <child> Running %v \n", os.Args[2:])

    cmd := exec.Command(os.Args[2], os.Args[3:]...)
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    syscall.Sethostname([]byte("container"))
    must(cmd.Run())
}

func must(err error) {
    if err != nil {
        panic(err)
    }
}
故障现象

在普通用户权限下执行命令go run main1.go run /bin/bash,触发如下报错:

In <Run> Running [/bin/bash] 
panic: fork/exec /proc/self/exe: operation not permitted

goroutine 1 [running]:
main.must(...)
    /home/jia/cs_study_plan/docker/containers-from-scratch/main1.go:72
main.run()
    /home/jia/cs_study_plan/docker/containers-from-scratch/main1.go:44 +0x27f
main.main()
    /home/jia/cs_study_plan/docker/containers-from-scratch/main1.go:17 +0x4d
exit status 2
已完成排查操作
  1. 尝试先编译二进制再提权执行,可正常运行,操作流程如下(原步骤4存在笔误,gp build应为go build):
# 进入main1.go所在目录
# 初始化go模块(模块名与文件名保持一致)
go mod init main1
# 拉取依赖(可选步骤)
go mod tidy
# 编译生成二进制文件
go build
# 提权执行二进制
sudo ./main1 run /bin/bash

执行成功后输出:

In <Run> Running [/bin/bash] 
In <child> Running [/bin/bash]

此时执行ps查看进程列表输出如下:

PID TTY          TIME CMD
15774 pts/0    00:00:00 sudo
15775 pts/0    00:00:00 main1
15781 pts/0    00:00:00 exe
15786 pts/0    00:00:00 bash
16047 pts/0    00:00:00 ps
  1. 尝试停用系统AppArmor服务排除拦截影响,停用后直接执行go run main1.go run echo hello仍返回相同的operation not permitted错误。
问题根因
  • 核心权限要求:代码中调用clone系统调用时携带syscall.CLONE_NEWUTS标记创建独立UTS命名空间,属于Linux内核定义的特权操作,要求执行进程持有CAP_SYS_ADMIN权能,普通用户默认不具备该权限。该错误是内核权能检查阶段直接返回的,不属于AppArmor等安全模块的拦截范围,因此停用AppArmor无法解决问题。
  • go run执行逻辑差异:go run不会在当前目录输出永久可执行文件,而是先将源码编译到/tmp目录下的随机临时路径,再以执行go run命令的当前用户身份启动该临时二进制,全程不会自动提权。普通用户身份启动的进程没有创建命名空间所需的特权,因此必然报错。而手动编译二进制后通过sudo执行时,进程以root身份运行,持有全部所需权能,因此可以正常创建命名空间、fork子进程。
直接使用go run执行的修复方案
  • 最简便稳妥的方案:执行go run时直接加sudo提权即可,命令示例:
    sudo go run main1.go run /bin/bash
    
    该方式下编译、执行全流程均以root身份运行,持有CAP_SYS_ADMIN权能,可正常完成命名空间创建操作,无额外系统配置改动,测试完成后不会留下安全配置残留。
  • 不推荐的测试方案:修改系统内核参数允许非特权用户创建命名空间,该操作会降低系统安全边界,仅建议在完全隔离的虚机测试环境中使用,生产环境和日常办公环境禁止配置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 01:01:25