What's new
  • SNBForums Code of Conduct

    SNBForums is a community for everyone, no matter what their level of experience.

    Please be tolerant and patient of others, especially newcomers. We are all here to share and learn!

    The rules are simple: Be patient, be nice, be helpful or be gone!

VPNMON VPNMON-R3 Custom WireGuard Server List Generation Tutorials and Examples

iTyPsIDg

Senior Member
This thread will share the same essence as VPNMON-R3 Custom OVPN Server List Generation Tutorials and Examples; however, this is for generating WireGuard server lists.

I currently only have Nord and Proton as VPN providers, so I'll focus on those for now.

Current VPN Providers Supported that utilize API functionality and include WireGuard details:
  • NordVPN
The examples below are provided to demonstrate how CURL + JQ queries can be used to auto-populate custom WG Server Lists within VPNMON-R3. For example, you may want your WG Client Slot 1 to only connect to servers in New York. WG Slot 2 might be populated with all the servers within the United States. Etc. I'm providing examples of what might be possible, and how to craft these statements so you can copy/paste them directly into VPNMON-R3 with minimal effort.

As shown below, the "WG/VPN Client Slot Server List Automation" menu allows you to View/Edit and Execute these statements, which will then pull the required VPN Server IPs from these various VPN Providers, and copy them into local server lists that VPNMON-R3 uses to reconnect your connections to.

1753111256996.png


NordVPN
Step 1)
Nord uses a single private key for all VPN servers. You will need to visit Nord and generate an access token before you can locate your private key.
  1. Go to this NordVPN link.
  2. Click the "Set up NordVPN manually" button.
    1753111577873.png
  3. After completing the email verification, you will be directed to a page that allows you to generate an access token. Click the "Generate new token" button.
    1753111602065.png
  4. Leave the token expiration as is (private keys don't seem to change, so it is fine if it expires in 30 days). Click the "Generate token" button.
    1753111618334.png
  5. Copy the access token for use in the next step and close the pop-up dialog.
    1753111630906.png

Step 2) Obtain your private key
Bash:
curl -s -u token:"{ACCESS_TOKEN_FROM_PREVIOUS_STEP}" "https://api.nordvpn.com/v1/users/services/credentials" | jq -r .nordlynx_private_key
Use this private key in the following curl examples.

