2016-12-20 32 views
1

我想通过golang制作一个自定义的网络服务器。 它需要根绑定到端口80. 但是我想尽快删除根目录。 syscall.SetUid()根据ticket #1435返回“不支持”。Golang下降权限(v1.7)

我随时可以重新路由端口80通过iptables的别的东西,但是这开辟了任何非根进程伪装成我的网络服务器 - 我不想成为可能。

如何放弃特权我的应用程序(或可替换地解决这个干净)。

+3

的优选方法是使用Linux功能仅允许程序绑定到正确的端口,而无需完整的根的能力。 – JimB

+1

您链接到的问题实际上有一些可能的解决方法/解决方案IIRC。 – Carpetsmoker

+0

Setuid没有像预期的那样为golang工作。这些解决方法是使其行为更加一致。然而,最终他们并不能保证成功,这意味着在setuid调用之后,你仍然可能最终成为root。这就是为什么决定完全禁用该功能的原因。关于linux-capabilities:你在这里建议的是什么? – user2089648

回答

1

我会做什么@JimB建议。

在另一方面,在Linux上还有另外一个诀窍:您可以使用os/exec.Command()执行/proc/self/exe同时告诉它在它生成的​​3210实例的SysProcAttr.Credential领域使用替代凭据。

参见go doc os/exec.Cmd,go doc syscall.SysProcAttrgo doc syscall.Credential

确保当您让程序自行重新执行时,您需要确保生成的程序将其标准I/O流连接到其父文件的标准I/O流,并且所有必要的打开文件也都将被继承。


另一个alternatve值得一提的是不要尝试绑定到端口80的一切,有一个正确的Web服务器挂在那里,然后反向代理无论是基于主机的虚拟主机或特定的URL路径前缀(或前缀)到您的Go进程侦听任何TCP或Unix套接字。 Apache(至少2.4)和Nginx都可以轻松完成。

0

最近,我通过这个问题的工作,并Go有你需要的所有作品。在这个示例中,我更进一步并实施了SSL。本质上,您打开端口,检测UID,如果它是0,请查找所需的用户,获取UID,然后使用glibc调用来设置进程的UID和GID。我可能会强调最好在绑定端口后立即调用setuid代码。删除权限最大的区别是您不能使用http.ListenAndServe(TLS)?辅助函数 - 你必须手动设置你的net.Listener,然后在绑定端口后调用setuid,但是在调用http.Serve之前。

做的这种方式效果很好,因为你可以,例如,作为运行UID!= 0在高端口“发展”的模式,没有进一步的考虑。请记住,这只是一个存根 - 我建议在配置文件中设置地址,端口,用户,组和TLS文件名。

package main 

import (
    "crypto/tls" 
    "log" 
    "net/http" 
    "os/user" 
    "strconv" 
    "syscall" 
) 

import (
    //#include <unistd.h> 
    //#include <errno.h> 
    "C" 
) 

func main() { 
    cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem") 
    if err != nil { 
     log.Fatalln("Can't load certificates!", err) 
    } 
    var tlsconf tls.Config 
    tlsconf.Certificates = make([]tls.Certificate, 1) 
    tlsconf.Certificates[0] = cert 
    listener, err := tls.Listen("tcp4", "127.0.0.1:445", &tlsconf) 
    if err != nil { 
     log.Fatalln("Error opening port:", err) 
    } 
    if syscall.Getuid() == 0 { 
     log.Println("Running as root, downgrading to user www-data") 
     user, err := user.Lookup("www-data") 
     if err != nil { 
      log.Fatalln("User not found or other error:", err) 
     } 
     // TODO: Write error handling for int from string parsing 
     uid, _ := strconv.ParseInt(user.Uid, 10, 32) 
     gid, _ := strconv.ParseInt(user.Gid, 10, 32) 
     cerr, errno := C.setgid(C.__gid_t(gid)) 
     if cerr != 0 { 
      log.Fatalln("Unable to set GID due to error:", errno) 
     } 
     cerr, errno = C.setuid(C.__uid_t(uid)) 
     if cerr != 0 { 
      log.Fatalln("Unable to set UID due to error:", errno) 
     } 
    } 
    http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { 
     w.Write([]byte("Hello, world!")) 
    }) 
    err = http.Serve(listener, nil) 
    log.Fatalln(err) 
}