Skip to content

Instantly share code, notes, and snippets.

@skipperTux
Last active April 25, 2024 10:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skipperTux/5bbc3d261c82c21bfee530d518a4bc38 to your computer and use it in GitHub Desktop.
Save skipperTux/5bbc3d261c82c21bfee530d518a4bc38 to your computer and use it in GitHub Desktop.
Bash script for managing WireGuard connections using NetworkManager CLI
#!/usr/bin/env bash
set -o nounset
set -o pipefail
#set -o xtrace
# http://www.tldp.org/LDP/abs/html/exitcodes.html
# user-defined exit codes range 64 - 113 (in addition to 0, for success)
readonly SUCCESS=0
readonly E_NO_CONNECTION_PROFILE=64
readonly E_UNKNOWN_CONNECTION_PROFILE=65
readonly E_INVALID_CONNECTION_STATUS=66
readonly E_CONNECTION_NAME_TOO_LONG=67
# constant variables
YELLOW=$(tput setaf 226)
readonly YELLOW
ORANGE=$(tput setaf 208)
readonly ORANGE
RED=$(tput setaf 196)
readonly RED
RESET=$(tput sgr0)
readonly RESET
readonly INFO="${YELLOW}INFO:${RESET} "
readonly WARNING="${ORANGE}WARNING:${RESET} "
readonly ERROR="${RED}ERROR:${RESET} "
readonly DQT='"'
readonly WIREGUARD_TYPE='wireguard'
readonly WIREGUARD_CONFIGS='/etc/wireguard'
readonly CONNECTION_NAME_MAX_LENGTH=15
# variables
connection='up'
autoconnect=false
# https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash/39398359#39398359
# As long as there is at least one more argument, keep looping
while [[ $# -gt 0 ]]; do
key="$1"
case "$key" in
# Also a flag type option. Will catch either -i or --import
-i|--import)
connection='import'
;;
# Also a flag type option. Will catch either -rm or --remove
-rm|--remove)
connection='remove'
;;
# Also a flag type option. Will catch either -r or --reload
-r|--reload)
connection='reload'
;;
# This is a flag type option. Will catch either -u or --up
-u|--up)
connection='up'
;;
# Also a flag type option. Will catch either -d or --down
-d|--down)
connection='down'
;;
# This is a flag type option. Will catch either -h or --help
-h|--help)
connection='help'
;;
# Also a flag type option. Will catch either -a or --auto-connect
-a|--auto-connect)
autoconnect=true
;;
# This is an arg value type option. Will catch -ds value or --dns-search value
-ds|--dns-search)
shift # past the key and to the value
dns_search="$1"
;;
# This is an arg=value type option. Will catch -ds=value or --dns-search=value
-ds=*|--dns-search=*)
# No need to shift here since the value is part of the same string
dns_search="${key#*=}"
;;
# This is an arg value type option. Will catch -cn value or --connection-name value
-cn|--connection-name)
shift # past the key and to the value
connection_name="$1"
;;
# This is an arg=value type option. Will catch -cn=value or --connection-name=value
-cn=*|--connection-name=*)
# No need to shift here since the value is part of the same string
connection_name="${key#*=}"
;;
*)
# Do whatever you want with extra argument
echo "Unknown argument '$key'"
connection='help'
;;
esac
# Shift after checking all the cases to get the next option
shift
done
# functions
show_help() {
# display help
echo "Usage: wg-nmcli [COMMAND] [IMPORT-OPTIONS] { CONNECTION-NAME | help }"
echo "Manage WireGuard connections using NetworkManager CLI."
echo
echo "COMMAND"
echo " -i, --import import an external WireGuard configuration as a NetworkManager connection profile"
echo "-rm, --remove delete a WireGuard connection profile"
echo " -r, --reload re-import (remove and import) an external WireGuard configuration"
echo " -u, --up activate a WireGuard connection (default)"
echo " -d, --down deactivate a WireGuard connection"
echo " -h, --help print this help"
echo
echo "IMPORT-OPTIONS"
echo " -a, --auto-connect enable auto-connect for WireGuard connection profile"
echo "-ds, --dns-search[=]ARG Modify 'DNS domain' property of the WireGuard connection profile"
echo " ARG := ${DQT}comma separated list of domains${DQT} (see https://blogs.gnome.org/mcatanzaro/2020/12/17/understanding-systemd-resolved-split-dns-and-vpn-configuration/)"
echo
echo "CONNECTION-NAME"
echo "-cn, --connection-name[=]ARG name of the WireGuard connection profile (mandatory)"
echo " ARG := ${DQT}WireGuard connection profile name${DQT}"
echo
}
connection_status() {
if [ "${connection_name:-}" = "" ]; then
echo "${ERROR}Please provide a WireGuard connection profile name."
exit ${E_NO_CONNECTION_PROFILE}
fi
if [ "${#connection_name}" -gt ${CONNECTION_NAME_MAX_LENGTH} ]; then
echo "${ERROR}WireGuard connection name too long, must not exceed ${CONNECTION_NAME_MAX_LENGTH}."
exit ${E_CONNECTION_NAME_TOO_LONG}
fi
conn_exists=$(nmcli connection show | grep -icP "(${connection_name})(.*)(${WIREGUARD_TYPE})")
conn_up=$(nmcli connection show | grep -icP "(${connection_name})(.*)(${WIREGUARD_TYPE})(\s*)(${connection_name})")
if [ "${conn_exists}" -eq 0 ]; then
if [ "${connection:-}" != "import" ]; then
echo "${ERROR}WireGuard connection profile '${connection_name}' does not exist."
exit ${E_UNKNOWN_CONNECTION_PROFILE}
fi
else
if [ "${connection:-}" = "import" ]; then
connection='reload'
echo "${INFO}WireGuard connection profile '${connection_name}' already exists, reloading."
fi
fi
}
connection_up() {
if [ "${conn_up}" -eq 1 ]; then
echo "${WARNING}WireGuard connection '${connection_name}' already active."
exit ${E_INVALID_CONNECTION_STATUS}
fi
nmcli connection up "${connection_name}"
}
connection_down() {
if [ "${conn_up}" -eq 0 ]; then
echo "${WARNING}WireGuard connection '${connection_name}' not active."
exit ${E_INVALID_CONNECTION_STATUS}
fi
nmcli connection down "${connection_name}"
}
connection_import() {
sudo nmcli connection import type ${WIREGUARD_TYPE} file "${WIREGUARD_CONFIGS}/${connection_name}.conf"
if [ "${autoconnect:-}" != "true" ]; then
nmcli connection modify "${connection_name}" autoconnect no
fi
nmcli connection modify "${connection_name}" ipv4.never-default true
if [ "${dns_search:-}" != "" ]; then
nmcli connection modify "${connection_name}" ipv4.dns-search "${dns_search}"
fi
nmcli connection modify "${connection_name}" connection.permissions "user:$(id -nu)"
}
connection_remove() {
sudo nmcli connection delete "${connection_name}"
}
connection_reload() {
connection_remove
connection_import
}
# script main
if [ "${connection:-}" = "help" ]; then
show_help
exit ${SUCCESS}
fi
connection_status
case "${connection:-}" in
up)
connection_up
;;
down)
connection_down
;;
import)
connection_import
;;
remove)
connection_remove
;;
reload)
connection_reload
;;
*)
show_help
;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment