mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-01 09:32:08 +00:00
cmd/tailscale/web: add support for QNAP
Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
21
util/groupmember/groupmember.go
Normal file
21
util/groupmember/groupmember.go
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package groupmemeber verifies group membership of the provided user on the
|
||||
// local system.
|
||||
package groupmember
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var ErrNotImplemented = errors.New("not implemented for GOOS=" + runtime.GOOS)
|
||||
|
||||
// IsMemberOfGroup verifies if the provided user is member of the provided
|
||||
// system group.
|
||||
// If verfication fails, an error is returned.
|
||||
func IsMemberOfGroup(group, userName string) (bool, error) {
|
||||
return isMemberOfGroup(group, userName)
|
||||
}
|
||||
48
util/groupmember/groupmember_cgo.go
Normal file
48
util/groupmember/groupmember_cgo.go
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build cgo
|
||||
|
||||
package groupmember
|
||||
|
||||
import (
|
||||
"os/user"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func isMemberOfGroup(group, name string) (bool, error) {
|
||||
u, err := user.Lookup(name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
ugids, err := u.GroupIds()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
gid, err := getGroupID(group)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, ugid := range ugids {
|
||||
if gid == ugid {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
var groupIDCache sync.Map // of string
|
||||
|
||||
func getGroupID(groupName string) (string, error) {
|
||||
s, ok := groupIDCache.Load(groupName)
|
||||
if ok {
|
||||
return s.(string), nil
|
||||
}
|
||||
g, err := user.LookupGroup(groupName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
groupIDCache.Store(groupName, g.Gid)
|
||||
return g.Gid, nil
|
||||
}
|
||||
9
util/groupmember/groupmember_noimpl.go
Normal file
9
util/groupmember/groupmember_noimpl.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !cgo,!linux,!darwin
|
||||
|
||||
package groupmember
|
||||
|
||||
func isMemberOfGroup(group, name string) (bool, error) { return false, ErrNotImplemented }
|
||||
71
util/groupmember/groupmember_notcgo.go
Normal file
71
util/groupmember/groupmember_notcgo.go
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !cgo
|
||||
// +build linux darwin
|
||||
|
||||
package groupmember
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"go4.org/mem"
|
||||
"tailscale.com/version/distro"
|
||||
)
|
||||
|
||||
func isMemberOfGroup(group, name string) (bool, error) {
|
||||
if distro.Get() == distro.Synology {
|
||||
return isMemberOfGroupEtcGroup(group, name)
|
||||
}
|
||||
cmd := exec.Command("/usr/bin/env", "groups", name)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
groups := strings.Split(strings.TrimSpace(string(out)), " ")
|
||||
for _, g := range groups {
|
||||
if g == group {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func isMemberOfGroupEtcGroup(group, name string) (bool, error) {
|
||||
f, err := os.Open("/etc/group")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer f.Close()
|
||||
s := bufio.NewScanner(f)
|
||||
var agLine string
|
||||
for s.Scan() {
|
||||
if !mem.HasPrefix(mem.B(s.Bytes()), mem.S(fmt.Sprintf("%s:", group))) {
|
||||
continue
|
||||
}
|
||||
agLine = s.Text()
|
||||
break
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if agLine == "" {
|
||||
return false, fmt.Errorf("admin group not defined")
|
||||
}
|
||||
agEntry := strings.Split(agLine, ":")
|
||||
if len(agEntry) < 4 {
|
||||
return false, fmt.Errorf("malformed admin group entry")
|
||||
}
|
||||
agMembers := agEntry[3]
|
||||
for _, m := range strings.Split(agMembers, ",") {
|
||||
if m == name {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
Reference in New Issue
Block a user