Golang中如何通过exec.Command在非root进程中使用sudo

Golang中如何通过exec.Command在非root进程中使用sudo 我目前正尝试从一个非root进程中,使用sudo执行一个新进程。然而,我不断收到以下错误信息:

sudo: /etc/sudo.conf is owned by uid 65534, should be 0
sudo: error in /etc/sudo.conf, line 0 while loading plugin "sudoers_policy"
sudo: /usr/libexec/sudo/sudoers.so must be owned by uid 0
sudo: fatal error, unable to load plugins

我使用以下代码片段来执行命令:

	cmd := exec.Command("sudo", os.Args[0], "--cgroup-manager=cgroupfs", "save", "--output", scpOpts.Save.Output)
	cmd.Env = os.Environ()
	outp, err := cmd.CombinedOutput()
	fmt.Println(string(outp))
	if err != nil {
		return err
	}

我觉得这是一个常见的用例。映射UID和GID也没有起作用。有什么建议吗?


更多关于Golang中如何通过exec.Command在非root进程中使用sudo的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

@christophberger 感谢您的反馈,我自己运行这些命令时它们工作正常。这个问题似乎与Red Hat的问题惊人地相似,但似乎还没有人找到解决方案。

更多关于Golang中如何通过exec.Command在非root进程中使用sudo的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


与其使用sudo来运行命令,不如利用Linux的SUID特性更为简便。您可以将文件的所有者设置为root,并在文件上设置一个标志,使其以所有者的身份执行。

搜索“linux suid privilege escalation”

// 代码部分保持原样

@precisionpete 的观点很好,使用SUID代替sudo可能是一个可行的变通方法。

当然,SUID通常的注意事项也适用。设置SUID位意味着文件始终以文件所有者的权限运行。非特权用户可能会滥用SUID二进制文件来获取提升的权限。因此,请确保没有人能滥用你的二进制文件来生成root shell或类似的东西。

回到最初的错误:错误信息提到了uid 65534,它属于用户nobody。我想知道sudo.conf或sudoers.so怎么可能被nobody拥有?你是在一个特殊的环境中工作吗?(虚拟机、容器或其他沙盒环境)

你好 @cdoern,

错误信息提示两个与sudo相关的文件必须由root用户(uid 0)拥有,但当前并非如此。

你能否在shell提示符下手动运行一个sudo命令?

如果这也失败了,我猜你至少需要检查错误信息中列出的那两个文件的所有权。

你错误信息中特定的UID(65534)表明你可能遇到了一个特定的与RedHat相关的问题,否则你可能需要查看Ask Ubuntu上的这个帖子

请告诉我这是否有帮助。

这是一个典型的容器环境中的sudo权限问题。错误表明sudo相关的配置文件所有权不正确,通常发生在容器内UID映射不匹配的情况下。

在容器环境中,当以非root用户运行但需要sudo权限时,可以通过以下几种方式解决:

1. 使用gosu替代sudo(推荐)

gosu是专门为容器设计的sudo替代方案,可以避免权限问题:

package main

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

func main() {
    // 使用gosu代替sudo
    cmd := exec.Command("gosu", "root", os.Args[0], "--cgroup-manager=cgroupfs", "save", "--output", "output.tar")
    cmd.Env = os.Environ()
    
    outp, err := cmd.CombinedOutput()
    fmt.Println(string(outp))
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        os.Exit(1)
    }
}

2. 直接设置capabilities(如果不需要完整root权限)

如果只需要特定权限,可以使用capabilities:

package main

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

func main() {
    cmd := exec.Command(os.Args[0], "--cgroup-manager=cgroupfs", "save", "--output", "output.tar")
    
    // 设置capabilities(需要容器有CAP_SETUID权限)
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Credential: &syscall.Credential{
            Uid: 0,
            Gid: 0,
        },
    }
    
    cmd.Env = os.Environ()
    outp, err := cmd.CombinedOutput()
    fmt.Println(string(outp))
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        os.Exit(1)
    }
}

3. 在Dockerfile中配置sudo(如果必须使用sudo)

如果必须使用sudo,需要在容器构建时正确配置:

# Dockerfile示例
FROM ubuntu:20.04

# 安装sudo并正确配置
RUN apt-get update && apt-get install -y sudo && \
    echo "ALL ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \
    chown root:root /etc/sudo.conf && \
    chmod 644 /etc/sudo.conf

# 添加用户并设置UID
RUN useradd -m -u 1000 appuser && \
    usermod -aG sudo appuser

USER appuser
WORKDIR /app
COPY . .

4. 使用nsenter进入主机命名空间

如果需要访问主机资源,可以使用nsenter:

package main

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

func main() {
    // 通过nsenter进入主机命名空间执行命令
    cmd := exec.Command("nsenter", "-t", "1", "-m", "-u", "-n", "-i", os.Args[0], 
        "--cgroup-manager=cgroupfs", "save", "--output", "output.tar")
    
    cmd.Env = os.Environ()
    outp, err := cmd.CombinedOutput()
    fmt.Println(string(outp))
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        os.Exit(1)
    }
}

5. 检查并修复sudo文件所有权

如果坚持使用sudo,可以在容器启动时修复文件所有权:

package main

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

func fixSudoPermissions() error {
    // 修复sudo配置文件所有权
    fixCmd := exec.Command("chown", "-R", "root:root", "/etc/sudo.conf", "/usr/libexec/sudo")
    return fixCmd.Run()
}

func main() {
    // 先修复权限
    if err := fixSudoPermissions(); err != nil {
        fmt.Printf("Failed to fix sudo permissions: %v\n", err)
    }
    
    // 然后执行sudo命令
    cmd := exec.Command("sudo", os.Args[0], "--cgroup-manager=cgroupfs", "save", "--output", "output.tar")
    cmd.Env = os.Environ()
    
    outp, err := cmd.CombinedOutput()
    fmt.Println(string(outp))
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        os.Exit(1)
    }
}

选择哪种方案取决于具体需求:

  • 如果只需要以root身份运行命令,使用gosu
  • 如果只需要特定权限,使用capabilities
  • 如果需要访问主机资源,使用nsenter
  • 如果必须使用sudo,确保容器内正确配置了sudo权限
回到顶部