Python3 script to open a NORD VPN connection using OpenVPN, run some custom code when connection succeeded and close the VPN connection when complete.

Prerequisties

  • OpenVPN (Version 2.6.010)
  • NORD VPN Configuration (https://nordvpn.com/servers/tools/)
  • NORD Service Credentials
  • Elevation Required, run script as administrator.


NORDCredentials.txt

Service Username
Service Password


NORDVPN.py

# Script ran as Administrator.

import subprocess
import threading
import time
from datetime import datetime


def monitor_process_output(process, target_string, stop_event):
    """
    Monitors the output of the subprocess and sets the stop_event when the target string is found.
    :param process: The subprocess to monitor.
    :param target_string: The string to look for in the subprocess output.
    :param stop_event: The threading.Event object to signal the main thread to stop waiting.
    """
    while process.poll() is None:
        output = process.stdout.readline()
        print(output, end="")
        if target_string in output:
            stop_event.set()  # Set the event to signal the main thread to stop waiting
            break


def start_vpn():
    """
    Starts the VPN process and creates a thread to monitor its output.
    :return: The output_monitor_thread and the subprocess object.
    """
    target_string = "Initialization Sequence Completed"  # Replace this with the string you're looking for
    config_file_path = r"C:\PROGRA~1\OpenVPN\config\uk2394.nordvpn.com.tcp.ovpn"
    cmd = r"C:\PROGRA~1\OpenVPN\bin\openvpn.exe --config " + config_file_path + " --auth-user-pass NORDCredentials.txt"

    process = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        text=True,
        bufsize=1,
        universal_newlines=True
    )

    stop_event = threading.Event()
    output_monitor_thread = threading.Thread(target=monitor_process_output, args=(process, target_string, stop_event))
    output_monitor_thread.daemon = True
    output_monitor_thread.start()

    # Wait for the thread to finish, but also check for the stop_event to exit early
    while not stop_event.is_set():
        output_monitor_thread.join(timeout=0.1)  # Adjust the timeout value as needed

    return output_monitor_thread, process


def stop_vpn(output_monitor_thread, process):
    """
    Stops the VPN process and joins the monitoring thread.
    :param output_monitor_thread: The thread monitoring the subprocess output.
    :param process: The subprocess to terminate.
    """
    # Terminate the subprocess
    if process.poll() is None:
        print(datetime.now().strftime("%Y-%m-%d %H:%M:%S") + " Stopping OpenVPN Process.")
        process.terminate()

    # Join the monitoring thread if it's still running
    if output_monitor_thread.is_alive():
        output_monitor_thread.join()


def main():
    """
    Main function to start and stop the VPN process.
    """
    output_monitor_thread, process = start_vpn()

    # <<<<<<<<<<<<<<< CODE HERE >>>>>>>>>>>>>>>
    print(datetime.now().strftime("%Y-%m-%d %H:%M:%S") + " <<<<<<<<<<<<<<< CODE HERE >>>>>>>>>>>>>>>")
    time.sleep(15)  # Placeholder for any other code execution. Adjust the sleep time as needed.

    stop_vpn(output_monitor_thread, process)


if __name__ == "__main__":
    main()

Example Output:

