Skip to content

Instantly share code, notes, and snippets.

@sefidel
Created January 6, 2024 10:57
Show Gist options
  • Select an option

  • Save sefidel/201c2febc875fac4361169430e5fa5b7 to your computer and use it in GitHub Desktop.

Select an option

Save sefidel/201c2febc875fac4361169430e5fa5b7 to your computer and use it in GitHub Desktop.
Bring your own WireGuard key to ProtonVPN (hacky; see NOTE for limitations)
#! /usr/bin/env nix-shell
#! nix-shell -i python3 -p python3Packages.inquirer -p 'python3Packages.proton-client.overridePythonAttrs { doCheck = false; patches = [(fetchpatch {url = "https://github.com/ProtonMail/proton-python-client/pull/47/commits/faee82fdcad9fd2963a8080573e9b5315697dac7.patch";sha256 = "sha256-AvPgsBBnlp0bmHbIwA4BQ6KmsAZmlgwJnHynWIL6an8=";})]; }'
# ProtonVPN - Bring your own (WireGuard) key
# ------------------------------------------
# This script lets you roll your own WireGuard key instead of using the one
# generated from ProtonVPN.
# This may be particularly useful when using ProtonVPN as an exit node in
# overlay network solutions, such as Tailscale.
import proton
import getpass
import inquirer
import sys
import platform
ENDPOINT="https://api.protonvpn.ch"
proton_session = proton.api.Session(
api_url=ENDPOINT,
appversion="Other",
log_dir_path="./",
cache_dir_path="./",
)
proton_session.enable_alternative_routing = True
username = input("ProtonVPN Username: ")
password = getpass.getpass("ProtonVPN Password: ")
proton_session.authenticate(username, password)
logical_servers = proton_session.api_request(method="get", endpoint="/vpn/logicals")["LogicalServers"]
server_names = [server["Name"] for server in logical_servers]
# NOTE: ProtonVPN doesn't let you use the same pubkey, even if it's a DIFFERENT NODE.
# Therefore, only single selection is provided.
# NOTE: Same pubkey cannot be used even if the existing certificate is deleted.
# Users will have to regenerate their WireGuard pubkey when switching servers.
server_selection = inquirer.list_input("Select server to roll the WireGuard key to", choices=sorted(server_names))
selected_server = next(server for server in logical_servers if server["Name"] in server_selection)
if len(selected_server) == 0:
print("No servers selected!", file=sys.stderr)
exit(1)
netshield_level = inquirer.list_input("NetShield Level", choices=[("No filter", 0), ("Block malware only", 1), ("Block malware, ads and trackers", 2)])
random_nat = inquirer.list_input("Moderate NAT", choices=[("Yes", True), ("No", False)], default=False)
port_forwarding = inquirer.list_input("NAT-PMP (Port Forwarding)", choices=[("Yes", True), ("No", False)], default=True)
split_tcp = inquirer.list_input("Split TCP (VPN Accelerator)", choices=[("Yes", True), ("No", False)], default=True)
safe_mode = inquirer.list_input("SafeMode (Non-standard ports)", choices=[("Yes", True), ("No", False)], default=False)
device_prefix = inquirer.text("Set a prefix for DeviceName", default=platform.node())
wireguard_pubkey = inquirer.text("Enter your WireGuard pubkey")
print(f"Creating certificate for Node {selected_server['Name']}")
payload = {
"ClientPublicKey": wireguard_pubkey,
"Mode": "persistent",
"DeviceName": f"{device_prefix}-{selected_server['Name']} ({wireguard_pubkey})",
"Features": {
"peerName": selected_server["Name"],
"peerIp": selected_server["Servers"][0]["EntryIP"],
"peerPublicKey": selected_server["Servers"][0]["X25519PublicKey"],
"platform": "Linux",
"NetShieldLevel": netshield_level,
"RandomNAT": random_nat,
"PortForwarding": port_forwarding,
"SplitTCP": split_tcp,
"SafeMode": safe_mode,
}
}
certificate = proton_session.api_request(method="post", endpoint="/vpn/v1/certificate", jsondata=payload)
print(certificate)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment