Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[client] Add UI client event notifications #3207

Merged
merged 8 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 15 additions & 20 deletions client/cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ type peerStateDetailOutput struct {
TransferSent int64 `json:"transferSent" yaml:"transferSent"`
Latency time.Duration `json:"latency" yaml:"latency"`
RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"`
Routes []string `json:"routes" yaml:"routes"`
Networks []string `json:"networks" yaml:"networks"`
}

Expand Down Expand Up @@ -98,9 +97,9 @@ type statusOutputOverview struct {
FQDN string `json:"fqdn" yaml:"fqdn"`
RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"`
RosenpassPermissive bool `json:"quantumResistancePermissive" yaml:"quantumResistancePermissive"`
Routes []string `json:"routes" yaml:"routes"`
Networks []string `json:"networks" yaml:"networks"`
NSServerGroups []nsServerGroupStateOutput `json:"dnsServers" yaml:"dnsServers"`
Events []systemEventOutput `json:"events" yaml:"events"`
}

var (
Expand Down Expand Up @@ -284,9 +283,9 @@ func convertToStatusOutputOverview(resp *proto.StatusResponse) statusOutputOverv
FQDN: pbFullStatus.GetLocalPeerState().GetFqdn(),
RosenpassEnabled: pbFullStatus.GetLocalPeerState().GetRosenpassEnabled(),
RosenpassPermissive: pbFullStatus.GetLocalPeerState().GetRosenpassPermissive(),
Routes: pbFullStatus.GetLocalPeerState().GetNetworks(),
Networks: pbFullStatus.GetLocalPeerState().GetNetworks(),
NSServerGroups: mapNSGroups(pbFullStatus.GetDnsServers()),
Events: mapEvents(pbFullStatus.GetEvents()),
}

