Skip to content

Instantly share code, notes, and snippets.

@jonasjancarik
Last active November 24, 2024 18:28
Show Gist options
  • Save jonasjancarik/d5b73a1c1274defd290304db1cb0dfaf to your computer and use it in GitHub Desktop.
Save jonasjancarik/d5b73a1c1274defd290304db1cb0dfaf to your computer and use it in GitHub Desktop.
ProtonVPN from Terminal - use a random config file
echo -e "\n\nDownloading custom ProtonVPN scripts...\n\n"
curl -s -o ~/protonvpn.sh "https://gist.githubusercontent.com/jonasjancarik/d5b73a1c1274defd290304db1cb0dfaf/raw/protonvpn.sh?$RANDOM"
chmod +x ~/protonvpn.sh
curl -s -o ~/Desktop/protonvpn_script.desktop "https://gist.githubusercontent.com/jonasjancarik/d5b73a1c1274defd290304db1cb0dfaf/raw/protonvpn_script.desktop?$RANDOM"
chmod 755 ~/Desktop/protonvpn_script.desktop
gio set ~/Desktop/protonvpn_script.desktop metadata::trusted true
curl -s -o ~/Desktop/protonvpn_disconnect.desktop "https://gist.githubusercontent.com/jonasjancarik/d5b73a1c1274defd290304db1cb0dfaf/raw/protonvpn_disconnect.desktop?$RANDOM"
chmod 755 ~/Desktop/protonvpn_disconnect.desktop
gio set ~/Desktop/protonvpn_disconnect.desktop metadata::trusted true
echo -e "\n\n\e[32m$(tput bold)The script is now installed.$(tput sgr0)\e[0m \nUse the \"ProtonVPN (Free)\" shortcut on the desktop to launch it.\n\n"
#!/bin/bash
################
# Auto-update #
################
# Get the path of the currently installed script
current_script_path=~/protonvpn.sh
# Get the hash of the currently installed script
current_script_hash=$(sha256sum $current_script_path | awk '{print $1}')
# Download the latest version of this script
latest_script_hash=$(set -o pipefail && curl -s https://gist.githubusercontent.com/jonasjancarik/d5b73a1c1274defd290304db1cb0dfaf/raw/protonvpn.sh?$RANDOM | sha256sum | awk '{print $1}')
# Check if the curl command was successful
if [ $? -ne 0 ]; then
echo "${YELLOW}WARNING${ENDCOLOR}: unable to check for updates."
else
# Compare the hashes of the two scripts
if [ ! "$current_script_hash" == "$latest_script_hash" ]; then
read -p "Update available, do you want to download it? (yes/no)" response
if [ "$(echo "$response" | tr '[:upper:]' '[:lower:]')" == "yes" ]; then
echo -e "\nDownloading update to ~/Downloads/protonvpn_install.sh..."
curl -s https://gist.githubusercontent.com/jonasjancarik/d5b73a1c1274defd290304db1cb0dfaf/raw/install.sh?$RANDOM > ~/Downloads/protonvpn_install.sh
chmod +x ~/Downloads/protonvpn_install.sh
echo -e "\nUpdate downloaded, running the install script..."
exec ~/Downloads/protonvpn_install.sh # also exits the current script
else
echo "Update skipped."
fi
fi
fi
################
# Main script #
################
zip_file=~/Downloads/ProtonVPN_server_configs.zip
config_dir=~/protonvpn_configs
checksum_file="$config_dir/checksum.txt"
auth_fail_counter_file="$config_dir/auth_fail_counter.txt"
# colors
RED="\e[31m"
GREEN="\e[32m"
YELLOW="\e[33m"
ENDCOLOR="\e[0m"
child_process_exit_code=""
# https://www.baeldung.com/linux/background-process-get-exit-code
handle_sigchld() {
if [ -n "$pid" -a ! -d "/proc/$pid" ]; then
wait $pid
child_process_exit_code=$?
# echo pid $pid terminated with exit code $child_process_exit_code
unset pid
fi
}
extract_files() {
# create the target directory if it doesn't exist
mkdir -p "$config_dir"
# remove all .ovpn files from the target directory
find "$config_dir" -type f -name "*.ovpn" -delete 2>/dev/null
# extract the files, silently
# unzip -q -o "$zip_file" -d "$config_dir" 'jp*'
unzip -q -o "$zip_file" -d "$config_dir"
# store the new checksum
echo "$new_checksum" >"$checksum_file"
}
retry_prompt_result=""
retry_prompt() {
# ask user to retry or exit
read -p "Retry? (yes/no)" response
if [ "$(echo "$response" | tr '[:upper:]' '[:lower:]')" == "yes" ]; then
retry_prompt_result="true"
else
retry_prompt_result="false"
echo "Exiting..."
exit 1
fi
}
check_zip_file() {
config_files=$(find "$config_dir" -type f -name "*.ovpn" 2>/dev/null)
if [ -f "$zip_file" ]; then
# zip file exists
# calculate checksum of the zip file
new_checksum=$(sha256sum "$zip_file" | awk '{print $1}')
if [[ $(find "$zip_file" -mtime +30) ]]; then
echo -e "${YELLOW}WARNING${ENDCOLOR}: The ProtonVPN server configs zip file is older than 30 days. It is recommended to download a new one."
fi
if [ -f "$checksum_file" ]; then
# read stored checksum
old_checksum=$(cat "$checksum_file")
# compare new and stored checksums
if [ "$new_checksum" != "$old_checksum" ]; then
# checksums are different, extract the files
extract_files
fi
else
# checksum file doesn't exist, extract the files
extract_files
fi
else
# zip file doesn't exist
if [[ -z "$config_files" ]]; then
echo -e "\n${RED}ERROR${ENDCOLOR}: No .ovpn config files found in the $config_dir directory. The configs zip file has to be in ~/Downloads to create those."
echo -e "\nPlease download it from https://account.protonvpn.com/downloads#openvpn-configuration-files under OpenVPN configuration files. \n\n Choose these options: \n 1. Select platform: GNU/Linux \n 2. Select protocol: UDP \n 3. Select config file and download: Free server configs \n 4. Click on Download all configurations under the table. \n\n Make sure the downloaded file is in the Downloads folder and that its name is ProtonVPN_server_configs.zip. \n "
# ask user to retry or exit
retry_prompt
if [ "$retry_prompt_result" == "true" ]; then
check_zip_file
fi
else
echo -e "\n${YELLOW}WARNING${ENDCOLOR}: The ProtonVPN server configs zip file was not found. It should be in the ~/Downloads directory to help keep the configs up-to-date. \n\n Please download it from https://account.protonvpn.com/downloads#openvpn-configuration-files under OpenVPN configuration files. \n\n Choose these options: \n 1. Select platform: GNU/Linux \n 2. Select protocol: UDP \n 3. Select config file and download: Free server configs \n 4. Click on Download all configurations under the table. \n\n Make sure the downloaded file is in the Downloads folder and that its name is ProtonVPN_server_configs.zip. \n "
fi
fi
}
check_zip_file
if ! ls "$config_dir"/*.ovpn 1>/dev/null 2>&1; then
echo -e "\n${YELLOW}WARNING${ENDCOLOR}: No configuration files found in the directory $config_dir. Trying to get them from the downloaded configs file...\n"
extract_files
fi
# Check if the current version of update-resolv-conf script is different from the one at GitHub
update_resolv_conf_file="/etc/openvpn/update-resolv-conf"
update_resolv_conf_url="https://raw.githubusercontent.com/ProtonVPN/scripts/master/update-resolv-conf.sh"
if [ -f "$update_resolv_conf_file" ]; then
local_hash=$(sha256sum "$update_resolv_conf_file" | awk '{print $1}')
remote_hash=$(wget -qO- "$update_resolv_conf_url" | sha256sum | awk '{print $1}')
if [ "$local_hash" != "$remote_hash" ]; then
echo "Backing up the current version of update-resolv-conf script to $update_resolv_conf_file.bak"
sudo cp "$update_resolv_conf_file" "$update_resolv_conf_file.bak"
sudo wget "$update_resolv_conf_url" -O "$update_resolv_conf_file"
sudo chmod +x "$update_resolv_conf_file"
fi
else
sudo wget "$update_resolv_conf_url" -O "$update_resolv_conf_file"
sudo chmod +x "$update_resolv_conf_file"
fi
# check if the credentials.txt file exists, if not, ask the user to provide credentials
credentials_file="$config_dir/credentials.txt"
if [ ! -f "$credentials_file" ]; then
echo -e "We need special login details for this - i.e. not your regular ProtonVPN password. Get them from the OpenVPN/IKEv2 username section at https://account.protonvpn.com/account#openvpn\n"
read -p "Username: " username
read -p "Password: " password
echo "$username" >"$credentials_file"
echo "$password" >>"$credentials_file"
rm -f "$auth_fail_counter_file"
fi
if ! sudo -n true 2>/dev/null; then
echo -e "Please enter your password to enable the connection: "
# dummy sudo command to enable sudo for this session
sudo -v
fi
attempt_counter=0
random_config_file=""
no_config_files_left() {
# echo a message starting with an empty line
echo -e "\n${RED}ERROR${ENDCOLOR}: No configuration files left in the directory $config_dir."
echo -e "Removing the configs zip file to prevent reusing outdated configs..."
rm -f $zip_file
echo -e "\nPlease download a new one from https://account.protonvpn.com/downloads#openvpn-configuration-files under OpenVPN configuration files. \n\n Choose these options: \n 1. Select platform: GNU/Linux \n 2. Select protocol: UDP \n 3. Select config file and download: Free server configs \n 4. Click on Download all configurations under the table. \n\n Make sure the downloaded file is in the Downloads folder and that its name is ProtonVPN_server_configs.zip. \n "
retry_prompt
if [ "$retry_prompt_result" == "true" ]; then
exec $current_script_path
else
exit 1
fi
}
choose_country() {
# ask the user what country they want to connect through, JP, US or NL:
echo -e "\nPlease choose a country to connect through: \n 1. Japan \n 2. United States \n 3. Netherlands \n"
read -p "Enter a number: " country_number
if [ "$country_number" == "1" ]; then
country="jp"
country_full="Japan"
elif [ "$country_number" == "2" ]; then
country="us"
country_full="the United States"
elif [ "$country_number" == "3" ]; then
country="nl"
country_full="the Netherlands"
else
echo -e "\n${RED}ERROR${ENDCOLOR}: Invalid input. Please enter a number between 1 and 3.\n"
choose_country
fi
}
choose_country
echo -e "\n"
# start OpenVPN using the credentials file and the randomly chosen .ovpn file
while true; do
if ls "$config_dir"/*.ovpn 1>/dev/null 2>&1; then
# store a random config file in the variable random_config_file
random_config_file=$(find $config_dir -name "$country*.ovpn" | shuf -n 1)
else
no_config_files_left
fi
if [ "$attempt_counter" -ge 1 ]; then
echo -e "Trying configuration file: $random_config_file..."
fi
# create the logs directory if it doesn't exist
mkdir -p $config_dir/log
# clear the log file
truncate -s 0 $config_dir/log/openvpn.log
# kill any previously open OpenVPN processes - redirect errors to /dev/null
sudo killall openvpn 2>/dev/null
# count the number of lines in the config file which start with "remote "
# number_of_servers=$(grep -c "^remote " "$random_config_file")
# start OpenVPN in the background
# sudo openvpn --mute-replay-warnings --config "$random_config_file" --auth-user-pass $credentials_file --connect-timeout 10 --connect-retry-max 1 --resolv-retry $number_of_servers --log $config_dir/log/openvpn.log &
sudo openvpn --mute-replay-warnings --config "$random_config_file" --auth-user-pass $credentials_file --connect-timeout 10 --connect-retry 0 --connect-retry-max 1 --log $config_dir/log/openvpn.log &
# save the PID of the OpenVPN process
pid=$!
# echo $pid # for debugging
# Trap signals and kill the subprocess when the script is interrupted or terminated
trap "if ps -p $pid > /dev/null; then kill $pid; fi" SIGINT SIGTERM INT TERM SIGHUP EXIT
trap handle_sigchld SIGCHLD
connection_detected="false"
counter=0
# wait for the OpenVPN process to finish
while kill -0 $pid 2> /dev/null; do
if ! $connection_detected; then
echo -ne "\r${YELLOW}Connecting through ${country_full} using ProtonVPN... (${counter} seconds)${ENDCOLOR}"
# increment counter
counter=$((counter+1))
fi
# check if the connection was successful
if ! $connection_detected && sudo grep -q "Initialization Sequence Completed" "$config_dir/log/openvpn.log"; then
connection_detected="true"
echo -ne "\r"
tput el
echo -e "${GREEN}You are now connected through ${country_full} using ProtonVPN.${ENDCOLOR}"
echo -e "\r- To disconnect, press Ctrl+C"
echo -e "\r- If you just close this window, you may have to use the 'Disconnect ProtonVPN' shortcut on the desktop to actually disconnect."
echo "0" >> "$auth_fail_counter_file"
ip_api_response=""
get_ip_details() {
ip_api_response=$(curl -s http://ip-api.com/json/?fields=country,regionName,city,zip,lat,lon,isp,query)
}
api_details_obtained="false"
# try to get the IP details 5 times
for i in {1..5}; do
get_ip_details
if [ $? -eq 0 ]; then
api_details_obtained="true"
echo -e "\n\r"
# clear line
tput el
echo -e "You are connected through this location:"
query=$(echo $ip_api_response | jq -r '.query')
country=$(echo $ip_api_response | jq -r '.country')
regionName=$(echo $ip_api_response | jq -r '.regionName')
city=$(echo $ip_api_response | jq -r '.city')
zip=$(echo $ip_api_response | jq -r '.zip')
lat=$(echo $ip_api_response | jq -r '.lat')
lon=$(echo $ip_api_response | jq -r '.lon')
isp=$(echo $ip_api_response | jq -r '.isp')
org=$(echo $ip_api_response | jq -r '.org')
echo -en "\n\rIP:\t\t$query"
echo -en "\n\rCountry:\t$country"
echo -en "\n\rRegion:\t\t$regionName"
echo -en "\n\rCity:\t\t$city"
echo -en "\n\rZip:\t\t$zip"
echo -en "\n\rLat:\t\t$lat, lon: $lon"
echo -en "\n\rISP:\t\t$isp"
echo -en "\n\rOrg.:\t\t$org"
echo -e "\n\r"
break
fi
sleep 1
done
fi
if ! $connection_detected; then
sleep 1
fi
done
echo -e "\n"
echo -ne "\r"
# echo $pid # for debugging
# echo $! # for debugging
# echo $? # for debugging
# reset trap
trap - SIGINT SIGTERM
echo -e "\n"
if [ $child_process_exit_code -ne 0 ]; then
echo "OpenVPN connection failed."
if sudo grep -qE "(All connections have been connect-retry-max.*times unsuccessful, exiting)|(Cannot resolve host address)" "$config_dir/log/openvpn.log"; then
echo "The current configuration file is not working. Renaming it to ${random_config_file}_old"
mv "$random_config_file" "${random_config_file}_old"
if [ $(ls "$config_dir"/*.ovpn | wc -l) -eq 0 ]; then # todo: redirect errors
no_config_files_left
else
echo -e "\n\r"
read -p "Do you want to try again with a different configuration file? (yes/no)" answer
if [ "$(echo "$answer" | tr '[:upper:]' '[:lower:]')" != "yes" ]; then
echo "Exiting."
exit 0
fi
attempt_counter=$((attempt_counter + 1))
fi
else
echo -e "\n\rAn unknown error occurred. Exiting.\n\r"
read -n1 -p "Press any key to exit."
echo -e "\n\r"
exit 1
fi
else
# for some reason auth fail does not exit with a non-zero exit code
if sudo grep -q "AUTH: Received control message: AUTH_FAILED" "$config_dir/log/openvpn.log"; then
echo -e "\n\rAuthentication failed."
# load the counter file
if [ ! -f "$auth_fail_counter_file" ]; then
echo "1" >> "$auth_fail_counter_file"
auth_fail_count=1
echo -e "\rSometimes this is temporary. Please try again.\n"
else
auth_fail_count=$(cat "$auth_fail_counter_file")
auth_fail_count=$((auth_fail_count + 1))
echo "$auth_fail_count" > "$auth_fail_counter_file"
read -p "This may be temporary, but your saved login details have already failed ${auth_fail_count} times. Would you like to remove them? (yes/no)" answer
if [ "$answer" = "yes" ]; then
mv "$credentials_file" "$credentials_file"_old
rm "$auth_fail_counter_file"
echo -e "Your login details have been removed (backed up to ${credentials_file}_old). Please try again."
exit 0
fi
fi
read -n1 -p "Press any key to exit."
echo -e "\n"
exit 1
fi
echo -e "\nOpenVPN connection closed.\n\r"
read -n1 -p "Press any key to exit."
echo -e "\n\r"
exit 0
fi
done
[Desktop Entry]
Type=Application
Name=Disconnect ProtonVPN (Free)
Exec=sh -c "sudo killall openvpn 2>/dev/null"
Icon=gnome-globe-net
Terminal=true
[Desktop Entry]
Type=Application
Name=ProtonVPN (Free)
Exec=sh -c "~/protonvpn.sh"
Icon=gnome-globe-net
Terminal=true
@jonasjancarik
Copy link
Author

Install with

curl -s "https://gist.githubusercontent.com/jonasjancarik/d5b73a1c1274defd290304db1cb0dfaf/raw/install.sh?$RANDOM" | bash

@mooleshacat
Copy link

if I am not mistaken, this looks like a shell script to query the protonvpn api to get a server and/or openvpn configuration?

Is there any documentation or a repository somewhere like github?

I've been looking for a script to get all the premium configurations from the API - either all of them, or just for example, the united states ones.

If only I could find a script to get the lowest load premium US server and spit out the configuration file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment