Skip to content

Commit

Permalink
Merge pull request #535 from Darkren/feature/run-vpn-server-non-root
Browse files Browse the repository at this point in the history
Run vpn server non root
  • Loading branch information
jdknives authored Oct 12, 2020
2 parents b76686c + 541f736 commit fb5aabd
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 120 deletions.
40 changes: 22 additions & 18 deletions internal/vpn/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,14 @@ const (

// Client is a VPN client.
type Client struct {
cfg ClientConfig
log logrus.FieldLogger
conn net.Conn
directIPSMu sync.Mutex
directIPs []net.IP
defaultGateway net.IP
sysPrivilegesMx sync.Mutex
closeC chan struct{}
closeOnce sync.Once
cfg ClientConfig
log logrus.FieldLogger
conn net.Conn
directIPSMu sync.Mutex
directIPs []net.IP
defaultGateway net.IP
closeC chan struct{}
closeOnce sync.Once
}

// NewClient creates VPN client instance.
Expand Down Expand Up @@ -112,7 +111,7 @@ func (c *Client) AddDirectRoute(ip net.IP) error {

c.directIPs = append(c.directIPs, ip)

suid, err := c.setupSysPrivileges()
suid, err := setupClientSysPrivileges()
if err != nil {
return fmt.Errorf("failed to setup system privileges: %w", err)
}
Expand Down Expand Up @@ -156,7 +155,7 @@ func (c *Client) Serve() error {
c.log.Infof("Local TUN IP: %s", tunIP.String())
c.log.Infof("Local TUN gateway: %s", tunGateway.String())

suid, err := c.setupSysPrivileges()
suid, err := setupClientSysPrivileges()
if err != nil {
return fmt.Errorf("failed to setup system privileges: %w", err)
}
Expand Down Expand Up @@ -200,8 +199,13 @@ func (c *Client) Serve() error {
return fmt.Errorf("error routing traffic through TUN %s: %w", tun.Name(), err)
}

// we release privileges here (user is not root for Mac OS systems from here on)suid
// we release privileges here (user is not root for Mac OS systems from here on)
c.releaseSysPrivileges(suid)
// this will be executed first on return, so we setup privileges once again,
// so other deferred clear up calls may be done successfully
if _, err := setupClientSysPrivileges(); err != nil {
c.log.WithError(err).Errorln("Failed to setup system privileges to clear up")
}

connToTunDoneCh := make(chan struct{})
tunToConnCh := make(chan struct{})
Expand All @@ -228,12 +232,6 @@ func (c *Client) Serve() error {
case <-c.closeC:
}

// we setup system privileges again here, so that the deferred call could perform clean up
suid, err = c.setupSysPrivileges()
if err != nil {
c.log.WithError(err).Errorln("Failed to setup system privileges for clean up")
}

return nil
}

Expand Down Expand Up @@ -447,6 +445,12 @@ func (c *Client) shakeHands() (TUNIP, TUNGateway net.IP, err error) {
return sHello.TUNIP, sHello.TUNGateway, nil
}

func (c *Client) releaseSysPrivileges(suid int) {
if err := releaseClientSysPrivileges(suid); err != nil {
c.log.WithError(err).Errorln("Failed to release system privileges")
}
}

func ipFromEnv(key string) (net.IP, error) {
ip, ok, err := IPFromEnv(key)
if err != nil {
Expand Down
28 changes: 0 additions & 28 deletions internal/vpn/client_darwin.go

This file was deleted.

53 changes: 0 additions & 53 deletions internal/vpn/client_linux.go

This file was deleted.

22 changes: 22 additions & 0 deletions internal/vpn/os_client_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"fmt"
"net"
"os/exec"
"sync"
"syscall"
)

const (
Expand Down Expand Up @@ -36,3 +38,23 @@ func DefaultNetworkGateway() (net.IP, error) {

return nil, errCouldFindDefaultNetworkGateway
}

var clientSysPrivilegesMx sync.Mutex

func setupClientSysPrivileges() (suid int, err error) {
clientSysPrivilegesMx.Lock()

suid = syscall.Getuid()

if err := syscall.Setuid(0); err != nil {
return 0, fmt.Errorf("failed to setuid 0: %w", err)
}

return suid, nil
}

func releaseClientSysPrivileges(suid int) error {
err := syscall.Setuid(suid)
clientSysPrivilegesMx.Unlock()
return err
}
44 changes: 44 additions & 0 deletions internal/vpn/os_client_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import (
"fmt"
"net"
"os/exec"
"sync"

"github.com/syndtr/gocapability/capability"
"golang.org/x/sys/unix"
)

const (
Expand Down Expand Up @@ -38,3 +42,43 @@ func DefaultNetworkGateway() (net.IP, error) {

return nil, errCouldFindDefaultNetworkGateway
}

var setupClientOnce sync.Once

func setupClientSysPrivileges() (suid int, err error) {
setupClientOnce.Do(func() {
var caps capability.Capabilities

caps, err = capability.NewPid2(0)
if err != nil {
err = fmt.Errorf("failed to init capabilities: %w", err)
return
}

err = caps.Load()
if err != nil {
err = fmt.Errorf("failed to load capabilities: %w", err)
return
}

// set `CAP_NET_ADMIN` capability to needed caps sets.
caps.Set(capability.CAPS|capability.BOUNDS|capability.AMBIENT, capability.CAP_NET_ADMIN)
if err := caps.Apply(capability.CAPS | capability.BOUNDS | capability.AMBIENT); err != nil {
err = fmt.Errorf("failed to apply capabilties: %w", err)
return
}

// let child process keep caps sets from the parent, so we may do calls to
// system utilities with these caps.
if err := unix.Prctl(unix.PR_SET_KEEPCAPS, 1, 0, 0, 0); err != nil {
err = fmt.Errorf("failed to set PR_SET_KEEPCAPS: %w", err)
return
}
})

return 0, nil
}

func releaseClientSysPrivileges(_ int) error {
return nil
}
8 changes: 8 additions & 0 deletions internal/vpn/os_client_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,11 @@ func DefaultNetworkGateway() (net.IP, error) {

return nil, errCouldFindDefaultNetworkGateway
}

func setupSysPrivileges() (suid int, err error) {
return 0, nil
}

func releaseSysPrivileges(suid int) {
return
}
6 changes: 0 additions & 6 deletions internal/vpn/os_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package vpn
import (
"fmt"
"strconv"
"syscall"
)

// SetupTUN sets the allocated TUN interface up, setting its IP, gateway, netmask and MTU.
Expand Down Expand Up @@ -37,8 +36,3 @@ func DeleteRoute(ipCIDR, gateway string) error {

return run("route", "delete", "-net", ip, gateway, netmask)
}

// Setuid sets uid of current OS user.
func Setuid(uid int) error {
return syscall.Setuid(uid)
}
7 changes: 0 additions & 7 deletions internal/vpn/os_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,3 @@ func DeleteRoute(ipCIDR, gateway string) error {
cmd := fmt.Sprintf(deleteRouteCMDFmt, ip, netmask, gateway)
return run("cmd", "/C", cmd)
}

// Setuid sets uid of current OS user.
func Setuid(_ int) (err error) {
// this is unsupported yet, but we allow app to be run with super user,
// so no privilege escalation, and no error here.
return nil
}
19 changes: 11 additions & 8 deletions internal/vpn/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ type Server struct {

// NewServer creates VPN server instance.
func NewServer(cfg ServerConfig, l logrus.FieldLogger) (*Server, error) {
s := &Server{
cfg: cfg,
log: l,
ipGen: NewIPGenerator(),
}

defaultNetworkIfc, err := DefaultNetworkInterface()
if err != nil {
return nil, fmt.Errorf("error getting default network interface: %w", err)
Expand All @@ -44,14 +50,11 @@ func NewServer(cfg ServerConfig, l logrus.FieldLogger) (*Server, error) {
l.Infoln("Old IP forwarding values:")
l.Infof("IPv4: %s, IPv6: %s", ipv4ForwardingVal, ipv6ForwardingVal)

return &Server{
cfg: cfg,
log: l,
ipGen: NewIPGenerator(),
defaultNetworkInterface: defaultNetworkIfc,
ipv4ForwardingVal: ipv4ForwardingVal,
ipv6ForwardingVal: ipv6ForwardingVal,
}, nil
s.defaultNetworkInterface = defaultNetworkIfc
s.ipv4ForwardingVal = ipv4ForwardingVal
s.ipv6ForwardingVal = ipv6ForwardingVal

return s, nil
}

// Serve accepts connections from `l` and serves them.
Expand Down

0 comments on commit fb5aabd

Please sign in to comment.