sfiorini / NordVPN-Wireguard

A bash scripts that generates Wireguard configuration file for a NordVPN connection.
229 stars 40 forks source link

Won't generate a config for anything other than the local "recommended" server #2

Open freefly42 opened 2 years ago

freefly42 commented 2 years ago

While you connect to the specified country when you run the nordvpn c $COUNTRY $CITY to get the private key and local address information, the API call you make using curl where you get the hostname and public key for the server doesn't specify the specific server you connected to in the proper country. So you should either edit your curl call to specify the same server as you connect to with nordvpn, or modify the curl call to get the server info (adding the country/city specification to the API call) and then connect to that host with nordvpn to get the private key and IP information. Or you could do something like this while connected to get the endpoint name and public key (leaving no reason to make the API call in curl):

ENDPOINT=$(nordvpn status | grep 'Current server' | awk '{print $3}')
PUBKEY=$(sudo wg show nordlynx public-key)

For client connections I would recommend removing the PersistentKeepAlive option as you only need it if you want the server to be able to arbitrarily reach you, or hosts on the server side to be able to reach you. It defeats the "keep quiet unless needed" security benefit of wireguard.

You could also make your script much easier to follow (and in the event of an error less likely to overwrite a good configuration file with a partially generated configuration file) by doing something like this at the end to write the file:

cat <<EOF > wg0.conf
#config for ${INFO}
[Interface]
Address = ${MYIP}
PrivateKey = ${PRIVATE}
ListenPort = 51820
DNS = 103.86.96.100, 103.86.99.100

[Peer]
PublicKey = ${PUBKEY}
AllowedIPs = 0.0.0.0/0, ::0/0
Endpoint = ${ENDPOINT}:51820
EOF
sfiorini commented 2 years ago

@freefly42 If you specify the country it gives you back the best server for that country. If you just specify the city, it gives you the best server for that city. You are right, I don't think they work together at the moment. Would you mind to submit a pull request with the proposed changes?

I'd love your contribution. Stefano

freefly42 commented 2 years ago

I can't give you a pull request as I don't use your code, and haven't forked it. I use a significantly different approach as I've found the private key and IP don't change, so I can really quickly get a server from just the API using stored private data. If the connection doesn't work, I regenerate the private data. However, my script for getting the private data will get exactly the information you want from your nordvpn connection. I will share both scripts here for your edification:

nordregen.sh Any arguments passed to this script I pass to the nordvpn connection, so I can specify a country or other options if desired. It collects the private data and stores it in a form that's designed to source as a shell script (so no further parsing is required):

#!/bin/bash
SOURCE_SCRIPT=$(dirname "$(realpath "$0")")/.nord.sh

nordvpn connect $@ || {
    echo "Unable to connect to NordVPN."
    exit 1
}

MYIP=$(ifconfig nordlynx | grep inet | awk '{print $2}')
PRIVATE=$(sudo wg show nordlynx private-key)
PUBKEY=$(sudo wg show nordlynx | grep peer | awk '{print $2}')
ENDPOINT=$(nordvpn status | grep 'Current server' | awk '{print $3}')

nordvpn disconnect &

cat <<EOF > "${SOURCE_SCRIPT}"
PRIVATE='${PRIVATE}'
MYIP='${MYIP}'
PUBKEY='${PUBKEY}'
ENDPOINT='${ENDPOINT}'
EOF

Since the connections to nord vpn take some time, I have a different script for quickly generating a config based on the API nordgen.sh:

#!/bin/bash
MYDIR=$(dirname "$(realpath "$0")")
SOURCE_SCRIPT="${MYDIR}"/.nord.sh

source "${SOURCE_SCRIPT}" || {
    echo "no secrets found, rerun nordregen.sh"
    . "${MYDIR}"/nordregen.sh
    source "${SOURCE_SCRIPT}" || exit 1
}

FULL_JSON=$(curl -s "https://api.nordvpn.com/v1/servers/recommendations?&filters\[servers_groups\]\]identifier\]=legacy_p2p&\[servers_groups\]\]identifier\]=legacy_obfuscated_servers&\[servers_technologies\]\[identifier\]=wireguard_udp&limit=1")
ENDPOINT=$(echo "$FULL_JSON" | jq -r '.[]|.station')
CITY=$(echo "$FULL_JSON" | jq -r '.[]|(.locations|.[]|.country|.city.name)')
COUNTRY=$(echo "$FULL_JSON" | jq -r '.[]|(.locations|.[]|.country|.name)')
HOSTNAME=$(echo "$FULL_JSON" | jq -r '.[]|.hostname')
PUBKEY=$(echo "$FULL_JSON" | jq -r '.[]|(.technologies|.[].metadata|.[].value)')
LOAD=$(echo "$FULL_JSON" | jq -r '.[]|.load')
INFO=$(echo $HOSTNAME $COUNTRY $CITY was loaded $LOAD)
echo $INFO

cat <<EOF > wg0.conf
#config for ${INFO}
[Interface]
Address = ${MYIP}
PrivateKey = ${PRIVATE}
ListenPort = 51820
DNS = 103.86.96.100, 103.86.99.100

[Peer]
PublicKey = ${PUBKEY}
AllowedIPs = 0.0.0.0/0, ::0/0
Endpoint = ${ENDPOINT}:51820
EOF

So you could either add a filter to the curl call to get a server in the country you're interested in: filters\[country_id\]=81 for example for Germany, I believe. You'd have to look up the country ID then get the server name and supply it as an argument to nordvpn connect, but I think with your approach you'd be better off just getting the information off the nordvpn connection while it's connected and bypass the API altogether (my nordregen.sh script does this, but doesn't put the output into a wg conf file).

sfiorini commented 2 years ago

@freefly42 Thank you! I will take a look as soon as I have a change and sure use your code for improvements. Let's leave this open so I don't forget to get to it. Thank you again, Stefano

freefly42 commented 2 years ago

Ok I decided to pare it down to a single script for you. This would do what you want, supply the arguments to the script to specify options to nordvpn to specify country server type etc. So to get a connection for Germany just do: ./script.sh Germany

#!/bin/bash

nordvpn connect $@ || {
    echo "Unable to connect to NordVPN."
    exit 1
}

MYIP=$(ifconfig nordlynx | grep inet | awk '{print $2}')
PRIVATE=$(sudo wg show nordlynx private-key)
PUBKEY=$(sudo wg show nordlynx | grep peer | awk '{print $2}')
ENDPOINT=$(nordvpn status | grep 'Current server' | awk '{print $3}')

nordvpn disconnect &

cat <<EOF > wg0.conf
[Interface]
Address = ${MYIP}
PrivateKey = ${PRIVATE}
ListenPort = 51820
DNS = 103.86.96.100, 103.86.99.100

[Peer]
PublicKey = ${PUBKEY}
AllowedIPs = 0.0.0.0/0, ::0/0
Endpoint = ${ENDPOINT}:51820
EOF