if anonymizeFlag {
Expand Down Expand Up @@ -393,7 +392,6 @@ func mapPeers(peers []*proto.PeerState) peersStateOutput {
TransferSent: transferSent,
Latency: pbPeerState.GetLatency().AsDuration(),
RosenpassEnabled: pbPeerState.GetRosenpassEnabled(),
Routes: pbPeerState.GetNetworks(),
Networks: pbPeerState.GetNetworks(),
}

Expand Down Expand Up @@ -559,7 +557,6 @@ func parseGeneralSummary(overview statusOutputOverview, showURL bool, showRelays
"NetBird IP: %s\n"+
"Interface type: %s\n"+
"Quantum resistance: %s\n"+
"Routes: %s\n"+
"Networks: %s\n"+
"Peers count: %s\n",
fmt.Sprintf("%s/%s%s", goos, goarch, goarm),
Expand All @@ -574,21 +571,24 @@ func parseGeneralSummary(overview statusOutputOverview, showURL bool, showRelays
interfaceTypeString,
rosenpassEnabledStatus,
networks,
networks,
peersCountString,
)
return summary
}

func parseToFullDetailSummary(overview statusOutputOverview) string {
parsedPeersString := parsePeers(overview.Peers, overview.RosenpassEnabled, overview.RosenpassPermissive)
parsedEventsString := parseEvents(overview.Events)
summary := parseGeneralSummary(overview, true, true, true)

return fmt.Sprintf(
"Peers detail:"+
"%s\n"+
"Events:"+
"%s\n"+
"%s",
parsedPeersString,
parsedEventsString,
summary,
)
}
Expand Down Expand Up @@ -657,7 +657,6 @@ func parsePeers(peers peersStateOutput, rosenpassEnabled, rosenpassPermissive bo
" Last WireGuard handshake: %s\n"+
" Transfer status (received/sent) %s/%s\n"+
" Quantum resistance: %s\n"+
" Routes: %s\n"+
" Networks: %s\n"+
" Latency: %s\n",
peerState.FQDN,
Expand All @@ -676,7 +675,6 @@ func parsePeers(peers peersStateOutput, rosenpassEnabled, rosenpassPermissive bo
toIEC(peerState.TransferSent),
rosenpassEnabledStatus,
networks,
networks,
peerState.Latency.String(),
)

Expand Down Expand Up @@ -825,14 +823,6 @@ func anonymizePeerDetail(a *anonymize.Anonymizer, peer *peerStateDetailOutput) {
for i, route := range peer.Networks {
peer.Networks[i] = a.AnonymizeRoute(route)
}

for i, route := range peer.Routes {
peer.Routes[i] = a.AnonymizeIPString(route)
}

for i, route := range peer.Routes {
peer.Routes[i] = a.AnonymizeRoute(route)
}
}

func anonymizeOverview(a *anonymize.Anonymizer, overview *statusOutputOverview) {
Expand Down Expand Up @@ -870,9 +860,14 @@ func anonymizeOverview(a *anonymize.Anonymizer, overview *statusOutputOverview)
overview.Networks[i] = a.AnonymizeRoute(route)
}

for i, route := range overview.Routes {
overview.Routes[i] = a.AnonymizeRoute(route)
}

overview.FQDN = a.AnonymizeDomain(overview.FQDN)

for i, event := range overview.Events {
overview.Events[i].Message = a.AnonymizeString(event.Message)
overview.Events[i].UserMessage = a.AnonymizeString(event.UserMessage)

for k, v := range event.Metadata {
event.Metadata[k] = a.AnonymizeString(v)
}
}
}
69 changes: 69 additions & 0 deletions client/cmd/status_event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package cmd

import (
"fmt"
"sort"
"strings"
"time"

"github.com/netbirdio/netbird/client/proto"
)

type systemEventOutput struct {
ID string `json:"id" yaml:"id"`
Severity string `json:"severity" yaml:"severity"`
Category string `json:"category" yaml:"category"`
Message string `json:"message" yaml:"message"`
UserMessage string `json:"userMessage" yaml:"userMessage"`
Timestamp time.Time `json:"timestamp" yaml:"timestamp"`
Metadata map[string]string `json:"metadata" yaml:"metadata"`
}

func mapEvents(protoEvents []*proto.SystemEvent) []systemEventOutput {
events := make([]systemEventOutput, len(protoEvents))
for i, event := range protoEvents {
events[i] = systemEventOutput{
ID: event.GetId(),
Severity: event.GetSeverity().String(),
Category: event.GetCategory().String(),
Message: event.GetMessage(),
UserMessage: event.GetUserMessage(),
Timestamp: event.GetTimestamp().AsTime(),
Metadata: event.GetMetadata(),
}
}
return events
}

func parseEvents(events []systemEventOutput) string {
if len(events) == 0 {
return " No events recorded"
}

var eventsString strings.Builder
for _, event := range events {
timeStr := timeAgo(event.Timestamp)

metadataStr := ""
if len(event.Metadata) > 0 {
pairs := make([]string, 0, len(event.Metadata))
for k, v := range event.Metadata {
pairs = append(pairs, fmt.Sprintf("%s: %s", k, v))
}
sort.Strings(pairs)
metadataStr = fmt.Sprintf("\n Metadata: %s", strings.Join(pairs, ", "))
}

eventsString.WriteString(fmt.Sprintf("\n [%s] %s (%s)"+
"\n Message: %s"+
"\n Time: %s%s",
event.Severity,
event.Category,
event.ID,
event.Message,
timeStr,
metadataStr,
))
}
return eventsString.String()
}
28 changes: 5 additions & 23 deletions client/cmd/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,6 @@ var overview = statusOutputOverview{
LastWireguardHandshake: time.Date(2001, 1, 1, 1, 1, 2, 0, time.UTC),
TransferReceived: 200,
TransferSent: 100,
Routes: []string{
"10.1.0.0/24",
},
Networks: []string{
"10.1.0.0/24",
},
Expand Down Expand Up @@ -176,6 +173,7 @@ var overview = statusOutputOverview{
},
},
},
Events: []systemEventOutput{},
CliVersion: version.NetbirdVersion(),
DaemonVersion: "0.14.1",
ManagementState: managementStateOutput{
Expand Down Expand Up @@ -230,9 +228,6 @@ var overview = statusOutputOverview{
Error: "timeout",
},
},
Routes: []string{
"10.10.0.0/24",
},
Networks: []string{
"10.10.0.0/24",
},
Expand Down Expand Up @@ -299,9 +294,6 @@ func TestParsingToJSON(t *testing.T) {
"transferSent": 100,
"latency": 10000000,
"quantumResistance": false,
"routes": [
"10.1.0.0/24"
],
"networks": [
"10.1.0.0/24"
]
Expand All @@ -327,7 +319,6 @@ func TestParsingToJSON(t *testing.T) {
"transferSent": 1000,
"latency": 10000000,
"quantumResistance": false,
"routes": null,
"networks": null
}
]
Expand Down Expand Up @@ -366,9 +357,6 @@ func TestParsingToJSON(t *testing.T) {
"fqdn": "some-localhost.awesome-domain.com",
"quantumResistance": false,
"quantumResistancePermissive": false,
"routes": [
"10.10.0.0/24"
],
"networks": [
"10.10.0.0/24"
],
Expand All @@ -393,7 +381,8 @@ func TestParsingToJSON(t *testing.T) {
"enabled": false,
"error": "timeout"
}
]
],
"events": []
}`
// @formatter:on

Expand Down Expand Up @@ -429,8 +418,6 @@ func TestParsingToYAML(t *testing.T) {
transferSent: 100
latency: 10ms
quantumResistance: false
routes:
- 10.1.0.0/24
networks:
- 10.1.0.0/24
- fqdn: peer-2.awesome-domain.com
Expand All @@ -451,7 +438,6 @@ func TestParsingToYAML(t *testing.T) {
transferSent: 1000
latency: 10ms
quantumResistance: false
routes: []
networks: []
cliVersion: development
daemonVersion: 0.14.1
Expand Down Expand Up @@ -479,8 +465,6 @@ usesKernelInterface: true
fqdn: some-localhost.awesome-domain.com
quantumResistance: false
quantumResistancePermissive: false
routes:
- 10.10.0.0/24
networks:
- 10.10.0.0/24
dnsServers:
Expand All @@ -497,6 +481,7 @@ dnsServers:
- example.net
enabled: false
error: timeout
events: []
`

