#!/usr/bin/env 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: interface - define wireguard interface # 2: "add|del" - add or delete client config # 3: username - client username # 4: address - client ip address # 5: additional - client description # -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() { printf "%s\n" "$(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 "${1}" } ####################################### # Error exit procedure. # Globals: # None # Arguments: # 1: message to print and logging ####################################### execerror() { addtologs "error: $1" execquite 1 } ####################################### # Parsing config file and creating global vars. # Globals: # None # Arguments: # None ####################################### getconfig() { logs=/var/log/wireguard/$(basename -s .sh "$(realpath "$0")").log conf="$(dirname "$(realpath "$0")")/$(basename -s .sh "$(realpath "$0")").conf" # wireguard configuration serverpublkey=$(cat /etc/wireguard/pki/server-public.key) servercfgname="/etc/wireguard/${iface_name}.conf" clientpublkey='' clientprivkey='' clientconfdef="/etc/wireguard/client.conf.default" # mail configuration from="$(grep "from=" "${conf}" | cut -d= -f2)" pass="$(grep "pass=" "${conf}" | cut -d= -f2)" dest="$(grep "dest=" "${conf}" | cut -d= -f2)" smtp="$(grep "smtp=" "${conf}" | cut -d= -f2)" port="$(grep "port=" "${conf}" | cut -d= -f2)" # telegram configuration API_KEY=$(grep "API_KEY=" "${conf}" | cut -d= -f2) CHAT_ID=$(grep "CHAT_ID=" "${conf}" | cut -d= -f2) THRD_ID=$(grep "THRD_ID=" "${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 } ####################################### # Send email notification about client config. # Globals: # clientname # faqprofile # Arguments: # None ####################################### startsendmail() { subj="[WG Settings] $(cat /etc/hostname): ${clientname} client profile" ( python3 /usr/local/bin/sendmail.py \ -u "${from}" \ -p "${pass}" \ -d "${dest}" \ --smtp "${smtp}" \ --port "${port}" \ --stls "True" \ --subj "${subj}" \ --text "$(printf "%s\n" "${faqprofile}" | sed 's|`||g')" \ --file "/etc/wireguard/${clientname}.png,/etc/wireguard/${clientname}.conf" ) > /dev/null 2>&1 addtologs "sent mail with subject '${subj}' to ${dest}" } ####################################### # Send telegram notification about client config. # Globals: # clientname # faqprofile # ovpncfgdir # API_KEY # CHAT_ID # THRD_ID # Arguments: # None ####################################### # shellcheck disable=SC2030,2031 startsendtlgm() { ( API_URL="https://api.telegram.org/bot${API_KEY}/sendMediaGroup?chat_id=${CHAT_ID}" if grep -q "_" <<< "${CHAT_ID}"; then THRD_ID=$(printf "%s\n" "${CHAT_ID}" | cut -d_ -f2) CHAT_ID=$(printf "%s\n" "${CHAT_ID}" | cut -d_ -f1) fi if [ -n "${THRD_ID}" ]; then API_URL="${API_URL}&message_thread_id=${THRD_ID}" fi curl "${API_URL}" \ -F "media=[{\"type\": \"document\", \"media\": \"attach://qr\", \"caption\": \"${faqprofile}\", \"parse_mode\": \"Markdown\"}, {\"type\": \"document\", \"media\": \"attach://cf\" }]" \ -F "qr=@/etc/wireguard/${clientname}.png" \ -F "cf=@/etc/wireguard/${clientname}.conf" ) > /dev/null 2>&1 addtologs "sent telegram media with ${clientname}.conf client profile to ${CHAT_ID}" } ####################################### # Create wireguard client peer. # Globals: # clientname # clientaddr # servercfgname # Arguments: # None ####################################### createpeer() { if [ -f "/etc/wireguard/pki/${clientname}-private.key" ]; then addtologs "${clientname} private key exists, create skipped" else wg genkey | tee "/etc/wireguard/pki/${clientname}-private.key" > /dev/null 2>&1 addtologs "created ${clientname} new private key" fi wg pubkey > "/etc/wireguard/pki/${clientname}-public.key" < "/etc/wireguard/pki/${clientname}-private.key" addtologs "created ${clientname} wireguard certificates" clientpublkey=$(cat "/etc/wireguard/pki/${clientname}-public.key") if grep -q -w "${clientpublkey}" "${servercfgname}"; then addtologs "${clientname} peer configuration exists, create skipped" else wg set "${iface_name}" peer "${clientpublkey}" \ allowed-ips "${clientaddr}/32" \ persistent-keepalive 5 { printf "%s\n" "[Peer]" printf "%s\n" " PublicKey = ${clientpublkey}" printf "%s\n" " AllowedIPs = ${clientaddr}/32" printf "%s\n" " PersistentKeepalive = 5" } >> "${servercfgname}" addtologs "created ${clientname} wireguard peer configuration" fi if ip ro | grep -q -w "${clientaddr}"; then addtologs "${clientname} peer address exists in routes, create skipped" else ip -4 route add "${clientaddr}/32" dev "${iface_name}" addtologs "created ${clientname} peer route" fi } ####################################### # Create wireguard client configuration. # Globals: # clientname # clientaddr # clientconfdef # clientprivkey # serverpublkey # Arguments: # None ####################################### createconf() { clientprivkey=$(cat "/etc/wireguard/pki/${clientname}-private.key") 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" addtologs "created ${clientname} wireguard config file" } ####################################### # Create wireguard client info, qr-code. # Globals: # clientname # Arguments: # None ####################################### createinfo() { faqprofile=$(printf "%s\n" \ "WireGuard client:" \ "https://www.wireguard.com/install/" \ "" \ "${additional}" \ "" \ "Peer Address: \`$(grep 'Address' "/etc/wireguard/${clientname}.conf" | awk '{print $3}')\`" \ ) qrencode -o "/etc/wireguard/${clientname}.png" -t png -s 6 < "/etc/wireguard/${clientname}.conf" addtologs "created ${clientname} qr code" } ####################################### # Delete wireguard client peer. # Globals: # clientname # clientpublkey # servercfgname # Arguments: # None ####################################### deletepeer() { if [ -f "/etc/wireguard/pki/${clientname}-private.key" ]; then wg pubkey > "/etc/wireguard/pki/${clientname}-public.key" < "/etc/wireguard/pki/${clientname}-private.key" fi if [ -f "/etc/wireguard/pki/${clientname}-public.key" ]; then clientpublkey=$(cat "/etc/wireguard/pki/${clientname}-public.key") if grep -q -w "${clientpublkey}" "${servercfgname}"; then wg set "${iface_name}" peer "${clientpublkey}" remove # PublicKey = s2=$(grep -n "${clientpublkey}" "${servercfgname}" | cut -d":" -f1) # [Peer] s1=$(( s2 - 1 )) # AllowedIPs = s3=$(( s2 + 1 )) # PersistentKeepalive = s4=$(( s2 + 2 )) sed -i "${s1}d;${s2}d;${s3}d;${s4}d" "${servercfgname}" addtologs "deleted ${clientname} wireguard peer configuration" else addtologs "${clientname} peer configuration does not exist, delete skipped" fi rm -f "/etc/wireguard/pki/${clientname}-public.key" > /dev/null 2>&1 rm -f "/etc/wireguard/pki/${clientname}-private.key" > /dev/null 2>&1 addtologs "deleted ${clientname} wireguard certificates" else addtologs "${clientname} certificates do not exist, delete skipped" fi if ip ro | grep -q -w "${clientaddr} dev ${iface_name}"; then ip -4 route del "${clientaddr}/32" dev "${iface_name}" addtologs "deleted ${clientname} peer route" else addtologs "${clientname} peer route does not exist, delete skipped" fi } ####################################### # Delete wireguard client configuration. # Globals: # clientname # Arguments: # None ####################################### deleteconf() { if [ -f "/etc/wireguard/${clientname}.conf" ]; then rm -f "/etc/wireguard/${clientname}.conf" addtologs "deleted ${clientname} wireguard config file" else addtologs "${clientname} wireguard config does not exist, delete skipped" fi } ####################################### # Delete wireguard client qr-code. # Globals: # clientname # Arguments: # None ####################################### deleteinfo() { if [ -f "/etc/wireguard/${clientname}.png" ]; then rm -f "/etc/wireguard/${clientname}.png" addtologs "deleted ${clientname} qr code" else addtologs "${clientname} qr code does not exist, delete skipped" fi } # # VARIABLES: # iface_name=$1 clienttodo=$2 clientname=$3 clientaddr=$4 additional=$5 resetforce=0 for argument in "${@}"; do case $argument in -f | --force ) resetforce=1 ;; -* ) ;; esac done time=$(date +%s) logs=/dev/null # # MAIN: # if checkroot; then if ! command -v qrencode &> /dev/null || \ ! command -v grepcidr &> /dev/null || \ ! command -v /usr/local/bin/sendmail.py &> /dev/null || \ ! command -v python3 &> /dev/null || \ ! command -v curl &> /dev/null; then execerror "Not found dependencies" fi getconfig if [ "${clienttodo}" == "add" ] && \ [ -n "${iface_name}" ] && \ [ -n "${clientname}" ] && \ grepcidr "0.0.0.0/0" <(echo "${clientaddr}") > /dev/null; then if ip ro | grep -q -w "${clientaddr}" || \ grep -q -w "${clientaddr}/32" "${servercfgname}"; then addtologs "${clientaddr} address used, create peer, conf skipped" else createpeer createconf fi if [ ! -f "/etc/wireguard/${clientname}.conf" ]; then addtologs "${clientname}.conf not found, create info skipped" else createinfo startsendmail startsendtlgm fi if [ "${resetforce}" -eq 1 ];then addtologs "restarting wg-quick@${iface_name}..." systemctl restart "wg-quick@${iface_name}" fi elif [ "${clienttodo}" == "del" ] && \ [ -n "${iface_name}" ] && \ [ -n "${clientname}" ] && \ grepcidr "0.0.0.0/0" <(echo "${clientaddr}") >/dev/null; then deleteconf deleteinfo deletepeer if [ "${resetforce}" -eq 1 ];then addtologs "restarting wg-quick@${iface_name}..." systemctl "restart wg-quick@${iface_name}" fi else printf "%s\n" "Usage example: $0 'wg0' 'add' 'username(surname)' 'address(ww.xx.yy.zz)'" printf "%s\n" "Usage example: $0 'wg0' 'add' 'username(surname)' 'address(ww.xx.yy.zz) 'additional client description'" printf "%s\n" "Usage example: $0 'wg0' 'del' 'username(surname)' 'address(ww.xx.yy.zz)'" printf "%s\n" "Usage example: $0 'wg0' 'del' 'username(surname)' 'address(ww.xx.yy.zz)' -f" fi else execerror "Restart this as root!" fi execquite 0