Git Product home page Git Product logo

nord's Introduction

Nord: an unofficial NordVPN client

GPLv3 License PyPi package Documentation Status

DEPRECATION NOTICE: This repository will no longer be actively worked on now that NordVPN has an official Linux client: https://support.nordvpn.com/Connectivity/Linux/

Overview

Nord is a client for interacting with the NordVPN service.

At its core is a high-level Python API for interacting both with the web service provided by NordVPN, and for connecting to VPN servers using OpenVPN.

Nord also contains components that expose this API as a command line tool, and (soon) as a web service and frontend.

Licence

Nord is licensed under the terms of the GNU GPLv3. See the LICENSE file for details.

The file web/static/world.geo.json is Copyright (c) 2017 Richard Zimmerman and is included here under the conditions of the MIT License

Installation

pip install nord

Usage

Run nord --help for the full usage instructions.

Connect to a specific NordVPN server:

nord connect -u my_user -p my_password us893

Connect to any NordVPN server in a given country:

nord connect -u my_user -p my_password US

Serve a web app that allows you to select the country to which you wish to connect:

nord web -u my_user -p my_password

You can also supply your password from a file using the -f flag. The special value - means "read from stdin". This is particularly useful when your password is stored in a utility such as pass:

pass nordvpn_password | nord connect -u my_user -f - us893

Prerequesites

  • GNU/Linux system
  • Python 3.6
  • openvpn
  • sudo

nord contains many Linux-isms (e.g. using the sudo program to obtain root access) so it will certainly not work on Windows, it may possibly work on OSX and *BSD, but support for these platforms is not a goal.

Most recent versions of popular GNU/Linux distributions (with the exception of Debian) have both an OpenVPN client and Python 3.6 in their official repositories. Debian users will have to take additional steps to get a Python 3.6 installation.

Ubuntu 16.10 and newer

Ubuntu comes with sudo already installed, so we just need to install Python and openVPN:

sudo apt-get install python3.6 openvpn

Fedora 26 and newer

Fedora comes with sudo already installed, so we just need to install Python and openVPN:

sudo dnf install python36 openvpn

Arch Linux

Run the following as root:

pacman -S sudo python openvpn

Then configure sudo by following the Arch wiki to give privileges to the user that nord will be running as.

Debian

First run the following as root to install the openVPN client and sudo from the Debian repositories:

apt install sudo openvpn

Then configure sudo by following the Debian wiki to give privileges to the user that nord will be running as.

