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

Check if established connection is post quantum before enabling meshnet #647

Merged
merged 4 commits into from
Oct 15, 2024
Merged
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: 2 additions & 0 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,8 @@ func meshnetErrorToError(code meshpb.MeshnetErrorCode) error {
return errors.New(DisconnectNotConnected)
case meshpb.MeshnetErrorCode_CONFLICT_WITH_PQ:
return errors.New(SetPqAndMeshnet)
case meshpb.MeshnetErrorCode_CONFLICT_WITH_PQ_SERVER:
return errors.New(SetPqAndMeshnetServer)
default:
return errors.New(AccountInternalError)
}
Expand Down
1 change: 1 addition & 0 deletions cli/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,5 +366,6 @@ Provide a [transfer_id] argument to list files in the specified transfer.`
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."
SetPqAndMeshnet = "The post-quantum VPN and Meshnet can't run at the same time. Please disable one feature to use the other."
SetPqAndMeshnetServer = "Meshnet is not supported while connected to post-quantum server, disconnect from the server and try again."
SetPqUsageText = "Enables or disables post-quantum VPN. When enabled, your connection uses cutting-edge cryptography designed to resist quantum computer attacks. Not compatible with Meshnet."
)
3 changes: 3 additions & 0 deletions daemon/jobs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ func (*meshNetworker) StatusMap() (map[string]string, error) {
return map[string]string{}, nil
}
func (*meshNetworker) LastServerName() string { return "" }
func (*meshNetworker) GetConnectionParameters() (vpn.ServerData, bool) {
return vpn.ServerData{}, false
}

func TestStartAutoMeshnet(t *testing.T) {
category.Set(t, category.Unit)
Expand Down
2 changes: 2 additions & 0 deletions daemon/rpc_set_postquantum.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package daemon
import (
"context"
"log"
"strconv"

"github.com/NordSecurity/nordvpn-linux/config"
"github.com/NordSecurity/nordvpn-linux/daemon/pb"
Expand Down Expand Up @@ -41,5 +42,6 @@ func (r *RPC) SetPostQuantum(ctx context.Context, in *pb.SetGenericRequest) (*pb

return &pb.Payload{
Type: internal.CodeSuccess,
Data: []string{strconv.FormatBool(r.netw.IsVPNActive())},
}, nil
}
28 changes: 28 additions & 0 deletions daemon/rpc_set_postquantum_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package daemon

import (
"context"
"strconv"
"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/internal"
"github.com/NordSecurity/nordvpn-linux/test/mock/networker"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -35,14 +37,22 @@ func TestSetPostquantumVpn(t *testing.T) {

mockPublisherSubscriber := events.MockPublisherSubscriber[bool]{}
mockEvents := events.Events{Settings: &events.SettingsEvents{PostquantumVPN: &mockPublisherSubscriber}}
mockNetworker := networker.Mock{}

r := RPC{
cm: &mockConfigManager,
events: &mockEvents,
netw: &mockNetworker,
}

successPayload := pb.Payload{
Type: internal.CodeSuccess,
Data: []string{strconv.FormatBool(false)},
}

successWithVPNPayload := pb.Payload{
Type: internal.CodeSuccess,
Data: []string{strconv.FormatBool(true)},
}

conflictMeshPayload := pb.Payload{
Expand All @@ -57,6 +67,7 @@ func TestSetPostquantumVpn(t *testing.T) {
testName string
pq bool
meshnet bool
vpnActive bool
tech config.Technology
payload *pb.Payload
eventPublished bool
Expand All @@ -65,6 +76,7 @@ func TestSetPostquantumVpn(t *testing.T) {
testName: "pq off mesh is off tech unknown",
pq: false,
meshnet: false,
vpnActive: false,
tech: config.Technology_UNKNOWN_TECHNOLOGY,
payload: &conflictTechPayload,
eventPublished: false,
Expand All @@ -73,6 +85,7 @@ func TestSetPostquantumVpn(t *testing.T) {
testName: "pq off mesh is off tech nlx",
pq: false,
meshnet: false,
vpnActive: false,
tech: config.Technology_NORDLYNX,
payload: &successPayload,
eventPublished: true,
Expand All @@ -81,6 +94,7 @@ func TestSetPostquantumVpn(t *testing.T) {
testName: "pq on mesh is off tech unknown",
pq: true,
meshnet: false,
vpnActive: false,
tech: config.Technology_UNKNOWN_TECHNOLOGY,
payload: &conflictTechPayload,
eventPublished: false,
Expand All @@ -89,6 +103,7 @@ func TestSetPostquantumVpn(t *testing.T) {
testName: "pq on mesh is off tech nlx",
pq: true,
meshnet: false,
vpnActive: false,
tech: config.Technology_NORDLYNX,
payload: &successPayload,
eventPublished: true,
Expand All @@ -97,9 +112,19 @@ func TestSetPostquantumVpn(t *testing.T) {
testName: "pq on mesh is on",
pq: true,
meshnet: true,
vpnActive: false,
payload: &conflictMeshPayload,
eventPublished: false,
},
{
testName: "pq off mesh is off tech nlx vpn on",
pq: false,
meshnet: false,
vpnActive: true,
tech: config.Technology_NORDLYNX,
payload: &successWithVPNPayload,
eventPublished: true,
},
}

for _, test := range tests {
Expand All @@ -108,6 +133,9 @@ func TestSetPostquantumVpn(t *testing.T) {
mockConfigManager.c.Technology = test.tech
mockConfigManager.c.AutoConnectData.PostquantumVpn = !test.pq

mockNetworker.ConnectRetries = 0
mockNetworker.VpnActive = test.vpnActive

req := pb.SetGenericRequest{Enabled: test.pq}
resp, err := r.SetPostQuantum(context.Background(), &req)

Expand Down
9 changes: 9 additions & 0 deletions daemon/vpn/nordlynx/kernel_space.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type KernelSpace struct {
fwmark uint32
tun *tunnel.Tunnel
eventsPublisher *vpn.Events
serverData vpn.ServerData
sync.Mutex
}

Expand Down Expand Up @@ -68,6 +69,8 @@ func (k *KernelSpace) Start(
serverData.IP,
)

k.serverData = serverData

//check if wireguard is not up already
if _, err := exec.Command("ip", "link", "show", "dev", InterfaceName).Output(); err == nil {
return vpn.ErrTunnelAlreadyExists
Expand Down Expand Up @@ -157,6 +160,12 @@ func (k *KernelSpace) State() vpn.State {
return k.state
}

func (k *KernelSpace) GetConnectionParameters() (vpn.ServerData, bool) {
k.Lock()
defer k.Unlock()
return k.serverData, k.active
}

// stop is used on errors
func (k *KernelSpace) stop() error {
if k.tun != nil {
Expand Down
7 changes: 7 additions & 0 deletions daemon/vpn/nordlynx/libtelio/libtelio.go
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,13 @@ func (l *Libtelio) Public(private string) string {
return teliogo.GeneratePublicKey(private)
}

func (l *Libtelio) GetConnectionParameters() (vpn.ServerData, bool) {
l.mu.Lock()
defer l.mu.Unlock()

return l.currentServer, l.active
}

// isConnected function designed to be called before performing an action which trigger events.
// libtelio is sending back events via callback, to properly catch event from libtelio, event
// is being received in goroutine, but this goroutine has to be 100% started before invoking
Expand Down
6 changes: 6 additions & 0 deletions daemon/vpn/openvpn/openvpn.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,12 @@ func (ovpn *OpenVPN) State() vpn.State {
return ovpn.state
}

func (ovpn *OpenVPN) GetConnectionParameters() (vpn.ServerData, bool) {
ovpn.Lock()
defer ovpn.Unlock()
return ovpn.serverData, ovpn.active
}

func (ovpn *OpenVPN) startOpenVPN() error {
stdout, err := ovpn.process.StdoutPipe()
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions daemon/vpn/vpn.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ type VPN interface {
IsActive() bool
Tun() tunnel.T // required because of OpenVPN
NetworkChanged() error
// GetConnectionParameters returns ServerData of current connection and true if connection is established, or empty
// ServerData and false if it isn't.
GetConnectionParameters() (ServerData, bool)
}

// Credentials define a possible set of credentials required to
Expand Down
1 change: 1 addition & 0 deletions meshnet/networker.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type Networker interface {
bool, // enableLocalTraffic
) error
Stop() error
GetConnectionParameters() (vpn.ServerData, bool)
}

// UniqueAddress a member of mesh network.
Expand Down
47 changes: 26 additions & 21 deletions meshnet/pb/service_response.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions meshnet/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"log"
"strings"
"time"

Expand Down Expand Up @@ -135,7 +136,19 @@ func (s *Server) EnableMeshnet(ctx context.Context, _ *pb.Empty) (*pb.MeshnetRes
}, nil
}

if serverData, ok := s.netw.GetConnectionParameters(); ok {
if serverData.PostQuantum {
return &pb.MeshnetResponse{
Response: &pb.MeshnetResponse_MeshnetError{
MeshnetError: pb.MeshnetErrorCode_CONFLICT_WITH_PQ_SERVER,
},
}, nil
}
}

log.Println("DEBUG: get token data")
token := cfg.TokensData[cfg.AutoConnectData.ID].Token
log.Println("DEBUG: is MeshDevice nil: ", cfg.MeshDevice == nil)
resp, err := s.reg.Map(token, cfg.MeshDevice.ID)
if err != nil {
if errors.Is(err, core.ErrUnauthorized) {
Expand Down
3 changes: 3 additions & 0 deletions meshnet/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ func (*workingNetworker) StatusMap() (map[string]string, error) {
return map[string]string{}, nil
}
func (*workingNetworker) LastServerName() string { return "" }
func (*workingNetworker) GetConnectionParameters() (vpn.ServerData, bool) {
return vpn.ServerData{}, false
}

type invitationsAPI struct{}

Expand Down
7 changes: 7 additions & 0 deletions networker/networker.go
Original file line number Diff line number Diff line change
Expand Up @@ -1751,3 +1751,10 @@ func (netw *Combined) SetLanDiscovery(enabled bool) {
err)
}
}

func (netw *Combined) GetConnectionParameters() (vpn.ServerData, bool) {
netw.mu.Lock()
defer netw.mu.Unlock()

return netw.vpnet.GetConnectionParameters()
}
12 changes: 12 additions & 0 deletions test/mock/vpn.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ func (w *WorkingVPN) NetworkChanged() error {

return w.ErrNetworkChanges
}
func (w *WorkingVPN) GetConnectionParameters() (vpn.ServerData, bool) {
return vpn.ServerData{}, false
}

type WorkingInactiveVPN struct{}

Expand All @@ -60,6 +63,9 @@ func (WorkingInactiveVPN) State() vpn.State { return vpn.ConnectedState }
func (WorkingInactiveVPN) IsActive() bool { return false }
func (WorkingInactiveVPN) Tun() tunnel.T { return WorkingT{} }
func (WorkingInactiveVPN) NetworkChanged() error { return nil }
func (WorkingInactiveVPN) GetConnectionParameters() (vpn.ServerData, bool) {
return vpn.ServerData{}, false
}

// FailingVPN stub of a github.com/NordSecurity/nordvpn-linux/daemon/vpn.VPN interface.
type FailingVPN struct{}
Expand All @@ -72,6 +78,9 @@ func (FailingVPN) State() vpn.State { return vpn.ExitedState }
func (FailingVPN) IsActive() bool { return false }
func (FailingVPN) Tun() tunnel.T { return WorkingT{} }
func (FailingVPN) NetworkChanged() error { return ErrOnPurpose }
func (FailingVPN) GetConnectionParameters() (vpn.ServerData, bool) {
return vpn.ServerData{}, false
}

// ActiveVPN stub of a github.com/NordSecurity/nordvpn-linux/daemon/vpn.VPN interface.
type ActiveVPN struct{}
Expand All @@ -84,6 +93,9 @@ func (ActiveVPN) State() vpn.State { return vpn.ExitedState }
func (ActiveVPN) IsActive() bool { return true }
func (ActiveVPN) Tun() tunnel.T { return WorkingT{} }
func (ActiveVPN) NetworkChanged() error { return nil }
func (ActiveVPN) GetConnectionParameters() (vpn.ServerData, bool) {
return vpn.ServerData{}, false
}

type MeshnetAndVPN struct {
WorkingVPN
Expand Down
Loading