Step 3) Generate your server list query using the Private Key you generated above. Examples:
  1. Top 5 Recommended WireGuard NordVPN Servers: This example shows you how to export all recommended NordVPN Server IPs based on a NordVPN algorithm per your location, and should be the fastest with the lowest load. Copy and paste the curl statements into an SSH window on your router to test their output.

    First, determine the ID for your chosen country. (Exact number is of great importance!). The ID is in [ ] directly next to your country name.
    Bash:
    curl --silent "https://api.nordvpn.com/v1/servers/countries" | jq --raw-output '.[] | . as $parent | .cities[] | [$parent.name, $parent.id, .name, .id] | "\(.[0]) [\(.[1])] - \(.[2]) [\(.[3])]"'
    Once you have found your Country ID, replace the ID in the curl statement below. In this example, I'm using the United States, which is ID 228. Also, replace your Private Key with the value you obtained previously. If the output creates a five-column list that looks like a CSV (example output below), you can copy/paste this statement into your VPNMON-R3 Server List Automation Slot and execute it in order to generate a list.
    Bash:
    curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 1 --retry-all-errors "https://api.nordvpn.com/v1/servers/recommendations?filters\[country_id\]=228&filters\[servers_groups\]\[identifier\]=legacy_standard&filters\[servers_technologies\]\[identifier\]=wireguard_udp&limit=5" | jq --raw-output '.[] | . as $parent | .technologies | .[] | select(.identifier == "wireguard_udp") | [$parent.name, $parent.station, .metadata[0].value] | "\(.[0]),\(.[1]),51820,{REPLACE_WITH_YOUR_PRIVATE_KEY},\(.[2])"'
    Output:
    Code:
    United States #9397,185.203.218.154,51820,PRIVATE_KEY,e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
    United States #9391,185.203.218.142,51820,PRIVATE_KEY,e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
    United States #9392,185.203.218.144,51820,PRIVATE_KEY,e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
    Another option, if you want to display the city and server ID in column one, looks like this (and is my preferred method):
    Bash:
    curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 1 --retry-all-errors "https://api.nordvpn.com/v1/servers/recommendations?filters\[country_id\]=228&filters\[servers_groups\]\[identifier\]=legacy_standard&filters\[servers_technologies\]\[identifier\]=wireguard_udp&limit=5" | jq --raw-output '.[] | . as $parent | .technologies | .[] | select(.identifier == "wireguard_udp") | [$parent.locations[].country.city.name, ($parent.name | split("#") | .[1]), $parent.station, .metadata[0].value] | "\(.[0]) #\(.[1]),\(.[2]),51820,{REPLACE_WITH_YOUR_PRIVATE_KEY},\(.[3])"'
    Output:
    Code:
    Miami #6610,37.120.215.35,51820,PRIVATE_KEY,e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
    Miami #9382,185.203.218.124,51820,PRIVATE_KEY,e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
    Miami #9396,185.203.218.152,51820,PRIVATE_KEY,e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
    Miami #5940,185.245.86.54,51820,PRIVATE_KEY,e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
    Miami #9394,185.203.218.148,51820,PRIVATE_KEY,e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
  2. All NordVPN Servers in a certain Country: This example shows you how to export all VPN Server IPs for a certain country. Copy and paste the curl statements into an SSH window on your router to test their output.

    First, determine the name of your chosen country using the same query as above that provided the ID number. (Exact spelling is of great importance!)

    Once you have found your country's name, replace it with the corresponding name in the curl statement below; repeat for the private key. In this example, I'm using "United States". If the output creates a five-column list that looks like a CSV, you can copy/paste this statement into your VPNMON-R3 Server List Automation Slot and execute it in order to generate a list.
    Bash:
    curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 1 --retry-all-errors https://api.nordvpn.com/v1/servers?limit=16354 | jq --raw-output '.[] | select(.locations[0].country.name == "United States") | . as $parent | .technologies | .[] | select(.identifier == "wireguard_udp") | [$parent.locations[].country.city.name, ($parent.name | split("#") | .[1]), $parent.station, .metadata[0].value] | "\(.[0]) #\(.[1]),\(.[2]),51820,{REPLACE_WITH_YOUR_PRIVATE_KEY},\(.[3])"'
  3. All NordVPN Servers in a certain City: In this example, I will show you how to filter all VPN Servers for a certain city. Copy and paste the curl statements into an SSH window on your router to test their output.

    Use the same query above to obtain country names/IDs to also determine accurate city names/IDs.

    Once you have found your country/city in the list, replace the city name (exact spelling) in the curl statement below. In this example, I'm using "New York". If the output creates a five-column list that looks like a CSV, you can copy/paste this statement into your VPNMON-R3 Server List Automation Slot and execute it in order to generate a list.
    Bash:
    curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 1 --retry-all-errors https://api.nordvpn.com/v1/servers?limit=16354 | jq --raw-output '.[] | select(.locations[0].country.city.name == "New York") | . as $parent | .technologies | .[] | select(.identifier == "wireguard_udp") | [$parent.locations[].country.city.name, ($parent.name | split("#") | .[1]), $parent.station, .metadata[0].value] | "\(.[0]) #\(.[1]),\(.[2]),51820,{REPLACE_WITH_YOUR_PRIVATE_KEY},\(.[3])"'
 
Last edited:
Note: VPNMON-R3 does not add the address or allowed IPs to the WireGuard configuration. You need to add these two items manually and ensure you enable WireGuard for the client you configured. I use 0.0.0.0/0 in allowed IPs. I also configured the Network section the way I wanted it.
1753299925215.png

NordVPN uses 10.5.0.2/32
ProtonVPN uses 10.2.0.2/32

ProtonVPN Note: You will need Python installed to run this. I used Python 3.12.6 and venv. Here is the list of packages you'll need to install via pip:
Code:
pip list
Package      Version
------------ -------
cffi         1.17.1
cryptography 45.0.5
pip          25.1.1
pycparser    2.22

  1. Go to network tab in developer tools in browser.
  2. Go to Downloads - ProtonVPN.
  3. Select the default file.
    1754057411465.png
  4. Scroll down to the request headers section to obtain required creds.
    1754057769376.png
  5. Update Creds section of Python script. Change Settings section as needed. Run script.
    Python:
    import http.client
    import http.cookies
    import json
    import base64
    import hashlib
    import os
    import random
    import argparse
    import time
    from cryptography.hazmat.primitives import serialization
    from cryptography.hazmat.primitives.asymmetric import x25519
    from cryptography.hazmat.primitives.asymmetric import ec
    from cryptography.hazmat.primitives import hashes
    
    """
    Copyright - FuseTim 2024
    
    This code is dual-licensed under both the MIT License and the Apache License 2.0.
    
    You may choose either license to govern your use of this code.
    
    MIT License:
    https://opensource.org/licenses/MIT
    
    Apache License 2.0:
    https://www.apache.org/licenses/LICENSE-2.0
    
    By contributing to this project, you agree that your contributions will be licensed under
    both the MIT License and the Apache License 2.0.
    """
    
    ######################################################################################
    
    # Credentials (found in Headers and Cookies) for certificate file
    auth_server = "" # See `x-pm-uid` header
    auth_token  = "" # See `AUTH-<x-pm-uid>` cookie
    session_id  = "" # See `Session-Id` cookie
    web_app_version = "" # See `x-pm-appversion` header
    
    # Settings
    prefix = "VPNMON-R3" # Prefix is used for config file and name in ProtonVPN Dashboard
    output_dir = "./"
    selected_countries = ["CO"]
    selected_cities = [ "Bogota" ]
    selected_tier = 2  # 0 = Free, 2 = Plus
    selected_features = [ "-TOR", "-SecureCore" ]  # Features that a server should have ("P2P", "TOR", "SecureCore", "XOR", etc) or not ("-P2P", etc)
    max_servers = 30  # Maximum of generated config
    max_servers_per_city = 30  # Maximum number of servers per city
    listing_only = False  # Do not generate config, just list available servers with previous selectors
    
    config_features = {
        "SafeMode": False,
        "SplitTCP": True,
        "PortForwarding": True,
        "RandomNAT": False,
        "NetShieldLevel": 0,  # 0, 1 or 2
    }
    
    ######################################################################################
    
    # Contants
    connection = http.client.HTTPSConnection("account.protonvpn.com")
    C = http.cookies.SimpleCookie()
    C["AUTH-"+auth_server] = auth_token
    C["Session-Id"] = session_id
    headers = {
        "x-pm-appversion": web_app_version,
        "x-pm-uid": auth_server,
        "Accept": "application/vnd.protonmail.v1+json",
        "Cookie": C.output(attrs=[],header="", sep="; ")
    }
    
    
    def generateKeys():
        """Generate a client key-pair using the API. Could be generated offline but need more work..."""
        print("Generating key-pair...")
        connection.request("GET", "/api/vpn/v1/certificate/key/EC", headers=headers)
        response = connection.getresponse()
        print("Status: {} and reason: {}".format(response.status, response.reason))
        resp = json.loads(response.read().decode())
        priv = resp["PrivateKey"].split("\n")[1]
        pub = resp["PublicKey"].split("\n")[1]
        print("Key generated:")
        print("priv:", priv)
        print("pub:", pub)
        return [resp["PrivateKey"], pub, priv]
    
    
    def getPubPEM(priv):
        """Return the Public key as string without headers"""
        return priv[1]
    
    def getPrivPEM(priv):
        """Return the Private key as PKCS#8 without headers"""
        return priv[2]
    
    def getPrivx25519(priv):
        """Return the x25519 base64-encoded private key, to be used in Wireguard config."""
        hash__ = hashlib.sha512(base64.b64decode(priv[2])[-32:]).digest()
        hash_ = list(hash__)[:32]
        hash_[0] &= 0xf8
        hash_[31] &= 0x7f
        hash_[31] |= 0x40
        new_priv = base64.b64encode(bytes(hash_)).decode()
        return new_priv
    
    
    def registerConfig(server, priv):
        """Register a Wireguard configuration and return its raw response."""
        h = headers.copy()
        h["Content-Type"]= "application/json"
        print("Registering Config for server", server["Name"],"...")
        body = {
    	"ClientPublicKey": getPubPEM(priv),
    	"Mode": "persistent",
    	"DeviceName": prefix+"-"+server["Name"],
    	"Features": {
    		"peerName": server["Name"],
    		"peerIp": server["Servers"][0]["EntryIP"],
    		"peerPublicKey": server["Servers"][0]["X25519PublicKey"],
    		"platform": "Windows",
                    # You can add features there (PortForwarding, SplitTCP, ModerateNAT
                    # See https://github.com/ProtonMail/WebClients/blob/8b5035d6f848b76d005814fca260bb616e83a4b2/packages/components/containers/vpn/WireGuardConfigurationSection/feature.ts#L53
                    "SafeMode": config_features["SafeMode"],
                    "SplitTCP": config_features["SplitTCP"],
                    "PortForwarding": config_features["PortForwarding"] if server["Features"] & 4 == 4 else False,
                    "RandomNAT": config_features["RandomNAT"],
                    "NetShieldLevel": config_features["NetShieldLevel"], # 0, 1 or 2
    	}
        }
        connection.request("POST", "/api/vpn/v1/certificate", body=json.dumps(body), headers=h)
        response = connection.getresponse()
        print("Status: {} and reason: {}".format(response.status, response.reason))
        resp = json.loads(response.read().decode())
        print(resp)
        return resp
    
    def generateConfig(priv, register):
        """Generate a Wireguard config using the ProtonVPN API answer."""
        conf = """[Interface]
    # Key for {prefix}
    PrivateKey = {priv}
    Address = 10.2.0.2/32
    
    [Peer]
    # {server_name}
    PublicKey = {server_pub}
    AllowedIPs = 0.0.0.0/0
    Endpoint = {server_endpoint}:51820
        """.format(prefix=prefix, priv=getPrivx25519(priv), server_name=register["Features"]["peerName"], server_pub=register["Features"]["peerPublicKey"], server_endpoint=register["Features"]["peerIp"])
        return conf
    
    
    def write_config_to_disk(name, conf):
        f = open(output_dir+"/"+name+".conf", "w")
        f.write(conf)
        f.close()
    
    
    # VPN Listings
    
    connection.request("GET", "/api/vpn/logicals", headers=headers)
    response = connection.getresponse()
    print("Status: {} and reason: {}".format(response.status, response.reason))
    
    servers = json.loads(response.read().decode())["LogicalServers"]
    
    for s in servers:
        feat = [
        "SecureCore" if s["Features"] & 1 == 1 else "-SecureCore",
        "TOR" if s["Features"] & 2 == 2 else "-TOR",
        "P2P" if s["Features"] & 4 == 4 else "-P2P",
        "XOR" if s["Features"] & 8 == 8 else "-XOR",
        "IPv6" if s["Features"] & 16 == 16 else "-IPv6"
        ]
        if (not s["EntryCountry"] in selected_countries and not s["ExitCountry"] in selected_countries) or s["Tier"] != selected_tier:
            continue
        if len(list(filter(lambda sf: not (sf in feat), selected_features))) > 0:
            continue
        print("- Server", s["Name"])
        print("  > ID:", s["ID"])
        print("  > EntryCountry:", s["EntryCountry"])
        print("  > ExitCountry:", s["ExitCountry"])
        print("  > Tier:", s["Tier"])
        print("  > Features:")
        print("      - SecureCore:", "Y" if s["Features"] & 1 == 1 else "N")
        print("      - Tor:", "Y" if s["Features"] & 2 == 2 else "N")
        print("      - P2P:", "Y" if s["Features"] & 4 == 4 else "N")
        print("      - XOR:", "Y" if s["Features"] & 8 == 8 else "N")
        print("      - IPv6:", "Y" if s["Features"] & 16 == 16 else "N")
        print("  > Score:", s["Score"])
        print("  > Load:", s["Load"])
        print("  > Status:", s["Status"])
        print("  > Instance:")
        for i in s["Servers"]:
            print("    - Instance n°",i["Label"],":", i["ID"])
            print("      > EntryIP:", i["EntryIP"])
            print("      > ExitIP:", i["ExitIP"])
            print("      > Domain:", i["Domain"])
            print("      > X25519PublicKey:", i["X25519PublicKey"])
        if not listing_only:
            keys = generateKeys()
            reg = registerConfig(s, keys)
            config = generateConfig(keys, reg)
            write_config_to_disk(reg["DeviceName"], config)
            max_servers-=1
        if (max_servers <= 0):
            break
        time.sleep(5)
    
    connection.close()
  6. Script generates CONF files in the same directory. Run this bash script where the files exist:
    Bash:
    #!/usr/bin/env bash
    
    PREFIX="Proton"
    
    for f in *.conf; do
      # Extract line 8, remove leading '# ' and trim
      name=$(sed -n '8s/^# *//p' "$f" | xargs)
      # Extract Endpoint, replace : with ,
      endpoint=$(grep -m1 '^Endpoint' "$f" | awk '{print $3}' | sed 's/:/,/')
      # Extract PrivateKey (first occurrence)
      privkey=$(grep -m1 '^PrivateKey' "$f" | awk '{print $3}')
      # Extract PublicKey (first occurrence)
      pubkey=$(grep -m1 '^PublicKey' "$f" | awk '{print $3}')
      # Output as CSV line
      echo "$PREFIX $name,$endpoint,$privkey,$pubkey"
    done > summary.txt
  7. Copy contents of summary.txt into the appropriate WG Client Slot Server List.
  8. Files expire after a year. Log into ProtonVPN via the web to extend the configurations' expiration dates.
 
Last edited:
Awesome @iTyPsIDg!! Thanks so much for doing this! :)

If you get a chance, could you also please include that step on how to create a .conf file to import and get your slot configured? That was also super helpful and a necessary step!
 
Awesome @iTyPsIDg!! Thanks so much for doing this! :)

If you get a chance, could you also please include that step on how to create a .conf file to import and get your slot configured? That was also super helpful and a necessary step!
See post #2. I think that handles it in the best way for now.
 
Awesome @iTyPsIDg!! Thanks so much for doing this! :)

If you get a chance, could you also please include that step on how to create a .conf file to import and get your slot configured? That was also super helpful and a necessary step!
See post #2. I think that handles it in the best way for now.
oh man! now I am itching to post the tutorial with my VPN provider steps here eventually as well. Should I reserve a couple of threads so they are at the beginning like yours ? @iTyPsIDg ?
 
This thread will share the same essence as VPNMON-R3 Custom OVPN Server List Generation Tutorials and Examples; however, this is for generating WireGuard server lists.

I currently only have Nord and Proton as VPN providers, so I'll focus on those for now.

Current VPN Providers Supported that utilize API functionality and include WireGuard details:
  • NordVPN
The examples below are provided to demonstrate how CURL + JQ queries can be used to auto-populate custom WG Server Lists within VPNMON-R3. For example, you may want your WG Client Slot 1 to only connect to servers in New York. WG Slot 2 might be populated with all the servers within the United States. Etc. I'm providing examples of what might be possible, and how to craft these statements so you can copy/paste them directly into VPNMON-R3 with minimal effort.

