major refactor, replace ovpn.zip file, leverage API instead.

This commit is contained in:
2024-09-11 08:33:47 -05:00
parent be53726479
commit 5b0c2c9038
3 changed files with 160 additions and 55 deletions

View File

@@ -6,17 +6,20 @@ import datetime
import requests import requests
import re import re
import sys import sys
from psql_utils import connect_to_database, update_or_insert_ip from psql_utils import connect_to_database, update_or_insert_ip, should_skip_file
from ovpn_downloader import download_and_extract_ovpn_configs, download_ovpn_if_needed
from openvpn_manager import establish_vpn_connection, is_vpn_active, disconnect_vpn, get_external_ip from openvpn_manager import establish_vpn_connection, is_vpn_active, disconnect_vpn, get_external_ip
from ovpn_template import DEFAULT_OVPN_CONFIG
# Variables # Variables
CREDENTIAL_FILE = "nord.creds" CREDENTIAL_FILE = "nord.creds"
OVPN_DIR = "ovpn_configs/ovpn_tcp" TEMP_OVPN_DIR = "/tmp/ovpn_configs" # Directory for temporary OVPN files
LOG_FILE = "openvpn.log" # This is not used anymore LOG_FILE = "openvpn.log" # This is not used anymore
RESULT_FILE = "vpnlist.txt" RESULT_FILE = "vpnlist.txt"
PING_URL = "https://health.ext.ben.io/ping/a1a55915-1051-48ed-bd13-ea3f1717a3ee" 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 # Define DEBUG_MODE
DEBUG_MODE = False DEBUG_MODE = False
if len(sys.argv) > 1 and sys.argv[1] == "-d": if len(sys.argv) > 1 and sys.argv[1] == "-d":
@@ -27,7 +30,7 @@ def debug_print(message):
print(f"{datetime.datetime.now()} + [Debug Mode]:{message}") print(f"{datetime.datetime.now()} + [Debug Mode]:{message}")
try: try:
requests.get(PING_URL, timeout=10) requests.get(PING_URL, auth=('local', 'local'), timeout=10)
print(f"{datetime.datetime.now()} [Main Script]: Ping sent to: %s" % PING_URL) print(f"{datetime.datetime.now()} [Main Script]: Ping sent to: %s" % PING_URL)
except requests.RequestException as e: except requests.RequestException as e:
# Log ping failure here... # Log ping failure here...
@@ -64,7 +67,17 @@ def is_script_running():
print(f"{datetime.datetime.now()} [Main Script]: Error checking for running instances: {e}") print(f"{datetime.datetime.now()} [Main Script]: Error checking for running instances: {e}")
return False 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) # Get current IP before starting (using get_external_ip)
@@ -91,76 +104,85 @@ except subprocess.CalledProcessError:
# Connect to PostgreSQL database # Connect to PostgreSQL database
conn, cursor = connect_to_database() conn, cursor = connect_to_database()
# Download and extract OVPN configurations, and get the list of files to check # Get the list of recommended NordVPN servers
debug_print(f"{datetime.datetime.now()} [Main Script]: Downloading and extracting OVPN configurations...") servers = get_nordvpn_servers()
ovpn_files_to_check = download_and_extract_ovpn_configs(cursor, conn, debug_print=debug_print)
total_files = len(ovpn_files_to_check) # Create the temporary directory if it doesn't exist
debug_print(f"{datetime.datetime.now()} [Main Script]: Found {total_files} OVPN files to check.") if not os.path.exists(TEMP_OVPN_DIR):
print(f"{datetime.datetime.now()} [Main Script]: Found {total_files} OVPN files to check.") os.makedirs(TEMP_OVPN_DIR)
# Process each OVPN file # Process each server
FILE_NUM = 1 server_count = len(servers)
for OVPN_FILE in ovpn_files_to_check: for index, server in enumerate(servers):
OVPN_FILENAME = os.path.basename(OVPN_FILE) # 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)
debug_print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Processing OVPN file...") 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.")
continue # Skip to the next server
# Check if the OVPN file exists, download if needed server_name = server.get('hostname')
file_exists = download_ovpn_if_needed(OVPN_FILENAME, cursor, conn, debug_print=debug_print) 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
if not file_exists: debug_print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: Load: {server_load}, Status: {server_status} Processing server... ")
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Skipping, file not found and removed from database.")
FILE_NUM += 1 # 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 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 # Establish VPN connection
connection_successful, connection_message = establish_vpn_connection(f"{OVPN_DIR}/{OVPN_FILE}", CREDENTIAL_FILE, LOG_FILE, debug_print=debug_print) connection_successful, connection_message = establish_vpn_connection(
ovpn_filepath, CREDENTIAL_FILE, LOG_FILE, debug_print=debug_print
)
if connection_successful: if connection_successful:
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: {connection_message}") print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: {connection_message}")
if not connection_successful:
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Error starting OpenVPN connection:{connection_message}")
# Wait for a random time between 1 and 4 seconds # Check if the VPN connection is active
sleep_time = random.randint(8, 15) try:
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Waiting for {sleep_time} seconds before the next connection...") subprocess.run(["pgrep", "-f", ovpn_filename], check=True, capture_output=True)
time.sleep(sleep_time)
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Resuming...")
continue # Skip to the next OVPN file # Check external IP (with validation and retries)
external_ip = get_external_ip()
# Check if the VPN connection is active (using the function from openvpn_manager.py) if external_ip is None:
try: print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: Unable to get external IP. Skipping this server.")
subprocess.run(["pgrep", "-f", OVPN_FILENAME], check=True, capture_output=True) continue
# Check external IP (with validation and retries) print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: External IP via VPN: {external_ip}")
external_ip = get_external_ip()
if external_ip is None: # Update or insert into database
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: Unable to get external IP. Skipping this OVPN file.") update_or_insert_ip(cursor, conn, ovpn_filename, external_ip)
continue
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: External IP via VPN: {external_ip}") except subprocess.CalledProcessError:
print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: VPN connection failed to establish.")
# Update or insert into database else:
update_or_insert_ip(cursor, conn, OVPN_FILENAME, external_ip) print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: Error starting OpenVPN connection: {connection_message}")
except subprocess.CalledProcessError:
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: VPN connection failed to establish.")
# Disconnect VPN # Disconnect VPN
disconnect_vpn() disconnect_vpn()
print(f"{datetime.datetime.now()} [{FILE_NUM}/{total_files}] [{OVPN_FILENAME}]: VPN disconnected.") print(f"{datetime.datetime.now()} [{index+1}/{server_count}] [{server_name}]: VPN disconnected.")
# Wait for a random time between 5 and 8 seconds # Wait for a random time between 5 and 8 seconds
sleep_time = random.randint(5, 8) sleep_time = random.randint(5, 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()} [{index+1}/{server_count}] [{server_name}]: 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()} [{index+1}/{server_count}] [{server_name}]: Resuming...")
# Increment file number # Clean up the temporary OVPN file
FILE_NUM += 1 os.remove(ovpn_filepath)
# Close the database connection # Close the database connection
cursor.close() cursor.close()

