我最近解决了这个问题,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)
}
Run Code Online (Sandbox Code Playgroud)
我会按照@JimB的建议去做:
首选方法是使用 Linux 功能,只允许您的程序绑定到正确的端口,而不具有完整的 root 功能。
另一方面,在 Linux 上还有另一个技巧:您可以使用os/exec.Command()来执行,同时告诉它在它生成的实例字段/proc/self/exe中使用替代凭据。SysProcAttr.Credentialos/exec.Cmd
参见go doc os/exec.Cmd、go doc syscall.SysProcAttr和go doc syscall.Credential。
确保当您让程序重新执行自身时,您需要确保生成的程序将其标准 I/O 流连接到其父程序的标准 I/O 流,并且所有必需的打开的文件也会继承。
另一个值得一提的替代方案是根本不尝试绑定到端口 80 并在那里挂起一个适当的 Web 服务器,然后将基于主机名的虚拟主机或特定的 URL 路径前缀(或多个前缀)反向代理到您的 Go 进程监听任何 TCP 或 Unix 套接字。Apache(至少 2.4)和 Nginx 都可以轻松做到这一点。
| 归档时间: |
|
| 查看次数: |
4039 次 |
| 最近记录: |