Fix Unbound run loop logic
- Plain DNS is used only for the first resolving of github.com to obtain block lists and crypto files required by Unbound - DNS over TLS is used at all time by the system and the Go program thereafter, even between periodic restarts - Downtime during a periodic update is < 1 second - On an Unbound start or unexpected exit error, the container falls back on the unencrypted version of the DNS in order to try restarting Unbound
This commit is contained in:
97
cmd/main.go
97
cmd/main.go
@@ -398,59 +398,59 @@ func fallbackToUnencryptedDNS(dnsConf dns.Configurator, provider models.DNSProvi
|
|||||||
return dnsConf.UseDNSSystemWide(targetIP)
|
return dnsConf.UseDNSSystemWide(targetIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unboundRun(ctx, unboundCtx context.Context, unboundCancel context.CancelFunc, dnsConf dns.Configurator, settings settings.DNS, uid, gid int,
|
func unboundRun(ctx, oldCtx context.Context, oldCancel context.CancelFunc, timer *time.Timer,
|
||||||
streamMerger command.StreamMerger, waiter command.Waiter, httpServer server.Server) (newCtx context.Context, newCancel context.CancelFunc, err error) {
|
dnsConf dns.Configurator, settings settings.DNS, uid, gid int,
|
||||||
|
streamMerger command.StreamMerger, waiter command.Waiter, httpServer server.Server) (
|
||||||
|
newCtx context.Context, newCancel context.CancelFunc, setupErr, startErr, waitErr error) {
|
||||||
|
if timer != nil {
|
||||||
|
timer.Stop()
|
||||||
|
timer.Reset(settings.UpdatePeriod)
|
||||||
|
}
|
||||||
if err := dnsConf.DownloadRootHints(uid, gid); err != nil {
|
if err := dnsConf.DownloadRootHints(uid, gid); err != nil {
|
||||||
return unboundCtx, unboundCancel, err
|
return oldCtx, oldCancel, err, nil, nil
|
||||||
}
|
}
|
||||||
if err := dnsConf.DownloadRootKey(uid, gid); err != nil {
|
if err := dnsConf.DownloadRootKey(uid, gid); err != nil {
|
||||||
return unboundCtx, unboundCancel, err
|
return oldCtx, oldCancel, err, nil, nil
|
||||||
}
|
}
|
||||||
if err := dnsConf.MakeUnboundConf(settings, uid, gid); err != nil {
|
if err := dnsConf.MakeUnboundConf(settings, uid, gid); err != nil {
|
||||||
return unboundCtx, unboundCancel, err
|
return oldCtx, oldCancel, err, nil, nil
|
||||||
}
|
}
|
||||||
unboundCancel()
|
|
||||||
if settings.UpdatePeriod > 0 {
|
|
||||||
newCtx, newCancel = context.WithTimeout(ctx, settings.UpdatePeriod)
|
|
||||||
} else {
|
|
||||||
newCtx, newCancel = context.WithCancel(ctx)
|
newCtx, newCancel = context.WithCancel(ctx)
|
||||||
}
|
oldCancel()
|
||||||
stream, waitFn, err := dnsConf.Start(newCtx, settings.VerbosityDetailsLevel)
|
stream, waitFn, err := dnsConf.Start(newCtx, settings.VerbosityDetailsLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newCancel()
|
return newCtx, newCancel, nil, err, nil
|
||||||
if fallbackErr := fallbackToUnencryptedDNS(dnsConf, settings.Providers[0], settings.IPv6); err != nil {
|
|
||||||
return newCtx, newCancel, fmt.Errorf("%s: %w", err, fallbackErr)
|
|
||||||
}
|
|
||||||
return newCtx, newCancel, err
|
|
||||||
}
|
}
|
||||||
go streamMerger.Merge(newCtx, stream, command.MergeName("unbound"), command.MergeColor(constants.ColorUnbound()))
|
go streamMerger.Merge(newCtx, stream, command.MergeName("unbound"), command.MergeColor(constants.ColorUnbound()))
|
||||||
dnsConf.UseDNSInternally(net.IP{127, 0, 0, 1}) // use Unbound
|
dnsConf.UseDNSInternally(net.IP{127, 0, 0, 1}) // use Unbound
|
||||||
if err := dnsConf.UseDNSSystemWide(net.IP{127, 0, 0, 1}); err != nil { // use Unbound
|
if err := dnsConf.UseDNSSystemWide(net.IP{127, 0, 0, 1}); err != nil { // use Unbound
|
||||||
newCancel()
|
return newCtx, newCancel, nil, err, nil
|
||||||
if fallbackErr := fallbackToUnencryptedDNS(dnsConf, settings.Providers[0], settings.IPv6); err != nil {
|
|
||||||
return newCtx, newCancel, fmt.Errorf("%s: %w", err, fallbackErr)
|
|
||||||
}
|
|
||||||
return newCtx, newCancel, err
|
|
||||||
}
|
}
|
||||||
if err := dnsConf.WaitForUnbound(); err != nil {
|
if err := dnsConf.WaitForUnbound(); err != nil {
|
||||||
newCancel()
|
return newCtx, newCancel, nil, err, nil
|
||||||
if fallbackErr := fallbackToUnencryptedDNS(dnsConf, settings.Providers[0], settings.IPv6); err != nil {
|
|
||||||
return newCtx, newCancel, fmt.Errorf("%s: %w", err, fallbackErr)
|
|
||||||
}
|
|
||||||
return newCtx, newCancel, err
|
|
||||||
}
|
}
|
||||||
// Unbound is up and running at this point
|
// Unbound is up and running at this point
|
||||||
httpServer.SetUnboundRestart(newCancel)
|
httpServer.SetUnboundRestart(newCancel)
|
||||||
waitErrors := make(chan error)
|
waitError := make(chan error)
|
||||||
|
waiterError := make(chan error)
|
||||||
waiter.Add(func() error { //nolint:scopelint
|
waiter.Add(func() error { //nolint:scopelint
|
||||||
return <-waitErrors
|
return <-waiterError
|
||||||
})
|
})
|
||||||
err = waitFn()
|
go func() {
|
||||||
waitErrors <- err
|
err := fmt.Errorf("unbound: %w", waitFn())
|
||||||
if newCtx.Err() == context.Canceled || newCtx.Err() == context.DeadlineExceeded {
|
waitError <- err
|
||||||
return newCtx, newCancel, nil
|
waiterError <- err
|
||||||
|
}()
|
||||||
|
if timer == nil {
|
||||||
|
waitErr := <-waitError
|
||||||
|
return newCtx, newCancel, nil, nil, waitErr
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
return newCtx, newCancel, nil, nil, nil
|
||||||
|
case waitErr := <-waitError:
|
||||||
|
return newCtx, newCancel, nil, nil, waitErr
|
||||||
}
|
}
|
||||||
return newCtx, newCancel, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func unboundRunLoop(ctx context.Context, logger logging.Logger, dnsConf dns.Configurator,
|
func unboundRunLoop(ctx context.Context, logger logging.Logger, dnsConf dns.Configurator,
|
||||||
@@ -461,19 +461,38 @@ func unboundRunLoop(ctx context.Context, logger logging.Logger, dnsConf dns.Conf
|
|||||||
if err := fallbackToUnencryptedDNS(dnsConf, settings.Providers[0], settings.IPv6); err != nil {
|
if err := fallbackToUnencryptedDNS(dnsConf, settings.Providers[0], settings.IPv6); err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
}
|
}
|
||||||
|
var timer *time.Timer
|
||||||
|
if settings.UpdatePeriod > 0 {
|
||||||
|
timer = time.NewTimer(settings.UpdatePeriod)
|
||||||
|
}
|
||||||
unboundCtx, unboundCancel := context.WithCancel(ctx)
|
unboundCtx, unboundCancel := context.WithCancel(ctx)
|
||||||
defer unboundCancel()
|
defer unboundCancel()
|
||||||
for ctx.Err() == nil {
|
for ctx.Err() == nil {
|
||||||
var err error
|
var setupErr, startErr, waitErr error
|
||||||
unboundCtx, unboundCancel, err = unboundRun(ctx, unboundCtx, unboundCancel, dnsConf, settings, uid, gid, streamMerger, waiter, httpServer)
|
unboundCtx, unboundCancel, setupErr, startErr, waitErr = unboundRun(
|
||||||
if err != nil {
|
ctx, unboundCtx, unboundCancel, timer, dnsConf, settings,
|
||||||
|
uid, gid, streamMerger, waiter, httpServer)
|
||||||
|
switch {
|
||||||
|
case ctx.Err() != nil:
|
||||||
|
logger.Warn("context canceled: exiting unbound run loop")
|
||||||
|
case timer != nil && !timer.Stop():
|
||||||
|
logger.Info("planned restart of unbound")
|
||||||
|
case setupErr != nil:
|
||||||
|
logger.Warn(setupErr)
|
||||||
|
case startErr != nil:
|
||||||
|
logger.Error(startErr)
|
||||||
|
unboundCancel()
|
||||||
|
if err := fallbackToUnencryptedDNS(dnsConf, settings.Providers[0], settings.IPv6); err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
logger.Info("attempting restart")
|
case waitErr != nil:
|
||||||
|
logger.Warn(waitErr)
|
||||||
|
if err := fallbackToUnencryptedDNS(dnsConf, settings.Providers[0], settings.IPv6); err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
}
|
||||||
|
logger.Warn("restarting unbound because of unexpected exit")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
logger.Info("shutting down")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupPortForwarding(logger logging.Logger, piaConf pia.Configurator, settings settings.PIA, uid, gid int) {
|
func setupPortForwarding(logger logging.Logger, piaConf pia.Configurator, settings settings.PIA, uid, gid int) {
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package constants
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// Announcement is a message announcement
|
// Announcement is a message announcement
|
||||||
Announcement = "New HTTP control server, see https://github.com/qdm12/private-internet-access-docker#http-control-server"
|
Announcement = "Auto update of DNS over TLS block lists and crypto files"
|
||||||
// AnnouncementExpiration is the expiration date of the announcement in format yyyy-mm-dd
|
// AnnouncementExpiration is the expiration date of the announcement in format yyyy-mm-dd
|
||||||
AnnouncementExpiration = "2020-05-20"
|
AnnouncementExpiration = "2020-05-28"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
Reference in New Issue
Block a user