diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4806b5676..4fc7f3154 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,6 +65,13 @@ jobs: with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_TOKEN }} + - name: Log in to the GitHub container registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.CI_DOCKER_PUSH_GITHUB_TOKEN }} - name: Install OS build dependencies run: sudo apt update && sudo apt install -y -q gcc-arm-linux-gnueabihf gcc-aarch64-linux-gnu diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 112659d1c..693fbfe01 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -175,10 +175,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/netbird:{{ .Version }}-arm + - ghcr.io/netbirdio/netbird:{{ .Version }}-arm ids: - netbird goarch: arm @@ -191,11 +192,12 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/netbird:{{ .Version }}-rootless-amd64 + - ghcr.io/netbirdio/netbird:{{ .Version }}-rootless-amd64 ids: - netbird goarch: amd64 @@ -207,9 +209,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/netbird:{{ .Version }}-rootless-arm64v8 + - ghcr.io/netbirdio/netbird:{{ .Version }}-rootless-arm64v8 ids: - netbird goarch: arm64 @@ -221,9 +225,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/netbird:{{ .Version }}-rootless-arm + - ghcr.io/netbirdio/netbird:{{ .Version }}-rootless-arm ids: - netbird goarch: arm @@ -236,10 +242,12 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/relay:{{ .Version }}-amd64 + - ghcr.io/netbirdio/relay:{{ .Version }}-amd64 ids: - netbird-relay goarch: amd64 @@ -251,10 +259,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/relay:{{ .Version }}-arm64v8 + - ghcr.io/netbirdio/relay:{{ .Version }}-arm64v8 ids: - netbird-relay goarch: arm64 @@ -266,10 +275,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/relay:{{ .Version }}-arm + - ghcr.io/netbirdio/relay:{{ .Version }}-arm ids: - netbird-relay goarch: arm @@ -282,10 +292,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/signal:{{ .Version }}-amd64 + - ghcr.io/netbirdio/signal:{{ .Version }}-amd64 ids: - netbird-signal goarch: amd64 @@ -297,10 +308,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/signal:{{ .Version }}-arm64v8 + - ghcr.io/netbirdio/signal:{{ .Version }}-arm64v8 ids: - netbird-signal goarch: arm64 @@ -312,10 +324,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/signal:{{ .Version }}-arm + - ghcr.io/netbirdio/signal:{{ .Version }}-arm ids: - netbird-signal goarch: arm @@ -328,10 +341,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/management:{{ .Version }}-amd64 + - ghcr.io/netbirdio/management:{{ .Version }}-amd64 ids: - netbird-mgmt goarch: amd64 @@ -343,10 +357,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/management:{{ .Version }}-arm64v8 + - ghcr.io/netbirdio/management:{{ .Version }}-arm64v8 ids: - netbird-mgmt goarch: arm64 @@ -358,10 +373,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/management:{{ .Version }}-arm + - ghcr.io/netbirdio/management:{{ .Version }}-arm ids: - netbird-mgmt goarch: arm @@ -374,10 +390,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/management:{{ .Version }}-debug-amd64 + - ghcr.io/netbirdio/management:{{ .Version }}-debug-amd64 ids: - netbird-mgmt goarch: amd64 @@ -389,10 +406,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/management:{{ .Version }}-debug-arm64v8 + - ghcr.io/netbirdio/management:{{ .Version }}-debug-arm64v8 ids: - netbird-mgmt goarch: arm64 @@ -404,11 +422,12 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/management:{{ .Version }}-debug-arm + - ghcr.io/netbirdio/management:{{ .Version }}-debug-arm ids: - netbird-mgmt goarch: arm @@ -421,10 +440,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/upload:{{ .Version }}-amd64 + - ghcr.io/netbirdio/upload:{{ .Version }}-amd64 ids: - netbird-upload goarch: amd64 @@ -436,10 +456,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/upload:{{ .Version }}-arm64v8 + - ghcr.io/netbirdio/upload:{{ .Version }}-arm64v8 ids: - netbird-upload goarch: arm64 @@ -451,10 +472,11 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" - image_templates: - netbirdio/upload:{{ .Version }}-arm + - ghcr.io/netbirdio/upload:{{ .Version }}-arm ids: - netbird-upload goarch: arm @@ -467,7 +489,7 @@ dockers: - "--label=org.opencontainers.image.title={{.ProjectName}}" - "--label=org.opencontainers.image.version={{.Version}}" - "--label=org.opencontainers.image.revision={{.FullCommit}}" - - "--label=org.opencontainers.image.version={{.Version}}" + - "--label=org.opencontainers.image.source=https://github.com/netbirdio/{{.ProjectName}}" - "--label=maintainer=dev@netbird.io" docker_manifests: - name_template: netbirdio/netbird:{{ .Version }} @@ -546,6 +568,84 @@ docker_manifests: - netbirdio/upload:{{ .Version }}-arm64v8 - netbirdio/upload:{{ .Version }}-arm - netbirdio/upload:{{ .Version }}-amd64 + + - name_template: ghcr.io/netbirdio/netbird:{{ .Version }} + image_templates: + - ghcr.io/netbirdio/netbird:{{ .Version }}-arm64v8 + - ghcr.io/netbirdio/netbird:{{ .Version }}-arm + - ghcr.io/netbirdio/netbird:{{ .Version }}-amd64 + + - name_template: ghcr.io/netbirdio/netbird:latest + image_templates: + - ghcr.io/netbirdio/netbird:{{ .Version }}-arm64v8 + - ghcr.io/netbirdio/netbird:{{ .Version }}-arm + - ghcr.io/netbirdio/netbird:{{ .Version }}-amd64 + + - name_template: ghcr.io/netbirdio/netbird:{{ .Version }}-rootless + image_templates: + - ghcr.io/netbirdio/netbird:{{ .Version }}-rootless-arm64v8 + - ghcr.io/netbirdio/netbird:{{ .Version }}-rootless-arm + - ghcr.io/netbirdio/netbird:{{ .Version }}-rootless-amd64 + + - name_template: ghcr.io/netbirdio/netbird:rootless-latest + image_templates: + - ghcr.io/netbirdio/netbird:{{ .Version }}-rootless-arm64v8 + - ghcr.io/netbirdio/netbird:{{ .Version }}-rootless-arm + - ghcr.io/netbirdio/netbird:{{ .Version }}-rootless-amd64 + + - name_template: ghcr.io/netbirdio/relay:{{ .Version }} + image_templates: + - ghcr.io/netbirdio/relay:{{ .Version }}-arm64v8 + - ghcr.io/netbirdio/relay:{{ .Version }}-arm + - ghcr.io/netbirdio/relay:{{ .Version }}-amd64 + + - name_template: ghcr.io/netbirdio/relay:latest + image_templates: + - ghcr.io/netbirdio/relay:{{ .Version }}-arm64v8 + - ghcr.io/netbirdio/relay:{{ .Version }}-arm + - ghcr.io/netbirdio/relay:{{ .Version }}-amd64 + + - name_template: ghcr.io/netbirdio/signal:{{ .Version }} + image_templates: + - ghcr.io/netbirdio/signal:{{ .Version }}-arm64v8 + - ghcr.io/netbirdio/signal:{{ .Version }}-arm + - ghcr.io/netbirdio/signal:{{ .Version }}-amd64 + + - name_template: ghcr.io/netbirdio/signal:latest + image_templates: + - ghcr.io/netbirdio/signal:{{ .Version }}-arm64v8 + - ghcr.io/netbirdio/signal:{{ .Version }}-arm + - ghcr.io/netbirdio/signal:{{ .Version }}-amd64 + + - name_template: ghcr.io/netbirdio/management:{{ .Version }} + image_templates: + - ghcr.io/netbirdio/management:{{ .Version }}-arm64v8 + - ghcr.io/netbirdio/management:{{ .Version }}-arm + - ghcr.io/netbirdio/management:{{ .Version }}-amd64 + + - name_template: ghcr.io/netbirdio/management:latest + image_templates: + - ghcr.io/netbirdio/management:{{ .Version }}-arm64v8 + - ghcr.io/netbirdio/management:{{ .Version }}-arm + - ghcr.io/netbirdio/management:{{ .Version }}-amd64 + + - name_template: ghcr.io/netbirdio/management:debug-latest + image_templates: + - ghcr.io/netbirdio/management:{{ .Version }}-debug-arm64v8 + - ghcr.io/netbirdio/management:{{ .Version }}-debug-arm + - ghcr.io/netbirdio/management:{{ .Version }}-debug-amd64 + + - name_template: ghcr.io/netbirdio/upload:{{ .Version }} + image_templates: + - ghcr.io/netbirdio/upload:{{ .Version }}-arm64v8 + - ghcr.io/netbirdio/upload:{{ .Version }}-arm + - ghcr.io/netbirdio/upload:{{ .Version }}-amd64 + + - name_template: ghcr.io/netbirdio/upload:latest + image_templates: + - ghcr.io/netbirdio/upload:{{ .Version }}-arm64v8 + - ghcr.io/netbirdio/upload:{{ .Version }}-arm + - ghcr.io/netbirdio/upload:{{ .Version }}-amd64 brews: - ids: - default diff --git a/client/android/client.go b/client/android/client.go index 229bcd974..3b8a5bd0f 100644 --- a/client/android/client.go +++ b/client/android/client.go @@ -59,6 +59,8 @@ type Client struct { deviceName string uiVersion string networkChangeListener listener.NetworkChangeListener + + connectClient *internal.ConnectClient } // NewClient instantiate a new Client @@ -106,8 +108,8 @@ func (c *Client) Run(urlOpener URLOpener, dns *DNSList, dnsReadyListener DnsRead // todo do not throw error in case of cancelled context ctx = internal.CtxInitState(ctx) - connectClient := internal.NewConnectClient(ctx, cfg, c.recorder) - return connectClient.RunOnAndroid(c.tunAdapter, c.iFaceDiscover, c.networkChangeListener, dns.items, dnsReadyListener) + c.connectClient = internal.NewConnectClient(ctx, cfg, c.recorder) + return c.connectClient.RunOnAndroid(c.tunAdapter, c.iFaceDiscover, c.networkChangeListener, dns.items, dnsReadyListener) } // RunWithoutLogin we apply this type of run function when the backed has been started without UI (i.e. after reboot). @@ -132,8 +134,8 @@ func (c *Client) RunWithoutLogin(dns *DNSList, dnsReadyListener DnsReadyListener // todo do not throw error in case of cancelled context ctx = internal.CtxInitState(ctx) - connectClient := internal.NewConnectClient(ctx, cfg, c.recorder) - return connectClient.RunOnAndroid(c.tunAdapter, c.iFaceDiscover, c.networkChangeListener, dns.items, dnsReadyListener) + c.connectClient = internal.NewConnectClient(ctx, cfg, c.recorder) + return c.connectClient.RunOnAndroid(c.tunAdapter, c.iFaceDiscover, c.networkChangeListener, dns.items, dnsReadyListener) } // Stop the internal client and free the resources @@ -174,6 +176,53 @@ func (c *Client) PeersList() *PeerInfoArray { return &PeerInfoArray{items: peerInfos} } +func (c *Client) Networks() *NetworkArray { + if c.connectClient == nil { + log.Error("not connected") + return nil + } + + engine := c.connectClient.Engine() + if engine == nil { + log.Error("could not get engine") + return nil + } + + routeManager := engine.GetRouteManager() + if routeManager == nil { + log.Error("could not get route manager") + return nil + } + + networkArray := &NetworkArray{ + items: make([]Network, 0), + } + + for id, routes := range routeManager.GetClientRoutesWithNetID() { + if len(routes) == 0 { + continue + } + + if routes[0].IsDynamic() { + continue + } + + peer, err := c.recorder.GetPeer(routes[0].Peer) + if err != nil { + log.Errorf("could not get peer info for %s: %v", routes[0].Peer, err) + continue + } + network := Network{ + Name: string(id), + Network: routes[0].Network.String(), + Peer: peer.FQDN, + Status: peer.ConnStatus.String(), + } + networkArray.Add(network) + } + return networkArray +} + // OnUpdatedHostDNS update the DNS servers addresses for root zones func (c *Client) OnUpdatedHostDNS(list *DNSList) error { dnsServer, err := dns.GetServerDns() diff --git a/client/android/networks.go b/client/android/networks.go new file mode 100644 index 000000000..aa130420b --- /dev/null +++ b/client/android/networks.go @@ -0,0 +1,27 @@ +//go:build android + +package android + +type Network struct { + Name string + Network string + Peer string + Status string +} + +type NetworkArray struct { + items []Network +} + +func (array *NetworkArray) Add(s Network) *NetworkArray { + array.items = append(array.items, s) + return array +} + +func (array *NetworkArray) Get(i int) *Network { + return &array.items[i] +} + +func (array *NetworkArray) Size() int { + return len(array.items) +} diff --git a/client/android/peer_notifier.go b/client/android/peer_notifier.go index 9f6fcddd6..1f5564c72 100644 --- a/client/android/peer_notifier.go +++ b/client/android/peer_notifier.go @@ -7,30 +7,23 @@ type PeerInfo struct { ConnStatus string // Todo replace to enum } -// PeerInfoCollection made for Java layer to get non default types as collection -type PeerInfoCollection interface { - Add(s string) PeerInfoCollection - Get(i int) string - Size() int -} - -// PeerInfoArray is the implementation of the PeerInfoCollection +// PeerInfoArray is a wrapper of []PeerInfo type PeerInfoArray struct { items []PeerInfo } // Add new PeerInfo to the collection -func (array PeerInfoArray) Add(s PeerInfo) PeerInfoArray { +func (array *PeerInfoArray) Add(s PeerInfo) *PeerInfoArray { array.items = append(array.items, s) return array } // Get return an element of the collection -func (array PeerInfoArray) Get(i int) *PeerInfo { +func (array *PeerInfoArray) Get(i int) *PeerInfo { return &array.items[i] } // Size return with the size of the collection -func (array PeerInfoArray) Size() int { +func (array *PeerInfoArray) Size() int { return len(array.items) } diff --git a/client/android/preferences.go b/client/android/preferences.go index 08485eafc..2a8b197e7 100644 --- a/client/android/preferences.go +++ b/client/android/preferences.go @@ -71,6 +71,42 @@ func (p *Preferences) SetPreSharedKey(key string) { p.configInput.PreSharedKey = &key } +// SetRosenpassEnabled store if rosenpass is enabled +func (p *Preferences) SetRosenpassEnabled(enabled bool) { + p.configInput.RosenpassEnabled = &enabled +} + +// GetRosenpassEnabled read rosenpass enabled from config file +func (p *Preferences) GetRosenpassEnabled() (bool, error) { + if p.configInput.RosenpassEnabled != nil { + return *p.configInput.RosenpassEnabled, nil + } + + cfg, err := internal.ReadConfig(p.configInput.ConfigPath) + if err != nil { + return false, err + } + return cfg.RosenpassEnabled, err +} + +// SetRosenpassPermissive store the given permissive and wait for commit +func (p *Preferences) SetRosenpassPermissive(permissive bool) { + p.configInput.RosenpassPermissive = &permissive +} + +// GetRosenpassPermissive read rosenpass permissive from config file +func (p *Preferences) GetRosenpassPermissive() (bool, error) { + if p.configInput.RosenpassPermissive != nil { + return *p.configInput.RosenpassPermissive, nil + } + + cfg, err := internal.ReadConfig(p.configInput.ConfigPath) + if err != nil { + return false, err + } + return cfg.RosenpassPermissive, err +} + // Commit write out the changes into config file func (p *Preferences) Commit() error { _, err := internal.UpdateOrCreateConfig(p.configInput) diff --git a/client/internal/dns/local/local.go b/client/internal/dns/local/local.go index de3d8514b..c19356a7e 100644 --- a/client/internal/dns/local/local.go +++ b/client/internal/dns/local/local.go @@ -12,16 +12,19 @@ import ( "github.com/netbirdio/netbird/client/internal/dns/types" nbdns "github.com/netbirdio/netbird/dns" + "github.com/netbirdio/netbird/management/domain" ) type Resolver struct { mu sync.RWMutex records map[dns.Question][]dns.RR + domains map[domain.Domain]struct{} } func NewResolver() *Resolver { return &Resolver{ records: make(map[dns.Question][]dns.RR), + domains: make(map[domain.Domain]struct{}), } } @@ -64,8 +67,12 @@ func (d *Resolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { replyMessage.Rcode = dns.RcodeSuccess replyMessage.Answer = append(replyMessage.Answer, records...) } else { - // TODO: return success if we have a different record type for the same name, relevant for search domains - replyMessage.Rcode = dns.RcodeNameError + // Check if we have any records for this domain name with different types + if d.hasRecordsForDomain(domain.Domain(question.Name)) { + replyMessage.Rcode = dns.RcodeSuccess // NOERROR with 0 records + } else { + replyMessage.Rcode = dns.RcodeNameError // NXDOMAIN + } } if err := w.WriteMsg(replyMessage); err != nil { @@ -73,6 +80,15 @@ func (d *Resolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { } } +// hasRecordsForDomain checks if any records exist for the given domain name regardless of type +func (d *Resolver) hasRecordsForDomain(domainName domain.Domain) bool { + d.mu.RLock() + defer d.mu.RUnlock() + + _, exists := d.domains[domainName] + return exists +} + // lookupRecords fetches *all* DNS records matching the first question in r. func (d *Resolver) lookupRecords(question dns.Question) []dns.RR { d.mu.RLock() @@ -111,6 +127,7 @@ func (d *Resolver) Update(update []nbdns.SimpleRecord) { defer d.mu.Unlock() maps.Clear(d.records) + maps.Clear(d.domains) for _, rec := range update { if err := d.registerRecord(rec); err != nil { @@ -144,6 +161,7 @@ func (d *Resolver) registerRecord(record nbdns.SimpleRecord) error { } d.records[q] = append(d.records[q], rr) + d.domains[domain.Domain(q.Name)] = struct{}{} return nil } diff --git a/client/internal/dns/local/local_test.go b/client/internal/dns/local/local_test.go index 1d38191e7..8b13b69ff 100644 --- a/client/internal/dns/local/local_test.go +++ b/client/internal/dns/local/local_test.go @@ -470,3 +470,115 @@ func TestLocalResolver_CNAMEFallback(t *testing.T) { }) } } + +// TestLocalResolver_NoErrorWithDifferentRecordType verifies that querying for a record type +// that doesn't exist but where other record types exist for the same domain returns NOERROR +// with 0 records instead of NXDOMAIN +func TestLocalResolver_NoErrorWithDifferentRecordType(t *testing.T) { + resolver := NewResolver() + + recordA := nbdns.SimpleRecord{ + Name: "example.netbird.cloud.", + Type: int(dns.TypeA), + Class: nbdns.DefaultClass, + TTL: 300, + RData: "192.168.1.100", + } + + recordCNAME := nbdns.SimpleRecord{ + Name: "alias.netbird.cloud.", + Type: int(dns.TypeCNAME), + Class: nbdns.DefaultClass, + TTL: 300, + RData: "target.example.com.", + } + + resolver.Update([]nbdns.SimpleRecord{recordA, recordCNAME}) + + testCases := []struct { + name string + queryName string + queryType uint16 + expectedRcode int + shouldHaveData bool + }{ + { + name: "Query A record that exists", + queryName: "example.netbird.cloud.", + queryType: dns.TypeA, + expectedRcode: dns.RcodeSuccess, + shouldHaveData: true, + }, + { + name: "Query AAAA for domain with only A record", + queryName: "example.netbird.cloud.", + queryType: dns.TypeAAAA, + expectedRcode: dns.RcodeSuccess, + shouldHaveData: false, + }, + { + name: "Query other record with different case and non-fqdn", + queryName: "EXAMPLE.netbird.cloud", + queryType: dns.TypeAAAA, + expectedRcode: dns.RcodeSuccess, + shouldHaveData: false, + }, + { + name: "Query TXT for domain with only A record", + queryName: "example.netbird.cloud.", + queryType: dns.TypeTXT, + expectedRcode: dns.RcodeSuccess, + shouldHaveData: false, + }, + { + name: "Query A for domain with only CNAME record", + queryName: "alias.netbird.cloud.", + queryType: dns.TypeA, + expectedRcode: dns.RcodeSuccess, + shouldHaveData: true, + }, + { + name: "Query AAAA for domain with only CNAME record", + queryName: "alias.netbird.cloud.", + queryType: dns.TypeAAAA, + expectedRcode: dns.RcodeSuccess, + shouldHaveData: true, + }, + { + name: "Query for completely non-existent domain", + queryName: "nonexistent.netbird.cloud.", + queryType: dns.TypeA, + expectedRcode: dns.RcodeNameError, + shouldHaveData: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var responseMSG *dns.Msg + + msg := new(dns.Msg).SetQuestion(tc.queryName, tc.queryType) + + responseWriter := &test.MockResponseWriter{ + WriteMsgFunc: func(m *dns.Msg) error { + responseMSG = m + return nil + }, + } + + resolver.ServeDNS(responseWriter, msg) + + require.NotNil(t, responseMSG, "Should have received a response message") + + assert.Equal(t, tc.expectedRcode, responseMSG.Rcode, + "Response code should be %d (%s)", + tc.expectedRcode, dns.RcodeToString[tc.expectedRcode]) + + if tc.shouldHaveData { + assert.Greater(t, len(responseMSG.Answer), 0, "Response should contain answers") + } else { + assert.Equal(t, 0, len(responseMSG.Answer), "Response should contain no answers") + } + }) + } +} diff --git a/go.mod b/go.mod index 11dc88c43..a12058278 100644 --- a/go.mod +++ b/go.mod @@ -63,7 +63,7 @@ require ( github.com/miekg/dns v1.1.59 github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/nadoo/ipset v0.5.0 - github.com/netbirdio/management-integrations/integrations v0.0.0-20250529122842-6700aa91190c + github.com/netbirdio/management-integrations/integrations v0.0.0-20250612164546-6bd7e2338d65 github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20250514131221-a464fd5f30cb github.com/okta/okta-sdk-golang/v2 v2.18.0 github.com/oschwald/maxminddb-golang v1.12.0 diff --git a/go.sum b/go.sum index f887cee94..6ce503dd1 100644 --- a/go.sum +++ b/go.sum @@ -503,8 +503,8 @@ github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944 h1:TDtJKmM6S github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944/go.mod h1:sHA6TRxjQ6RLbnI+3R4DZo2Eseg/iKiPRfNmcuNySVQ= github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e h1:PURA50S8u4mF6RrkYYCAvvPCixhqqEiEy3Ej6avh04c= github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e/go.mod h1:YMLU7qbKfVjmEv7EoZPIVEI+kNYxWCdPK3VS0BU+U4Q= -github.com/netbirdio/management-integrations/integrations v0.0.0-20250529122842-6700aa91190c h1:SdZxYjR9XXHLyRsTbS1EHBr6+RI15oie1K9Q8yvi3FY= -github.com/netbirdio/management-integrations/integrations v0.0.0-20250529122842-6700aa91190c/go.mod h1:Gi9raplYzCCyh07Olw/DVfCJTFgpr1WCXJ/Q+8TSA9Q= +github.com/netbirdio/management-integrations/integrations v0.0.0-20250612164546-6bd7e2338d65 h1:5OfYiLjpr4dbQYJI5ouZaylkVdi2KlErLFOwBeBo5Hw= +github.com/netbirdio/management-integrations/integrations v0.0.0-20250612164546-6bd7e2338d65/go.mod h1:Gi9raplYzCCyh07Olw/DVfCJTFgpr1WCXJ/Q+8TSA9Q= github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502 h1:3tHlFmhTdX9axERMVN63dqyFqnvuD+EMJHzM7mNGON8= github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20250514131221-a464fd5f30cb h1:Cr6age+ePALqlSvtp7wc6lYY97XN7rkD1K4XEDmY+TU= diff --git a/management/cmd/management.go b/management/cmd/management.go index 5fb07890f..bce09efdd 100644 --- a/management/cmd/management.go +++ b/management/cmd/management.go @@ -159,6 +159,12 @@ var ( if err != nil { return err } + + integrationMetrics, err := integrations.InitIntegrationMetrics(ctx, appMetrics) + if err != nil { + return err + } + store, err := store.NewStore(ctx, config.StoreConfig.Engine, config.Datadir, appMetrics, false) if err != nil { return fmt.Errorf("failed creating Store: %s: %v", config.Datadir, err) @@ -176,7 +182,7 @@ var ( if disableSingleAccMode { mgmtSingleAccModeDomain = "" } - eventStore, key, err := integrations.InitEventStore(ctx, config.Datadir, config.DataStoreEncryptionKey) + eventStore, key, err := integrations.InitEventStore(ctx, config.Datadir, config.DataStoreEncryptionKey, integrationMetrics) if err != nil { return fmt.Errorf("failed to initialize database: %s", err) } diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index 5786dc871..2b27f9e0f 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -392,6 +392,18 @@ func extractPeerMeta(ctx context.Context, meta *proto.PeerSystemMeta) nbpeer.Pee Cloud: meta.GetEnvironment().GetCloud(), Platform: meta.GetEnvironment().GetPlatform(), }, + Flags: nbpeer.Flags{ + RosenpassEnabled: meta.GetFlags().GetRosenpassEnabled(), + RosenpassPermissive: meta.GetFlags().GetRosenpassPermissive(), + ServerSSHAllowed: meta.GetFlags().GetServerSSHAllowed(), + DisableClientRoutes: meta.GetFlags().GetDisableClientRoutes(), + DisableServerRoutes: meta.GetFlags().GetDisableServerRoutes(), + DisableDNS: meta.GetFlags().GetDisableDNS(), + DisableFirewall: meta.GetFlags().GetDisableFirewall(), + BlockLANAccess: meta.GetFlags().GetBlockLANAccess(), + BlockInbound: meta.GetFlags().GetBlockInbound(), + LazyConnectionEnabled: meta.GetFlags().GetLazyConnectionEnabled(), + }, Files: files, } } diff --git a/management/server/peer/peer.go b/management/server/peer/peer.go index afda55d17..8ce1dfb4e 100644 --- a/management/server/peer/peer.go +++ b/management/server/peer/peer.go @@ -94,6 +94,22 @@ type File struct { ProcessIsRunning bool } +// Flags defines a set of options to control feature behavior +type Flags struct { + RosenpassEnabled bool + RosenpassPermissive bool + ServerSSHAllowed bool + + DisableClientRoutes bool + DisableServerRoutes bool + DisableDNS bool + DisableFirewall bool + BlockLANAccess bool + BlockInbound bool + + LazyConnectionEnabled bool +} + // PeerSystemMeta is a metadata of a Peer machine system type PeerSystemMeta struct { //nolint:revive Hostname string @@ -111,6 +127,7 @@ type PeerSystemMeta struct { //nolint:revive SystemProductName string SystemManufacturer string Environment Environment `gorm:"serializer:json"` + Flags Flags `gorm:"serializer:json"` Files []File `gorm:"serializer:json"` } @@ -155,7 +172,8 @@ func (p PeerSystemMeta) isEqual(other PeerSystemMeta) bool { p.SystemProductName == other.SystemProductName && p.SystemManufacturer == other.SystemManufacturer && p.Environment.Cloud == other.Environment.Cloud && - p.Environment.Platform == other.Environment.Platform + p.Environment.Platform == other.Environment.Platform && + p.Flags.isEqual(other.Flags) } func (p PeerSystemMeta) isEmpty() bool { @@ -315,3 +333,16 @@ func (p *Peer) UpdateLastLogin() *Peer { p.Status = newStatus return p } + +func (f Flags) isEqual(other Flags) bool { + return f.RosenpassEnabled == other.RosenpassEnabled && + f.RosenpassPermissive == other.RosenpassPermissive && + f.ServerSSHAllowed == other.ServerSSHAllowed && + f.DisableClientRoutes == other.DisableClientRoutes && + f.DisableServerRoutes == other.DisableServerRoutes && + f.DisableDNS == other.DisableDNS && + f.DisableFirewall == other.DisableFirewall && + f.BlockLANAccess == other.BlockLANAccess && + f.BlockInbound == other.BlockInbound && + f.LazyConnectionEnabled == other.LazyConnectionEnabled +} diff --git a/management/server/peer/peer_test.go b/management/server/peer/peer_test.go index 3d3a2e311..1aa3f6ffc 100644 --- a/management/server/peer/peer_test.go +++ b/management/server/peer/peer_test.go @@ -4,6 +4,8 @@ import ( "fmt" "net/netip" "testing" + + "github.com/stretchr/testify/require" ) // FQDNOld is the original implementation for benchmarking purposes @@ -83,3 +85,59 @@ func TestIsEqual(t *testing.T) { t.Error("meta1 should be equal to meta2") } } + +func TestFlags_IsEqual(t *testing.T) { + tests := []struct { + name string + f1 Flags + f2 Flags + expect bool + }{ + { + name: "should be equal when all fields are identical", + f1: Flags{ + RosenpassEnabled: true, RosenpassPermissive: false, ServerSSHAllowed: true, + DisableClientRoutes: false, DisableServerRoutes: true, DisableDNS: false, + DisableFirewall: true, BlockLANAccess: false, BlockInbound: true, LazyConnectionEnabled: true, + }, + f2: Flags{ + RosenpassEnabled: true, RosenpassPermissive: false, ServerSSHAllowed: true, + DisableClientRoutes: false, DisableServerRoutes: true, DisableDNS: false, + DisableFirewall: true, BlockLANAccess: false, BlockInbound: true, LazyConnectionEnabled: true, + }, + expect: true, + }, + { + name: "shouldn't be equal when fields are different", + f1: Flags{ + RosenpassEnabled: true, RosenpassPermissive: false, ServerSSHAllowed: true, + DisableClientRoutes: false, DisableServerRoutes: true, DisableDNS: false, + DisableFirewall: true, BlockLANAccess: false, BlockInbound: true, LazyConnectionEnabled: true, + }, + f2: Flags{ + RosenpassEnabled: false, RosenpassPermissive: true, ServerSSHAllowed: false, + DisableClientRoutes: true, DisableServerRoutes: false, DisableDNS: true, + DisableFirewall: false, BlockLANAccess: true, BlockInbound: false, LazyConnectionEnabled: false, + }, + expect: false, + }, + { + name: "should be equal when both are empty", + f1: Flags{}, + f2: Flags{}, + expect: true, + }, + { + name: "shouldn't be equal when at least one field differs", + f1: Flags{RosenpassEnabled: true}, + f2: Flags{RosenpassEnabled: false}, + expect: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.expect, tt.f1.isEqual(tt.f2)) + }) + } +} diff --git a/management/server/telemetry/app_metrics.go b/management/server/telemetry/app_metrics.go index 09deb8127..988f91779 100644 --- a/management/server/telemetry/app_metrics.go +++ b/management/server/telemetry/app_metrics.go @@ -184,10 +184,10 @@ func (appMetrics *defaultAppMetrics) Expose(ctx context.Context, port int, endpo } appMetrics.listener = listener go func() { - err := http.Serve(listener, rootRouter) - if err != nil { - return + if err := http.Serve(listener, rootRouter); err != nil && err != http.ErrServerClosed { + log.WithContext(ctx).Errorf("metrics server error: %v", err) } + log.WithContext(ctx).Info("metrics server stopped") }() log.WithContext(ctx).Infof("enabled application metrics and exposing on http://%s", listener.Addr().String()) @@ -204,7 +204,7 @@ func (appMetrics *defaultAppMetrics) GetMeter() metric2.Meter { func NewDefaultAppMetrics(ctx context.Context) (AppMetrics, error) { exporter, err := prometheus.New() if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create prometheus exporter: %w", err) } provider := metric.NewMeterProvider(metric.WithReader(exporter)) @@ -213,32 +213,32 @@ func NewDefaultAppMetrics(ctx context.Context) (AppMetrics, error) { idpMetrics, err := NewIDPMetrics(ctx, meter) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to initialize IDP metrics: %w", err) } middleware, err := NewMetricsMiddleware(ctx, meter) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to initialize HTTP middleware metrics: %w", err) } grpcMetrics, err := NewGRPCMetrics(ctx, meter) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to initialize gRPC metrics: %w", err) } storeMetrics, err := NewStoreMetrics(ctx, meter) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to initialize store metrics: %w", err) } updateChannelMetrics, err := NewUpdateChannelMetrics(ctx, meter) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to initialize update channel metrics: %w", err) } accountManagerMetrics, err := NewAccountManagerMetrics(ctx, meter) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to initialize account manager metrics: %w", err) } return &defaultAppMetrics{