*(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
This commit is contained in:
parent
49a92605b8
commit
b43223d302
70
home/home.go
70
home/home.go
|
@ -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()
|
||||||
|
@ -334,36 +330,54 @@ func StartMods() error {
|
||||||
|
|
||||||
// Check if the current user has root (administrator) rights
|
// Check if the current user has root (administrator) rights
|
||||||
// and if not, ask and try to run as root
|
// and if not, ask and try to run as root
|
||||||
func requireAdminRights() {
|
func checkPermissions() {
|
||||||
admin, _ := util.HaveAdminRights()
|
log.Info("Checking if AdGuard Home has necessary permissions")
|
||||||
if //noinspection ALL
|
|
||||||
admin || isdelve.Enabled {
|
if runtime.GOOS == "windows" {
|
||||||
// Don't forget that for this to work you need to add "delve" tag explicitly
|
// On Windows we need to have admin rights to run properly
|
||||||
// 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
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue