From 079514829e0f2e4b4bb55453e6741950056971c8 Mon Sep 17 00:00:00 2001 From: Ross Chadwick Date: Wed, 13 Sep 2017 16:59:56 +0200 Subject: [PATCH] Initial pypi packaging --- .gitignore | 3 ++ README.md | 36 ++++++++++++--------- nordnm/__init__.py | 3 +- nordnm/__main__.py | 15 +++++---- nordnm/benchmarking.py | 4 +-- nordnm/config.py | 4 +-- nordnm/networkmanager.py | 2 +- nordnm/nordnm.py | 68 ++++++++++++++++----------------------- nordnm/paths.py | 10 ++++++ nordnm/utils.py | 13 +++++--- requirements.txt | 4 +-- setup.cfg | 3 ++ setup.py | 69 ++++++++++++++++++++++++++++++++++++++++ 13 files changed, 159 insertions(+), 75 deletions(-) create mode 100644 nordnm/paths.py create mode 100644 setup.cfg create mode 100644 setup.py diff --git a/.gitignore b/.gitignore index 3bf848e..a10a23c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ nordnm/__pycache__/ +*.pyc +dist/ +*.egg-info diff --git a/README.md b/README.md index ce5d25a..f0e49dd 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# NordVPN-NetworkManager +# NordNM This tool removes the need for manually handling OpenVPN configurations from NordVPN. It will synchronise the best servers from chosen countries into the NetworkManager VPN list. A synchronised VPN can then be chosen to auto-connect to, whenever NetworkManager brings an network connection up. -More documentation will be available when this repository is ready for public use. +More documentation will be available when this tool is out of Alpha releases. **Warning:** -*This tool is still highly experimental and is definitely not yet robust enough to use reliably. I take no responsibility for any unforseen problems it may cause.* +*This tool is still highly under development and is not yet robust enough to use reliably. I take no responsibility for any unforseen problems it may cause.* ## Features: - Uses the latest NordVPN OpenVPN configuration files. @@ -17,29 +17,35 @@ More documentation will be available when this repository is ready for public us - Disables IPv6 by default, to avoid IPv6 leaks. - Sets the auto connect server of your choice for all NetworkManager connections, instead of per connection. (optional) -## Requirements -### System -#### Debian/Ubuntu +## 1. Requirements + +### Debian/Ubuntu ``` -sudo apt update && sudo apt install openvpn network-manager-openvpn-gnome +sudo apt update && sudo apt install openvpn network-manager network-manager-gnome network-manager-openvpn-gnome ``` -#### Arch + +### Arch + ``` -sudo pacman -S openvpn networkmanager-openvpn +sudo pacman -S networkmanager openvpn networkmanager-openvpn ``` -### Python +## 2. Installation + ``` -sudo -H pip3 install -r requirements.txt +sudo -H pip3 install nordnm ``` -## Usage Examples -*get the latest configs, synchronise and then select auto-connect server:* +## 3. Usage Examples + +*Get the latest configs, synchronise and then select a "normal" Netherlands server using tcp as auto-connect server:* + ``` -sudo python3 nordnm --update --sync --auto-connect nl normal tcp +sudo nordnm --update --sync --auto-connect nl normal tcp ``` *View descriptions of other options:* + ``` -sudo python3 nordnm --help +sudo nordnm --help ``` diff --git a/nordnm/__init__.py b/nordnm/__init__.py index b728fcf..8c30d15 100644 --- a/nordnm/__init__.py +++ b/nordnm/__init__.py @@ -1,2 +1,3 @@ -__version__ = "0.1" +__package__ = "nordnm" +__version__ = "0.0a10" __license__ = "GNU General Public License v3 or later (GPLv3+)" diff --git a/nordnm/__main__.py b/nordnm/__main__.py index cec9606..24475ce 100644 --- a/nordnm/__main__.py +++ b/nordnm/__main__.py @@ -1,19 +1,20 @@ #!/usr/bin/env python3 -from nordnm import NordNM +from nordnm import nordnm import sys import logging import os -def main(argv): - NordNM() - - -if __name__ == "__main__": +def main(): if os.getuid() != 0: print("This script must be run as root! Exiting.") sys.exit(1) logging.basicConfig(format='[%(levelname)s] [%(name)s]: %(message)s', level=logging.INFO, stream=sys.stdout) - main(sys.argv) + + nordnm.NordNM() + + +if __name__ == "__main__": + main() diff --git a/nordnm/benchmarking.py b/nordnm/benchmarking.py index ed87390..50469f2 100644 --- a/nordnm/benchmarking.py +++ b/nordnm/benchmarking.py @@ -1,5 +1,5 @@ -import utils -import nordapi +from nordnm import utils +from nordnm import nordapi import multiprocessing from functools import partial diff --git a/nordnm/config.py b/nordnm/config.py index 8747766..007c992 100644 --- a/nordnm/config.py +++ b/nordnm/config.py @@ -1,5 +1,5 @@ -import utils -import nordapi +from nordnm import utils +from nordnm import nordapi import configparser import logging diff --git a/nordnm/networkmanager.py b/nordnm/networkmanager.py index 7c091e8..745a809 100644 --- a/nordnm/networkmanager.py +++ b/nordnm/networkmanager.py @@ -1,4 +1,4 @@ -import utils +from nordnm import utils import subprocess import shutil diff --git a/nordnm/nordnm.py b/nordnm/nordnm.py index ca76b81..203d4b2 100644 --- a/nordnm/nordnm.py +++ b/nordnm/nordnm.py @@ -1,9 +1,12 @@ -from config import ConfigHandler -from credentials import CredentialsHandler -import nordapi -import networkmanager -import utils -import benchmarking +#!/usr/bin/env python3 + +from nordnm.credentials import CredentialsHandler +from nordnm.config import ConfigHandler +from nordnm import nordapi +from nordnm import networkmanager +from nordnm import utils +from nordnm import benchmarking +from nordnm import paths import argparse import os @@ -14,18 +17,6 @@ import copy from timeit import default_timer as timer -# TODO: Terminate script/wait cleanly if network connection goes down - -# TODO: Put these paths somewhere more appropriate -HOME_DIR = os.path.expanduser('~' + utils.get_current_user()) -USER_DIR = os.path.join(HOME_DIR, '.nordnm/') -OVPN_DIR = os.path.join(USER_DIR, 'configs/') -CONFIG_PATH = os.path.join(USER_DIR, 'settings.conf') - -ROOT_DIR = '/usr/share/nordnm/' -ACTIVE_SERVERS_PATH = os.path.join(ROOT_DIR, 'active_servers') -CREDENTIALS_PATH = os.path.join(ROOT_DIR, 'credentials') - class NordNM(object): def __init__(self): @@ -51,15 +42,15 @@ def setup(self): self.create_directories() - self.config = ConfigHandler(CONFIG_PATH) - self.credentials = CredentialsHandler(CREDENTIALS_PATH) + self.config = ConfigHandler(paths.SETTINGS) + self.credentials = CredentialsHandler(paths.CREDENTIALS) self.black_list = self.config.get_blacklist() self.white_list = self.config.get_whitelist() self.active_servers = {} - if os.path.isfile(ACTIVE_SERVERS_PATH): - self.active_servers = self.load_active_servers(ACTIVE_SERVERS_PATH) + if os.path.isfile(paths.ACTIVE_SERVERS): + self.active_servers = self.load_active_servers(paths.ACTIVE_SERVERS) def run(self, credentials, update, sync, purge, auto_connect): updated = False @@ -83,23 +74,20 @@ def run(self, credentials, update, sync, purge, auto_connect): networkmanager.restart() def create_directories(self): - if not os.path.exists(USER_DIR): - os.mkdir(USER_DIR) - utils.chown_path_to_user(USER_DIR) - - if not os.path.exists(OVPN_DIR): - os.mkdir(OVPN_DIR) - utils.chown_path_to_user(OVPN_DIR) + if not os.path.exists(paths.DIR_ROOT): + os.mkdir(paths.DIR_ROOT) + utils.chown_path_to_user(paths.DIR_ROOT) - if not os.path.exists(ROOT_DIR): - os.mkdir(ROOT_DIR) + if not os.path.exists(paths.DIR_OVPN): + os.mkdir(paths.DIR_OVPN) + utils.chown_path_to_user(paths.DIR_OVPN) def get_configs(self): self.logger.info("Attempting to download and extract the latest NordVPN configurations.") configs = nordapi.get_configs() if configs: - utils.extract_zip(configs, OVPN_DIR) + utils.extract_zip(configs, paths.DIR_OVPN) else: self.logger.error("Could not retrieve configs from NordVPN") @@ -108,11 +96,11 @@ def get_ovpn_path(self, domain, protocol): ovpn_path = None try: - for f in os.listdir(OVPN_DIR): - file_path = os.path.join(OVPN_DIR, f) + for f in os.listdir(paths.DIR_OVPN): + file_path = os.path.join(paths.DIR_OVPN, f) if os.path.isfile(file_path): if fnmatch(f, wildcard): - ovpn_path = os.path.join(OVPN_DIR, f) + ovpn_path = os.path.join(paths.DIR_OVPN, f) except Exception as ex: self.logger.error(ex) @@ -146,7 +134,7 @@ def purge_active_connections(self, remove_autoconnect=True): networkmanager.remove_connection(connection_name) del active_servers[key] - self.save_active_servers(active_servers, ACTIVE_SERVERS_PATH) # Save after every successful removal, in case importer is killed abruptly + self.save_active_servers(active_servers, paths.ACTIVE_SERVERS) # Save after every successful removal, in case importer is killed abruptly self.active_servers = active_servers @@ -158,7 +146,7 @@ def purge_active_connections(self, remove_autoconnect=True): def load_active_servers(self, path): try: - with open(ACTIVE_SERVERS_PATH, 'rb') as fp: + with open(paths.ACTIVE_SERVERS, 'rb') as fp: active_servers = pickle.load(fp) return active_servers except Exception as ex: @@ -167,7 +155,7 @@ def load_active_servers(self, path): def save_active_servers(self, active_servers, path): try: - with open(ACTIVE_SERVERS_PATH, 'wb') as fp: + with open(paths.ACTIVE_SERVERS, 'wb') as fp: pickle.dump(active_servers, fp) except Exception as ex: self.logger.error(ex) @@ -226,7 +214,7 @@ def connection_exists(self, connection_name): return False def configs_exist(self): - configs = os.listdir(OVPN_DIR) + configs = os.listdir(paths.DIR_OVPN) if configs: return True else: @@ -280,7 +268,7 @@ def sync_servers(self): # If the connection already existed, or the import was successful, add the server combination to the active servers if imported: self.active_servers[key] = best_servers[key] - self.save_active_servers(self.active_servers, ACTIVE_SERVERS_PATH) + self.save_active_servers(self.active_servers, paths.ACTIVE_SERVERS) if new_connections > 0: self.logger.info("%i new connections added.", new_connections) diff --git a/nordnm/paths.py b/nordnm/paths.py new file mode 100644 index 0000000..5561518 --- /dev/null +++ b/nordnm/paths.py @@ -0,0 +1,10 @@ +import os +from nordnm import utils + +DIR_USERHOME = os.path.expanduser('~' + utils.get_current_user()) +DIR_ROOT = os.path.join(DIR_USERHOME, '.nordnm/') +DIR_OVPN = os.path.join(DIR_ROOT, 'configs/') + +SETTINGS = os.path.join(DIR_ROOT, 'settings.conf') +ACTIVE_SERVERS = os.path.join(DIR_ROOT, '.active_servers') +CREDENTIALS = os.path.join(DIR_ROOT, 'credentials.conf') diff --git a/nordnm/utils.py b/nordnm/utils.py index c6a5f14..887e821 100644 --- a/nordnm/utils.py +++ b/nordnm/utils.py @@ -1,18 +1,21 @@ -import utils - import os import stat from io import BytesIO from zipfile import ZipFile import subprocess import logging +import getpass logger = logging.getLogger(__name__) # Since we're running with root priveledges, this will return the current username def get_current_user(): - return os.getenv("SUDO_USER") + username = os.getenv("SUDO_USER") + if not username: + username = getpass.getuser() + + return username # Change the owner and group of a given path to the current user @@ -80,11 +83,11 @@ def get_rtt_loss(host, ping_attempts): logger.error("Could not interpret output of ping command.\nOutput: %s", ex) except subprocess.CalledProcessError: - err = utils.format_std_string(output.stderr) + err = format_std_string(output.stderr) if err: logger.error("Ping failed with error: %s", err) else: - out = utils.format_std_string(output.stdout) + out = format_std_string(output.stdout) logger.warning("Ping failed with output: %s", out) return (None, 100) # If anything failed, return rtt as None and 100% loss diff --git a/requirements.txt b/requirements.txt index 48d1375..54d584d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -numpy==1.13.0 -requests==2.18.1 +numpy>=1.13.0 +requests>=2.18.1 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..f48fdad --- /dev/null +++ b/setup.cfg @@ -0,0 +1,3 @@ +[metadata] +description-file = README.md +license_file = LICENSE diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..b6525af --- /dev/null +++ b/setup.py @@ -0,0 +1,69 @@ +from setuptools import setup +import sys +from nordnm import __version__, __license__, __package__ + +# Minimal version numbers +PYTHON_VERSION = 3 +PYTHON_SUBVERSION = 5 + +if sys.version_info < (PYTHON_VERSION, PYTHON_SUBVERSION): + sys.exit("Error: %s requires Python %i.%i or greater to be installed. Please use a pip corresponding to this version or greater." % (__package__, PYTHON_VERSION, PYTHON_SUBVERSION)) + + +def get_readme(): + try: + import pypandoc + long_description = pypandoc.convert('README.md', 'rst') + long_description = long_description.replace("\r", "") + except OSError: + print("ERROR: Pandoc not found. Long_description conversion failure.") + import io + # pandoc is not installed, fallback to using raw contents + with io.open('README.md', encoding="utf-8") as f: + long_description = f.read() + + return long_description + + +def get_requirements(): + with open('requirements.txt') as f: + requirements = f.read().splitlines() + return requirements + + +setup( + name=__package__, + version=__version__, + author='Ross Chadwick', + author_email='ross@rchadwick.co.uk', + packages=[__package__], + url='https://github.com/Chadsr/NordVPN-NetworkManager', + license=__license__, + description='A Python 3 CLI tool for automating the management of NordVPN OpenVPN servers through NetworkManager.', + long_description=get_readme(), + install_requires=get_requirements(), + platforms=['GNU/Linux', 'Ubuntu', 'Debian', 'Kali', 'CentOS', 'Arch', 'Fedora'], + zip_safe=False, + keywords=['openvpn', 'nordvpn', 'networkmanager', 'network-manager', 'vpn'], + entry_points={ + 'console_scripts': [ + 'nordnm = nordnm.__main__:main' + ]}, + classifiers=[ + 'Environment :: Console', + 'Intended Audience :: End Users/Desktop', + 'Intended Audience :: System Administrators', + 'Intended Audience :: Developers', + 'Topic :: Internet', + 'Topic :: Desktop Environment :: Gnome', + 'Topic :: Utilities', + 'Topic :: Security', + 'Topic :: System :: Networking', + 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', + 'Natural Language :: English', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python :: 3 :: Only', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + ] +)