Skip to content

Instantly share code, notes, and snippets.

@colemar
Last active September 8, 2024 17:01
Show Gist options
  • Save colemar/030fe6eef8f01858052dc6c29c8cc022 to your computer and use it in GitHub Desktop.
Save colemar/030fe6eef8f01858052dc6c29c8cc022 to your computer and use it in GitHub Desktop.
Manage Wireguard peers (IPv4 only)

Manage Wireguard peers (IPv4 only)

List, add or delete Wireguard peers. Also show client configuration for already added peers.

wg-peer [add|show <peer>|del <peer>|list]

where:

  • a[dd] add a new peer
  • s[how] show peer configuration
  • d[el] delete peer
  • l[ist] list peers

<peer> is the peer public key as shown by list command

Configuration files for clients are saved in /etc/wireguard/clients. If there are multiple Wireguard interfaces, wg-peer chooses the first one listed by wg show interfaces (most likely wg0).

Examples

  • list peers: wg-peer l
  • add a new peer: wg-peer a
  • show client configuration for peer: wg-peer s kTU5yhp1qPBHsKhKs4aSgPKRotU4bGPhl3y8dHD1Ki4=
  • delete peer: wg-peer d kTU5yhp1qPBHsKhKs4aSgPKRotU4bGPhl3y8dHD1Ki4=

Screenshots

Screenshot Screenshot Screenshot Screenshot

Based on new-wireguard-peer.sh by robinlandstrom.

License

#!/bin/bash
# License: https://creativecommons.org/publicdomain/zero/1.0/
readonly CFOLDER=/etc/wireguard/clients
readonly THISFILE=$(basename $0)
# =================================================
main () {
# Check for root privileges
[[ $USER == root ]] || { echo "Root privileges required. Try: sudo $THISFILE"; return 1; }
# Find Wireguard interface
if=$(wg show interfaces)
[[ $if =~ $'\n' ]] && { echo "WARNING: multiple Wireguard interfaces detected"; }
read -r INTERFACE <<<$if; readonly INTERFACE
echo "Using interface $INTERFACE"
case $1 in
a*)
add_new_peer
;;
s*)
show_peer_conf $2
;;
d*)
delete_peer $2
;;
l*)
list_peers
;;
*)
usage
;;
esac
}
# =================================================
usage () {
cat <<USAGE
$THISFILE [add|show <peer>|del <peer>|list]
a[dd] : add a new peer
s[how] : show peer configuration
d[el] : delete peer
l[ist] : list peers
<peer> is peer public key as shown by list command
USAGE
}
build_peer_list () {
PEERLIST=':'$(wg show $INTERFACE peers | awk '{printf $1":"}')
}
check_peer_exists () {
local pk=$1
build_peer_list
[[ $PEERLIST =~ ":${pk}:" ]] || { echo "Cannot find peer $pk"; return 1; }
}
show_peer_conf () {
local pk=$1
check_peer_exists $pk || return 1
local clientfile=$CFOLDER/$(md5sum <<<${pk}); clientfile=${clientfile%% *}
[[ -r $clientfile ]] || { echo "Cannot read $clientfile"; return 1; }
qrencode -t ANSIUTF8 < $clientfile
cat $clientfile
echo -e "---------\n"
wg show $INTERFACE | awk '$0=="peer: '${pk}'"{f=1; print; next} /^peer:/{f=0} f'
}
delete_peer () {
local pk=$1
check_peer_exists $pk || return 1
wg set $INTERFACE peer $pk remove && {
wg-quick save ${INTERFACE}
clientfile=$CFOLDER/$(md5sum <<<${pk}); clientfile=${clientfile%% *}
rm -f $clientfile
echo "Removed peer $pk"
} || { echo "Cannot remove peer $pk"; return 1; }
}
list_peers () {
wg show $INTERFACE |\
awk '/^peer:/{
f=1
print "# '$THISFILE' show '\''"$2"'\''"
print "# '$THISFILE' del '\''"$2"'\''"
print
next
}
f'
}
add_new_peer () {
# Generate peer keys
PRIVATE_KEY=$(wg genkey)
PUBLIC_KEY=$(echo ${PRIVATE_KEY} | wg pubkey)
PRESHARED_KEY=$(wg genpsk)
# Read server key from interface
SERVER_PUBLIC_KEY=$(wg show ${INTERFACE} public-key)
# Get next free peer IP (This will break after x.x.x.255)
PEER_ADDRESS=$(wg show ${INTERFACE} allowed-ips |\
cut -f 2 |\
awk -F'[./]' '{print $1"."$2"."$3"."1+$4"/"$5}' |\
sort -t '.' -k 1,1 -k 2,2 -k 3,3 -k 4,4 -n | tail -n1)
# Try to guess nameserver
NAMESERVER=$(ip -j addr show $INTERFACE | jq -r '.[0].addr_info[0].local')
nslookup google.com $NAMESERVER > /dev/null || {
NAMESERVER=$(nslookup bogusname | awk '/^Server:/{print $2}')
[[ ! $NAMESERVER =~ ^127 ]] && nslookup google.com $NAMESERVER > /dev/null || NAMESERVER="8.8.8.8, 1.1.1.1"
}
echo "Guessed nameserver: $NAMESERVER"
LISTENPORT=$(wg show ${INTERFACE} listen-port)
ENDPOINT=$(curl -s ipinfo.io | jq -r '.ip')
# Add peer
wg set ${INTERFACE} peer ${PUBLIC_KEY} preshared-key <(echo ${PRESHARED_KEY}) allowed-ips ${PEER_ADDRESS} || {
echo "Cannot add peer ${PEER_ADDRESS} with public key ${PUBLIC_KEY}"
return 1
}
wg-quick save ${INTERFACE}
# Generate peer config
read -r -d$'\x04' CONFIG << END_OF_CONFIG
[Interface]
Address = ${PEER_ADDRESS}
PrivateKey = ${PRIVATE_KEY}
DNS = ${NAMESERVER}
[Peer]
PublicKey = ${SERVER_PUBLIC_KEY}
PresharedKey = ${PRESHARED_KEY}
AllowedIPs = 0.0.0.0/1, 128.0.0.0/1
Endpoint = ${ENDPOINT}:${LISTENPORT}
END_OF_CONFIG
# Save added peer config
clientfile=$CFOLDER/$(md5sum <<<${PUBLIC_KEY}); clientfile=${clientfile%% *}
mkdir -p $CFOLDER
touch $clientfile
chmod go-rwx $clientfile
echo "$CONFIG" >${clientfile}
# Show added peer config
show_peer_conf ${PUBLIC_KEY}
}
# =================================================
main "$@"
@nicholasmireles
Copy link

