Я недавно проработал эту проблему, и у 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)
}
Предпочитаемый метод заключается в использовании возможностей linux, позволяющих вашей программе связываться с правильным портом, не имея полноценных возможностей root. – JimB
Эта проблема, с которой вы ссылаетесь, на самом деле имеет ряд возможных обходных решений/решений IIRC. – Carpetsmoker
Setuid не работал должным образом для golang. Эти обходные пути заключались в том, чтобы сделать его поведение более последовательным. Однако, в конце концов, они не гарантировали успех, а это значит, что вы все равно можете стать root после вызова setuid. Вот почему было принято решение полностью отключить эту функцию. О возможностях linux: Что именно вы предлагаете здесь? – user2089648