diff --git a/README.md b/README.md index af904ee..b38ac55 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,106 @@ -# template-bash +# wireguard-management -Template repository for projects on bash +Wireguard management and monitoring utils. -* [`script.sh`](https://git.hmp.today/pavel.muhortov/template-bash#script-sh) +* [`wg-client-management`.sh](https://git.hmp.today/pavel.muhortov/wireguard-management#wg-client-management-sh) +* [`wg-connect-handling`.sh](https://git.hmp.today/pavel.muhortov/wireguard-management#wg-connect-handling-sh) ____ -## `script.sh` +## `wg-client-management`.sh **Description:** -> returning current username if privileged rights are exist -> or -> returning error, if privileged rights are not exist +> Creating or deleting client config for wireguard and sending config and info to email. **Dependencies:** > -> * [bash](https://www.gnu.org/software/bash/) (tested version 5.1.4 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/)) -> * [whoami](https://www.gnu.org/software/coreutils/whoami) (tested version 8.30 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/)) +> * privileged rights +> * [wireguard](https://www.wireguard.com/) (tested version 1.0.2 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/)) +> * [qrencode](https://github.com/fukuchi/libqrencode) (tested version 4.1.1 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/)) +> * [grepcidr](https://github.com/ryantig/grepcidr) (tested version 2.0 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/)) +> * [Python 3](https://www.python.org/downloads/) (tested version 3.9.5 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/)) +> * existing [/usr/local/bin/sendmail.py](https://git.hmp.today/pavel.muhortov/utils#sendmail-py) | POSITION | PARAMETERS | DESCRIPTION | DEFAULT | |-----------|--------------|------------------------|---------------| -| 1 |**[qn]**|execution without pauses|| -| 2 |**[/path/to/conf]**|path to config|`./script.conf`| +| 1 |**add\|del**|add or delete client config|**REQUIRED**| +| 2 |**\**|client username|**REQUIRED**| +| 3 |**\**|client ip address|**REQUIRED**| +| 4 |**[-f]**,**[--force]**|service will restart after add\|del username|| -Example usage in terminal with bash: +Example usage: ```bash -bash ./script.sh qn ./script.conf +# download +sudo wget https://git.hmp.today/pavel.muhortov/wireguard-managemen/raw/branch/master/wg-client-management.sh -O /etc/wireguard/wg-client-management.sh +sudo chmod +x /etc/wireguard/wg-client-management.sh ``` -Example usage in terminal with make the script executable: - ```bash -chmod u+x ./script.sh -script.sh +# create link +ln -s /etc/wireguard/wg-client-management.sh ./wg +``` + +```bash +# create client +sudo ./wg add username address +``` + +```bash +# delete client and restart service +sudo ./wg del username address -f +``` + +```bash +# check journal +tail -f /var/log/wireguard/wg.log +``` + +____ + +## `wg-connect-handling`.sh + +**Description:** +> Handling client connection and preparing stats for monitoring. + +**Dependencies:** +> +> * privileged rights +> * [jq](https://github.com/stedolan/jq) (tested version 1.6 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/)) +> * [grepcidr](https://github.com/ryantig/grepcidr) (tested version 2.0 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/)) +> * [Python 3](https://www.python.org/downloads/) (tested version 3.9.5 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/)) +> * existing [/usr/local/bin/sendmail.py](https://git.hmp.today/pavel.muhortov/utils#sendmail-py) +> * [bash](https://www.gnu.org/software/bash/) (tested versions: 5.1.4 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/), 5.0.17 on [Ubuntu 20](https://wiki.ubuntu.com/FocalFossa/ReleaseNotes), 4.2.46 on [CentOS 7](https://wiki.centos.org/Manuals/ReleaseNotes/CentOS7.2009)) + +| POSITION | PARAMETERS | DESCRIPTION | DEFAULT | +|-----------|--------------|------------------------|---------------| +| 1 |****|root path for counter, names, log|**REQUIRED**| +| 2 |**[mail]**|send email notification|| +| 3 |**[geo]**|check client address geolocation|| + +Example usage: + +```bash +# download +sudo wget https://git.hmp.today/pavel.muhortov/wireguard-managemen/raw/branch/master/wg-connect-handling.sh -O /etc/wireguard/wg-connect-handling.sh +sudo chmod +x /etc/wireguard/wg-connect-handling.sh +``` + +```bash +# create root path for counter, names, log +sudo mkdir /var/log/wireguard +sudo chown -R root:root /var/log/wireguard +sudo chmod -R 755 /var/log/wireguard +``` + +```bash +# sudo crontab -e +* * * * * bash /etc/wireguard/wg-connect-handling.sh /var/log/wireguard mail geo +``` + +```bash +# check counter and names +watch cat /var/log/wireguard/wg-counts.log +# check journal +tail -f /var/log/wireguard/wg-connect-handling.log ``` diff --git a/script.sh b/script.sh deleted file mode 100644 index 60f4d6b..0000000 --- a/script.sh +++ /dev/null @@ -1,125 +0,0 @@ -#! /bin/bash - -# DESCRIPTION: -# returning current username if privileged rights are exist -# or -# returning error, if privileged rights are not exist -# -# DEPENDENCIES: -# - whoami -# -# PARAMETERS: -# 1: "qn" - execution without pauses -# 2: custom configuration file path -# -# FUNCTIONS: -# - -####################################### -# Print message and add to log. -# Globals: -# logs -# Arguments: -# 1: message to print and logging -####################################### -addtologs() { - echo "$(date +'%Y.%m.%d-%H:%M:%S') $1" | tee -a "${logs}" -} - -####################################### -# Waiting for press [ENTER]. -# Globals: -# None -# Arguments: -# None -####################################### -execpause() { - read -r -p "Press [ENTER] to continue... " -} - -####################################### -# Exit procedure. -# Globals: -# show -# Arguments: -# None -####################################### -execquite() { - addtologs "execution time is $(($(date +%s)-time)) seconds, exit" - if [ "${show}" != "qn" ]; then - execpause - fi - exit -} - -####################################### -# Error exit procedure -# Globals: -# None -# Arguments: -# 1: message to print and logging -####################################### -execerror() { - addtologs "error: $1" - execquite -} - -####################################### -# Parsing config file and creating global vars. -# Globals: -# None -# Arguments: -# None -####################################### -getconfig() { - logs=$(grep "logs=" "${conf}" | cut -d= -f2) -} - -####################################### -# Checking user rights. -# Globals: -# None -# Arguments: -# None -# return: -# 0 - if privileged rights, 1 - if not privileged rights -####################################### -checkroot() { - if [ "${EUID}" -ne 0 ]; then - return 1 # false - else - return 0 # true - fi -} - -# -# VARIABLES: -# - -show=$1 -conf=$2 -if [ -z "${conf}" ] || [ "${conf}" == "-" ]; then - conf="$(dirname "$(realpath "$0")")/$(basename -s .sh "$0").conf" -fi - -time=$(date +%s) -cd "$(dirname "$(realpath "$0")")" || execerror -if [ ! -e "${conf}" ]; then - : -else - getconfig -fi -if [ -z "${logs}" ]; then - logs=/dev/null -elif [ ! -e "${logs}" ]; then - touch "${logs}" -fi - -# -# MAIN: -# - -checkroot \ -&& echo "Running as $(whoami)" \ -&& execquite \ -|| execerror "Restart this as root!" diff --git a/wg-client-management.sh b/wg-client-management.sh new file mode 100644 index 0000000..276e14c --- /dev/null +++ b/wg-client-management.sh @@ -0,0 +1,284 @@ +#! /bin/bash + +# DESCRIPTION: +# creating or deleting client config for wireguard +# and +# sending config and info to email +# +# DEPENDENCIES: +# - privileged rights +# - wireguard +# - qrencode +# - grepcidr +# - Python 3 +# - existing /usr/local/bin/sendmail.py +# +# PARAMETERS: +# 1: "add|del" - add or delete client config +# 2: username - client username +# 3: address - client ip address +# -f|--force - service will restart after username add|del +# +# FUNCTIONS: +# + +####################################### +# Print message and add to log. +# Globals: +# logs +# Arguments: +# 1: message to print and logging +####################################### +addtologs() { + echo "$(date +'%Y.%m.%d-%H:%M:%S') $1" | tee -a "${logs}" +} + +####################################### +# Exit procedure. +# Globals: +# show +# Arguments: +# None +####################################### +execquite() { + addtologs "execution time is $(($(date +%s)-time)) seconds, exit" + exit +} + +####################################### +# Error exit procedure +# Globals: +# None +# Arguments: +# 1: message to print and logging +####################################### +execerror() { + addtologs "error: $1" + execquite +} + +####################################### +# Checking user rights. +# Globals: +# None +# Arguments: +# None +# return: +# 0 - if privileged rights, 1 - if not privileged rights +####################################### +checkroot() { + if [ "${EUID}" -ne 0 ]; then + return 1 # false + else + return 0 # true + fi +} + +####################################### +# Send email notification about client connect +# Globals: +# clientname +# faqprofile +# Arguments: +# None +####################################### +startsendmail() { + subj="[WG Settings] $(cat /etc/hostname): ${clientname} client profile" + ( + python3 /usr/local/bin/sendmail.py \ + -u "$(grep "from=" /usr/local/bin/sendmail.config | cut -d= -f2)" \ + -p "$(grep "pass=" /usr/local/bin/sendmail.config | cut -d= -f2)" \ + -d "$(grep "dest=" /usr/local/bin/sendmail.config | cut -d= -f2)" \ + --smtp "$(grep "smtp=" /usr/local/bin/sendmail.config | cut -d= -f2)" \ + --port "$(grep "port=" /usr/local/bin/sendmail.config | cut -d= -f2)" \ + --stls "True" \ + --subj "${subj}" \ + --text "${faqprofile}" \ + --file "/etc/wireguard/${clientname}.png,/etc/wireguard/${clientname}.conf" \ + >> /dev/null 2>&1 & + ) + addtologs "sent mail with subject '${subj}'" +} + +####################################### +# Create wireguard client certificates +# Globals: +# clientname +# clientaddr +# servercfgname +# Arguments: +# None +####################################### +createcert() { + wg genkey | tee "/etc/wireguard/pki/${clientname}-private.key" | wg pubkey > "/etc/wireguard/pki/${clientname}-public.key" + clientpublkey=$(cat "/etc/wireguard/pki/${clientname}-public.key") + clientprivkey=$(cat "/etc/wireguard/pki/${clientname}-private.key") + wg set wg0 peer "${clientpublkey}" allowed-ips "${clientaddr}/32" + { + echo -e "[Peer]" + echo -e " PublicKey = ${clientpublkey}" + echo -e " AllowedIPs = ${clientaddr}/32" + } >> ${servercfgname} + ip -4 route add "${clientaddr}/32" dev wg0 +} + +####################################### +# Create wireguard client configuration +# Globals: +# clientname +# clientaddr +# clientconfdef +# clientprivkey +# serverpublkey +# Arguments: +# None +####################################### +createconf() { + clientconf=$(cat "${clientconfdef}") + clientconf=${clientconf//clientaddr/${clientaddr}} + clientconf=${clientconf//clientprivkey/${clientprivkey}} + clientconf=${clientconf//serverpublkey/${serverpublkey}} + clientconf=${clientconf//clientaddrs/${clientaddr}} + printf "%s\n" "${clientconf}" > "/etc/wireguard/${clientname}.conf" +} + +####################################### +# Create wireguard client info, qr-code +# Globals: +# clientname +# Arguments: +# None +####################################### +createinfo() { +faqprofile=$(cat < /dev/null || \ + ! command -v /usr/local/bin/sendmail.py &> /dev/null || \ + ! command -v python3 &> /dev/null; then + execerror "Not found dependencies" +fi + +# +# MAIN: +# + +if checkroot; then + serverpublkey=$(cat /etc/wireguard/pki/server-public.key) + servercfgname="/etc/wireguard/wg0.conf" + clientpublkey='' + clientprivkey='' + clientconfdef="/etc/wireguard/client.conf.default" + logs=/var/log/wireguard/$(basename -s .sh "$0").log + + if [ "${clienttodo}" == "add" ] && \ + [ -n "${clientname}" ] && \ + grepcidr "0.0.0.0/0" <(echo "${clientaddr}") >/dev/null; then + if [ -f "/etc/wireguard/${clientname}.conf" ] || \ + grep -q -w "${clientaddr}/32" ${servercfgname}; then + execerror "wireguard config exist or address used, exit" + else + createcert && addtologs "created certificate for ${clientname}" + createconf && addtologs "created wg config file for ${clientname}" + createinfo && addtologs "created info file for ${clientname}" + startsendmail + fi + if [ "${resetforce}" -eq 1 ];then + addtologs "restarting wg-quick@wg0..." + systemctl restart wg-quick@wg0 + fi + elif [ "${clienttodo}" == "del" ] && \ + [ -n "${clientname}" ] && \ + grepcidr "0.0.0.0/0" <(echo "${clientaddr}") >/dev/null; then + if [ -f "/etc/wireguard/${clientname}.conf" ]; then + deleteconf && addtologs "deleted wg config file for ${clientname}" + fi + if [ -f "/etc/wireguard/${clientname}.png" ]; then + deleteinfo && addtologs "deleted info file for ${clientname}" + fi + if grep -q -w "${clientaddr}/32" ${servercfgname}; then + deletecert && addtologs "deleted certificate for ${clientname}" + fi + if [ "${resetforce}" -eq 1 ];then + addtologs "restarting wg-quick@wg0..." + systemctl restart wg-quick@wg0 + fi + else + printf "%s\n" "Usage example: $0 'add' 'username(surname)' 'address(ww.xx.yy.zz) -f'" + printf "%s\n" "Usage example: $0 'del' 'username(surname)' 'address(ww.xx.yy.zz)'" + fi +else + execerror "Restart this as root!" +fi +execquite diff --git a/wg-connect-handling.sh b/wg-connect-handling.sh new file mode 100644 index 0000000..4de263a --- /dev/null +++ b/wg-connect-handling.sh @@ -0,0 +1,208 @@ +#! /bin/bash + +# DESCRIPTION: +# handling client connection +# and +# preparing stats for monitoring +# +# DEPENDENCIES: +# - privileged rights +# - jq +# - grepcidr +# - Python 3 +# - existing /usr/local/bin/sendmail.py +# +# PARAMETERS: +# 1: root path for counter, names, log +# 2: "mail" - send email notification +# 3: "geo" - check client address geolocation +# +# FUNCTIONS: +# + +####################################### +# Print message and add to log. +# Globals: +# logs +# Arguments: +# 1: message to print and logging +####################################### +addtologs() { + echo "$(date +'%Y.%m.%d-%H:%M:%S') $1" | tee -a "${logs}" +} + +####################################### +# Exit procedure. +# Globals: +# show +# Arguments: +# None +####################################### +execquite() { + addtologs "execution time is $(($(date +%s)-time)) seconds, exit" + exit +} + +####################################### +# Error exit procedure +# Globals: +# None +# Arguments: +# 1: message to print and logging +####################################### +execerror() { + addtologs "error: $1" + execquite +} + +####################################### +# Checking user rights. +# Globals: +# None +# Arguments: +# None +# return: +# 0 - if privileged rights, 1 - if not privileged rights +####################################### +checkroot() { + if [ "${EUID}" -ne 0 ]; then + return 1 # false + else + return 0 # true + fi +} + +####################################### +# Get information about client address +# Globals: +# flaggeol +# show_from_addr +# conf_client_nm +# Arguments: +# None +####################################### +# shellcheck disable=SC2154 +expandaddress() { + ipinfo="Source address is ${show_from_addr}" + localnetworks="10.0.0.0/8 + 100.64.0.0/10 + 127.0.0.1/8 + 172.16.0.0/12 + 192.168.0.0/16 + " + if ! grepcidr "${localnetworks}" <(echo "${show_from_addr}") >/dev/null; then + if [ "${flaggeol}" == "geo" ]; then + ipinfo=$(curl "https://api.ipbase.com/v1/json/${show_from_addr}") + if [ "$(jq -r '.country_name' <<< "$ipinfo")" != "" ]; then + z=$(jq -r '.zip_code' <<< "$ipinfo") + c=$(jq -r '.country_name' <<< "$ipinfo") + r=$(jq -r '.region_name' <<< "$ipinfo") + t=$(jq -r '.city' <<< "$ipinfo") + ipinfo="Source address ${show_from_addr} is from ${z}, ${c}, ${r}, ${t}" + fi + fi + fi + addtologs "client ${conf_client_nm} checked. ${ipinfo}" +} + +####################################### +# Send email notification about client connect +# Globals: +# ipinfo +# conf_client_nm +# conf_ipaddress +# Arguments: +# None +####################################### +startsendmail() { + subj="[VPN Connected] $(cat /etc/hostname): ${conf_client_nm} connect to ${conf_ipaddress}" + ( + python3 /usr/local/bin/sendmail.py \ + -u "$(grep "from=" /usr/local/bin/sendmail.config | cut -d= -f2)" \ + -p "$(grep "pass=" /usr/local/bin/sendmail.config | cut -d= -f2)" \ + -d "$(grep "dest=" /usr/local/bin/sendmail.config | cut -d= -f2)" \ + --smtp "$(grep "smtp=" /usr/local/bin/sendmail.config | cut -d= -f2)" \ + --port "$(grep "port=" /usr/local/bin/sendmail.config | cut -d= -f2)" \ + --stls "True" \ + --subj "${subj}" \ + --text "${ipinfo}" \ + >> /dev/null 2>&1 & + ) + addtologs "sent mail with subject '${subj}'" +} + +# +# VARIABLES: +# + +pathroot=$1 +flagmail=$2 +flaggeol=$3 + +time=$(date +%s) +logs="${pathroot}/$(basename -s .sh "$0").log" +counts_file="${pathroot}/wg-counts.log" +counts_temp=$(cat "${counts_file}") +if [ -z "${pathroot}" ]; then + logs=/dev/null + execerror "Usage example: $0 '/var/log/wireguard' '-' '-'" +elif [ ! -e "${logs}" ]; then + touch "${logs}" +fi + +if ! command -v curl &> /dev/null || \ + ! command -v /usr/local/bin/sendmail.py &> /dev/null || \ + ! command -v python3 &> /dev/null || \ + ! command -v grepcidr &> /dev/null || \ + ! command -v jq &> /dev/null; then + execerror "Not found dependencies" +fi + +# +# MAIN: +# + +if checkroot; then + allowed_cfg=$(find /etc/wireguard/ -name "*.conf" | grep -v "wg0.conf") + counter_now=0 + clients_now="" + while read -r file; do + conf_keepalive=$(grep "PersistentKeepalive" "${file}" |cut -d"=" -f2 |tr -d " ") + conf_ipaddress=$(grep "Address" "${file}" |cut -d"=" -f2 |cut -d"/" -f1 |tr -d " ") + conf_client_nm=$(basename -s .conf "${file}") + show_handshake=$(wg show all dump |grep "${conf_ipaddress}" |cut -f6) + show_from_addr=$(wg show all dump |grep "${conf_ipaddress}" |cut -f4 |cut -d":" -f1) + if [ "${show_handshake}" -ne 0 ]; then + calc_handshake="$(date -d "-${conf_keepalive} min" +"%s")" + if [ "${show_handshake}" -ge "${calc_handshake}" ]; then + (( counter_now ++)) + connect_status="connected" + if ping -q -c 1 -W 1 "${conf_ipaddress}" > /dev/null; then + connect_status="connected, ping responded" + else + connect_status="connected, ping loss" + fi + clients_now+=$(printf "%s\n\r" "${conf_client_nm}_${conf_ipaddress}") + if ! grep -q "${conf_client_nm}_${conf_ipaddress}" <<< "${counts_temp}"; then + addtologs "client ${conf_client_nm} ${connect_status}" + expandaddress + if [ "${flagmail}" == "mail" ]; then + startsendmail + fi + fi + else + connect_status="disconnected" + if grep -q "${conf_client_nm}_${conf_ipaddress}" <<< "${counts_temp}"; then + addtologs "client ${conf_client_nm} ${connect_status}" + fi + fi + else + connect_status="never connected" + fi + done <<< "$allowed_cfg" + printf "%s\n" "total=${counter_now}=" > "${counts_file}" + printf "%s\n" "${clients_now}" >> "${counts_file}" +else + execerror "Restart this as root!" +fi +execquite