Skip to content

Commit

Permalink
Hide quench technology based on compile flag and remote config
Browse files Browse the repository at this point in the history
  • Loading branch information
bartoszWojciechO committed Dec 11, 2024
1 parent 7ab6dec commit b558563
Show file tree
Hide file tree
Showing 31 changed files with 531 additions and 51 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
path = third-party/moose-worker
url = ../../../../moose/moose-events.git
[submodule "third-party/libquench-go"]
url = ../../libquench-go.git
path = third-party/libquench-go
url = ../../libquench-go
2 changes: 2 additions & 0 deletions ci/generate_protobuf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ protoc --go_opt=module=github.com/NordSecurity/nordvpn-linux --go_out=. protobuf
protoc --go_opt=module=github.com/NordSecurity/nordvpn-linux --go_out=. protobuf/fileshare/fileshare.proto -I protobuf/fileshare
protoc --go_opt=module=github.com/NordSecurity/nordvpn-linux --go_out=. protobuf/snapconf/snapconf.proto -I protobuf/snapconf
protoc --go_opt=module=github.com/NordSecurity/nordvpn-linux --go_out=. protobuf/norduser/norduser.proto -I protobuf/norduser
protoc --go_opt=module=github.com/NordSecurity/nordvpn-linux --go_out=. protobuf/daemon/state.proto -I protobuf/daemon
protoc --go_opt=module=github.com/NordSecurity/nordvpn-linux --go_out=. protobuf/daemon/quench_hidden.proto -I protobuf/daemon

protoc --go_grpc_opt=module=github.com/NordSecurity/nordvpn-linux --go_grpc_out=. protobuf/daemon/service.proto -I protobuf/daemon
protoc --go_grpc_opt=module=github.com/NordSecurity/nordvpn-linux --go_grpc_out=. protobuf/meshnet/service.proto -I protobuf/meshnet
Expand Down
2 changes: 1 addition & 1 deletion ci/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ if [ "${1:-""}" = "full" ]; then

excluded_packages="thisshouldneverexist"
excluded_categories="root,link"
tags="internal,moose"
tags="internal,quench,moose"
fi

