Merge branch 'master' into 4990-custom-ciphers

This commit is contained in:
Ainar Garipov 2022-10-14 19:41:43 +03:00
commit a736f67205
109 changed files with 3936 additions and 2010 deletions
internal/home

View file

@ -10,6 +10,7 @@ import (
"net"
"net/http"
"net/http/pprof"
"net/netip"
"net/url"
"os"
"os/signal"
@ -58,7 +59,7 @@ type homeContext struct {
auth *Auth // HTTP authentication module
filters *filtering.DNSFilter // DNS filtering module
web *Web // Web (HTTP, HTTPS) module
tls *TLSMod // TLS module
tls *tlsManager // TLS module
// etcHosts is an IP-hostname pairs set taken from system configuration
// (e.g. /etc/hosts) files.
etcHosts *aghnet.HostsContainer
@ -97,9 +98,15 @@ var Context homeContext
// Main is the entry point
func Main(clientBuildFS fs.FS) {
// config can be specified, which reads options from there, but other command line flags have to override config values
// therefore, we must do it manually instead of using a lib
args := loadOptions()
initCmdLineOpts()
// The configuration file path can be overridden, but other command-line
// options have to override config values. Therefore, do it manually
// instead of using package flag.
//
// TODO(a.garipov): The comment above is most likely false. Replace with
// package flag.
opts := loadCmdLineOpts()
Context.appSignalChannel = make(chan os.Signal)
signal.Notify(Context.appSignalChannel, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
@ -110,7 +117,7 @@ func Main(clientBuildFS fs.FS) {
switch sig {
case syscall.SIGHUP:
Context.clients.Reload()
Context.tls.Reload()
Context.tls.reload()
default:
cleanup(context.Background())
@ -120,26 +127,18 @@ func Main(clientBuildFS fs.FS) {
}
}()
if args.serviceControlAction != "" {
handleServiceControlAction(args, clientBuildFS)
if opts.serviceControlAction != "" {
handleServiceControlAction(opts, clientBuildFS)
return
}
// run the protection
run(args, clientBuildFS)
run(opts, clientBuildFS)
}
func setupContext(args options) {
Context.runningAsService = args.runningAsService
Context.disableUpdate = args.disableUpdate ||
version.Channel() == version.ChannelDevelopment
Context.firstRun = detectFirstRun()
if Context.firstRun {
log.Info("This is the first time AdGuard Home is launched")
checkPermissions()
}
func setupContext(opts options) {
setupContextFlags(opts)
switch version.Channel() {
case version.ChannelEdge, version.ChannelDevelopment:
@ -148,7 +147,7 @@ func setupContext(args options) {
// Go on.
}
Context.tlsRoots = LoadSystemRootCAs()
Context.tlsRoots = aghtls.SystemRootCAs()
Context.transport = &http.Transport{
DialContext: customDialContext,
Proxy: getHTTPProxy,
@ -174,13 +173,13 @@ func setupContext(args options) {
os.Exit(1)
}
if args.checkConfig {
if opts.checkConfig {
log.Info("configuration file is ok")
os.Exit(0)
}
if !args.noEtcHosts && config.Clients.Sources.HostsFile {
if !opts.noEtcHosts && config.Clients.Sources.HostsFile {
err = setupHostsContainer()
fatalOnError(err)
}
@ -189,6 +188,24 @@ func setupContext(args options) {
Context.mux = http.NewServeMux()
}
// setupContextFlags sets global flags and prints their status to the log.
func setupContextFlags(opts options) {
Context.firstRun = detectFirstRun()
if Context.firstRun {
log.Info("This is the first time AdGuard Home is launched")
checkPermissions()
}
Context.runningAsService = opts.runningAsService
// Don't print the runningAsService flag, since that has already been done
// in [run].
Context.disableUpdate = opts.disableUpdate || version.Channel() == version.ChannelDevelopment
if Context.disableUpdate {
log.Info("AdGuard Home updates are disabled")
}
}
// logIfUnsupported logs a formatted warning if the error is one of the
// unsupported errors and returns nil. If err is nil, logIfUnsupported returns
// nil. Otherwise, it returns err.
@ -270,7 +287,7 @@ func setupHostsContainer() (err error) {
return nil
}
func setupConfig(args options) (err error) {
func setupConfig(opts options) (err error) {
config.DNS.DnsfilterConf.EtcHosts = Context.etcHosts
config.DNS.DnsfilterConf.ConfigModified = onConfigModified
config.DNS.DnsfilterConf.HTTPRegister = httpRegister
@ -312,9 +329,9 @@ func setupConfig(args options) (err error) {
Context.clients.Init(config.Clients.Persistent, Context.dhcpServer, Context.etcHosts, arpdb)
if args.bindPort != 0 {
if opts.bindPort != 0 {
tcpPorts := aghalg.UniqChecker[tcpPort]{}
addPorts(tcpPorts, tcpPort(args.bindPort), tcpPort(config.BetaBindPort))
addPorts(tcpPorts, tcpPort(opts.bindPort), tcpPort(config.BetaBindPort))
udpPorts := aghalg.UniqChecker[udpPort]{}
addPorts(udpPorts, udpPort(config.DNS.Port))
@ -336,23 +353,23 @@ func setupConfig(args options) (err error) {
return fmt.Errorf("validating udp ports: %w", err)
}
config.BindPort = args.bindPort
config.BindPort = opts.bindPort
}
// override bind host/port from the console
if args.bindHost != nil {
config.BindHost = args.bindHost
if opts.bindHost.IsValid() {
config.BindHost = opts.bindHost
}
if len(args.pidFile) != 0 && writePIDFile(args.pidFile) {
Context.pidFileName = args.pidFile
if len(opts.pidFile) != 0 && writePIDFile(opts.pidFile) {
Context.pidFileName = opts.pidFile
}
return nil
}
func initWeb(args options, clientBuildFS fs.FS) (web *Web, err error) {
func initWeb(opts options, clientBuildFS fs.FS) (web *Web, err error) {
var clientFS, clientBetaFS fs.FS
if args.localFrontend {
if opts.localFrontend {
log.Info("warning: using local frontend files")
clientFS = os.DirFS("build/static")
@ -406,24 +423,24 @@ func fatalOnError(err error) {
}
// run configures and starts AdGuard Home.
func run(args options, clientBuildFS fs.FS) {
func run(opts options, clientBuildFS fs.FS) {
// configure config filename
initConfigFilename(args)
initConfigFilename(opts)
// configure working dir and config path
initWorkingDir(args)
initWorkingDir(opts)
// configure log level and output
configureLogger(args)
configureLogger(opts)
// Print the first message after logger is configured.
log.Info(version.Full())
log.Debug("current working directory is %s", Context.workDir)
if args.runningAsService {
if opts.runningAsService {
log.Info("AdGuard Home is running as a service")
}
setupContext(args)
setupContext(opts)
err := configureOS(config)
fatalOnError(err)
@ -433,7 +450,7 @@ func run(args options, clientBuildFS fs.FS) {
// but also avoid relying on automatic Go init() function
filtering.InitModule()
err = setupConfig(args)
err = setupConfig(opts)
fatalOnError(err)
if !Context.firstRun {
@ -462,7 +479,7 @@ func run(args options, clientBuildFS fs.FS) {
}
sessFilename := filepath.Join(Context.getDataDir(), "sessions.db")
GLMode = args.glinetMode
GLMode = opts.glinetMode
var rateLimiter *authRateLimiter
if config.AuthAttempts > 0 && config.AuthBlockMin > 0 {
rateLimiter = newAuthRateLimiter(
@ -484,19 +501,19 @@ func run(args options, clientBuildFS fs.FS) {
}
config.Users = nil
Context.tls = tlsCreate(config.TLS)
if Context.tls == nil {
log.Fatalf("Can't initialize TLS module")
Context.tls, err = newTLSManager(config.TLS)
if err != nil {
log.Fatalf("initializing tls: %s", err)
}
Context.web, err = initWeb(args, clientBuildFS)
Context.web, err = initWeb(opts, clientBuildFS)
fatalOnError(err)
if !Context.firstRun {
err = initDNSServer()
fatalOnError(err)
Context.tls.Start()
Context.tls.start()
go func() {
serr := startDNSServer()
@ -520,20 +537,22 @@ func run(args options, clientBuildFS fs.FS) {
select {}
}
// StartMods initializes and starts the DNS server after installation.
func StartMods() error {
// startMods initializes and starts the DNS server after installation.
func startMods() error {
err := initDNSServer()
if err != nil {
return err
}
Context.tls.Start()
Context.tls.start()
err = startDNSServer()
if err != nil {
closeDNSServer()
return err
}
return nil
}
@ -546,7 +565,7 @@ func checkPermissions() {
}
// We should check if AdGuard Home is able to bind to port 53
err := aghnet.CheckPort("tcp", net.IP{127, 0, 0, 1}, defaultPortDNS)
err := aghnet.CheckPort("tcp", netip.AddrPortFrom(aghnet.IPv4Localhost(), defaultPortDNS))
if err != nil {
if errors.Is(err, os.ErrPermission) {
log.Fatal(`Permission check failed.
@ -581,10 +600,10 @@ func writePIDFile(fn string) bool {
return true
}
func initConfigFilename(args options) {
func initConfigFilename(opts options) {
// config file path can be overridden by command-line arguments:
if args.configFilename != "" {
Context.configFilename = args.configFilename
if opts.confFilename != "" {
Context.configFilename = opts.confFilename
} else {
// Default config file name
Context.configFilename = "AdGuardHome.yaml"
@ -593,15 +612,15 @@ func initConfigFilename(args options) {
// initWorkingDir initializes the workDir
// if no command-line arguments specified, we use the directory where our binary file is located
func initWorkingDir(args options) {
func initWorkingDir(opts options) {
execPath, err := os.Executable()
if err != nil {
panic(err)
}
if args.workDir != "" {
if opts.workDir != "" {
// If there is a custom config file, use it's directory as our working dir
Context.workDir = args.workDir
Context.workDir = opts.workDir
} else {
Context.workDir = filepath.Dir(execPath)
}
@ -615,15 +634,15 @@ func initWorkingDir(args options) {
}
// configureLogger configures logger level and output
func configureLogger(args options) {
func configureLogger(opts options) {
ls := getLogSettings()
// command-line arguments can override config settings
if args.verbose || config.Verbose {
if opts.verbose || config.Verbose {
ls.Verbose = true
}
if args.logFile != "" {
ls.File = args.logFile
if opts.logFile != "" {
ls.File = opts.logFile
} else if config.File != "" {
ls.File = config.File
}
@ -644,7 +663,7 @@ func configureLogger(args options) {
// happen pretty quickly.
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
if args.runningAsService && ls.File == "" && runtime.GOOS == "windows" {
if opts.runningAsService && ls.File == "" && runtime.GOOS == "windows" {
// When running as a Windows service, use eventlog by default if nothing
// else is configured. Otherwise, we'll simply lose the log output.
ls.File = configSyslog
@ -717,7 +736,6 @@ func cleanup(ctx context.Context) {
}
if Context.tls != nil {
Context.tls.Close()
Context.tls = nil
}
}
@ -727,32 +745,37 @@ func cleanupAlways() {
if len(Context.pidFileName) != 0 {
_ = os.Remove(Context.pidFileName)
}
log.Info("Stopped")
log.Info("stopped")
}
func exitWithError() {
os.Exit(64)
}
// loadOptions reads command line arguments and initializes configuration
func loadOptions() options {
o, f, err := parse(os.Args[0], os.Args[1:])
// loadCmdLineOpts reads command line arguments and initializes configuration
// from them. If there is an error or an effect, loadCmdLineOpts processes them
// and exits.
func loadCmdLineOpts() (opts options) {
opts, eff, err := parseCmdOpts(os.Args[0], os.Args[1:])
if err != nil {
log.Error(err.Error())
_ = printHelp(os.Args[0])
printHelp(os.Args[0])
exitWithError()
} else if f != nil {
err = f()
}
if eff != nil {
err = eff()
if err != nil {
log.Error(err.Error())
exitWithError()
} else {
os.Exit(0)
}
os.Exit(0)
}
return o
return opts
}
// printWebAddrs prints addresses built from proto, addr, and an appropriate
@ -901,6 +924,6 @@ func getTLSCiphers() (cipherIds []uint16, err error) {
return aghtls.SaferCipherSuites(), nil
} else {
log.Info("Overriding TLS Ciphers : %s", config.TLS.OverrideTLSCiphers)
return aghtls.ParseCipherIDs(config.TLS.OverrideTLSCiphers)
return aghtls.ParseCiphers(config.TLS.OverrideTLSCiphers)
}
}