From b43223d30226f70b4616c71429f93a38e89cb163 Mon Sep 17 00:00:00 2001 From: Andrey Meshkov Date: Tue, 23 Jun 2020 18:02:28 +0300 Subject: [PATCH 1/2] *(home): do not require root privileges on the first run Instead of requiring root privileges, we now check if AdGuard Home can bind to privileged ports. If it cannot, we suggest either running it with root privileges or grant CAP_NET_BIND_SERVICE capability. Please note, that on Windows we still require root access. Closes: #1699 --- home/home.go | 70 +++++++++++++++++++++++++++++++--------------------- util/net.go | 21 ++++++++++++++++ 2 files changed, 63 insertions(+), 28 deletions(-) create mode 100644 util/net.go diff --git a/home/home.go b/home/home.go index e93daa60..f73064c7 100644 --- a/home/home.go +++ b/home/home.go @@ -1,23 +1,19 @@ package home import ( - "bufio" "context" "crypto/tls" "crypto/x509" "fmt" - "io" "io/ioutil" "net" "net/http" "net/url" "os" - "os/exec" "os/signal" "path/filepath" "runtime" "strconv" - "strings" "sync" "syscall" "time" @@ -172,7 +168,7 @@ func run(args options) { Context.firstRun = detectFirstRun() if Context.firstRun { log.Info("This is the first time AdGuard Home is launched") - requireAdminRights() + checkPermissions() } initConfig() @@ -334,36 +330,54 @@ func StartMods() error { // Check if the current user has root (administrator) rights // and if not, ask and try to run as root -func requireAdminRights() { - admin, _ := util.HaveAdminRights() - if //noinspection ALL - admin || isdelve.Enabled { - // Don't forget that for this to work you need to add "delve" tag explicitly - // https://stackoverflow.com/questions/47879070/how-can-i-see-if-the-goland-debugger-is-running-in-the-program +func checkPermissions() { + log.Info("Checking if AdGuard Home has necessary permissions") + + if runtime.GOOS == "windows" { + // On Windows we need to have admin rights to run properly + + admin, _ := util.HaveAdminRights() + if //noinspection ALL + admin || isdelve.Enabled { + // Don't forget that for this to work you need to add "delve" tag explicitly + // https://stackoverflow.com/questions/47879070/how-can-i-see-if-the-goland-debugger-is-running-in-the-program + return + } + + log.Fatal("This is the first launch of AdGuard Home. You must run it as Administrator.") + } + + // We should check if AdGuard Home is able to bind to port 53 + ok, err := util.CanBindPort(53) + + if ok { + log.Info("AdGuard Home can bind to port 53") return } - if runtime.GOOS == "windows" { - log.Fatal("This is the first launch of AdGuard Home. You must run it as Administrator.") + if opErr, ok := err.(*net.OpError); ok { + if sysErr, ok := opErr.Err.(*os.SyscallError); ok { + if errno, ok := sysErr.Err.(syscall.Errno); ok && errno == syscall.EACCES { + msg := `Permission check failed. - } else { - log.Error("This is the first launch of AdGuard Home. You must run it as root.") +AdGuard Home is not allowed to bind to privileged ports (for instance, port 53). +Please note, that this is crucial for a server to be able to use privileged ports. - _, _ = io.WriteString(os.Stdout, "Do you want to start AdGuard Home as root user? [y/n] ") - stdin := bufio.NewReader(os.Stdin) - buf, _ := stdin.ReadString('\n') - buf = strings.TrimSpace(buf) - if buf != "y" { - os.Exit(1) +You have two options: +1. Run AdGuard Home with root privileges +2. On Linux you can grant the CAP_NET_BIND_SERVICE capability: +https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started#running-without-superuser` + + log.Fatal(msg) + } } - - cmd := exec.Command("sudo", os.Args...) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - _ = cmd.Run() - os.Exit(1) } + + msg := fmt.Sprintf(`AdGuard failed to bind to port 53 due to %v + +Please note, that this is crucial for a DNS server to be able to use that port.`, err) + + log.Info(msg) } // Write PID to a file diff --git a/util/net.go b/util/net.go new file mode 100644 index 00000000..e6d4d58e --- /dev/null +++ b/util/net.go @@ -0,0 +1,21 @@ +package util + +import ( + "fmt" + "net" +) + +// CanBindPort - checks if we can bind to this port or not +func CanBindPort(port int) (bool, error) { + addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:%d", port)) + if err != nil { + return false, err + } + + l, err := net.ListenTCP("tcp", addr) + if err != nil { + return false, err + } + _ = l.Close() + return true, nil +} From 7b2cc51e35bfb725f02e91cb991ddd099b81ee0c Mon Sep 17 00:00:00 2001 From: Andrey Meshkov Date: Tue, 23 Jun 2020 18:04:26 +0300 Subject: [PATCH 2/2] fix function comment --- home/home.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/home/home.go b/home/home.go index f73064c7..f5b9375f 100644 --- a/home/home.go +++ b/home/home.go @@ -328,8 +328,7 @@ func StartMods() error { return nil } -// Check if the current user has root (administrator) rights -// and if not, ask and try to run as root +// Check if the current user permissions are enough to run AdGuard Home func checkPermissions() { log.Info("Checking if AdGuard Home has necessary permissions")