mirror of
https://github.com/safing/portmaster
synced 2025-09-01 18:19:12 +00:00
Show notification about manual DNS setup instead of compatibility notice
This commit is contained in:
parent
f5afe8b5df
commit
2c3def3bc4
5 changed files with 98 additions and 28 deletions
|
@ -16,7 +16,7 @@ var (
|
|||
module *modules.Module
|
||||
|
||||
selfcheckTask *modules.Task
|
||||
selfcheckTaskRetryAfter = 10 * time.Second
|
||||
selfcheckTaskRetryAfter = 5 * time.Second
|
||||
|
||||
// selfCheckIsFailing holds whether or not the self-check is currently
|
||||
// failing. This helps other failure systems to not make noise when there is
|
||||
|
@ -47,6 +47,8 @@ func prep() error {
|
|||
}
|
||||
|
||||
func start() error {
|
||||
startNotify()
|
||||
|
||||
selfcheckTask = module.NewTask("compatibility self-check", selfcheckTaskFunc).
|
||||
Repeat(5 * time.Minute).
|
||||
MaxDelay(selfcheckTaskRetryAfter).
|
||||
|
@ -74,12 +76,18 @@ func stop() error {
|
|||
}
|
||||
|
||||
func selfcheckTaskFunc(ctx context.Context, task *modules.Task) error {
|
||||
// Create tracing logger.
|
||||
ctx, tracer := log.AddTracer(ctx)
|
||||
defer tracer.Submit()
|
||||
tracer.Tracef("compat: running self-check")
|
||||
|
||||
// Run selfcheck and return if successful.
|
||||
issue, err := selfcheck(ctx)
|
||||
if err == nil {
|
||||
selfCheckIsFailing.UnSet()
|
||||
selfcheckFails = 0
|
||||
resetSystemIssue()
|
||||
tracer.Debugf("compat: self-check successful")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -88,7 +96,7 @@ func selfcheckTaskFunc(ctx context.Context, task *modules.Task) error {
|
|||
selfCheckIsFailing.Set()
|
||||
selfcheckFails++
|
||||
|
||||
log.Errorf("compat: %s", err)
|
||||
tracer.Errorf("compat: %s", err)
|
||||
if selfcheckFails >= selfcheckFailThreshold {
|
||||
issue.notify(err)
|
||||
}
|
||||
|
@ -100,7 +108,7 @@ func selfcheckTaskFunc(ctx context.Context, task *modules.Task) error {
|
|||
selfcheckFails = 0
|
||||
|
||||
// Only log internal errors, but don't notify.
|
||||
log.Warningf("compat: %s", err)
|
||||
tracer.Warningf("compat: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -3,10 +3,12 @@ package compat
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portbase/notifications"
|
||||
|
@ -15,10 +17,11 @@ import (
|
|||
)
|
||||
|
||||
type baseIssue struct {
|
||||
id string //nolint:structcheck // Inherited.
|
||||
title string //nolint:structcheck // Inherited.
|
||||
message string //nolint:structcheck // Inherited.
|
||||
level notifications.Type //nolint:structcheck // Inherited.
|
||||
id string //nolint:structcheck // Inherited.
|
||||
title string //nolint:structcheck // Inherited.
|
||||
message string //nolint:structcheck // Inherited.
|
||||
level notifications.Type //nolint:structcheck // Inherited.
|
||||
actions []*notifications.Action //nolint:structcheck // Inherited.
|
||||
}
|
||||
|
||||
type systemIssue baseIssue
|
||||
|
@ -26,6 +29,10 @@ type systemIssue baseIssue
|
|||
type appIssue baseIssue
|
||||
|
||||
var (
|
||||
// Copy of firewall.CfgOptionDNSQueryInterceptionKey.
|
||||
cfgOptionDNSQueryInterceptionKey = "filter/dnsQueryInterception"
|
||||
dnsQueryInterception config.BoolOption
|
||||
|
||||
systemIssueNotification *notifications.Notification
|
||||
systemIssueNotificationLock sync.Mutex
|
||||
|
||||
|
@ -41,6 +48,22 @@ var (
|
|||
message: "Portmaster detected that something is interfering with its operation. This could be a VPN, an Anti-Virus or another network protection software. Please check if you are running an incompatible [VPN client](https://docs.safing.io/portmaster/install/status/vpn-compatibility) or [software](https://docs.safing.io/portmaster/install/status/software-compatibility). Otherwise, please report the issue via [GitHub](https://github.com/safing/portmaster/issues) or send a mail to [support@safing.io](mailto:support@safing.io) so we can help you out.",
|
||||
level: notifications.Error,
|
||||
}
|
||||
// manualDNSSetupRequired is additionally initialized in startNotify().
|
||||
manualDNSSetupRequired = &systemIssue{
|
||||
id: "compat:manual-dns-setup-required",
|
||||
title: "Manual DNS Setup Required",
|
||||
level: notifications.Error,
|
||||
actions: []*notifications.Action{
|
||||
{
|
||||
Text: "Revert",
|
||||
Type: notifications.ActionTypeOpenSetting,
|
||||
Payload: ¬ifications.ActionTypeOpenSettingPayload{
|
||||
Key: cfgOptionDNSQueryInterceptionKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
manualDNSSetupRequiredMessage = "You have disabled Seamless DNS Integration. As a result, Portmaster can no longer protect you or filter connections reliably. To fix this, you have to manually configure %s as the DNS Server in your system and in any conflicting application. This message will disappear 10 seconds after correct configuration."
|
||||
|
||||
secureDNSBypassIssue = &appIssue{
|
||||
id: "compat:secure-dns-bypass-%s",
|
||||
|
@ -58,6 +81,37 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
func startNotify() {
|
||||
dnsQueryInterception = config.Concurrent.GetAsBool(cfgOptionDNSQueryInterceptionKey, true)
|
||||
|
||||
systemIssueNotificationLock.Lock()
|
||||
defer systemIssueNotificationLock.Unlock()
|
||||
|
||||
manualDNSSetupRequired.message = fmt.Sprintf(
|
||||
manualDNSSetupRequiredMessage,
|
||||
`"127.0.0.1"`,
|
||||
)
|
||||
}
|
||||
|
||||
// SetNameserverListenIP sets the IP address the nameserver is listening on.
|
||||
// The IP address is used in compatibility notifications.
|
||||
func SetNameserverListenIP(ip net.IP) {
|
||||
systemIssueNotificationLock.Lock()
|
||||
defer systemIssueNotificationLock.Unlock()
|
||||
|
||||
manualDNSSetupRequired.message = fmt.Sprintf(
|
||||
manualDNSSetupRequiredMessage,
|
||||
`"`+ip.String()+`"`,
|
||||
)
|
||||
}
|
||||
|
||||
func systemCompatOrManualDNSIssue() *systemIssue {
|
||||
if dnsQueryInterception() {
|
||||
return systemCompatibilityIssue
|
||||
}
|
||||
return manualDNSSetupRequired
|
||||
}
|
||||
|
||||
func (issue *systemIssue) notify(err error) {
|
||||
systemIssueNotificationLock.Lock()
|
||||
defer systemIssueNotificationLock.Unlock()
|
||||
|
@ -74,11 +128,12 @@ func (issue *systemIssue) notify(err error) {
|
|||
|
||||
// Create new notification.
|
||||
n := ¬ifications.Notification{
|
||||
EventID: issue.id,
|
||||
Type: issue.level,
|
||||
Title: issue.title,
|
||||
Message: issue.message,
|
||||
ShowOnSystem: true,
|
||||
EventID: issue.id,
|
||||
Type: issue.level,
|
||||
Title: issue.title,
|
||||
Message: issue.message,
|
||||
ShowOnSystem: true,
|
||||
AvailableActions: issue.actions,
|
||||
}
|
||||
notifications.Notify(n)
|
||||
|
||||
|
@ -141,17 +196,20 @@ func (issue *appIssue) notify(proc *process.Process) {
|
|||
|
||||
// Create a new notification.
|
||||
n = ¬ifications.Notification{
|
||||
EventID: eventID,
|
||||
Type: issue.level,
|
||||
Title: fmt.Sprintf(issue.title, p.Name),
|
||||
Message: message,
|
||||
ShowOnSystem: true,
|
||||
AvailableActions: []*notifications.Action{
|
||||
EventID: eventID,
|
||||
Type: issue.level,
|
||||
Title: fmt.Sprintf(issue.title, p.Name),
|
||||
Message: message,
|
||||
ShowOnSystem: true,
|
||||
AvailableActions: issue.actions,
|
||||
}
|
||||
if len(n.AvailableActions) == 0 {
|
||||
n.AvailableActions = []*notifications.Action{
|
||||
{
|
||||
ID: "ack",
|
||||
Text: "OK",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
notifications.Notify(n)
|
||||
|
||||
|
|
|
@ -28,12 +28,12 @@ var (
|
|||
systemIntegrationCheckDialNet = fmt.Sprintf("ip4:%d", uint8(SystemIntegrationCheckProtocol))
|
||||
systemIntegrationCheckDialIP = SystemIntegrationCheckDstIP.String()
|
||||
systemIntegrationCheckPackets = make(chan packet.Packet, 1)
|
||||
systemIntegrationCheckWaitDuration = 30 * time.Second
|
||||
systemIntegrationCheckWaitDuration = 20 * time.Second
|
||||
|
||||
// DNSCheckInternalDomainScope is the domain scope to use for dns checks.
|
||||
DNSCheckInternalDomainScope = ".self-check." + resolver.InternalSpecialUseDomain
|
||||
dnsCheckReceivedDomain = make(chan string, 1)
|
||||
dnsCheckWaitDuration = 30 * time.Second
|
||||
dnsCheckWaitDuration = 20 * time.Second
|
||||
dnsCheckAnswerLock sync.Mutex
|
||||
dnsCheckAnswer net.IP
|
||||
)
|
||||
|
@ -61,7 +61,7 @@ func selfcheck(ctx context.Context) (issue *systemIssue, err error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create system integration conn: %w", err)
|
||||
}
|
||||
_, err = conn.Write([]byte("SELF-CHECK"))
|
||||
_, err = conn.Write([]byte("PORTMASTER SELF CHECK"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to send system integration packet: %w", err)
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ func selfcheck(ctx context.Context) (issue *systemIssue, err error) {
|
|||
select {
|
||||
case <-systemIntegrationCheckPackets:
|
||||
// Check passed!
|
||||
log.Tracef("compat: self-check #1: system integration check passed")
|
||||
log.Tracer(ctx).Tracef("compat: self-check #1: system integration check passed")
|
||||
case <-time.After(systemIntegrationCheckWaitDuration):
|
||||
return systemIntegrationIssue, fmt.Errorf("self-check #1: system integration check failed: did not receive test packet after %s", systemIntegrationCheckWaitDuration)
|
||||
case <-ctx.Done():
|
||||
|
@ -139,12 +139,12 @@ func selfcheck(ctx context.Context) (issue *systemIssue, err error) {
|
|||
select {
|
||||
case receivedTestDomain := <-dnsCheckReceivedDomain:
|
||||
if receivedTestDomain != randomSubdomain {
|
||||
return systemCompatibilityIssue, fmt.Errorf("self-check #2: dns integration check failed: received unmatching subdomain %q", receivedTestDomain)
|
||||
return systemCompatOrManualDNSIssue(), fmt.Errorf("self-check #2: dns integration check failed: received unmatching subdomain %q", receivedTestDomain)
|
||||
}
|
||||
case <-time.After(dnsCheckWaitDuration):
|
||||
return systemCompatibilityIssue, fmt.Errorf("self-check #2: dns integration check failed: did not receive test query after %s", dnsCheckWaitDuration)
|
||||
return systemCompatOrManualDNSIssue(), fmt.Errorf("self-check #2: dns integration check failed: did not receive test query after %s", dnsCheckWaitDuration)
|
||||
}
|
||||
log.Tracef("compat: self-check #2: dns integration query check passed")
|
||||
log.Tracer(ctx).Tracef("compat: self-check #2: dns integration query check passed")
|
||||
|
||||
// Step 3: Have the nameserver respond with random data in the answer section.
|
||||
|
||||
|
@ -164,7 +164,7 @@ func selfcheck(ctx context.Context) (issue *systemIssue, err error) {
|
|||
if !dnsCheckReturnedIP.Equal(randomAnswer) {
|
||||
return systemCompatibilityIssue, fmt.Errorf("self-check #3: dns integration check failed: received unmatching response %q", dnsCheckReturnedIP)
|
||||
}
|
||||
log.Tracef("compat: self-check #3: dns integration response check passed")
|
||||
log.Tracer(ctx).Tracef("compat: self-check #3: dns integration response check passed")
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portbase/modules/subsystems"
|
||||
"github.com/safing/portmaster/compat"
|
||||
"github.com/safing/portmaster/firewall"
|
||||
"github.com/safing/portmaster/netenv"
|
||||
)
|
||||
|
@ -53,6 +54,9 @@ func start() error {
|
|||
return fmt.Errorf("failed to parse nameserver listen address: %w", err)
|
||||
}
|
||||
|
||||
// Tell the compat module where we are listening.
|
||||
compat.SetNameserverListenIP(ip1)
|
||||
|
||||
// Get own hostname.
|
||||
hostname, err = os.Hostname()
|
||||
if err != nil {
|
||||
|
|
|
@ -168,7 +168,7 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg)
|
|||
}
|
||||
|
||||
default:
|
||||
tracer.Warningf("nameserver: external request for %s%s, ignoring", q.FQDN, q.QType)
|
||||
tracer.Warningf("nameserver: external request from %s for %s%s, ignoring", remoteAddr, q.FQDN, q.QType)
|
||||
return reply(nsutil.Refused("external queries are not permitted"))
|
||||
}
|
||||
conn.Lock()
|
||||
|
|
Loading…
Add table
Reference in a new issue