De API: https://api.protonvpn.ch/vpn/logicals is volgens Proton Support officieel stopgezet. Het enige wat hij nog teruggeeft is:
{"Code":0,"Error":"Invalid access token","Details":{}}Dit artikel is daarmee helaas achterhaald. Iemand is een petitie begonnen om deze functionaliteit terug te krijgen.
De achterhaalde handleiding die niet meer werkt sinds 06/2025. Voor de historie bewaard.
Toen ProtonVPN zijn command-line interface (CLI)-tool stopzette, bleven gebruikers van headless systemen zoals de Raspberry Pi zonder een eenvoudige manier om VPN-verbindingen te beheren. Zonder grafische interface is het handmatig wisselen van VPN-servers een gedoe geworden. Ook het selecteren van de snelste VPN uit een pool van servers is onmogelijk geworden.
Om dit probleem op te lossen, heb ik een Python-script ontwikkeld dat automatisch de snelste ProtonVPN-server selecteert op basis van latency en load, en daarna je WireGuard-configuratie voor je bijwerkt. Deze handleiding neemt je mee door het instellen van WireGuard voor ProtonVPN, het installeren van het script en het automatiseren van je VPN-verbinding, zodat je Raspberry Pi altijd verbonden is met de beste server.
Waarom heb ik dit Protonss-script nodig?
Dit Python-script automatiseert het verbinden van je Raspberry Pi met de snelst beschikbare ProtonVPN-server, met WireGuard als VPN-protocol. Het controleert de latency en load van meerdere VPN-servers via de API van ProtonVPN, kiest de server met de beste prestaties, werkt je WireGuard-configuratie bij met de nieuwe servergegevens en maakt alleen opnieuw verbinding als er een duidelijk snellere server is gevonden (niemand zit eigenlijk te wachten op reconnects). Het script logt het hele proces, wat het debuggen een stuk makkelijker maakt.
- Geen handmatig VPN-wisselen meer: Zonder de CLI van ProtonVPN zou het wisselen naar de snelste server handwerk betekenen. Dit script automatiseert dat proces door regelmatig de beschikbare servers te controleren en de verbinding bij te werken op basis van realtime prestaties.
- Optimaliseert de verbindingssnelheid: Het script zorgt dat je altijd verbonden bent met de VPN-server met de laagste latency en load. Dit is vooral handig om snelle verbindingen te behouden in landen met snelle ProtonVPN-servers of servers met weinig verkeer.
- Vermindert downtime: Door het herverbinden te automatiseren, minimaliseert het script mogelijke downtime door slechte serverprestaties of trage verbindingen.
- Gemaakt voor de Raspberry Pi: Als je een headless Raspberry Pi draait (zonder monitor), is dit script ideaal om VPN-taken te automatiseren zonder constante supervisie.
- Beveiliging & privacy: Door ervoor te zorgen dat je Raspberry Pi altijd via een VPN verbinding maakt, beschermt het je data en online activiteit, zeker als je de Pi gebruikt voor torrenting of als netwerkapparaat.
Dit script automatiseert dus het vinden van de optimale ProtonVPN-server, met hogere snelheden, meer betrouwbaarheid en gemak voor headless systemen zoals een Raspberry Pi.
Aan de slag!
Eerst moet je WireGuard installeren, een snel en lichtgewicht VPN-protocol dat we gebruiken om verbinding te maken met onze ProtonVPN-server.
# First, update your package list and install WireGuard:
sudo apt update
sudo apt install wireguard curl resolvconfGenereer een WireGuard-configuratie via ProtonVPN
- Log in op je ProtonVPN-account op hun website.
- Ga naar het gedeelte “Downloads” in je dashboard.
- Genereer een nieuw configuratiebestand onder “WireGuard Configurations”.
- Gebruik je torrents, schakel dan NAT-PMP in.

Sla dit configuratiebestand op je Raspberry Pi op:
# Save the configuration file to your Raspberry Pi:
# Place it in the WireGuard directory and rename it for simplicity:
sudo nano /etc/wireguard/protonvpn.confZodra je het configuratiebestand in de juiste map hebt geplaatst, controleer je de verbinding.
# Verify connection to ProtonVPN:
sudo wg-quick up protonvpn
# This should give an ip address different to the one frome your isp
curl ifconfig.me
# Verify connection to ProtonVPN:
sudo wg-quick down protonvpn
curl ifconfig.me Als deze twee ip-adressen van ifconfig.me verschillen, ben je verbonden via een VPN. Ook als je je eigen ip-adres niet kent omdat het een DHCP-adres of iets dergelijks is.
Onze Python-omgeving installeren voor Protonss.
Bij het ontwikkelen en draaien van Python-scripts op je Raspberry Pi is het verstandig om een Python virtual environment te gebruiken. Zo’n virtuele omgeving laat je Python-packages geïsoleerd installeren en beheren, zodat de standaard Python-configuratie van je systeem onaangeroerd blijft.
Veranderingen in je omgeving beïnvloeden dan alleen het project waaraan je werkt — zoals het installeren van specifieke packages zoals requests — zonder conflicten te veroorzaken met andere Python-projecten of systeemafhankelijkheden.
Zo stel je een virtuele omgeving in voor ons script:
- Installeer de virtuele omgeving: Installeer eerst het package
python3-venv, waarmee je virtuele omgevingen kunt aanmaken en beheren.
sudo apt install -y python3-venv python3-full- Maak een virtuele omgeving: Na installatie kun je een virtuele omgeving aanmaken. Dit genereert een geïsoleerde omgeving in de door jou opgegeven map (in dit geval
~/protonss).
python3 -m venv ~/protonss- Activeer de virtuele omgeving: Na het aanmaken moet je de omgeving activeren. Dit zorgt dat je systeem de Python-versie en packages uit de virtuele omgeving gebruikt in plaats van de systeembrede Python-installatie.
source ~/protonss/bin/activate- Nu de omgeving actief is, wordt elke Python-package die je installeert (zoals
requests) in deze geïsoleerde omgeving geïnstalleerd.
pip install requestsDeze virtuele omgeving in de toekomst gebruiken: Elke keer dat je Python-scripts wilt draaien die van deze virtuele omgeving afhankelijk zijn, moet je hem opnieuw activeren met het commando source ~/venv/bin/activate. Eenmaal actief draaien alle Python-commando’s en packages in deze omgeving.
Ons ProtonSS-script installeren om automatisch met de snelste VPN te verbinden.
We maken een configuratiebestand aan met alle variabelen die het Python-script gebruikt. Daarin definieer je bijvoorbeeld het pad naar je WireGuard-configuratie, het logbestand en het aantal pings om latency te testen.
cd ~/protonssVoorbeeld config.ini:
nano config.ini[Settings]
# Path to your WireGuard configuration file
wg_config_path = /etc/wireguard/protonvpn.conf
# Path to your log file
log_file = /var/log/wireguard_updater.log
# Number of pings to measure latency
ping_count = 3
# Maximum score difference before reconnection 0 - 100
# Average score of servers is between 10-40 it seems. so setting this to high results in no switches from servers
score_threshold = 5.0
# Comma-separated list of preferred countries
preferred_countries = US
# Comma-separated list of preferred cities
# If specified, servers must be in these cities (unless they're in a country without any specified city)
# Preferred cities to connect to. If specified, servers must be in these cities
# (unless they're in a country without any specified city).
# Example: preferred_cities = Phoenix, Los Angeles, Seattle
# This allows you to target specific cities within your preferred countries.
# If left empty, any city within the preferred countries will be considered.
# Note: City names are case sensitive and must match ProtonVPN's naming exactly.
# Multiple cities should be comma-separated.
preferred_cities =Phoenix
# Weight for latency in the server score
# latency is subjective since this is probably measured through the vpn and only counts as server latency.
# Checking the latency without a vpn isnt recommended since the proton servers wont reply back without vpn.
# Value cant be higher then 1. 0.3 translates to 30% of the score_treshold.
latency_weight = 0.3
# Weight for server load in the server score
# Load is the best factor 0.7 translates to 70% of the weight of the score_threshold. this cant be higher then 1.
load_weight = 0.7
# Enable or disable pinging servers (True or False)
# Since ping might not be reliable enough for you to keep on switching servers this can be disabled here.
ping_enabled = True
# Specify the interface to use for ping tests. By setting this option you can ping to the fastest vpn server
# this will ensure the lowest latency VPN from your location.
ping_interface = eth0
[Features]
# Choose which type of servers you want to use
# Options: p2p, plus, secure_core, tor
# Default is p2p if not specified
feature_type = p2p
# Feature explanation:
# Each server has exactly ONE feature number:
# 1, 17 = Secure Core servers
# 2 = TOR servers
# 0, 8, 16, 24 = Plus-only servers
# 4, 12, 20, 28 = P2P servers (these are also Plus servers)Je kunt de variabelen naar behoefte aanpassen. Laat je preferred_countries leeg, dan controleert het script een HELEBOEL! servers, waardoor het script erg lang gaat draaien!
Maak het Python-script aan
Nu gaan we het Python-script maken dat je VPN-verbinding automatiseert. Eerst moeten we een paar stappen voorbereiden.
De eerste regel van het script moet een shebang zijn, die er altijd voor zorgt dat het script in de juiste Python-omgeving draait:
source ~/protonss/bin/activate
which pythonDit geeft je het pad dat je op de eerste regel van het script moet zetten.
/home/pi/venv/bin/pythonPas het script aan door een shebang toe te voegen: zet de volgende regel helemaal bovenaan je Python-script. Vervang /home/{user}/venv/bin/python door het daadwerkelijke pad dat je kreeg toen je eerder het commando which python uitvoerde. Laat de #! aan het begin van de regel staan—daaruit maakt het systeem op welke interpreter het script moet draaien. We hebben het absolute pad nodig, dus ~/ werkt niet.
nano protonvpn_auto_connect.py#!/home/{user}/protonss/bin/python
import requests
import subprocess
import os
import sys
import configparser
import logging
import re
from datetime import datetime
import math
import shutil
# Get the directory of the current script
script_dir = os.path.dirname(os.path.abspath(__file__))
# Load the config file from the same directory as the script
config = configparser.ConfigParser()
config.read(os.path.join(script_dir, 'config.ini'))
WG_CONFIG_PATH = config.get('Settings', 'wg_config_path')
LOG_FILE = config.get('Settings', 'log_file')
PING_COUNT = config.getint('Settings', 'ping_count')
SCORE_THRESHOLD = config.getfloat('Settings', 'score_threshold', fallback=5.0)
PREFERRED_COUNTRIES = config.get('Settings', 'preferred_countries', fallback='').split(',')
PREFERRED_CITIES = config.get('Settings', 'preferred_cities', fallback='').split(',')
PING_INTERFACE = config.get('Settings', 'ping_interface', fallback=None)
# Weighting factors for latency and load from the config
LATENCY_WEIGHT = config.getfloat('Settings', 'latency_weight', fallback=0.3)
LOAD_WEIGHT = config.getfloat('Settings', 'load_weight', fallback=0.7)
# Ping option from config
PING_ENABLED = config.getboolean('Settings', 'ping_enabled', fallback=True)
# Clean up country codes (remove spaces and convert to uppercase)
PREFERRED_COUNTRIES = [country.strip().upper() for country in PREFERRED_COUNTRIES if country.strip()]
# Clean up city names (remove spaces)
PREFERRED_CITIES = [city.strip() for city in PREFERRED_CITIES if city.strip()]
# Set up logging
logging.basicConfig(
filename=LOG_FILE,
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logging.info('--- ProtonVPN Auto Connect Script Start ---')
# Feature definitions
FEATURE_SECURE_CORE = [1, 17] # Secure Core servers
FEATURE_TOR = [2] # TOR servers
FEATURE_PLUS = [0, 8, 16, 24] # Plus-only servers
FEATURE_P2P = [4, 12, 20, 28] # P2P servers (these are also Plus servers)
# check https://api.protonmail.ch/vpn/logicals for more info.
# be aware that the secure core and tor server are very limited so check if one is available for your country as stated in the config.ini file.
# FREE servers are not included in the api response. so they are already left out.
def get_feature_description(features):
# If no feature type is specified in config.ini, default to P2P
# Features is a number representing server capabilities
if not features: # If features is 0 or None
return "P2P" # Default to P2P
elif features in FEATURE_SECURE_CORE:
return "Secure Core"
elif features in FEATURE_TOR:
return "TOR"
elif features in FEATURE_P2P:
return "P2P"
elif features in FEATURE_PLUS:
return "Plus"
else:
return "P2P" # Default to P2P for any unrecognized feature
def get_wanted_features():
"""Get the wanted feature type based on config priorities"""
feature_type = config.get('Features', 'feature_type', fallback='p2p').lower()
if feature_type == 'secure_core':
return FEATURE_SECURE_CORE
elif feature_type == 'tor':
return FEATURE_TOR
elif feature_type == 'plus':
return FEATURE_PLUS
else: # Default to P2P
return FEATURE_P2P
# Function to fetch the list of servers from the ProtonVPN API
def get_servers(full_list=False):
try:
url = 'https://api.protonmail.ch/vpn/logicals'
response = requests.get(url)
response.raise_for_status()
data = response.json()
logging.info("API response data received.")
return data['LogicalServers']
except requests.RequestException as e:
logging.error(f'Error fetching server list: {e}')
sys.exit(1)
except ValueError as e:
logging.error(f'Error parsing JSON: {e}')
sys.exit(1)
# Function to filter P2P servers and servers in preferred countries
def filter_servers(servers):
filtered_servers = []
# Get feature preferences from config (nu maar één type)
wanted_features = get_wanted_features()
logging.info(f"Filtering servers with preferences - Countries: {PREFERRED_COUNTRIES or 'Any'}, "
f"Cities: {PREFERRED_CITIES or 'Any'}, Features: {[get_feature_description(f) for f in wanted_features]}")
for server in servers:
if not isinstance(server, dict):
logging.error(f"Unexpected type for server: {type(server)} - Value: {server}")
continue
features = server.get('Features', 0)
country = server['ExitCountry']
city = server.get('City', '')
# Check if server features match our wanted features
if features in wanted_features:
# Check country and city preferences
if country in PREFERRED_COUNTRIES:
if not PREFERRED_CITIES or city in PREFERRED_CITIES:
filtered_servers.append(server)
logging.info(f"Server matched - Country: {country}, City: {city}, "
f"Features: {get_feature_description(features)}")
if not filtered_servers:
logging.warning(f"No servers found matching filters. Countries: {PREFERRED_COUNTRIES}, "
f"Cities: {PREFERRED_CITIES}, Features: {[get_feature_description(f) for f in wanted_features]}")
else:
logging.info(f"Found {len(filtered_servers)} matching servers")
return filtered_servers
# Function to measure latency by pinging the server (optional)
def get_latency(hostname):
if not PING_ENABLED:
logging.info(f"Ping disabled, skipping latency check for {hostname}.")
return 0 # Return 0 latency if ping is disabled
latencies = []
for i in range(PING_COUNT):
try:
# Add the interface option if it's specified
ping_command = ['ping', '-c', '1', '-W', '1', hostname]
if PING_INTERFACE:
ping_command.insert(1, '-I')
ping_command.insert(2, PING_INTERFACE)
result = subprocess.run(ping_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if result.returncode == 0:
output = result.stdout.decode()
latency_line = [line for line in output.split('\n') if 'time=' in line][0]
latency = float(latency_line.split('time=')[1].split(' ms')[0])
latencies.append(latency)
else:
latencies.append(float('inf'))
except Exception as e:
latencies.append(float('inf'))
logging.error(f'Ping error for {hostname}: {e}')
# Return the lowest (best) latency value
best_latency = min(latencies)
# Handle inf latency
if math.isinf(best_latency):
logging.warning(f"Latency for {hostname} is inf. Assigning high latency value.")
best_latency = 1000 # Assign a high latency value
return best_latency
# Function to select the best server based on a weighted combination of latency and load
def select_best_server(servers):
best_server = None
best_score = float('inf')
for server in servers:
server_load = server.get('Load')
# Handle inf load
if server_load is None or math.isinf(server_load):
logging.warning(f"Server {server['Name']} has inf load. Assigning high load value.")
server_load = 100 # Assign a high load value if it's inf or None
else:
server_load = int(server_load)
for srv in server['Servers']:
exit_ip = srv.get('ExitIP')
hostname = exit_ip
# Get latency, only if ping is enabled
latency = get_latency(hostname)
# Combine latency and load using the configured weights
score = (latency * LATENCY_WEIGHT) + (server_load * LOAD_WEIGHT)
logging.info(f"Server {server['Name']} - Latency: {latency:.2f} ms, Load: {server_load}%, Score: {score:.2f}")
if score < best_score:
best_score = score
best_server = server
best_server_instance = srv
return best_server, best_server_instance, best_score
# Function to read the current server IP from the WireGuard configuration
def get_current_server_ip():
try:
with open(WG_CONFIG_PATH, 'r') as f:
config_text = f.read()
# Extract the current Endpoint IP
match = re.search(r'Endpoint\s*=\s*([\d\.]+):\d+', config_text)
if match:
logging.info(f"Current server IP found in WireGuard configuration: {match.group(1)}")
return match.group(1)
else:
logging.warning('No current Endpoint IP found in WireGuard configuration.')
return None
except Exception as e:
logging.error(f'Error reading the WireGuard configuration: {e}')
return None
# Function to find the current server's performance in the live server data
def get_current_server_performance(servers, current_ip):
if current_ip is None:
logging.warning('Current server IP is None, skipping performance check.')
return None, None, float('inf')
for server in servers:
for srv in server['Servers']:
if srv['ExitIP'] == current_ip:
server_load = server.get('Load')
if server_load is None or math.isinf(server_load):
logging.warning(f"Server {server['Name']} (IP: {current_ip}) has inf load. Assigning high load value.")
server_load = 100 # Assign a high load value
else:
server_load = int(server_load)
latency = get_latency(current_ip) if PING_ENABLED else 0
score = (latency * LATENCY_WEIGHT) + (server_load * LOAD_WEIGHT)
logging.info(f"Current server {server['Name']} - Latency: {latency:.2f} ms, Load: {server_load}%, Score: {score:.2f}")
return server, srv, score
logging.warning(f"Current server with IP {current_ip} not found in live data.")
return None, None, float('inf')
# Function to find the current server in the full ProtonVPN list if not found in filtered data
def find_current_server_full_list(current_ip):
servers = get_servers(full_list=True)
return get_current_server_performance(servers, current_ip)
# Function to update the WireGuard configuration file
def update_wg_config(server, server_instance, best_score):
try:
endpoint_ip = server_instance['EntryIP']
endpoint_port = 51820 # Default WireGuard port
endpoint = f"{endpoint_ip}:{endpoint_port}"
public_key = server_instance['X25519PublicKey']
server_name = server['Name']
# Read the current WireGuard configuration
with open(WG_CONFIG_PATH, 'r') as f:
config_text = f.read()
# Strip comments that start with # under [Peer] before PublicKey
config_text = re.sub(r'#.*\n', '', config_text, flags=re.M)
# Update the Endpoint in the configuration
config_text = re.sub(r'Endpoint\s*=.*', f'Endpoint = {endpoint}', config_text)
# Update the PublicKey in the configuration
config_text = re.sub(r'PublicKey\s*=.*', f'PublicKey = {public_key}', config_text)
# Add the new comment indicating the chosen server and score
config_text = re.sub(r'\[Peer\]\n', f'[Peer]\n# {server_name} - Auto-generated by ProtonVPN Auto Connect - Score: {best_score:.2f}\n', config_text)
# Write the updated configuration back to the file
with open(WG_CONFIG_PATH, 'w') as f:
f.write(config_text)
os.chmod(WG_CONFIG_PATH, 0o600)
logging.info(f"WireGuard configuration updated with server: {server_name} ({server['ExitCountry']}), Endpoint: {endpoint}, PublicKey: {public_key}, Score: {best_score:.2f}")
except Exception as e:
logging.error(f'Error updating the WireGuard configuration: {e}')
sys.exit(1)
# Function to restart the WireGuard interface
def is_resolvconf_available():
"""Check if resolvconf is available on the system."""
return shutil.which("resolvconf") is not None
def is_wg_quick_available():
"""Check if wg-quick is available on the system."""
return shutil.which("wg-quick") is not None
def restart_wg():
interface_name = os.path.basename(WG_CONFIG_PATH).split('.')[0]
# Check if wg-quick is available before proceeding
if not is_wg_quick_available():
logging.error("wg-quick not found. Ensure WireGuard is installed correctly.")
sys.exit(1)
try:
# Check if resolvconf is available before proceeding
if not is_resolvconf_available():
logging.warning("resolvconf not found, DNS settings may not be updated correctly.")
# Bring down the interface
subprocess.run(['wg-quick', 'down', interface_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Bring up the interface
subprocess.run(['wg-quick', 'up', interface_name], check=True)
logging.info(f'WireGuard interface {interface_name} started.')
except subprocess.CalledProcessError as e:
logging.error(f'Error starting the WireGuard interface: {e}')
sys.exit(1)
# Main function
def main():
logging.info('--- Starting server selection process ---')
# Fetch the list of servers
servers = get_servers()
if not isinstance(servers, list):
logging.error(f'Unexpected type for servers: {type(servers)}')
sys.exit(1)
# Filter the servers
filtered_servers = filter_servers(servers)
if not filtered_servers:
logging.error('No suitable servers available after filtering.')
sys.exit(1)
# Get the current server's IP address from the WireGuard configuration
current_ip = get_current_server_ip()
# Get the current server's performance if it exists
current_server, current_server_instance, current_score = get_current_server_performance(filtered_servers, current_ip)
# If the current server is not found, check the full list of servers
if current_server is None:
logging.info(f"Current server not found in filtered list, checking full server list...")
current_server, current_server_instance, current_score = find_current_server_full_list(current_ip)
# Select the best server based on weighted latency and load
best_server, best_server_instance, best_score = select_best_server(filtered_servers)
if not best_server:
logging.error('No suitable server found.')
sys.exit(1)
if current_server:
logging.info(f"Current server {current_server['Name']} (IP: {current_ip}) - Score: {current_score:.2f}")
logging.info(f"Selected best server {best_server['Name']} - Score: {best_score:.2f}")
if current_server and current_score <= best_score + SCORE_THRESHOLD:
logging.info(f"Current server {current_server['Name']} is within threshold. No switch necessary.")
else:
logging.info(f"Switching to server {best_server['Name']} due to better performance.")
update_wg_config(best_server, best_server_instance, best_score)
restart_wg()
logging.info('--- ProtonVPN Auto Connect Script End ---')
if __name__ == '__main__':
main()Het script testen
chmod +x protonvpn_auto_connect.pyZodra het script is opgeslagen, geef je het uitvoerrechten en draai je het.
sudo ./protonvpn_auto_connect.pycat /var/log/wireguard_updater.log
tail -f /var/log/wireguard_updater.logJe kunt het logbestand tailen of catten om de output te controleren. Tail heeft de voorkeur, want dan kun je het blijven volgen terwijl je script draait. Open gewoon een tweede ssh-sessie met de tail om het in de gaten te houden.
2024-09-25 22:19:10 [INFO] --- ProtonVPN Auto Connect Script Start ---
2024-09-25 22:19:10 [INFO] --- Starting server selection process ---
2024-09-25 22:19:11 [INFO] API response data received.
2024-09-25 22:19:11 [INFO] Current server IP found in WireGuard configuration: 79.135.105.20
2024-09-25 22:19:11 [WARNING] Current server with IP 79.135.105.20 not found in filtered data.
2024-09-25 22:19:11 [INFO] Current server not found in filtered list, checking full server list...
2024-09-25 22:19:12 [INFO] API response data received.
2024-09-25 22:19:13 [INFO] Current server BH#1 - Latency: 26.90 ms, Load: 10%, Score: 15.07
2024-09-25 22:19:14 [INFO] Server AO#1 - Latency: 27.10 ms, Load: 7%, Score: 13.03
2024-09-25 22:19:15 [INFO] Server AO#2 - Latency: 26.60 ms, Load: 14%, Score: 17.78
2024-09-25 22:19:16 [INFO] Server AO#3 - Latency: 27.10 ms, Load: 9%, Score: 14.43
2024-09-25 22:19:17 [INFO] Server AO#4 - Latency: 26.60 ms, Load: 16%, Score: 19.18
2024-09-25 22:19:17 [INFO] Current server BH#1 (IP: 79.135.105.20) - Score: 15.07
2024-09-25 22:19:17 [INFO] Selected best server AO#1 - Score: 13.03
2024-09-25 22:19:17 [INFO] Current server BH#1 is within threshold. No switch necessary.
2024-09-25 22:19:17 [INFO] --- ProtonVPN Auto Connect Script End ---sudo cat /etc/wireguard/protonvpn.confJe kunt ook controleren of alles werkt door het bestand protonvpn.conf te bekijken of door je externe ip-adres na te gaan.
[Peer]
# BH#3 - Auto-generated by ProtonVPN Auto Connect - Score: 7.74Je ziet dan iets zoals hierboven. Deze comment wordt bijgewerkt zodra er verbinding wordt gemaakt met een andere VPN, en het ip-adres binnen de peer verandert dan uiteraard mee.
curl ifconfig.me #this wil give the external ip in the cli.Als extra stap kun je ook je externe ip controleren. Die moet anders zijn dan het ip-adres dat je provider je geeft.
Dit script elk uur draaien
sudo crontab -e0 * * * * cd /home/{user}/protonss && /home/{user}/protonss/protonvpn_auto_connect.py >> /home/{user}/protonss/protonvpn_cron.log 2>&1Hier hebben we ingesteld dat het elk uur draait, maar je kunt het plannen zoals je wilt.
sudo crontab -lSla de crontab op en controleer dat cron is geïnstalleerd.
Optioneel: specifiek verkeer blokkeren als je VPN uitvalt
Als je er zeker van wilt zijn dat er geen verkeer je device verlaat als de VPN-verbinding uitvalt, kun je iptables gebruiken om verkeer te blokkeren wanneer de WireGuard-interface down is.
sudo apt-get install iptablesControleer dat je iptables hebt geïnstalleerd, of installeer het met het bovenstaande commando.
ifconfig -aprotonvpn: flags=209<UP,POINTOPOINT,RUNNING,NOARP> mtu 1420
inet 10.2.1.3 netmask 255.255.255.255 destination 10.2.1.3
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 1000 (UNSPEC)
RX packets 612 bytes 101768 (99.3 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 765 bytes 101144 (98.7 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0Bepaal eerst de naam van je netwerkinterface. Als je je bestand protonvpn.conf hebt genoemd, heet de interface protonvpn.
sudo iptables -A OUTPUT -p tcp --dport 51413 ! -o protonvpn -j REJECT --reject-with icmp-port-unreachable
sudo iptables -A OUTPUT -p udp --dport 51413 ! -o protonvpn -j REJECT --reject-with icmp-port-unreachableJe kunt elke regel aan je iptables toevoegen. De 2 regels hieronder zorgen ervoor dat poort 51413 wordt geweigerd zodra de netwerkinterface met de naam protonvpn niet beschikbaar is. Er is een regel voor UDP- en voor TCP-verkeer.
sudo iptables -L OUTPUT -v -nNadat je alle benodigde regels hebt toegevoegd, kun je ze met dit commando controleren.
sudo apt-get install iptables-persistentEn maak de regels persistent, zodat je ze niet bij elke reboot opnieuw hoeft toe te voegen.
sudo netfilter-persistent saveConclusie
Met dit Python-script is je Raspberry Pi altijd verbonden met de snelste beschikbare ProtonVPN-server, zodat je de best mogelijke VPN-prestaties hebt zonder handmatig ingrijpen. Of je je Pi nu gebruikt voor netwerken, torrenten of algemene online beveiliging: deze automatisering zorgt voor optimale prestaties en betrouwbare privacybescherming.
Update - 20-12-2024
Preferred_cities toegevoegd als optie om de selectie voor de VS te verkleinen. Dat land bevat anders ruim 2000 servers om te controleren. De performance-check kan zo in elk geval wat korter worden :-).
En ik heb uitgevonden wat de Feature-sectie van elke server in de API betekent. Hieronder leg ik het uit. Ik heb mijn code daarnaar aangepast om er correct mee te werken. Let op: de configuratie die je eerder hebt gemaakt moet overeenkomen met het type server waarmee je verbindt. Als je configuratie niet is ingesteld om met een Secure Core te verbinden, selecteer dan ook geen Secure Core in dit script. Sluit aan bij de ProtonVPN-configuratie die je hebt aangemaakt.
Uitleg van de features:
Elke server heeft precies één feature-nummer:
- 1, 17 = Secure Core-servers
- 2 = TOR-servers
- 0, 8, 16, 24 = Plus-only servers
- 4, 12, 20, 28 = P2P-servers (dat lijken ook Plus-servers te zijn)
{
"Name": "US-AZ#105",
"EntryCountry": "US",
"ExitCountry": "US",
"Domain": "node-us-260.protonvpn.net",
"Tier": 2,
"Features": 12,Na wat meer onderzoek naar vragen over de gratis servers van ProtonVPN blijkt dat ProtonVPN de publieke API-toegang tot hun free server-endpoints heeft verwijderd. Het /vpn/servers-endpoint dat eerder informatie over gratis servers had moeten leveren, geeft nu een 404-error:
{ "Code": 404, "Error": "Path not found", "Details": {}}Op dit moment is alleen het Plus-server-endpoint via de publieke
API bereikbaar: url = ‘https://api.protonmail.ch/vpn/logicals’
Dit is waarschijnlijk een bewuste keuze van ProtonVPN. Helaas kan dit script daardoor de gratis servers niet ondersteunen.



