Drop privileges with exec (#467)
* Drop privileges with exec and SysProcAttr * Fix windows build * Fix passing logfile fd
This commit is contained in:
parent
8d99e95288
commit
aab7e6380f
|
@ -9,6 +9,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"os"
|
||||
)
|
||||
|
||||
type CryptoConstruction uint16
|
||||
|
@ -36,6 +37,11 @@ var (
|
|||
InitialMinQuestionSize = 256
|
||||
)
|
||||
|
||||
var (
|
||||
FileDescriptors = make([]*os.File, 0)
|
||||
FileDescriptorNum = 0
|
||||
)
|
||||
|
||||
func PrefixWithSize(packet []byte) ([]byte, error) {
|
||||
packetLen := len(packet)
|
||||
if packetLen > 0xffff {
|
||||
|
|
|
@ -26,6 +26,7 @@ type Config struct {
|
|||
ServerNames []string `toml:"server_names"`
|
||||
ListenAddresses []string `toml:"listen_addresses"`
|
||||
Daemonize bool
|
||||
Username string `toml:"username"`
|
||||
ForceTCP bool `toml:"force_tcp"`
|
||||
Timeout int `toml:"timeout"`
|
||||
KeepAlive int `toml:"keepalive"`
|
||||
|
@ -186,6 +187,8 @@ func ConfigLoad(proxy *Proxy, svcFlag *string) error {
|
|||
jsonOutput := flag.Bool("json", false, "output list as JSON")
|
||||
check := flag.Bool("check", false, "check the configuration file and exit")
|
||||
configFile := flag.String("config", DefaultConfigFileName, "Path to the configuration file")
|
||||
username := flag.String("username", "", "After binding to the port user privileges are dropped")
|
||||
child := flag.Bool("start-child", false, "Invokes program as a child process")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
|
@ -225,11 +228,22 @@ func ConfigLoad(proxy *Proxy, svcFlag *string) error {
|
|||
dlog.UseSyslog(true)
|
||||
} else if config.LogFile != nil {
|
||||
dlog.UseLogFile(*config.LogFile)
|
||||
if !*child {
|
||||
FileDescriptors = append(FileDescriptors, dlog.GetFileDescriptor())
|
||||
} else {
|
||||
FileDescriptorNum++
|
||||
dlog.SetFileDescriptor(os.NewFile(uintptr(3), "logFile"))
|
||||
}
|
||||
}
|
||||
proxy.logMaxSize = config.LogMaxSize
|
||||
proxy.logMaxAge = config.LogMaxAge
|
||||
proxy.logMaxBackups = config.LogMaxBackups
|
||||
|
||||
proxy.username = config.Username
|
||||
if len(*username) > 0 {
|
||||
proxy.username = *username
|
||||
}
|
||||
proxy.child = *child
|
||||
proxy.xTransport = NewXTransport()
|
||||
proxy.xTransport.tlsDisableSessionTickets = config.TLSDisableSessionTickets
|
||||
proxy.xTransport.tlsCipherSuite = config.TLSCipherSuite
|
||||
|
|
|
@ -40,6 +40,11 @@ listen_addresses = ['127.0.0.1:53', '[::1]:53']
|
|||
|
||||
max_clients = 250
|
||||
|
||||
## After binding to the port(s) user privileges are dropped by calling itself
|
||||
## via exec.command() with SysProcAttr.Credential()
|
||||
|
||||
username = 'nobody'
|
||||
|
||||
|
||||
## Require servers (from static + remote sources) to satisfy specific properties
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
// +build !windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"github.com/jedisct1/dlog"
|
||||
)
|
||||
|
||||
func (proxy *Proxy) dropPrivilege(userStr string, fds []*os.File) {
|
||||
user, err := user.Lookup(userStr)
|
||||
args := os.Args
|
||||
|
||||
if err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
uid, err := strconv.Atoi(user.Uid)
|
||||
if err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
gid, err := strconv.Atoi(user.Gid)
|
||||
if err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
exec_path, err := exec.LookPath(args[0])
|
||||
if err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
path, err := filepath.Abs(exec_path)
|
||||
if err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
|
||||
// remove arg[0]
|
||||
copy(args[0:], args[0+1:])
|
||||
args[len(args)-1] = ""
|
||||
args = args[:len(args)-1]
|
||||
args = append(args, "-start-child")
|
||||
|
||||
cmd := exec.Command(path, args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.ExtraFiles = fds
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
|
||||
dlog.Notice("Dropping privileges")
|
||||
if err := cmd.Start(); err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package main
|
||||
|
||||
import "os"
|
||||
|
||||
func (proxy *Proxy) dropPrivilege(userStr string, fds []*os.File) {}
|
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
|
@ -15,6 +16,8 @@ import (
|
|||
)
|
||||
|
||||
type Proxy struct {
|
||||
username string
|
||||
child bool
|
||||
proxyPublicKey [32]byte
|
||||
proxySecretKey [32]byte
|
||||
ephemeralKeys bool
|
||||
|
@ -71,6 +74,7 @@ func (proxy *Proxy) StartProxy() {
|
|||
for _, registeredServer := range proxy.registeredServers {
|
||||
proxy.serversInfo.registerServer(proxy, registeredServer.name, registeredServer.stamp)
|
||||
}
|
||||
|
||||
for _, listenAddrStr := range proxy.listenAddresses {
|
||||
listenUDPAddr, err := net.ResolveUDPAddr("udp", listenAddrStr)
|
||||
if err != nil {
|
||||
|
@ -80,13 +84,69 @@ func (proxy *Proxy) StartProxy() {
|
|||
if err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
if err := proxy.udpListenerFromAddr(listenUDPAddr); err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
if err := proxy.tcpListenerFromAddr(listenTCPAddr); err != nil {
|
||||
dlog.Fatal(err)
|
||||
|
||||
// if 'username' is not set, continue as before (Todo: refactor for DRYniss)
|
||||
if !(len(proxy.username) > 0) {
|
||||
if err := proxy.udpListenerFromAddr(listenUDPAddr); err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
if err := proxy.tcpListenerFromAddr(listenTCPAddr); err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
// if 'username' is set and we are the parent process
|
||||
if !proxy.child {
|
||||
// parent
|
||||
listenerUDP, err := net.ListenUDP("udp", listenUDPAddr)
|
||||
if err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
listenerTCP, err := net.ListenTCP("tcp", listenTCPAddr)
|
||||
if err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
|
||||
fdUDP, err := listenerUDP.File() // On Windows, the File method of UDPConn is not implemented.
|
||||
if err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
fdTCP, err := listenerTCP.File() // On Windows, the File method of TCPListener is not implemented.
|
||||
if err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
defer listenerUDP.Close()
|
||||
defer listenerTCP.Close()
|
||||
FileDescriptors = append(FileDescriptors, fdUDP)
|
||||
FileDescriptors = append(FileDescriptors, fdTCP)
|
||||
|
||||
// if 'username' is set and we are the child process
|
||||
} else {
|
||||
// child
|
||||
listenerUDP, err := net.FilePacketConn(os.NewFile(uintptr(3+FileDescriptorNum), "listenerUDP"))
|
||||
if err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
FileDescriptorNum++
|
||||
|
||||
listenerTCP, err := net.FileListener(os.NewFile(uintptr(3+FileDescriptorNum), "listenerTCP"))
|
||||
if err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
FileDescriptorNum++
|
||||
|
||||
dlog.Noticef("Now listening to %v [UDP]", listenUDPAddr)
|
||||
go proxy.udpListener(listenerUDP.(*net.UDPConn))
|
||||
|
||||
dlog.Noticef("Now listening to %v [TCP]", listenAddrStr)
|
||||
go proxy.tcpListener(listenerTCP.(*net.TCPListener))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if 'username' is set and we are the parent process drop privilege and exit
|
||||
if len(proxy.username) > 0 && !proxy.child {
|
||||
proxy.dropPrivilege(proxy.username, FileDescriptors)
|
||||
}
|
||||
if err := proxy.SystemDListeners(); err != nil {
|
||||
dlog.Fatal(err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue