Skip to content

Commit

Permalink
first version
Browse files Browse the repository at this point in the history
Signed-off-by: Marius Sincovici <marius.sincovici@nordsec.com>
  • Loading branch information
mariusSincovici committed Jan 27, 2025
1 parent 4e09d14 commit 0444cee
Show file tree
Hide file tree
Showing 13 changed files with 423 additions and 47 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ __pycache__

.vagrant
.vscode/*
!.vscode/settings.json
.vscode/settings.json
!.vscode/launch.json
!.vscode/tasks.json
contrib/manual/nordvpn.1
Expand Down
5 changes: 4 additions & 1 deletion cmd/daemon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/NordSecurity/nordvpn-linux/daemon/firewall/allowlist"
"github.com/NordSecurity/nordvpn-linux/daemon/firewall/iptables"
"github.com/NordSecurity/nordvpn-linux/daemon/firewall/notables"
"github.com/NordSecurity/nordvpn-linux/daemon/models"
"github.com/NordSecurity/nordvpn-linux/daemon/netstate"
"github.com/NordSecurity/nordvpn-linux/daemon/pb"
"github.com/NordSecurity/nordvpn-linux/daemon/response"
Expand Down Expand Up @@ -449,6 +450,7 @@ func main() {
daemon.CountryDataFilePath,
daemon.VersionFilePath,
dataUpdateEvents,
models.NewCachedValue(nil, errors.New("empty"), time.Time{}, internal.MeshnetPeersUpdateInterval, func(cv *models.CachedValue[*meshpb.GetPeersResponse]) {}),
)

sharedContext := sharedctx.New()
Expand Down Expand Up @@ -491,6 +493,7 @@ func main() {
daemonEvents,
norduserClient,
sharedContext,
dm,
)

opts := []grpc.ServerOption{
Expand Down Expand Up @@ -577,7 +580,7 @@ func main() {
}
}()
rpc.StartJobs(statePublisher, heartBeatSubject)
meshService.StartJobs()
meshService.StartJobs(daemonEvents.Settings.Meshnet)
rpc.StartKillSwitch()
if internal.IsSystemd() {
go rpc.StartSystemShutdownMonitor()
Expand Down
2 changes: 1 addition & 1 deletion cmd/downloader/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ var Salt = ""
func main() {
dataPath := os.Args[1]
cm := config.NewFilesystemConfigManager(config.SettingsDataFilePath, config.InstallFilePath, Salt, config.NewMachineID(os.ReadFile, os.Hostname), config.StdFilesystemHandle{}, nil)
dm := daemon.NewDataManager(dataPath+InsightsFilename, dataPath+ServersFilename, dataPath+countriesFilename, "", events.NewDataUpdateEvents())
dm := daemon.NewDataManager(dataPath+InsightsFilename, dataPath+ServersFilename, dataPath+countriesFilename, "", events.NewDataUpdateEvents(), nil)
client := request.NewStdHTTP()
validator, err := response.NewNordValidator()
if err != nil {
Expand Down
16 changes: 15 additions & 1 deletion daemon/data_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import (
"github.com/NordSecurity/nordvpn-linux/config"
"github.com/NordSecurity/nordvpn-linux/core"
"github.com/NordSecurity/nordvpn-linux/daemon/events"
"github.com/NordSecurity/nordvpn-linux/daemon/models"
"github.com/NordSecurity/nordvpn-linux/daemon/pb"
"github.com/NordSecurity/nordvpn-linux/internal"
meshnet "github.com/NordSecurity/nordvpn-linux/meshnet/pb"

"github.com/coreos/go-semver/semver"
mapset "github.com/deckarep/golang-set/v2"
Expand All @@ -32,19 +34,23 @@ type DataManager struct {
versionData VersionData
dataUpdateEvents *events.DataUpdateEvents
mu sync.Mutex
meshnetPeers *models.CachedValue[*meshnet.GetPeersResponse]
}

func NewDataManager(insightsFilePath,
serversFilePath,
countryFilePath,
versionFilePath string,
dataUpdateEvents *events.DataUpdateEvents) *DataManager {
dataUpdateEvents *events.DataUpdateEvents,
meshnetPeers *models.CachedValue[*meshnet.GetPeersResponse],
) *DataManager {
return &DataManager{
countryData: CountryData{filePath: countryFilePath},
insightsData: InsightsData{filePath: insightsFilePath},
serversData: ServersData{filePath: serversFilePath},
versionData: VersionData{filePath: versionFilePath},
dataUpdateEvents: dataUpdateEvents,
meshnetPeers: meshnetPeers,
}
}

Expand Down Expand Up @@ -380,3 +386,11 @@ func (dm *DataManager) CountryCodeToCountryName(code string) string {

return ""
}

func (dm *DataManager) GetMeshnetPeers() (*meshnet.GetPeersResponse, error) {
return dm.meshnetPeers.Get()
}

func (dm *DataManager) SetMeshnetPeers(peers *meshnet.GetPeersResponse, err error) bool {
return dm.meshnetPeers.Set(peers, err)
}
58 changes: 58 additions & 0 deletions daemon/models/cached_value.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package models

import (
"sync"
"time"
)

type CachedValue[T comparable] struct {
value T
latestError error
cachedDate time.Time
validity time.Duration
updaterFn func(self *CachedValue[T])
mu *sync.Mutex
}

func NewCachedValue[T comparable](
value T,
err error,
cachedDate time.Time,
validity time.Duration,
updaterFn func(*CachedValue[T]),
) *CachedValue[T] {
return &CachedValue[T]{
value: value,
latestError: err,
cachedDate: cachedDate,
validity: validity,
updaterFn: updaterFn,
mu: &sync.Mutex{},
}
}

func (c *CachedValue[T]) Get() (T, error) {
shouldUpdate := false
c.mu.Lock()
defer func() {
c.mu.Unlock()
if shouldUpdate {
c.updaterFn(c)
}
}()
shouldUpdate = c.cachedDate.Add(c.validity).Before(time.Now())
return c.value, c.latestError
}

func (c *CachedValue[T]) Set(value T, err error) bool {

c.mu.Lock()
defer c.mu.Unlock()

c.cachedDate = time.Now()
updated := c.value == value
c.value = value
c.latestError = err

return updated
}
76 changes: 76 additions & 0 deletions daemon/models/cached_value_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package models_test

import (
"errors"
"testing"
"time"

"github.com/NordSecurity/nordvpn-linux/daemon/models"
"github.com/NordSecurity/nordvpn-linux/test/category"
"github.com/stretchr/testify/assert"
)

func TestCachedValue(t *testing.T) {
category.Set(t, category.Unit)

tests := []struct {
name string
initialValue int
err error
validity time.Duration
updatedValue int
shouldUpdate bool
}{
{
name: "works for initial value and update not called",
initialValue: 1,
err: nil,
validity: 1 * time.Minute,
shouldUpdate: false,
},
{
name: "update callback function works",
initialValue: 2,
err: nil,
validity: 0 * time.Second,
shouldUpdate: true,
updatedValue: 10,
},
{
name: "initial value is error and update fixes it",
initialValue: 0,
err: errors.New("failed"),
validity: 0 * time.Second,
shouldUpdate: true,
updatedValue: 10,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
updateCalled := false
item := models.NewCachedValue(
test.initialValue,
test.err, time.Now(),
test.validity,
func(self *models.CachedValue[int]) {
updateCalled = true
self.Set(test.updatedValue, nil)
},
)
value, err := item.Get()

if err != nil {
assert.Equal(t, value, test.initialValue)
}
assert.ErrorIs(t, err, test.err)

assert.Equal(t, updateCalled, test.shouldUpdate)
if test.shouldUpdate {
value, err := item.Get()
assert.Equal(t, value, test.updatedValue)
assert.NoError(t, err)
}
})
}
}
7 changes: 7 additions & 0 deletions internal/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os/user"
"path/filepath"
"strconv"
"time"
)

const (
Expand Down Expand Up @@ -142,6 +143,12 @@ var (
FileshareBinaryPath = filepath.Join(AppDataPathStatic, Fileshare)

NorduserdBinaryPath = filepath.Join(AppDataPathStatic, Norduserd)

// How often will meshnet peers get updated from the API
MeshnetPeersUpdateInterval = 30 * time.Second

// The tag of the update peer job
MeshnetPeersJobTag = "meshnet-peers"
)

func GetSupportedIPTables() []string {
Expand Down
16 changes: 16 additions & 0 deletions meshnet/internal/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package internal

import "github.com/NordSecurity/nordvpn-linux/meshnet/pb"

type MeshnetChecker interface {
IsMeshnetOn() bool
}

type MeshnetFetcher interface {
FetchMeshnetPeers() *pb.GetPeersResponse
}

type MeshnetDataManager interface {
GetMeshnetPeers() (*pb.GetPeersResponse, error)
SetMeshnetPeers(peers *pb.GetPeersResponse, err error) bool
}
45 changes: 38 additions & 7 deletions meshnet/jobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@ import (

"github.com/go-co-op/gocron/v2"

"github.com/NordSecurity/nordvpn-linux/events"
"github.com/NordSecurity/nordvpn-linux/internal"
meshInternal "github.com/NordSecurity/nordvpn-linux/meshnet/internal"
"github.com/NordSecurity/nordvpn-linux/meshnet/jobs"
)

func (s *Server) StartJobs() {
const ()

func (s *Server) StartJobs(
meshnetPublisher events.PublishSubcriber[bool],
) {
if _, err := s.scheduler.NewJob(gocron.DurationJob(2*time.Hour), gocron.NewTask(JobRefreshMeshnet(s)), gocron.WithName("job refresh meshnet")); err != nil {
log.Println(internal.WarningPrefix, "job refresh meshnet schedule error:", err)
}
Expand All @@ -28,6 +35,34 @@ func (s *Server) StartJobs() {
log.Println(internal.WarningPrefix, job.Name(), "first run error:", err)
}
}

// monitors the meshnet status and starts/stops the peers refreshing job
meshnetPublisher.Subscribe(func(enabled bool) error {
// TODO: check what happens if meshnet is started
if enabled {
job, err := s.scheduler.NewJob(
gocron.DurationRandomJob(internal.MeshnetPeersUpdateInterval/2, internal.MeshnetPeersUpdateInterval),
gocron.NewTask(jobs.JobUpdateMeshnetPeers(s, s, s.dataManager, s.subjectPeerUpdate)),
gocron.WithName("refresh peers"),
gocron.WithTags(internal.MeshnetPeersJobTag),
)
if err != nil {
log.Println(internal.ErrorPrefix, "failed to schedule peers refresh job", err)
return err
}

log.Println(internal.DebugPrefix, "peers refresh job scheduled")

if err := job.RunNow(); err != nil {
log.Println(internal.ErrorPrefix, "failed to run peers refresh job", err)
return err
}
} else {
s.scheduler.RemoveByTags(internal.MeshnetPeersJobTag)
log.Println(internal.DebugPrefix, "stop peer refresh job")
}
return nil
})
}

func JobRefreshMeshnet(s *Server) func() error {
Expand All @@ -49,7 +84,7 @@ func JobMonitorFileshareProcess(s *Server) func() error {
}

func (j *monitorFileshareProcessJob) run() error {
if !j.meshChecker.isMeshOn() {
if !j.meshChecker.IsMeshnetOn() {
if j.isFileshareAllowed {
if err := j.rulesController.ForbidFileshare(); err == nil {
j.isFileshareAllowed = false
Expand Down Expand Up @@ -77,15 +112,11 @@ func (defaultProcessChecker) isFileshareRunning() bool {

type monitorFileshareProcessJob struct {
isFileshareAllowed bool
meshChecker meshChecker
meshChecker meshInternal.MeshnetChecker
rulesController rulesController
processChecker processChecker
}

type meshChecker interface {
isMeshOn() bool
}

type rulesController interface {
ForbidFileshare() error
PermitFileshare() error
Expand Down
Loading

0 comments on commit 0444cee

Please sign in to comment.