refactor nord-checker to remove file and sql operations.
This commit is contained in:
@@ -5,13 +5,16 @@ import time
|
|||||||
import datetime
|
import datetime
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
from psql_utils import connect_to_database, should_skip_file, update_or_insert_ip
|
||||||
|
from ovpn_downloader import download_and_extract_ovpn_configs
|
||||||
|
from openvpn_manager import establish_vpn_connection, is_vpn_active, disconnect_vpn, get_external_ip
|
||||||
|
|
||||||
|
|
||||||
# Variables
|
# Variables
|
||||||
CREDENTIAL_FILE = "nord.creds"
|
CREDENTIAL_FILE = "nord.creds"
|
||||||
OVPN_DIR = "/root/nordvpn/ovpn_configs/ovpn_tcp"
|
OVPN_DIR = "/root/nordvpn/ovpn_configs/ovpn_tcp"
|
||||||
LOG_FILE = "/root/nordvpn/openvpn.log"
|
LOG_FILE = "/root/nordvpn/openvpn.log"
|
||||||
RESULT_FILE = "vpnlist.txt"
|
RESULT_FILE = "vpnlist.txt"
|
||||||
OVPN_ZIP_URL = "https://downloads.nordcdn.com/configs/archives/servers/ovpn.zip"
|
|
||||||
|
|
||||||
# Check for debug flag
|
# Check for debug flag
|
||||||
DEBUG_MODE = False
|
DEBUG_MODE = False
|
||||||
@@ -29,30 +32,25 @@ print(f"{datetime.datetime.now()} [Main Script]: Current IP before VPN connectio
|
|||||||
# Terminate any existing OpenVPN connections
|
# Terminate any existing OpenVPN connections
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(["pgrep", "openvpn"], check=True, capture_output=True)
|
result = subprocess.run(["pgrep", "openvpn"], check=True, capture_output=True)
|
||||||
pids = result.stdout.decode().strip().split('\n')
|
pids = result.stdout.decode().strip().split('\n') # Get PIDs as a list
|
||||||
for pid in pids:
|
for pid in pids:
|
||||||
subprocess.run(["kill", pid])
|
subprocess.run(["kill", pid])
|
||||||
print(f"{datetime.datetime.now()} [Main Script]: Existing OpenVPN connections terminated.")
|
print(f"{datetime.datetime.now()} [Main Script]: Existing OpenVPN connections terminated.")
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
pass # No openvpn process found
|
pass # No openvpn process found
|
||||||
|
|
||||||
# Download and extract OVPN configurations
|
# Download and extract OVPN configurations (using the function from ovpn_downloader.py)
|
||||||
debug_print(f"{datetime.datetime.now()} [Main Script]: Downloading and extracting OVPN configurations...")
|
debug_print(f"{datetime.datetime.now()} [Main Script]: Downloading and extracting OVPN configurations...")
|
||||||
if not os.path.exists(OVPN_DIR):
|
ovpn_files = download_and_extract_ovpn_configs() # The function now returns the shuffled list
|
||||||
os.makedirs(OVPN_DIR)
|
|
||||||
|
|
||||||
subprocess.run(["curl", "-s", "-L", "-o", f"{OVPN_DIR}/ovpn.zip", OVPN_ZIP_URL])
|
|
||||||
subprocess.run(["unzip", "-q", "-o", f"{OVPN_DIR}/ovpn.zip", "-d", OVPN_DIR])
|
|
||||||
os.remove(f"{OVPN_DIR}/ovpn.zip")
|
|
||||||
|
|
||||||
# Find all OVPN files
|
|
||||||
ovpn_files = [f for f in os.listdir(OVPN_DIR) if f.endswith(".ovpn")]
|
|
||||||
|
|
||||||
total_files = len(ovpn_files)
|
total_files = len(ovpn_files)
|
||||||
|
|
||||||
debug_print(f"{datetime.datetime.now()} [Main Script]: Found {total_files} OVPN files to process.")
|
debug_print(f"{datetime.datetime.now()} [Main Script]: Found {total_files} OVPN files to process.")
|
||||||
debug_print(ovpn_files) # Print the list of OVPN files
|
debug_print(ovpn_files) # Print the list of OVPN files
|
||||||
|
|
||||||
|
# Connect to PostgreSQL database
|
||||||
|
conn, cursor = connect_to_database()
|
||||||
|
|
||||||
# Process each OVPN file
|
# Process each OVPN file
|
||||||
FILE_NUM = 1
|
FILE_NUM = 1
|
||||||
for OVPN_FILE in ovpn_files:
|
for OVPN_FILE in ovpn_files:
|
||||||
@@ -60,12 +58,21 @@ for OVPN_FILE in ovpn_files:
|
|||||||
|
|
||||||
debug_print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Processing OVPN file...")
|
debug_print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Processing OVPN file...")
|
||||||
|
|
||||||
|
# Check if file exists in database and if last check is older than 3 days
|
||||||
|
skip_file = should_skip_file(cursor, OVPN_FILENAME)
|
||||||
|
debug_print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: should_skip_file returned: {skip_file}")
|
||||||
|
|
||||||
|
if skip_file:
|
||||||
|
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Skipping, last check was less than 3 days ago.")
|
||||||
|
FILE_NUM += 1
|
||||||
|
continue
|
||||||
|
|
||||||
# Read credentials
|
# Read credentials
|
||||||
with open(CREDENTIAL_FILE, 'r') as f:
|
with open(CREDENTIAL_FILE, 'r') as f:
|
||||||
username = f.readline().strip()
|
username = f.readline().strip()
|
||||||
password = f.readline().strip()
|
password = f.readline().strip()
|
||||||
|
|
||||||
debug_print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Read credentials from {CREDENTIAL_FILE}")
|
debug_print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Read credentials: username={username}")
|
||||||
|
|
||||||
# Establish VPN connection (capture stdout and stderr)
|
# Establish VPN connection (capture stdout and stderr)
|
||||||
openvpn_command = ["openvpn", "--config", f"{OVPN_DIR}/{OVPN_FILE}",
|
openvpn_command = ["openvpn", "--config", f"{OVPN_DIR}/{OVPN_FILE}",
|
||||||
@@ -97,33 +104,24 @@ for OVPN_FILE in ovpn_files:
|
|||||||
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: VPN connection established.")
|
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: VPN connection established.")
|
||||||
|
|
||||||
# Check external IP (with validation and retries)
|
# Check external IP (with validation and retries)
|
||||||
external_ip = subprocess.check_output(["curl", "-s", "ifconfig.me"]).decode().strip()
|
external_ip = get_external_ip()
|
||||||
|
|
||||||
if not re.match(r"^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$", external_ip):
|
if external_ip is None:
|
||||||
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Invalid response from ifconfig.me, retrying with ifconfig.co")
|
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Unable to get external IP. Skipping this OVPN file.")
|
||||||
external_ip = subprocess.check_output(["curl", "-s", "ifconfig.co"]).decode().strip()
|
continue
|
||||||
|
|
||||||
if not re.match(r"^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$", external_ip):
|
|
||||||
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Invalid response from ifconfig.co, retrying with ipinfo.io/ip")
|
|
||||||
external_ip = subprocess.check_output(["curl", "-s", "ipinfo.io/ip"]).decode().strip()
|
|
||||||
|
|
||||||
if not re.match(r"^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$", external_ip):
|
|
||||||
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Unable to get external IP. Skipping this OVPN file.")
|
|
||||||
continue
|
|
||||||
|
|
||||||
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: External IP via VPN: {external_ip}")
|
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: External IP via VPN: {external_ip}")
|
||||||
|
|
||||||
# Append result to file
|
# Update or insert into database
|
||||||
with open(RESULT_FILE, 'a') as f:
|
update_or_insert_ip(cursor, conn, OVPN_FILENAME, external_ip)
|
||||||
f.write(f"{external_ip} # {OVPN_FILENAME}\n")
|
|
||||||
|
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: VPN connection failed to establish.")
|
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: VPN connection failed to establish.")
|
||||||
|
|
||||||
# Disconnect VPN (replace killall with pgrep and kill)
|
# Disconnect VPN
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(["pgrep", "openvpn"], check=True, capture_output=True)
|
result = subprocess.run(["pgrep", "openvpn"], check=True, capture_output=True)
|
||||||
pids = result.stdout.decode().strip().split('\n') # Get PIDs as a list
|
pids = result.stdout.decode().strip().split('\n')
|
||||||
for pid in pids:
|
for pid in pids:
|
||||||
subprocess.run(["kill", pid])
|
subprocess.run(["kill", pid])
|
||||||
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: VPN disconnected.")
|
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: VPN disconnected.")
|
||||||
@@ -131,10 +129,14 @@ for OVPN_FILE in ovpn_files:
|
|||||||
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: No openvpn process found.")
|
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: No openvpn process found.")
|
||||||
|
|
||||||
# Wait for a random time between 8 and 30 seconds
|
# Wait for a random time between 8 and 30 seconds
|
||||||
sleep_time = random.randint(1, 8)
|
sleep_time = random.randint(6, 8)
|
||||||
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Waiting for {sleep_time} seconds before the next connection...")
|
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Waiting for {sleep_time} seconds before the next connection...")
|
||||||
time.sleep(sleep_time)
|
time.sleep(sleep_time)
|
||||||
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Resuming...")
|
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Resuming...")
|
||||||
|
|
||||||
# Increment file number
|
# Increment file number
|
||||||
FILE_NUM += 1
|
FILE_NUM += 1
|
||||||
|
|
||||||
|
# Close the database connection
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
73
openvpn_manager.py
Normal file
73
openvpn_manager.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
import re
|
||||||
|
|
||||||
|
def establish_vpn_connection(ovpn_file, credential_file, log_file):
|
||||||
|
"""
|
||||||
|
Establishes a VPN connection using the provided OVPN file and credentials.
|
||||||
|
Returns True if the connection is successful, False otherwise.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Read credentials
|
||||||
|
with open(credential_file, 'r') as f:
|
||||||
|
username = f.readline().strip()
|
||||||
|
password = f.readline().strip()
|
||||||
|
|
||||||
|
# Establish VPN connection (capture stdout and stderr)
|
||||||
|
openvpn_command = ["openvpn", "--config", ovpn_file,
|
||||||
|
"--auth-user-pass", credential_file, "--daemon",
|
||||||
|
"--log-append", log_file, "--verb", "3"]
|
||||||
|
|
||||||
|
process = subprocess.Popen(openvpn_command,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE)
|
||||||
|
stdout, stderr = process.communicate(input=password.encode())
|
||||||
|
|
||||||
|
# Check for errors
|
||||||
|
if process.returncode != 0:
|
||||||
|
return False, stderr.decode()
|
||||||
|
else:
|
||||||
|
return True, stdout.decode()
|
||||||
|
|
||||||
|
def is_vpn_active(ovpn_filename):
|
||||||
|
"""
|
||||||
|
Checks if the VPN connection associated with the given OVPN filename is active.
|
||||||
|
Returns True if active, False otherwise.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
subprocess.run(["pgrep", "-f", ovpn_filename], check=True, capture_output=True)
|
||||||
|
return True
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def disconnect_vpn():
|
||||||
|
"""
|
||||||
|
Disconnects the active VPN connection.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(["pgrep", "openvpn"], check=True, capture_output=True)
|
||||||
|
pids = result.stdout.decode().strip().split('\n')
|
||||||
|
for pid in pids:
|
||||||
|
subprocess.run(["kill", pid])
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
pass # No openvpn process found
|
||||||
|
|
||||||
|
def get_external_ip():
|
||||||
|
"""
|
||||||
|
Gets the external IP address using various services, with retries.
|
||||||
|
Returns the external IP if successful, or None if unable to get it.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for service in ["ifconfig.me", "ifconfig.co", "ipinfo.io/ip"]:
|
||||||
|
try:
|
||||||
|
external_ip = subprocess.check_output(["curl", "-s", service]).decode().strip()
|
||||||
|
if re.match(r"^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$", external_ip):
|
||||||
|
return external_ip
|
||||||
|
else:
|
||||||
|
print(f"{datetime.datetime.now()} [Main Script]: Invalid response from {service}")
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
print(f"{datetime.datetime.now()} [Main Script]: Error getting external IP from {service}")
|
||||||
|
|
||||||
|
return None # Unable to get external IP
|
||||||
69
psql_utils.py
Normal file
69
psql_utils.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import psycopg2
|
||||||
|
import datetime
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
|
def connect_to_database():
|
||||||
|
"""
|
||||||
|
Connects to the PostgreSQL database using credentials from 'psql.creds'.
|
||||||
|
Returns a connection and cursor object.
|
||||||
|
"""
|
||||||
|
with open('psql.creds', 'r') as f:
|
||||||
|
config = {}
|
||||||
|
for line in f:
|
||||||
|
key, value = line.strip().split(' = ')
|
||||||
|
config[key] = value
|
||||||
|
|
||||||
|
conn = psycopg2.connect(
|
||||||
|
dbname=config['db_name'],
|
||||||
|
user=config['db_user'],
|
||||||
|
password=config['db_password'],
|
||||||
|
host=config['db_host'],
|
||||||
|
port=config['db_port']
|
||||||
|
)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
return conn, cursor
|
||||||
|
|
||||||
|
def should_skip_file(cursor, filename):
|
||||||
|
"""
|
||||||
|
Checks if the given OVPN file should be skipped based on the last check time in the database.
|
||||||
|
Returns True if it should be skipped, False otherwise.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cursor.execute("SELECT last_exit_ip_check FROM ovpn_files WHERE file_name = %s", (filename,))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
|
||||||
|
if row is None:
|
||||||
|
print(f"No record found for {filename} in the database.")
|
||||||
|
return False # No record found, so don't skip
|
||||||
|
|
||||||
|
last_check = row[0]
|
||||||
|
|
||||||
|
if last_check is None: # Handle NULL value
|
||||||
|
return False # If last_check is NULL, don't skip
|
||||||
|
|
||||||
|
# Get current time in UTC timezone using zoneinfo
|
||||||
|
now = datetime.datetime.now(tz=ZoneInfo('UTC'))
|
||||||
|
|
||||||
|
time_difference = now - last_check
|
||||||
|
if time_difference.days < 3:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
except psycopg2.Error as e:
|
||||||
|
print(f"Error executing database query: {e}")
|
||||||
|
return False # In case of an error, don't skip the file
|
||||||
|
|
||||||
|
|
||||||
|
def update_or_insert_ip(cursor, conn, filename, external_ip):
|
||||||
|
"""
|
||||||
|
Updates or inserts the external IP information for the given OVPN file in the database.
|
||||||
|
"""
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO ovpn_files (file_name, last_observed, last_exit_ip_check, exit_ip)
|
||||||
|
VALUES (%s, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, %s)
|
||||||
|
ON CONFLICT (file_name) DO UPDATE
|
||||||
|
SET last_observed = CURRENT_TIMESTAMP,
|
||||||
|
last_exit_ip_check = CURRENT_TIMESTAMP,
|
||||||
|
exit_ip = %s
|
||||||
|
""", (filename, external_ip, external_ip))
|
||||||
|
conn.commit()
|
||||||
Reference in New Issue
Block a user