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

如何在Go的os/exec.Start()后、命令启动前加载seccomp过滤器?

在Go中为命令加载Seccomp规则的实现方案

我来帮你解决这个问题——在Go的沙箱模块中,要在目标命令启动前加载seccomp规则,核心是抓住fork之后、exec之前的窗口来配置过滤器。下面是两种可行的方案,都不需要修改程序主函数,很适合作为沙箱模块的实现方式:

方法一:利用os/exec的PreExec钩子

os/exec包提供了Cmd.SysProcAttr.PreExec钩子,它会在子进程fork完成后、exec目标命令前执行。这个阶段正好可以用来加载seccomp规则,而且完全符合Go的惯用写法。

首先你需要引入seccomp的Go绑定库:github.com/seccomp/libseccomp-golang(它封装了libseccomp的C接口,避免手动编写复杂的BPF规则)。

示例代码:

package main

import (
	"os"
	"os/exec"
	"syscall"

	"github.com/seccomp/libseccomp-golang"
)

func main() {
	// 初始化目标命令
	cmd := exec.Command("bash")
	
	// 配置PreExec钩子,在子进程中加载seccomp规则
	cmd.SysProcAttr = &syscall.SysProcAttr{
		PreExec: func() {
			// 创建过滤器,默认动作是杀死进程并记录日志
			ctx, err := seccomp.NewFilter(seccomp.ActKill.Log())
			if err != nil {
				// 子进程中不能用Go runtime的日志工具,直接调用syscall退出
				syscall.Exit(1)
			}
			defer ctx.Release()

			// 添加允许的系统调用
			allowSyscalls := []seccomp.Syscall{
				seccomp.SysRead,
				seccomp.SysWrite,
				seccomp.SysExit,
				seccomp.SysSigreturn,
			}
			for _, syscall := range allowSyscalls {
				if err := ctx.AddRule(syscall, seccomp.ActAllow); err != nil {
					syscall.Exit(1)
				}
			}

			// 加载过滤器到内核
			if err := ctx.Load(); err != nil {
				syscall.Exit(1)
			}
		},
	}

	// 绑定标准输入输出
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// 运行命令
	if err := cmd.Run(); err != nil {
		panic(err)
	}
}

关键注意点

  • PreExec函数运行在子进程环境中,不能使用任何依赖Go Runtime的功能(比如fmt、log、goroutine等),因为fork后的子进程没有初始化Go Runtime,这类操作会直接导致崩溃。出错时只能用syscall.Exit退出。
  • 这个方案可以很容易封装成库函数,供其他代码调用,完全符合沙箱模块的需求。

方法二:手动调用syscall.ForkExec

如果你想完全控制fork/exec流程,更贴近你给出的C语言例子,可以手动调用syscall.Fork和syscall.Exec,在子进程中加载seccomp规则后再执行目标命令。

示例代码:

package main

import (
	"os"
	"syscall"

	"github.com/seccomp/libseccomp-golang"
)

func main() {
	// 找到目标命令的路径
	bashPath, err := exec.LookPath("bash")
	if err != nil {
		panic(err)
	}
	cmdArgs := []string{"bash"}

	// Fork子进程
	pid, err := syscall.Fork()
	if err != nil {
		panic(err)
	}

	if pid == 0 {
		// 子进程:加载seccomp规则
		ctx, err := seccomp.NewFilter(seccomp.ActKill.Log())
		if err != nil {
			syscall.Exit(1)
		}
		defer ctx.Release()

		// 添加允许的系统调用
		if err := ctx.AddRule(seccomp.SysRead, seccomp.ActAllow); err != nil {
			syscall.Exit(1)
		}
		if err := ctx.AddRule(seccomp.SysWrite, seccomp.ActAllow); err != nil {
			syscall.Exit(1)
		}
		if err := ctx.AddRule(seccomp.SysExit, seccomp.ActAllow); err != nil {
			syscall.Exit(1)
		}
		if err := ctx.AddRule(seccomp.SysSigreturn, seccomp.ActAllow); err != nil {
			syscall.Exit(1)
		}

		if err := ctx.Load(); err != nil {
			syscall.Exit(1)
		}

		// 执行目标命令
		if err := syscall.Exec(bashPath, cmdArgs, os.Environ()); err != nil {
			syscall.Exit(1)
		}
	} else {
		// 父进程:等待子进程结束,出错时强制杀死
		var status syscall.WaitStatus
		_, err := syscall.Wait4(pid, &status, 0, nil)
		if err != nil {
			syscall.Kill(pid, syscall.SIGKILL)
		}
	}
}

适用场景

这种方式完全脱离os/exec的封装,适合需要更精细控制进程生命周期的场景,比如自定义等待逻辑、资源监控等。

封装成沙箱模块

如果你要把这个功能做成可复用的沙箱模块,可以把加载规则的逻辑封装成函数:

import (
	"os/exec"
	"syscall"

	"github.com/seccomp/libseccomp-golang"
)

// RunCommandWithSeccomp 运行带seccomp限制的命令
func RunCommandWithSeccomp(cmdPath string, args []string) error {
	cmd := exec.Command(cmdPath, args...)
	cmd.SysProcAttr = &syscall.SysProcAttr{
		PreExec: setupSeccompFilter,
	}
	return cmd.Run()
}

// setupSeccompFilter 加载seccomp规则,只能在子进程中调用
func setupSeccompFilter() {
	ctx, err := seccomp.NewFilter(seccomp.ActKill.Log())
	if err != nil {
		syscall.Exit(1)
	}
	defer ctx.Release()

	allowSyscalls := []seccomp.Syscall{
		seccomp.SysRead,
		seccomp.SysWrite,
		seccomp.SysExit,
		seccomp.SysSigreturn,
	}
	for _, syscall := range allowSyscalls {
		if err := ctx.AddRule(syscall, seccomp.ActAllow); err != nil {
			syscall.Exit(1)
		}
	}

	if err := ctx.Load(); err != nil {
		syscall.Exit(1)
	}
}

其他代码只需要调用RunCommandWithSeccomp,就能直接运行带seccomp限制的命令,完全不需要修改主函数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:46:07