def update_config_files(): root.verify_root_access("Root access needed to write files in " + "'" + __basefilepath__ + "files/" + "'") try: subprocess.check_call( ["sudo", "wget", "https://downloads.nordcdn.com/configs/archives/servers/ovpn.zip", "-P", __basefilepath__]) except subprocess.CalledProcessError: print( Fore.RED + "Exception occured while wgetting zip, is the internet working? \ is nordcdn.com blocked by your ISP or Country?, If so use Privoxy \ [https://github.com/jotyGill/openpyn-nordvpn/issues/109]" + Style.RESET_ALL) sys.exit() try: subprocess.check_call( ["sudo", "unzip", "-u", "-o", __basefilepath__ + "ovpn", "-d", __basefilepath__ + "files/"], stderr=subprocess.DEVNULL) subprocess.check_call( ["sudo", "rm", __basefilepath__ + "ovpn.zip"]) except subprocess.CalledProcessError: try: subprocess.check_call( ["sudo", "rm", "-rf", __basefilepath__ + "files/ovpn_udp"]) subprocess.check_call( ["sudo", "rm", "-rf", __basefilepath__ + "files/ovpn_tcp"]) subprocess.check_call( ["sudo", "unzip", __basefilepath__ + "ovpn", "-d", __basefilepath__ + "files/"]) subprocess.check_call( ["sudo", "rm", __basefilepath__ + "ovpn.zip"]) except subprocess.CalledProcessError: print(Fore.RED + "Exception occured while unzipping ovpn.zip, is unzip installed?" + Style.RESET_ALL) sys.exit()
def apply_fw_rules(interfaces_details, vpn_server_ip): root.verify_root_access("Root access needed to modify 'iptables' rules") # Empty the INPUT and OUTPUT chain of any current rules subprocess.run(["sudo", "iptables", "-F", "OUTPUT"]) subprocess.run(["sudo", "iptables", "-F", "INPUT"]) # Allow all traffic out over the vpn tunnel subprocess.run("sudo iptables -A OUTPUT -o tun+ -j ACCEPT", shell=True) # accept traffic that comes through tun that you connect to subprocess.run( "sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -i tun+ -j ACCEPT", shell=True) for interface in interfaces_details: # if interface is active with an IP in it, don't send DNS requests to it if len(interface) == 3 and "tun" not in interface[0]: subprocess.run([ "sudo", "iptables", "-A", "OUTPUT", "-o", interface[0], "-p", "udp", "--destination-port", "53", "-j", "DROP" ]) # subprocess.run( # ["sudo", "iptables", "-A", "OUTPUT", "-o", interface[0], "-p", # "tcp", "--destination-port", "53", "-j", "DROP"]) if interface[0] != "lo": # allow access to vpn_server_ip subprocess.run([ "sudo", "iptables", "-A", "OUTPUT", "-o", interface[0], "-d", vpn_server_ip, "-j", "ACCEPT" ]) # talk to the vpnServer ip to connect to it subprocess.run([ "sudo", "iptables", "-A", "INPUT", "-m", "conntrack", "--ctstate", "ESTABLISHED,RELATED", "-i", interface[0], "-s", vpn_server_ip, "-j", "ACCEPT" ]) # allow access to internal ip range # print("internal ip with range", interface[2]) subprocess.run([ "sudo", "iptables", "-A", "OUTPUT", "-o", interface[0], "-d", interface[2], "-j", "ACCEPT" ]) subprocess.run([ "sudo", "iptables", "-A", "INPUT", "-m", "conntrack", "--ctstate", "ESTABLISHED,RELATED", "-i", interface[0], "-s", interface[2], "-j", "ACCEPT" ]) # Allow loopback traffic subprocess.run("sudo iptables -A INPUT -i lo -j ACCEPT", shell=True) subprocess.run("sudo iptables -A OUTPUT -o lo -j ACCEPT", shell=True) # best practice, stops spoofing subprocess.run("sudo iptables -A INPUT -s 127.0.0.0/8 -j DROP", shell=True) # Default action if no other rules match subprocess.run("sudo iptables -P OUTPUT DROP", shell=True) subprocess.run("sudo iptables -P INPUT DROP", shell=True) return
def kill_openpyn_process() -> None: try: root.verify_root_access("Root access needed to kill openpyn process") subprocess.call(["sudo", "killall", "openpyn"]) except subprocess.CalledProcessError: # when Exception, the openvpn_processes issued non 0 result, "not found" pass return
def kill_management_client(): # kill the management client if it is for some reason still alive try: root.verify_root_access("Root access needed to kill 'openpyn-management' process") subprocess.check_output(["sudo", "killall", "openpyn-management"], stderr=subprocess.DEVNULL) except subprocess.CalledProcessError: # when Exception, the openvpn_processes issued non 0 result, "not found" pass return
def kill_vpn_processes(): try: print("Killing any running openvpn processes") openvpn_processes = subprocess.check_output(["pgrep", "openvpn"]) # When it returns "0", proceed root.verify_root_access("Root access needed to kill openvpn process") subprocess.run(["sudo", "killall", "openvpn"]) except subprocess.CalledProcessError as ce: # when Exception, the openvpn_processes issued non 0 result, "not found" print("No openvpn process found") return
def kill_vpn_processes() -> None: try: subprocess.check_output(["pgrep", "openvpn"]) # When it returns "0", proceed root.verify_root_access("Root access needed to kill openvpn process") subprocess.call(["sudo", "killall", "openvpn"]) logger.notice("Killing the running openvpn process") time.sleep(1) except subprocess.CalledProcessError: # when Exception, the openvpn_processes issued non 0 result, "not found" pass return
def kill_management_client(): # kill the management client if it is for some reason still alive try: openvpn_processes = subprocess.check_output( ["pgrep", "openpyn-management"]) # When it returns "0", proceed root.verify_root_access( "Root access needed to kill 'openpyn-management' process") subprocess.call(["sudo", "killall", "openpyn-management"]) except subprocess.CalledProcessError as ce: # when Exception, the openvpn_processes issued non 0 result, "not found" pass return
def update_config_files(): root.verify_root_access( "Root access needed to write files in '/usr/share/openpyn/files'") try: subprocess.check_call( "sudo wget -N https://nordvpn.com/api/files/zip -P /usr/share/openpyn/" .split()) subprocess.check_call( "sudo unzip -u -o /usr/share/openpyn/zip -d /usr/share/openpyn/files/" .split()) subprocess.check_call("sudo rm /usr/share/openpyn/zip".split()) except subprocess.CalledProcessError: print("Exception occured while wgetting zip")
def update_config_files(): root.verify_root_access("Root access needed to write files in " + "'" + __basefilepath__ + "files/" + "'") try: subprocess.check_call([ "sudo", "wget", "https://downloads.nordcdn.com/configs/archives/servers/ovpn.zip", "-P", __basefilepath__ ]) subprocess.check_call([ "sudo", "unzip", "-u", "-o", __basefilepath__ + "ovpn", "-d", __basefilepath__ + "files/" ]) subprocess.check_call(["sudo", "rm", __basefilepath__ + "ovpn.zip"]) except subprocess.CalledProcessError: print("Exception occured while wgetting zip")
def clear_fw_rules() -> None: root.verify_root_access("Root access needed to modify 'iptables' rules") logger.info("Flushing iptables INPUT and OUTPUT chains AND Applying default Rules") subprocess.call(["sudo", "iptables", "-F", "OUTPUT"]) # allow all outgoing traffic subprocess.call("sudo iptables -P OUTPUT ACCEPT".split()) subprocess.call(["sudo", "iptables", "-F", "INPUT"]) subprocess.call(["sudo", "iptables", "-A", "INPUT", "-i", "lo", "-j", "ACCEPT"]) subprocess.call(["sudo", "iptables", "-A", "OUTPUT", "-o", "lo", "-j", "ACCEPT"]) subprocess.call( "sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT".split()) # allow ICMP traffic subprocess.call("sudo iptables -A INPUT -p icmp --icmp-type any -j ACCEPT".split()) # best practice, stops spoofing subprocess.call("sudo iptables -A INPUT -s 127.0.0.0/8 -j DROP".split()) # drop anything else incoming subprocess.call("sudo iptables -P INPUT DROP".split()) return
def clear_fw_rules(): root.verify_root_access("Root access needed to modify 'iptables' rules") print( "Flushing iptables INPUT and OUTPUT chains AND Applying default Rules") subprocess.run(["sudo", "iptables", "-F", "OUTPUT"]) # allow all outgoing traffic subprocess.run("sudo iptables -P OUTPUT ACCEPT", shell=True) subprocess.run(["sudo", "iptables", "-F", "INPUT"]) subprocess.run( ["sudo", "iptables", "-A", "INPUT", "-i", "lo", "-j", "ACCEPT"]) subprocess.run( ["sudo", "iptables", "-A", "OUTPUT", "-o", "lo", "-j", "ACCEPT"]) subprocess.run( "sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT", shell=True) # best practice, stops spoofing subprocess.run("sudo iptables -A INPUT -s 127.0.0.0/8 -j DROP", shell=True) # drop anything else incoming subprocess.run("sudo iptables -P INPUT DROP", shell=True) return
def clear_fw_rules() -> None: root.verify_root_access("Root access needed to modify 'iptables' rules") logger.info( "Flushing iptables INPUT and OUTPUT chains AND Applying default Rules") subprocess.call(["sudo", "-u", sudo_user, "iptables", "-F", "OUTPUT"]) # allow all outgoing traffic subprocess.call( ["sudo", "-u", sudo_user, "iptables", "-P", "OUTPUT", "ACCEPT"]) subprocess.call(["sudo", "-u", sudo_user, "iptables", "-F", "INPUT"]) subprocess.call([ "sudo", "-u", sudo_user, "iptables", "-A", "INPUT", "-i", "lo", "-j", "ACCEPT" ]) subprocess.call([ "sudo", "-u", sudo_user, "iptables", "-A", "OUTPUT", "-o", "lo", "-j", "ACCEPT" ]) subprocess.call([ "sudo", "-u", sudo_user, "iptables", "-A", "INPUT", "-m", "conntrack", "--ctstate", "ESTABLISHED,RELATED", "-j", "ACCEPT" ]) # allow ICMP traffic subprocess.call([ "sudo", "-u", sudo_user, "iptables", "-A", "INPUT", "-p", "icmp", "--icmp-type", "any", "-j", "ACCEPT" ]) # best practice, stops spoofing subprocess.call([ "sudo", "-u", sudo_user, "iptables", "-A", "INPUT", "-s", "127.0.0.0/8", "-j", "DROP" ]) # drop anything else incoming subprocess.call( ["sudo", "-u", sudo_user, "iptables", "-P", "INPUT", "DROP"])
def run(init: bool, server: str, country_code: str, country: str, area: str, tcp: bool, daemon: bool, max_load: int, top_servers: int, pings: str, kill: bool, kill_flush: bool, update: bool, list_servers: bool, force_fw_rules: bool, p2p: bool, dedicated: bool, double_vpn: bool, tor_over_vpn: bool, anti_ddos: bool, netflix: bool, test: bool, internally_allowed: List, skip_dns_patch: bool, silent: bool, nvram: str, openvpn_options: str, location: float) -> bool: if init: initialise(log_folder) fieldstyles = { 'asctime': {'color': 'green'}, 'hostname': {'color': 'magenta'}, 'levelname': {'color': 'black', 'bold': True}, 'name': {'color': 'blue'}, 'programname': {'color': 'cyan'}, } levelstyles = { 'spam': {'color': 'green', 'faint': True}, 'debug': {'color': 'green', 'bold': True}, 'verbose': {'color': 'blue', 'bold': True}, 'info': {}, 'notice': {'color': 'magenta', 'bold': True}, 'warning': {'color': 'yellow', 'bold': True}, 'success': {'color': 'green', 'bold': True}, 'error': {'color': 'red', 'bold': True}, 'critical': {'color': 'white', 'background': 'red', 'bold': True} } logger.addHandler(logging.StreamHandler()) # if log folder doesnt exist, exit, "--init" creates it if not os.path.exists(log_folder): logger.error( "Please initialise first by running 'sudo openpyn --init', then start using 'openpyn' without sudo") return 1 # Add another rotating handler to log to .log files # fix permissions if needed for attempt in range(2): try: file_handler = logging.handlers.TimedRotatingFileHandler( log_folder + '/openpyn.log', when='W0', interval=4) file_handler_formatter = logging.Formatter(log_format) file_handler.setFormatter(file_handler_formatter) logger.addHandler(file_handler) except PermissionError: root.verify_root_access( "Root access needed to set permissions of {}/openpyn.log".format(log_folder)) subprocess.run("sudo chmod 777 {}/openpyn.log".format(log_folder).split()) subprocess.run("sudo chmod 777 {}/openpyn-notifications.log".format(log_folder).split()) else: break # In this case only log messages originating from this logger will show up on the terminal. coloredlogs.install(level="verbose", logger=logger, fmt=log_format, level_styles=levelstyles, field_styles=fieldstyles) stats = True if sys.__stdin__.isatty(): logger.debug("Interactive") else: logger.addHandler(logging.StreamHandler(sys.stdout)) logger.setLevel(logging.WARNING) logger.debug("Non-Interactive") stats = False port = "udp" if tcp: port = "tcp" detected_os = sys.platform if detected_os == "linux": if subprocess.check_output(["/bin/uname", "-o"]).decode(sys.stdout.encoding).strip() == "ASUSWRT-Merlin": force_fw_rules = False silent = True skip_dns_patch = True if openvpn_options: openvpn_options += " " + "--syslog openpyn" else: openvpn_options = "--syslog openpyn" logger.debug(openvpn_options) elif os.path.exists("/etc/openwrt_release"): force_fw_rules = False silent = True skip_dns_patch = True nvram = None else: nvram = None elif detected_os == "win32": logger.error("Are you even a l33t mate? Try GNU/Linux") return 1 # check if dependencies are installed if shutil.which("openvpn") is None or shutil.which("wget") is None or shutil.which("unzip") is None: # In case of Debian Sid where "openvpn" is only in root's PATH, don't error out try: root_access = root.verify_root_access( "Sudo credentials required to check if 'openvpn' is available in root's PATH") if root_access is False: root.obtain_root_access() subprocess.check_output(["sudo", "which", "wget"]) subprocess.check_output(["sudo", "which", "unzip"]) # subprocess.check_output(["sudo", "which", "openvpn"]) except subprocess.CalledProcessError: logger.error("Please Install 'openvpn' 'wget' 'unzip' first") return 1 elif daemon: if detected_os != "linux": logger.error("Daemon mode is only available in GNU/Linux distros") return 1 if not root.verify_running_as_root(): logger.error("Please run '--daemon' or '-d' mode with sudo") return 1 openpyn_options = "" # if only positional argument used if country_code is None and server is None: country_code = country # consider the positional arg e.g "us" same as "-c us" # if either "-c" or positional arg f.e "au" is present if country_code: if len(country_code) > 2: # full country name # get the country_code from the full name country_code = api.get_country_code(full_name=country_code) country_code = country_code.lower() openpyn_options += country_code elif server: openpyn_options += " --server " + server if area: openpyn_options += " --area " + area if tcp: openpyn_options += " --tcp" if max_load: openpyn_options += " --max-load " + str(max_load) if top_servers: openpyn_options += " --top-servers " + str(top_servers) if pings: openpyn_options += " --pings " + str(pings) if force_fw_rules: openpyn_options += " --force-fw-rules" if p2p: openpyn_options += " --p2p" if dedicated: openpyn_options += " --dedicated" if double_vpn: openpyn_options += " --double" if tor_over_vpn: openpyn_options += " --tor" if anti_ddos: openpyn_options += " --anti-ddos" if netflix: openpyn_options += " --netflix" if test: openpyn_options += " --test" if internally_allowed: open_ports = "" for port_number in internally_allowed: open_ports += " " + port_number openpyn_options += " --allow" + open_ports if skip_dns_patch: openpyn_options += " --skip-dns-patch" if nvram: openpyn_options += " --nvram " + str(nvram) if openvpn_options: openpyn_options += " --openvpn-options '" + openvpn_options + "'" # logger.debug(openpyn_options) if subprocess.check_output(["/bin/uname", "-o"]).decode(sys.stdout.encoding).strip() == "ASUSWRT-Merlin": initd.update_service(openpyn_options, run=True) elif os.path.exists("/etc/openwrt_release"): initd.update_service(openpyn_options, run=True) else: systemd.update_service(openpyn_options, run=True) return 0 elif kill: logger.warning("Killing the running processes") kill_management_client() kill_vpn_processes() # don't touch iptable rules kill_openpyn_process() elif kill_flush: firewall.clear_fw_rules() # also clear iptable rules # if --allow present, allow those ports internally logger.info("Re-enabling ipv6") firewall.manage_ipv6(disable=False) if internally_allowed: network_interfaces = get_network_interfaces() firewall.internally_allow_ports(network_interfaces, internally_allowed) kill_management_client() kill_vpn_processes() kill_openpyn_process() elif update: update_config_files() # a hack to list all countries and their codes when no arg supplied with "-l" elif list_servers != 'nope': # means "-l" supplied if list_servers is None: # no arg given with "-l" if p2p or dedicated or double_vpn or tor_over_vpn or anti_ddos or netflix: # show the special servers in all countries display_servers( list_servers="all", port=port, area=area, p2p=p2p, dedicated=dedicated, double_vpn=double_vpn, tor_over_vpn=tor_over_vpn, anti_ddos=anti_ddos, netflix=netflix, location=location) else: api.list_all_countries() # if a country code is supplied give details about that country only. else: # if full name of the country supplied get country_code if len(list_servers) > 2: list_servers = api.get_country_code(full_name=list_servers) display_servers( list_servers=list_servers, port=port, area=area, p2p=p2p, dedicated=dedicated, double_vpn=double_vpn, tor_over_vpn=tor_over_vpn, anti_ddos=anti_ddos, netflix=netflix, location=location) # only clear/touch FW Rules if "-f" used elif force_fw_rules: firewall.clear_fw_rules() # check if OpenVPN config files exist if not download them. check_config_files() # if only positional argument used if country_code is None and server is None: country_code = country # consider the positional arg e.g "us" same as "-c us" # if either "-c" or positional arg f.e "au" is present if country_code: # ask for and store credentials if not present, skip if "--test" if not test: if credentials.check_credentials() is False: credentials.save_credentials() if len(country_code) > 2: # full country name # get the country_code from the full name country_code = api.get_country_code(full_name=country_code) country_code = country_code.lower() # keep trying to connect to new servers for tries in range(3): # pylint: disable=W0612 better_servers_list = find_better_servers( country_code, area, max_load, top_servers, tcp, p2p, dedicated, double_vpn, tor_over_vpn, anti_ddos, netflix, location, stats) pinged_servers_list = ping_servers(better_servers_list, pings, stats) chosen_servers = choose_best_servers(pinged_servers_list, stats) # connect to chosen_servers, if one fails go to next for aserver in chosen_servers: if stats: print(Style.BRIGHT + Fore.BLUE + "Out of the Best Available Servers, Chose", (Fore.GREEN + aserver + Fore.BLUE) + "\n") # if "-f" used apply firewall rules if force_fw_rules: network_interfaces = get_network_interfaces() vpn_server_ip = get_vpn_server_ip(aserver, port) firewall.apply_fw_rules(network_interfaces, vpn_server_ip, skip_dns_patch) if internally_allowed: firewall.internally_allow_ports(network_interfaces, internally_allowed) if nvram: # TODO return 0 on success else 1 in asus.run() asus.run(aserver, country_code, nvram, "All", "adaptive", "Strict", tcp, test) logger.success("SAVED SERVER " + aserver + " ON PORT " + port + " TO NVRAM") return(connect(aserver, port, silent, test, skip_dns_patch, openvpn_options)) elif server: # ask for and store credentials if not present, skip if "--test" if not test: if credentials.check_credentials() is False: credentials.save_credentials() server = server.lower() # if "-f" used apply firewall rules if force_fw_rules: network_interfaces = get_network_interfaces() vpn_server_ip = get_vpn_server_ip(server, port) firewall.apply_fw_rules(network_interfaces, vpn_server_ip, skip_dns_patch) if internally_allowed: firewall.internally_allow_ports(network_interfaces, internally_allowed) if nvram: asus.run(server, country_code, nvram, "All", "adaptive", "Strict", tcp, test) logger.success("SAVED SERVER " + server + " ON PORT " + port + " TO NVRAM") return 0 for i in range(20): # pylint: disable=W0612 return(connect(server, port, silent, test, skip_dns_patch, openvpn_options)) else: logger.info('To see usage options type: "openpyn -h" or "openpyn --help"') return 0 # if everything went ok
def apply_fw_rules(interfaces_details: List, vpn_server_ip: str, skip_dns_patch: bool) -> None: root.verify_root_access("Root access needed to modify 'iptables' rules") # Empty the INPUT and OUTPUT chain of any current rules subprocess.check_call(["sudo", "iptables", "-F", "OUTPUT"]) subprocess.check_call(["sudo", "iptables", "-F", "INPUT"]) apply_dns_rules() logger.notice("Temporarily disabling ipv6 to prevent leakage") manage_ipv6(disable=True) # Allow all traffic out over the vpn tunnel # except for DNS, which is handled by systemd-resolved script # NOTE: that def helped with leaky DNS queries, nothing in wireshark too # weird that ping ya.ru was showing "operation not permitted" subprocess.check_call([ "sudo", "iptables", "-A", "OUTPUT", "-o", "tun+", # "-p", "all", "-d", "0.0.0.0/0", "!", "--dport", "53", "-p", "all", "-d", "0.0.0.0/0", "-j", "ACCEPT" ]) # accept traffic that comes through tun that you connect to subprocess.check_call( "sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED\ -i tun+ -j ACCEPT".split()) for interface in interfaces_details: if len(interface) != 3: continue # TODO what does that mean? iname = interface[0] if len(iname) == 0: print("WARNING: empty {}".format(interface)) continue # allow access to vpn_server_ip subprocess.check_call([ "sudo", "iptables", "-A", "OUTPUT", "-o", iname, "-d", vpn_server_ip, "-j", "ACCEPT" ]) # talk to the vpnServer ip to connect to it subprocess.check_call([ "sudo", "iptables", "-A", "INPUT", "-m", "conntrack", "--ctstate", "ESTABLISHED,RELATED", "-i", iname, "-s", vpn_server_ip, "-j", "ACCEPT" ]) # allow access to internal ip range # print("internal ip with range", interface[2]) subprocess.check_call([ "sudo", "iptables", "-A", "OUTPUT", "-o", iname, "-d", interface[2], "-j", "ACCEPT" ]) subprocess.check_call([ "sudo", "iptables", "-A", "INPUT", "-m", "conntrack", "--ctstate", "ESTABLISHED,RELATED", "-i", iname, "-s", interface[2], "-j", "ACCEPT" ]) # Allow loopback traffic subprocess.check_call("sudo iptables -A INPUT -i lo -j ACCEPT".split()) subprocess.check_call("sudo iptables -A OUTPUT -o lo -j ACCEPT".split()) # best practice, stops spoofing subprocess.check_call( "sudo iptables -A INPUT -s 127.0.0.0/8 -j DROP".split()) # Default action if no other rules match subprocess.check_call("sudo iptables -P OUTPUT DROP".split()) subprocess.check_call("sudo iptables -P INPUT DROP".split()) return
def apply_dns_rules(): root.verify_root_access("Root access needed to modify 'iptables' rules") for ndns in NORDVPN_DNS: do_dns("lo", ndns, "ACCEPT") do_dns("tun+", ndns, "ACCEPT")
def connect(server, port, silent, test, skip_dns_patch, openvpn_options, server_provider="nordvpn"): detected_os = sys.platform if server_provider == "nordvpn": if port == "tcp": folder = "ovpn_tcp/" else: folder = "ovpn_udp/" vpn_config_file = __basefilepath__ + "files/" + folder + server +\ ".nordvpn.com." + port + ".ovpn" # print("CONFIG FILE", vpn_config_file) if os.path.isfile(vpn_config_file) is False: print( Fore.RED + "VPN configuration file", vpn_config_file, "doesn't exist, don't worry running 'openpyn --update' for you :)" + Fore.BLUE) time.sleep(6) update_config_files() elif server_provider == "ipvanish": vpn_config_file = __basefilepath__ + "files/" + "ipvanish/" + server # print("ipvanish") if test: print( "Simulation end reached, openpyn would have connected to Server:" + Fore.GREEN, server, Fore.BLUE + "on port:" + Fore.GREEN, port, Fore.BLUE + "with 'silent' mode:" + Fore.GREEN, silent) print(Style.RESET_ALL) sys.exit(1) kill_vpn_processes() # kill existing openvpn processes # kill_management_client() print(Fore.BLUE + "CONNECTING TO SERVER" + Fore.GREEN, server, Fore.BLUE + "ON PORT", Fore.GREEN + port + Fore.BLUE) root_access = root.verify_root_access( Fore.GREEN + "Sudo credentials required to run 'openvpn'" + Fore.BLUE) if root_access is False: root.obtain_root_access() if not silent: # notifications Don't work with 'sudo' if detected_os == "linux" and root.running_with_sudo(): print( Fore.RED + "Desktop notifications don't work when using 'sudo', run without it, " + "when asked, provide the sudo credentials" + Fore.BLUE) else: subprocess.Popen("openpyn-management".split()) if detected_os == "linux": resolvconf_exists = os.path.isfile("/sbin/resolvconf") # resolvconf_exists = False else: resolvconf_exists = False skip_dns_patch = True if not openvpn_options: openvpn_options = "" if resolvconf_exists is True and skip_dns_patch is False: # Debian Based OS + do DNS patching # tunnel dns throught vpn by changing /etc/resolv.conf using # "update-resolv-conf.sh" to change the dns servers to NordVPN's. try: print( "Your OS'" + Fore.GREEN + detected_os + Fore.BLUE + "' Does have '/sbin/resolvconf'", "using it to update DNS Resolver Entries") print(Style.RESET_ALL) if silent: subprocess.run([ "sudo", "openvpn", "--redirect-gateway", "--auth-retry", "nointeract", "--config", vpn_config_file, "--auth-user-pass", __basefilepath__ + "credentials", "--script-security", "2", "--up", __basefilepath__ + "scripts/update-resolv-conf.sh", "--down", __basefilepath__ + "scripts/update-resolv-conf.sh" ] + openvpn_options.split(), check=True) else: # print(openvpn_options) subprocess.run([ "sudo", "openvpn", "--redirect-gateway", "--auth-retry", "nointeract", "--config", vpn_config_file, "--auth-user-pass", __basefilepath__ + "credentials", "--script-security", "2", "--up", __basefilepath__ + "scripts/update-resolv-conf.sh", "--down", __basefilepath__ + "scripts/update-resolv-conf.sh", "--management", "127.0.0.1", "7015", "--management-up-down" ] + openvpn_options.split(), check=True) except subprocess.CalledProcessError as openvpn_err: # print(openvpn_err.output) if 'Error opening configuration file' in str(openvpn_err.output): print("Error opening configuration file", vpn_config_file, "Make Sure it exists, run 'openpyn --update'") sys.exit() except KeyboardInterrupt: print('\nShutting down safely, please wait until process exits\n') sys.exit() except PermissionError: # needed cause complains when killing sudo process sys.exit() else: # If not Debian Based or skip_dns_patch # if skip_dns_patch, do not touch etc/resolv.conf if skip_dns_patch is False: print( "Your OS", Fore.GREEN + detected_os + Fore.BLUE, "Does not have" + Fore.GREEN + " '/sbin/resolvconf':\n" + Fore.BLUE + "Manually Applying Patch to Tunnel DNS Through" + "The VPN Tunnel By Modifying" + Fore.GREEN + "' /etc/resolv.conf'") print(Style.RESET_ALL) subprocess.call( ["sudo", __basefilepath__ + "scripts/manual-dns-patch.sh"]) else: print(Fore.RED + "Not Modifying /etc/resolv.conf, DNS traffic", "likely won't go through the encrypted tunnel") print(Style.RESET_ALL) try: # pylint: disable=R1702 if silent: if detected_os == "linux": if subprocess.check_output(["/bin/uname", "-o"]).decode( sys.stdout.encoding).strip() == "ASUSWRT-Merlin": # make sure module is loaded if os.popen( "test ! -c /dev/net/tun && echo 0 || echo 1" ).read()[0:-1] == '0': subprocess.call("modprobe tun", shell=True) if os.popen( "test ! -c /dev/net/tun && echo 0 || echo 1" ).read()[0:-1] == '0': print( Style.BRIGHT + Fore.RED + "Cannot open TUN/TAP dev /dev/net/tun: No such file or directory" ) print(Style.RESET_ALL) sys.exit(0) subprocess.run([ "sudo", "openvpn", "--redirect-gateway", "--auth-retry", "nointeract", "--config", vpn_config_file, "--auth-user-pass", __basefilepath__ + "credentials" ] + openvpn_options.split(), check=True) else: subprocess.run([ "sudo", "openvpn", "--redirect-gateway", "--auth-retry", "nointeract", "--config", vpn_config_file, "--auth-user-pass", __basefilepath__ + "credentials", "--management", "127.0.0.1", "7015", "--management-up-down" ] + openvpn_options.split(), check=True) except subprocess.CalledProcessError as openvpn_err: # print(openvpn_err.output) if 'Error opening configuration file' in str(openvpn_err.output): print("Error opening configuration file", vpn_config_file, "Make Sure it exists, run 'openpyn --update'") sys.exit() except KeyboardInterrupt: print('\nShutting down safely, please wait until process exits\n') sys.exit() except PermissionError: # needed cause complains when killing sudo process sys.exit()
def connect(server, port, daemon, test, skip_dns_patch, server_provider="nordvpn"): if server_provider == "nordvpn": vpn_config_file = "/usr/share/openpyn/files/" + server + ".nordvpn.com."\ + port + ".ovpn" # print("CONFIG FILE", vpn_config_file) elif server_provider == "ipvanish": vpn_config_file = "/usr/share/openpyn/files/ipvanish/" + server # print("ipvanish") if test: print( "Simulation end reached, openpyn would have connected to Server:" + Fore.GREEN, server, Fore.BLUE + " on port:" + Fore.GREEN, port, Fore.BLUE + " with 'daemon' mode:" + Fore.GREEN, daemon) sys.exit(1) kill_vpn_processes() # kill existing openvpn processes # kill_management_client() print(Fore.BLUE + "CONNECTING TO SERVER" + Fore.GREEN, server, Fore.BLUE + "ON PORT", Fore.GREEN + port + Fore.BLUE) root_access = root.verify_root_access( Fore.GREEN + "Sudo credentials required to run 'openvpn'" + Fore.BLUE) if root_access is False: root.obtain_root_access() # notifications Don't work with 'sudo' if root.running_with_sudo(): print( Fore.RED + "Desktop notifications don't work when using 'sudo', run without it, " + "when asked, provide the sudo credentials" + Fore.BLUE) else: subprocess.Popen("openpyn-management".split()) resolvconf_exists = os.path.isfile("/sbin/resolvconf") # resolvconf_exists = False detected_os = platform.linux_distribution()[0] if resolvconf_exists is True and skip_dns_patch is False: # Debian Based OS + do DNS patching # tunnel dns throught vpn by changing /etc/resolv.conf using # "update-resolv-conf.sh" to change the dns servers to NordVPN's. if daemon: subprocess.Popen([ "sudo", "openvpn", "--redirect-gateway", "--auth-retry", "nointeract", "--config", vpn_config_file, "--auth-user-pass", "/usr/share/openpyn/credentials", "--script-security", "2", "--up", "/usr/share/openpyn/update-resolv-conf.sh", "--down", "/usr/share/openpyn/update-resolv-conf.sh", "--daemon", "--management", "127.0.0.1", "7015", "--management-up-down" ]) print("Started 'openvpn' in" + Fore.GREEN + "--daemon" + Fore.BLUE + "mode") else: try: print( "Your OS'" + Fore.GREEN + detected_os + Fore.BLUE + "' Does have '/sbin/resolvconf'", "using it to update DNS Resolver Entries") print(Style.RESET_ALL) subprocess.call( "sudo openvpn --redirect-gateway --auth-retry nointeract" + " --config " + vpn_config_file + " --auth-user-pass \ /usr/share/openpyn/credentials --script-security 2 --up \ /usr/share/openpyn/update-resolv-conf.sh --down \ /usr/share/openpyn/update-resolv-conf.sh \ --management 127.0.0.1 7015 --management-up-down", shell=True) except (KeyboardInterrupt) as err: print( '\nShutting down safely, please wait until process exits\n' ) sys.exit() except PermissionError: # needed cause complains when killing sudo process sys.exit() else: # If not Debian Based or skip_dns_patch # if skip_dns_patch, do not touch etc/resolv.conf if skip_dns_patch is False: print( "Your OS", Fore.GREEN + detected_os + Fore.BLUE, "Does not have" + Fore.GREEN + " '/sbin/resolvconf':\n" + Fore.BLUE + "Manually Applying Patch to Tunnel DNS Through" + "The VPN Tunnel By Modifying" + Fore.GREEN + "' /etc/resolv.conf'") apply_dns_patch = subprocess.call( ["sudo", "/usr/share/openpyn/manual-dns-patch.sh"]) else: print(Fore.RED + "Not Modifying /etc/resolv.conf, DNS traffic", "likely won't go through the encrypted tunnel") print(Style.RESET_ALL) if daemon: subprocess.Popen([ "sudo", "openvpn", "--redirect-gateway", "--auth-retry", "nointeract", "--config", vpn_config_file, "--auth-user-pass", "/usr/share/openpyn/credentials", "--daemon", "--management", "127.0.0.1", "7015", "--management-up-down" ]) print("Started 'openvpn' in --daemon mode") else: try: subprocess.call(( "sudo openvpn --redirect-gateway --auth-retry nointeract " + "--config " + vpn_config_file + " --auth-user-pass " + "/usr/share/openpyn/credentials --management 127.0.0.1 7015 " + "--management-up-down").split()) except (KeyboardInterrupt) as err: print( '\nShutting down safely, please wait until process exits\n' ) sys.exit() except PermissionError: # needed cause complains when killing sudo process sys.exit()
def apply_allowed_port_rules(interfaces_details: List, allowed_ports_config: List) -> bool: if not validate_allowed_ports_json(allowed_ports_config): return False root.verify_root_access("Root access needed to pre load fire wall rules") DEFAULT_PORT_CONFIG = { "internal": True, "protocol": "tcp", "allowed_ip_range": None } # Merge default config with existing config allowed_ports_config = [{ **DEFAULT_PORT_CONFIG, **port_config } for port_config in allowed_ports_config] ip_table_rules = [] for port_config in allowed_ports_config: # Get perms for the connection type port_protocol_permiatations = [] if port_config["protocol"] in ["tcp", "both"]: port_protocol_permiatations.append("tcp") if port_config["protocol"] in ["udp", "both"]: port_protocol_permiatations.append("udp") # Create the flags for the port range / port if "-" in str(port_config["port"]): port_range = port_config["port"].split("-") port_flag = "--match multiport --dports {0}:{1}".format( *port_range) else: port_flag = "--dport {0}".format(port_config["port"]) for interface, port_type in itertools.product( interfaces_details, port_protocol_permiatations): # Skip any tunnel interfaces that might be invalid if len(interface) != 3 or "tun" in interface[0]: continue ip_flag = "" ip_ranges = [] if port_config["internal"]: ip_ranges.append(interface[2]) if port_config["allowed_ip_range"] is not None: if isinstance(port_config["allowed_ip_range"], list): ip_ranges += port_config["allowed_ip_range"] else: ip_ranges.append(port_config["allowed_ip_range"]) if ip_ranges != []: ip_flag = " -s " + ",".join(ip_ranges) ip_table_rules.append( "sudo -u {user} iptables -A INPUT -p {port_type} {port_flag} -i {interface}{ip_flag} -j ACCEPT" .format(user=sudo_user, port_type=port_type, port_flag=port_flag, interface=interface[0], ip_flag=ip_flag)) for rule in ip_table_rules: subprocess.call(rule.split(" ")) return True
def flush_input_output() -> None: root.verify_root_access("Root access needed to modify 'iptables' rules") logger.info("Flushing ALL INPUT and OUTPUT Rules") subprocess.call(["sudo", "-u", sudo_user, "iptables", "-F", "OUTPUT"]) subprocess.call(["sudo", "-u", sudo_user, "iptables", "-F", "INPUT"])
def connect(server, port, daemon, test): if test: print( "Simulation end reached, openpyn would have connected to Server:", server, "on port:", port, " with 'daemon' mode:", daemon) sys.exit(1) kill_vpn_processes() # kill existing openvpn processes print("CONNECTING TO SERVER", server, " ON PORT", port) is_root = root.verify_root_access("Root access required to run 'openvpn'") if daemon is True and is_root is False: root.obtain_root_access() resolvconf_exists = os.path.isfile("/sbin/resolvconf") # resolvconf_exists = False detected_os = platform.linux_distribution()[0] if resolvconf_exists: # Debian Based OS # tunnel dns throught vpn by changing /etc/resolv.conf using # "update-resolv-conf.sh" to change the dns servers to NordVPN's. if daemon: subprocess.Popen([ "sudo", "openvpn", "--redirect-gateway", "--config", "/usr/share/openpyn/files/" + server + ".nordvpn.com." + port + ".ovpn", "--auth-user-pass", "/usr/share/openpyn/credentials", "--script-security", "2", "--up", "/usr/share/openpyn/update-resolv-conf.sh", "--down", "/usr/share/openpyn/update-resolv-conf.sh", "--daemon" ]) print("Started 'openvpn' in --daemon mode") else: try: print( "Your OS '" + detected_os + "' Does have '/sbin/resolvconf'", "using it to update DNS Resolver Entries") subprocess.run("sudo openvpn --redirect-gateway --config" + " /usr/share/openpyn/files/" + server + ".nordvpn.com." + port + ".ovpn --auth-user-pass \ /usr/share/openpyn/credentials --script-security 2 --up \ /usr/share/openpyn/update-resolv-conf.sh --down \ /usr/share/openpyn/update-resolv-conf.sh", shell=True) except (KeyboardInterrupt) as err: print( '\nShutting down safely, please wait until process exits\n' ) else: # If not Debian Based print( "Your OS ", detected_os, "Does not have '/sbin/resolvconf': Manually Applying Patch" + " to Tunnel DNS Through The VPN Tunnel By Modifying '/etc/resolv.conf'" ) apply_dns_patch = subprocess.run( ["sudo", "/usr/share/openpyn/manual-dns-patch.sh"]) if daemon: subprocess.Popen([ "sudo", "openvpn", "--redirect-gateway", "--config", "/usr/share/openpyn/files/" + server + ".nordvpn.com." + port + ".ovpn", "--auth-user-pass", "/usr/share/openpyn/credentials", "--daemon" ]) print("Started 'openvpn' in --daemon mode") else: try: subprocess.run("sudo openvpn --redirect-gateway --config" + " /usr/share/openpyn/files/" + server + ".nordvpn.com." + port + ".ovpn --auth-user-pass \ /usr/share/openpyn/credentials", shell=True) except (KeyboardInterrupt) as err: print( '\nShutting down safely, please wait until process exits\n' )
def connect(server: str, port: str, silent: bool, test: bool, skip_dns_patch: bool, openvpn_options: str, server_provider="nordvpn") -> bool: detected_os = sys.platform if server_provider == "nordvpn": if port == "tcp": folder = "ovpn_tcp/" else: folder = "ovpn_udp/" vpn_config_file = __basefilepath__ + "files/" + folder + server +\ ".nordvpn.com." + port + ".ovpn" # logger.debug("CONFIG FILE %s", vpn_config_file) if os.path.isfile(vpn_config_file) is False: logger.notice("VPN configuration file %s doesn't exist, \ don't worry running 'openpyn --update' for you :)", vpn_config_file) time.sleep(6) update_config_files() elif server_provider == "ipvanish": vpn_config_file = __basefilepath__ + "files/" + "ipvanish/" + server # logger.debug("ipvanish") if test: logger.success("Simulation end reached, \ openpyn would have connected to server: " + server + " on port: " + port + " with 'silent' mode: " + str(silent).lower()) return 0 kill_vpn_processes() # kill existing OpenVPN processes kill_management_client() logger.success("CONNECTING TO SERVER " + server + " ON PORT " + port) root_access = root.verify_root_access("Sudo credentials required to run 'openvpn'") if root_access is False: root.obtain_root_access() if not silent: # notifications Don't work with 'sudo' if detected_os == "linux" and root.running_with_sudo(): logger.warning("Desktop notifications don't work when using 'sudo', run without it, \ when asked, provide the sudo credentials") subprocess.Popen("openpyn-management".split()) else: subprocess.Popen("openpyn-management --do-notify".split()) use_systemd_resolved = False use_resolvconf = False if detected_os == "linux": if subprocess.check_output(["/bin/uname", "-o"]).decode(sys.stdout.encoding).strip() == "ASUSWRT-Merlin": skip_dns_patch = True elif os.path.exists("/etc/openwrt_release"): skip_dns_patch = True else: use_systemd_resolved = uses_systemd_resolved() use_resolvconf = os.path.isfile("/sbin/resolvconf") else: use_systemd_resolved = False use_resolvconf = False skip_dns_patch = True if not openvpn_options: openvpn_options = "" if (use_systemd_resolved or use_resolvconf) and skip_dns_patch is False: # Debian Based OS + do DNS patching try: route_up_script = __basefilepath__ + "scripts/docker-shim.sh" if use_systemd_resolved: openvpn_options += " " + "--dhcp-option DOMAIN-ROUTE ." up_down_script = __basefilepath__ + "scripts/update-systemd-resolved.sh" logger.success("Your OS '%s' has systemd-resolve running, \ using it to update DNS Resolver Entries", detected_os) elif use_resolvconf: # tunnel DNS through VPN by changing /etc/resolv.conf using # "update-resolv-conf.sh" to change the DNS servers to NordVPN's. up_down_script = __basefilepath__ + "scripts/update-resolv-conf.sh" logger.success("Your OS '%s' Does have '/sbin/resolvconf', \ using it to update DNS Resolver Entries", detected_os) else: raise RuntimeError("Should not happen") def run_openvpn(*args): cmdline = [ "sudo", "openvpn", "--redirect-gateway", "--auth-retry", "nointeract", "--config", vpn_config_file, "--auth-user-pass", __basefilepath__ + "credentials", "--script-security", "2", "--route-up", route_up_script, "--up", up_down_script, "--down", up_down_script, "--down-pre", *args, ] + openvpn_options.split() subprocess.run(cmdline, check=True) if silent: run_openvpn() else: run_openvpn( "--management", "127.0.0.1", "7015", "--management-up-down", ) except subprocess.CalledProcessError as openvpn_err: # logger.debug(openvpn_err.output) if "Error opening configuration file" in str(openvpn_err.output): logger.error( "Error opening config file %s, make sure it exists, run 'openpyn --update'", vpn_config_file) return 1 except KeyboardInterrupt: logger.info("Shutting down safely, please wait until process exits") return 0 except PermissionError: # needed cause complains when killing sudo process return 0 else: # If not Debian Based or skip_dns_patch # if skip_dns_patch, do not touch etc/resolv.conf if skip_dns_patch is False: logger.warning("Your OS '%s' Does not have '/sbin/resolvconf'", detected_os) logger.notice( "Manually applying patch to tunnel DNS through the VPN tunnel by modifying '/etc/resolv.conf'") subprocess.call(["sudo", __basefilepath__ + "scripts/manual-dns-patch.sh"]) else: logger.warning( "Not modifying '/etc/resolv.conf', DNS traffic likely won't go through the encrypted tunnel") try: # pylint: disable=R1702 if silent: if detected_os == "linux": if subprocess.check_output(["/bin/uname", "-o"]).decode(sys.stdout.encoding).strip() == "ASUSWRT-Merlin": # make sure module is loaded if os.popen("test ! -c /dev/net/tun && echo 0 || echo 1").read()[0:-1] == '0': subprocess.call("modprobe tun", shell=True) if os.popen("test ! -c /dev/net/tun && echo 0 || echo 1").read()[0:-1] == '0': logger.error( "Cannot open TUN/TAP dev /dev/net/tun: No such file or directory") return 1 subprocess.run( ["sudo", "openvpn", "--redirect-gateway", "--auth-retry", "nointeract", "--config", vpn_config_file, "--auth-user-pass", __basefilepath__ + "credentials"] + openvpn_options.split(), check=True) else: subprocess.run( ["sudo", "openvpn", "--redirect-gateway", "--auth-retry", "nointeract", "--config", vpn_config_file, "--auth-user-pass", __basefilepath__ + "credentials", "--management", "127.0.0.1", "7015", "--management-up-down"] + openvpn_options.split(), check=True) except subprocess.CalledProcessError as openvpn_err: # logger.debug(openvpn_err.output) if 'Error opening configuration file' in str(openvpn_err.output): logger.error( "Error opening config file %s, make sure it exists, run 'openpyn --update'", vpn_config_file) return 1 except KeyboardInterrupt: logger.info('Shutting down safely, please wait until process exits') return 0 except PermissionError: # needed cause complains when killing sudo process return 0
def apply_fw_rules(interfaces_details: List, vpn_server_ips: List, skip_dns_patch: bool) -> None: root.verify_root_access("Root access needed to modify 'iptables' rules") apply_dns_rules() logger.notice("Temporarily disabling ipv6 to prevent leakage") manage_ipv6(disable=True) # allow all traffic out over the VPN tunnel # except for DNS, which is handled by systemd-resolved script # NOTE: that def helped with leaky DNS queries, nothing in Wireshark too # weird that ping ya.ru was showing "operation not permitted" subprocess.check_call([ "sudo", "-u", sudo_user, "iptables", "-A", "OUTPUT", "-o", "tun+", # "-p", "all", "-d", "0.0.0.0/0", "!", "--dport", "53", "-p", "all", "-d", "0.0.0.0/0", "-j", "ACCEPT" ]) # accept traffic that comes through tun that you connect to subprocess.check_call([ "sudo", "-u", sudo_user, "iptables", "-A", "INPUT", "-m", "conntrack", "--ctstate", "ESTABLISHED,RELATED", "-i", "tun+", "-j", "ACCEPT" ]) for interface in interfaces_details: if len(interface) != 3: continue # TODO what does that mean? iname = interface[0] if not iname: print("WARNING: empty {}".format(interface)) continue # Allow currently chosen vpn_server_ips for vpn_server_ip in vpn_server_ips: # allow access to vpn_server_ip subprocess.check_call([ "sudo", "-u", sudo_user, "iptables", "-A", "OUTPUT", "-o", iname, "-d", vpn_server_ip, "-j", "ACCEPT" ]) # talk to the vpn_server_ip to connect to it subprocess.check_call([ "sudo", "-u", sudo_user, "iptables", "-A", "INPUT", "-m", "conntrack", "--ctstate", "ESTABLISHED,RELATED", "-i", iname, "-s", vpn_server_ip, "-j", "ACCEPT" ]) # allow access to internal IP range # print("internal IP with range", interface[2]) subprocess.check_call([ "sudo", "-u", sudo_user, "iptables", "-A", "OUTPUT", "-o", iname, "-d", interface[2], "-j", "ACCEPT" ]) subprocess.check_call([ "sudo", "-u", sudo_user, "iptables", "-A", "INPUT", "-m", "conntrack", "--ctstate", "ESTABLISHED,RELATED", "-i", iname, "-s", interface[2], "-j", "ACCEPT" ]) # allow loopback traffic subprocess.check_call([ "sudo", "-u", sudo_user, "iptables", "-A", "INPUT", "-i", "lo", "-j", "ACCEPT" ]) subprocess.check_call([ "sudo", "-u", sudo_user, "iptables", "-A", "OUTPUT", "-o", "lo", "-j", "ACCEPT" ]) # best practice, stops spoofing subprocess.check_call([ "sudo", "-u", sudo_user, "iptables", "-A", "INPUT", "-s", "127.0.0.0/8", "-j", "DROP" ]) # default action if no other rules match subprocess.check_call( ["sudo", "-u", sudo_user, "iptables", "-P", "OUTPUT", "DROP"]) subprocess.check_call( ["sudo", "-u", sudo_user, "iptables", "-P", "INPUT", "DROP"])