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

Release 3.20.0 #726

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion cli/cli_set_postquantum_vpn.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (c *cmd) SetPostquantumVpn(ctx *cli.Context) error {
case internal.CodePqAndMeshnetSimultaneously:
return formatError(errors.New(SetPqAndMeshnet))
case internal.CodePqWithoutNordlynx:
return formatError(errors.New(SetPqUnavailable))
return formatError(fmt.Errorf(SetPqUnavailable, resp.Data[0]))
case internal.CodeSuccess:
color.Green(fmt.Sprintf(MsgSetSuccess, "Post-quantum VPN", nstrings.GetBoolLabel(flag)))
flag, _ := strconv.ParseBool(resp.Data[0])
Expand Down
2 changes: 1 addition & 1 deletion cli/cli_set_technology.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (c *cmd) SetTechnology(ctx *cli.Context) error {
case internal.CodeDependencyError:
return formatError(fmt.Errorf(SetTechnologyDepsError, internal.StringsToInterfaces(resp.Data)...))
case internal.CodePqWithoutNordlynx:
return formatError(fmt.Errorf(SetTechnologyDisablePQ))
return formatError(fmt.Errorf(SetTechnologyDisablePQ, resp.Data[0]))
case internal.CodeNothingToDo:
color.Yellow(fmt.Sprintf(MsgAlreadySet, "Technology", strings.Join(resp.Data, " ")))
case internal.CodeSuccessWithoutAC:
Expand Down
4 changes: 2 additions & 2 deletions cli/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,8 @@ Provide a [transfer_id] argument to list files in the specified transfer.`

MsgShowListOfServers = "Shows a list of %s where servers are available.\n\nLocations marked with a different color in the list are virtual. Virtual location servers let you connect to more places worldwide. They run on dedicated physical servers, which are placed outside the intended location but configured to use its IP address."

SetPqUnavailable = "The post-quantum VPN is not compatible with OpenVPN. Switch to NordLynx to use post-quantum VPN capabilities."
SetTechnologyDisablePQ = "This setting is not compatible with the post-quantum VPN. To use OpenVPN, disable the post-quantum VPN first."
SetPqUnavailable = "The post-quantum VPN is not compatible with %s. Switch to NordLynx to use post-quantum VPN capabilities."
SetTechnologyDisablePQ = "This setting is not compatible with the post-quantum VPN. To use %s, disable the post-quantum VPN first."
SetPqAndMeshnet = "The post-quantum VPN and Meshnet can't run at the same time. Please disable one feature to use the other."
SetPqAndMeshnetServer = "Meshnet isn’t compatible with post-quantum servers. Reconnect to the VPN to fully disable post-quantum protection and try again."
SetPqUsageText = "Enables or disables the post-quantum VPN. When enabled, your connection uses cutting-edge cryptography designed to resist quantum computer attacks.\nNote: The feature is not compatible with a dedicated IP, Meshnet, and OpenVPN."
Expand Down
17 changes: 17 additions & 0 deletions config/technology.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package config

// TechNameToUpperCamelCase returns technology name as an UpperCamelCase string
func TechNameToUpperCamelCase(tech Technology) string {
switch tech {
case Technology_NORDLYNX:
return "NordLynx"
case Technology_OPENVPN:
return "OpenVPN"
case Technology_NORDWHISPER:
return "NordWhisper"
case Technology_UNKNOWN_TECHNOLOGY:
fallthrough
default:
return ""
}
}
3 changes: 3 additions & 0 deletions contrib/changelog/prod/3.20.0_1735571639.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
* We're excited to introduce NordWhisper, our latest VPN protocol! If you've ever been on a network that restricts the use of VPNs, this is the solution you've been waiting for. Unlike traditional VPN protocols, it doesn't have distinct traffic signatures or behaviors that reveal it as a VPN connection. Keep in mind that this protocol may work slower than other available protocols. We recommend using it only on networks where regular VPNs are blocked. To switch your connection protocol to NordWhisper, run the command nordvpn set technology nordwhisper.
* This update brings significant security improvements. Updating is strongly encouraged.
* A few fixes here and there to ensure your VPN app works as expected. Update the app now to make sure you don't miss a thing.
36 changes: 29 additions & 7 deletions daemon/jobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,28 @@ func connectErrorCheck(err error) bool {
return err == nil
}

func (r *RPC) fallbackTechnology(targetTechnology config.Technology) error {
log.Println(internal.DebugPrefix,
"technology was configured to NordWhisper, but NordWhisper was disabled, switching to",
targetTechnology.String())
v, err := r.factory(targetTechnology)
if err != nil {
return fmt.Errorf("failed to build VPN instance: %s", err)
}

err = r.cm.SaveWith(func(c config.Config) config.Config {
c.Technology = targetTechnology
c.AutoConnectData.Protocol = config.Protocol_UDP
return c
})
if err != nil {
return fmt.Errorf("failed to fallback to %s tech: %s", targetTechnology.String(), err)
}

r.netw.SetVPN(v)
return nil
}

// StartAutoConnect connect to VPN server if autoconnect is enabled
func (r *RPC) StartAutoConnect(timeoutFn GetTimeoutFunc) error {
tries := 1
Expand All @@ -234,13 +256,13 @@ func (r *RPC) StartAutoConnect(timeoutFn GetTimeoutFunc) error {
}

if cfg.Technology == config.Technology_NORDWHISPER && !r.isNordWhisperEnabled() {
err := r.cm.SaveWith(func(c config.Config) config.Config {
c.Technology = config.Technology_NORDLYNX
c.AutoConnectData.Protocol = config.Protocol_UDP
return c
})
if err != nil {
log.Println(internal.ErrorPrefix, "failed to fallback to Nordlynx tech:", err)
log.Println(internal.DebugPrefix,
"technology was configured to NordWhisper, but NordWhisper was disabled, switching to NordLynx")
if err := r.fallbackTechnology(config.Technology_NORDLYNX); err != nil {
log.Println(internal.ErrorPrefix, "failed to fall back to NordLynx technology, will try OpenVPN")
if err := r.fallbackTechnology(config.Technology_OPENVPN); err != nil {
return fmt.Errorf("falling back to OpenVPN technology: %s", err)
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions daemon/jobs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func (failingLoginChecker) IsMFAEnabled() (bool, error) { return false, nil }
func (failingLoginChecker) IsVPNExpired() (bool, error) {
return true, errors.New("IsVPNExpired error")
}

func (failingLoginChecker) GetDedicatedIPServices() ([]auth.DedicatedIPService, error) {
return nil, fmt.Errorf("Not implemented")
}
Expand Down Expand Up @@ -128,6 +129,10 @@ func (n *meshNetworker) AllowFileshare(address meshnet.UniqueAddress) error {
return nil
}

func (n *meshNetworker) PermitFileshare() error {
return nil
}

func (n *meshNetworker) AllowIncoming(address meshnet.UniqueAddress, lanAllowed bool) error {
n.allowedIncoming = append(n.allowedIncoming, address)
return nil
Expand All @@ -138,6 +143,10 @@ func (n *meshNetworker) BlockIncoming(address meshnet.UniqueAddress) error {
return nil
}

func (n *meshNetworker) ForbidFileshare() error {
return nil
}

func (n *meshNetworker) BlockFileshare(address meshnet.UniqueAddress) error {
n.blockedFileshare = append(n.blockedFileshare, address)
return nil
Expand Down
3 changes: 2 additions & 1 deletion daemon/rpc_set_postquantum.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ func (r *RPC) SetPostQuantum(ctx context.Context, in *pb.SetGenericRequest) (*pb
}

if cfg.Technology != config.Technology_NORDLYNX {
return &pb.Payload{Type: internal.CodePqWithoutNordlynx}, nil
return &pb.Payload{Type: internal.CodePqWithoutNordlynx,
Data: []string{config.TechNameToUpperCamelCase(cfg.Technology)}}, nil
}

if err := r.cm.SaveWith(func(c config.Config) config.Config {
Expand Down
35 changes: 32 additions & 3 deletions daemon/rpc_set_postquantum_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,19 @@ func TestSetPostquantumVpn(t *testing.T) {
Type: internal.CodePqAndMeshnetSimultaneously,
}

conflictTechPayload := pb.Payload{
conflictUnknownTechPayload := pb.Payload{
Type: internal.CodePqWithoutNordlynx,
Data: []string{""},
}

conflictOpenVPNTechPayload := pb.Payload{
Type: internal.CodePqWithoutNordlynx,
Data: []string{"OpenVPN"},
}

conflictNordWhisperPayload := pb.Payload{
Type: internal.CodePqWithoutNordlynx,
Data: []string{"NordWhisper"},
}

tests := []struct {
Expand All @@ -78,7 +89,7 @@ func TestSetPostquantumVpn(t *testing.T) {
meshnet: false,
vpnActive: false,
tech: config.Technology_UNKNOWN_TECHNOLOGY,
payload: &conflictTechPayload,
payload: &conflictUnknownTechPayload,
eventPublished: false,
},
{
Expand All @@ -96,7 +107,7 @@ func TestSetPostquantumVpn(t *testing.T) {
meshnet: false,
vpnActive: false,
tech: config.Technology_UNKNOWN_TECHNOLOGY,
payload: &conflictTechPayload,
payload: &conflictUnknownTechPayload,
eventPublished: false,
},
{
Expand Down Expand Up @@ -125,6 +136,24 @@ func TestSetPostquantumVpn(t *testing.T) {
payload: &successWithVPNPayload,
eventPublished: true,
},
{
testName: "pq on mesh is off tech openvpn",
pq: true,
meshnet: false,
vpnActive: false,
tech: config.Technology_OPENVPN,
payload: &conflictOpenVPNTechPayload,
eventPublished: false,
},
{
testName: "pq on mesh is off tech nordwhisper",
pq: true,
meshnet: false,
vpnActive: false,
tech: config.Technology_NORDWHISPER,
payload: &conflictNordWhisperPayload,
eventPublished: false,
},
}

for _, test := range tests {
Expand Down
2 changes: 1 addition & 1 deletion daemon/rpc_set_protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func (r *RPC) SetProtocol(ctx context.Context, in *pb.SetProtocolRequest) (*pb.S
}, nil
}

if cfg.Technology == config.Technology_NORDLYNX {
if cfg.Technology != config.Technology_OPENVPN {
return &pb.SetProtocolResponse{
Response: &pb.SetProtocolResponse_SetProtocolStatus{
SetProtocolStatus: pb.SetProtocolStatus_INVALID_TECHNOLOGY,
Expand Down
13 changes: 12 additions & 1 deletion daemon/rpc_set_protocol_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func TestSetProtocol_Error(t *testing.T) {
},
},
{
name: "set protocol invalid technology",
name: "set protocol invalid technology NordLynx",
currentTechnology: config.Technology_NORDLYNX,
currentProtocol: config.Protocol_UDP,
desiredProtocol: config.Protocol_TCP,
Expand All @@ -139,6 +139,17 @@ func TestSetProtocol_Error(t *testing.T) {
},
},
},
{
name: "set protocol invalid technology NordWhisper",
currentTechnology: config.Technology_NORDWHISPER,
currentProtocol: config.Protocol_Webtunnel,
desiredProtocol: config.Protocol_TCP,
expectedResponse: &pb.SetProtocolResponse{
Response: &pb.SetProtocolResponse_SetProtocolStatus{
SetProtocolStatus: pb.SetProtocolStatus_INVALID_TECHNOLOGY,
},
},
},
{
name: "set protocol config error",
currentTechnology: config.Technology_OPENVPN,
Expand Down
16 changes: 10 additions & 6 deletions daemon/rpc_set_technology.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (r *RPC) SetTechnology(ctx context.Context, in *pb.SetTechnologyRequest) (*
if cfg.Technology == in.GetTechnology() {
return &pb.Payload{
Type: internal.CodeNothingToDo,
Data: []string{in.GetTechnology().String()},
Data: []string{config.TechNameToUpperCamelCase(in.GetTechnology())},
}, nil
}

Expand All @@ -67,17 +67,20 @@ func (r *RPC) SetTechnology(ctx context.Context, in *pb.SetTechnologyRequest) (*

protocol := cfg.AutoConnectData.Protocol
obfuscate := cfg.AutoConnectData.Obfuscate
if in.GetTechnology() == config.Technology_NORDLYNX {
protocol = config.Protocol_UDP
if in.GetTechnology() != config.Technology_OPENVPN {
obfuscate = false
} else if in.GetTechnology() == config.Technology_NORDWHISPER {
}

if in.GetTechnology() == config.Technology_NORDWHISPER {
protocol = config.Protocol_Webtunnel
obfuscate = false
} else {
protocol = config.Protocol_UDP
}

if in.GetTechnology() != config.Technology_NORDLYNX && cfg.AutoConnectData.PostquantumVpn {
return &pb.Payload{
Type: internal.CodePqWithoutNordlynx,
Data: []string{config.TechNameToUpperCamelCase(in.GetTechnology())},
}, nil
}

Expand All @@ -98,6 +101,7 @@ func (r *RPC) SetTechnology(ctx context.Context, in *pb.SetTechnologyRequest) (*

r.events.Settings.Technology.Publish(in.GetTechnology())

payload.Data = []string{strconv.FormatBool(r.netw.IsVPNActive()), in.GetTechnology().String()}
payload.Data = []string{strconv.FormatBool(r.netw.IsVPNActive()),
config.TechNameToUpperCamelCase(in.GetTechnology())}
return payload, nil
}
67 changes: 59 additions & 8 deletions daemon/rpc_set_technology_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,91 @@ import (
"testing"

"github.com/NordSecurity/nordvpn-linux/config"
"github.com/NordSecurity/nordvpn-linux/daemon/events"
"github.com/NordSecurity/nordvpn-linux/daemon/pb"
"github.com/NordSecurity/nordvpn-linux/daemon/vpn"
"github.com/NordSecurity/nordvpn-linux/internal"
"github.com/NordSecurity/nordvpn-linux/test/category"
"github.com/NordSecurity/nordvpn-linux/test/mock"
"github.com/NordSecurity/nordvpn-linux/test/mock/networker"
"github.com/stretchr/testify/assert"
)

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

remoteConfigGetter := mock.NewRemoteConfigMock()
r := RPC{
remoteConfigGetter: remoteConfigGetter,
}

tests := []struct {
name string
nordwhisperEnabled bool
nordWhisperEnabledErr error
currentTech config.Technology
currentProtocol config.Protocol
targetTech config.Technology
expectedTech config.Technology
expectedProtocol config.Protocol
expectedResponseType int64
}{
{
name: "NordWhisper disabled",
name: "NordWhisper disabled",
currentTech: config.Technology_NORDLYNX,
currentProtocol: config.Protocol_UDP,
targetTech: config.Technology_NORDWHISPER,
expectedTech: config.Technology_NORDLYNX,
expectedProtocol: config.Protocol_UDP,
expectedResponseType: internal.CodeFeatureHidden,
},
{
name: "failed to get NordWhisper status",
nordWhisperEnabledErr: fmt.Errorf("failed to get NordWhisper status"),
currentTech: config.Technology_NORDLYNX,
currentProtocol: config.Protocol_TCP,
targetTech: config.Technology_NORDWHISPER,
expectedTech: config.Technology_NORDLYNX,
expectedProtocol: config.Protocol_TCP,
expectedResponseType: internal.CodeFeatureHidden,
},
{
name: "switch from NordWhisper to OpenVPN",
nordwhisperEnabled: true,
currentTech: config.Technology_NORDWHISPER,
currentProtocol: config.Protocol_Webtunnel,
targetTech: config.Technology_OPENVPN,
expectedTech: config.Technology_OPENVPN,
expectedProtocol: config.Protocol_UDP,
expectedResponseType: internal.CodeSuccess,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
remoteConfigGetter := mock.NewRemoteConfigMock()
remoteConfigGetter.GetNordWhisperErr = test.nordWhisperEnabledErr

configManager := mock.NewMockConfigManager()
configManager.Cfg = &config.Config{
Technology: test.currentTech,
AutoConnectData: config.AutoConnectData{
Protocol: test.currentProtocol,
},
}

networker := networker.Mock{}

r := RPC{
remoteConfigGetter: remoteConfigGetter,
cm: configManager,
netw: &networker,
factory: func(t config.Technology) (vpn.VPN, error) { return nil, nil },
events: events.NewEventsEmpty(),
}

resp, err := r.SetTechnology(context.Background(),
&pb.SetTechnologyRequest{Technology: config.Technology_NORDWHISPER})
&pb.SetTechnologyRequest{Technology: test.targetTech})
assert.Nil(t, err, "Unexpected error returned by IsNordWhisperEnabled rpc.")
assert.Equal(t, resp.Type, internal.CodeFeatureHidden, "Unexpected response type received.")
assert.Equal(t, test.expectedResponseType, resp.Type, "Unexpected response type received.")
assert.Equal(t, test.expectedTech, configManager.Cfg.Technology, "Unexpected technology saved in config.")
assert.Equal(t, test.expectedProtocol, configManager.Cfg.AutoConnectData.Protocol,
"Unexpected protocol saved in config.")
})
}
}
1 change: 1 addition & 0 deletions events/moose/moose_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func TestIsPaymentValid(t *testing.T) {
{name: "empty payment", valid: false},
{name: "invalid status", valid: false, payment: core.Payment{Status: "invalid"}},
{name: "done status", valid: true, payment: core.Payment{Status: "done"}},
{name: "error status", valid: false, payment: core.Payment{Status: "error"}},
} {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(
Expand Down
Loading
Loading