mirror of https://github.com/tailscale/tailscale/
doctor/permissions: add new check to print process permissions
Since users can run tailscaled in a variety of ways (root, non-root, non-root with process capabilities on Linux), this check will print the current process permissions to the log to aid in debugging. Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: Ida93a206123f98271a0c664775d0baba98b330c7pull/7727/head
parent
524f53de89
commit
c98652c333
@ -0,0 +1,56 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Package permissions provides a doctor.Check that prints the process
|
||||||
|
// permissions for the running process.
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os/user"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
"tailscale.com/types/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check implements the doctor.Check interface.
|
||||||
|
type Check struct{}
|
||||||
|
|
||||||
|
func (Check) Name() string {
|
||||||
|
return "permissions"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Check) Run(_ context.Context, logf logger.Logf) error {
|
||||||
|
return permissionsImpl(logf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatUserID[T constraints.Integer](id T) string {
|
||||||
|
idStr := fmt.Sprint(id)
|
||||||
|
if uu, err := user.LookupId(idStr); err != nil {
|
||||||
|
return idStr + "(<unknown>)"
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("%s(%q)", idStr, uu.Username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatGroupID[T constraints.Integer](id T) string {
|
||||||
|
idStr := fmt.Sprint(id)
|
||||||
|
if g, err := user.LookupGroupId(idStr); err != nil {
|
||||||
|
return idStr + "(<unknown>)"
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("%s(%q)", idStr, g.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatGroups[T constraints.Integer](groups []T) string {
|
||||||
|
var buf strings.Builder
|
||||||
|
for i, group := range groups {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteByte(',')
|
||||||
|
}
|
||||||
|
buf.WriteString(formatGroupID(group))
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
//go:build darwin || freebsd || openbsd
|
||||||
|
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"tailscale.com/types/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func permissionsImpl(logf logger.Logf) error {
|
||||||
|
groups, _ := unix.Getgroups()
|
||||||
|
logf("uid=%s euid=%s gid=%s egid=%s groups=%s",
|
||||||
|
formatUserID(unix.Getuid()),
|
||||||
|
formatUserID(unix.Geteuid()),
|
||||||
|
formatGroupID(unix.Getgid()),
|
||||||
|
formatGroupID(unix.Getegid()),
|
||||||
|
formatGroups(groups),
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
//go:build linux
|
||||||
|
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"tailscale.com/types/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func permissionsImpl(logf logger.Logf) error {
|
||||||
|
// NOTE: getresuid and getresgid never fail unless passed an
|
||||||
|
// invalid address.
|
||||||
|
var ruid, euid, suid uint64
|
||||||
|
unix.Syscall(unix.SYS_GETRESUID,
|
||||||
|
uintptr(unsafe.Pointer(&ruid)),
|
||||||
|
uintptr(unsafe.Pointer(&euid)),
|
||||||
|
uintptr(unsafe.Pointer(&suid)),
|
||||||
|
)
|
||||||
|
|
||||||
|
var rgid, egid, sgid uint64
|
||||||
|
unix.Syscall(unix.SYS_GETRESGID,
|
||||||
|
uintptr(unsafe.Pointer(&rgid)),
|
||||||
|
uintptr(unsafe.Pointer(&egid)),
|
||||||
|
uintptr(unsafe.Pointer(&sgid)),
|
||||||
|
)
|
||||||
|
|
||||||
|
groups, _ := unix.Getgroups()
|
||||||
|
|
||||||
|
var buf strings.Builder
|
||||||
|
fmt.Fprintf(&buf, "ruid=%s euid=%s suid=%s rgid=%s egid=%s sgid=%s groups=%s",
|
||||||
|
formatUserID(ruid), formatUserID(euid), formatUserID(suid),
|
||||||
|
formatGroupID(rgid), formatGroupID(egid), formatGroupID(sgid),
|
||||||
|
formatGroups(groups),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get process capabilities
|
||||||
|
var (
|
||||||
|
capHeader = unix.CapUserHeader{
|
||||||
|
Version: unix.LINUX_CAPABILITY_VERSION_3,
|
||||||
|
Pid: 0, // 0 means 'ourselves'
|
||||||
|
}
|
||||||
|
capData unix.CapUserData
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := unix.Capget(&capHeader, &capData); err != nil {
|
||||||
|
fmt.Fprintf(&buf, " caperr=%v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(&buf, " cap_effective=%08x cap_permitted=%08x cap_inheritable=%08x",
|
||||||
|
capData.Effective, capData.Permitted, capData.Inheritable,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
logf("%s", buf.String())
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
//go:build !(linux || darwin || freebsd || openbsd)
|
||||||
|
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"tailscale.com/types/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func permissionsImpl(logf logger.Logf) error {
|
||||||
|
logf("unsupported on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestPermissionsImpl(t *testing.T) {
|
||||||
|
if err := permissionsImpl(t.Logf); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue