From f37db1caf7ecaa18ec6f2a4f5fe99f096d18d696 Mon Sep 17 00:00:00 2001 From: Harrison Sand <14326961+hsand@users.noreply.github.com> Date: Sun, 8 Nov 2020 21:28:09 -0500 Subject: [PATCH] Initial commit --- ca.rsa.4096.crt | 43 +++++++++++++++++++++++++ generate-config.py | 57 +++++++++++++++++++++++++++++++++ piawg.py | 79 ++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 4 +++ 4 files changed, 183 insertions(+) create mode 100644 ca.rsa.4096.crt create mode 100644 generate-config.py create mode 100644 piawg.py create mode 100644 requirements.txt diff --git a/ca.rsa.4096.crt b/ca.rsa.4096.crt new file mode 100644 index 0000000..82dec69 --- /dev/null +++ b/ca.rsa.4096.crt @@ -0,0 +1,43 @@ +-----BEGIN CERTIFICATE----- +MIIHqzCCBZOgAwIBAgIJAJ0u+vODZJntMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNV +BAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIElu +dGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3Mx +IDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkB +FiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzQw +MzNaFw0zNDA0MTIxNzQwMzNaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex +EzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQg +QWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UE +AxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50 +ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVy +bmV0YWNjZXNzLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALVk +hjumaqBbL8aSgj6xbX1QPTfTd1qHsAZd2B97m8Vw31c/2yQgZNf5qZY0+jOIHULN +De4R9TIvyBEbvnAg/OkPw8n/+ScgYOeH876VUXzjLDBnDb8DLr/+w9oVsuDeFJ9K +V2UFM1OYX0SnkHnrYAN2QLF98ESK4NCSU01h5zkcgmQ+qKSfA9Ny0/UpsKPBFqsQ +25NvjDWFhCpeqCHKUJ4Be27CDbSl7lAkBuHMPHJs8f8xPgAbHRXZOxVCpayZ2SND +fCwsnGWpWFoMGvdMbygngCn6jA/W1VSFOlRlfLuuGe7QFfDwA0jaLCxuWt/BgZyl +p7tAzYKR8lnWmtUCPm4+BtjyVDYtDCiGBD9Z4P13RFWvJHw5aapx/5W/CuvVyI7p +Kwvc2IT+KPxCUhH1XI8ca5RN3C9NoPJJf6qpg4g0rJH3aaWkoMRrYvQ+5PXXYUzj +tRHImghRGd/ydERYoAZXuGSbPkm9Y/p2X8unLcW+F0xpJD98+ZI+tzSsI99Zs5wi +jSUGYr9/j18KHFTMQ8n+1jauc5bCCegN27dPeKXNSZ5riXFL2XX6BkY68y58UaNz +meGMiUL9BOV1iV+PMb7B7PYs7oFLjAhh0EdyvfHkrh/ZV9BEhtFa7yXp8XR0J6vz +1YV9R6DYJmLjOEbhU8N0gc3tZm4Qz39lIIG6w3FDAgMBAAGjggFUMIIBUDAdBgNV +HQ4EFgQUrsRtyWJftjpdRM0+925Y6Cl08SUwggEfBgNVHSMEggEWMIIBEoAUrsRt +yWJftjpdRM0+925Y6Cl08SWhge6kgeswgegxCzAJBgNVBAYTAlVTMQswCQYDVQQI +EwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRl +cm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAw +HgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0 +ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRl +aW50ZXJuZXRhY2Nlc3MuY29tggkAnS7684Nkme0wDAYDVR0TBAUwAwEB/zANBgkq +hkiG9w0BAQ0FAAOCAgEAJsfhsPk3r8kLXLxY+v+vHzbr4ufNtqnL9/1Uuf8NrsCt +pXAoyZ0YqfbkWx3NHTZ7OE9ZRhdMP/RqHQE1p4N4Sa1nZKhTKasV6KhHDqSCt/dv +Em89xWm2MVA7nyzQxVlHa9AkcBaemcXEiyT19XdpiXOP4Vhs+J1R5m8zQOxZlV1G +tF9vsXmJqWZpOVPmZ8f35BCsYPvv4yMewnrtAC8PFEK/bOPeYcKN50bol22QYaZu +LfpkHfNiFTnfMh8sl/ablPyNY7DUNiP5DRcMdIwmfGQxR5WEQoHL3yPJ42LkB5zs +6jIm26DGNXfwura/mi105+ENH1CaROtRYwkiHb08U6qLXXJz80mWJkT90nr8Asj3 +5xN2cUppg74nG3YVav/38P48T56hG1NHbYF5uOCske19F6wi9maUoto/3vEr0rnX +JUp2KODmKdvBI7co245lHBABWikk8VfejQSlCtDBXn644ZMtAdoxKNfR2WTFVEwJ +iyd1Fzx0yujuiXDROLhISLQDRjVVAvawrAtLZWYK31bY7KlezPlQnl/D9Asxe85l +8jO5+0LdJ6VyOs/Hd4w52alDW/MFySDZSfQHMTIc30hLBJ8OnCEIvluVQQ2UQvoW ++no177N9L2Y+M9TcTA62ZyMXShHQGeh20rb4kK8f+iFX8NxtdHVSkxMEFSfDDyQ= +-----END CERTIFICATE----- diff --git a/generate-config.py b/generate-config.py new file mode 100644 index 0000000..e7bd8d2 --- /dev/null +++ b/generate-config.py @@ -0,0 +1,57 @@ +from piawg import piawg +from pick import pick +import os +from getpass import getpass +from datetime import datetime + +pia = piawg() + +# Generate public and private key pair +while True: + if os.path.exists('privatekey') or os.path.exists('publickey'): + value = input("Key files already exist. If you want to overwrite them type 'YES': ") + if value == 'YES': + pia.generate_keys() + break + else: + pia.generate_keys() + break + +# Select region +title = 'Please choose a region: ' +options = sorted(list(pia.server_list.keys())) +option, index = pick(options, title) +pia.set_region(option) + +# Get token +while True: + username = input("\nEnter PIA username: ") + password = getpass() + if pia.get_token(username, password): + print("Login successful!") + break + else: + print("Error logging in, please try again...") + +# Add key +status, response = pia.addkey() +if status: + print("Added key to server!") +else: + print("Error adding key to server") + print(response) + +# Build config +timestamp = int(datetime.now().timestamp()) +location = pia.region.replace(' ', '-') +config_file = 'PIA-{}-{}.conf'.format(location, timestamp) +print("Saving configuration file {}".format(config_file)) +with open(config_file, 'w') as file: + file.write('[Interface]\n') + file.write('Address = {}\n'.format(pia.connection['peer_ip'])) + file.write('PrivateKey = {}\n'.format(pia.privatekey)) + file.write('DNS = {},{}\n\n'.format(pia.connection['dns_servers'][0], pia.connection['dns_servers'][1])) + file.write('[Peer]\n') + file.write('PublicKey = {}\n'.format(pia.connection['server_key'])) + file.write('Endpoint = {}:1337\n'.format(pia.connection['server_ip'])) + file.write('AllowedIPs = 0.0.0.0/0\n') diff --git a/piawg.py b/piawg.py new file mode 100644 index 0000000..d354a49 --- /dev/null +++ b/piawg.py @@ -0,0 +1,79 @@ +import requests +import json +from requests_toolbelt.adapters import host_header_ssl +import urllib3 +import os +import urllib.parse + +# PIA uses the CN attribute for certificates they issue themselves. +# This will be deprecated by urllib3 at some point in the future, and generates a warning (that we ignore). +urllib3.disable_warnings(urllib3.exceptions.SubjectAltNameWarning) + + +class piawg: + def __init__(self): + self.server_list = {} + self.get_server_list() + self.region = None + self.token = None + self.publickey = None + self.privatekey = None + self.connection = None + + def get_server_list(self): + r = requests.get('https://serverlist.piaservers.net/vpninfo/servers/v4') + # Only process first line of response, there's some base64 data at the end we're ignoring + data = json.loads(r.text.splitlines()[0]) + for server in data['regions']: + self.server_list[server['name']] = server + + def set_region(self, region_name): + self.region = region_name + + def get_token(self, username, password): + # Get common name and IP address for metadata endpoint in region + meta_cn = self.server_list[self.region]['servers']['meta'][0]['cn'] + meta_ip = self.server_list[self.region]['servers']['meta'][0]['ip'] + + # Some tricks to verify PIA certificate, even though we're sending requests to an IP and not a proper domain + # https://toolbelt.readthedocs.io/en/latest/adapters.html#requests_toolbelt.adapters.host_header_ssl.HostHeaderSSLAdapter + s = requests.Session() + s.mount('https://', host_header_ssl.HostHeaderSSLAdapter()) + s.verify = 'ca.rsa.4096.crt' + + r = s.get("https://{}/authv3/generateToken".format(meta_ip), headers={"Host": meta_cn}, + auth=(username, password)) + data = r.json() + if r.status_code == 200 and data['status'] == 'OK': + self.token = data['token'] + return True + else: + return False + + def generate_keys(self): + os.system('wg genkey > privatekey') + os.system('wg pubkey < privatekey > publickey') + + # Read newly created keys + with open('publickey', 'r') as file: + self.publickey = file.read().splitlines()[0] + + with open('privatekey', 'r') as file: + self.privatekey = file.read().splitlines()[0] + + def addkey(self): + # Get common name and IP address for wireguard endpoint in region + cn = self.server_list[self.region]['servers']['wg'][0]['cn'] + ip = self.server_list[self.region]['servers']['wg'][0]['ip'] + + s = requests.Session() + s.mount('https://', host_header_ssl.HostHeaderSSLAdapter()) + s.verify = 'ca.rsa.4096.crt' + + r = s.get("https://{}:1337/addKey?pt={}&pubkey={}".format(ip, urllib.parse.quote(self.token), + urllib.parse.quote(self.publickey)), headers={"Host": cn}) + if r.status_code == 200 and r.json()['status'] == 'OK': + self.connection = r.json() + return True, r.content + else: + return False, r.content diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9a03048 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +pick==1.0.0 +requests==2.24.0 +requests-toolbelt==0.9.1 +urllib3==1.25.11