mirror of
https://github.com/fosrl/newt.git
synced 2026-03-03 01:06:44 +00:00
Check permissions
This commit is contained in:
12
clients.go
12
clients.go
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/fosrl/newt/clients"
|
"github.com/fosrl/newt/clients"
|
||||||
wgnetstack "github.com/fosrl/newt/clients"
|
wgnetstack "github.com/fosrl/newt/clients"
|
||||||
|
"github.com/fosrl/newt/clients/permissions"
|
||||||
"github.com/fosrl/newt/logger"
|
"github.com/fosrl/newt/logger"
|
||||||
"github.com/fosrl/newt/netstack2"
|
"github.com/fosrl/newt/netstack2"
|
||||||
"github.com/fosrl/newt/websocket"
|
"github.com/fosrl/newt/websocket"
|
||||||
@@ -28,6 +29,17 @@ func setupClients(client *websocket.Client) {
|
|||||||
host = strings.TrimSuffix(host, "/")
|
host = strings.TrimSuffix(host, "/")
|
||||||
|
|
||||||
logger.Info("Setting up clients with netstack2...")
|
logger.Info("Setting up clients with netstack2...")
|
||||||
|
|
||||||
|
// if useNativeInterface is true make sure we have permission to use native interface
|
||||||
|
if useNativeInterface {
|
||||||
|
logger.Debug("Checking permissions for native interface")
|
||||||
|
err := permissions.CheckNativeInterfacePermissions()
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("Insufficient permissions to create native TUN interface: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create WireGuard service
|
// Create WireGuard service
|
||||||
wgService, err = wgnetstack.NewWireGuardService(interfaceName, mtuInt, host, id, client, dns, useNativeInterface)
|
wgService, err = wgnetstack.NewWireGuardService(interfaceName, mtuInt, host, id, client, dns, useNativeInterface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
18
clients/permissions/permissions_darwin.go
Normal file
18
clients/permissions/permissions_darwin.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//go:build darwin
|
||||||
|
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckNativeInterfacePermissions checks if the process has sufficient
|
||||||
|
// permissions to create a native TUN interface on macOS.
|
||||||
|
// This typically requires root privileges.
|
||||||
|
func CheckNativeInterfacePermissions() error {
|
||||||
|
if os.Geteuid() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("insufficient permissions: need root to create TUN interface on macOS")
|
||||||
|
}
|
||||||
96
clients/permissions/permissions_linux.go
Normal file
96
clients/permissions/permissions_linux.go
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
//go:build linux
|
||||||
|
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/fosrl/newt/logger"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TUN device constants
|
||||||
|
tunDevice = "/dev/net/tun"
|
||||||
|
ifnamsiz = 16
|
||||||
|
iffTun = 0x0001
|
||||||
|
iffNoPi = 0x1000
|
||||||
|
tunSetIff = 0x400454ca
|
||||||
|
)
|
||||||
|
|
||||||
|
// ifReq is the structure for TUNSETIFF ioctl
|
||||||
|
type ifReq struct {
|
||||||
|
Name [ifnamsiz]byte
|
||||||
|
Flags uint16
|
||||||
|
_ [22]byte // padding to match kernel structure
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckNativeInterfacePermissions checks if the process has sufficient
|
||||||
|
// permissions to create a native TUN interface on Linux.
|
||||||
|
// This requires either root privileges (UID 0) or CAP_NET_ADMIN capability.
|
||||||
|
func CheckNativeInterfacePermissions() error {
|
||||||
|
logger.Debug("Checking native interface permissions on Linux")
|
||||||
|
|
||||||
|
// Check if running as root
|
||||||
|
if os.Geteuid() == 0 {
|
||||||
|
logger.Debug("Running as root, sufficient permissions for native TUN interface")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for CAP_NET_ADMIN capability
|
||||||
|
caps := unix.CapUserHeader{
|
||||||
|
Version: unix.LINUX_CAPABILITY_VERSION_3,
|
||||||
|
Pid: 0, // 0 means current process
|
||||||
|
}
|
||||||
|
|
||||||
|
var data [2]unix.CapUserData
|
||||||
|
if err := unix.Capget(&caps, &data[0]); err != nil {
|
||||||
|
logger.Debug("Failed to get capabilities: %v, will try creating test TUN", err)
|
||||||
|
} else {
|
||||||
|
// CAP_NET_ADMIN is capability bit 12
|
||||||
|
const CAP_NET_ADMIN = 12
|
||||||
|
if data[0].Effective&(1<<CAP_NET_ADMIN) != 0 {
|
||||||
|
logger.Debug("Process has CAP_NET_ADMIN capability, sufficient permissions for native TUN interface")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
logger.Debug("Process does not have CAP_NET_ADMIN capability in effective set")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actually try to create a TUN interface to verify permissions
|
||||||
|
// This is the most reliable check as it tests the actual operation
|
||||||
|
return tryCreateTestTun()
|
||||||
|
}
|
||||||
|
|
||||||
|
// tryCreateTestTun attempts to create a temporary TUN interface to verify
|
||||||
|
// we have the necessary permissions. This tests the actual ioctl call that
|
||||||
|
// will be used when creating the real interface.
|
||||||
|
func tryCreateTestTun() error {
|
||||||
|
f, err := os.OpenFile(tunDevice, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot open %s: %v (need root or CAP_NET_ADMIN capability)", tunDevice, err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
// Try to create a TUN interface with a test name
|
||||||
|
// Using a random-ish name to avoid conflicts
|
||||||
|
var req ifReq
|
||||||
|
copy(req.Name[:], "tuntest0")
|
||||||
|
req.Flags = iffTun | iffNoPi
|
||||||
|
|
||||||
|
_, _, errno := unix.Syscall(
|
||||||
|
unix.SYS_IOCTL,
|
||||||
|
f.Fd(),
|
||||||
|
uintptr(tunSetIff),
|
||||||
|
uintptr(unsafe.Pointer(&req)),
|
||||||
|
)
|
||||||
|
|
||||||
|
if errno != 0 {
|
||||||
|
return fmt.Errorf("cannot create TUN interface (ioctl TUNSETIFF failed): %v (need root or CAP_NET_ADMIN capability)", errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success - the interface will be automatically destroyed when we close the fd
|
||||||
|
logger.Debug("Successfully created test TUN interface, sufficient permissions for native TUN interface")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
38
clients/permissions/permissions_windows.go
Normal file
38
clients/permissions/permissions_windows.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckNativeInterfacePermissions checks if the process has sufficient
|
||||||
|
// permissions to create a native TUN interface on Windows.
|
||||||
|
// This requires Administrator privileges.
|
||||||
|
func CheckNativeInterfacePermissions() error {
|
||||||
|
var sid *windows.SID
|
||||||
|
err := windows.AllocateAndInitializeSid(
|
||||||
|
&windows.SECURITY_NT_AUTHORITY,
|
||||||
|
2,
|
||||||
|
windows.SECURITY_BUILTIN_DOMAIN_RID,
|
||||||
|
windows.DOMAIN_ALIAS_RID_ADMINS,
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
&sid)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to initialize SID: %v", err)
|
||||||
|
}
|
||||||
|
defer windows.FreeSid(sid)
|
||||||
|
|
||||||
|
token := windows.Token(0)
|
||||||
|
member, err := token.IsMember(sid)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to check admin group membership: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !member {
|
||||||
|
return fmt.Errorf("insufficient permissions: need Administrator to create TUN interface on Windows")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
2
main.go
2
main.go
@@ -236,7 +236,7 @@ func main() {
|
|||||||
flag.StringVar(&interfaceName, "interface", "newt", "Name of the WireGuard interface")
|
flag.StringVar(&interfaceName, "interface", "newt", "Name of the WireGuard interface")
|
||||||
}
|
}
|
||||||
if useNativeInterfaceEnv == "" {
|
if useNativeInterfaceEnv == "" {
|
||||||
flag.BoolVar(&useNativeInterface, "native", false, "Use native WireGuard interface (requires WireGuard kernel module) and linux")
|
flag.BoolVar(&useNativeInterface, "native", false, "Use native WireGuard interface")
|
||||||
}
|
}
|
||||||
if disableClientsEnv == "" {
|
if disableClientsEnv == "" {
|
||||||
flag.BoolVar(&disableClients, "disable-clients", false, "Disable clients on the WireGuard interface")
|
flag.BoolVar(&disableClients, "disable-clients", false, "Disable clients on the WireGuard interface")
|
||||||
|
|||||||
Reference in New Issue
Block a user