2023-07-27 22:08:25 DEPRECATED OPTION: --cipher set to 'AES-256-CBC' but missing in --data-ciphers (AES-256-GCM:AES-128-GCM). OpenVPN ignores --cipher for cipher negotiations. 
2023-07-27 22:08:25 Note: '--allow-compression' is not set to 'no', disabling data channel offload.
2023-07-27 22:08:25 OpenVPN 2.6.0 [git:v2.6.0/b999466418dddb89] Windows-MSVC [SSL (OpenSSL)] [LZO] [LZ4] [PKCS11] [AEAD] [DCO] built on Feb 15 2023
2023-07-27 22:08:25 Windows version 10.0 (Windows 10 or greater), amd64 executable
2023-07-27 22:08:25 library versions: OpenSSL 3.0.8 7 Feb 2023, LZO 2.10
2023-07-27 22:08:25 WARNING: --ping should normally be used with --ping-restart or --ping-exit
2023-07-27 22:08:25 NOTE: --fast-io is disabled since we are running on Windows
2023-07-27 22:08:25 Outgoing Control Channel Authentication: Using 512 bit message hash 'SHA512' for HMAC authentication
2023-07-27 22:08:25 Incoming Control Channel Authentication: Using 512 bit message hash 'SHA512' for HMAC authentication
2023-07-27 22:08:25 TCP/UDP: Preserving recently used remote address: [AF_INET]103.214.45.102:443
2023-07-27 22:08:25 Socket Buffers: R=[65536->65536] S=[65536->65536]
2023-07-27 22:08:25 Attempting to establish TCP connection with [AF_INET]xxx.xxx.xxx.xxx:443
2023-07-27 22:08:25 TCP connection established with [AF_INET]xxx.xxx.xxx.xxx:443
2023-07-27 22:08:25 TCPv4_CLIENT link local: (not bound)
2023-07-27 22:08:25 TCPv4_CLIENT link remote: [AF_INET]xxx.xxx.xxx.xxx:443
2023-07-27 22:08:25 TLS: Initial packet from [AF_INET]xxx.xxx.xxx.xxx:443, sid=1eb1bab9 2c508898
2023-07-27 22:08:25 WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this
2023-07-27 22:08:25 VERIFY OK: depth=2, C=PA, O=NordVPN, CN=NordVPN Root CA
2023-07-27 22:08:25 VERIFY OK: depth=1, O=NordVPN, CN=NordVPN CA8
2023-07-27 22:08:25 VERIFY KU OK
2023-07-27 22:08:25 Validating certificate extended key usage
2023-07-27 22:08:25 ++ Certificate has EKU (str) TLS Web Server Authentication, expects TLS Web Server Authentication
2023-07-27 22:08:25 VERIFY EKU OK
2023-07-27 22:08:25 VERIFY X509NAME OK: CN=uk2392.nordvpn.com
2023-07-27 22:08:25 VERIFY OK: depth=0, CN=uk2392.nordvpn.com
2023-07-27 22:08:25 Control Channel: TLSv1.3, cipher TLSv1.3 TLS_AES_256_GCM_SHA384, peer certificate: 4096 bit RSA, signature: RSA-SHA512
2023-07-27 22:08:25 [uk2392.nordvpn.com] Peer Connection Initiated with [AF_INET]xxx.xxx.xxx.xxx:443
2023-07-27 22:08:25 TLS: move_session: dest=TM_ACTIVE src=TM_INITIAL reinit_src=1
2023-07-27 22:08:25 TLS: tls_multi_process: initial untrusted session promoted to trusted
2023-07-27 22:08:26 SENT CONTROL [uk2392.nordvpn.com]: 'PUSH_REQUEST' (status=1)
2023-07-27 22:08:26 PUSH: Received control message: 'PUSH_REPLY,redirect-gateway def1,dhcp-option DNS xxx.xxx.xxx.xxx,dhcp-option DNS xxx.xxx.xxx.xxx,explicit-exit-notify,comp-lzo no,route-gateway xxx.xxx.xxx.xxx,topology subnet,ping 60,ping-restart 180,ifconfig xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx,peer-id 0,cipher AES-256-GCM'
2023-07-27 22:08:26 OPTIONS IMPORT: timers and/or timeouts modified
2023-07-27 22:08:26 OPTIONS IMPORT: --explicit-exit-notify can only be used with --proto udp
2023-07-27 22:08:26 OPTIONS IMPORT: compression parms modified
2023-07-27 22:08:26 OPTIONS IMPORT: --ifconfig/up options modified
2023-07-27 22:08:26 OPTIONS IMPORT: route options modified
2023-07-27 22:08:26 OPTIONS IMPORT: route-related options modified
2023-07-27 22:08:26 OPTIONS IMPORT: --ip-win32 and/or --dhcp-option options modified
2023-07-27 22:08:26 OPTIONS IMPORT: peer-id set
2023-07-27 22:08:26 OPTIONS IMPORT: data channel crypto options modified
2023-07-27 22:08:26 interactive service msg_channel=0
2023-07-27 22:08:26 open_tun
2023-07-27 22:08:26 tap-windows6 device [OpenVPN TAP-Windows6] opened
2023-07-27 22:08:26 TAP-Windows Driver Version 9.24 
2023-07-27 22:08:26 Set TAP-Windows TUN subnet mode network/local/netmask = xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx [SUCCEEDED]
2023-07-27 22:08:26 Notified TAP-Windows driver to set a DHCP IP/netmask of xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx on interface {xxxxxxxxxx} [DHCP-serv: xxx.xxx.xxx.xxx, lease-time: 31536000]
2023-07-27 22:08:26 Successful ARP Flush on interface [25] {xxxxxxxxxx}
2023-07-27 22:08:26 IPv4 MTU set to 1500 on interface 25 using SetIpInterfaceEntry()
2023-07-27 22:08:26 Block_DNS: WFP engine opened
2023-07-27 22:08:26 Block_DNS: Using existing sublayer
2023-07-27 22:08:26 Block_DNS: Added permit filters for exe_path
2023-07-27 22:08:26 Block_DNS: Added block filters for all interfaces
2023-07-27 22:08:26 Block_DNS: Added permit filters for TAP interface
2023-07-27 22:08:26 Data Channel: using negotiated cipher 'AES-256-GCM'
2023-07-27 22:08:26 Outgoing Data Channel: Cipher 'AES-256-GCM' initialized with 256 bit key
2023-07-27 22:08:26 Incoming Data Channel: Cipher 'AES-256-GCM' initialized with 256 bit key
2023-07-27 22:08:31 TEST ROUTES: 1/1 succeeded len=0 ret=1 a=0 u/d=up
2023-07-27 22:08:31 C:\Windows\system32\route.exe ADD xxx.xxx.xxx.xxx MASK xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx
2023-07-27 22:08:31 Route addition via ipapi [adaptive] failed because route exists
2023-07-27 22:08:31 C:\Windows\system32\route.exe ADD xxx.xxx.xxx.xxx MASK xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx
2023-07-27 22:08:31 Route addition via ipapi [adaptive] failed because route exists
2023-07-27 22:08:31 C:\Windows\system32\route.exe ADD xxx.xxx.xxx.xxx MASK xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx
2023-07-27 22:08:31 Route addition via ipapi [adaptive] failed because route exists
2023-07-27 22:08:31 Initialization Sequence Completed
2023-07-27 22:08:31 <<<<<<<<<<<<<<< CODE HERE >>>>>>>>>>>>>>>
2023-07-27 22:08:46 Stopping OpenVPN Process.

Process finished with exit code 0