mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-21 17:56:39 +00:00
Compare commits
22 Commits
v0.25.5
...
debug-goog
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f41e2bd13 | ||
|
|
cb3408a10b | ||
|
|
0afd738509 | ||
|
|
e3d038da8a | ||
|
|
cf87f1e702 | ||
|
|
e890fdae54 | ||
|
|
dd14db6478 | ||
|
|
88747e3e01 | ||
|
|
fb30931365 | ||
|
|
a7547b9990 | ||
|
|
62bacee8dc | ||
|
|
71cd2e3e03 | ||
|
|
bdf71ab7ff | ||
|
|
a2f2a6e21a | ||
|
|
f89332fcd2 | ||
|
|
8604add997 | ||
|
|
93cab49696 | ||
|
|
b6835d9467 | ||
|
|
846d486366 | ||
|
|
9c56f74235 | ||
|
|
25b3641be8 | ||
|
|
c41504b571 |
18
.github/ISSUE_TEMPLATE/bug-issue-report.md
vendored
18
.github/ISSUE_TEMPLATE/bug-issue-report.md
vendored
@@ -2,15 +2,17 @@
|
|||||||
name: Bug/Issue report
|
name: Bug/Issue report
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us improve
|
||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
labels: ['triage']
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Describe the problem**
|
**Describe the problem**
|
||||||
|
|
||||||
A clear and concise description of what the problem is.
|
A clear and concise description of what the problem is.
|
||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
|
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
2. Click on '....'
|
2. Click on '....'
|
||||||
@@ -18,13 +20,25 @@ Steps to reproduce the behavior:
|
|||||||
4. See error
|
4. See error
|
||||||
|
|
||||||
**Expected behavior**
|
**Expected behavior**
|
||||||
|
|
||||||
A clear and concise description of what you expected to happen.
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Are you using NetBird Cloud?**
|
||||||
|
|
||||||
|
Please specify whether you use NetBird Cloud or self-host NetBird's control plane.
|
||||||
|
|
||||||
|
**NetBird version**
|
||||||
|
|
||||||
|
`netbird version`
|
||||||
|
|
||||||
**NetBird status -d output:**
|
**NetBird status -d output:**
|
||||||
If applicable, add the output of the `netbird status -d` command
|
|
||||||
|
If applicable, add the `netbird status -d' command output.
|
||||||
|
|
||||||
**Screenshots**
|
**Screenshots**
|
||||||
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
|
|
||||||
Add any other context about the problem here.
|
Add any other context about the problem here.
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -2,7 +2,7 @@
|
|||||||
name: Feature request
|
name: Feature request
|
||||||
about: Suggest an idea for this project
|
about: Suggest an idea for this project
|
||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
labels: ['feature-request']
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -139,6 +139,11 @@ func (c *Client) SetTraceLogLevel() {
|
|||||||
log.SetLevel(log.TraceLevel)
|
log.SetLevel(log.TraceLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetInfoLogLevel configure the logger to info level
|
||||||
|
func (c *Client) SetInfoLogLevel() {
|
||||||
|
log.SetLevel(log.InfoLevel)
|
||||||
|
}
|
||||||
|
|
||||||
// PeersList return with the list of the PeerInfos
|
// PeersList return with the list of the PeerInfos
|
||||||
func (c *Client) PeersList() *PeerInfoArray {
|
func (c *Client) PeersList() *PeerInfoArray {
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,12 @@ import (
|
|||||||
"github.com/kardianos/service"
|
"github.com/kardianos/service"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/proto"
|
"github.com/netbirdio/netbird/client/proto"
|
||||||
"github.com/netbirdio/netbird/client/server"
|
"github.com/netbirdio/netbird/client/server"
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *program) Start(svc service.Service) error {
|
func (p *program) Start(svc service.Service) error {
|
||||||
@@ -109,7 +110,6 @@ var runCmd = &cobra.Command{
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cmd.Printf("Netbird service is running")
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package internal
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -93,6 +94,12 @@ func runClient(
|
|||||||
) error {
|
) error {
|
||||||
log.Infof("starting NetBird client version %s", version.NetbirdVersion())
|
log.Infof("starting NetBird client version %s", version.NetbirdVersion())
|
||||||
|
|
||||||
|
// Check if client was not shut down in a clean way and restore DNS config if required.
|
||||||
|
// Otherwise, we might not be able to connect to the management server to retrieve new config.
|
||||||
|
if err := dns.CheckUncleanShutdown(config.WgIface); err != nil {
|
||||||
|
log.Errorf("checking unclean shutdown error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
backOff := &backoff.ExponentialBackOff{
|
backOff := &backoff.ExponentialBackOff{
|
||||||
InitialInterval: time.Second,
|
InitialInterval: time.Second,
|
||||||
RandomizationFactor: 1,
|
RandomizationFactor: 1,
|
||||||
@@ -244,7 +251,7 @@ func runClient(
|
|||||||
|
|
||||||
log.Info("stopped NetBird client")
|
log.Info("stopped NetBird client")
|
||||||
|
|
||||||
if _, err := state.Status(); err == ErrResetConnection {
|
if _, err := state.Status(); errors.Is(err, ErrResetConnection) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/godbus/dbus/v5"
|
"github.com/godbus/dbus/v5"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const dbusDefaultFlag = 0
|
const dbusDefaultFlag = 0
|
||||||
@@ -14,6 +16,7 @@ const dbusDefaultFlag = 0
|
|||||||
func isDbusListenerRunning(dest string, path dbus.ObjectPath) bool {
|
func isDbusListenerRunning(dest string, path dbus.ObjectPath) bool {
|
||||||
obj, closeConn, err := getDbusObject(dest, path)
|
obj, closeConn, err := getDbusObject(dest, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Tracef("error getting dbus object: %s", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
defer closeConn()
|
defer closeConn()
|
||||||
@@ -21,14 +24,18 @@ func isDbusListenerRunning(dest string, path dbus.ObjectPath) bool {
|
|||||||
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
err = obj.CallWithContext(ctx, "org.freedesktop.DBus.Peer.Ping", 0).Store()
|
if err = obj.CallWithContext(ctx, "org.freedesktop.DBus.Peer.Ping", 0).Store(); err != nil {
|
||||||
return err == nil
|
log.Tracef("error calling dbus: %s", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDbusObject(dest string, path dbus.ObjectPath) (dbus.BusObject, func(), error) {
|
func getDbusObject(dest string, path dbus.ObjectPath) (dbus.BusObject, func(), error) {
|
||||||
conn, err := dbus.SystemBus()
|
conn, err := dbus.SystemBus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, fmt.Errorf("get dbus: %w", err)
|
||||||
}
|
}
|
||||||
obj := conn.Object(dest, path)
|
obj := conn.Object(dest, path)
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package dns
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ func (f *fileConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
if backupFileExist {
|
if backupFileExist {
|
||||||
err = f.restore()
|
err = f.restore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to configure DNS for this peer using file manager without a Primary nameserver group. Restoring the original file return err: %s", err)
|
return fmt.Errorf("unable to configure DNS for this peer using file manager without a Primary nameserver group. Restoring the original file return err: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fmt.Errorf("unable to configure DNS for this peer using file manager without a nameserver group with all domains configured")
|
return fmt.Errorf("unable to configure DNS for this peer using file manager without a nameserver group with all domains configured")
|
||||||
@@ -58,7 +59,7 @@ func (f *fileConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
if !backupFileExist {
|
if !backupFileExist {
|
||||||
err = f.backup()
|
err = f.backup()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to backup the resolv.conf file")
|
return fmt.Errorf("unable to backup the resolv.conf file: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ func (f *fileConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
|
|
||||||
resolvConf, err := parseBackupResolvConf()
|
resolvConf, err := parseBackupResolvConf()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Errorf("could not read original search domains from %s: %s", fileDefaultResolvConfBackupLocation, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
f.repair.stopWatchFileChanges()
|
f.repair.stopWatchFileChanges()
|
||||||
@@ -96,10 +97,16 @@ func (f *fileConfigurator) updateConfig(nbSearchDomains []string, nbNameserverIP
|
|||||||
if restoreErr != nil {
|
if restoreErr != nil {
|
||||||
log.Errorf("attempt to restore default file failed with error: %s", err)
|
log.Errorf("attempt to restore default file failed with error: %s", err)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("got an creating resolver file %s. Error: %s", defaultResolvConfPath, err)
|
return fmt.Errorf("creating resolver file %s. Error: %w", defaultResolvConfPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("created a NetBird managed %s file with the DNS settings. Added %d search domains. Search list: %s", defaultResolvConfPath, len(searchDomainList), searchDomainList)
|
||||||
|
|
||||||
|
// create another backup for unclean shutdown detection right after overwriting the original resolv.conf
|
||||||
|
if err := createUncleanShutdownIndicator(fileDefaultResolvConfBackupLocation, fileManager, nbNameserverIP); err != nil {
|
||||||
|
log.Errorf("failed to create unclean shutdown resolv.conf backup: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("created a NetBird managed %s file with your DNS settings. Added %d search domains. Search list: %s", defaultResolvConfPath, len(searchDomainList), searchDomainList)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,14 +118,14 @@ func (f *fileConfigurator) restoreHostDNS() error {
|
|||||||
func (f *fileConfigurator) backup() error {
|
func (f *fileConfigurator) backup() error {
|
||||||
stats, err := os.Stat(defaultResolvConfPath)
|
stats, err := os.Stat(defaultResolvConfPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got an error while checking stats for %s file. Error: %s", defaultResolvConfPath, err)
|
return fmt.Errorf("checking stats for %s file. Error: %w", defaultResolvConfPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
f.originalPerms = stats.Mode()
|
f.originalPerms = stats.Mode()
|
||||||
|
|
||||||
err = copyFile(defaultResolvConfPath, fileDefaultResolvConfBackupLocation)
|
err = copyFile(defaultResolvConfPath, fileDefaultResolvConfBackupLocation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got error while backing up the %s file. Error: %s", defaultResolvConfPath, err)
|
return fmt.Errorf("backing up %s: %w", defaultResolvConfPath, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -126,12 +133,58 @@ func (f *fileConfigurator) backup() error {
|
|||||||
func (f *fileConfigurator) restore() error {
|
func (f *fileConfigurator) restore() error {
|
||||||
err := copyFile(fileDefaultResolvConfBackupLocation, defaultResolvConfPath)
|
err := copyFile(fileDefaultResolvConfBackupLocation, defaultResolvConfPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got error while restoring the %s file from %s. Error: %s", defaultResolvConfPath, fileDefaultResolvConfBackupLocation, err)
|
return fmt.Errorf("restoring %s from %s: %w", defaultResolvConfPath, fileDefaultResolvConfBackupLocation, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := removeUncleanShutdownIndicator(); err != nil {
|
||||||
|
log.Errorf("failed to remove unclean shutdown resolv.conf backup: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return os.RemoveAll(fileDefaultResolvConfBackupLocation)
|
return os.RemoveAll(fileDefaultResolvConfBackupLocation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *fileConfigurator) restoreUncleanShutdownDNS(storedDNSAddress *netip.Addr) error {
|
||||||
|
resolvConf, err := parseDefaultResolvConf()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parse current resolv.conf: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// no current nameservers set -> restore
|
||||||
|
if len(resolvConf.nameServers) == 0 {
|
||||||
|
return restoreResolvConfFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDNSAddress, err := netip.ParseAddr(resolvConf.nameServers[0])
|
||||||
|
// not a valid first nameserver -> restore
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("restoring unclean shutdown: parse dns address %s failed: %s", resolvConf.nameServers[1], err)
|
||||||
|
return restoreResolvConfFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
// current address is still netbird's non-available dns address -> restore
|
||||||
|
// comparing parsed addresses only, to remove ambiguity
|
||||||
|
if currentDNSAddress.String() == storedDNSAddress.String() {
|
||||||
|
return restoreResolvConfFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("restoring unclean shutdown: first current nameserver differs from saved nameserver pre-netbird: not restoring")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func restoreResolvConfFile() error {
|
||||||
|
log.Debugf("restoring unclean shutdown: restoring %s from %s", defaultResolvConfPath, fileUncleanShutdownResolvConfLocation)
|
||||||
|
|
||||||
|
if err := copyFile(fileUncleanShutdownResolvConfLocation, defaultResolvConfPath); err != nil {
|
||||||
|
return fmt.Errorf("restoring %s from %s: %w", defaultResolvConfPath, fileUncleanShutdownResolvConfLocation, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := removeUncleanShutdownIndicator(); err != nil {
|
||||||
|
log.Errorf("failed to remove unclean shutdown resolv.conf file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// generateNsList generates a list of nameservers from the config and adds the primary nameserver to the beginning of the list
|
// generateNsList generates a list of nameservers from the config and adds the primary nameserver to the beginning of the list
|
||||||
func generateNsList(nbNameserverIP string, cfg *resolvConf) []string {
|
func generateNsList(nbNameserverIP string, cfg *resolvConf) []string {
|
||||||
ns := make([]string, 1, len(cfg.nameServers)+1)
|
ns := make([]string, 1, len(cfg.nameServers)+1)
|
||||||
@@ -231,17 +284,17 @@ func validateAndFillSearchDomains(initialLineChars int, s *[]string, vs []string
|
|||||||
func copyFile(src, dest string) error {
|
func copyFile(src, dest string) error {
|
||||||
stats, err := os.Stat(src)
|
stats, err := os.Stat(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got an error while checking stats for %s file when copying it. Error: %s", src, err)
|
return fmt.Errorf("checking stats for %s file when copying it. Error: %s", src, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bytesRead, err := os.ReadFile(src)
|
bytesRead, err := os.ReadFile(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got an error while reading the file %s file for copy. Error: %s", src, err)
|
return fmt.Errorf("reading the file %s file for copy. Error: %s", src, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.WriteFile(dest, bytesRead, stats.Mode())
|
err = os.WriteFile(dest, bytesRead, stats.Mode())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got an writing the destination file %s for copy. Error: %s", dest, err)
|
return fmt.Errorf("writing the destination file %s for copy. Error: %s", dest, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,9 +33,15 @@ func parseBackupResolvConf() (*resolvConf, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseResolvConfFile(resolvConfFile string) (*resolvConf, error) {
|
func parseResolvConfFile(resolvConfFile string) (*resolvConf, error) {
|
||||||
|
rconf := &resolvConf{
|
||||||
|
searchDomains: make([]string, 0),
|
||||||
|
nameServers: make([]string, 0),
|
||||||
|
others: make([]string, 0),
|
||||||
|
}
|
||||||
|
|
||||||
file, err := os.Open(resolvConfFile)
|
file, err := os.Open(resolvConfFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to open %s file: %w", resolvConfFile, err)
|
return rconf, fmt.Errorf("failed to open %s file: %w", resolvConfFile, err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := file.Close(); err != nil {
|
if err := file.Close(); err != nil {
|
||||||
@@ -45,17 +51,11 @@ func parseResolvConfFile(resolvConfFile string) (*resolvConf, error) {
|
|||||||
|
|
||||||
cur, err := os.ReadFile(resolvConfFile)
|
cur, err := os.ReadFile(resolvConfFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read %s file: %w", resolvConfFile, err)
|
return rconf, fmt.Errorf("failed to read %s file: %w", resolvConfFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cur) == 0 {
|
if len(cur) == 0 {
|
||||||
return nil, fmt.Errorf("file is empty")
|
return rconf, fmt.Errorf("file is empty")
|
||||||
}
|
|
||||||
|
|
||||||
rconf := &resolvConf{
|
|
||||||
searchDomains: make([]string, 0),
|
|
||||||
nameServers: make([]string, 0),
|
|
||||||
others: make([]string, 0),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, line := range strings.Split(string(cur), "\n") {
|
for _, line := range strings.Split(string(cur), "\n") {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -16,11 +16,11 @@ func Test_parseResolvConf(t *testing.T) {
|
|||||||
expectedOther []string
|
expectedOther []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
input: `domain chello.hu
|
input: `domain example.org
|
||||||
search chello.hu
|
search example.org
|
||||||
nameserver 192.168.0.1
|
nameserver 192.168.0.1
|
||||||
`,
|
`,
|
||||||
expectedSearch: []string{"chello.hu"},
|
expectedSearch: []string{"example.org"},
|
||||||
expectedNS: []string{"192.168.0.1"},
|
expectedNS: []string{"192.168.0.1"},
|
||||||
expectedOther: []string{},
|
expectedOther: []string{},
|
||||||
},
|
},
|
||||||
@@ -75,40 +75,13 @@ options debug
|
|||||||
expectedNS: []string{"192.168.2.1", "100.81.99.197"},
|
expectedNS: []string{"192.168.2.1", "100.81.99.197"},
|
||||||
expectedOther: []string{"options debug"},
|
expectedOther: []string{"options debug"},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
input: `# This is /run/systemd/resolve/resolv.conf managed by man:systemd-resolved(8).
|
|
||||||
# Do not edit.
|
|
||||||
#
|
|
||||||
# This file might be symlinked as /etc/resolv.conf. If you're looking at
|
|
||||||
# /etc/resolv.conf and seeing this text, you have followed the symlink.
|
|
||||||
#
|
|
||||||
# This is a dynamic resolv.conf file for connecting local clients directly to
|
|
||||||
# all known uplink DNS servers. This file lists all configured search domains.
|
|
||||||
#
|
|
||||||
# Third party programs should typically not access this file directly, but only
|
|
||||||
# through the symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a
|
|
||||||
# different way, replace this symlink by a static file or a different symlink.
|
|
||||||
#
|
|
||||||
# See man:systemd-resolved.service(8) for details about the supported modes of
|
|
||||||
# operation for /etc/resolv.conf.
|
|
||||||
|
|
||||||
nameserver 192.168.2.1
|
|
||||||
nameserver 100.81.99.197
|
|
||||||
search netbird.cloud
|
|
||||||
options debug
|
|
||||||
options edns0 trust-ad
|
|
||||||
`,
|
|
||||||
expectedSearch: []string{"netbird.cloud"},
|
|
||||||
expectedNS: []string{"192.168.2.1", "100.81.99.197"},
|
|
||||||
expectedOther: []string{"options debug", "options edns0 trust-ad"},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
testCase := testCase
|
testCase := testCase
|
||||||
t.Run("test", func(t *testing.T) {
|
t.Run("test", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
tmpResolvConf := fmt.Sprintf("%s/%s", t.TempDir(), "resolv.conf")
|
tmpResolvConf := filepath.Join(t.TempDir(), "resolv.conf")
|
||||||
err := os.WriteFile(tmpResolvConf, []byte(testCase.input), 0644)
|
err := os.WriteFile(tmpResolvConf, []byte(testCase.input), 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -147,3 +120,55 @@ func compareLists(search []string, search2 []string) bool {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_emptyFile(t *testing.T) {
|
||||||
|
cfg, err := parseResolvConfFile("/tmp/nothing")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error, got nil")
|
||||||
|
}
|
||||||
|
if len(cfg.others) != 0 || len(cfg.searchDomains) != 0 || len(cfg.nameServers) != 0 {
|
||||||
|
t.Errorf("expected empty config, got %v", cfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_symlink(t *testing.T) {
|
||||||
|
input := `# This is /run/systemd/resolve/resolv.conf managed by man:systemd-resolved(8).
|
||||||
|
# Do not edit.
|
||||||
|
#
|
||||||
|
# This file might be symlinked as /etc/resolv.conf. If you're looking at
|
||||||
|
# /etc/resolv.conf and seeing this text, you have followed the symlink.
|
||||||
|
#
|
||||||
|
# This is a dynamic resolv.conf file for connecting local clients directly to
|
||||||
|
# all known uplink DNS servers. This file lists all configured search domains.
|
||||||
|
#
|
||||||
|
# Third party programs should typically not access this file directly, but only
|
||||||
|
# through the symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a
|
||||||
|
# different way, replace this symlink by a static file or a different symlink.
|
||||||
|
#
|
||||||
|
# See man:systemd-resolved.service(8) for details about the supported modes of
|
||||||
|
# operation for /etc/resolv.conf.
|
||||||
|
|
||||||
|
nameserver 192.168.0.1
|
||||||
|
`
|
||||||
|
|
||||||
|
tmpResolvConf := filepath.Join(t.TempDir(), "resolv.conf")
|
||||||
|
err := os.WriteFile(tmpResolvConf, []byte(input), 0644)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpLink := filepath.Join(t.TempDir(), "symlink")
|
||||||
|
err = os.Symlink(tmpResolvConf, tmpLink)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := parseResolvConfFile(tmpLink)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cfg.nameServers) != 1 {
|
||||||
|
t.Errorf("unexpected resolv.conf content: %v", cfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
@@ -32,9 +32,10 @@ type repair struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newRepair(operationFile string, updateFn repairConfFn) *repair {
|
func newRepair(operationFile string, updateFn repairConfFn) *repair {
|
||||||
|
targetFile := targetFile(operationFile)
|
||||||
return &repair{
|
return &repair{
|
||||||
operationFile: operationFile,
|
operationFile: targetFile,
|
||||||
watchDir: path.Dir(operationFile),
|
watchDir: path.Dir(targetFile),
|
||||||
updateFn: updateFn,
|
updateFn: updateFn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,7 +45,7 @@ func (f *repair) watchFileChanges(nbSearchDomains []string, nbNameserverIP strin
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("start to watch resolv.conf")
|
log.Infof("start to watch resolv.conf: %s", f.operationFile)
|
||||||
inotify, err := fsnotify.NewWatcher()
|
inotify, err := fsnotify.NewWatcher()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to start inotify watcher for resolv.conf: %s", err)
|
log.Errorf("failed to start inotify watcher for resolv.conf: %s", err)
|
||||||
@@ -60,7 +61,7 @@ func (f *repair) watchFileChanges(nbSearchDomains []string, nbNameserverIP strin
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Tracef("resolv.conf changed, check if it is broken")
|
log.Tracef("%s changed, check if it is broken", f.operationFile)
|
||||||
|
|
||||||
rConf, err := parseResolvConfFile(f.operationFile)
|
rConf, err := parseResolvConfFile(f.operationFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -87,7 +88,7 @@ func (f *repair) watchFileChanges(nbSearchDomains []string, nbNameserverIP strin
|
|||||||
|
|
||||||
err = f.inotify.Add(f.watchDir)
|
err = f.inotify.Add(f.watchDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to readd inotify watch for resolv.conf: %s", err)
|
log.Errorf("failed to re-add inotify watch for resolv.conf: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,8 +125,7 @@ func (f *repair) isEventRelevant(event fsnotify.Event) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
operationFileSymlink := fmt.Sprintf("%s~", f.operationFile)
|
if event.Name == f.operationFile {
|
||||||
if event.Name == f.operationFile || event.Name == operationFileSymlink {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -149,3 +149,11 @@ func isNbParamsMissing(nbSearchDomains []string, nbNameserverIP string, rConf *r
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func targetFile(filename string) string {
|
||||||
|
target, err := filepath.EvalSymlinks(filename)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("evarl err: %s", err)
|
||||||
|
}
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package dns
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -126,5 +127,49 @@ nameserver 8.8.8.8`,
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_newRepairSymlink(t *testing.T) {
|
||||||
|
resolvConfContent := `
|
||||||
|
nameserver 10.0.0.1
|
||||||
|
nameserver 8.8.8.8
|
||||||
|
searchdomain netbird.cloud something`
|
||||||
|
|
||||||
|
modifyContent := `nameserver 8.8.8.8`
|
||||||
|
|
||||||
|
tmpResolvConf := filepath.Join(t.TempDir(), "resolv.conf")
|
||||||
|
err := os.WriteFile(tmpResolvConf, []byte(resolvConfContent), 0644)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpLink := filepath.Join(t.TempDir(), "symlink")
|
||||||
|
err = os.Symlink(tmpResolvConf, tmpLink)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var changed bool
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||||
|
updateFn := func([]string, string, *resolvConf) error {
|
||||||
|
changed = true
|
||||||
|
cancel()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r := newRepair(tmpLink, updateFn)
|
||||||
|
r.watchFileChanges([]string{"netbird.cloud"}, "10.0.0.1")
|
||||||
|
|
||||||
|
err = os.WriteFile(tmpLink, []byte(modifyContent), 0755)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to write out resolv.conf: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
<-ctx.Done()
|
||||||
|
|
||||||
|
r.stopWatchFileChanges()
|
||||||
|
|
||||||
|
if changed != true {
|
||||||
|
t.Errorf("unexpected result: want: %v, got: %v", true, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
@@ -11,6 +12,7 @@ type hostManager interface {
|
|||||||
applyDNSConfig(config HostDNSConfig) error
|
applyDNSConfig(config HostDNSConfig) error
|
||||||
restoreHostDNS() error
|
restoreHostDNS() error
|
||||||
supportCustomPort() bool
|
supportCustomPort() bool
|
||||||
|
restoreUncleanShutdownDNS(storedDNSAddress *netip.Addr) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type HostDNSConfig struct {
|
type HostDNSConfig struct {
|
||||||
@@ -30,6 +32,7 @@ type mockHostConfigurator struct {
|
|||||||
applyDNSConfigFunc func(config HostDNSConfig) error
|
applyDNSConfigFunc func(config HostDNSConfig) error
|
||||||
restoreHostDNSFunc func() error
|
restoreHostDNSFunc func() error
|
||||||
supportCustomPortFunc func() bool
|
supportCustomPortFunc func() bool
|
||||||
|
restoreUncleanShutdownDNSFunc func(*netip.Addr) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockHostConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
func (m *mockHostConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
||||||
@@ -53,11 +56,19 @@ func (m *mockHostConfigurator) supportCustomPort() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockHostConfigurator) restoreUncleanShutdownDNS(storedDNSAddress *netip.Addr) error {
|
||||||
|
if m.restoreUncleanShutdownDNSFunc != nil {
|
||||||
|
return m.restoreUncleanShutdownDNSFunc(storedDNSAddress)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("method restoreUncleanShutdownDNS is not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
func newNoopHostMocker() hostManager {
|
func newNoopHostMocker() hostManager {
|
||||||
return &mockHostConfigurator{
|
return &mockHostConfigurator{
|
||||||
applyDNSConfigFunc: func(config HostDNSConfig) error { return nil },
|
applyDNSConfigFunc: func(config HostDNSConfig) error { return nil },
|
||||||
restoreHostDNSFunc: func() error { return nil },
|
restoreHostDNSFunc: func() error { return nil },
|
||||||
supportCustomPortFunc: func() bool { return true },
|
supportCustomPortFunc: func() bool { return true },
|
||||||
|
restoreUncleanShutdownDNSFunc: func(*netip.Addr) error { return nil },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
|
import "net/netip"
|
||||||
|
|
||||||
type androidHostManager struct {
|
type androidHostManager struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHostManager(wgInterface WGIface) (hostManager, error) {
|
func newHostManager() (hostManager, error) {
|
||||||
return &androidHostManager{}, nil
|
return &androidHostManager{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,3 +20,7 @@ func (a androidHostManager) restoreHostDNS() error {
|
|||||||
func (a androidHostManager) supportCustomPort() bool {
|
func (a androidHostManager) supportCustomPort() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a androidHostManager) restoreUncleanShutdownDNS(*netip.Addr) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/netip"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -34,7 +36,7 @@ type systemConfigurator struct {
|
|||||||
createdKeys map[string]struct{}
|
createdKeys map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHostManager(_ WGIface) (hostManager, error) {
|
func newHostManager() (hostManager, error) {
|
||||||
return &systemConfigurator{
|
return &systemConfigurator{
|
||||||
createdKeys: make(map[string]struct{}),
|
createdKeys: make(map[string]struct{}),
|
||||||
}, nil
|
}, nil
|
||||||
@@ -50,17 +52,22 @@ func (s *systemConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
if config.RouteAll {
|
if config.RouteAll {
|
||||||
err = s.addDNSSetupForAll(config.ServerIP, config.ServerPort)
|
err = s.addDNSSetupForAll(config.ServerIP, config.ServerPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("add dns setup for all: %w", err)
|
||||||
}
|
}
|
||||||
} else if s.primaryServiceID != "" {
|
} else if s.primaryServiceID != "" {
|
||||||
err = s.removeKeyFromSystemConfig(getKeyWithInput(primaryServiceSetupKeyFormat, s.primaryServiceID))
|
err = s.removeKeyFromSystemConfig(getKeyWithInput(primaryServiceSetupKeyFormat, s.primaryServiceID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("remote key from system config: %w", err)
|
||||||
}
|
}
|
||||||
s.primaryServiceID = ""
|
s.primaryServiceID = ""
|
||||||
log.Infof("removed %s:%d as main DNS resolver for this peer", config.ServerIP, config.ServerPort)
|
log.Infof("removed %s:%d as main DNS resolver for this peer", config.ServerIP, config.ServerPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create a file for unclean shutdown detection
|
||||||
|
if err := createUncleanShutdownIndicator(); err != nil {
|
||||||
|
log.Errorf("failed to create unclean shutdown file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
searchDomains []string
|
searchDomains []string
|
||||||
matchDomains []string
|
matchDomains []string
|
||||||
@@ -85,7 +92,7 @@ func (s *systemConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
err = s.removeKeyFromSystemConfig(matchKey)
|
err = s.removeKeyFromSystemConfig(matchKey)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("add match domains: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
searchKey := getKeyWithInput(netbirdDNSStateKeyFormat, searchSuffix)
|
searchKey := getKeyWithInput(netbirdDNSStateKeyFormat, searchSuffix)
|
||||||
@@ -96,7 +103,7 @@ func (s *systemConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
err = s.removeKeyFromSystemConfig(searchKey)
|
err = s.removeKeyFromSystemConfig(searchKey)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("add search domains: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -119,7 +126,11 @@ func (s *systemConfigurator) restoreHostDNS() error {
|
|||||||
_, err := runSystemConfigCommand(wrapCommand(lines))
|
_, err := runSystemConfigCommand(wrapCommand(lines))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("got an error while cleaning the system configuration: %s", err)
|
log.Errorf("got an error while cleaning the system configuration: %s", err)
|
||||||
return err
|
return fmt.Errorf("clean system: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := removeUncleanShutdownIndicator(); err != nil {
|
||||||
|
log.Errorf("failed to remove unclean shutdown file: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -129,7 +140,7 @@ func (s *systemConfigurator) removeKeyFromSystemConfig(key string) error {
|
|||||||
line := buildRemoveKeyOperation(key)
|
line := buildRemoveKeyOperation(key)
|
||||||
_, err := runSystemConfigCommand(wrapCommand(line))
|
_, err := runSystemConfigCommand(wrapCommand(line))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("remove key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(s.createdKeys, key)
|
delete(s.createdKeys, key)
|
||||||
@@ -140,7 +151,7 @@ func (s *systemConfigurator) removeKeyFromSystemConfig(key string) error {
|
|||||||
func (s *systemConfigurator) addSearchDomains(key, domains string, ip string, port int) error {
|
func (s *systemConfigurator) addSearchDomains(key, domains string, ip string, port int) error {
|
||||||
err := s.addDNSState(key, domains, ip, port, true)
|
err := s.addDNSState(key, domains, ip, port, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("add dns state: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("added %d search domains to the state. Domain list: %s", len(strings.Split(domains, " ")), domains)
|
log.Infof("added %d search domains to the state. Domain list: %s", len(strings.Split(domains, " ")), domains)
|
||||||
@@ -153,7 +164,7 @@ func (s *systemConfigurator) addSearchDomains(key, domains string, ip string, po
|
|||||||
func (s *systemConfigurator) addMatchDomains(key, domains, dnsServer string, port int) error {
|
func (s *systemConfigurator) addMatchDomains(key, domains, dnsServer string, port int) error {
|
||||||
err := s.addDNSState(key, domains, dnsServer, port, false)
|
err := s.addDNSState(key, domains, dnsServer, port, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("add dns state: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("added %d match domains to the state. Domain list: %s", len(strings.Split(domains, " ")), domains)
|
log.Infof("added %d match domains to the state. Domain list: %s", len(strings.Split(domains, " ")), domains)
|
||||||
@@ -178,33 +189,37 @@ func (s *systemConfigurator) addDNSState(state, domains, dnsServer string, port
|
|||||||
|
|
||||||
_, err := runSystemConfigCommand(stdinCommands)
|
_, err := runSystemConfigCommand(stdinCommands)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got error while applying state for domains %s, error: %s", domains, err)
|
return fmt.Errorf("applying state for domains %s, error: %w", domains, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *systemConfigurator) addDNSSetupForAll(dnsServer string, port int) error {
|
func (s *systemConfigurator) addDNSSetupForAll(dnsServer string, port int) error {
|
||||||
primaryServiceKey, existingNameserver := s.getPrimaryService()
|
primaryServiceKey, existingNameserver, err := s.getPrimaryService()
|
||||||
if primaryServiceKey == "" {
|
if err != nil || primaryServiceKey == "" {
|
||||||
return fmt.Errorf("couldn't find the primary service key")
|
return fmt.Errorf("couldn't find the primary service key: %w", err)
|
||||||
}
|
}
|
||||||
err := s.addDNSSetup(getKeyWithInput(primaryServiceSetupKeyFormat, primaryServiceKey), dnsServer, port, existingNameserver)
|
|
||||||
|
err = s.addDNSSetup(getKeyWithInput(primaryServiceSetupKeyFormat, primaryServiceKey), dnsServer, port, existingNameserver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("add dns setup: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("configured %s:%d as main DNS resolver for this peer", dnsServer, port)
|
log.Infof("configured %s:%d as main DNS resolver for this peer", dnsServer, port)
|
||||||
s.primaryServiceID = primaryServiceKey
|
s.primaryServiceID = primaryServiceKey
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *systemConfigurator) getPrimaryService() (string, string) {
|
func (s *systemConfigurator) getPrimaryService() (string, string, error) {
|
||||||
line := buildCommandLine("show", globalIPv4State, "")
|
line := buildCommandLine("show", globalIPv4State, "")
|
||||||
stdinCommands := wrapCommand(line)
|
stdinCommands := wrapCommand(line)
|
||||||
|
|
||||||
b, err := runSystemConfigCommand(stdinCommands)
|
b, err := runSystemConfigCommand(stdinCommands)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("got error while sending the command: ", err)
|
return "", "", fmt.Errorf("sending the command: %w", err)
|
||||||
return "", ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scanner := bufio.NewScanner(bytes.NewReader(b))
|
scanner := bufio.NewScanner(bytes.NewReader(b))
|
||||||
primaryService := ""
|
primaryService := ""
|
||||||
router := ""
|
router := ""
|
||||||
@@ -217,7 +232,11 @@ func (s *systemConfigurator) getPrimaryService() (string, string) {
|
|||||||
router = strings.TrimSpace(strings.Split(text, ":")[1])
|
router = strings.TrimSpace(strings.Split(text, ":")[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return primaryService, router
|
if err := scanner.Err(); err != nil && err != io.EOF {
|
||||||
|
return primaryService, router, fmt.Errorf("scan: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return primaryService, router, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *systemConfigurator) addDNSSetup(setupKey, dnsServer string, port int, existingDNSServer string) error {
|
func (s *systemConfigurator) addDNSSetup(setupKey, dnsServer string, port int, existingDNSServer string) error {
|
||||||
@@ -228,7 +247,14 @@ func (s *systemConfigurator) addDNSSetup(setupKey, dnsServer string, port int, e
|
|||||||
stdinCommands := wrapCommand(addDomainCommand)
|
stdinCommands := wrapCommand(addDomainCommand)
|
||||||
_, err := runSystemConfigCommand(stdinCommands)
|
_, err := runSystemConfigCommand(stdinCommands)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got error while applying dns setup, error: %s", err)
|
return fmt.Errorf("applying dns setup, error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *systemConfigurator) restoreUncleanShutdownDNS(*netip.Addr) error {
|
||||||
|
if err := s.restoreHostDNS(); err != nil {
|
||||||
|
return fmt.Errorf("restoring dns via scutil: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -266,7 +292,7 @@ func runSystemConfigCommand(command string) ([]byte, error) {
|
|||||||
cmd.Stdin = strings.NewReader(command)
|
cmd.Stdin = strings.NewReader(command)
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("got error while running system configuration command: \"%s\", error: %s", command, err)
|
return nil, fmt.Errorf("running system configuration command: \"%s\", error: %w", command, err)
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@@ -20,7 +22,7 @@ func newHostManager(dnsManager IosDnsManager) (hostManager, error) {
|
|||||||
func (a iosHostManager) applyDNSConfig(config HostDNSConfig) error {
|
func (a iosHostManager) applyDNSConfig(config HostDNSConfig) error {
|
||||||
jsonData, err := json.Marshal(config)
|
jsonData, err := json.Marshal(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("marshal: %w", err)
|
||||||
}
|
}
|
||||||
jsonString := string(jsonData)
|
jsonString := string(jsonData)
|
||||||
log.Debugf("Applying DNS settings: %s", jsonString)
|
log.Debugf("Applying DNS settings: %s", jsonString)
|
||||||
@@ -35,3 +37,7 @@ func (a iosHostManager) restoreHostDNS() error {
|
|||||||
func (a iosHostManager) supportCustomPort() bool {
|
func (a iosHostManager) supportCustomPort() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a iosHostManager) restoreUncleanShutdownDNS(*netip.Addr) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -19,8 +21,27 @@ const (
|
|||||||
resolvConfManager
|
resolvConfManager
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrUnknownOsManagerType = errors.New("unknown os manager type")
|
||||||
|
|
||||||
type osManagerType int
|
type osManagerType int
|
||||||
|
|
||||||
|
func newOsManagerType(osManager string) (osManagerType, error) {
|
||||||
|
switch osManager {
|
||||||
|
case "netbird":
|
||||||
|
return fileManager, nil
|
||||||
|
case "file":
|
||||||
|
return netbirdManager, nil
|
||||||
|
case "networkManager":
|
||||||
|
return networkManager, nil
|
||||||
|
case "systemd":
|
||||||
|
return systemdManager, nil
|
||||||
|
case "resolvconf":
|
||||||
|
return resolvConfManager, nil
|
||||||
|
default:
|
||||||
|
return 0, ErrUnknownOsManagerType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (t osManagerType) String() string {
|
func (t osManagerType) String() string {
|
||||||
switch t {
|
switch t {
|
||||||
case netbirdManager:
|
case netbirdManager:
|
||||||
@@ -38,13 +59,17 @@ func (t osManagerType) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHostManager(wgInterface WGIface) (hostManager, error) {
|
func newHostManager(wgInterface string) (hostManager, error) {
|
||||||
osManager, err := getOSDNSManagerType()
|
osManager, err := getOSDNSManagerType()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("discovered mode is: %s", osManager)
|
log.Debugf("discovered mode is: %s", osManager)
|
||||||
|
return newHostManagerFromType(wgInterface, osManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHostManagerFromType(wgInterface string, osManager osManagerType) (hostManager, error) {
|
||||||
switch osManager {
|
switch osManager {
|
||||||
case networkManager:
|
case networkManager:
|
||||||
return newNetworkManagerDbusConfigurator(wgInterface)
|
return newNetworkManagerDbusConfigurator(wgInterface)
|
||||||
@@ -58,12 +83,15 @@ func newHostManager(wgInterface WGIface) (hostManager, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getOSDNSManagerType() (osManagerType, error) {
|
func getOSDNSManagerType() (osManagerType, error) {
|
||||||
|
|
||||||
file, err := os.Open(defaultResolvConfPath)
|
file, err := os.Open(defaultResolvConfPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("unable to open %s for checking owner, got error: %s", defaultResolvConfPath, err)
|
return 0, fmt.Errorf("unable to open %s for checking owner, got error: %w", defaultResolvConfPath, err)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer func() {
|
||||||
|
if err := file.Close(); err != nil {
|
||||||
|
log.Errorf("close file %s: %s", defaultResolvConfPath, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
scanner := bufio.NewScanner(file)
|
scanner := bufio.NewScanner(file)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
@@ -101,6 +129,10 @@ func getOSDNSManagerType() (osManagerType, error) {
|
|||||||
return resolvConfManager, nil
|
return resolvConfManager, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := scanner.Err(); err != nil && err != io.EOF {
|
||||||
|
return 0, fmt.Errorf("scan: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return fileManager, nil
|
return fileManager, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -9,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
dnsPolicyConfigMatchPath = "SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DnsPolicyConfig\\NetBird-Match"
|
dnsPolicyConfigMatchPath = `SYSTEM\CurrentControlSet\Services\Dnscache\Parameters\DnsPolicyConfig\NetBird-Match`
|
||||||
dnsPolicyConfigVersionKey = "Version"
|
dnsPolicyConfigVersionKey = "Version"
|
||||||
dnsPolicyConfigVersionValue = 2
|
dnsPolicyConfigVersionValue = 2
|
||||||
dnsPolicyConfigNameKey = "Name"
|
dnsPolicyConfigNameKey = "Name"
|
||||||
@@ -19,7 +21,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
interfaceConfigPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"
|
interfaceConfigPath = `SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces`
|
||||||
interfaceConfigNameServerKey = "NameServer"
|
interfaceConfigNameServerKey = "NameServer"
|
||||||
interfaceConfigSearchListKey = "SearchList"
|
interfaceConfigSearchListKey = "SearchList"
|
||||||
)
|
)
|
||||||
@@ -34,12 +36,16 @@ func newHostManager(wgInterface WGIface) (hostManager, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return newHostManagerWithGuid(guid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHostManagerWithGuid(guid string) (hostManager, error) {
|
||||||
return ®istryConfigurator{
|
return ®istryConfigurator{
|
||||||
guid: guid,
|
guid: guid,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *registryConfigurator) supportCustomPort() bool {
|
func (r *registryConfigurator) supportCustomPort() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,17 +54,22 @@ func (r *registryConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
if config.RouteAll {
|
if config.RouteAll {
|
||||||
err = r.addDNSSetupForAll(config.ServerIP)
|
err = r.addDNSSetupForAll(config.ServerIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("add dns setup: %w", err)
|
||||||
}
|
}
|
||||||
} else if r.routingAll {
|
} else if r.routingAll {
|
||||||
err = r.deleteInterfaceRegistryKeyProperty(interfaceConfigNameServerKey)
|
err = r.deleteInterfaceRegistryKeyProperty(interfaceConfigNameServerKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("delete interface registry key property: %w", err)
|
||||||
}
|
}
|
||||||
r.routingAll = false
|
r.routingAll = false
|
||||||
log.Infof("removed %s as main DNS forwarder for this peer", config.ServerIP)
|
log.Infof("removed %s as main DNS forwarder for this peer", config.ServerIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create a file for unclean shutdown detection
|
||||||
|
if err := createUncleanShutdownIndicator(r.guid); err != nil {
|
||||||
|
log.Errorf("failed to create unclean shutdown file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
searchDomains []string
|
searchDomains []string
|
||||||
matchDomains []string
|
matchDomains []string
|
||||||
@@ -80,12 +91,12 @@ func (r *registryConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
err = removeRegistryKeyFromDNSPolicyConfig(dnsPolicyConfigMatchPath)
|
err = removeRegistryKeyFromDNSPolicyConfig(dnsPolicyConfigMatchPath)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("add dns match policy: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = r.updateSearchDomains(searchDomains)
|
err = r.updateSearchDomains(searchDomains)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("update search domains: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -94,7 +105,7 @@ func (r *registryConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
func (r *registryConfigurator) addDNSSetupForAll(ip string) error {
|
func (r *registryConfigurator) addDNSSetupForAll(ip string) error {
|
||||||
err := r.setInterfaceRegistryKeyStringValue(interfaceConfigNameServerKey, ip)
|
err := r.setInterfaceRegistryKeyStringValue(interfaceConfigNameServerKey, ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("adding dns setup for all failed with error: %s", err)
|
return fmt.Errorf("adding dns setup for all failed with error: %w", err)
|
||||||
}
|
}
|
||||||
r.routingAll = true
|
r.routingAll = true
|
||||||
log.Infof("configured %s:53 as main DNS forwarder for this peer", ip)
|
log.Infof("configured %s:53 as main DNS forwarder for this peer", ip)
|
||||||
@@ -106,33 +117,33 @@ func (r *registryConfigurator) addDNSMatchPolicy(domains []string, ip string) er
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
err = registry.DeleteKey(registry.LOCAL_MACHINE, dnsPolicyConfigMatchPath)
|
err = registry.DeleteKey(registry.LOCAL_MACHINE, dnsPolicyConfigMatchPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to remove existing key from registry, key: HKEY_LOCAL_MACHINE\\%s, error: %s", dnsPolicyConfigMatchPath, err)
|
return fmt.Errorf("unable to remove existing key from registry, key: HKEY_LOCAL_MACHINE\\%s, error: %w", dnsPolicyConfigMatchPath, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
regKey, _, err := registry.CreateKey(registry.LOCAL_MACHINE, dnsPolicyConfigMatchPath, registry.SET_VALUE)
|
regKey, _, err := registry.CreateKey(registry.LOCAL_MACHINE, dnsPolicyConfigMatchPath, registry.SET_VALUE)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create registry key, key: HKEY_LOCAL_MACHINE\\%s, error: %s", dnsPolicyConfigMatchPath, err)
|
return fmt.Errorf("unable to create registry key, key: HKEY_LOCAL_MACHINE\\%s, error: %w", dnsPolicyConfigMatchPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = regKey.SetDWordValue(dnsPolicyConfigVersionKey, dnsPolicyConfigVersionValue)
|
err = regKey.SetDWordValue(dnsPolicyConfigVersionKey, dnsPolicyConfigVersionValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to set registry value for %s, error: %s", dnsPolicyConfigVersionKey, err)
|
return fmt.Errorf("unable to set registry value for %s, error: %w", dnsPolicyConfigVersionKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = regKey.SetStringsValue(dnsPolicyConfigNameKey, domains)
|
err = regKey.SetStringsValue(dnsPolicyConfigNameKey, domains)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to set registry value for %s, error: %s", dnsPolicyConfigNameKey, err)
|
return fmt.Errorf("unable to set registry value for %s, error: %w", dnsPolicyConfigNameKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = regKey.SetStringValue(dnsPolicyConfigGenericDNSServersKey, ip)
|
err = regKey.SetStringValue(dnsPolicyConfigGenericDNSServersKey, ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to set registry value for %s, error: %s", dnsPolicyConfigGenericDNSServersKey, err)
|
return fmt.Errorf("unable to set registry value for %s, error: %w", dnsPolicyConfigGenericDNSServersKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = regKey.SetDWordValue(dnsPolicyConfigConfigOptionsKey, dnsPolicyConfigConfigOptionsValue)
|
err = regKey.SetDWordValue(dnsPolicyConfigConfigOptionsKey, dnsPolicyConfigConfigOptionsValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to set registry value for %s, error: %s", dnsPolicyConfigConfigOptionsKey, err)
|
return fmt.Errorf("unable to set registry value for %s, error: %w", dnsPolicyConfigConfigOptionsKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("added %d match domains to the state. Domain list: %s", len(domains), domains)
|
log.Infof("added %d match domains to the state. Domain list: %s", len(domains), domains)
|
||||||
@@ -141,18 +152,25 @@ func (r *registryConfigurator) addDNSMatchPolicy(domains []string, ip string) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *registryConfigurator) restoreHostDNS() error {
|
func (r *registryConfigurator) restoreHostDNS() error {
|
||||||
err := removeRegistryKeyFromDNSPolicyConfig(dnsPolicyConfigMatchPath)
|
if err := removeRegistryKeyFromDNSPolicyConfig(dnsPolicyConfigMatchPath); err != nil {
|
||||||
if err != nil {
|
log.Errorf("remove registry key from dns policy config: %s", err)
|
||||||
log.Error(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.deleteInterfaceRegistryKeyProperty(interfaceConfigSearchListKey)
|
if err := r.deleteInterfaceRegistryKeyProperty(interfaceConfigSearchListKey); err != nil {
|
||||||
|
return fmt.Errorf("remove interface registry key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := removeUncleanShutdownIndicator(); err != nil {
|
||||||
|
log.Errorf("failed to remove unclean shutdown file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *registryConfigurator) updateSearchDomains(domains []string) error {
|
func (r *registryConfigurator) updateSearchDomains(domains []string) error {
|
||||||
err := r.setInterfaceRegistryKeyStringValue(interfaceConfigSearchListKey, strings.Join(domains, ","))
|
err := r.setInterfaceRegistryKeyStringValue(interfaceConfigSearchListKey, strings.Join(domains, ","))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("adding search domain failed with error: %s", err)
|
return fmt.Errorf("adding search domain failed with error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("updated the search domains in the registry with %d domains. Domain list: %s", len(domains), domains)
|
log.Infof("updated the search domains in the registry with %d domains. Domain list: %s", len(domains), domains)
|
||||||
@@ -163,13 +181,13 @@ func (r *registryConfigurator) updateSearchDomains(domains []string) error {
|
|||||||
func (r *registryConfigurator) setInterfaceRegistryKeyStringValue(key, value string) error {
|
func (r *registryConfigurator) setInterfaceRegistryKeyStringValue(key, value string) error {
|
||||||
regKey, err := r.getInterfaceRegistryKey()
|
regKey, err := r.getInterfaceRegistryKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("get interface registry key: %w", err)
|
||||||
}
|
}
|
||||||
defer regKey.Close()
|
defer closer(regKey)
|
||||||
|
|
||||||
err = regKey.SetStringValue(key, value)
|
err = regKey.SetStringValue(key, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("applying key %s with value \"%s\" for interface failed with error: %s", key, value, err)
|
return fmt.Errorf("applying key %s with value \"%s\" for interface failed with error: %w", key, value, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -178,13 +196,13 @@ func (r *registryConfigurator) setInterfaceRegistryKeyStringValue(key, value str
|
|||||||
func (r *registryConfigurator) deleteInterfaceRegistryKeyProperty(propertyKey string) error {
|
func (r *registryConfigurator) deleteInterfaceRegistryKeyProperty(propertyKey string) error {
|
||||||
regKey, err := r.getInterfaceRegistryKey()
|
regKey, err := r.getInterfaceRegistryKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("get interface registry key: %w", err)
|
||||||
}
|
}
|
||||||
defer regKey.Close()
|
defer closer(regKey)
|
||||||
|
|
||||||
err = regKey.DeleteValue(propertyKey)
|
err = regKey.DeleteValue(propertyKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("deleting registry key %s for interface failed with error: %s", propertyKey, err)
|
return fmt.Errorf("deleting registry key %s for interface failed with error: %w", propertyKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -197,20 +215,33 @@ func (r *registryConfigurator) getInterfaceRegistryKey() (registry.Key, error) {
|
|||||||
|
|
||||||
regKey, err := registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.SET_VALUE)
|
regKey, err := registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.SET_VALUE)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return regKey, fmt.Errorf("unable to open the interface registry key, key: HKEY_LOCAL_MACHINE\\%s, error: %s", regKeyPath, err)
|
return regKey, fmt.Errorf("unable to open the interface registry key, key: HKEY_LOCAL_MACHINE\\%s, error: %w", regKeyPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return regKey, nil
|
return regKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *registryConfigurator) restoreUncleanShutdownDNS(*netip.Addr) error {
|
||||||
|
if err := r.restoreHostDNS(); err != nil {
|
||||||
|
return fmt.Errorf("restoring dns via registry: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func removeRegistryKeyFromDNSPolicyConfig(regKeyPath string) error {
|
func removeRegistryKeyFromDNSPolicyConfig(regKeyPath string) error {
|
||||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.QUERY_VALUE)
|
k, err := registry.OpenKey(registry.LOCAL_MACHINE, regKeyPath, registry.QUERY_VALUE)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
k.Close()
|
defer closer(k)
|
||||||
err = registry.DeleteKey(registry.LOCAL_MACHINE, regKeyPath)
|
err = registry.DeleteKey(registry.LOCAL_MACHINE, regKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to remove existing key from registry, key: HKEY_LOCAL_MACHINE\\%s, error: %s", regKeyPath, err)
|
return fmt.Errorf("unable to remove existing key from registry, key: HKEY_LOCAL_MACHINE\\%s, error: %w", regKeyPath, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func closer(closer io.Closer) {
|
||||||
|
if err := closer.Close(); err != nil {
|
||||||
|
log.Errorf("failed to close: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ func (d *localResolver) lookupRecord(r *dns.Msg) dns.RR {
|
|||||||
func (d *localResolver) registerRecord(record nbdns.SimpleRecord) error {
|
func (d *localResolver) registerRecord(record nbdns.SimpleRecord) error {
|
||||||
fullRecord, err := dns.NewRR(record.String())
|
fullRecord, err := dns.NewRR(record.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("register record: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fullRecord.Header().Rdlength = record.Len()
|
fullRecord.Header().Rdlength = record.Len()
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ package dns
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/godbus/dbus/v5"
|
"github.com/godbus/dbus/v5"
|
||||||
@@ -41,9 +43,13 @@ const (
|
|||||||
networkManagerDbusPrimaryDNSPriority int32 = -500
|
networkManagerDbusPrimaryDNSPriority int32 = -500
|
||||||
networkManagerDbusWithMatchDomainPriority int32 = 0
|
networkManagerDbusWithMatchDomainPriority int32 = 0
|
||||||
networkManagerDbusSearchDomainOnlyPriority int32 = 50
|
networkManagerDbusSearchDomainOnlyPriority int32 = 50
|
||||||
supportedNetworkManagerVersionConstraint = ">= 1.16, < 1.28"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var supportedNetworkManagerVersionConstraints = []string{
|
||||||
|
">= 1.16, < 1.27",
|
||||||
|
">= 1.44, < 1.45",
|
||||||
|
}
|
||||||
|
|
||||||
type networkManagerDbusConfigurator struct {
|
type networkManagerDbusConfigurator struct {
|
||||||
dbusLinkObject dbus.ObjectPath
|
dbusLinkObject dbus.ObjectPath
|
||||||
routingAll bool
|
routingAll bool
|
||||||
@@ -71,19 +77,19 @@ func (s networkManagerConnSettings) cleanDeprecatedSettings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNetworkManagerDbusConfigurator(wgInterface WGIface) (hostManager, error) {
|
func newNetworkManagerDbusConfigurator(wgInterface string) (hostManager, error) {
|
||||||
obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusObjectNode)
|
obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusObjectNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("get nm dbus: %w", err)
|
||||||
}
|
}
|
||||||
defer closeConn()
|
defer closeConn()
|
||||||
var s string
|
var s string
|
||||||
err = obj.Call(networkManagerDbusGetDeviceByIPIfaceMethod, dbusDefaultFlag, wgInterface.Name()).Store(&s)
|
err = obj.Call(networkManagerDbusGetDeviceByIPIfaceMethod, dbusDefaultFlag, wgInterface).Store(&s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("call: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("got network manager dbus Link Object: %s from net interface %s", s, wgInterface.Name())
|
log.Debugf("got network manager dbus Link Object: %s from net interface %s", s, wgInterface)
|
||||||
|
|
||||||
return &networkManagerDbusConfigurator{
|
return &networkManagerDbusConfigurator{
|
||||||
dbusLinkObject: dbus.ObjectPath(s),
|
dbusLinkObject: dbus.ObjectPath(s),
|
||||||
@@ -97,14 +103,14 @@ func (n *networkManagerDbusConfigurator) supportCustomPort() bool {
|
|||||||
func (n *networkManagerDbusConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
func (n *networkManagerDbusConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
||||||
connSettings, configVersion, err := n.getAppliedConnectionSettings()
|
connSettings, configVersion, err := n.getAppliedConnectionSettings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got an error while retrieving the applied connection settings, error: %s", err)
|
return fmt.Errorf("retrieving the applied connection settings, error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
connSettings.cleanDeprecatedSettings()
|
connSettings.cleanDeprecatedSettings()
|
||||||
|
|
||||||
dnsIP, err := netip.ParseAddr(config.ServerIP)
|
dnsIP, err := netip.ParseAddr(config.ServerIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to parse ip address, error: %s", err)
|
return fmt.Errorf("unable to parse ip address, error: %w", err)
|
||||||
}
|
}
|
||||||
convDNSIP := binary.LittleEndian.Uint32(dnsIP.AsSlice())
|
convDNSIP := binary.LittleEndian.Uint32(dnsIP.AsSlice())
|
||||||
connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSKey] = dbus.MakeVariant([]uint32{convDNSIP})
|
connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSKey] = dbus.MakeVariant([]uint32{convDNSIP})
|
||||||
@@ -145,23 +151,37 @@ func (n *networkManagerDbusConfigurator) applyDNSConfig(config HostDNSConfig) er
|
|||||||
connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSPriorityKey] = dbus.MakeVariant(priority)
|
connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSPriorityKey] = dbus.MakeVariant(priority)
|
||||||
connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSSearchKey] = dbus.MakeVariant(newDomainList)
|
connSettings[networkManagerDbusIPv4Key][networkManagerDbusDNSSearchKey] = dbus.MakeVariant(newDomainList)
|
||||||
|
|
||||||
|
// create a backup for unclean shutdown detection before adding domains, as these might end up in the resolv.conf file.
|
||||||
|
// The file content itself is not important for network-manager restoration
|
||||||
|
if err := createUncleanShutdownIndicator(defaultResolvConfPath, networkManager, dnsIP.String()); err != nil {
|
||||||
|
log.Errorf("failed to create unclean shutdown resolv.conf backup: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
log.Infof("adding %d search domains and %d match domains. Search list: %s , Match list: %s", len(searchDomains), len(matchDomains), searchDomains, matchDomains)
|
log.Infof("adding %d search domains and %d match domains. Search list: %s , Match list: %s", len(searchDomains), len(matchDomains), searchDomains, matchDomains)
|
||||||
err = n.reApplyConnectionSettings(connSettings, configVersion)
|
err = n.reApplyConnectionSettings(connSettings, configVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got an error while reapplying the connection with new settings, error: %s", err)
|
return fmt.Errorf("reapplying the connection with new settings, error: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *networkManagerDbusConfigurator) restoreHostDNS() error {
|
func (n *networkManagerDbusConfigurator) restoreHostDNS() error {
|
||||||
// once the interface is gone network manager cleans all config associated with it
|
// once the interface is gone network manager cleans all config associated with it
|
||||||
return n.deleteConnectionSettings()
|
if err := n.deleteConnectionSettings(); err != nil {
|
||||||
|
return fmt.Errorf("delete connection settings: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := removeUncleanShutdownIndicator(); err != nil {
|
||||||
|
log.Errorf("failed to remove unclean shutdown resolv.conf backup: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *networkManagerDbusConfigurator) getAppliedConnectionSettings() (networkManagerConnSettings, networkManagerConfigVersion, error) {
|
func (n *networkManagerDbusConfigurator) getAppliedConnectionSettings() (networkManagerConnSettings, networkManagerConfigVersion, error) {
|
||||||
obj, closeConn, err := getDbusObject(networkManagerDest, n.dbusLinkObject)
|
obj, closeConn, err := getDbusObject(networkManagerDest, n.dbusLinkObject)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, fmt.Errorf("got error while attempting to retrieve the applied connection settings, err: %s", err)
|
return nil, 0, fmt.Errorf("attempting to retrieve the applied connection settings, err: %w", err)
|
||||||
}
|
}
|
||||||
defer closeConn()
|
defer closeConn()
|
||||||
|
|
||||||
@@ -176,7 +196,7 @@ func (n *networkManagerDbusConfigurator) getAppliedConnectionSettings() (network
|
|||||||
err = obj.CallWithContext(ctx, networkManagerDbusDeviceGetAppliedConnectionMethod, dbusDefaultFlag,
|
err = obj.CallWithContext(ctx, networkManagerDbusDeviceGetAppliedConnectionMethod, dbusDefaultFlag,
|
||||||
networkManagerDbusDefaultBehaviorFlag).Store(&connSettings, &configVersion)
|
networkManagerDbusDefaultBehaviorFlag).Store(&connSettings, &configVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, fmt.Errorf("got error while calling GetAppliedConnection method with context, err: %s", err)
|
return nil, 0, fmt.Errorf("calling GetAppliedConnection method with context, err: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return connSettings, configVersion, nil
|
return connSettings, configVersion, nil
|
||||||
@@ -185,7 +205,7 @@ func (n *networkManagerDbusConfigurator) getAppliedConnectionSettings() (network
|
|||||||
func (n *networkManagerDbusConfigurator) reApplyConnectionSettings(connSettings networkManagerConnSettings, configVersion networkManagerConfigVersion) error {
|
func (n *networkManagerDbusConfigurator) reApplyConnectionSettings(connSettings networkManagerConnSettings, configVersion networkManagerConfigVersion) error {
|
||||||
obj, closeConn, err := getDbusObject(networkManagerDest, n.dbusLinkObject)
|
obj, closeConn, err := getDbusObject(networkManagerDest, n.dbusLinkObject)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got error while attempting to retrieve the applied connection settings, err: %s", err)
|
return fmt.Errorf("attempting to retrieve the applied connection settings, err: %w", err)
|
||||||
}
|
}
|
||||||
defer closeConn()
|
defer closeConn()
|
||||||
|
|
||||||
@@ -195,7 +215,7 @@ func (n *networkManagerDbusConfigurator) reApplyConnectionSettings(connSettings
|
|||||||
err = obj.CallWithContext(ctx, networkManagerDbusDeviceReapplyMethod, dbusDefaultFlag,
|
err = obj.CallWithContext(ctx, networkManagerDbusDeviceReapplyMethod, dbusDefaultFlag,
|
||||||
connSettings, configVersion, networkManagerDbusDefaultBehaviorFlag).Store()
|
connSettings, configVersion, networkManagerDbusDefaultBehaviorFlag).Store()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got error while calling ReApply method with context, err: %s", err)
|
return fmt.Errorf("calling ReApply method with context, err: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -204,21 +224,34 @@ func (n *networkManagerDbusConfigurator) reApplyConnectionSettings(connSettings
|
|||||||
func (n *networkManagerDbusConfigurator) deleteConnectionSettings() error {
|
func (n *networkManagerDbusConfigurator) deleteConnectionSettings() error {
|
||||||
obj, closeConn, err := getDbusObject(networkManagerDest, n.dbusLinkObject)
|
obj, closeConn, err := getDbusObject(networkManagerDest, n.dbusLinkObject)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got error while attempting to retrieve the applied connection settings, err: %s", err)
|
return fmt.Errorf("attempting to retrieve the applied connection settings, err: %w", err)
|
||||||
}
|
}
|
||||||
defer closeConn()
|
defer closeConn()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
// this call is required to remove the device for DNS cleanup, even if it fails
|
||||||
err = obj.CallWithContext(ctx, networkManagerDbusDeviceDeleteMethod, dbusDefaultFlag).Store()
|
err = obj.CallWithContext(ctx, networkManagerDbusDeviceDeleteMethod, dbusDefaultFlag).Store()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got error while calling delete method with context, err: %s", err)
|
var dbusErr dbus.Error
|
||||||
|
if errors.As(err, &dbusErr) && dbusErr.Name == dbus.ErrMsgUnknownMethod.Name {
|
||||||
|
// interface is gone already
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("calling delete method with context, err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *networkManagerDbusConfigurator) restoreUncleanShutdownDNS(*netip.Addr) error {
|
||||||
|
if err := n.restoreHostDNS(); err != nil {
|
||||||
|
return fmt.Errorf("restoring dns via network-manager: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func isNetworkManagerSupported() bool {
|
func isNetworkManagerSupported() bool {
|
||||||
return isNetworkManagerSupportedVersion() && isNetworkManagerSupportedMode()
|
return isNetworkManagerSupportedVersion() && isNetworkManagerSupportedMode()
|
||||||
}
|
}
|
||||||
@@ -250,13 +283,13 @@ func isNetworkManagerSupportedMode() bool {
|
|||||||
func getNetworkManagerDNSProperty(property string, store any) error {
|
func getNetworkManagerDNSProperty(property string, store any) error {
|
||||||
obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusDNSManagerObjectNode)
|
obj, closeConn, err := getDbusObject(networkManagerDest, networkManagerDbusDNSManagerObjectNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got error while attempting to retrieve the network manager dns manager object, error: %s", err)
|
return fmt.Errorf("attempting to retrieve the network manager dns manager object, error: %w", err)
|
||||||
}
|
}
|
||||||
defer closeConn()
|
defer closeConn()
|
||||||
|
|
||||||
v, e := obj.GetProperty(property)
|
v, e := obj.GetProperty(property)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return fmt.Errorf("got an error getting property %s: %v", property, e)
|
return fmt.Errorf("getting property %s: %w", property, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.Store(store)
|
return v.Store(store)
|
||||||
@@ -278,15 +311,26 @@ func isNetworkManagerSupportedVersion() bool {
|
|||||||
}
|
}
|
||||||
versionValue, err := parseVersion(value.Value().(string))
|
versionValue, err := parseVersion(value.Value().(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Errorf("nm: parse version: %s", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
constraints, err := version.NewConstraint(supportedNetworkManagerVersionConstraint)
|
var supported bool
|
||||||
|
for _, constraint := range supportedNetworkManagerVersionConstraints {
|
||||||
|
constr, err := version.NewConstraint(constraint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Errorf("nm: create constraint: %s", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return constraints.Check(versionValue)
|
if met := constr.Check(versionValue); met {
|
||||||
|
supported = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("network manager constraints [%s] met: %t", strings.Join(supportedNetworkManagerVersionConstraints, " | "), supported)
|
||||||
|
return supported
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseVersion(inputVersion string) (*version.Version, error) {
|
func parseVersion(inputVersion string) (*version.Version, error) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package dns
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -21,14 +22,14 @@ type resolvconf struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// supported "openresolv" only
|
// supported "openresolv" only
|
||||||
func newResolvConfConfigurator(wgInterface WGIface) (hostManager, error) {
|
func newResolvConfConfigurator(wgInterface string) (hostManager, error) {
|
||||||
resolvConfEntries, err := parseDefaultResolvConf()
|
resolvConfEntries, err := parseDefaultResolvConf()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Errorf("could not read original search domains from %s: %s", defaultResolvConfPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &resolvconf{
|
return &resolvconf{
|
||||||
ifaceName: wgInterface.Name(),
|
ifaceName: wgInterface,
|
||||||
originalSearchDomains: resolvConfEntries.searchDomains,
|
originalSearchDomains: resolvConfEntries.searchDomains,
|
||||||
originalNameServers: resolvConfEntries.nameServers,
|
originalNameServers: resolvConfEntries.nameServers,
|
||||||
othersConfigs: resolvConfEntries.others,
|
othersConfigs: resolvConfEntries.others,
|
||||||
@@ -44,7 +45,7 @@ func (r *resolvconf) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
if !config.RouteAll {
|
if !config.RouteAll {
|
||||||
err = r.restoreHostDNS()
|
err = r.restoreHostDNS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Errorf("restore host dns: %s", err)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("unable to configure DNS for this peer using resolvconf manager without a nameserver group with all domains configured")
|
return fmt.Errorf("unable to configure DNS for this peer using resolvconf manager without a nameserver group with all domains configured")
|
||||||
}
|
}
|
||||||
@@ -57,9 +58,14 @@ func (r *resolvconf) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
append([]string{config.ServerIP}, r.originalNameServers...),
|
append([]string{config.ServerIP}, r.originalNameServers...),
|
||||||
r.othersConfigs)
|
r.othersConfigs)
|
||||||
|
|
||||||
|
// create a backup for unclean shutdown detection before the resolv.conf is changed
|
||||||
|
if err := createUncleanShutdownIndicator(defaultResolvConfPath, resolvConfManager, config.ServerIP); err != nil {
|
||||||
|
log.Errorf("failed to create unclean shutdown resolv.conf backup: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
err = r.applyConfig(buf)
|
err = r.applyConfig(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("apply config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("added %d search domains. Search list: %s", len(searchDomainList), searchDomainList)
|
log.Infof("added %d search domains. Search list: %s", len(searchDomainList), searchDomainList)
|
||||||
@@ -67,20 +73,34 @@ func (r *resolvconf) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *resolvconf) restoreHostDNS() error {
|
func (r *resolvconf) restoreHostDNS() error {
|
||||||
|
// openresolv only, debian resolvconf doesn't support "-f"
|
||||||
cmd := exec.Command(resolvconfCommand, "-f", "-d", r.ifaceName)
|
cmd := exec.Command(resolvconfCommand, "-f", "-d", r.ifaceName)
|
||||||
_, err := cmd.Output()
|
_, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got an error while removing resolvconf configuration for %s interface, error: %s", r.ifaceName, err)
|
return fmt.Errorf("removing resolvconf configuration for %s interface, error: %w", r.ifaceName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := removeUncleanShutdownIndicator(); err != nil {
|
||||||
|
log.Errorf("failed to remove unclean shutdown resolv.conf backup: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resolvconf) applyConfig(content bytes.Buffer) error {
|
func (r *resolvconf) applyConfig(content bytes.Buffer) error {
|
||||||
|
// openresolv only, debian resolvconf doesn't support "-x"
|
||||||
cmd := exec.Command(resolvconfCommand, "-x", "-a", r.ifaceName)
|
cmd := exec.Command(resolvconfCommand, "-x", "-a", r.ifaceName)
|
||||||
cmd.Stdin = &content
|
cmd.Stdin = &content
|
||||||
_, err := cmd.Output()
|
_, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got an error while applying resolvconf configuration for %s interface, error: %s", r.ifaceName, err)
|
return fmt.Errorf("applying resolvconf configuration for %s interface, error: %w", r.ifaceName, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *resolvconf) restoreUncleanShutdownDNS(*netip.Addr) error {
|
||||||
|
if err := r.restoreHostDNS(); err != nil {
|
||||||
|
return fmt.Errorf("restoring dns for interface %s: %w", r.ifaceName, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,10 +31,13 @@ func (r *responseWriter) RemoteAddr() net.Addr {
|
|||||||
func (r *responseWriter) WriteMsg(msg *dns.Msg) error {
|
func (r *responseWriter) WriteMsg(msg *dns.Msg) error {
|
||||||
buff, err := msg.Pack()
|
buff, err := msg.Pack()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("pack: %w", err)
|
||||||
}
|
}
|
||||||
_, err = r.Write(buff)
|
|
||||||
return err
|
if _, err := r.Write(buff); err != nil {
|
||||||
|
return fmt.Errorf("write: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write writes a raw buffer back to the client.
|
// Write writes a raw buffer back to the client.
|
||||||
|
|||||||
@@ -142,12 +142,15 @@ func (s *DefaultServer) Initialize() (err error) {
|
|||||||
if s.permanent {
|
if s.permanent {
|
||||||
err = s.service.Listen()
|
err = s.service.Listen()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("service listen: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.hostManager, err = s.initialize()
|
s.hostManager, err = s.initialize()
|
||||||
return err
|
if err != nil {
|
||||||
|
return fmt.Errorf("initialize: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DnsIP returns the DNS resolver server IP address
|
// DnsIP returns the DNS resolver server IP address
|
||||||
@@ -225,7 +228,7 @@ func (s *DefaultServer) UpdateDNSServer(serial uint64, update nbdns.Config) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := s.applyConfiguration(update); err != nil {
|
if err := s.applyConfiguration(update); err != nil {
|
||||||
return err
|
return fmt.Errorf("apply configuration: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.updateSerial = serial
|
s.updateSerial = serial
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
func (s *DefaultServer) initialize() (manager hostManager, err error) {
|
func (s *DefaultServer) initialize() (manager hostManager, err error) {
|
||||||
return newHostManager(s.wgInterface)
|
return newHostManager()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,5 +3,5 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
func (s *DefaultServer) initialize() (manager hostManager, err error) {
|
func (s *DefaultServer) initialize() (manager hostManager, err error) {
|
||||||
return newHostManager(s.wgInterface)
|
return newHostManager()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,5 +3,5 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
func (s *DefaultServer) initialize() (manager hostManager, err error) {
|
func (s *DefaultServer) initialize() (manager hostManager, err error) {
|
||||||
return newHostManager(s.wgInterface)
|
return newHostManager(s.wgInterface.Name())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ type serviceViaListener struct {
|
|||||||
customAddr *netip.AddrPort
|
customAddr *netip.AddrPort
|
||||||
server *dns.Server
|
server *dns.Server
|
||||||
listenIP string
|
listenIP string
|
||||||
listenPort int
|
listenPort uint16
|
||||||
listenerIsRunning bool
|
listenerIsRunning bool
|
||||||
listenerFlagLock sync.Mutex
|
listenerFlagLock sync.Mutex
|
||||||
ebpfService ebpfMgr.Manager
|
ebpfService ebpfMgr.Manager
|
||||||
@@ -63,18 +63,9 @@ func (s *serviceViaListener) Listen() error {
|
|||||||
s.listenIP, s.listenPort, err = s.evalListenAddress()
|
s.listenIP, s.listenPort, err = s.evalListenAddress()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to eval runtime address: %s", err)
|
log.Errorf("failed to eval runtime address: %s", err)
|
||||||
return err
|
return fmt.Errorf("eval listen address: %w", err)
|
||||||
}
|
}
|
||||||
s.server.Addr = fmt.Sprintf("%s:%d", s.listenIP, s.listenPort)
|
s.server.Addr = fmt.Sprintf("%s:%d", s.listenIP, s.listenPort)
|
||||||
|
|
||||||
if s.shouldApplyPortFwd() {
|
|
||||||
s.ebpfService = ebpf.GetEbpfManagerInstance()
|
|
||||||
err = s.ebpfService.LoadDNSFwd(s.listenIP, s.listenPort)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("failed to load DNS port forwarder, custom port may not work well on some Linux operating systems: %s", err)
|
|
||||||
s.ebpfService = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Debugf("starting dns on %s", s.server.Addr)
|
log.Debugf("starting dns on %s", s.server.Addr)
|
||||||
go func() {
|
go func() {
|
||||||
s.setListenerStatus(true)
|
s.setListenerStatus(true)
|
||||||
@@ -128,7 +119,7 @@ func (s *serviceViaListener) RuntimePort() int {
|
|||||||
if s.ebpfService != nil {
|
if s.ebpfService != nil {
|
||||||
return defaultPort
|
return defaultPort
|
||||||
} else {
|
} else {
|
||||||
return s.listenPort
|
return int(s.listenPort)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,54 +131,112 @@ func (s *serviceViaListener) setListenerStatus(running bool) {
|
|||||||
s.listenerIsRunning = running
|
s.listenerIsRunning = running
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceViaListener) getFirstListenerAvailable() (string, int, error) {
|
// evalListenAddress figure out the listen address for the DNS server
|
||||||
ips := []string{defaultIP, customIP}
|
// first check the 53 port availability on WG interface or lo, if not success
|
||||||
if runtime.GOOS != "darwin" {
|
// pick a random port on WG interface for eBPF, if not success
|
||||||
ips = append([]string{s.wgInterface.Address().IP.String()}, ips...)
|
// check the 5053 port availability on WG interface or lo without eBPF usage,
|
||||||
|
func (s *serviceViaListener) evalListenAddress() (string, uint16, error) {
|
||||||
|
if s.customAddr != nil {
|
||||||
|
return s.customAddr.Addr().String(), s.customAddr.Port(), nil
|
||||||
}
|
}
|
||||||
ports := []int{defaultPort, customPort}
|
|
||||||
for _, port := range ports {
|
ip, ok := s.testFreePort(defaultPort)
|
||||||
|
if ok {
|
||||||
|
return ip, defaultPort, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ebpfSrv, port, ok := s.tryToUseeBPF()
|
||||||
|
if ok {
|
||||||
|
s.ebpfService = ebpfSrv
|
||||||
|
return s.wgInterface.Address().IP.String(), port, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, ok = s.testFreePort(customPort)
|
||||||
|
if ok {
|
||||||
|
return ip, customPort, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", 0, fmt.Errorf("failed to find a free port for DNS server")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serviceViaListener) testFreePort(port int) (string, bool) {
|
||||||
|
var ips []string
|
||||||
|
if runtime.GOOS != "darwin" {
|
||||||
|
ips = []string{s.wgInterface.Address().IP.String(), defaultIP, customIP}
|
||||||
|
} else {
|
||||||
|
ips = []string{defaultIP, customIP}
|
||||||
|
}
|
||||||
|
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
|
if !s.tryToBind(ip, port) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip, true
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serviceViaListener) tryToBind(ip string, port int) bool {
|
||||||
addrString := fmt.Sprintf("%s:%d", ip, port)
|
addrString := fmt.Sprintf("%s:%d", ip, port)
|
||||||
udpAddr := net.UDPAddrFromAddrPort(netip.MustParseAddrPort(addrString))
|
udpAddr := net.UDPAddrFromAddrPort(netip.MustParseAddrPort(addrString))
|
||||||
probeListener, err := net.ListenUDP("udp", udpAddr)
|
probeListener, err := net.ListenUDP("udp", udpAddr)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
|
log.Warnf("binding dns on %s is not available, error: %s", addrString, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
err = probeListener.Close()
|
err = probeListener.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("got an error closing the probe listener, error: %s", err)
|
log.Errorf("got an error closing the probe listener, error: %s", err)
|
||||||
}
|
}
|
||||||
return ip, port, nil
|
|
||||||
}
|
|
||||||
log.Warnf("binding dns on %s is not available, error: %s", addrString, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", 0, fmt.Errorf("unable to find an unused ip and port combination. IPs tested: %v and ports %v", ips, ports)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *serviceViaListener) evalListenAddress() (string, int, error) {
|
|
||||||
if s.customAddr != nil {
|
|
||||||
return s.customAddr.Addr().String(), int(s.customAddr.Port()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.getFirstListenerAvailable()
|
|
||||||
}
|
|
||||||
|
|
||||||
// shouldApplyPortFwd decides whether to apply eBPF program to capture DNS traffic on port 53.
|
|
||||||
// This is needed because on some operating systems if we start a DNS server not on a default port 53, the domain name
|
|
||||||
// resolution won't work.
|
|
||||||
// So, in case we are running on Linux and picked a non-default port (53) we should fall back to the eBPF solution that will capture
|
|
||||||
// traffic on port 53 and forward it to a local DNS server running on 5053.
|
|
||||||
func (s *serviceViaListener) shouldApplyPortFwd() bool {
|
|
||||||
if runtime.GOOS != "linux" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.customAddr != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.listenPort == defaultPort {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tryToUseeBPF decides whether to apply eBPF program to capture DNS traffic on port 53.
|
||||||
|
// This is needed because on some operating systems if we start a DNS server not on a default port 53,
|
||||||
|
// the domain name resolution won't work. So, in case we are running on Linux and picked a free
|
||||||
|
// port we should fall back to the eBPF solution that will capture traffic on port 53 and forward
|
||||||
|
// it to a local DNS server running on the chosen port.
|
||||||
|
func (s *serviceViaListener) tryToUseeBPF() (ebpfMgr.Manager, uint16, bool) {
|
||||||
|
if runtime.GOOS != "linux" {
|
||||||
|
return nil, 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
port, err := s.generateFreePort() //nolint:staticcheck,unused
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to generate a free port for eBPF DNS forwarder server: %s", err)
|
||||||
|
return nil, 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
ebpfSrv := ebpf.GetEbpfManagerInstance()
|
||||||
|
err = ebpfSrv.LoadDNSFwd(s.wgInterface.Address().IP.String(), int(port))
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to load DNS forwarder eBPF program, error: %s", err)
|
||||||
|
return nil, 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return ebpfSrv, port, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serviceViaListener) generateFreePort() (uint16, error) {
|
||||||
|
ok := s.tryToBind(s.wgInterface.Address().IP.String(), customPort)
|
||||||
|
if ok {
|
||||||
|
return customPort, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
udpAddr := net.UDPAddrFromAddrPort(netip.MustParseAddrPort("0.0.0.0:0"))
|
||||||
|
probeListener, err := net.ListenUDP("udp", udpAddr)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("failed to bind random port for DNS: %s", err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addrPort := netip.MustParseAddrPort(probeListener.LocalAddr().String()) // might panic if address is incorrect
|
||||||
|
err = probeListener.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("failed to free up DNS port: %s", err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return addrPort.Port(), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func (s *serviceViaMemory) Listen() error {
|
|||||||
var err error
|
var err error
|
||||||
s.udpFilterHookID, err = s.filterDNSTraffic()
|
s.udpFilterHookID, err = s.filterDNSTraffic()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("filter dns traffice: %w", err)
|
||||||
}
|
}
|
||||||
s.listenerIsRunning = true
|
s.listenerIsRunning = true
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@@ -30,6 +31,8 @@ const (
|
|||||||
systemdDbusSetDefaultRouteMethodSuffix = systemdDbusLinkInterface + ".SetDefaultRoute"
|
systemdDbusSetDefaultRouteMethodSuffix = systemdDbusLinkInterface + ".SetDefaultRoute"
|
||||||
systemdDbusSetDomainsMethodSuffix = systemdDbusLinkInterface + ".SetDomains"
|
systemdDbusSetDomainsMethodSuffix = systemdDbusLinkInterface + ".SetDomains"
|
||||||
systemdDbusResolvConfModeForeign = "foreign"
|
systemdDbusResolvConfModeForeign = "foreign"
|
||||||
|
|
||||||
|
dbusErrorUnknownObject = "org.freedesktop.DBus.Error.UnknownObject"
|
||||||
)
|
)
|
||||||
|
|
||||||
type systemdDbusConfigurator struct {
|
type systemdDbusConfigurator struct {
|
||||||
@@ -52,22 +55,22 @@ type systemdDbusLinkDomainsInput struct {
|
|||||||
MatchOnly bool
|
MatchOnly bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSystemdDbusConfigurator(wgInterface WGIface) (hostManager, error) {
|
func newSystemdDbusConfigurator(wgInterface string) (hostManager, error) {
|
||||||
iface, err := net.InterfaceByName(wgInterface.Name())
|
iface, err := net.InterfaceByName(wgInterface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("get interface: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
obj, closeConn, err := getDbusObject(systemdResolvedDest, systemdDbusObjectNode)
|
obj, closeConn, err := getDbusObject(systemdResolvedDest, systemdDbusObjectNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("get dbus resolved dest: %w", err)
|
||||||
}
|
}
|
||||||
defer closeConn()
|
defer closeConn()
|
||||||
|
|
||||||
var s string
|
var s string
|
||||||
err = obj.Call(systemdDbusGetLinkMethod, dbusDefaultFlag, iface.Index).Store(&s)
|
err = obj.Call(systemdDbusGetLinkMethod, dbusDefaultFlag, iface.Index).Store(&s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("get dbus link method: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("got dbus Link interface: %s from net interface %s and index %d", s, iface.Name, iface.Index)
|
log.Debugf("got dbus Link interface: %s from net interface %s and index %d", s, iface.Name, iface.Index)
|
||||||
@@ -84,7 +87,7 @@ func (s *systemdDbusConfigurator) supportCustomPort() bool {
|
|||||||
func (s *systemdDbusConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
func (s *systemdDbusConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
||||||
parsedIP, err := netip.ParseAddr(config.ServerIP)
|
parsedIP, err := netip.ParseAddr(config.ServerIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to parse ip address, error: %s", err)
|
return fmt.Errorf("unable to parse ip address, error: %w", err)
|
||||||
}
|
}
|
||||||
ipAs4 := parsedIP.As4()
|
ipAs4 := parsedIP.As4()
|
||||||
defaultLinkInput := systemdDbusDNSInput{
|
defaultLinkInput := systemdDbusDNSInput{
|
||||||
@@ -93,7 +96,7 @@ func (s *systemdDbusConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
}
|
}
|
||||||
err = s.callLinkMethod(systemdDbusSetDNSMethodSuffix, []systemdDbusDNSInput{defaultLinkInput})
|
err = s.callLinkMethod(systemdDbusSetDNSMethodSuffix, []systemdDbusDNSInput{defaultLinkInput})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("setting the interface DNS server %s:%d failed with error: %s", config.ServerIP, config.ServerPort, err)
|
return fmt.Errorf("setting the interface DNS server %s:%d failed with error: %w", config.ServerIP, config.ServerPort, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -121,7 +124,7 @@ func (s *systemdDbusConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
log.Infof("configured %s:%d as main DNS forwarder for this peer", config.ServerIP, config.ServerPort)
|
log.Infof("configured %s:%d as main DNS forwarder for this peer", config.ServerIP, config.ServerPort)
|
||||||
err = s.callLinkMethod(systemdDbusSetDefaultRouteMethodSuffix, true)
|
err = s.callLinkMethod(systemdDbusSetDefaultRouteMethodSuffix, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("setting link as default dns router, failed with error: %s", err)
|
return fmt.Errorf("setting link as default dns router, failed with error: %w", err)
|
||||||
}
|
}
|
||||||
domainsInput = append(domainsInput, systemdDbusLinkDomainsInput{
|
domainsInput = append(domainsInput, systemdDbusLinkDomainsInput{
|
||||||
Domain: nbdns.RootZone,
|
Domain: nbdns.RootZone,
|
||||||
@@ -132,6 +135,12 @@ func (s *systemdDbusConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
log.Infof("removing %s:%d as main DNS forwarder for this peer", config.ServerIP, config.ServerPort)
|
log.Infof("removing %s:%d as main DNS forwarder for this peer", config.ServerIP, config.ServerPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create a backup for unclean shutdown detection before adding domains, as these might end up in the resolv.conf file.
|
||||||
|
// The file content itself is not important for systemd restoration
|
||||||
|
if err := createUncleanShutdownIndicator(defaultResolvConfPath, systemdManager, parsedIP.String()); err != nil {
|
||||||
|
log.Errorf("failed to create unclean shutdown resolv.conf backup: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
log.Infof("adding %d search domains and %d match domains. Search list: %s , Match list: %s", len(searchDomains), len(matchDomains), searchDomains, matchDomains)
|
log.Infof("adding %d search domains and %d match domains. Search list: %s , Match list: %s", len(searchDomains), len(matchDomains), searchDomains, matchDomains)
|
||||||
err = s.setDomainsForInterface(domainsInput)
|
err = s.setDomainsForInterface(domainsInput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -143,7 +152,7 @@ func (s *systemdDbusConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
func (s *systemdDbusConfigurator) setDomainsForInterface(domainsInput []systemdDbusLinkDomainsInput) error {
|
func (s *systemdDbusConfigurator) setDomainsForInterface(domainsInput []systemdDbusLinkDomainsInput) error {
|
||||||
err := s.callLinkMethod(systemdDbusSetDomainsMethodSuffix, domainsInput)
|
err := s.callLinkMethod(systemdDbusSetDomainsMethodSuffix, domainsInput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("setting domains configuration failed with error: %s", err)
|
return fmt.Errorf("setting domains configuration failed with error: %w", err)
|
||||||
}
|
}
|
||||||
return s.flushCaches()
|
return s.flushCaches()
|
||||||
}
|
}
|
||||||
@@ -153,17 +162,29 @@ func (s *systemdDbusConfigurator) restoreHostDNS() error {
|
|||||||
if !isDbusListenerRunning(systemdResolvedDest, s.dbusLinkObject) {
|
if !isDbusListenerRunning(systemdResolvedDest, s.dbusLinkObject) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this call is required for DNS cleanup, even if it fails
|
||||||
err := s.callLinkMethod(systemdDbusRevertMethodSuffix, nil)
|
err := s.callLinkMethod(systemdDbusRevertMethodSuffix, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to revert link configuration, got error: %s", err)
|
var dbusErr dbus.Error
|
||||||
|
if errors.As(err, &dbusErr) && dbusErr.Name == dbusErrorUnknownObject {
|
||||||
|
// interface is gone already
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
return fmt.Errorf("unable to revert link configuration, got error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := removeUncleanShutdownIndicator(); err != nil {
|
||||||
|
log.Errorf("failed to remove unclean shutdown resolv.conf backup: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
return s.flushCaches()
|
return s.flushCaches()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *systemdDbusConfigurator) flushCaches() error {
|
func (s *systemdDbusConfigurator) flushCaches() error {
|
||||||
obj, closeConn, err := getDbusObject(systemdResolvedDest, systemdDbusObjectNode)
|
obj, closeConn, err := getDbusObject(systemdResolvedDest, systemdDbusObjectNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got error while attempting to retrieve the object %s, err: %s", systemdDbusObjectNode, err)
|
return fmt.Errorf("attempting to retrieve the object %s, err: %w", systemdDbusObjectNode, err)
|
||||||
}
|
}
|
||||||
defer closeConn()
|
defer closeConn()
|
||||||
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
|
||||||
@@ -171,7 +192,7 @@ func (s *systemdDbusConfigurator) flushCaches() error {
|
|||||||
|
|
||||||
err = obj.CallWithContext(ctx, systemdDbusFlushCachesMethod, dbusDefaultFlag).Store()
|
err = obj.CallWithContext(ctx, systemdDbusFlushCachesMethod, dbusDefaultFlag).Store()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got error while calling the FlushCaches method with context, err: %s", err)
|
return fmt.Errorf("calling the FlushCaches method with context, err: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -180,7 +201,7 @@ func (s *systemdDbusConfigurator) flushCaches() error {
|
|||||||
func (s *systemdDbusConfigurator) callLinkMethod(method string, value any) error {
|
func (s *systemdDbusConfigurator) callLinkMethod(method string, value any) error {
|
||||||
obj, closeConn, err := getDbusObject(systemdResolvedDest, s.dbusLinkObject)
|
obj, closeConn, err := getDbusObject(systemdResolvedDest, s.dbusLinkObject)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got error while attempting to retrieve the object, err: %s", err)
|
return fmt.Errorf("attempting to retrieve the object, err: %w", err)
|
||||||
}
|
}
|
||||||
defer closeConn()
|
defer closeConn()
|
||||||
|
|
||||||
@@ -194,22 +215,29 @@ func (s *systemdDbusConfigurator) callLinkMethod(method string, value any) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got error while calling command with context, err: %s", err)
|
return fmt.Errorf("calling command with context, err: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *systemdDbusConfigurator) restoreUncleanShutdownDNS(*netip.Addr) error {
|
||||||
|
if err := s.restoreHostDNS(); err != nil {
|
||||||
|
return fmt.Errorf("restoring dns via systemd: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func getSystemdDbusProperty(property string, store any) error {
|
func getSystemdDbusProperty(property string, store any) error {
|
||||||
obj, closeConn, err := getDbusObject(systemdResolvedDest, systemdDbusObjectNode)
|
obj, closeConn, err := getDbusObject(systemdResolvedDest, systemdDbusObjectNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got error while attempting to retrieve the systemd dns manager object, error: %s", err)
|
return fmt.Errorf("attempting to retrieve the systemd dns manager object, error: %w", err)
|
||||||
}
|
}
|
||||||
defer closeConn()
|
defer closeConn()
|
||||||
|
|
||||||
v, e := obj.GetProperty(property)
|
v, e := obj.GetProperty(property)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return fmt.Errorf("got an error getting property %s: %v", property, e)
|
return fmt.Errorf("getting property %s: %w", property, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.Store(store)
|
return v.Store(store)
|
||||||
|
|||||||
5
client/internal/dns/unclean_shutdown_android.go
Normal file
5
client/internal/dns/unclean_shutdown_android.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
func CheckUncleanShutdown(string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
59
client/internal/dns/unclean_shutdown_darwin.go
Normal file
59
client/internal/dns/unclean_shutdown_darwin.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
//go:build !ios
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const fileUncleanShutdownFileLocation = "/var/lib/netbird/unclean_shutdown_dns"
|
||||||
|
|
||||||
|
func CheckUncleanShutdown(string) error {
|
||||||
|
if _, err := os.Stat(fileUncleanShutdownFileLocation); err != nil {
|
||||||
|
if errors.Is(err, fs.ErrNotExist) {
|
||||||
|
// no file -> clean shutdown
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("state: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Warnf("detected unclean shutdown, file %s exists. Restoring unclean shutdown dns settings.", fileUncleanShutdownFileLocation)
|
||||||
|
|
||||||
|
manager, err := newHostManager()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create host manager: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := manager.restoreUncleanShutdownDNS(nil); err != nil {
|
||||||
|
return fmt.Errorf("restore unclean shutdown backup: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createUncleanShutdownIndicator() error {
|
||||||
|
dir := filepath.Dir(fileUncleanShutdownFileLocation)
|
||||||
|
if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
|
||||||
|
return fmt.Errorf("create dir %s: %w", dir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(fileUncleanShutdownFileLocation, nil, 0644); err != nil { //nolint:gosec
|
||||||
|
return fmt.Errorf("create %s: %w", fileUncleanShutdownFileLocation, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeUncleanShutdownIndicator() error {
|
||||||
|
if err := os.Remove(fileUncleanShutdownFileLocation); err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||||
|
return fmt.Errorf("remove %s: %w", fileUncleanShutdownFileLocation, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
5
client/internal/dns/unclean_shutdown_ios.go
Normal file
5
client/internal/dns/unclean_shutdown_ios.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
func CheckUncleanShutdown(string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
96
client/internal/dns/unclean_shutdown_linux.go
Normal file
96
client/internal/dns/unclean_shutdown_linux.go
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
//go:build !android
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
fileUncleanShutdownResolvConfLocation = "/var/lib/netbird/resolv.conf"
|
||||||
|
fileUncleanShutdownManagerTypeLocation = "/var/lib/netbird/manager"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CheckUncleanShutdown(wgIface string) error {
|
||||||
|
if _, err := os.Stat(fileUncleanShutdownResolvConfLocation); err != nil {
|
||||||
|
if errors.Is(err, fs.ErrNotExist) {
|
||||||
|
// no file -> clean shutdown
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("state: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Warnf("detected unclean shutdown, file %s exists", fileUncleanShutdownResolvConfLocation)
|
||||||
|
|
||||||
|
managerData, err := os.ReadFile(fileUncleanShutdownManagerTypeLocation)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("read %s: %w", fileUncleanShutdownManagerTypeLocation, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
managerFields := strings.Split(string(managerData), ",")
|
||||||
|
if len(managerFields) < 2 {
|
||||||
|
return errors.New("split manager data: insufficient number of fields")
|
||||||
|
}
|
||||||
|
osManagerTypeStr, dnsAddressStr := managerFields[0], managerFields[1]
|
||||||
|
|
||||||
|
dnsAddress, err := netip.ParseAddr(dnsAddressStr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parse dns address %s failed: %w", dnsAddressStr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Warnf("restoring unclean shutdown dns settings via previously detected manager: %s", osManagerTypeStr)
|
||||||
|
|
||||||
|
// determine os manager type, so we can invoke the respective restore action
|
||||||
|
osManagerType, err := newOsManagerType(osManagerTypeStr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("detect previous host manager: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
manager, err := newHostManagerFromType(wgIface, osManagerType)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create previous host manager: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := manager.restoreUncleanShutdownDNS(&dnsAddress); err != nil {
|
||||||
|
return fmt.Errorf("restore unclean shutdown backup: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createUncleanShutdownIndicator(sourcePath string, managerType osManagerType, dnsAddress string) error {
|
||||||
|
dir := filepath.Dir(fileUncleanShutdownResolvConfLocation)
|
||||||
|
if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
|
||||||
|
return fmt.Errorf("create dir %s: %w", dir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := copyFile(sourcePath, fileUncleanShutdownResolvConfLocation); err != nil {
|
||||||
|
return fmt.Errorf("create %s: %w", sourcePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
managerData := fmt.Sprintf("%s,%s", managerType, dnsAddress)
|
||||||
|
|
||||||
|
if err := os.WriteFile(fileUncleanShutdownManagerTypeLocation, []byte(managerData), 0644); err != nil { //nolint:gosec
|
||||||
|
return fmt.Errorf("create %s: %w", fileUncleanShutdownManagerTypeLocation, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeUncleanShutdownIndicator() error {
|
||||||
|
if err := os.Remove(fileUncleanShutdownResolvConfLocation); err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||||
|
return fmt.Errorf("remove %s: %w", fileUncleanShutdownResolvConfLocation, err)
|
||||||
|
}
|
||||||
|
if err := os.Remove(fileUncleanShutdownManagerTypeLocation); err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||||
|
return fmt.Errorf("remove %s: %w", fileUncleanShutdownManagerTypeLocation, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
75
client/internal/dns/unclean_shutdown_windows.go
Normal file
75
client/internal/dns/unclean_shutdown_windows.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
netbirdProgramDataLocation = "Netbird"
|
||||||
|
fileUncleanShutdownFile = "unclean_shutdown_dns.txt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CheckUncleanShutdown(string) error {
|
||||||
|
file := getUncleanShutdownFile()
|
||||||
|
|
||||||
|
if _, err := os.Stat(file); err != nil {
|
||||||
|
if errors.Is(err, fs.ErrNotExist) {
|
||||||
|
// no file -> clean shutdown
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("state: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Warnf("detected unclean shutdown, file %s exists. Restoring unclean shutdown dns settings.", file)
|
||||||
|
|
||||||
|
guid, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("read %s: %w", file, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
manager, err := newHostManagerWithGuid(string(guid))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create host manager: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := manager.restoreUncleanShutdownDNS(nil); err != nil {
|
||||||
|
return fmt.Errorf("restore unclean shutdown backup: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createUncleanShutdownIndicator(guid string) error {
|
||||||
|
file := getUncleanShutdownFile()
|
||||||
|
|
||||||
|
dir := filepath.Dir(file)
|
||||||
|
if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
|
||||||
|
return fmt.Errorf("create dir %s: %w", dir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(file, []byte(guid), 0600); err != nil {
|
||||||
|
return fmt.Errorf("create %s: %w", file, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeUncleanShutdownIndicator() error {
|
||||||
|
file := getUncleanShutdownFile()
|
||||||
|
|
||||||
|
if err := os.Remove(file); err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||||
|
return fmt.Errorf("remove %s: %w", file, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUncleanShutdownFile() string {
|
||||||
|
return filepath.Join(os.Getenv("PROGRAMDATA"), netbirdProgramDataLocation, fileUncleanShutdownFile)
|
||||||
|
}
|
||||||
@@ -25,8 +25,7 @@ const (
|
|||||||
const testRecord = "."
|
const testRecord = "."
|
||||||
|
|
||||||
type upstreamClient interface {
|
type upstreamClient interface {
|
||||||
exchange(upstream string, r *dns.Msg) (*dns.Msg, time.Duration, error)
|
exchange(ctx context.Context, upstream string, r *dns.Msg) (*dns.Msg, time.Duration, error)
|
||||||
exchangeContext(ctx context.Context, upstream string, r *dns.Msg) (*dns.Msg, time.Duration, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpstreamResolver interface {
|
type UpstreamResolver interface {
|
||||||
@@ -80,8 +79,15 @@ func (u *upstreamResolverBase) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, upstream := range u.upstreamServers {
|
for _, upstream := range u.upstreamServers {
|
||||||
|
var rm *dns.Msg
|
||||||
|
var t time.Duration
|
||||||
|
var err error
|
||||||
|
|
||||||
rm, t, err := u.upstreamClient.exchange(upstream, r)
|
func() {
|
||||||
|
ctx, cancel := context.WithTimeout(u.ctx, u.upstreamTimeout)
|
||||||
|
defer cancel()
|
||||||
|
rm, t, err = u.upstreamClient.exchange(ctx, upstream, r)
|
||||||
|
}()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, context.DeadlineExceeded) || isTimeout(err) {
|
if errors.Is(err, context.DeadlineExceeded) || isTimeout(err) {
|
||||||
@@ -213,7 +219,7 @@ func (u *upstreamResolverBase) waitUntilResponse() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Tracef("checking connectivity with upstreams %s failed. Retrying in %s", u.upstreamServers, exponentialBackOff.NextBackOff())
|
log.Tracef("checking connectivity with upstreams %s failed. Retrying in %s", u.upstreamServers, exponentialBackOff.NextBackOff())
|
||||||
return fmt.Errorf("got an error from upstream check call")
|
return fmt.Errorf("upstream check call error")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := backoff.Retry(operation, exponentialBackOff)
|
err := backoff.Retry(operation, exponentialBackOff)
|
||||||
@@ -259,6 +265,6 @@ func (u *upstreamResolverBase) testNameserver(server string) error {
|
|||||||
|
|
||||||
r := new(dns.Msg).SetQuestion(testRecord, dns.TypeSOA)
|
r := new(dns.Msg).SetQuestion(testRecord, dns.TypeSOA)
|
||||||
|
|
||||||
_, _, err := u.upstreamClient.exchangeContext(ctx, server, r)
|
_, _, err := u.upstreamClient.exchange(ctx, server, r)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,34 +40,38 @@ func newUpstreamResolver(parentCTX context.Context, interfaceName string, ip net
|
|||||||
return ios, nil
|
return ios, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *upstreamResolverIOS) exchange(upstream string, r *dns.Msg) (rm *dns.Msg, t time.Duration, err error) {
|
func (u *upstreamResolverIOS) exchange(ctx context.Context, upstream string, r *dns.Msg) (rm *dns.Msg, t time.Duration, err error) {
|
||||||
return u.exchangeContext(context.Background(), upstream, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *upstreamResolverIOS) exchangeContext(ctx context.Context, upstream string, r *dns.Msg) (rm *dns.Msg, t time.Duration, err error) {
|
|
||||||
client := &dns.Client{}
|
client := &dns.Client{}
|
||||||
upstreamHost, _, err := net.SplitHostPort(upstream)
|
upstreamHost, _, err := net.SplitHostPort(upstream)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("error while parsing upstream host: %s", err)
|
log.Errorf("error while parsing upstream host: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timeout := upstreamTimeout
|
||||||
|
if deadline, ok := ctx.Deadline(); ok {
|
||||||
|
timeout = time.Until(deadline)
|
||||||
|
}
|
||||||
|
client.DialTimeout = timeout
|
||||||
|
|
||||||
upstreamIP := net.ParseIP(upstreamHost)
|
upstreamIP := net.ParseIP(upstreamHost)
|
||||||
if u.lNet.Contains(upstreamIP) || net.IP.IsPrivate(upstreamIP) {
|
if u.lNet.Contains(upstreamIP) || net.IP.IsPrivate(upstreamIP) {
|
||||||
log.Debugf("using private client to query upstream: %s", upstream)
|
log.Debugf("using private client to query upstream: %s", upstream)
|
||||||
client = u.getClientPrivate()
|
client = u.getClientPrivate(timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
return client.ExchangeContext(ctx, r, upstream)
|
// Cannot use client.ExchangeContext because it overwrites our Dialer
|
||||||
|
return client.Exchange(r, upstream)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getClientPrivate returns a new DNS client bound to the local IP address of the Netbird interface
|
// getClientPrivate returns a new DNS client bound to the local IP address of the Netbird interface
|
||||||
// This method is needed for iOS
|
// This method is needed for iOS
|
||||||
func (u *upstreamResolverIOS) getClientPrivate() *dns.Client {
|
func (u *upstreamResolverIOS) getClientPrivate(dialTimeout time.Duration) *dns.Client {
|
||||||
dialer := &net.Dialer{
|
dialer := &net.Dialer{
|
||||||
LocalAddr: &net.UDPAddr{
|
LocalAddr: &net.UDPAddr{
|
||||||
IP: u.lIP,
|
IP: u.lIP,
|
||||||
Port: 0, // Let the OS pick a free port
|
Port: 0, // Let the OS pick a free port
|
||||||
},
|
},
|
||||||
Timeout: upstreamTimeout,
|
Timeout: dialTimeout,
|
||||||
Control: func(network, address string, c syscall.RawConn) error {
|
Control: func(network, address string, c syscall.RawConn) error {
|
||||||
var operr error
|
var operr error
|
||||||
fn := func(s uintptr) {
|
fn := func(s uintptr) {
|
||||||
|
|||||||
@@ -23,14 +23,7 @@ func newUpstreamResolver(parentCTX context.Context, interfaceName string, ip net
|
|||||||
return nonIOS, nil
|
return nonIOS, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *upstreamResolverNonIOS) exchange(upstream string, r *dns.Msg) (rm *dns.Msg, t time.Duration, err error) {
|
func (u *upstreamResolverNonIOS) exchange(ctx context.Context, upstream string, r *dns.Msg) (rm *dns.Msg, t time.Duration, err error) {
|
||||||
// default upstream timeout
|
|
||||||
ctx, cancel := context.WithTimeout(u.ctx, u.upstreamTimeout)
|
|
||||||
defer cancel()
|
|
||||||
return u.exchangeContext(ctx, upstream, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *upstreamResolverNonIOS) exchangeContext(ctx context.Context, upstream string, r *dns.Msg) (rm *dns.Msg, t time.Duration, err error) {
|
|
||||||
upstreamExchangeClient := &dns.Client{}
|
upstreamExchangeClient := &dns.Client{}
|
||||||
return upstreamExchangeClient.ExchangeContext(ctx, r, upstream)
|
return upstreamExchangeClient.ExchangeContext(ctx, r, upstream)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,13 +105,8 @@ type mockUpstreamResolver struct {
|
|||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exchange mock implementation of Exchangefrom upstreamResolver
|
// exchange mock implementation of exchange from upstreamResolver
|
||||||
func (c mockUpstreamResolver) exchange(upstream string, r *dns.Msg) (*dns.Msg, time.Duration, error) {
|
func (c mockUpstreamResolver) exchange(_ context.Context, _ string, _ *dns.Msg) (*dns.Msg, time.Duration, error) {
|
||||||
return c.exchangeContext(context.Background(), upstream, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExchangeContext mock implementation of ExchangeContext from upstreamResolver
|
|
||||||
func (c mockUpstreamResolver) exchangeContext(_ context.Context, _ string, _ *dns.Msg) (*dns.Msg, time.Duration, error) {
|
|
||||||
return c.r, c.rtt, c.err
|
return c.r, c.rtt, c.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (tf *GeneralManager) LoadDNSFwd(ip string, dnsPort int) error {
|
func (tf *GeneralManager) LoadDNSFwd(ip string, dnsPort int) error {
|
||||||
log.Debugf("load ebpf DNS forwarder: address: %s:%d", ip, dnsPort)
|
log.Debugf("load eBPF DNS forwarder, watching addr: %s:53, redirect to port: %d", ip, dnsPort)
|
||||||
tf.lock.Lock()
|
tf.lock.Lock()
|
||||||
defer tf.lock.Unlock()
|
defer tf.lock.Unlock()
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
# Debug
|
# DNS forwarder
|
||||||
|
|
||||||
|
The agent attach the XDP program to the lo device. We can not use fake address in eBPF because the
|
||||||
|
traffic does not appear in the eBPF program. The program capture the traffic on wg_ip:53 and
|
||||||
|
overwrite in it the destination port to 5053.
|
||||||
|
|
||||||
|
# Debug
|
||||||
|
|
||||||
The CONFIG_BPF_EVENTS kernel module is required for bpf_printk.
|
The CONFIG_BPF_EVENTS kernel module is required for bpf_printk.
|
||||||
Apply this code to use bpf_printk
|
Apply this code to use bpf_printk
|
||||||
|
|||||||
@@ -1080,6 +1080,11 @@ func (e *Engine) close() {
|
|||||||
log.Errorf("failed closing ebpf proxy: %s", err)
|
log.Errorf("failed closing ebpf proxy: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stop/restore DNS first so dbus and friends don't complain because of a missing interface
|
||||||
|
if e.dnsServer != nil {
|
||||||
|
e.dnsServer.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
log.Debugf("removing Netbird interface %s", e.config.WgIfaceName)
|
log.Debugf("removing Netbird interface %s", e.config.WgIfaceName)
|
||||||
if e.wgInterface != nil {
|
if e.wgInterface != nil {
|
||||||
if err := e.wgInterface.Close(); err != nil {
|
if err := e.wgInterface.Close(); err != nil {
|
||||||
@@ -1098,10 +1103,6 @@ func (e *Engine) close() {
|
|||||||
e.routeManager.Stop()
|
e.routeManager.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.dnsServer != nil {
|
|
||||||
e.dnsServer.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.firewall != nil {
|
if e.firewall != nil {
|
||||||
err := e.firewall.Reset()
|
err := e.firewall.Reset()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ type Conn struct {
|
|||||||
|
|
||||||
adapter iface.TunAdapter
|
adapter iface.TunAdapter
|
||||||
iFaceDiscover stdnet.ExternalIFaceDiscover
|
iFaceDiscover stdnet.ExternalIFaceDiscover
|
||||||
|
sentExtraSrflx bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// meta holds meta information about a connection
|
// meta holds meta information about a connection
|
||||||
@@ -464,6 +465,8 @@ func (conn *Conn) cleanup() error {
|
|||||||
conn.mu.Lock()
|
conn.mu.Lock()
|
||||||
defer conn.mu.Unlock()
|
defer conn.mu.Unlock()
|
||||||
|
|
||||||
|
conn.sentExtraSrflx = false
|
||||||
|
|
||||||
var err1, err2, err3 error
|
var err1, err2, err3 error
|
||||||
if conn.agent != nil {
|
if conn.agent != nil {
|
||||||
err1 = conn.agent.Close()
|
err1 = conn.agent.Close()
|
||||||
@@ -557,6 +560,30 @@ func (conn *Conn) onICECandidate(candidate ice.Candidate) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed signaling candidate to the remote peer %s %s", conn.config.Key, err)
|
log.Errorf("failed signaling candidate to the remote peer %s %s", conn.config.Key, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sends an extra server reflexive candidate to the remote peer with our related port (usually the wireguard port)
|
||||||
|
// this is useful when network has an existing port forwarding rule for the wireguard port and this peer
|
||||||
|
if !conn.sentExtraSrflx && candidate.Type() == ice.CandidateTypeServerReflexive && candidate.Port() != candidate.RelatedAddress().Port {
|
||||||
|
relatedAdd := candidate.RelatedAddress()
|
||||||
|
extraSrflx, err := ice.NewCandidateServerReflexive(&ice.CandidateServerReflexiveConfig{
|
||||||
|
Network: candidate.NetworkType().String(),
|
||||||
|
Address: candidate.Address(),
|
||||||
|
Port: relatedAdd.Port,
|
||||||
|
Component: candidate.Component(),
|
||||||
|
RelAddr: relatedAdd.Address,
|
||||||
|
RelPort: relatedAdd.Port,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed creating extra server reflexive candidate %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = conn.signalCandidate(extraSrflx)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed signaling the extra server reflexive candidate to the remote peer %s: %s", conn.config.Key, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conn.sentExtraSrflx = true
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package encryption
|
package encryption
|
||||||
|
|
||||||
import (
|
import (
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"golang.org/x/crypto/acme/autocert"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/crypto/acme/autocert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateCertManager wraps common logic of generating Let's encrypt certificate.
|
// CreateCertManager wraps common logic of generating Let's encrypt certificate.
|
||||||
@@ -12,7 +13,7 @@ func CreateCertManager(datadir string, letsencryptDomain string) (*autocert.Mana
|
|||||||
certDir := filepath.Join(datadir, "letsencrypt")
|
certDir := filepath.Join(datadir, "letsencrypt")
|
||||||
|
|
||||||
if _, err := os.Stat(certDir); os.IsNotExist(err) {
|
if _, err := os.Stat(certDir); os.IsNotExist(err) {
|
||||||
err = os.MkdirAll(certDir, os.ModeDir)
|
err = os.MkdirAll(certDir, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
5
go.mod
5
go.mod
@@ -27,7 +27,7 @@ require (
|
|||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3
|
golang.zx2c4.com/wireguard/windows v0.5.3
|
||||||
google.golang.org/grpc v1.56.3
|
google.golang.org/grpc v1.56.3
|
||||||
google.golang.org/protobuf v1.30.0
|
google.golang.org/protobuf v1.31.0
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -47,6 +47,7 @@ require (
|
|||||||
github.com/google/go-cmp v0.5.9
|
github.com/google/go-cmp v0.5.9
|
||||||
github.com/google/gopacket v1.1.19
|
github.com/google/gopacket v1.1.19
|
||||||
github.com/google/nftables v0.0.0-20220808154552-2eca00135732
|
github.com/google/nftables v0.0.0-20220808154552-2eca00135732
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.2-0.20240202184442-37827591b26c
|
||||||
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2
|
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2
|
||||||
github.com/hashicorp/go-version v1.6.0
|
github.com/hashicorp/go-version v1.6.0
|
||||||
github.com/libp2p/go-netroute v0.2.0
|
github.com/libp2p/go-netroute v0.2.0
|
||||||
@@ -170,3 +171,5 @@ replace github.com/getlantern/systray => github.com/netbirdio/systray v0.0.0-202
|
|||||||
replace golang.zx2c4.com/wireguard => github.com/netbirdio/wireguard-go v0.0.0-20240105182236-6c340dd55aed
|
replace golang.zx2c4.com/wireguard => github.com/netbirdio/wireguard-go v0.0.0-20240105182236-6c340dd55aed
|
||||||
|
|
||||||
replace github.com/cloudflare/circl => github.com/cunicu/circl v0.0.0-20230801113412-fec58fc7b5f6
|
replace github.com/cloudflare/circl => github.com/cunicu/circl v0.0.0-20230801113412-fec58fc7b5f6
|
||||||
|
|
||||||
|
replace github.com/grpc-ecosystem/go-grpc-middleware/v2 => github.com/surik/go-grpc-middleware/v2 v2.0.0-20240206110057-98a38fc1f86f
|
||||||
|
|||||||
5
go.sum
5
go.sum
@@ -517,6 +517,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
|||||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/surik/go-grpc-middleware/v2 v2.0.0-20240206110057-98a38fc1f86f h1:J+egXEDkpg/vOYYzPO5IwF8OufGb7g+KcwEF1AWIzhQ=
|
||||||
|
github.com/surik/go-grpc-middleware/v2 v2.0.0-20240206110057-98a38fc1f86f/go.mod h1:w9Y7gY31krpLmrVU5ZPG9H7l9fZuRu5/3R3S3FMtVQ4=
|
||||||
github.com/things-go/go-socks5 v0.0.4 h1:jMQjIc+qhD4z9cITOMnBiwo9dDmpGuXmBlkRFrl/qD0=
|
github.com/things-go/go-socks5 v0.0.4 h1:jMQjIc+qhD4z9cITOMnBiwo9dDmpGuXmBlkRFrl/qD0=
|
||||||
github.com/things-go/go-socks5 v0.0.4/go.mod h1:sh4K6WHrmHZpjxLTCHyYtXYH8OUuD+yZun41NomR1IQ=
|
github.com/things-go/go-socks5 v0.0.4/go.mod h1:sh4K6WHrmHZpjxLTCHyYtXYH8OUuD+yZun41NomR1IQ=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
@@ -940,8 +942,9 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
|||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
|
||||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||||
|
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string,
|
|||||||
wgIFace.tun = newTunUSPDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet)
|
wgIFace.tun = newTunUSPDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet)
|
||||||
wgIFace.userspaceBind = true
|
wgIFace.userspaceBind = true
|
||||||
return wgIFace, nil
|
return wgIFace, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateOnAndroid this function make sense on mobile only
|
// CreateOnAndroid this function make sense on mobile only
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ version: "3"
|
|||||||
services:
|
services:
|
||||||
#UI dashboard
|
#UI dashboard
|
||||||
dashboard:
|
dashboard:
|
||||||
image: wiretrustee/dashboard:$NETBIRD_DASHBOARD_TAG
|
image: netbirdio/dashboard:$NETBIRD_DASHBOARD_TAG
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- 80:80
|
- 80:80
|
||||||
|
|||||||
@@ -705,7 +705,7 @@ services:
|
|||||||
- ./Caddyfile:/etc/caddy/Caddyfile
|
- ./Caddyfile:/etc/caddy/Caddyfile
|
||||||
#UI dashboard
|
#UI dashboard
|
||||||
dashboard:
|
dashboard:
|
||||||
image: wiretrustee/dashboard:latest
|
image: netbirdio/dashboard:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks: [netbird]
|
networks: [netbird]
|
||||||
env_file:
|
env_file:
|
||||||
|
|||||||
@@ -7,17 +7,20 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/netbirdio/management-integrations/integrations"
|
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/netbirdio/management-integrations/integrations"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -29,6 +32,7 @@ import (
|
|||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
|
|
||||||
|
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/realip"
|
||||||
"github.com/netbirdio/netbird/encryption"
|
"github.com/netbirdio/netbird/encryption"
|
||||||
mgmtProto "github.com/netbirdio/netbird/management/proto"
|
mgmtProto "github.com/netbirdio/netbird/management/proto"
|
||||||
"github.com/netbirdio/netbird/management/server"
|
"github.com/netbirdio/netbird/management/server"
|
||||||
@@ -115,7 +119,7 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, err = os.Stat(config.Datadir); os.IsNotExist(err) {
|
if _, err = os.Stat(config.Datadir); os.IsNotExist(err) {
|
||||||
err = os.MkdirAll(config.Datadir, os.ModeDir)
|
err = os.MkdirAll(config.Datadir, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed creating datadir: %s: %v", config.Datadir, err)
|
return fmt.Errorf("failed creating datadir: %s: %v", config.Datadir, err)
|
||||||
}
|
}
|
||||||
@@ -167,7 +171,31 @@ var (
|
|||||||
|
|
||||||
turnManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig)
|
turnManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig)
|
||||||
|
|
||||||
gRPCOpts := []grpc.ServerOption{grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp)}
|
trustedPeers := config.ReverseProxy.TrustedPeers
|
||||||
|
defaultTrustedPeers := []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0")}
|
||||||
|
if len(trustedPeers) == 0 || slices.Equal[[]netip.Prefix](trustedPeers, defaultTrustedPeers) {
|
||||||
|
log.Warn("TrustedPeers are configured to default value '0.0.0.0/0', '::/0'. This allows connection IP spoofing.")
|
||||||
|
trustedPeers = defaultTrustedPeers
|
||||||
|
}
|
||||||
|
trustedHTTPProxies := config.ReverseProxy.TrustedHTTPProxies
|
||||||
|
trustedProxiesCount := config.ReverseProxy.TrustedHTTPProxiesCount
|
||||||
|
if len(trustedHTTPProxies) > 0 && trustedProxiesCount > 0 {
|
||||||
|
log.Warn("TrustedHTTPProxies and TrustedHTTPProxiesCount both are configured. " +
|
||||||
|
"This is not recommended way to extract X-Forwarded-For. Consider using one of these options.")
|
||||||
|
}
|
||||||
|
realipOpts := realip.Opts{
|
||||||
|
TrustedPeers: trustedPeers,
|
||||||
|
TrustedProxies: trustedHTTPProxies,
|
||||||
|
TrustedProxiesCount: trustedProxiesCount,
|
||||||
|
Headers: []string{realip.XForwardedFor, realip.XRealIp},
|
||||||
|
}
|
||||||
|
gRPCOpts := []grpc.ServerOption{
|
||||||
|
grpc.KeepaliveEnforcementPolicy(kaep),
|
||||||
|
grpc.KeepaliveParams(kasp),
|
||||||
|
grpc.ChainUnaryInterceptor(realip.UnaryServerInterceptorOpts(realipOpts)),
|
||||||
|
grpc.ChainStreamInterceptor(realip.StreamServerInterceptorOpts(realipOpts)),
|
||||||
|
}
|
||||||
|
|
||||||
var certManager *autocert.Manager
|
var certManager *autocert.Manager
|
||||||
var tlsConfig *tls.Config
|
var tlsConfig *tls.Config
|
||||||
tlsEnabled := false
|
tlsEnabled := false
|
||||||
|
|||||||
@@ -1223,7 +1223,21 @@ func (am *DefaultAccountManager) lookupUserInCache(userID string, account *Accou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil //nolint:nilnil
|
// add extra check on external cache manager. We may get to this point when the user is not yet findable in IDP,
|
||||||
|
// or it didn't have its metadata updated with am.addAccountIDToIDPAppMeta
|
||||||
|
user, err := account.FindUser(userID)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed finding user %s in account %s", userID, account.Id)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
key := user.IntegrationReference.CacheKey(account.Id, userID)
|
||||||
|
ud, err := am.externalCacheManager.Get(am.ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("failed to get externalCache for key: %s, error: %s", key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ud, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *DefaultAccountManager) refreshCache(accountID string) ([]*idp.UserData, error) {
|
func (am *DefaultAccountManager) refreshCache(accountID string) ([]*idp.UserData, error) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/idp"
|
"github.com/netbirdio/netbird/management/server/idp"
|
||||||
@@ -47,6 +48,8 @@ type Config struct {
|
|||||||
PKCEAuthorizationFlow *PKCEAuthorizationFlow
|
PKCEAuthorizationFlow *PKCEAuthorizationFlow
|
||||||
|
|
||||||
StoreConfig StoreConfig
|
StoreConfig StoreConfig
|
||||||
|
|
||||||
|
ReverseProxy ReverseProxy
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAuthAudiences returns the audience from the http config and device authorization flow config
|
// GetAuthAudiences returns the audience from the http config and device authorization flow config
|
||||||
@@ -143,6 +146,27 @@ type StoreConfig struct {
|
|||||||
Engine StoreEngine
|
Engine StoreEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReverseProxy contains reverse proxy configuration in front of management.
|
||||||
|
type ReverseProxy struct {
|
||||||
|
// TrustedHTTPProxies represents a list of trusted HTTP proxies by their IP prefixes.
|
||||||
|
// When extracting the real IP address from request headers, the middleware will verify
|
||||||
|
// if the peer's address falls within one of these trusted IP prefixes.
|
||||||
|
TrustedHTTPProxies []netip.Prefix
|
||||||
|
|
||||||
|
// TrustedHTTPProxiesCount specifies the count of trusted HTTP proxies between the internet
|
||||||
|
// and the server. When using the trusted proxy count method to extract the real IP address,
|
||||||
|
// the middleware will search the X-Forwarded-For IP list from the rightmost by this count
|
||||||
|
// minus one.
|
||||||
|
TrustedHTTPProxiesCount uint
|
||||||
|
|
||||||
|
// TrustedPeers represents a list of trusted peers by their IP prefixes.
|
||||||
|
// These peers are considered trustworthy by the gRPC server operator,
|
||||||
|
// and the middleware will attempt to extract the real IP address from
|
||||||
|
// request headers if the peer's address falls within one of these
|
||||||
|
// trusted IP prefixes.
|
||||||
|
TrustedPeers []netip.Prefix
|
||||||
|
}
|
||||||
|
|
||||||
// validateURL validates input http url
|
// validateURL validates input http url
|
||||||
func validateURL(httpURL string) bool {
|
func validateURL(httpURL string) bool {
|
||||||
_, err := url.ParseRequestURI(httpURL)
|
_, err := url.ParseRequestURI(httpURL)
|
||||||
|
|||||||
@@ -115,6 +115,13 @@ func (am *DefaultAccountManager) SaveGroup(accountID, userID string, newGroup *G
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, peerID := range newGroup.Peers {
|
||||||
|
if account.Peers[peerID] == nil {
|
||||||
|
return status.Errorf(status.InvalidArgument, "peer with ID \"%s\" not found", peerID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
oldGroup, exists := account.Groups[newGroup.ID]
|
oldGroup, exists := account.Groups[newGroup.ID]
|
||||||
account.Groups[newGroup.ID] = newGroup
|
account.Groups[newGroup.ID] = newGroup
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import (
|
|||||||
|
|
||||||
pb "github.com/golang/protobuf/proto" // nolint
|
pb "github.com/golang/protobuf/proto" // nolint
|
||||||
"github.com/golang/protobuf/ptypes/timestamp"
|
"github.com/golang/protobuf/ptypes/timestamp"
|
||||||
|
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/realip"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
gRPCPeer "google.golang.org/grpc/peer"
|
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/encryption"
|
"github.com/netbirdio/netbird/encryption"
|
||||||
@@ -109,6 +109,13 @@ func (s *GRPCServer) GetServerKey(ctx context.Context, req *proto.Empty) (*proto
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getRealIP(ctx context.Context) string {
|
||||||
|
if ip, ok := realip.FromContext(ctx); ok {
|
||||||
|
return ip.String()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// Sync validates the existence of a connecting peer, sends an initial state (all available for the connecting peers) and
|
// Sync validates the existence of a connecting peer, sends an initial state (all available for the connecting peers) and
|
||||||
// notifies the connected peer of any updates (e.g. new peers under the same account)
|
// notifies the connected peer of any updates (e.g. new peers under the same account)
|
||||||
func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementService_SyncServer) error {
|
func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementService_SyncServer) error {
|
||||||
@@ -116,10 +123,8 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
|
|||||||
if s.appMetrics != nil {
|
if s.appMetrics != nil {
|
||||||
s.appMetrics.GRPCMetrics().CountSyncRequest()
|
s.appMetrics.GRPCMetrics().CountSyncRequest()
|
||||||
}
|
}
|
||||||
p, ok := gRPCPeer.FromContext(srv.Context())
|
realIP := getRealIP(srv.Context())
|
||||||
if ok {
|
log.Debugf("Sync request from peer [%s] [%s]", req.WgPubKey, realIP)
|
||||||
log.Debugf("Sync request from peer [%s] [%s]", req.WgPubKey, p.Addr.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
syncReq := &proto.SyncRequest{}
|
syncReq := &proto.SyncRequest{}
|
||||||
peerKey, err := s.parseRequest(req, syncReq)
|
peerKey, err := s.parseRequest(req, syncReq)
|
||||||
@@ -290,10 +295,8 @@ func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*p
|
|||||||
if s.appMetrics != nil {
|
if s.appMetrics != nil {
|
||||||
s.appMetrics.GRPCMetrics().CountLoginRequest()
|
s.appMetrics.GRPCMetrics().CountLoginRequest()
|
||||||
}
|
}
|
||||||
p, ok := gRPCPeer.FromContext(ctx)
|
realIP := getRealIP(ctx)
|
||||||
if ok {
|
log.Debugf("Login request from peer [%s] [%s]", req.WgPubKey, realIP)
|
||||||
log.Debugf("Login request from peer [%s] [%s]", req.WgPubKey, p.Addr.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
loginReq := &proto.LoginRequest{}
|
loginReq := &proto.LoginRequest{}
|
||||||
peerKey, err := s.parseRequest(req, loginReq)
|
peerKey, err := s.parseRequest(req, loginReq)
|
||||||
@@ -303,8 +306,7 @@ func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*p
|
|||||||
|
|
||||||
if loginReq.GetMeta() == nil {
|
if loginReq.GetMeta() == nil {
|
||||||
msg := status.Errorf(codes.FailedPrecondition,
|
msg := status.Errorf(codes.FailedPrecondition,
|
||||||
"peer system meta has to be provided to log in. Peer %s, remote addr %s", peerKey.String(),
|
"peer system meta has to be provided to log in. Peer %s, remote addr %s", peerKey.String(), realIP)
|
||||||
p.Addr.String())
|
|
||||||
log.Warn(msg)
|
log.Warn(msg)
|
||||||
return nil, msg
|
return nil, msg
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -765,15 +765,10 @@ components:
|
|||||||
description: Policy status
|
description: Policy status
|
||||||
type: boolean
|
type: boolean
|
||||||
example: true
|
example: true
|
||||||
query:
|
|
||||||
description: Policy Rego query
|
|
||||||
type: string
|
|
||||||
example: "package netbird\\n\\nall[rule] {\\n is_peer_in_any_group([\\\"ch8i4ug6lnn4g9hqv7m0\\\",\\\"ch8i4ug6lnn4g9hqv7m0\\\"])\\n rule := {\\n rules_from_group(\\\"ch8i4ug6lnn4g9hqv7m0\\\", \\\"dst\\\", \\\"accept\\\", \\\"\\\"),\\n rules_from_group(\\\"ch8i4ug6lnn4g9hqv7m0\\\", \\\"src\\\", \\\"accept\\\", \\\"\\\"),\\n }[_][_]\\n}\\n"
|
|
||||||
required:
|
required:
|
||||||
- name
|
- name
|
||||||
- description
|
- description
|
||||||
- enabled
|
- enabled
|
||||||
- query
|
|
||||||
PolicyUpdate:
|
PolicyUpdate:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/PolicyMinimum'
|
- $ref: '#/components/schemas/PolicyMinimum'
|
||||||
@@ -909,7 +904,7 @@ components:
|
|||||||
nameservers:
|
nameservers:
|
||||||
description: Nameserver list
|
description: Nameserver list
|
||||||
minLength: 1
|
minLength: 1
|
||||||
maxLength: 2
|
maxLength: 3
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/Nameserver'
|
$ref: '#/components/schemas/Nameserver'
|
||||||
|
|||||||
@@ -567,9 +567,6 @@ type Policy struct {
|
|||||||
// Name Policy name identifier
|
// Name Policy name identifier
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
// Query Policy Rego query
|
|
||||||
Query string `json:"query"`
|
|
||||||
|
|
||||||
// Rules Policy rule object for policy UI editor
|
// Rules Policy rule object for policy UI editor
|
||||||
Rules []PolicyRule `json:"rules"`
|
Rules []PolicyRule `json:"rules"`
|
||||||
}
|
}
|
||||||
@@ -587,9 +584,6 @@ type PolicyMinimum struct {
|
|||||||
|
|
||||||
// Name Policy name identifier
|
// Name Policy name identifier
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
// Query Policy Rego query
|
|
||||||
Query string `json:"query"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PolicyRule defines model for PolicyRule.
|
// PolicyRule defines model for PolicyRule.
|
||||||
@@ -717,9 +711,6 @@ type PolicyUpdate struct {
|
|||||||
// Name Policy name identifier
|
// Name Policy name identifier
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
// Query Policy Rego query
|
|
||||||
Query string `json:"query"`
|
|
||||||
|
|
||||||
// Rules Policy rule object for policy UI editor
|
// Rules Policy rule object for policy UI editor
|
||||||
Rules []PolicyRuleUpdate `json:"rules"`
|
Rules []PolicyRuleUpdate `json:"rules"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -242,7 +242,6 @@ func toGroupResponse(account *server.Account, group *server.Group) *api.Group {
|
|||||||
gr := api.Group{
|
gr := api.Group{
|
||||||
Id: group.ID,
|
Id: group.ID,
|
||||||
Name: group.Name,
|
Name: group.Name,
|
||||||
PeersCount: len(group.Peers),
|
|
||||||
Issued: &group.Issued,
|
Issued: &group.Issued,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,5 +260,8 @@ func toGroupResponse(account *server.Account, group *server.Group) *api.Group {
|
|||||||
gr.Peers = append(gr.Peers, peerResp)
|
gr.Peers = append(gr.Peers, peerResp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gr.PeersCount = len(gr.Peers)
|
||||||
|
|
||||||
return &gr
|
return &gr
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -150,19 +152,37 @@ func (gm *GoogleWorkspaceManager) GetAllAccounts() (map[string][]*UserData, erro
|
|||||||
|
|
||||||
// getAllUsers returns all users in a Google Workspace account filtered by customer ID.
|
// getAllUsers returns all users in a Google Workspace account filtered by customer ID.
|
||||||
func (gm *GoogleWorkspaceManager) getAllUsers() ([]*UserData, error) {
|
func (gm *GoogleWorkspaceManager) getAllUsers() ([]*UserData, error) {
|
||||||
|
var usersLimit int64 = 500
|
||||||
|
if maxUsersLimitEnv := os.Getenv("GOOGLE_WORKSPACE_USERS_LIMIT"); maxUsersLimitEnv != "" {
|
||||||
|
maxUsersLimit, err := strconv.Atoi(maxUsersLimitEnv)
|
||||||
|
if err == nil {
|
||||||
|
log.Debugf("GOOGLE_WORKSPACE_USERS_LIMIT env is set using %d as users limit", maxUsersLimit)
|
||||||
|
usersLimit = int64(maxUsersLimit)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Debugf("GOOGLE_WORKSPACE_USERS_LIMIT env is not set using default users limit 500")
|
||||||
|
}
|
||||||
|
|
||||||
users := make([]*UserData, 0)
|
users := make([]*UserData, 0)
|
||||||
pageToken := ""
|
pageToken := ""
|
||||||
for {
|
for {
|
||||||
call := gm.usersService.List().Customer(gm.CustomerID).MaxResults(500)
|
call := gm.usersService.List().Customer(gm.CustomerID).MaxResults(usersLimit)
|
||||||
if pageToken != "" {
|
if pageToken != "" {
|
||||||
call.PageToken(pageToken)
|
call.PageToken(pageToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := call.Do()
|
resp, err := call.Do()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Debugf("failed to retrieve users from workspace error: %s, http status: %d, headers: %v",
|
||||||
|
err.Error(),
|
||||||
|
resp.HTTPStatusCode,
|
||||||
|
resp.Header,
|
||||||
|
)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debugf("fetched %d users from workspace", len(resp.Users))
|
||||||
|
|
||||||
for _, user := range resp.Users {
|
for _, user := range resp.Users {
|
||||||
users = append(users, parseGoogleWorkspaceUser(user))
|
users = append(users, parseGoogleWorkspaceUser(user))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -255,8 +255,8 @@ func validateNSGroupName(name, nsGroupID string, nsGroupMap map[string]*nbdns.Na
|
|||||||
|
|
||||||
func validateNSList(list []nbdns.NameServer) error {
|
func validateNSList(list []nbdns.NameServer) error {
|
||||||
nsListLenght := len(list)
|
nsListLenght := len(list)
|
||||||
if nsListLenght == 0 || nsListLenght > 2 {
|
if nsListLenght == 0 || nsListLenght > 3 {
|
||||||
return status.Errorf(status.InvalidArgument, "the list of nameservers should be 1 or 2, got %d", len(list))
|
return status.Errorf(status.InvalidArgument, "the list of nameservers should be 1 or 3, got %d", len(list))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ func TestCreateNameServerGroup(t *testing.T) {
|
|||||||
shouldCreate: false,
|
shouldCreate: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Create A NS Group With More Than 2 Nameservers Should Fail",
|
name: "Create A NS Group With More Than 3 Nameservers Should Fail",
|
||||||
inputArgs: input{
|
inputArgs: input{
|
||||||
name: "super",
|
name: "super",
|
||||||
description: "super",
|
description: "super",
|
||||||
@@ -238,6 +238,11 @@ func TestCreateNameServerGroup(t *testing.T) {
|
|||||||
NSType: nbdns.UDPNameServerType,
|
NSType: nbdns.UDPNameServerType,
|
||||||
Port: nbdns.DefaultDNSPort,
|
Port: nbdns.DefaultDNSPort,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
IP: netip.MustParseAddr("1.1.4.4"),
|
||||||
|
NSType: nbdns.UDPNameServerType,
|
||||||
|
Port: nbdns.DefaultDNSPort,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
@@ -457,6 +462,11 @@ func TestSaveNameServerGroup(t *testing.T) {
|
|||||||
NSType: nbdns.UDPNameServerType,
|
NSType: nbdns.UDPNameServerType,
|
||||||
Port: nbdns.DefaultDNSPort,
|
Port: nbdns.DefaultDNSPort,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
IP: netip.MustParseAddr("1.1.4.4"),
|
||||||
|
NSType: nbdns.UDPNameServerType,
|
||||||
|
Port: nbdns.DefaultDNSPort,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
invalidID := "doesntExist"
|
invalidID := "doesntExist"
|
||||||
validName := "12345678901234567890qw"
|
validName := "12345678901234567890qw"
|
||||||
|
|||||||
@@ -890,18 +890,6 @@ func (am *DefaultAccountManager) SaveOrAddUser(accountID, initiatorUserID string
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if userData == nil {
|
|
||||||
// lets check external cache
|
|
||||||
key := newUser.IntegrationReference.CacheKey(account.Id, newUser.Id)
|
|
||||||
log.Debugf("looking up user %s of account %s in external cache", key, account.Id)
|
|
||||||
info, err := am.externalCacheManager.Get(am.ctx, key)
|
|
||||||
if err != nil {
|
|
||||||
log.Infof("Get ExternalCache for key: %s, error: %s", key, err)
|
|
||||||
return nil, status.Errorf(status.NotFound, "user %s not found in the IdP", newUser.Id)
|
|
||||||
}
|
|
||||||
|
|
||||||
return newUser.ToUserInfo(info)
|
|
||||||
}
|
|
||||||
return newUser.ToUserInfo(userData)
|
return newUser.ToUserInfo(userData)
|
||||||
}
|
}
|
||||||
return newUser.ToUserInfo(nil)
|
return newUser.ToUserInfo(nil)
|
||||||
|
|||||||
Reference in New Issue
Block a user