Files
nordvpn/nord-checker.py

190 lines
8.3 KiB
Python

import os
import subprocess
import random
import time
import datetime
import requests
import re
import sys
from psql_utils import connect_to_database, update_or_insert_ip, should_skip_file
from openvpn_manager import establish_vpn_connection, is_vpn_active, disconnect_vpn, get_external_ip
from ovpn_template import DEFAULT_OVPN_CONFIG
# Variables
CREDENTIAL_FILE = "nord.creds"
TEMP_OVPN_DIR = "/tmp/ovpn_configs" # Directory for temporary OVPN files
LOG_FILE = "openvpn.log" # This is not used anymore
RESULT_FILE = "vpnlist.txt"
PING_URL = "https://health.ext.ben.io/ping/a1a55915-1051-48ed-bd13-ea3f1717a3ee"
# NordVPN API endpoint (updated to get all servers)
NORDVPN_API_URL = "https://api.nordvpn.com/v1/servers?limit=16384"
# Define DEBUG_MODE
DEBUG_MODE = False
if len(sys.argv) > 1 and sys.argv[1] == "-d":
DEBUG_MODE = True
def debug_print(message):
if DEBUG_MODE:
print(f"{datetime.datetime.now()} + [Debug Mode]:{message}")
try:
requests.get(PING_URL, auth=('local', 'local'), timeout=10)
print(f"{datetime.datetime.now()} [Main Script]: Ping sent to: %s" % PING_URL)
except requests.RequestException as e:
# Log ping failure here...
print(f"{datetime.datetime.now()} [Main Script]: Ping failed: %s" % e)
# Function to check if the script is already running
def is_script_running():
"""Checks if another instance of the script is already running."""
try:
# Use pgrep to find processes with the exact command line of the script
command_line = f"/usr/bin/python3 {os.path.abspath(__file__)} >> /var/log/nord-checker.log 2>&1" # Include redirection
process = subprocess.run(["pgrep", "-f", command_line],
capture_output=True, text=True)
# If pgrep finds any matches, it means the script is already running
if process.returncode == 0 and process.stdout:
# Get the PIDs of the running instances
pids = [int(pid) for pid in process.stdout.strip().split("\n")]
# Exclude the current process's PID
pids = [pid for pid in pids if pid != os.getpid()]
# Only quit if there are two or more other instances running
if len(pids) >= 2:
for pid in pids:
try:
# Get the CMD value using ps
process_info = subprocess.check_output(["ps", "-o", "cmd=", "-p", str(pid)], text=True).strip()
print(f"{datetime.datetime.now()} [Main Script]: Another instance of the script is already running (PID: {pid}): {process_info}")
except subprocess.CalledProcessError:
print(f"{datetime.datetime.now()} [Main Script]: Another instance of the script is already running (PID: {pid}), but unable to retrieve process information.")
return True
except Exception as e:
print(f"{datetime.datetime.now()} [Main Script]: Error checking for running instances: {e}")
return False
# Function to get a list of all NordVPN servers
def get_nordvpn_servers():
"""Fetches a list of all NordVPN servers from the API."""
try:
response = requests.get(NORDVPN_API_URL)
response.raise_for_status() # Raise an exception for bad status codes
servers = response.json()
return servers
except requests.exceptions.RequestException as e:
print(f"{datetime.datetime.now()} [Main Script]: Error fetching NordVPN servers: {e}")
return []
# Get current IP before starting (using get_external_ip)
CURRENT_IP = get_external_ip()
if CURRENT_IP is None:
print(f"{datetime.datetime.now()} [Main Script]: Unable to get current IP. Exiting.")
sys.exit(1) # Exit if unable to get current IP
else:
print(f"{datetime.datetime.now()} [Main Script]: Current IP before VPN connections: {CURRENT_IP}")
if is_script_running():
sys.exit(1)
# Terminate any existing OpenVPN connections
try:
result = subprocess.run(["pgrep", "openvpn"], check=True, capture_output=True)
pids = result.stdout.decode().strip().split('\n') # Get PIDs as a list
for pid in pids:
subprocess.run(["kill", pid])
print(f"{datetime.datetime.now()} [Main Script]: Existing OpenVPN connections terminated.")
except subprocess.CalledProcessError:
pass # No openvpn process found
# Connect to PostgreSQL database
conn, cursor = connect_to_database()
# Get the list of recommended NordVPN servers
servers = get_nordvpn_servers()
# Create the temporary directory if it doesn't exist
if not os.path.exists(TEMP_OVPN_DIR):
os.makedirs(TEMP_OVPN_DIR)
# Process each server
server_count = len(servers)
for index, server in enumerate(servers):
# Check if the server uses OpenVPN TCP Dedicated technology
technologies = server.get('technologies', [])
skip_server = any(tech.get('identifier') == 'openvpn_dedicated_tcp' for tech in technologies)
technologies_str = ', '.join([t.get('identifier', 'N/A') for t in technologies]) # Get technologies as a string
if skip_server:
server_name = server.get('hostname')
debug_print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: Skipping OpenVPN TCP Dedicated server. Technologies: {technologies_str}")
continue # Skip to the next server
server_name = server.get('hostname')
server_ip = server.get('station') # Get the server IP address
server_load = server.get('load', 'N/A') # Get server load, default to 'N/A' if not available
server_status = server.get('status', 'N/A') # Get server status, default to 'N/A' if not available
debug_print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: Load: {server_load}, Status: {server_status}, Technologies: {technologies_str} Processing server... ")
# Construct the temporary OVPN filename
ovpn_filename = f"{server_name}.tcp.ovpn"
ovpn_filepath = os.path.join(TEMP_OVPN_DIR, ovpn_filename)
# Check if the server should be skipped based on last check time
if should_skip_file(cursor, ovpn_filename, index+1, server_count, debug_print=debug_print):
debug_print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: Skipping server, checked within the last 3 days.")
continue
# Create temporary OVPN file from template
with open(ovpn_filepath, "w") as f:
f.write(DEFAULT_OVPN_CONFIG.format(server_ip=server_ip, server_cn=server_name))
# Establish VPN connection
connection_successful, connection_message = establish_vpn_connection(
ovpn_filepath, CREDENTIAL_FILE, LOG_FILE, debug_print=debug_print
)
if connection_successful:
print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: {connection_message}")
# Check if the VPN connection is active
try:
subprocess.run(["pgrep", "-f", ovpn_filename], check=True, capture_output=True)
# Check external IP (with validation and retries)
external_ip = get_external_ip()
if external_ip is None:
print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: Unable to get external IP. Skipping this server.")
continue
print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: External IP via VPN: {external_ip}")
# Update or insert into database
update_or_insert_ip(cursor, conn, ovpn_filename, external_ip)
except subprocess.CalledProcessError:
print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: VPN connection failed to establish.")
else:
print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: Error starting OpenVPN connection: {connection_message}")
# Disconnect VPN
disconnect_vpn()
print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: VPN disconnected.")
# Wait for a random time between 5 and 8 seconds
sleep_time = random.randint(2, 4)
print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: Waiting for {sleep_time} seconds before the next connection...")
time.sleep(sleep_time)
print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: Resuming...")
# Clean up the temporary OVPN file
os.remove(ovpn_filepath)
# Close the database connection
cursor.close()
conn.close()