As shown below, the "WG/VPN Client Slot Server List Automation" menu allows you to View/Edit and Execute these statements, which will then pull the required VPN Server IPs from these various VPN Providers, and copy them into local server lists that VPNMON-R3 uses to reconnect your connections to.

View attachment 66837

NordVPN
Step 1)
Nord uses a single private key for all VPN servers. You will need to visit Nord and generate an access token before you can locate your private key.
  1. Go to this NordVPN link.
  2. Click the "Set up NordVPN manually" button.
  3. After completing the email verification, you will be directed to a page that allows you to generate an access token. Click the "Generate new token" button.
  4. Leave the token expiration as is (private keys don't seem to change, so it is fine if it expires in 30 days). Click the "Generate token" button.
  5. Copy the access token for use in the next step and close the pop-up dialog.

Step 2) Obtain your private key
Bash:
curl -s -u token:"{ACCESS_TOKEN_FROM_PREVIOUS_STEP}" "https://api.nordvpn.com/v1/users/services/credentials" | jq -r .nordlynx_private_key
Use this private key in the following curl examples.

Step 3) Generate your server list query using the Private Key you generated above. Examples:
  1. Top 5 Recommended WireGuard NordVPN Servers: This example shows you how to export all recommended NordVPN Server IPs based on a NordVPN algorithm per your location, and should be the fastest with the lowest load. Copy and paste the curl statements into an SSH window on your router to test their output.

    First, determine the ID for your chosen country. (Exact number is of great importance!). The ID is in [ ] directly next to your country name.
    Bash:
    curl --silent "https://api.nordvpn.com/v1/servers/countries" | jq --raw-output '.[] | . as $parent | .cities[] | [$parent.name, $parent.id, .name, .id] | "\(.[0]) [\(.[1])] - \(.[2]) [\(.[3])]"'
    Once you have found your Country ID, replace the ID in the curl statement below. In this example, I'm using the United States, which is ID 228. Also, replace your Private Key with the value you obtained previously. If the output creates a five-column list that looks like a CSV (example output below), you can copy/paste this statement into your VPNMON-R3 Server List Automation Slot and execute it in order to generate a list.
    Bash:
    curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 1 --retry-all-errors "https://api.nordvpn.com/v1/servers/recommendations?filters\[country_id\]=228&filters\[servers_groups\]\[identifier\]=legacy_standard&filters\[servers_technologies\]\[identifier\]=wireguard_udp&limit=5" | jq --raw-output '.[] | . as $parent | .technologies | .[] | select(.identifier == "wireguard_udp") | [$parent.name, $parent.station, .metadata[0].value] | "\(.[0]),\(.[1]),51820,{REPLACE_WITH_YOUR_PRIVATE_KEY},\(.[2])"'
    Output:
    Code:
    United States #9397,185.203.218.154,51820,PRIVATE_KEY,e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
    United States #9391,185.203.218.142,51820,PRIVATE_KEY,e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
    United States #9392,185.203.218.144,51820,PRIVATE_KEY,e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
    Another option, if you want to display the city and server ID in column one, looks like this (and is my preferred method):
    Bash:
    curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 1 --retry-all-errors "https://api.nordvpn.com/v1/servers/recommendations?filters\[country_id\]=228&filters\[servers_groups\]\[identifier\]=legacy_standard&filters\[servers_technologies\]\[identifier\]=wireguard_udp&limit=5" | jq --raw-output '.[] | . as $parent | .technologies | .[] | select(.identifier == "wireguard_udp") | [$parent.locations[].country.city.name, ($parent.name | split("#") | .[1]), $parent.station, .metadata[0].value] | "\(.[0]) #\(.[1]),\(.[2]),51820,{REPLACE_WITH_YOUR_PRIVATE_KEY},\(.[3])"'
    Output:
    Code:
    Miami #6610,37.120.215.35,51820,PRIVATE_KEY,e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
    Miami #9382,185.203.218.124,51820,PRIVATE_KEY,e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
    Miami #9396,185.203.218.152,51820,PRIVATE_KEY,e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
    Miami #5940,185.245.86.54,51820,PRIVATE_KEY,e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
    Miami #9394,185.203.218.148,51820,PRIVATE_KEY,e8LKAc+f9xEzq9Ar7+MfKRrs+gZ/4yzvpRJLRJ/VJ1w=
  2. All NordVPN Servers in a certain Country: This example shows you how to export all VPN Server IPs for a certain country. Copy and paste the curl statements into an SSH window on your router to test their output.

    First, determine the name of your chosen country using the same query as above that provided the ID number. (Exact spelling is of great importance!)

    Once you have found your country's name, replace it with the corresponding name in the curl statement below; repeat for the private key. In this example, I'm using "United States". If the output creates a five-column list that looks like a CSV, you can copy/paste this statement into your VPNMON-R3 Server List Automation Slot and execute it in order to generate a list.
    Bash:
    curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 1 --retry-all-errors https://api.nordvpn.com/v1/servers?limit=16354 | jq --raw-output '.[] | select(.locations[0].country.name == "United States") | . as $parent | .technologies | .[] | select(.identifier == "wireguard_udp") | [$parent.locations[].country.city.name, ($parent.name | split("#") | .[1]), $parent.station, .metadata[0].value] | "\(.[0]) #\(.[1]),\(.[2]),51820,{REPLACE_WITH_YOUR_PRIVATE_KEY},\(.[3])"'
  3. All NordVPN Servers in a certain City: In this example, I will show you how to filter all VPN Servers for a certain city. Copy and paste the curl statements into an SSH window on your router to test their output.

    Use the same query above to obtain country names/IDs to also determine accurate city names/IDs.

    Once you have found your country/city in the list, replace the city name (exact spelling) in the curl statement below. In this example, I'm using "New York". If the output creates a five-column list that looks like a CSV, you can copy/paste this statement into your VPNMON-R3 Server List Automation Slot and execute it in order to generate a list.
    Bash:
    curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 1 --retry-all-errors https://api.nordvpn.com/v1/servers?limit=16354 | jq --raw-output '.[] | select(.locations[0].country.city.name == "New York") | . as $parent | .technologies | .[] | select(.identifier == "wireguard_udp") | [$parent.locations[].country.city.name, ($parent.name | split("#") | .[1]), $parent.station, .metadata[0].value] | "\(.[0]) #\(.[1]),\(.[2]),51820,{REPLACE_WITH_YOUR_PRIVATE_KEY},\(.[3])"'


RE: Private Internet Access (PIA)
Interested and able to do anything with the info found here? https://github.com/pia-foss/manual-connections?tab=readme-ov-file#3rd-party-repositories
 
Last edited:
oh man! now I am itching to post the tutorial with my VPN provider steps here eventually as well. Should I reserve a couple of threads so they are at the beginning like yours ? @iTyPsIDg ?
I'm happy to add and credit you in one of the reserved posts. I have some Proton instructions (though not nearly as elegant as Nord). I'll post those here next week.
 

Support SNBForums w/ Amazon

If you'd like to support SNBForums, just use this link and buy anything on Amazon. Thanks!

Sign Up For SNBForums Daily Digest

Get an update of what's new every day delivered to your mailbox. Sign up here!
Back
Top