83
ovpn_template.py Normal file
View File

@@ -0,0 +1,83 @@
# ovpn_template.py
DEFAULT_OVPN_CONFIG = """
client
dev tun
proto tcp
remote {server_ip} 443
resolv-retry infinite
remote-random
nobind
tun-mtu 1500
tun-mtu-extra 32
mssfix 1450
persist-key
persist-tun
ping 15
ping-restart 0
ping-timer-rem
reneg-sec 0
comp-lzo no
verify-x509-name CN={server_cn}
remote-cert-tls server
auth-user-pass
verb 3
pull
fast-io
cipher AES-256-CBC
auth SHA512
<ca>
-----BEGIN CERTIFICATE-----
MIIFCjCCAvKgAwIBAgIBATANBgkqhkiG9w0BAQ0FADA5MQswCQYDVQQGEwJQQTEQ
MA4GA1UEChMHTm9yZFZQTjEYMBYGA1UEAxMPTm9yZFZQTiBSb290IENBMB4XDTE2
MDEwMTAwMDAwMFoXDTM1MTIzMTIzNTk1OVowOTELMAkGA1UEBhMCUEExEDAOBgNV
BAoTB05vcmRWUE4xGDAWBgNVBAMTD05vcmRWUE4gUm9vdCBDQTCCAiIwDQYJKoZI
hvcNAQEBBQADggIPADCCAgoCggIBAMkr/BYhyo0F2upsIMXwC6QvkZps3NN2/eQF
kfQIS1gql0aejsKsEnmY0Kaon8uZCTXPsRH1gQNgg5D2gixdd1mJUvV3dE3y9FJr
XMoDkXdCGBodvKJyU6lcfEVF6/UxHcbBguZK9UtRHS9eJYm3rpL/5huQMCppX7kU
eQ8dpCwd3iKITqwd1ZudDqsWaU0vqzC2H55IyaZ/5/TnCk31Q1UP6BksbbuRcwOV
skEDsm6YoWDnn/IIzGOYnFJRzQH5jTz3j1QBvRIuQuBuvUkfhx1FEwhwZigrcxXu
MP+QgM54kezgziJUaZcOM2zF3lvrwMvXDMfNeIoJABv9ljw969xQ8czQCU5lMVmA
37ltv5Ec9U5hZuwk/9QO1Z+d/r6Jx0mlurS8gnCAKJgwa3kyZw6e4FZ8mYL4vpRR
hPdvRTWCMJkeB4yBHyhxUmTRgJHm6YR3D6hcFAc9cQcTEl/I60tMdz33G6m0O42s
Qt/+AR3YCY/RusWVBJB/qNS94EtNtj8iaebCQW1jHAhvGmFILVR9lzD0EzWKHkvy
WEjmUVRgCDd6Ne3eFRNS73gdv/C3l5boYySeu4exkEYVxVRn8DhCxs0MnkMHWFK6
MyzXCCn+JnWFDYPfDKHvpff/kLDobtPBf+Lbch5wQy9quY27xaj0XwLyjOltpiST
LWae/Q4vAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqG
SIb3DQEBDQUAA4ICAQC9fUL2sZPxIN2mD32VeNySTgZlCEdVmlq471o/bDMP4B8g
nQesFRtXY2ZCjs50Jm73B2LViL9qlREmI6vE5IC8IsRBJSV4ce1WYxyXro5rmVg/
k6a10rlsbK/eg//GHoJxDdXDOokLUSnxt7gk3QKpX6eCdh67p0PuWm/7WUJQxH2S
DxsT9vB/iZriTIEe/ILoOQF0Aqp7AgNCcLcLAmbxXQkXYCCSB35Vp06u+eTWjG0/
pyS5V14stGtw+fA0DJp5ZJV4eqJ5LqxMlYvEZ/qKTEdoCeaXv2QEmN6dVqjDoTAo
k0t5u4YRXzEVCfXAC3ocplNdtCA72wjFJcSbfif4BSC8bDACTXtnPC7nD0VndZLp
+RiNLeiENhk0oTC+UVdSc+n2nJOzkCK0vYu0Ads4JGIB7g8IB3z2t9ICmsWrgnhd
NdcOe15BincrGA8avQ1cWXsfIKEjbrnEuEk9b5jel6NfHtPKoHc9mDpRdNPISeVa
wDBM1mJChneHt59Nh8Gah74+TM1jBsw4fhJPvoc7Atcg740JErb904mZfkIEmojC
VPhBHVQ9LHBAdM8qFI2kRK0IynOmAZhexlP/aT/kpEsEPyaZQlnBn3An1CRz8h0S
PApL8PytggYKeQmRhl499+6jLxcZ2IegLfqq41dzIjwHwTMplg+1pKIOVojpWA==
-----END CERTIFICATE-----
</ca>
key-direction 1
<tls-auth>
#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
e685bdaf659a25a200e2b9e39e51ff03
0fc72cf1ce07232bd8b2be5e6c670143
f51e937e670eee09d4f2ea5a6e4e6996
5db852c275351b86fc4ca892d78ae002
d6f70d029bd79c4d1c26cf14e9588033
cf639f8a74809f29f72b9d58f9b8f5fe
fc7938eade40e9fed6cb92184abb2cc1
0eb1a296df243b251df0643d53724cdb
5a92a1d6cb817804c4a9319b57d53be5
80815bcfcb2df55018cc83fc43bc7ff8
2d51f9b88364776ee9d12fc85cc7ea5b
9741c4f598c485316db066d52db4540e
212e1518a9bd4828219e24b20d88f598
a196c9de96012090e333519ae18d3509
9427e7b372d348d352dc4c85e18cd4b9
3f8a56ddb2e64eb67adfc9b337157ff4
-----END OpenVPN Static key V1-----
</tls-auth>
"""

View File

@@ -23,7 +23,7 @@ def connect_to_database():
cursor = conn.cursor() cursor = conn.cursor()
return conn, cursor return conn, cursor
def should_skip_file(cursor, filename): def should_skip_file(cursor, filename, index, server_count, debug_print):
""" """
Checks if the given OVPN file should be skipped based on the last check time in the database. 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. Returns True if it should be skipped, False otherwise.
@@ -33,7 +33,7 @@ def should_skip_file(cursor, filename):
row = cursor.fetchone() row = cursor.fetchone()
if row is None: if row is None:
print(f"No record found for {filename} in the database.") debug_print(f"{datetime.datetime.now()} [{index}/{server_count}] [{filename}]: Skipping OpenVPN TCP Dedicated server.")
return False # No record found, so don't skip return False # No record found, so don't skip
last_check = row[0] last_check = row[0]