Some minor tweaks if you don't have/want jq installed:

Line 111:

  • This could simply be NAMESERVER=$(resolvectl dns eth0 | cut -d" " -f4) if you know eth0 is your normal interface.

Line 119:

  • This similarly can be changed to ENDPOINT=$(curl -s https://ipinfo.io/ip)

@sskras
Copy link

sskras commented Sep 5, 2024

ping @colemar: have you thought about converting this gist into fully featured repo?
Also I am interested in the license / conditions under which this script is to be distributed / modified :)

@colemar
Copy link
Author

colemar commented Sep 5, 2024

@sskras No time now for a full featured repo.
License: https://unlicense.org/
I only ask to refer this page if this is distributed as is or modified.

@sskras
Copy link

sskras commented Sep 5, 2024

@colemar: thanks, sure. Can you please confirm that CC0 is OK too? Asking because of the reasons mentioned in the article.

@colemar
Copy link
Author

colemar commented Sep 6, 2024

@colemar: thanks, sure. Can you please confirm that CC0 is OK too? Asking because of the reasons mentioned in the article.

Yes, confirmed.
Added CC0 license mention above.

@sskras
Copy link

sskras commented Sep 7, 2024

@colemar, thanks. Since you mentioned the @robinlandstrom implementation as the basis, I imported it as the first commit. Then I renamed the script to match wg-peer name from your Gist.

Then I exported all changes from it and overlaid them onto the original work, so it can be seen as the continuous history (which I suppose have been). During this I also added my comments to every of your change:
https://github.com/mirrors-git/wg-peer/commits/main/

For publishing I decided to use my GitHub organization dedicated for mirroring code.

Finally I renamed the #wg-peer.md file into README.md so readers can see its' content out of the box. For that I branched main as main.With-README, and made it the repo's default:
https://github.com/mirrors-git/wg-peer

In case you see something wrong, please ping me.

Now I am going to fork it and make some changes. There are at least three other GitHub repos named wg-peer, so I guess I will go with wg-peers for that.

@colemar
Copy link
Author

colemar commented Sep 8, 2024

In case you see something wrong, please ping me.

Now I am going to fork it and make some changes. There are at least three other GitHub repos named wg-peer, so I guess I will go with wg-peers for that.

@sskras Nice work. I have no objections.
I know it can be improved, but at one point I declared it satisfactory for my needs. I hope you can make it more general.

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