assert.Equal(t, expectedYAML, yaml)
Expand Down Expand Up @@ -526,7 +511,6 @@ func TestParsingToDetail(t *testing.T) {
Last WireGuard handshake: %s
Transfer status (received/sent) 200 B/100 B
Quantum resistance: false
Routes: 10.1.0.0/24
Networks: 10.1.0.0/24
Latency: 10ms

Expand All @@ -543,10 +527,10 @@ func TestParsingToDetail(t *testing.T) {
Last WireGuard handshake: %s
Transfer status (received/sent) 2.0 KiB/1000 B
Quantum resistance: false
Routes: -
Networks: -
Latency: 10ms

Events: No events recorded
OS: %s/%s
Daemon version: 0.14.1
CLI version: %s
Expand All @@ -562,7 +546,6 @@ FQDN: some-localhost.awesome-domain.com
NetBird IP: 192.168.178.100/16
Interface type: Kernel
Quantum resistance: false
Routes: 10.10.0.0/24
Networks: 10.10.0.0/24
Peers count: 2/2 Connected
`, lastConnectionUpdate1, lastHandshake1, lastConnectionUpdate2, lastHandshake2, runtime.GOOS, runtime.GOARCH, overview.CliVersion)
Expand All @@ -584,7 +567,6 @@ FQDN: some-localhost.awesome-domain.com
NetBird IP: 192.168.178.100/16
Interface type: Kernel
Quantum resistance: false
Routes: 10.10.0.0/24
Networks: 10.10.0.0/24
Peers count: 2/2 Connected
`
Expand Down
14 changes: 14 additions & 0 deletions client/internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ type ConfigInput struct {
DisableFirewall *bool

BlockLANAccess *bool

DisableNotifications *bool
}

// Config Configuration type
Expand All @@ -93,6 +95,8 @@ type Config struct {

BlockLANAccess bool

DisableNotifications bool

// SSHKey is a private SSH key in a PEM format
SSHKey string

Expand Down Expand Up @@ -469,6 +473,16 @@ func (config *Config) apply(input ConfigInput) (updated bool, err error) {
updated = true
}

if input.DisableNotifications != nil && *input.DisableNotifications != config.DisableNotifications {
if *input.DisableNotifications {
log.Infof("disabling notifications")
} else {
log.Infof("enabling notifications")
}
config.DisableNotifications = *input.DisableNotifications
updated = true
}

if input.ClientCertKeyPath != "" {
config.ClientCertKeyPath = input.ClientCertKeyPath
updated = true
Expand Down
10 changes: 10 additions & 0 deletions client/internal/dns/upstream.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"net"
"strings"
"sync"
"sync/atomic"
"time"
Expand All @@ -15,6 +16,7 @@ import (
log "github.com/sirupsen/logrus"

"github.com/netbirdio/netbird/client/internal/peer"
"github.com/netbirdio/netbird/client/proto"
)

const (
Expand Down Expand Up @@ -217,6 +219,14 @@ func (u *upstreamResolverBase) probeAvailability() {
// didn't find a working upstream server, let's disable and try later
if !success {
u.disable(errors.ErrorOrNil())

u.statusRecorder.PublishEvent(
proto.SystemEvent_WARNING,
proto.SystemEvent_DNS,
"All upstream servers failed",
"Unable to reach one or more DNS servers. This might affect your ability to connect to some services.",
map[string]string{"upstreams": strings.Join(u.upstreamServers, ", ")},
)
}
}

Expand Down
Loading
Loading