Run Golang as www-data

前端 未结 4 1639
梦毁少年i
梦毁少年i 2021-01-24 21:54

When I run a Node HTTP server app I usually call a custom function

function runAsWWW()
{
 try 
 {
  process.setgid(\'www-data\');
  process.setuid(\'www-data\');         


        
4条回答
  •  轮回少年
    2021-01-24 22:08

    A way to achieve this safely would be to fork yourself.

    This is a raw untested example on how you could achieve safe setuid:

    1) Make sure you are root 2) Listen on the wanted port (as root) 3) Fork as www-data user. 4) Accept and serve requests.

    http://play.golang.org/p/sT25P0KxXK

    package main
    
    import (
            "flag"
            "fmt"
            "log"
            "net"
            "net/http"
            "os"
            "os/exec"
            "os/user"
            "strconv"
            "syscall"
    )
    
    var listenFD = flag.Int("l", 0, "listen pid")
    
    func handler(w http.ResponseWriter, req *http.Request) {
            u, err := user.Current()
            if err != nil {
                    log.Println(err)
                    return
            }
            fmt.Fprintf(w, "%s\n", u.Name)
    }
    
    func lookupUser(username string) (uid, gid int, err error) {
            u, err := user.Lookup(username)
            if err != nil {
                    return -1, -1, err
            }
            uid, err = strconv.Atoi(u.Uid)
            if err != nil {
                    return -1, -1, err
            }
            gid, err = strconv.Atoi(u.Gid)
            if err != nil {
                    return -1, -1, err
            }
            return uid, gid, nil
    }
    
    // FDListener .
    type FDListener struct {
            file *os.File
    }
    
    // Accept .
    func (ln *FDListener) Accept() (net.Conn, error) {
            fd, _, err := syscall.Accept(int(*listenFD))
            if err != nil {
                    return nil, err
            }
            conn, err := net.FileConn(os.NewFile(uintptr(fd), ""))
            if err != nil {
                    return nil, err
            }
            return conn.(*net.TCPConn), nil
    }
    
    // Close .
    func (ln *FDListener) Close() error {
            return ln.file.Close()
    }
    
    // Addr .
    func (ln *FDListener) Addr() net.Addr {
            return nil
    }
    
    func start() error {
            u, err := user.Current()
            if err != nil {
                    return err
            }
            if u.Uid != "0" && *listenFD == 0 {
                    // we are not root and we have no listen fd. Error.
                    return fmt.Errorf("need to run as root: %s", u.Uid)
            } else if u.Uid == "0" && *listenFD == 0 {
                    // we are root and we have no listen fd. Do the listen.
                    l, err := net.Listen("tcp", "0.0.0.0:80")
                    if err != nil {
                            return fmt.Errorf("Listen error: %s", err)
                    }
                    f, err := l.(*net.TCPListener).File()
                    if err != nil {
                            return err
                    }
    
                    uid, gid, err := lookupUser("guillaume")
                    if err != nil {
                            return err
                    }
                    // First extra file: fd == 3
                    cmd := exec.Command(os.Args[0], "-l", fmt.Sprint(3))
                    cmd.Stdin = os.Stdin
                    cmd.Stdout = os.Stdout
                    cmd.Stderr = os.Stderr
                    cmd.ExtraFiles = append(cmd.ExtraFiles, f)
                    cmd.SysProcAttr = &syscall.SysProcAttr{
                            Credential: &syscall.Credential{
                                    Uid: uint32(uid),
                                    Gid: uint32(gid),
                            },
                    }
                    if err := cmd.Run(); err != nil {
                            return fmt.Errorf("cmd.Run error: %s", err)
                    }
                    return nil
            } else if u.Uid != "0" && *listenFD != 0 {
                    // We are not root and we have a listen fd. Do the accept.
                    ln := &FDListener{file: os.NewFile(uintptr(*listenFD), "net")}
                    if err := http.Serve(ln, http.HandlerFunc(handler)); err != nil {
                            return err
                    }
            }
            return fmt.Errorf("setuid fail: %s, %d", u.Uid, *listenFD)
    }
    
    func main() {
            flag.Parse()
    
            if err := start(); err != nil {
                    log.Fatal(err)
            }
    }
    

提交回复
热议问题