3 回答

TA貢獻1828條經(jīng)驗 獲得超13個贊
不可能以不同的用戶身份運行 goroutine 或方法,因為它們都在與父進程相同的上下文中運行。Goroutines 相當于綠色線程,甚至不一定在每個例程中產(chǎn)生適當?shù)?OS 線程。
這個答案也可能取決于操作系統(tǒng),但我認為這也不適用于 Windows。

TA貢獻1829條經(jīng)驗 獲得超7個贊
是的,您可以使用 Linux 系統(tǒng)調(diào)用setuid
(不是內(nèi)置函數(shù)setuid
)來做到這一點。我剛剛發(fā)現(xiàn)了這個問題并認為它必須是可能的,因為我也在其他編程語言中使用它。所以我解決了我的問題,并想報告如何做到這一點。
但是,SJP 寫的關(guān)于線程的內(nèi)容是正確的,并且正是我的問題的答案,但由于線程問題,它不會解決您的問題。
但是回到代碼……您需要調(diào)用LockOSThread
以將當前的 go 例程修復到您當前正在執(zhí)行的線程中,并且您可以使用 syscall 更改上下文setuid
。
這是 Linux 的工作示例:
package main
import (
? ? ? ? "fmt"
? ? ? ? "log"
? ? ? ? "os"
? ? ? ? "runtime"
? ? ? ? "sync"
? ? ? ? "syscall"
? ? ? ? "time"
)
func printUID() {
? ? ? ? fmt.Printf("Real UID: %d\n", syscall.Getuid())
? ? ? ? fmt.Printf("Effective UID: %d\n", syscall.Geteuid())
}
func main() {
? ? ? ? printUID()
? ? ? ? var wg sync.WaitGroup
? ? ? ? wg.Add(2)
? ? ? ? go func(wg *sync.WaitGroup) {
? ? ? ? ? ? ? ? defer wg.Done()
? ? ? ? ? ? ? ? time.Sleep(2 * time.Second)
? ? ? ? ? ? ? ? printUID()
? ? ? ? }(&wg)
? ? ? ? go func(wg *sync.WaitGroup) {
? ? ? ? ? ? ? ? runtime.LockOSThread()
? ? ? ? ? ? ? ? defer runtime.UnlockOSThread()
? ? ? ? ? ? ? ? defer wg.Done()
? ? ? ? ? ? ? ? _, _, serr := syscall.Syscall(syscall.SYS_SETUID, 1, 0, 0)
? ? ? ? ? ? ? ? if serr != 0 {
? ? ? ? ? ? ? ? ? ? ? ? log.Fatal(serr)
? ? ? ? ? ? ? ? ? ? ? ? os.Exit(1)
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? printUID()
? ? ? ? }(&wg)
? ? ? ? wg.Wait()
? ? ? ? printUID()
}
operation not supported如果您使用,您將收到syscall.Setuid:
serr := syscall.Setuid(1)
代替
_, _, serr := syscall.Syscall(syscall.SYS_SETUID, 1, 0, 0)

TA貢獻2051條經(jīng)驗 獲得超10個贊
我沒有足夠的聲譽來真正評論那個。希望這提供了更多完整的工作示例,并且重要的是,演示了保持運行時不會混淆使用不同 UID 運行的線程。]
首先,嚴格按照您的要求進行操作需要一些技巧,而且并不是那么安全...
[Go 喜歡使用POSIX 語義進行操作,而您想做的是通過在單個進程中同時使用兩個或多個 UID 進行操作來打破 POSIX 語義。Go 需要 POSIX 語義,因為它在任何可用的線程上運行 goroutines,并且運行時需要它們都表現(xiàn)相同才能可靠地工作。由于 Linux 的setuid()
系統(tǒng)調(diào)用不支持 POSIX 語義,Go 選擇不實現(xiàn) syscall.Setuid()直到最近在 go1.16 中使用POSIX 語義實現(xiàn)它成為可能。
請注意,
glibc
如果您調(diào)用setuid()
,系統(tǒng)調(diào)用本身會使用修復機制 (?glibc/nptl/setxid ) 進行包裝,并且會同時更改程序中所有線程的 UID 值。因此,即使在 C 中,您也必須進行一些修改才能解決此細節(jié)。]
話雖這么說,你可以讓 goroutines 以你想要的方式工作runtime.LockOSThread()
,但不要在每次專門使用后立即丟棄鎖定的線程來混淆 Go 運行時。
像這樣的東西(稱之為uidserve.go
):
// Program uidserve serves content as different uids. This is adapted
// from the https://golang.org/pkg/net/http/#ListenAndServe example.
package main
import (
? ? ? ? "fmt"
? ? ? ? "log"
? ? ? ? "net/http"
? ? ? ? "runtime"
? ? ? ? "syscall"
)
// Simple username to uid mapping.
var prefixUIDs = map[string]uintptr{
? ? ? ? "apple":? 100,
? ? ? ? "banana": 101,
? ? ? ? "cherry": 102,
}
type uidRunner struct {
? ? ? ? uid uintptr
}
func (u *uidRunner) ServeHTTP(w http.ResponseWriter, r *http.Request) {
? ? ? ? runtime.LockOSThread()
? ? ? ? // Note, we never runtime.UnlockOSThread().
? ? ? ? if _, _, e := syscall.RawSyscall(syscall.SYS_SETUID, u.uid, 0, 0); e != 0 {
? ? ? ? ? ? ? ? http.Error(w, "permission problem", http.StatusInternalServerError)
? ? ? ? ? ? ? ? return
? ? ? ? }
? ? ? ? fmt.Fprintf(w, "query %q executing as UID=%d\n", r.URL.Path, syscall.Getuid())
}
func main() {
? ? ? ? for u, uid := range prefixUIDs {
? ? ? ? ? ? ? ? h := &uidRunner{uid: uid}
? ? ? ? ? ? ? ? http.Handle(fmt.Sprint("/", u, "/"), h)
? ? ? ? }
? ? ? ? http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
? ? ? ? ? ? ? ? fmt.Fprintf(w, "general query %q executing as UID=%d\n", r.URL.Path, syscall.Getuid())
? ? ? ? })
? ? ? ? log.Fatal(http.ListenAndServe(":8080", nil))
}
像這樣構(gòu)建它:
$ go build uidserve.go
接下來,要讓它工作,您必須授予該程序一些特權(quán)。那就是做一個或另一個(是libcap 套件setcap中的一個工具):
$ sudo /sbin/setcap cap_setuid=ep ./uidserve
或者,更傳統(tǒng)的運行setuid-root的方式:
$ sudo chown root ./uidserve
$ sudo chmod +s ./uidserve
現(xiàn)在,如果您運行./uidserve
并連接到您的瀏覽器,localhost:8080
您可以嘗試獲取以下 URL:
localhost:8080/something
在這里顯示類似general query "/something" executing as UID=
您的 UID 的內(nèi)容。localhost:8080/apple/pie
這顯示了類似的東西query "/apple/pie" executing as UID=100
。ETC。
希望這有助于說明如何按照您的要求進行操作。[但是,由于它涉及很多技巧,所以我不建議真正這樣做……]
- 3 回答
- 0 關(guān)注
- 193 瀏覽
添加回答
舉報