Merge : *(home): do not require root privileges on the first run

* commit '7b2cc51e35bfb725f02e91cb991ddd099b81ee0c':
  fix function comment
  *(home): do not require root privileges on the first run
This commit is contained in:
Andrey Meshkov 2020-06-23 18:24:00 +03:00
commit 7d93457154
2 changed files with 64 additions and 30 deletions

View File

@ -1,23 +1,19 @@
package home package home
import ( import (
"bufio"
"context" "context"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"os/exec"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"sync" "sync"
"syscall" "syscall"
"time" "time"
@ -172,7 +168,7 @@ func run(args options) {
Context.firstRun = detectFirstRun() Context.firstRun = detectFirstRun()
if Context.firstRun { if Context.firstRun {
log.Info("This is the first time AdGuard Home is launched") log.Info("This is the first time AdGuard Home is launched")
requireAdminRights() checkPermissions()
} }
initConfig() initConfig()
@ -332,38 +328,55 @@ func StartMods() error {
return nil return nil
} }
// Check if the current user has root (administrator) rights // Check if the current user permissions are enough to run AdGuard Home
// and if not, ask and try to run as root func checkPermissions() {
func requireAdminRights() { log.Info("Checking if AdGuard Home has necessary permissions")
admin, _ := util.HaveAdminRights()
if //noinspection ALL if runtime.GOOS == "windows" {
admin || isdelve.Enabled { // On Windows we need to have admin rights to run properly
// 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 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 return
} }
if runtime.GOOS == "windows" { if opErr, ok := err.(*net.OpError); ok {
log.Fatal("This is the first launch of AdGuard Home. You must run it as Administrator.") if sysErr, ok := opErr.Err.(*os.SyscallError); ok {
if errno, ok := sysErr.Err.(syscall.Errno); ok && errno == syscall.EACCES {
msg := `Permission check failed.
} else { AdGuard Home is not allowed to bind to privileged ports (for instance, port 53).
log.Error("This is the first launch of AdGuard Home. You must run it as root.") 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] ") You have two options:
stdin := bufio.NewReader(os.Stdin) 1. Run AdGuard Home with root privileges
buf, _ := stdin.ReadString('\n') 2. On Linux you can grant the CAP_NET_BIND_SERVICE capability:
buf = strings.TrimSpace(buf) https://github.com/AdguardTeam/AdGuardHome/wiki/Getting-Started#running-without-superuser`
if buf != "y" {
os.Exit(1) 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 // Write PID to a file

21
util/net.go Normal file
View File

@ -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
}