# Execute tests in all the packages except the excluded ones
Expand Down
8 changes: 7 additions & 1 deletion cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ func NewApp(version, environment, hash, salt string,
cli.HelpFlag.(*cli.BoolFlag).Usage = "Show help"
cli.VersionFlag.(*cli.BoolFlag).Usage = "Print the version"

setTechnologyDescription := fmt.Sprintf(SetTechnologyDescription, SupportedValuesQuench)
quenchEnabled, err := cmd.client.IsQuenchEnabled(context.Background(), &pb.Empty{})
if err != nil || !quenchEnabled.Enabled {
setTechnologyDescription = fmt.Sprintf(SetTechnologyDescription, SupportedValuesNoQuench)
}

setCommand := cli.Command{
Name: "set",
Aliases: []string{"s"},
Expand Down Expand Up @@ -285,7 +291,7 @@ func NewApp(version, environment, hash, salt string,
Action: cmd.SetTechnology,
BashComplete: cmd.SetTechnologyAutoComplete,
ArgsUsage: SetTechnologyArgsUsageText,
Description: SetTechnologyDescription,
Description: setTechnologyDescription,
},
{
Name: "meshnet",
Expand Down
7 changes: 6 additions & 1 deletion cli/cli_set_technology.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ const (
SetTechnologyUsageText = "Sets the technology"
SetTechnologyArgsUsageText = `<technology>`
SetTechnologyDescription = `Use this command to set the technology.
Supported values for <technology>: OPENVPN or NORDLYNX.
Supported values for <technology>: %s.
Example: 'nordvpn set technology OPENVPN'`

SupportedValuesNoQuench = "OPENVPN or NORDLYNX"
SupportedValuesQuench = "OPENVPN, NORDLYNX or QUENCH"
)

func (c *cmd) SetTechnology(ctx *cli.Context) error {
Expand Down Expand Up @@ -69,6 +72,8 @@ func (c *cmd) SetTechnology(ctx *cli.Context) error {
// must be right before CodeSuccess
color.Yellow(SetAutoConnectForceOff)
fallthrough
case internal.CodeFeatureHidden:
return formatError(argsParseError(ctx))
case internal.CodeSuccess:
flag, _ := strconv.ParseBool(resp.Data[0])
color.Green(fmt.Sprintf(MsgSetSuccess, "Technology", strings.Join(resp.Data[1:], " ")))
Expand Down
12 changes: 12 additions & 0 deletions cmd/daemon/firebase_cfg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//go:build firebase

package main

import (
"github.com/NordSecurity/nordvpn-linux/config"
"github.com/NordSecurity/nordvpn-linux/config/remote"
)

func getRemoteConfigGetter(cm config.Manager) remote.RemoteConfigGetter {
return remote.NewRConfig(remote.UpdatePeriod, remote.NewFirebaseService(FirebaseToken), cm)
}
21 changes: 19 additions & 2 deletions cmd/daemon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import (
"github.com/NordSecurity/nordvpn-linux/events/meshunsetter"
"github.com/NordSecurity/nordvpn-linux/events/refresher"
"github.com/NordSecurity/nordvpn-linux/events/subs"
"github.com/NordSecurity/nordvpn-linux/features"
grpcmiddleware "github.com/NordSecurity/nordvpn-linux/grpc_middleware"
"github.com/NordSecurity/nordvpn-linux/internal"
"github.com/NordSecurity/nordvpn-linux/ipv6"
Expand Down Expand Up @@ -143,6 +144,22 @@ func main() {
}
}

rcConfig := getRemoteConfigGetter(fsystem)
quenchEnabled, err := rcConfig.GetQuenchEnabled(Version)
if err != nil {
log.Println("failed to determine if quench is enabled:", err)
}

// fallback to Nordlynx if quench was enabled in previous installation and is disabled now
if (!features.QuenchEnabled || !quenchEnabled) && cfg.Technology == config.Technology_QUENCH {
err := fsystem.SaveWith(func(c config.Config) config.Config {
c.Technology = config.Technology_NORDLYNX
return c
})

log.Println(internal.ErrorPrefix, "failed to fallback to Nordlynx tech:", err)
}

// Events

daemonEvents := daemonevents.NewEventsEmpty()
Expand Down Expand Up @@ -190,7 +207,6 @@ func main() {

// API
var validator response.Validator
var err error
if !internal.IsProdEnv(Environment) && os.Getenv(EnvIgnoreHeaderValidation) == "1" {
validator = response.NoopValidator{}
} else {
Expand Down Expand Up @@ -292,7 +308,7 @@ func main() {
daemonEvents.Service.UiItemsClick.Publish(events.UiItemsAction{ItemName: "first_open", ItemType: "button", ItemValue: "first_open", FormReference: "daemon"})
}

vpnLibConfigGetter := vpnLibConfigGetterImplementation(fsystem)
vpnLibConfigGetter := vpnLibConfigGetterImplementation(fsystem, rcConfig)

internalVpnEvents := vpn.NewInternalVPNEvents()

Expand Down Expand Up @@ -463,6 +479,7 @@ func main() {
meshAPIex,
statePublisher,
sharedContext,
rcConfig,
)
meshService := meshnet.NewServer(
authChecker,
Expand Down
25 changes: 25 additions & 0 deletions cmd/daemon/no_firebase_cfg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//go:build !firebase

package main

import (
"fmt"

"github.com/NordSecurity/nordvpn-linux/config"
"github.com/NordSecurity/nordvpn-linux/config/remote"
)

type RemoteConfigGetterStub struct {
}

func (r RemoteConfigGetterStub) GetTelioConfig(version string) (string, error) {
return "", fmt.Errorf("firebase config was not compiled into the app")
}

func (r RemoteConfigGetterStub) GetQuenchEnabled(version string) (bool, error) {
return false, nil
}

func getRemoteConfigGetter(_ config.Manager) remote.RemoteConfigGetter {
return RemoteConfigGetterStub{}
}
2 changes: 1 addition & 1 deletion cmd/daemon/vpn_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func getVpnFactory(eventsDbPath string, fwmark uint32, envIsDev bool,
case config.Technology_OPENVPN:
return openvpn.New(fwmark, eventsPublisher), nil
case config.Technology_QUENCH:
return getQuenchVPN(fwmark), nil
return getQuenchVPN(fwmark)
case config.Technology_UNKNOWN_TECHNOLOGY:
fallthrough
default:
Expand Down
10 changes: 7 additions & 3 deletions cmd/daemon/vpn_no_quench.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

package main

import "github.com/NordSecurity/nordvpn-linux/daemon/vpn"
import (
"fmt"

func getQuenchVPN(fwmark uint32) vpn.VPN {
return nil
"github.com/NordSecurity/nordvpn-linux/daemon/vpn"
)

func getQuenchVPN(fwmark uint32) (vpn.VPN, error) {
return nil, fmt.Errorf("quench is not enabled")
}
4 changes: 2 additions & 2 deletions cmd/daemon/vpn_quench.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ package main

import "github.com/NordSecurity/nordvpn-linux/daemon/vpn/quench"

func getQuenchVPN(fwmark uint32) *quench.Quench {
return quench.New(fwmark)
func getQuenchVPN(fwmark uint32) (*quench.Quench, error) {
return quench.New(fwmark), nil
}
3 changes: 2 additions & 1 deletion cmd/daemon/vpnconfig_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"

"github.com/NordSecurity/nordvpn-linux/config"
"github.com/NordSecurity/nordvpn-linux/config/remote"
"github.com/NordSecurity/nordvpn-linux/daemon/vpn"
)

Expand All @@ -15,6 +16,6 @@ func (noopConfigGetter) GetConfig(string) (string, error) {
return "", fmt.Errorf("config is not available")
}

func vpnLibConfigGetterImplementation(_ config.Manager) vpn.LibConfigGetter {
func vpnLibConfigGetterImplementation(_ config.Manager, _ remote.RemoteConfigGetter) vpn.LibConfigGetter {
return noopConfigGetter{}
}
3 changes: 1 addition & 2 deletions cmd/daemon/vpnconfig_telio.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/NordSecurity/nordvpn-linux/daemon/vpn/nordlynx/libtelio"
)

func vpnLibConfigGetterImplementation(cm config.Manager) vpn.LibConfigGetter {
rcConfig := remote.NewRConfig(remote.UpdatePeriod, remote.NewFirebaseService(FirebaseToken), cm)
func vpnLibConfigGetterImplementation(cm config.Manager, rcConfig remote.RemoteConfigGetter) vpn.LibConfigGetter {
return libtelio.NewTelioConfig(rcConfig)
}
39 changes: 35 additions & 4 deletions config/remote/firebase.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
"log"
"net/http"
"sort"
"strconv"
"strings"
"sync"
"time"

"github.com/NordSecurity/nordvpn-linux/config"
Expand Down Expand Up @@ -44,6 +46,7 @@ type ServiceAccount struct {
}

type RConfig struct {
mu sync.Mutex
updatePeriod time.Duration
config *firebaseremoteconfig.RemoteConfig
remoteService RemoteConfigService
Expand Down Expand Up @@ -122,7 +125,7 @@ func (rc *RConfig) fetchAndSaveRemoteConfig() (remoteConfig []byte, err error) {
}

// GetValue provides value of requested key from remote config
func (rc *RConfig) GetValue(cfgKey string) (string, error) {
func (rc *RConfig) getValue(cfgKey string) (string, error) {
err := rc.fetchRemoteConfigIfTime()
if err != nil {
log.Println(internal.WarningPrefix, "using cached config:", err)
Expand Down Expand Up @@ -159,9 +162,37 @@ func stringToSemVersion(stringVersion, prefix string) (*semver.Version, error) {
return semver.NewVersion(stringVersion)
}

func (rc *RConfig) GetQuenchEnabled(stringVersion string) (bool, error) {
rc.mu.Lock()
defer rc.mu.Unlock()

enabledStr, err := rc.getRemoteConfigByVersion(RcQuenchConfigFieldPrefix, stringVersion)
if err != nil {
return false, fmt.Errorf("fetching the telio config: %w", err)
}

enabled, err := strconv.ParseBool(enabledStr)
if err != nil {
return false, fmt.Errorf("parsing firebase parameter: %w", err)
}

return enabled, nil
}

// GetTelioConfig try to find remote config field for app version
// and load json block from that field
func (rc *RConfig) GetTelioConfig(stringVersion string) (string, error) {
rc.mu.Lock()
defer rc.mu.Unlock()

cfg, err := rc.getRemoteConfigByVersion(RcTelioConfigFieldPrefix, stringVersion)
if err != nil {
return "", fmt.Errorf("fetching the telio config: %w", err)
}
return cfg, nil
}

func (rc *RConfig) getRemoteConfigByVersion(prefix string, stringVersion string) (string, error) {
if err := rc.fetchRemoteConfigIfTime(); err != nil {
if len(rc.config.Parameters) == 0 {
return "", err
Expand All @@ -177,8 +208,8 @@ func (rc *RConfig) GetTelioConfig(stringVersion string) (string, error) {
// build descending ordered version list
orderedFields := []*fieldVersion{}
for key := range rc.config.Parameters {
if strings.HasPrefix(key, RcTelioConfigFieldPrefix) {
ver, err := stringToSemVersion(key, RcTelioConfigFieldPrefix)
if strings.HasPrefix(key, prefix) {
ver, err := stringToSemVersion(key, prefix)
if err != nil {
log.Println(err)
continue
Expand All @@ -194,7 +225,7 @@ func (rc *RConfig) GetTelioConfig(stringVersion string) (string, error) {
}
log.Println("remote config version field:", versionField)

jsonString, err := rc.GetValue(versionField)
jsonString, err := rc.getValue(versionField)
if err != nil {
return "", err
}
Expand Down
12 changes: 6 additions & 6 deletions config/remote/firebase_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestRemoteConfig_GetValue(t *testing.T) {
category.Set(t, category.File)
initConfig()
rc := NewRConfig(time.Duration(0), &remoteServiceMock{}, getConfigManager())
welcomeMessage, err := rc.GetValue("welcome_message")
welcomeMessage, err := rc.getValue("welcome_message")
assert.NoError(t, err)
assert.Equal(t, "hola", welcomeMessage)
}
Expand All @@ -48,10 +48,10 @@ func TestRemoteConfig_Caching(t *testing.T) {
initConfig()
rsm := remoteServiceMock{}
rc := NewRConfig(time.Hour*24, &rsm, getConfigManager())
_, err := rc.GetValue("welcome_message")
_, err := rc.getValue("welcome_message")
assert.NoError(t, err)
assert.Equal(t, 1, rsm.fetchCount)
welcomeMessage, err := rc.GetValue("welcome_message")
welcomeMessage, err := rc.getValue("welcome_message")
assert.NoError(t, err)
assert.Equal(t, "hola", welcomeMessage)
assert.Equal(t, 1, rsm.fetchCount)
Expand All @@ -62,10 +62,10 @@ func TestRemoteConfig_NoCaching(t *testing.T) {
initConfig()
rsm := remoteServiceMock{}
rc := NewRConfig(0, &rsm, getConfigManager())
_, err := rc.GetValue("welcome_message")
_, err := rc.getValue("welcome_message")
assert.NoError(t, err)
assert.Equal(t, 1, rsm.fetchCount)
welcomeMessage, err := rc.GetValue("welcome_message")
welcomeMessage, err := rc.getValue("welcome_message")
assert.NoError(t, err)
assert.Equal(t, "hola", welcomeMessage)
assert.Equal(t, 2, rsm.fetchCount)
Expand Down Expand Up @@ -361,7 +361,7 @@ func TestRemoteConfig_GetCachedData(t *testing.T) {
cm.Cfg.RCLastUpdate = time.Now()
cm.Cfg.RemoteConfig = test.cachedValue

value, err := rc.GetValue("welcome_message")
value, err := rc.getValue("welcome_message")

assert.Equal(t, test.expectedValue, value)
if test.expectedValue == "" {
Expand Down
5 changes: 3 additions & 2 deletions config/remote/remote_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ const (
// and digits, underscores) app will try to find field corresponding to
// app's version, but if exact match is not found then first older version
// is chosen, if that one is also not available, then use local defaults.
RcTelioConfigFieldPrefix = "telio_config_"
RcTelioConfigFieldPrefix = "telio_config_"
RcQuenchConfigFieldPrefix = "quench_enabled_"
)

// RemoteConfigGetter get values from remote config
type RemoteConfigGetter interface {
GetValue(key string) (string, error)
GetTelioConfig(version string) (string, error)
GetQuenchEnabled(version string) (bool, error)
}
Loading

0 comments on commit b558563

Please sign in to comment.