There are a couple of options for installing Python3.6 on Debian:

  • Installing from the unstable repositories
  • Installing from source (easier than you might think

Both of these methods are explained in top-rated answers to this stackexchange question.

Developing

You will need Python 3.6 and Yarn (for the web components).

git clone https://github.com/jbweston/nord
cd nord
virtualenv -p python3.6
source venv/bin/activate
pip install -e .[dev]
yarn install

Periodically check your code with the linter:

pylint nord

Web components

When developing the web frontend you can execute the following command to run an auto-reloading web server:

yarn dev

Building the API documentation

make -C docs html
xdg-open docs/build/html/index.html

nord's People

Contributors

jbweston avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

nord's Issues

Add tests

There are currently no tests (!). As the functionality is quite modular and configurable it should at least be easy to test pieces in isolation, even if testing all failure modes is a challenge.

Connect to the "best" server

Using the tools in #1 we can improve the CLI by connecting to the "best" server if no host is given.

This will mean that we need to add command-line options to control the selection.
Typically I would imagine the following would be sufficient:

  • Filter by Country (probably providing the 2-letter abbreviation is enough)
  • Filter by "search keywords" (e.g. 'P2P" or "sha512")
  • Ping timeout: larger means RTT is more accurate, lower means we get an RTT faster

Add a web frontend

A graphical frontend would be useful. Rather than using a GUI framework like GTK or QT, if we instead make nord into a web service then it can also run on a router and be controlled by clients on the network, which is useful when you want to have a whole network behind NordVPN.

In its basic form the web frontend would have a top dashboard showing the current connection status (up or down) and our current ip address. The main part of the page would show a world map with markers for each server, you can click through to get more detailed information and to connect to that server.

Extensions could be:

  • auto-select a server by selecting a given country
  • live-updating server load and RTT

Implement an endpoint for getting current app state

Currently clients get informed about state changes (connecting/connected/disconnecting/disconnected) but have no way to query the current state, which means that the UI is incorrect until the first state change.

We will need to store some more state on the app object that is written by the vpn coroutine and can be read by an API endpoint.

Create "frozen" builds for standalone applications

We depend on Python 3.6 so as to use improved asyncio among other things, however typically people will not have Python 3.6 installed, so we should also provide a "frozen" build that bundles the Python interpreter also.

Add a higher-level API

At the moment we expose just the raw API that NordVPN provides.

Specifically we would like to have:

  • Filtering servers by certain criteria: load, country, features...
    We probably don't need to support arbitrary filters, as these are just convenience
    functions anyway

  • Ping servers to get an approximate RTT. We should be able to combine this with filters
    to avoid pinging the whole internet.

Use a better method for selecting servers

Countries with large numbers of hosts (e.g. US) mean that we typically have to ping ~400 hosts. This is not inherently a problem, but as we have to spawn a subprocess for each ping this gets quite expensive.

There are several options:

  1. pre-select a fixed number of hosts with the smallest load and then ping these
  2. ping all hosts and wait only for the first "n" to return

The problem with 1 is that we may end up with servers with a very large round trip time. The problem with 2 is that we mix up latency from starting processes with round trip time.

The solution is probably that slight variations in RTT make less of a difference to VPN performance than load, so even if we don't choose the optimal server based on RTT, this isn't the end.

OTOH saving a few seconds on startup is not the end of the world, as the connection will tyically last a while

route traffic for VPN servers around VPN tunnel

We would like to be able to get an accurate RTT to VPN servers, even when the VPN is up.
This information could be used to automatically reconnect to a better server if the connected one becomes sluggish.

If we naively ping, then the ICMP packets will get routed through the VPN, which will give us inaccurate RTT for the purposes that we require.

The solution is to set up explicit routes so that traffic destined for the VPN servers themselves is not routed through the VPN. This can be achieved by placing lines at the end of the openvpn client config:

route <ip address> 255.255.255.255 net_gateway

These lines can be manually added to the config file that we get from the NordVPN web service before pass it off to OpenVPN.

Consistent separation of responsibilities between `nord` and `openvpn`

At the moment we pass basically no flags to openvpn, which means that we have to handle many things in Python code (e.g. if we want some action to be performed when openvpn starts up or goes down, or we want to reconnect when the connection goes down).

However, openvpn itself has many flags that can control this behaviour (--up, --down, ...).
The advantage of using these flags is that it is less likely that bugs in nord will cause the system to be left in an inconsistent state (e.g. DNS settings not reset).

Newly connected clients don't get the correct VPN state

If a single client connects to the web interface and connects to a server, the next client to connect will not see the 'connected' state. This can also be observed by connecting to a VPN and then refreshing the page.

Currently the coroutine that manages the VPN connection does not provide a method for querying its state, but it should.

Sudo credentials time out

Notice that if we kill nord some time after the initial startup, sudo complains that it needs a password.

We have the maintain_sudo context manager that is meant to take care of this, but clearly it is not working correctly.

set up appropriate logging handlers

At the moment all log messages are printed to the screen. We should be able to control exactly which messages are printed (e.g printing all OpenVPN stdout if --debug is specified)

Add logging to API

the VPN functionality has logging, so the API part should probably also

API Url not working anymore https://api.nordvpn.com/dns/smart

I don't know if the public api of NordVPN changed, but the url 'https://api.nordvpn.com/dns/smart' isn't working anymore...

It returns a Not Found message.

/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/nord/api.py:80: RuntimeWarning: coroutine 'ClientSession.close' was never awaited
  self._session.close()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/nord/cli.py", line 54, in main
    returncode = loop.run_until_complete(command(args))
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/base_events.py", line 641, in run_until_complete
    return future.result()
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/nord/cli.py", line 210, in connect
    output = await asyncio.gather(
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/decorator.py", line 221, in fun
    return await caller(func, *(extras + args), **kw)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/nord/_utils.py", line 105, in _memoized
    cache[key] = await func(*args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/nord/api.py", line 170, in dns_servers
    return await self._get_json('dns/smart')
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/nord/api.py", line 91, in _get_json
    async with self._session.get(url, headers=self.headers) as resp:
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/aiohttp/client.py", line 1138, in __aenter__
    self._resp = await self._coro
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/aiohttp/client.py", line 640, in _request
    resp.raise_for_status()
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/aiohttp/client_reqrep.py", line 1004, in raise_for_status
    raise ClientResponseError(
aiohttp.client_exceptions.ClientResponseError: 404, message='Not Found', url=URL('https://api.nordvpn.com/dns/smart')

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.10/bin/nord", line 8, in <module>
    sys.exit(main())
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/nord/cli.py", line 61, in main
    remaining_tasks = cancel_all_tasks()
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/nord/cli.py", line 71, in cancel_all_tasks
    remaining_tasks = asyncio.Task.all_tasks()
AttributeError: type object '_asyncio.Task' has no attribute 'all_tasks'

Python 3.10
macOs m1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.