From af204615909b44c520c1bcde7861bf0acce6c884 Mon Sep 17 00:00:00 2001 From: Giulio Coa Date: Wed, 27 Jul 2022 14:53:36 +0200 Subject: [PATCH] Reformatted the code --- ciscripts/startup.sh | 85 ++-- ciscripts/test.sh | 5 +- scripts/backup.sh | 156 +++--- scripts/openvpn/bash-completion | 38 +- scripts/openvpn/clientStat.sh | 139 +++--- scripts/openvpn/listOVPN.sh | 63 ++- scripts/openvpn/makeOVPN.sh | 780 ++++++++++++++++-------------- scripts/openvpn/pivpn.sh | 161 +++--- scripts/openvpn/pivpnDebug.sh | 64 ++- scripts/openvpn/removeOVPN.sh | 286 ++++++----- scripts/pivpn | 82 ++-- scripts/self_check.sh | 465 +++++++++++------- scripts/uninstall.sh | 574 +++++++++++++--------- scripts/update.sh | 199 ++++---- scripts/wireguard/bash-completion | 32 +- scripts/wireguard/clientSTAT.sh | 156 +++--- scripts/wireguard/disableCONF.sh | 196 ++++---- scripts/wireguard/enableCONF.sh | 189 ++++---- scripts/wireguard/listCONF.sh | 35 +- scripts/wireguard/makeCONF.sh | 288 +++++------ scripts/wireguard/pivpn.sh | 207 ++++---- scripts/wireguard/pivpnDEBUG.sh | 71 ++- scripts/wireguard/qrcodeCONF.sh | 129 ++--- scripts/wireguard/removeCONF.sh | 276 ++++++----- 24 files changed, 2655 insertions(+), 2021 deletions(-) diff --git a/ciscripts/startup.sh b/ciscripts/startup.sh index 33a2bc4..6f6e56d 100644 --- a/ciscripts/startup.sh +++ b/ciscripts/startup.sh @@ -1,47 +1,62 @@ -#!/bin/sh +#!/bin/bash -interface=$(ip -o link | awk '{print $2}' | cut -d':' -f1 | cut -d'@' -f1 | grep -v -w 'lo' | head -1) -ipaddress=$(ip addr show "$interface" | grep -o -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}/[0-9]{2}") +interface=$(ip -o link | + awk '{print $2}' | + cut -d ':' -f 1 | + cut -d '@' -f 1 | + grep -v -w 'lo' | + head -1) +ipaddress=$(ip addr show "${interface}" | + grep -o -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}/[0-9]{2}") gateway=$(ip route show | awk '/default/ {print $3}') hostname="pivpn.test" -common(){ - sed -i "s/INTERFACE/$interface/g" "$vpnconfig" - sed -i "s|IPADDRESS|$ipaddress|g" "$vpnconfig" - sed -i "s/GATEWAY/$gateway/g" "$vpnconfig" +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 } -openvpn(){ - vpnconfig="ciscripts/ci_openvpn.conf" - twofour=1 - common - sed -i "s/2POINT4/$twofour/g" "$vpnconfig" - cat $vpnconfig - exit 0 +common() { + sed -i "s/INTERFACE/${interface}/g" "${vpnconfig}" + sed -i "s|IPADDRESS|${ipaddress}|g" "${vpnconfig}" + sed -i "s/GATEWAY/${gateway}/g" "${vpnconfig}" } -wireguard(){ - vpnconfig="ciscripts/ci_wireguard.conf" - common - cat $vpnconfig - exit 0 +openvpn() { + vpnconfig="ciscripts/ci_openvpn.conf" + twofour=1 + common + sed -i "s/2POINT4/${twofour}/g" "${vpnconfig}" + cat "${vpnconfig}" + exit 0 } -if [ $# -lt 1 ]; then - echo "specifiy a VPN protocol to prepare" - exit 1 +wireguard() { + vpnconfig="ciscripts/ci_wireguard.conf" + common + cat "${vpnconfig}" + exit 0 +} + +if [[ "$#" -lt 1 ]]; then + err "specifiy a VPN protocol to prepare" + exit 1 else - chmod +x auto_install/install.sh - sudo hostnamectl set-hostname $hostname - cat /etc/os-release - while true; do - case "$1" in - -o | --openvpn ) openvpn - ;; - -w | --wireguard ) wireguard - ;; - * ) echo "unknown vpn protocol"; exit 1 - ;; - esac - done + chmod +x auto_install/install.sh + sudo hostnamectl set-hostname "${hostname}" + cat /etc/os-release + + while true; do + case "${1}" in + -o | --openvpn) + openvpn + ;; + -w | --wireguard) + wireguard + ;; + *) + err "unknown vpn protocol" + exit 1 + ;; + esac + done fi diff --git a/ciscripts/test.sh b/ciscripts/test.sh index c24db6f..2ba7e1f 100644 --- a/ciscripts/test.sh +++ b/ciscripts/test.sh @@ -1,10 +1,11 @@ #!/bin/bash if command -v systemctl > /dev/null; then - systemctl status openvpn + systemctl status openvpn elif command -v rc-service > /dev/null; then - rc-service openvpn status + rc-service openvpn status fi + pivpn add -n foo pivpn -qr foo pivpn -bk diff --git a/scripts/backup.sh b/scripts/backup.sh index 793a73d..dc932dd 100755 --- a/scripts/backup.sh +++ b/scripts/backup.sh @@ -2,111 +2,125 @@ # PiVPN: Backup Script # Find the rows and columns. Will default to 80x24 if it can not be detected. -screen_size=$(stty size 2>/dev/null || echo 24 80) -rows=$(echo "$screen_size" | awk '{print $1}') -columns=$(echo "$screen_size" | awk '{print $2}') +screen_size="$(stty size 2> /dev/null || echo 24 80)" +rows="$(echo "${screen_size}" | awk '{print $1}')" +columns="$(echo "${screen_size}" | awk '{print $2}')" # Divide by two so the dialogs take up half of the screen, which looks nice. -r=$(( rows / 2 )) -c=$(( columns / 2 )) +r=$((rows / 2)) +c=$((columns / 2)) # Unless the screen is tiny -r=$(( r < 20 ? 20 : r )) -c=$(( c < 70 ? 70 : c )) +r=$((r < 20 ? 20 : r)) +c=$((c < 70 ? 70 : c)) backupdir=pivpnbackup -date=$(date +%Y%m%d-%H%M%S) +date="$(date +%Y%m%d-%H%M%S)" setupVarsFile="setupVars.conf" setupConfigDir="/etc/pivpn" CHECK_PKG_INSTALLED='dpkg-query -s' -if [ -r "${setupConfigDir}/wireguard/${setupVarsFile}" ] && [ -r "${setupConfigDir}/openvpn/${setupVarsFile}" ]; then +if [[ -r "${setupConfigDir}/wireguard/${setupVarsFile}" ]] && + [[ -r "${setupConfigDir}/openvpn/${setupVarsFile}" ]]; then + # Two protocols have been installed, check if the script has passed + # an argument, otherwise ask the user which one he wants to remove + if [[ "$#" -ge 1 ]]; then + VPN="${1}" + echo "::: Backing up VPN: ${VPN}" + else + chooseVPNCmd=(whiptail + --backtitle "Setup PiVPN" + --title "Backup" + --separate-output + --radiolist "Both OpenVPN and WireGuard are installed, choose a VPN to \ +backup (press space to select):" + "${r}" "${c}" 2) + VPNChooseOptions=(WireGuard "" on + OpenVPN "" off) - # Two protocols have been installed, check if the script has passed - # an argument, otherwise ask the user which one he wants to remove - if [ $# -ge 1 ]; then - VPN="$1" - echo "::: Backing up VPN: $VPN" - else - chooseVPNCmd=(whiptail --backtitle "Setup PiVPN" --title "Backup" --separate-output --radiolist "Both OpenVPN and WireGuard are installed, choose a VPN to backup (press space to select):" "${r}" "${c}" 2) - VPNChooseOptions=(WireGuard "" on - OpenVPN "" off) + if VPN="$("${chooseVPNCmd[@]}" "${VPNChooseOptions[@]}" 2>&1 \ + > /dev/tty)"; then + echo "::: Backing up VPN: ${VPN}" + VPN="${VPN,,}" + else + err "::: Cancel selected, exiting...." + exit 1 + fi + fi - if VPN=$("${chooseVPNCmd[@]}" "${VPNChooseOptions[@]}" 2>&1 >/dev/tty) ; then - echo "::: Backing up VPN: $VPN" - VPN="${VPN,,}" - else - echo "::: Cancel selected, exiting...." - exit 1 - fi - fi - - setupVars="${setupConfigDir}/${VPN}/${setupVarsFile}" + setupVars="${setupConfigDir}/${VPN}/${setupVarsFile}" else - - if [ -r "${setupConfigDir}/wireguard/${setupVarsFile}" ]; then - setupVars="${setupConfigDir}/wireguard/${setupVarsFile}" - elif [ -r "${setupConfigDir}/openvpn/${setupVarsFile}" ]; then - setupVars="${setupConfigDir}/openvpn/${setupVarsFile}" - fi + if [[ -r "${setupConfigDir}/wireguard/${setupVarsFile}" ]]; then + setupVars="${setupConfigDir}/wireguard/${setupVarsFile}" + elif [[ -r "${setupConfigDir}/openvpn/${setupVarsFile}" ]]; then + setupVars="${setupConfigDir}/openvpn/${setupVarsFile}" + fi fi -if [ ! -f "${setupVars}" ]; then - echo "::: Missing setup vars file!" - exit 1 +if [[ ! -f "${setupVars}" ]]; then + err "::: Missing setup vars file!" + exit 1 fi # shellcheck disable=SC1090 source "${setupVars}" -if [ "${PLAT}" == 'Alpine' ]; then - CHECK_PKG_INSTALLED='apk --no-cache info -e' +if [[ "${PLAT}" == 'Alpine' ]]; then + CHECK_PKG_INSTALLED='apk --no-cache info -e' fi -checkbackupdir(){ - - # Disabling shellcheck error $install_home sourced from $setupVars - # shellcheck disable=SC2154 - if [[ ! -d $install_home/$backupdir ]]; then - mkdir -p "$install_home"/"$backupdir" - fi - +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 } -backup_openvpn(){ - - openvpndir=/etc/openvpn - ovpnsdir=${install_home}/ovpns - checkbackupdir - backupzip=$date-pivpnovpnbackup.tgz - # shellcheck disable=SC2210 - tar czpf "$install_home"/"$backupdir"/"$backupzip" "$openvpndir" "$ovpnsdir" > /dev/null 2>&1 - echo -e "Backup created in $install_home/$backupdir/$backupzip \nTo restore the backup, follow instructions at:\nhttps://docs.pivpn.io/openvpn/#migrating-pivpn-openvpn\n" - +checkbackupdir() { + # Disabling shellcheck error $install_home sourced from $setupVars + # shellcheck disable=SC2154 + mkdir -p "${install_home}/${backupdir}" } -backup_wireguard(){ +backup_openvpn() { + openvpndir=/etc/openvpn + ovpnsdir="${install_home}/ovpns" + backupzip="${date}-pivpnovpnbackup.tgz" - wireguarddir=/etc/wireguard - configsdir=${install_home}/configs - checkbackupdir - backupzip=$date-pivpnwgbackup.tgz - tar czpf "$install_home"/"$backupdir"/"$backupzip" "$wireguarddir" "$configsdir" > /dev/null 2>&1 - echo -e "Backup created in $install_home/$backupdir/$backupzip \nTo restore the backup, follow instructions at:\nhttps://docs.pivpn.io/wireguard/#migrating-pivpn-wireguard\n" + checkbackupdir + # shellcheck disable=SC2210 + tar czpf "${install_home}/${backupdir}/${backupzip}" "${openvpndir}" \ + "${ovpnsdir}" > /dev/null 2>&1 + echo -e "Backup created in ${install_home}/${backupdir}/${backupzip} " + echo -e "To restore the backup, follow instructions at:" + echo -ne "https://docs.pivpn.io/openvpn/" + echo -e "#migrating-pivpn-openvpn" } -if [[ ! $EUID -eq 0 ]]; then - if eval "${CHECK_PKG_INSTALLED} sudo" &> /dev/null; then - export SUDO="sudo" - else - echo "::: Please install sudo or run this as root." +backup_wireguard() { + wireguarddir=/etc/wireguard + configsdir="${install_home}/configs" + backupzip="${date}-pivpnwgbackup.tgz" + + checkbackupdir + tar czpf "${install_home}/${backupdir}/${backupzip}" "${wireguarddir}" \ + "${configsdir}" > /dev/null 2>&1 + + echo -e "Backup created in ${install_home}/${backupdir}/${backupzip} " + echo -e "To restore the backup, follow instructions at:" + echo -ne "https://docs.pivpn.io/openvpn/" + echo -e "wireguard/#migrating-pivpn-wireguard" +} + +if [[ "${EUID}" -ne 0 ]]; then + if ${CHECK_PKG_INSTALLED} sudo &> /dev/null; then + export SUDO="sudo" + else + err "::: Please install sudo or run this as root." exit 1 fi fi if [[ "${VPN}" == "wireguard" ]]; then - backup_wireguard + backup_wireguard else - backup_openvpn + backup_openvpn fi diff --git a/scripts/openvpn/bash-completion b/scripts/openvpn/bash-completion index 1a9cb96..775d221 100644 --- a/scripts/openvpn/bash-completion +++ b/scripts/openvpn/bash-completion @@ -1,21 +1,25 @@ #!/bin/bash -_pivpn() -{ - local cur prev opts - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - dashopts="-a -c -d -l -r -h -u -up -bk" - opts="debug add clients list revoke uninstall help update backup" - if [ "${#COMP_WORDS[@]}" -eq 2 ]; then - if [[ ${cur} == -* ]] ; then - COMPREPLY=( "$(compgen -W "${dashopts}" -- "${cur}")" ) - else - COMPREPLY=( "$(compgen -W "${opts}" -- "${cur}")" ) - fi - elif [[ ( "$prev" == "add" || "$prev" == "-a" ) && "${#COMP_WORDS[@]}" -eq 3 ]]; then - COMPREPLY=( "$(compgen -W "nopass" -- "${cur}")" ) + +_pivpn() { + local cur prev opts + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD - 1]}" + dashopts="-a -c -d -l -r -h -u -up -bk" + opts="debug add clients list revoke uninstall help update backup" + + if [[ "${#COMP_WORDS[@]}" -eq 2 ]]; then + if [[ "${cur}" == -* ]]; then + COMPREPLY=("$(compgen -W "${dashopts}" -- "${cur}")") + else + COMPREPLY=("$(compgen -W "${opts}" -- "${cur}")") fi - return 0 + elif [[ ("${prev}" == "add" || "${prev}" == "-a") ]] && + [[ "${#COMP_WORDS[@]}" -eq 3 ]]; then + COMPREPLY=("$(compgen -W "nopass" -- "${cur}")") + fi + + return 0 } + complete -F _pivpn pivpn diff --git a/scripts/openvpn/clientStat.sh b/scripts/openvpn/clientStat.sh index 82809f4..c810d79 100755 --- a/scripts/openvpn/clientStat.sh +++ b/scripts/openvpn/clientStat.sh @@ -1,84 +1,99 @@ -#!/usr/bin/env bash +#!/bin/bash # PiVPN: client status script STATUS_LOG="/var/log/openvpn-status.log" -if [ ! -f "${STATUS_LOG}" ]; then - echo "The file: $STATUS_LOG was not found!" - exit 1 +if [[ ! -f "${STATUS_LOG}" ]]; then + err "The file: ${STATUS_LOG} was not found!" + exit 1 fi -scriptusage(){ - echo "::: List any connected clients to the server" - echo ":::" - echo "::: Usage: pivpn <-c|clients> [-b|bytes]" - echo ":::" - echo "::: Commands:" - echo "::: [none] List clients with human readable format" - echo "::: -b, bytes List clients with dotted decimal notation" - echo "::: -h, help Show this usage dialog" +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 } -hr(){ - numfmt --to=iec-i --suffix=B "$1" +scriptusage() { + echo "::: List any connected clients to the server" + echo ":::" + echo "::: Usage: pivpn <-c|clients> [-b|bytes]" + echo ":::" + echo "::: Commands:" + echo "::: [none] List clients with human readable format" + echo "::: -b, bytes List clients with dotted decimal notation" + echo "::: -h, help Show this usage dialog" } +hr() { + numfmt --to=iec-i --suffix=B "${1}" +} -listClients(){ - printf ": NOTE : The output below is NOT real-time!\n" - printf ": : It may be off by a few minutes.\n" - printf "\n" - printf "\e[1m::: Client Status List :::\e[0m\n" +listClients() { + printf ": NOTE : The output below is NOT real-time!\n" + printf ": : It may be off by a few minutes.\n" + printf "\n" + printf "\e[1m::: Client Status List :::\e[0m\n" - { - printf "\e[4mName\e[0m \t \e[4mRemote IP\e[0m \t \e[4mVirtual IP\e[0m \t \e[4mBytes Received\e[0m \t \e[4mBytes Sent\e[0m \t \e[4mConnected Since\e[0m\n" + { + printf "\e[4mName\e[0m \t \e[4mRemote IP\e[0m \t " + printf "\e[4mVirtual IP\e[0m \t \e[4mBytes Received\e[0m \t " + printf "\e[4mBytes Sent\e[0m \t \e[4mConnected Since\e[0m\n" if grep -q "^CLIENT_LIST" "${STATUS_LOG}"; then - if [ -n "$(type -t numfmt)" ]; then - if [ "$HR" = 1 ]; then - while read -r line; do - read -r -a array <<< "$line" - [[ ${array[0]} = CLIENT_LIST ]] || continue - printf "%s \t %s \t %s \t %s \t %s \t %s %s %s - %s\n" "${array[1]}" "${array[2]}" "${array[3]}" "$(hr "${array[4]}")" "$(hr "${array[5]}")" "${array[7]}" "${array[8]}" "${array[10]}" "${array[9]}" - done <$STATUS_LOG - else - while read -r line; do - read -r -a array <<< "$line" - [[ ${array[0]} = CLIENT_LIST ]] || continue - printf "%s \t %s \t %s \t %'d \t %'d \t %s %s %s - %s\n" "${array[1]}" "${array[2]}" "${array[3]}" "${array[4]}" "${array[5]}" "${array[7]}" "${array[8]}" "${array[10]}" "${array[9]}" - done <$STATUS_LOG - fi - else - awk -F' ' -v s='CLIENT_LIST' '$1 == s {print $2"\t\t"$3"\t"$4"\t"$5"\t\t"$6"\t\t"$8" "$9" "$11" - "$10"\n"}' ${STATUS_LOG} - fi + if [[ -n "$(type -t numfmt)" ]]; then + while read -r line; do + read -r -a array <<< "${line}" + + [[ "${array[0]}" == 'CLIENT_LIST' ]] || continue + + printf "%s \t %s \t " "${array[1]}" "${array[2]}" + printf "%s \t " "${array[3]}" + + if [[ "${HR}" == 1 ]]; then + printf "%s \t %s" "$(hr "${array[4]}")" "$(hr "${array[5]}")" + else + printf "%'d \t %'d" "${array[4]}" "${array[5]}" + fi + + printf " \t %s %s %s " "${array[7]}" "${array[8]}" "${array[10]}" + printf "- %s\n" "${array[9]}" + done < "${STATUS_LOG}" + else + awk \ + -F' ' \ + -v s='CLIENT_LIST' \ + '$1 == s { + print $2"\t\t"$3"\t"$4"\t"$5"\t\t"$6"\t\t"$8" "$9" "$11" - "$10"\n" + }' \ + "${STATUS_LOG}" + fi else - printf "\nNo Clients Connected!\n" + printf "\nNo Clients Connected!\n" fi printf "\n" - } | column -t -s $'\t' + } | column -t -s $'\t' } -if [[ $# -eq 0 ]]; then - HR=1 - listClients +if [[ "$#" -eq 0 ]]; then + HR=1 + listClients else - while true; do - case "$1" in - -b|bytes) - HR=0 - listClients - exit 0 - ;; - -h|help) - scriptusage - exit 0 - ;; - *) - HR=0 - listClients - exit 0 - ;; - esac - done + while true; do + case "${1}" in + -b | bytes) + HR=0 + listClients + exit 0 + ;; + -h | help) + scriptusage + exit 0 + ;; + *) + HR=0 + listClients + exit 0 + ;; + esac + done fi diff --git a/scripts/openvpn/listOVPN.sh b/scripts/openvpn/listOVPN.sh index 009c82a..3257198 100755 --- a/scripts/openvpn/listOVPN.sh +++ b/scripts/openvpn/listOVPN.sh @@ -1,42 +1,57 @@ -#!/usr/bin/env bash +#!/bin/bash # PiVPN: list clients script -# Updated Script to include Expiration Dates and Clean up Escape Seq -- psgoundar +# Updated Script to include Expiration Dates and +# Clean up Escape Seq -- psgoundar INDEX="/etc/openvpn/easy-rsa/pki/index.txt" -if [ ! -f "${INDEX}" ]; then - echo "The file: $INDEX was not found!" - exit 1 + +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + +if [[ ! -f "${INDEX}" ]]; then + err "The file: ${INDEX} was not found!" + exit 1 fi EASYRSA="/etc/openvpn/easy-rsa/easyrsa" -if [ ! -f "${EASYRSA}" ]; then - echo "The file: $EASYRSA was not found!" - exit 1 + +if [[ ! -f "${EASYRSA}" ]]; then + err "The file: ${EASYRSA} was not found!" + exit 1 fi -$EASYRSA update-db >> /dev/null 2>&1 +"${EASYRSA}" update-db >> /dev/null 2>&1 -printf ": NOTE : The first entry is your server, which should always be valid!\n" +printf ": NOTE : The first entry is your server, " +printf "which should always be valid!\n" printf "\\n" printf "\\e[1m::: Certificate Status List :::\\e[0m\\n" + { -printf "\\e[4mStatus\\e[0m \t \\e[4mName\\e[0m\\e[0m \t \\e[4mExpiration\\e[0m\\n" + printf "\\e[4mStatus\\e[0m \t \\e[4mName\\e[0m\\e[0m \t " + printf "\\e[4mExpiration\\e[0m\\n" -while read -r line || [ -n "$line" ]; do - STATUS=$(echo "$line" | awk '{print $1}') - NAME=$(echo "$line" | awk -FCN= '{print $2}') - EXPD=$(echo "$line" | awk '{if (length($2) == 15) print $2; else print "20"$2}' | cut -b 1-8 | date +"%b %d %Y" -f -) + while read -r line || [[ -n "${line}" ]]; do + STATUS="$(echo "${line}" | awk '{print $1}')" + NAME="$(echo "${line}" | awk -FCN= '{print $2}')" + EXPD="$(echo "${line}" | + awk '{if (length($2) == 15) print $2; else print "20"$2}' | + cut -b 1-8 | + date +"%b %d %Y" -f -)" - if [ "${STATUS}" == "V" ]; then - printf "Valid \t %s \t %s\\n" "$(echo -e "$NAME")" "$EXPD" - elif [ "${STATUS}" == "R" ]; then - printf "Revoked \t %s \t %s\\n" "$(echo -e "$NAME")" "$EXPD" - elif [ "${STATUS}" == "E" ]; then - printf "Expired \t %s \t %s\\n" "$(echo -e "$NAME")" "$EXPD" + if [[ "${STATUS}" == "V" ]]; then + printf "Valid" + elif [[ "${STATUS}" == "R" ]]; then + printf "Revoked" + elif [[ "${STATUS}" == "E" ]]; then + printf "Expired" else - printf "Unknown \t %s \t %s\\n" "$(echo -e "$NAME")" "$EXPD" + printf "Unknown" fi -done <${INDEX} -printf "\\n" + printf " \t %s \t %s\\n" "$(echo -e "${NAME}")" "${EXPD}" + done < "${INDEX}" + + printf "\\n" } | column -t -s $'\t' diff --git a/scripts/openvpn/makeOVPN.sh b/scripts/openvpn/makeOVPN.sh index 172224c..0f9558b 100755 --- a/scripts/openvpn/makeOVPN.sh +++ b/scripts/openvpn/makeOVPN.sh @@ -1,4 +1,5 @@ #!/bin/bash + # Create OVPN Client # Default Variable Declarations setupVars="/etc/pivpn/openvpn/setupVars.conf" @@ -10,429 +11,498 @@ CA="ca.crt" TA="ta.key" INDEX="/etc/openvpn/easy-rsa/pki/index.txt" -if [ ! -f "${setupVars}" ]; then - echo "::: Missing setup vars file!" - exit 1 +if [[ ! -f "${setupVars}" ]]; then + err "::: Missing setup vars file!" + exit 1 fi # shellcheck disable=SC1090 source "${setupVars}" -helpFunc() { - echo "::: Create a client ovpn profile, optional nopass" - echo ":::" - echo "::: Usage: pivpn <-a|add> [-n|--name ] [-p|--password ]|[nopass] [-d|--days ] [-b|--bitwarden] [-i|--iOS] [-o|--ovpn] [-h|--help]" - echo ":::" - echo "::: Commands:" - echo "::: [none] Interactive mode" - echo "::: nopass Create a client without a password" - echo "::: -n,--name Name for the Client (default: \"$(hostname)\")" - echo "::: -p,--password Password for the Client (no default)" - echo "::: -d,--days Expire the certificate after specified number of days (default: 1080)" - echo "::: -b,--bitwarden Create and save a client through Bitwarden" - echo "::: -i,--iOS Generate a certificate that leverages iOS keychain" - echo "::: -o,--ovpn Regenerate a .ovpn config file for an existing client" - echo "::: -h,--help Show this help dialog" +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 } -if [ -z "$HELP_SHOWN" ]; then - helpFunc - echo - echo "HELP_SHOWN=1" >> "$setupVars" +helpFunc() { + echo "::: Create a client ovpn profile, optional nopass" + echo ":::" + echo -n "::: Usage: pivpn <-a|add> [-n|--name ] " + echo -n "[-p|--password ]|[nopass] [-d|--days ] " + echo "[-b|--bitwarden] [-i|--iOS] [-o|--ovpn] [-h|--help]" + echo ":::" + echo "::: Commands:" + echo "::: [none] Interactive mode" + echo "::: nopass Create a client without a password" + echo -n "::: -n,--name Name for the Client " + echo "(default: \"$(hostname)\")" + echo "::: -p,--password Password for the Client (no default)" + echo -n "::: -d,--days Expire the certificate after specified " + echo "number of days (default: 1080)" + echo "::: -b,--bitwarden Create and save a client through Bitwarden" + echo -n "::: -i,--iOS Generate a certificate that leverages iOS " + echo "keychain" + echo -n "::: -o,--ovpn Regenerate a .ovpn config file for an " + echo "existing client" + echo "::: -h,--help Show this help dialog" +} + +if [[ -z "${HELP_SHOWN}" ]]; then + helpFunc + echo + echo "HELP_SHOWN=1" >> "${setupVars}" fi # Parse input arguments -while test $# -gt 0; do - _key="$1" - case "$_key" in - -n|--name|--name=*) - _val="${_key##--name=}" - if test "$_val" = "$_key"; then - test $# -lt 2 && echo "Missing value for the optional argument '$_key'." && exit 1 - _val="$2" - shift - fi - NAME="$_val" - ;; - -p|--password|--password=*) - _val="${_key##--password=}" - if test "$_val" = "$_key"; then - test $# -lt 2 && echo "Missing value for the optional argument '$_key'." && exit 1 - _val="$2" - shift - fi - PASSWD="$_val" - ;; - -d|--days|--days=*) - _val="${_key##--days=}" - if test "$_val" = "$_key"; then - test $# -lt 2 && echo "Missing value for the optional argument '$_key'." && exit 1 - _val="$2" - shift - fi - DAYS="$_val" - ;; - -i|--iOS) - if [ "$TWO_POINT_FOUR" -ne 1 ]; then - iOS=1 - else - echo "Sorry, can't generate iOS-specific configs for ECDSA certificates" - echo "Generate traditional certificates using 'pivpn -a' or reinstall PiVPN without opting in for OpenVPN 2.4 features" - exit 1 - fi - ;; - -h|--help) - helpFunc - exit 0 - ;; - nopass) - NO_PASS="1" - ;; - -b|--bitwarden) - if command -v bw > /dev/null; then - BITWARDEN="2" - else - echo 'Bitwarden not found, please install bitwarden' +while [[ "$#" -gt 0 ]]; do + _key="${1}" - if [ "${PLAT}" == 'Alpine' ]; then - echo 'You can download it through the following commands:' - echo $'\t' 'curl -fLo bitwarden.zip --no-cache https://github.com/bitwarden/clients/releases/download/cli-v2022.6.2/bw-linux-2022.6.2.zip' - echo $'\t' 'apk --no-cache -X https://dl-cdn.alpinelinux.org/alpine/edge/testing/ add atool' - echo $'\t' 'aunpack -F zip bitwarden.zip' - echo $'\t' 'mv bw /opt/bw' - echo $'\t' 'chmod 755 /opt/bw' - echo $'\t' 'rm bitwarden.zip' - echo $'\t' 'apk --no-cache --purge del -r atool' - fi + case "${_key}" in + -n | --name | --name=*) + _val="${_key##--name=}" - exit 1 - fi + if [[ "${_val}" == "${_key}" ]]; then + [[ "$#" -lt 2 ]] && + err "Missing value for the optional argument '${_key}'." && + exit 1 - ;; - -o|--ovpn) - GENOVPNONLY=1 - ;; - *) - echo "Error: Got an unexpected argument '$1'" - helpFunc - exit 1 - ;; - esac - shift + _val="${2}" + shift + fi + + NAME="${_val}" + ;; + -p | --password | --password=*) + _val="${_key##--password=}" + + if [[ "${_val}" == "${_key}" ]]; then + [[ "$#" -lt 2 ]] && + err "Missing value for the optional argument '${_key}'." && + exit 1 + + _val="${2}" + shift + fi + + PASSWD="${_val}" + ;; + -d | --days | --days=*) + _val="${_key##--days=}" + + if [[ "${_val}" == "${_key}" ]]; then + [[ "$#" -lt 2 ]] && + err "Missing value for the optional argument '${_key}'." && + exit 1 + + _val="${2}" + shift + fi + + DAYS="${_val}" + ;; + -i | --iOS) + if [[ "${TWO_POINT_FOUR}" -ne 1 ]]; then + iOS=1 + else + err "Sorry, can't generate iOS-specific configs for ECDSA certificates" + err "Generate traditional certificates using 'pivpn -a' or reinstall PiVPN without opting in for OpenVPN 2.4 features" + exit 1 + fi + ;; + -h | --help) + helpFunc + exit 0 + ;; + nopass) + NO_PASS="1" + ;; + -b | --bitwarden) + if command -v bw > /dev/null; then + BITWARDEN="2" + else + echo 'Bitwarden not found, please install bitwarden' + + if [[ "${PLAT}" == 'Alpine' ]]; then + echo 'You can download it through the following commands:' + echo -n $'\t''curl -fLo bitwarden.zip --no-cache https://github.com/' + echo -n 'bitwarden/clients/releases/download/cli-v2022.6.2/' + echo 'bw-linux-2022.6.2.zip' + echo $'\t''apk --no-cache unzip' + echo $'\t''unzip bitwarden.zip' + echo $'\t''mv bw /opt/bw' + echo $'\t''chmod 755 /opt/bw' + echo $'\t''rm bitwarden.zip' + echo $'\t''apk --no-cache --purge del -r unzip' + fi + + exit 1 + fi + + ;; + -o | --ovpn) + GENOVPNONLY=1 + ;; + *) + err "Error: Got an unexpected argument '${1}'" + helpFunc + exit 1 + ;; + esac + + shift done # Functions def -function keynoPASS() { - - #Build the client key - expect << EOF +keynoPASS() { + # Build the client key + expect << EOF set timeout -1 set env(EASYRSA_CERT_EXPIRE) "${DAYS}" spawn ./easyrsa build-client-full "${NAME}" nopass expect eof EOF - - cd pki || exit - + cd pki || exit } -function useBitwarden() { +useBitwarden() { + # login and unlock vault + printf "****Bitwarden Login****" + printf "\n" - # login and unlock vault - printf "****Bitwarden Login****" - printf "\n" - SESSION_KEY=$(bw login --raw) - export BW_SESSION=$SESSION_KEY - printf "Successfully Logged in!" - printf "\n" + SESSION_KEY="$(bw login --raw)" + export BW_SESSION="${SESSION_KEY}" - # ask user for username - printf "Enter the username: " + printf "Successfully Logged in!" + printf "\n" + + # ask user for username + printf "Enter the username: " + read -r NAME + + # check name + until [[ "${NAME}" =~ ^[a-zA-Z0-9.@_-]+$ ]] && + [[ "${NAME::1}" != "." ]] && + [[ "${NAME::1}" != "-" ]]; do + echo -n "Name can only contain alphanumeric characters and these " + echo -n "characters (.-@_). The name also cannot start with a dot (.)" + echo " or a dash (-). Please try again." + # ask user for username again + printf "Enter the username: " read -r NAME + done - # check name - until [[ "$NAME" =~ ^[a-zA-Z0-9.@_-]+$ && ${NAME::1} != "." && ${NAME::1} != "-" ]]; do - echo "Name can only contain alphanumeric characters and these characters (.-@_). The name also cannot start with a dot (.) or a dash (-). Please try again." - # ask user for username again - printf "Enter the username: " - read -r NAME - done - + # ask user for length of password + printf "Please enter the length of characters you want your password to be " + printf "(minimum 12): " + read -r LENGTH + # check length + until [[ "${LENGTH}" -gt 11 ]] && [[ "${LENGTH}" -lt 129 ]]; do + echo "Password must be between from 12 to 128 characters, please try again." # ask user for length of password - printf "Please enter the length of characters you want your password to be (minimum 12): " + printf "Please enter the length of characters you want your password to be " + printf "(minimum 12): " read -r LENGTH + done - # check length - until [[ "$LENGTH" -gt 11 && "$LENGTH" -lt 129 ]]; do - echo "Password must be between from 12 to 128 characters, please try again." - # ask user for length of password - printf "Enter the length of characters you want your password to be (minimum 12): " - read -r LENGTH - done - - printf "Creating a PiVPN item for your vault..." - printf "\n" - # create a new item for your PiVPN Password - PASSWD=$(bw generate -usln --length "$LENGTH") - bw get template item | jq '.login.type = "1"'| jq '.name = "PiVPN"' | jq -r --arg NAME "$NAME" '.login.username = $NAME' | jq -r --arg PASSWD "$PASSWD" '.login.password = $PASSWD' | bw encode | bw create item - bw logout + printf "Creating a PiVPN item for your vault..." + printf "\n" + # create a new item for your PiVPN Password + PASSWD="$(bw generate -usln --length "${LENGTH}")" + bw get template item | + jq '.login.type = "1"' | + jq '.name = "PiVPN"' | + jq -r --arg NAME "${NAME}" '.login.username = $NAME' | + jq -r --arg PASSWD "${PASSWD}" '.login.password = $PASSWD' | + bw encode | + bw create item + bw logout } -function keyPASS() { +keyPASS() { + if [[ -z "${PASSWD}" ]]; then + stty -echo + + while true; do + printf "Enter the password for the client: " + read -r PASSWD + printf "\n" + printf "Enter the password again to verify: " + read -r PASSWD2 + printf "\n" + + [[ "${PASSWD}" == "${PASSWD2}" ]] && break + + printf "Passwords do not match! Please try again.\n" + done + + stty echo if [[ -z "${PASSWD}" ]]; then - stty -echo - while true; do - printf "Enter the password for the client: " - read -r PASSWD - printf "\n" - printf "Enter the password again to verify: " - read -r PASSWD2 - printf "\n" - [ "${PASSWD}" = "${PASSWD2}" ] && break - printf "Passwords do not match! Please try again.\n" - done - stty echo - if [[ -z "${PASSWD}" ]]; then - echo "You left the password blank" - echo "If you don't want a password, please run:" - echo "pivpn add nopass" - exit 1 - fi - fi - if [ ${#PASSWD} -lt 4 ] || [ ${#PASSWD} -gt 1024 ]; then - echo "Password must be between from 4 to 1024 characters" - exit 1 + err "You left the password blank" + err "If you don't want a password, please run:" + err "pivpn add nopass" + exit 1 fi + fi - #Escape chars in PASSWD - PASSWD_UNESCAPED="${PASSWD}" - PASSWD=$(echo -n "${PASSWD}" | sed -E -e 's/\\/\\\\/g' -e 's/\//\\\//g' -e 's/\$/\\\$/g' -e 's/!/\\!/g' -e 's/\./\\\./g' -e "s/'/\\'/g" -e 's/"/\\"/g' -e 's/\*/\\\*/g' -e 's/@/\\@/g' -e 's/#/\\#/g' -e 's/£/\\£/g' -e 's/%/\\%/g' -e 's/\^/\\\^/g' -e 's/&/\\&/g' -e 's/\(/\\\(/g' -e 's/\)/\\\)/g' -e 's/\-/\\\-/g' -e 's/_/\\_/g' -e 's/\+/\\\+/g' -e 's/=/\\=/g' -e 's/\[/\\\[/g' -e 's/\]/\\\]/g' -e 's/;/\\;/g' -e 's/:/\\:/g' -e 's/\|/\\\|/g' -e 's/\/\\\>/g' -e 's/,/\\,/g' -e 's/\?/\\\?/g' -e 's/~/\\~/g' -e 's/\{/\\\{/g' -e 's/\}/\\\}/g') + if [[ "${#PASSWD}" -lt 4 ]] || [[ "${#PASSWD}" -gt 1024 ]]; then + err "Password must be between from 4 to 1024 characters" + exit 1 + fi - #Build the client key and then encrypt the key + # Escape chars in PASSWD + PASSWD_UNESCAPED="${PASSWD}" - expect << EOF - set timeout -1 - set env(EASYRSA_CERT_EXPIRE) "${DAYS}" - spawn ./easyrsa build-client-full "${NAME}" - expect "Enter PEM pass phrase" { sleep 0.1; send -- "${PASSWD}\r" } - expect "Verifying - Enter PEM pass phrase" { sleep 0.1; send -- "${PASSWD}\r" } - expect eof + PASSWD="${PASSWD//\\/\\\\}" + PASSWD="${PASSWD//\//\\\/}" + PASSWD="${PASSWD//\$/\\\$}" + PASSWD="${PASSWD//!/\\!}" + PASSWD="${PASSWD//\./\\\.}" + PASSWD="${PASSWD//\'/\\\'}" + PASSWD="${PASSWD//\"/\\\"}" + PASSWD="${PASSWD//*/\\*}" + PASSWD="${PASSWD//@/\\@}" + PASSWD="${PASSWD//#/\\#}" + PASSWD="${PASSWD//\£/\\\£}" + PASSWD="${PASSWD//%/\\%}" + PASSWD="${PASSWD//\^/\\\^}" + PASSWD="${PASSWD//\&/\\\&}" + PASSWD="${PASSWD//\(/\\\(}" + PASSWD="${PASSWD//\)/\\\)}" + PASSWD="${PASSWD//\-/\\\-}" + PASSWD="${PASSWD//\_/\\\_}" + PASSWD="${PASSWD//\+/\\\+}" + PASSWD="${PASSWD//\=/\\\=}" + PASSWD="${PASSWD//\[/\\\[}" + PASSWD="${PASSWD//\]/\\\]}" + PASSWD="${PASSWD//:/\\:}" + PASSWD="${PASSWD//\;/\\\;}" + PASSWD="${PASSWD//\|/\\\|}" + PASSWD="${PASSWD//\/\\\>}" + PASSWD="${PASSWD//\,/\\\,}" + PASSWD="${PASSWD//\~/\\\~}" + PASSWD="${PASSWD//\?/\\\?}" + PASSWD="${PASSWD//\{/\\\{}" + PASSWD="${PASSWD//\}/\\\}}" + + # Build the client key and then encrypt the key + expect << EOF + set timeout -1 + set env(EASYRSA_CERT_EXPIRE) "${DAYS}" + spawn ./easyrsa build-client-full "${NAME}" + expect "Enter PEM pass phrase" {sleep 0.1; send -- "${PASSWD}\r"} + expect "Verifying - Enter PEM pass phrase" {sleep 0.1; send -- "${PASSWD}\r"} + expect eof EOF - cd pki || exit - + cd pki || exit } #make sure ovpns dir exists # Disabling warning for SC2154, var sourced externaly # shellcheck disable=SC2154 -if [ ! -d "$install_home/ovpns" ]; then - mkdir "$install_home/ovpns" - chown "$install_user":"$install_user" "$install_home/ovpns" - chmod 0750 "$install_home/ovpns" +if [[ ! -d "${install_home}/ovpns" ]]; then + mkdir "${install_home}/ovpns" + chown "${install_user}:${install_user}" "${install_home}/ovpns" + chmod 0750 "${install_home}/ovpns" fi #bitWarden if [[ "${BITWARDEN}" =~ "2" ]]; then - useBitwarden -fi - -if [ -z "${NAME}" ]; then - printf "Enter a Name for the Client: " - read -r NAME -fi - -if [[ ${NAME::1} == "." ]] || [[ ${NAME::1} == "-" ]]; then - echo "Names cannot start with a dot (.) or a dash (-)." - exit 1 -fi - -if [[ "${NAME}" =~ [^a-zA-Z0-9.@_-] ]]; then - echo "Name can only contain alphanumeric characters and these characters (.-@_)." - exit 1 -fi - -if [[ "${NAME}" =~ ^[0-9]+$ ]]; then - echo "Names cannot be integers." - exit 1 + useBitwarden fi if [[ -z "${NAME}" ]]; then - echo "You cannot leave the name blank." - exit 1 + printf "Enter a Name for the Client: " + read -r NAME +elif [[ "${NAME::1}" == "." ]] || [[ "${NAME::1}" == "-" ]]; then + err "Names cannot start with a dot (.) or a dash (-)." + exit 1 +elif [[ "${NAME}" =~ [^a-zA-Z0-9.@_-] ]]; then + err "Name can only contain alphanumeric characters and these symbols (.-@_)." + exit 1 +elif [[ "${NAME}" =~ ^[0-9]+$ ]]; then + err "Names cannot be integers." + exit 1 +elif [[ -z "${NAME}" ]]; then + err "You cannot leave the name blank." + exit 1 fi -if [ "${GENOVPNONLY}" == "1" ]; then - # Generate .ovpn configuration file - cd /etc/openvpn/easy-rsa/pki || exit +if [[ "${GENOVPNONLY}" == 1 ]]; then + # Generate .ovpn configuration file + cd /etc/openvpn/easy-rsa/pki || exit else - # Check if name is already in use - while read -r line || [ -n "${line}" ]; do - STATUS=$(echo "$line" | awk '{print $1}') + # Check if name is already in use + while read -r line || [[ -n "${line}" ]]; do + STATUS=$(echo "${line}" | awk '{print $1}') - if [ "${STATUS}" == "V" ]; then - # Disabling SC2001 as ${variable//search/replace} doesn't go well with regexp - # shellcheck disable=SC2001 - CERT=$(echo "$line" | sed -e 's:.*/CN=::') - if [ "${CERT}" == "${NAME}" ]; then - INUSE="1" - break - fi - fi - done <${INDEX} + if [[ "${STATUS}" == "V" ]]; then + # Disabling SC2001 as ${variable//search/replace} + # doesn't go well with regexp + # shellcheck disable=SC2001 + CERT="$(echo "${line}" | sed -e 's:.*/CN=::')" - if [ "${INUSE}" == "1" ]; then - printf "\n!! This name is already in use by a Valid Certificate." - printf "\nPlease choose another name or revoke this certificate first.\n" - exit 1 + if [[ "${CERT}" == "${NAME}" ]]; then + INUSE="1" + break + fi fi + done < "${INDEX}" - # Check if name is reserved - if [ "${NAME}" == "ta" ] || [ "${NAME}" == "server" ] || [ "${NAME}" == "ca" ]; then - echo "Sorry, this is in use by the server and cannot be used by clients." - exit 1 - fi + if [[ "${INUSE}" == 1 ]]; then + err "!! This name is already in use by a Valid Certificate." + err "Please choose another name or revoke this certificate first." + exit 1 + # Check if name is reserved + elif [[ "${NAME}" == "ta" ]] || + [[ "${NAME}" == "server" ]] || + [[ "${NAME}" == "ca" ]]; then + err "Sorry, this is in use by the server and cannot be used by clients." + exit 1 + fi - #As of EasyRSA 3.0.6, by default certificates last 1080 days, see https://github.com/OpenVPN/easy-rsa/blob/6b7b6bf1f0d3c9362b5618ad18c66677351cacd1/easyrsa3/vars.example - if [ -z "${DAYS}" ]; then - read -r -e -p "How many days should the certificate last? " -i 1080 DAYS - fi + # As of EasyRSA 3.0.6, by default certificates last 1080 days, + # see https://github.com/OpenVPN/easy-rsa/blob/6b7b6bf1f0d3c9362b5618ad18c66677351cacd1/easyrsa3/vars.example + if [[ -z "${DAYS}" ]]; then + read -r -e -p "How many days should the certificate last? " -i 1080 DAYS + fi - if [[ ! "$DAYS" =~ ^[0-9]+$ ]] || [ "$DAYS" -lt 1 ] || [ "$DAYS" -gt 3650 ]; then - #The CRL lasts 3650 days so it doesn't make much sense that certificates would last longer - echo "Please input a valid number of days, between 1 and 3650 inclusive." - exit 1 - fi + if [[ ! "${DAYS}" =~ ^[0-9]+$ ]] || + [[ "${DAYS}" -lt 1 ]] || + [[ "${DAYS}" -gt 3650 ]]; then + # The CRL lasts 3650 days so it doesn't make much sense + # that certificates would last longer + err "Please input a valid number of days, between 1 and 3650 inclusive." + exit 1 + fi - cd /etc/openvpn/easy-rsa || exit + cd /etc/openvpn/easy-rsa || exit - if [[ "${NO_PASS}" =~ "1" ]]; then - if [[ -n "${PASSWD}" ]]; then - echo "Both nopass and password arguments passed to the script. Please use either one." - exit 1 - else - keynoPASS - fi + if [[ "${NO_PASS}" =~ "1" ]]; then + if [[ -n "${PASSWD}" ]]; then + err "Both nopass and password arguments passed to the script. Please use either one." + exit 1 else - keyPASS + keynoPASS fi + else + keyPASS + fi fi #1st Verify that clients Public Key Exists -if [ ! -f "issued/${NAME}${CRT}" ]; then - echo "[ERROR]: Client Public Key Certificate not found: $NAME$CRT" - exit +if [[ ! -f "issued/${NAME}${CRT}" ]]; then + err "[ERROR]: Client Public Key Certificate not found: ${NAME}${CRT}" + exit fi -echo "Client's cert found: $NAME$CRT" + +echo "Client's cert found: ${NAME}${CRT}" #Then, verify that there is a private key for that client -if [ ! -f "private/${NAME}${KEY}" ]; then - echo "[ERROR]: Client Private Key not found: $NAME$KEY" - exit +if [[ ! -f "private/${NAME}${KEY}" ]]; then + err "[ERROR]: Client Private Key not found: ${NAME}${KEY}" + exit fi -echo "Client's Private Key found: $NAME$KEY" + +echo "Client's Private Key found: ${NAME}${KEY}" #Confirm the CA public key exists -if [ ! -f "${CA}" ]; then - echo "[ERROR]: CA Public Key not found: $CA" - exit +if [[ ! -f "${CA}" ]]; then + err "[ERROR]: CA Public Key not found: ${CA}" + exit fi -echo "CA public Key found: $CA" + +echo "CA public Key found: ${CA}" #Confirm the tls key file exists -if [ ! -f "${TA}" ]; then - echo "[ERROR]: tls Private Key not found: $TA" - exit +if [[ ! -f "${TA}" ]]; then + err "[ERROR]: tls Private Key not found: ${TA}" + exit fi -echo "tls Private Key found: $TA" +echo "tls Private Key found: ${TA}" ## Added new step to create an .ovpn12 file that can be stored on iOS keychain -## This step is more secure method and does not require the end-user to keep entering passwords, or storing the client private cert where it can be easily tampered +## This step is more secure method and does not require the end-user to keep +## entering passwords, or storing the client private cert where it can be easily +## tampered ## https://openvpn.net/faq/how-do-i-use-a-client-certificate-and-private-key-from-the-ios-keychain/ -if [ "$iOS" = "1" ]; then - #Generates the .ovpn file WITHOUT the client private key - { - # Start by populating with the default file - cat "${DEFAULT}" - #Now, append the CA Public Cert - echo "" - cat "${CA}" - echo "" +# Generates the .ovpn file WITHOUT the client private key +{ + # Start by populating with the default file + cat "${DEFAULT}" - #Next append the client Public Cert - echo "" - sed -n -e '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' < "issued/${NAME}${CRT}" - echo "" + # Now, append the CA Public Cert + echo "" + cat "${CA}" + echo "" - #Finally, append the tls Private Key - echo "" - cat "${TA}" - echo "" + # Next append the client Public Cert + echo "" + sed \ + -n \ + -e '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' \ + < "issued/${NAME}${CRT}" + echo "" - } > "${NAME}${FILEEXT}" - - # Copy the .ovpn profile to the home directory for convenient remote access - - printf "========================================================\n" - printf "Generating an .ovpn12 file for use with iOS devices\n" - printf "Please remember the export password\n" - printf "as you will need this import the certificate on your iOS device\n" - printf "========================================================\n" - openssl pkcs12 -passin pass:"$PASSWD_UNESCAPED" -export -in "issued/${NAME}${CRT}" -inkey "private/${NAME}${KEY}" -certfile ${CA} -name "${NAME}" -out "$install_home/ovpns/$NAME.ovpn12" - chown "$install_user":"$install_user" "$install_home/ovpns/$NAME.ovpn12" - chmod 640 "$install_home/ovpns/$NAME.ovpn12" - printf "========================================================\n" - printf "\e[1mDone! %s successfully created!\e[0m \n" "$NAME.ovpn12" - printf "You will need to transfer both the .ovpn and .ovpn12 files\n" - printf "to your iOS device.\n" - printf "========================================================\n\n" -else - #This is the standard non-iOS configuration -#Ready to make a new .ovpn file - { - # Start by populating with the default file - cat "${DEFAULT}" - - #Now, append the CA Public Cert - echo "" - cat "${CA}" - echo "" - - #Next append the client Public Cert - echo "" - sed -n -e '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' < "issued/${NAME}${CRT}" - echo "" - - #Then, append the client Private Key + if [[ "${iOS}" != 1 ]]; then + # Then, append the client Private Key echo "" cat "private/${NAME}${KEY}" echo "" + fi - #Finally, append the tls Private Key - if [ "$TWO_POINT_FOUR" -eq 1 ]; then - echo "" - cat "${TA}" - echo "" - else - echo "" - cat "${TA}" - echo "" - fi + # Finally, append the tls Private Key + if [[ "${iOS}" != 1 ]] && [[ "${TWO_POINT_FOUR}" -eq 1 ]]; then + echo "" + cat "${TA}" + echo "" + else + echo "" + cat "${TA}" + echo "" + fi +} > "${NAME}${FILEEXT}" - } > "${NAME}${FILEEXT}" +if [[ "${iOS}" == 1 ]]; then + # Copy the .ovpn profile to the home directory for convenient remote access + printf "========================================================\n" + printf "Generating an .ovpn12 file for use with iOS devices\n" + printf "Please remember the export password\n" + printf "as you will need this import the certificate on your iOS device\n" + printf "========================================================\n" + openssl pkcs12 \ + -passin pass:"${PASSWD_UNESCAPED}" \ + -export \ + -in "issued/${NAME}${CRT}" \ + -inkey "private/${NAME}${KEY}" \ + -certfile "${CA}" \ + -name "${NAME}" \ + -out "${install_home}/ovpns/${NAME}.ovpn12" + + chown "${install_user}:${install_user}" "${install_home}/ovpns/${NAME}.ovpn12" + chmod 640 "${install_home}/ovpns/${NAME}.ovpn12" + + printf "========================================================\n" + printf "\e[1mDone! %s successfully created!\e[0m \n" "${NAME}.ovpn12" + printf "You will need to transfer both the .ovpn and .ovpn12 files\n" + printf "to your iOS device.\n" + printf "========================================================\n\n" fi -cidrToMask(){ - # Source: https://stackoverflow.com/a/20767392 - set -- $(( 5 - ($1 / 8) )) 255 255 255 255 $(( (255 << (8 - ($1 % 8))) & 255 )) 0 0 0 - shift "$1" - echo "${1-0}"."${2-0}"."${3-0}"."${4-0}" +cidrToMask() { + # Source: https://stackoverflow.com/a/20767392 + set -- $((5 - (${1} / 8))) \ + 255 255 255 255 \ + $(((255 << (8 - (${1} % 8))) & 255)) \ + 0 0 0 + shift "${1}" + echo "${1-0}.${2-0}.${3-0}.${4-0}" } #disabling SC2514, variable sourced externaly @@ -441,37 +511,45 @@ NET_REDUCED="${pivpnNET::-2}" # Find an unused number for the last octet of the client IP for i in {2..254}; do - # find returns 0 if the folder is empty, so we create the 'ls -A [...]' - # exception to stop at the first static IP (10.8.0.2). Otherwise it would - # cycle to the end without finding and available octet. - # disabling SC2514, variable sourced externaly - # shellcheck disable=SC2154 - if [ -z "$(ls -A /etc/openvpn/ccd)" ] || ! find /etc/openvpn/ccd -type f -exec grep -q "${NET_REDUCED}.${i}" {} +; then - COUNT="${i}" - echo "ifconfig-push ${NET_REDUCED}.${i} $(cidrToMask "$subnetClass")" >> /etc/openvpn/ccd/"${NAME}" - break - fi + # find returns 0 if the folder is empty, so we create the 'ls -A [...]' + # exception to stop at the first static IP (10.8.0.2). Otherwise it would + # cycle to the end without finding and available octet. + # disabling SC2514, variable sourced externaly + # shellcheck disable=SC2154 + if [[ -z "$(ls -A /etc/openvpn/ccd)" ]] || + ! find /etc/openvpn/ccd \ + -type f \ + -exec grep -q "${NET_REDUCED}.${i}" {} +; then + COUNT="${i}" + echo -n "ifconfig-push ${NET_REDUCED}.${i}" >> /etc/openvpn/ccd/"${NAME}" + cidrToMask "${subnetClass}" >> /etc/openvpn/ccd/"${NAME}" + break + fi done -if [ -f /etc/pivpn/hosts.openvpn ]; then - echo "${NET_REDUCED}.${COUNT} ${NAME}.pivpn" >> /etc/pivpn/hosts.openvpn - if killall -SIGHUP pihole-FTL; then - echo "::: Updated hosts file for Pi-hole" - else - echo "::: Failed to reload pihole-FTL configuration" - fi +if [[ -f /etc/pivpn/hosts.openvpn ]]; then + echo "${NET_REDUCED}.${COUNT} ${NAME}.pivpn" >> /etc/pivpn/hosts.openvpn + + if killall -SIGHUP pihole-FTL; then + echo "::: Updated hosts file for Pi-hole" + else + err "::: Failed to reload pihole-FTL configuration" + fi fi # Copy the .ovpn profile to the home directory for convenient remote access -cp "/etc/openvpn/easy-rsa/pki/$NAME$FILEEXT" "$install_home/ovpns/$NAME$FILEEXT" -chown "$install_user":"$install_user" "$install_home/ovpns/$NAME$FILEEXT" -chmod 640 "/etc/openvpn/easy-rsa/pki/$NAME$FILEEXT" -chmod 640 "$install_home/ovpns/$NAME$FILEEXT" +dest_path="${install_home}/ovpns/${NAME}${FILEEXT}" +cp "/etc/openvpn/easy-rsa/pki/${NAME}${FILEEXT}" "${dest_path}" +chown "${install_user}:${install_user}" "${dest_path}" +chmod 640 "/etc/openvpn/easy-rsa/pki/${NAME}${FILEEXT}" +chmod 640 "${dest_path}" +unset dest_path + printf "\n\n" printf "========================================================\n" -printf "\e[1mDone! %s successfully created!\e[0m \n" "$NAME$FILEEXT" -printf "%s was copied to:\n" "$NAME$FILEEXT" -printf " %s/ovpns\n" "$install_home" +printf "\e[1mDone! %s successfully created!\e[0m \n" "${NAME}${FILEEXT}" +printf "%s was copied to:\n" "${NAME}${FILEEXT}" +printf " %s/ovpns\n" "${install_home}" printf "for easy transfer. Please use this profile only on one\n" printf "device and create additional profiles for other devices.\n" printf "========================================================\n\n" diff --git a/scripts/openvpn/pivpn.sh b/scripts/openvpn/pivpn.sh index d8d64fd..b67321a 100755 --- a/scripts/openvpn/pivpn.sh +++ b/scripts/openvpn/pivpn.sh @@ -3,15 +3,15 @@ CHECK_PKG_INSTALLED='dpkg-query -s' if grep -qsEe "^NAME\=['\"]?Alpine[a-zA-Z ]*['\"]?$" /etc/os-release; then - CHECK_PKG_INSTALLED='apk --no-cache info -e' + CHECK_PKG_INSTALLED='apk --no-cache info -e' fi # Must be root to use this tool -if [[ ! $EUID -eq 0 ]]; then - if eval "${CHECK_PKG_INSTALLED} sudo" &> /dev/null; then - export SUDO="sudo" +if [[ "${EUID}" -ne 0 ]]; then + if ${CHECK_PKG_INSTALLED} sudo &> /dev/null; then + export SUDO="sudo" else - echo "::: Please install sudo or run this as root." + err "::: Please install sudo or run this as root." exit 1 fi fi @@ -19,88 +19,113 @@ fi scriptDir="/opt/pivpn" vpn="openvpn" -function makeOVPNFunc { - shift - $SUDO ${scriptDir}/${vpn}/makeOVPN.sh "$@" - exit "$?" +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 } -function listClientsFunc { - shift - $SUDO ${scriptDir}/${vpn}/clientStat.sh "$@" - exit "$?" +makeOVPNFunc() { + shift + ${SUDO} "${scriptDir}/${vpn}/makeOVPN.sh" "$@" + exit "${?}" } -function listOVPNFunc { - $SUDO ${scriptDir}/${vpn}/listOVPN.sh - exit "$?" +listClientsFunc() { + shift + ${SUDO} "${scriptDir}/${vpn}/clientStat.sh" "$@" + exit "${?}" } -function debugFunc { - echo "::: Generating Debug Output" - $SUDO ${scriptDir}/${vpn}/pivpnDebug.sh | tee /tmp/debug.log - echo "::: " - echo "::: Debug output completed above." - echo "::: Copy saved to /tmp/debug.log" - echo "::: " - exit "$?" +listOVPNFunc() { + ${SUDO} "${scriptDir}/${vpn}/listOVPN.sh" + exit "${?}" } -function removeOVPNFunc { - shift - $SUDO ${scriptDir}/${vpn}/removeOVPN.sh "$@" - exit "$?" +debugFunc() { + echo "::: Generating Debug Output" + + ${SUDO} "${scriptDir}/${vpn}/pivpnDebug.sh" | tee /tmp/debug.log + + echo "::: " + echo "::: Debug output completed above." + echo "::: Copy saved to /tmp/debug.log" + echo "::: " + exit "${?}" } -function uninstallFunc { - $SUDO ${scriptDir}/uninstall.sh "${vpn}" - exit "$?" +removeOVPNFunc() { + shift + ${SUDO} "${scriptDir}/${vpn}/removeOVPN.sh" "$@" + exit "${?}" } -function update { - shift - $SUDO ${scriptDir}/update.sh "$@" - exit "$?" +uninstallFunc() { + ${SUDO} "${scriptDir}/uninstall.sh" "${vpn}" + exit "${?}" } -function backup { - $SUDO ${scriptDir}/backup.sh "${vpn}" - exit "$?" +update() { + shift + ${SUDO} "${scriptDir}/update.sh" "$@" + exit "${?}" } - -function helpFunc { - echo "::: Control all PiVPN specific functions!" - echo ":::" - echo "::: Usage: pivpn [option]" - echo ":::" - echo "::: Commands:" - echo "::: -a, add [nopass] Create a client ovpn profile, optional nopass" - echo "::: -c, clients List any connected clients to the server" - echo "::: -d, debug Start a debugging session if having trouble" - echo "::: -l, list List all valid and revoked certificates" - echo "::: -r, revoke Revoke a client ovpn profile" - echo "::: -h, help Show this help dialog" - echo "::: -u, uninstall Uninstall PiVPN from your system!" - echo "::: -up, update Updates PiVPN Scripts" - echo "::: -bk, backup Backup Openvpn and ovpns dir" - exit 0 +backup() { + ${SUDO} "${scriptDir}/backup.sh" "${vpn}" + exit "${?}" } -if [[ $# = 0 ]]; then - helpFunc +helpFunc() { + echo "::: Control all PiVPN specific functions!" + echo ":::" + echo "::: Usage: pivpn [option]" + echo ":::" + echo "::: Commands:" + echo "::: -a, add [nopass] Create a client ovpn profile, optional nopass" + echo "::: -c, clients List any connected clients to the server" + echo "::: -d, debug Start a debugging session if having trouble" + echo "::: -l, list List all valid and revoked certificates" + echo "::: -r, revoke Revoke a client ovpn profile" + echo "::: -h, help Show this help dialog" + echo "::: -u, uninstall Uninstall PiVPN from your system!" + echo "::: -up, update Updates PiVPN Scripts" + echo "::: -bk, backup Backup Openvpn and ovpns dir" + exit 0 +} + +if [[ "$#" == 0 ]]; then + helpFunc fi # Handle redirecting to specific functions based on arguments -case "$1" in -"-a" | "add" ) makeOVPNFunc "$@";; -"-c" | "clients" ) listClientsFunc "$@";; -"-d" | "debug" ) debugFunc;; -"-l" | "list" ) listOVPNFunc;; -"-r" | "revoke" ) removeOVPNFunc "$@";; -"-h" | "help" ) helpFunc;; -"-u" | "uninstall" ) uninstallFunc;; -"-up"| "update" ) update "$@" ;; -"-bk"| "backup" ) backup;; -* ) helpFunc;; +case "${1}" in + "-a" | "add") + makeOVPNFunc "$@" + ;; + "-c" | "clients") + listClientsFunc "$@" + ;; + "-d" | "debug") + debugFunc + ;; + "-l" | "list") + listOVPNFunc + ;; + "-r" | "revoke") + removeOVPNFunc "$@" + ;; + "-h" | "help") + helpFunc + ;; + "-u" | "uninstall") + uninstallFunc + ;; + "-up" | "update") + update "$@" + ;; + "-bk" | "backup") + backup + ;; + *) + helpFunc + ;; esac diff --git a/scripts/openvpn/pivpnDebug.sh b/scripts/openvpn/pivpnDebug.sh index 35a39f1..fc78e8d 100755 --- a/scripts/openvpn/pivpnDebug.sh +++ b/scripts/openvpn/pivpnDebug.sh @@ -1,11 +1,15 @@ -#!/usr/bin/env bash +#!/bin/bash # This scripts runs as root setupVars="/etc/pivpn/openvpn/setupVars.conf" -if [ ! -f "${setupVars}" ]; then - echo "::: Missing setup vars file!" - exit 1 +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + +if [[ ! -f "${setupVars}" ]]; then + err "::: Missing setup vars file!" + exit 1 fi # shellcheck disable=SC1090 @@ -15,41 +19,63 @@ echo -e "::::\t\t\e[4mPiVPN debug\e[0m\t\t ::::" printf "=============================================\n" echo -e "::::\t\t\e[4mLatest commit\e[0m\t\t ::::" echo -n "Branch: " + git --git-dir /usr/local/src/pivpn/.git rev-parse --abbrev-ref HEAD -git --git-dir /usr/local/src/pivpn/.git log -n 1 --format='Commit: %H%nAuthor: %an%nDate: %ad%nSummary: %s' +git \ + --git-dir /usr/local/src/pivpn/.git log \ + -n 1 \ + --format='Commit: %H%nAuthor: %an%nDate: %ad%nSummary: %s' + printf "=============================================\n" echo -e "::::\t \e[4mInstallation settings\e[0m \t ::::" + # shellcheck disable=SC2154 -sed "s/$pivpnHOST/REDACTED/" < ${setupVars} +sed "s/${pivpnHOST}/REDACTED/" < "${setupVars}" + printf "=============================================\n" echo -e ":::: \e[4mServer configuration shown below\e[0m ::::" + cat /etc/openvpn/server.conf + printf "=============================================\n" echo -e ":::: \e[4mClient template file shown below\e[0m ::::" -sed "s/$pivpnHOST/REDACTED/" < /etc/openvpn/easy-rsa/pki/Default.txt + +sed "s/${pivpnHOST}/REDACTED/" < /etc/openvpn/easy-rsa/pki/Default.txt + printf "=============================================\n" -echo -e ":::: \t\e[4mRecursive list of files in\e[0m\t ::::\n::: \e[4m/etc/openvpn/easy-rsa/pki shows below\e[0m :::" +echo -e ":::: \t\e[4mRecursive list of files in\e[0m\t ::::\n" +echo -e "::: \e[4m/etc/openvpn/easy-rsa/pki shows below\e[0m :::" + ls -LR /etc/openvpn/easy-rsa/pki/ -Ireqs -Icerts_by_serial + printf "=============================================\n" echo -e "::::\t\t\e[4mSelf check\e[0m\t\t ::::" + /opt/pivpn/self_check.sh "${VPN}" + printf "=============================================\n" echo -e ":::: Having trouble connecting? Take a look at the FAQ:" echo -e ":::: \e[1mhttps://docs.pivpn.io/faq\e[0m" printf "=============================================\n" -if [ "${PLAT}" != 'Alpine' ]; then - echo -e ":::: \e[4mSnippet of the server log\e[0m ::::" - OVPNLOG="$(tail -n 20 /var/log/openvpn.log)" +if [[ "${PLAT}" != 'Alpine' ]]; then + echo -e ":::: \e[4mSnippet of the server log\e[0m ::::" + OVPNLOG="$(tail -n 20 /var/log/openvpn.log)" - # Regular expession taken from https://superuser.com/a/202835, it will match invalid IPs - # like 123.456.789.012 but it's fine since the log only contains valid ones. - declare -a IPS_TO_HIDE=("$(grepcidr -v 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 <<< "$OVPNLOG" | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | uniq)") - for IP in "${IPS_TO_HIDE[@]}"; do - OVPNLOG="${OVPNLOG//"$IP"/REDACTED}" - done + # Regular expession taken from https://superuser.com/a/202835, + # it will match invalid IPs like 123.456.789.012 but it's fine + # since the log only contains valid ones. + declare -a IPS_TO_HIDE=("$(echo "${OVPNLOG}" | + grepcidr -v 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 | + grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | + uniq)") - echo "$OVPNLOG" - printf "=============================================\n" + for IP in "${IPS_TO_HIDE[@]}"; do + OVPNLOG="${OVPNLOG//"$IP"/REDACTED}" + done + + echo "${OVPNLOG}" + printf "=============================================\n" fi + echo -e "::::\t\t\e[4mDebug complete\e[0m\t\t ::::" diff --git a/scripts/openvpn/removeOVPN.sh b/scripts/openvpn/removeOVPN.sh index 3c50398..35210b2 100755 --- a/scripts/openvpn/removeOVPN.sh +++ b/scripts/openvpn/removeOVPN.sh @@ -1,172 +1,196 @@ -#!/usr/bin/env bash +#!/bin/bash # PiVPN: revoke client script setupVars="/etc/pivpn/openvpn/setupVars.conf" INDEX="/etc/openvpn/easy-rsa/pki/index.txt" -if [ ! -f "${setupVars}" ]; then - echo "::: Missing setup vars file!" - exit 1 +if [[ ! -f "${setupVars}" ]]; then + err "::: Missing setup vars file!" + exit 1 fi # shellcheck disable=SC1090 source "${setupVars}" +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + helpFunc() { - echo "::: Revoke a client ovpn profile" - echo ":::" - echo "::: Usage: pivpn <-r|revoke> [-y|--yes] [-h|--help] [] ... [] ..." - echo ":::" - echo "::: Commands:" - echo "::: [none] Interactive mode" - echo "::: Client(s) to to revoke" - echo "::: -y,--yes Remove Client(s) without confirmation" - echo "::: -h,--help Show this help dialog" + echo "::: Revoke a client ovpn profile" + echo ":::" + echo -n "::: Usage: pivpn <-r|revoke> [-y|--yes] [-h|--help] " + echo "[ ... [] ...]" + echo ":::" + echo "::: Commands:" + echo "::: [none] Interactive mode" + echo "::: Client(s) to to revoke" + echo "::: -y,--yes Remove Client(s) without confirmation" + echo "::: -h,--help Show this help dialog" } # Parse input arguments -while test $# -gt 0; do - _key="$1" - case "$_key" in - -h|--help) - helpFunc - exit 0 - ;; - -y|--yes) - CONFIRM=true - ;; - *) - CERTS_TO_REVOKE+=("$1") - ;; - esac - shift +while [[ "$#" -gt 0 ]]; do + _key="${1}" + + case "${_key}" in + -h | --help) + helpFunc + exit 0 + ;; + -y | --yes) + CONFIRM=true + ;; + *) + CERTS_TO_REVOKE+=("${1}") + ;; + esac + + shift done -if [ ! -f "${INDEX}" ]; then - printf "The file: %s was not found\n" "$INDEX" - exit 1 +if [[ ! -f "${INDEX}" ]]; then + err "The file: ${INDEX} was not found" + exit 1 fi # Disabling SC2128, just checking if variable is empty or not # shellcheck disable=SC2128 if [[ -z "${CERTS_TO_REVOKE}" ]]; then - printf "\n" - printf " ::\e[4m Certificate List \e[0m:: \n" + printf "\n" + printf " ::\e[4m Certificate List \e[0m:: \n" - i=0 - while read -r line || [ -n "$line" ]; do - STATUS=$(echo "$line" | awk '{print $1}') - if [[ "${STATUS}" = "V" ]]; then - # Disabling SC2001 warning, suggested method doesn't work with regexp - # shellcheck disable=SC2001 - NAME=$(echo "$line" | sed -e 's:.*/CN=::') - if [ "$i" != 0 ]; then - # Prevent printing "server" certificate - CERTS[$i]=$(echo -e "${NAME}") - fi - ((i++)) - fi - done <${INDEX} + i=0 + while read -r line || [[ -n "${line}" ]]; do + STATUS=$(echo "${line}" | awk '{print $1}') - i=1 - len=${#CERTS[@]} - while [ $i -le "${len}" ]; do - printf "%0${#len}s) %s\r\n" ${i} "${CERTS[(($i))]}" - ((i++)) - done - printf "\n" + if [[ "${STATUS}" == "V" ]]; then + # Disabling SC2001 warning, suggested method doesn't work with regexp + # shellcheck disable=SC2001 + NAME=$(echo "${line}" | sed -e 's:.*/CN=::') - echo -n "::: Please enter the Index/Name of the client to be revoked from the list above: " - read -r NAME + if [[ "${i}" != 0 ]]; then + # Prevent printing "server" certificate + CERTS["${i}"]=$(echo -e "${NAME}") + fi - if [[ -z "${NAME}" ]]; then - echo "You can not leave this blank!" - exit 1 + ((i++)) fi + done < "${INDEX}" - re='^[0-9]+$' - if [[ ${NAME} =~ $re ]] ; then - NAME=${CERTS[$((NAME))]} + i=1 + len="${#CERTS[@]}" + while [[ "${i}" -le "${len}" ]]; do + printf "%0${#len}s) %s\r\n" "${i}" "${CERTS[(($i))]}" + ((i++)) + done + + printf "\n" + echo -n "::: Please enter the Index/Name of the client to be revoked " + echo -n "from the list above: " + read -r NAME + + if [[ -z "${NAME}" ]]; then + err "You can not leave this blank!" + exit 1 + fi + + re='^[0-9]+$' + if [[ "${NAME}" =~ $re ]]; then + NAME="${CERTS[$((NAME))]}" + fi + + for ((x = 1; x <= i; ++x)); do + if [[ "${CERTS[$x]}" == "${NAME}" ]]; then + VALID=1 fi + done - for((x=1;x<=i;++x)); do - if [ "${CERTS[$x]}" = "${NAME}" ]; then - VALID=1 - fi - done + if [[ -z "${VALID}" ]]; then + err "You didn't enter a valid cert name!" + exit 1 + fi - if [ -z "${VALID}" ]; then - printf "You didn't enter a valid cert name!\n" - exit 1 - fi - - CERTS_TO_REVOKE=( "${NAME}" ) + CERTS_TO_REVOKE=("${NAME}") else - i=0 - while read -r line || [ -n "$line" ]; do - STATUS=$(echo "$line" | awk '{print $1}') - if [[ "${STATUS}" = "V" ]]; then - NAME=$(echo -e "$line" | sed -e 's:.*/CN=::') - CERTS[$i]=${NAME} - ((i++)) - fi - done <${INDEX} + i=0 + while read -r line || [[ -n "${line}" ]]; do + STATUS=$(echo "${line}" | awk '{print $1}') - for (( ii = 0; ii < ${#CERTS_TO_REVOKE[@]}; ii++)); do - VALID=0 - for((x=1;x<=i;++x)); do - if [ "${CERTS[$x]}" = "${CERTS_TO_REVOKE[ii]}" ]; then - VALID=1 - fi - done + if [[ "${STATUS}" == "V" ]]; then + NAME=$(echo -e "${line}" | sed -e 's:.*/CN=::') + CERTS["${i}"]="${NAME}" + ((i++)) + fi + done < "${INDEX}" - if [ "${VALID}" != 1 ]; then - printf "You passed an invalid cert name: '%s'! \n" "${CERTS_TO_REVOKE[ii]}" - exit 1 - fi + for ((ii = 0; ii < ${#CERTS_TO_REVOKE[@]}; ii++)); do + VALID=0 + + for ((x = 1; x <= i; ++x)); do + if [[ "${CERTS[$x]}" == "${CERTS_TO_REVOKE[ii]}" ]]; then + VALID=1 + fi done + + if [[ "${VALID}" != 1 ]]; then + err "You passed an invalid cert name: '${CERTS_TO_REVOKE[ii]}'!" + exit 1 + fi + done fi cd /etc/openvpn/easy-rsa || exit -for (( ii = 0; ii < ${#CERTS_TO_REVOKE[@]}; ii++)); do - if [ -n "$CONFIRM" ]; then - REPLY="y" - else - read -r -p "Do you really want to revoke '${CERTS_TO_REVOKE[ii]}'? [y/N] " - fi - if [[ $REPLY =~ ^[Yy]$ ]]; then - printf "\n::: Revoking certificate '%s'. \n" "${CERTS_TO_REVOKE[ii]}" - ./easyrsa --batch revoke "${CERTS_TO_REVOKE[ii]}" - ./easyrsa gen-crl - printf "\n::: Certificate revoked, and CRL file updated.\n" - printf "::: Removing certs and client configuration for this profile.\n" - rm -rf "pki/reqs/${CERTS_TO_REVOKE[ii]}.req" - rm -rf "pki/private/${CERTS_TO_REVOKE[ii]}.key" - rm -rf "pki/issued/${CERTS_TO_REVOKE[ii]}.crt" - - # Disabling SC2154 $pivpnNET sourced externally - # shellcheck disable=SC2154 - # Grab the client IP address - NET_REDUCED="${pivpnNET::-2}" - STATIC_IP=$(grep -v "^#" /etc/openvpn/ccd/"${CERTS_TO_REVOKE[ii]}" | grep -w ifconfig-push | grep -oE "${NET_REDUCED}\.[0-9]{1,3}") - rm -rf /etc/openvpn/ccd/"${CERTS_TO_REVOKE[ii]}" - - # disablung warning SC2154, $install_home sourced externally - # shellcheck disable=SC2154 - rm -rf "${install_home}/ovpns/${CERTS_TO_REVOKE[ii]}.ovpn" - rm -rf "/etc/openvpn/easy-rsa/pki/${CERTS_TO_REVOKE[ii]}.ovpn" - cp /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn/crl.pem - - # If using Pi-hole, remove the client from the hosts file - if [ -f /etc/pivpn/hosts.openvpn ]; then - sed "\#${STATIC_IP} ${CERTS_TO_REVOKE[ii]}.pivpn#d" -i /etc/pivpn/hosts.openvpn - if killall -SIGHUP pihole-FTL; then - echo "::: Updated hosts file for Pi-hole" - else - echo "::: Failed to reload pihole-FTL configuration" - fi - fi +for ((ii = 0; ii < ${#CERTS_TO_REVOKE[@]}; ii++)); do + if [[ -n "${CONFIRM}" ]]; then + REPLY="y" + else + read -r -p "Do you really want to revoke '${CERTS_TO_REVOKE[ii]}'? [y/N] " + fi + + if [[ "${REPLY}" =~ ^[Yy]$ ]]; then + printf "\n::: Revoking certificate '%s'. \n" "${CERTS_TO_REVOKE[ii]}" + + ./easyrsa --batch revoke "${CERTS_TO_REVOKE[ii]}" + ./easyrsa gen-crl + + printf "\n::: Certificate revoked, and CRL file updated.\n" + printf "::: Removing certs and client configuration for this profile.\n" + + rm -rf "pki/reqs/${CERTS_TO_REVOKE[ii]}.req" + rm -rf "pki/private/${CERTS_TO_REVOKE[ii]}.key" + rm -rf "pki/issued/${CERTS_TO_REVOKE[ii]}.crt" + + # Disabling SC2154 $pivpnNET sourced externally + # shellcheck disable=SC2154 + # Grab the client IP address + NET_REDUCED="${pivpnNET::-2}" + STATIC_IP="$(grep -v "^#" /etc/openvpn/ccd/"${CERTS_TO_REVOKE[ii]}" | + grep -w ifconfig-push | + grep -oE "${NET_REDUCED}\.[0-9]{1,3}")" + rm -rf /etc/openvpn/ccd/"${CERTS_TO_REVOKE[ii]}" + + # disablung warning SC2154, $install_home sourced externally + # shellcheck disable=SC2154 + rm -rf "${install_home}/ovpns/${CERTS_TO_REVOKE[ii]}.ovpn" + rm -rf "/etc/openvpn/easy-rsa/pki/${CERTS_TO_REVOKE[ii]}.ovpn" + cp /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn/crl.pem + + # If using Pi-hole, remove the client from the hosts file + if [[ -f /etc/pivpn/hosts.openvpn ]]; then + sed \ + -e "\#${STATIC_IP} ${CERTS_TO_REVOKE[ii]}.pivpn#d" \ + -i \ + /etc/pivpn/hosts.openvpn + + if killall -SIGHUP pihole-FTL; then + echo "::: Updated hosts file for Pi-hole" + else + err "::: Failed to reload pihole-FTL configuration" + fi fi + fi done + printf "::: Completed!\n" diff --git a/scripts/pivpn b/scripts/pivpn index dd97763..875d7aa 100755 --- a/scripts/pivpn +++ b/scripts/pivpn @@ -3,53 +3,69 @@ CHECK_PKG_INSTALLED='dpkg-query -s' if grep -qsEe "^NAME\=['\"]?Alpine[a-zA-Z ]*['\"]?$" /etc/os-release; then - CHECK_PKG_INSTALLED='apk --no-cache info -e' + CHECK_PKG_INSTALLED='apk --no-cache info -e' fi # Must be root to use this tool -if [ $EUID -ne 0 ]; then - if eval "${CHECK_PKG_INSTALLED} sudo" &> /dev/null; then - export SUDO="sudo" - else - echo "::: Please install sudo or run this as root." - exit 1 - fi +if [[ "${EUID}" -ne 0 ]]; then + if ${CHECK_PKG_INSTALLED} sudo &> /dev/null; then + export SUDO="sudo" + else + err "::: Please install sudo or run this as root." + exit 1 + fi fi scriptDir="/opt/pivpn" -uninstallServer(){ - $SUDO ${scriptDir}/uninstall.sh - exit "$?" +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 } -backup(){ - $SUDO ${scriptDir}/backup.sh - exit "$?" +uninstallServer() { + ${SUDO} "${scriptDir}/uninstall.sh" + exit "${?}" } -showHelp(){ - echo "::: To pass off to the pivpn command for each protocol" - echo ":::" - echo "::: Usage: pivpn wg [option]" - echo "::: Usage: pivpn ovpn [option]" - echo ":::" - echo "::: -h, help Show this help dialog" - echo "::: -u, uninstall Uninstall pivpn from your system!" - echo "::: -bk, backup Backup VPN configs and user profiles" - exit 0 +backup() { + ${SUDO} "${scriptDir}/backup.sh" + exit "${?}" } -if [ $# = 0 ]; then - showHelp +showHelp() { + echo "::: To pass off to the pivpn command for each protocol" + echo ":::" + echo "::: Usage: pivpn wg [option]" + echo "::: Usage: pivpn ovpn [option]" + echo ":::" + echo "::: -h, help Show this help dialog" + echo "::: -u, uninstall Uninstall pivpn from your system!" + echo "::: -bk, backup Backup VPN configs and user profiles" + exit 0 +} + +if [[ "$#" == 0 ]]; then + showHelp fi # Handle redirecting to specific functions based on arguments -case "$1" in - wg ) "${scriptDir}/wireguard/pivpn.sh" "${@:2}";; - ovpn ) "${scriptDir}/openvpn/pivpn.sh" "${@:2}";; -"-h" | "help" ) showHelp;; -"-u" | "uninstall" ) uninstallServer;; -"-bk" | "backup" ) backup ;; -* ) showHelp;; +case "${1}" in + wg) + "${scriptDir}/wireguard/pivpn.sh" "${@:2}" + ;; + ovpn) + "${scriptDir}/openvpn/pivpn.sh" "${@:2}" + ;; + "-h" | "help") + showHelp + ;; + "-u" | "uninstall") + uninstallServer + ;; + "-bk" | "backup") + backup + ;; + *) + showHelp + ;; esac diff --git a/scripts/self_check.sh b/scripts/self_check.sh index bee00b9..153cdab 100755 --- a/scripts/self_check.sh +++ b/scripts/self_check.sh @@ -1,217 +1,344 @@ #!/bin/bash -PLAT=$(grep -sEe '^NAME\=' /etc/os-release | sed -E -e "s/NAME\=[\'\"]?([^ ]*).*/\1/") +PLAT="$(grep -sEe '^NAME\=' /etc/os-release | + sed -E -e "s/NAME\=[\'\"]?([^ ]*).*/\1/")" # dual protocol, VPN type supplied as $1 -VPN=$1 +VPN="${1}" setupVars="/etc/pivpn/${VPN}/setupVars.conf" ERR=0 -if [ ! -f "${setupVars}" ]; then - echo "::: Missing setup vars file!" - exit 1 +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + +if [[ ! -f "${setupVars}" ]]; then + err "::: Missing setup vars file!" + exit 1 fi # SC1090 disabled as setupVars file differs from system to system # shellcheck disable=SC1090 source "${setupVars}" -if [ "$VPN" = "wireguard" ]; then - VPN_SERVICE="wg-quick@wg0" +if [[ "${VPN}" == "wireguard" ]]; then + VPN_PRETTY_NAME="WireGuard" + VPN_SERVICE="wg-quick@wg0" - if [ "${PLAT}" == 'Alpine' ]; then - VPN_SERVICE='wg-quick' - fi - - VPN_PRETTY_NAME="WireGuard" -elif [ "$VPN" = "openvpn" ]; then - VPN_SERVICE="openvpn" - VPN_PRETTY_NAME="OpenVPN" + if [[ "${PLAT}" == 'Alpine' ]]; then + VPN_SERVICE='wg-quick' + fi +elif [[ "${VPN}" == "openvpn" ]]; then + VPN_SERVICE="openvpn" + VPN_PRETTY_NAME="OpenVPN" fi -if [ "$( /dev/null; then + echo ":: [OK] Iptables MASQUERADE rule set" + else + ERR=1 + echo -n ":: [ERR] Iptables MASQUERADE rule is not set, " + echo -n "attempt fix now? [Y/n] " + read -r REPLY - # Disabled SC Warnings for SC2154, values for variables are sourced from setupVars - # shellcheck disable=SC2154 - if iptables -t nat -C POSTROUTING -s "${pivpnNET}/${subnetClass}" -o "${IPv4dev}" -j MASQUERADE -m comment --comment "${VPN}-nat-rule" &> /dev/null; then - echo ":: [OK] Iptables MASQUERADE rule set" - else - ERR=1 - read -r -p ":: [ERR] Iptables MASQUERADE rule is not set, attempt fix now? [Y/n] " REPLY - if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then - iptables -t nat -I POSTROUTING -s "${pivpnNET}/${subnetClass}" -o "${IPv4dev}" -j MASQUERADE -m comment --comment "${VPN}-nat-rule" - iptables-save > /etc/iptables/rules.v4 - echo "Done" - fi - fi + if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then + iptables \ + -t nat \ + -I POSTROUTING \ + -s "${pivpnNET}/${subnetClass}" \ + -o "${IPv4dev}" \ + -j MASQUERADE \ + -m comment \ + --comment "${VPN}-nat-rule" - if [ "$INPUT_CHAIN_EDITED" -eq 1 ]; then + iptables-save > /etc/iptables/rules.v4 + echo "Done" + fi + fi - # Disabled SC Warnings for SC2154, values for variables are sourced from setupVars - # shellcheck disable=SC2154 - if iptables -C INPUT -i "${IPv4dev}" -p "${pivpnPROTO}" --dport "${pivpnPORT}" -j ACCEPT -m comment --comment "${VPN}-input-rule" &> /dev/null; then - echo ":: [OK] Iptables INPUT rule set" - else - ERR=1 - read -r -p ":: [ERR] Iptables INPUT rule is not set, attempt fix now? [Y/n] " REPLY - if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then - iptables -I INPUT 1 -i "${IPv4dev}" -p "${pivpnPROTO}" --dport "${pivpnPORT}" -j ACCEPT -m comment --comment "${VPN}-input-rule" - iptables-save > /etc/iptables/rules.v4 - echo "Done" - fi - fi - fi + if [[ "${INPUT_CHAIN_EDITED}" -eq 1 ]]; then + # Disabled SC Warnings for SC2154, values + # for variables are sourced from setupVars + # shellcheck disable=SC2154 + if iptables \ + -C INPUT \ + -i "${IPv4dev}" \ + -p "${pivpnPROTO}" \ + --dport "${pivpnPORT}" \ + -j ACCEPT \ + -m comment \ + --comment "${VPN}-input-rule" &> /dev/null; then + echo ":: [OK] Iptables INPUT rule set" + else + ERR=1 + read \ + -r \ + -p ":: [ERR] Iptables INPUT rule is not set, attempt fix now? [Y/n] " \ + REPLY - if [ "$FORWARD_CHAIN_EDITED" -eq 1 ]; then + if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then + iptables \ + -I INPUT 1 \ + -i "${IPv4dev}" \ + -p "${pivpnPROTO}" \ + --dport "${pivpnPORT}" \ + -j ACCEPT \ + -m comment \ + --comment "${VPN}-input-rule" - # Disabled SC Warnings for SC2154, values for variables are sourced from setupVars - # shellcheck disable=SC2154 - if iptables -C FORWARD -s "${pivpnNET}/${subnetClass}" -i "${pivpnDEV}" -o "${IPv4dev}" -j ACCEPT -m comment --comment "${VPN}-forward-rule" &> /dev/null; then - echo ":: [OK] Iptables FORWARD rule set" - else - ERR=1 - read -r -p ":: [ERR] Iptables FORWARD rule is not set, attempt fix now? [Y/n] " REPLY - if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then - iptables -I FORWARD 1 -d "${pivpnNET}/${subnetClass}" -i "${IPv4dev}" -o "${pivpnDEV}" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -m comment --comment "${VPN}-forward-rule" - iptables -I FORWARD 2 -s "${pivpnNET}/${subnetClass}" -i "${pivpnDEV}" -o "${IPv4dev}" -j ACCEPT -m comment --comment "${VPN}-forward-rule" - iptables-save > /etc/iptables/rules.v4 - echo "Done" - fi - fi - fi + iptables-save > /etc/iptables/rules.v4 + echo "Done" + fi + fi + fi + if [[ "${FORWARD_CHAIN_EDITED}" -eq 1 ]]; then + # Disabled SC Warnings for SC2154, values + # for variables are sourced from setupVars + # shellcheck disable=SC2154 + if iptables \ + -C FORWARD \ + -s "${pivpnNET}/${subnetClass}" \ + -i "${pivpnDEV}" \ + -o "${IPv4dev}" \ + -j ACCEPT \ + -m comment \ + --comment "${VPN}-forward-rule" &> /dev/null; then + echo ":: [OK] Iptables FORWARD rule set" + else + ERR=1 + echo -n ":: [ERR] Iptables FORWARD rule is not set, " + echo -n "attempt fix now? [Y/n] " + read -r REPLY + + if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then + iptables \ + -I FORWARD 1 \ + -d "${pivpnNET}/${subnetClass}" \ + -i "${IPv4dev}" \ + -o "${pivpnDEV}" \ + -m conntrack \ + --ctstate RELATED,ESTABLISHED \ + -j ACCEPT \ + -m comment \ + --comment "${VPN}-forward-rule" + + iptables \ + -I FORWARD 2 \ + -s "${pivpnNET}/${subnetClass}" \ + -i "${pivpnDEV}" \ + -o "${IPv4dev}" \ + -j ACCEPT \ + -m comment \ + --comment "${VPN}-forward-rule" + + iptables-save > /etc/iptables/rules.v4 + echo "Done" + fi + fi + fi else + if LANG="en_US.UTF-8" ufw status | grep -qw 'active'; then + echo ":: [OK] Ufw is enabled" + else + ERR=1 + echo -n ":: [ERR] Ufw is not enabled, " + echo -n "try to enable now? [Y/n] " + read -r REPLY - if LANG="en_US.UTF-8" ufw status | grep -qw 'active'; then - echo ":: [OK] Ufw is enabled" - else - ERR=1 - read -r -p ":: [ERR] Ufw is not enabled, try to enable now? [Y/n] " REPLY - if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then - ufw enable - fi - fi + if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then + ufw enable + fi + fi - if iptables -t nat -C POSTROUTING -s "${pivpnNET}/${subnetClass}" -o "${IPv4dev}" -j MASQUERADE -m comment --comment "${VPN}-nat-rule" &> /dev/null; then - echo ":: [OK] Iptables MASQUERADE rule set" - else - ERR=1 - read -r -p ":: [ERR] Iptables MASQUERADE rule is not set, attempt fix now? [Y/n] " REPLY - if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then - sed "/delete these required/i *nat\n:POSTROUTING ACCEPT [0:0]\n-I POSTROUTING -s ${pivpnNET}/${subnetClass} -o ${IPv4dev} -j MASQUERADE -m comment --comment ${VPN}-nat-rule\nCOMMIT\n" -i /etc/ufw/before.rules - ufw reload - echo "Done" - fi - fi + if iptables \ + -t nat \ + -C POSTROUTING \ + -s "${pivpnNET}/${subnetClass}" \ + -o "${IPv4dev}" \ + -j MASQUERADE \ + -m comment \ + --comment "${VPN}-nat-rule" &> /dev/null; then + echo ":: [OK] Iptables MASQUERADE rule set" + else + ERR=1 + echo -n ":: [ERR] Iptables MASQUERADE rule is not set, " + echo -n "attempt fix now? [Y/n] " + read -r REPLY - if iptables -C ufw-user-input -p "${pivpnPROTO}" --dport "${pivpnPORT}" -j ACCEPT &> /dev/null; then - echo ":: [OK] Ufw input rule set" - else - ERR=1 - read -r -p ":: [ERR] Ufw input rule is not set, attempt fix now? [Y/n] " REPLY - if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then - ufw insert 1 allow "${pivpnPORT}"/"${pivpnPROTO}" - ufw reload - echo "Done" - fi - fi + if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then + sed_pattern='/delete these required/i' + sed_pattern="${sed_pattern} *nat\n:POSTROUTING ACCEPT [0:0]\n" + sed_pattern="${sed_pattern} -I POSTROUTING" + sed_pattern="${sed_pattern} -s ${pivpnNET}/${subnetClass}" + sed_pattern="${sed_pattern} -o ${IPv4dev}" + sed_pattern="${sed_pattern} -j MASQUERADE" + sed_pattern="${sed_pattern} -m comment" + sed_pattern="${sed_pattern} --comment ${VPN}-nat-rule\n" + sed_pattern="${sed_pattern}COMMIT\n" - if iptables -C ufw-user-forward -i "${pivpnDEV}" -o "${IPv4dev}" -s "${pivpnNET}/${subnetClass}" -j ACCEPT &> /dev/null; then - echo ":: [OK] Ufw forwarding rule set" - else - ERR=1 - read -r -p ":: [ERR] Ufw forwarding rule is not set, attempt fix now? [Y/n] " REPLY - if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then - ufw route insert 1 allow in on "${pivpnDEV}" from "${pivpnNET}/${subnetClass}" out on "${IPv4dev}" to any - ufw reload - echo "Done" - fi - fi + sed "${sed_pattern}" -i /etc/ufw/before.rules + ufw reload + echo "Done" + unset sed_pattern + fi + fi + if iptables \ + -C ufw-user-input \ + -p "${pivpnPROTO}" \ + --dport "${pivpnPORT}" \ + -j ACCEPT &> /dev/null; then + echo ":: [OK] Ufw input rule set" + else + ERR=1 + read \ + -r \ + -p ":: [ERR] Ufw input rule is not set, attempt fix now? [Y/n] " \ + REPLY + + if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then + ufw insert 1 allow "${pivpnPORT}"/"${pivpnPROTO}" + ufw reload + echo "Done" + fi + fi + + if iptables \ + -C ufw-user-forward \ + -i "${pivpnDEV}" \ + -o "${IPv4dev}" \ + -s "${pivpnNET}/${subnetClass}" \ + -j ACCEPT &> /dev/null; then + echo ":: [OK] Ufw forwarding rule set" + else + ERR=1 + read \ + -r \ + -p ":: [ERR] Ufw forwarding rule is not set, attempt fix now? [Y/n] " \ + REPLY + + if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then + ufw route insert 1 allow in on "${pivpnDEV}" \ + from "${pivpnNET}/${subnetClass}" out on "${IPv4dev}" to any + ufw reload + echo "Done" + fi + fi fi -if [ "${PLAT}" == 'Alpine' ]; then - if [ "$(rc-service "${VPN_SERVICE}" status | sed -E -e 's/.*status\: (.*)/\1/')" == 'started' ]; then - echo ":: [OK] ${VPN_PRETTY_NAME} is running" - else - ERR=1 - read -r -p ":: [ERR] ${VPN_PRETTY_NAME} is not running, try to start now? [Y/n] " REPLY +if [[ "${PLAT}" == 'Alpine' ]]; then + if [[ "$(rc-service "${VPN_SERVICE}" status | + sed -E -e 's/.*status\: (.*)/\1/')" == 'started' ]]; then + echo ":: [OK] ${VPN_PRETTY_NAME} is running" + else + ERR=1 + echo -n ":: [ERR] ${VPN_PRETTY_NAME} is not running, " + echo -n "try to start now? [Y/n] " + read -r REPLY - if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then - rc-service -s "${VPN_SERVICE}" restart - rc-service -N "${VPN_SERVICE}" start + if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then + rc-service -s "${VPN_SERVICE}" restart + rc-service -N "${VPN_SERVICE}" start + echo "Done" + fi + fi - echo "Done" - fi - fi + if rc-update show default | + grep -sEe "\s*${VPN_SERVICE} .*" &> /dev/null; then + echo -n ":: [OK] ${VPN_PRETTY_NAME} is enabled " + echo "(it will automatically start on reboot)" + else + ERR=1 + echo -n ":: [ERR] ${VPN_PRETTY_NAME} is not enabled, " + echo -n "try to enable now? [Y/n] " + read -r REPLY - if rc-update show default | grep -sEe "\s*${VPN_SERVICE} .*" &> /dev/null; then - echo ":: [OK] ${VPN_PRETTY_NAME} is enabled (it will automatically start on reboot)" - else - ERR=1 - read -r -p ":: [ERR] ${VPN_PRETTY_NAME} is not enabled, try to enable now? [Y/n] " REPLY - - if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then - rc-update add "${VPN_SERVICE}" default - - echo "Done" - fi - fi + if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then + rc-update add "${VPN_SERVICE}" default + echo "Done" + fi + fi else - if systemctl is-active -q "${VPN_SERVICE}"; then - echo ":: [OK] ${VPN_PRETTY_NAME} is running" - else - ERR=1 - read -r -p ":: [ERR] ${VPN_PRETTY_NAME} is not running, try to start now? [Y/n] " REPLY - if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then - systemctl start "${VPN_SERVICE}" - echo "Done" - fi - fi + if systemctl is-active -q "${VPN_SERVICE}"; then + echo ":: [OK] ${VPN_PRETTY_NAME} is running" + else + ERR=1 + echo -n ":: [ERR] ${VPN_PRETTY_NAME} is not running, " + echo -n "try to start now? [Y/n] " + read -r REPLY - if systemctl is-enabled -q "${VPN_SERVICE}"; then - echo ":: [OK] ${VPN_PRETTY_NAME} is enabled (it will automatically start on reboot)" - else - ERR=1 - read -r -p ":: [ERR] ${VPN_PRETTY_NAME} is not enabled, try to enable now? [Y/n] " REPLY - if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then - systemctl enable "${VPN_SERVICE}" - echo "Done" - fi - fi + if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then + systemctl start "${VPN_SERVICE}" + echo "Done" + fi + fi + + if systemctl is-enabled -q "${VPN_SERVICE}"; then + echo ":: [OK] ${VPN_PRETTY_NAME} is enabled " + echo "(it will automatically start on reboot)" + else + ERR=1 + echo -n ":: [ERR] ${VPN_PRETTY_NAME} is not enabled, " + echo -n "try to enable now? [Y/n] " + read -r REPLY + + if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then + systemctl enable "${VPN_SERVICE}" + echo "Done" + fi + fi fi # grep -w (whole word) is used so port 11940 won't match when looking for 1194 if netstat -antu | grep -wqE "${pivpnPROTO}.*${pivpnPORT}"; then - echo ":: [OK] ${VPN_PRETTY_NAME} is listening on port ${pivpnPORT}/${pivpnPROTO}" + echo -n ":: [OK] ${VPN_PRETTY_NAME} is listening " + echo "on port ${pivpnPORT}/${pivpnPROTO}" else - ERR=1 - read -r -p ":: [ERR] ${VPN_PRETTY_NAME} is not listening, try to restart now? [Y/n] " REPLY + ERR=1 + echo -n ":: [ERR] ${VPN_PRETTY_NAME} is not listening, " + echo -n "try to restart now? [Y/n] " + read -r REPLY - if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then - if [ "${PLAT}" == 'Alpine' ]; then - rc-service -s "${VPN_SERVICE}" restart - rc-service -N "${VPN_SERVICE}" start - else - systemctl restart "${VPN_SERVICE}" - fi + if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then + if [[ "${PLAT}" == 'Alpine' ]]; then + rc-service -s "${VPN_SERVICE}" restart + rc-service -N "${VPN_SERVICE}" start + else + systemctl restart "${VPN_SERVICE}" + fi - echo "Done" - fi + echo "Done" + fi fi -if [ "$ERR" -eq 1 ]; then - echo -e "[INFO] Run \e[1mpivpn -d\e[0m again to see if we detect issues" +if [[ "${ERR}" -eq 1 ]]; then + echo -e "[INFO] Run \e[1mpivpn -d\e[0m again to see if we detect issues" fi diff --git a/scripts/uninstall.sh b/scripts/uninstall.sh index 9c4c4d8..1a1ba91 100755 --- a/scripts/uninstall.sh +++ b/scripts/uninstall.sh @@ -1,20 +1,22 @@ -#!/usr/bin/env bash +#!/bin/bash # PiVPN: Uninstall Script -### FIXME: global: config storage, refactor all scripts to adhere to the storage -### FIXME: use variables where appropriate, reduce magic numbers by 99.9%, at least. +### FIXME: +### global: config storage, refactor all scripts to adhere to the storage +### FIXME: +### use variables where appropriate, reduce magic numbers by 99.9%, at least. # Find the rows and columns. Will default to 80x24 if it can not be detected. -screen_size=$(stty size 2>/dev/null || echo 24 80) -rows=$(echo "$screen_size" | awk '{print $1}') -columns=$(echo "$screen_size" | awk '{print $2}') +screen_size="$(stty size 2> /dev/null || echo 24 80)" +rows="$(echo "${screen_size}" | awk '{print $1}')" +columns="$(echo "${screen_size}" | awk '{print $2}')" # Divide by two so the dialogs take up half of the screen, which looks nice. -r=$(( rows / 2 )) -c=$(( columns / 2 )) +r=$((rows / 2)) +c=$((columns / 2)) # Unless the screen is tiny -r=$(( r < 20 ? 20 : r )) -c=$(( c < 70 ? 70 : c )) +r=$((r < 20 ? 20 : r)) +c=$((c < 70 ? 70 : c)) PKG_MANAGER="apt-get" PKG_REMOVE="${PKG_MANAGER} -y remove --purge" @@ -24,295 +26,393 @@ setupConfigDir="/etc/pivpn" pivpnFilesDir="/usr/local/src/pivpn" pivpnScriptDir="/opt/pivpn" -PLAT=$(grep -sEe '^NAME\=' /etc/os-release | sed -E -e "s/NAME\=[\'\"]?([^ ]*).*/\1/") +PLAT="$(grep -sEe '^NAME\=' /etc/os-release | + sed -E -e "s/NAME\=[\'\"]?([^ ]*).*/\1/")" -if [ "${PLAT}" == 'Alpine' ]; then - PKG_MANAGER='apk' - PKG_REMOVE="${PKG_MANAGER} --no-cache --purge del -r" +if [[ "${PLAT}" == 'Alpine' ]]; then + PKG_MANAGER='apk' + PKG_REMOVE="${PKG_MANAGER} --no-cache --purge del -r" fi UPDATE_PKG_CACHE="${PKG_MANAGER} update" -if [ -r "${setupConfigDir}/wireguard/${setupVarsFile}" ] && [ -r "${setupConfigDir}/openvpn/${setupVarsFile}" ]; then - vpnStillExists=1 +if [[ -r "${setupConfigDir}/wireguard/${setupVarsFile}" ]] && + [[ -r "${setupConfigDir}/openvpn/${setupVarsFile}" ]]; then + vpnStillExists=1 - # Two protocols have been installed, check if the script has passed - # an argument, otherwise ask the user which one he wants to remove - if [ $# -ge 1 ]; then - VPN="$1" - echo "::: Uninstalling VPN: $VPN" - else - chooseVPNCmd=(whiptail --backtitle "Setup PiVPN" --title "Uninstall" --separate-output --radiolist "Both OpenVPN and WireGuard are installed, choose a VPN to uninstall (press space to select):" "${r}" "${c}" 2) - VPNChooseOptions=(WireGuard "" on - OpenVPN "" off) + # Two protocols have been installed, check if the script has passed + # an argument, otherwise ask the user which one he wants to remove + if [[ "$#" -ge 1 ]]; then + VPN="${1}" + echo "::: Uninstalling VPN: ${VPN}" + else + chooseVPNCmd=(whiptail + --backtitle "Setup PiVPN" + --title "Uninstall" + --separate-output + --radiolist "Both OpenVPN and WireGuard are installed, \ +choose a VPN to uninstall (press space to select):" + "${r}" "${c}" 2) + VPNChooseOptions=(WireGuard "" on + OpenVPN "" off) - if VPN=$("${chooseVPNCmd[@]}" "${VPNChooseOptions[@]}" 2>&1 >/dev/tty) ; then - echo "::: Uninstalling VPN: $VPN" - VPN="${VPN,,}" - else - echo "::: Cancel selected, exiting...." - exit 1 - fi - fi + if VPN="$("${chooseVPNCmd[@]}" "${VPNChooseOptions[@]}" 2>&1 \ + > /dev/tty)"; then + echo "::: Uninstalling VPN: ${VPN}" + VPN="${VPN,,}" + else + err "::: Cancel selected, exiting...." + exit 1 + fi + fi - setupVars="${setupConfigDir}/${VPN}/${setupVarsFile}" + setupVars="${setupConfigDir}/${VPN}/${setupVarsFile}" else - vpnStillExists=0 + vpnStillExists=0 - if [ -r "${setupConfigDir}/wireguard/${setupVarsFile}" ]; then - setupVars="${setupConfigDir}/wireguard/${setupVarsFile}" - elif [ -r "${setupConfigDir}/openvpn/${setupVarsFile}" ]; then - setupVars="${setupConfigDir}/openvpn/${setupVarsFile}" - fi + if [[ -r "${setupConfigDir}/wireguard/${setupVarsFile}" ]]; then + setupVars="${setupConfigDir}/wireguard/${setupVarsFile}" + elif [[ -r "${setupConfigDir}/openvpn/${setupVarsFile}" ]]; then + setupVars="${setupConfigDir}/openvpn/${setupVarsFile}" + fi fi -if [ ! -f "${setupVars}" ]; then - echo "::: Missing setup vars file!" - exit 1 +if [[ ! -f "${setupVars}" ]]; then + err "::: Missing setup vars file!" + exit 1 fi # shellcheck disable=SC1090 source "${setupVars}" +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + ### FIXME: introduce global lib -spinner(){ - local pid=$1 - local delay=0.50 - local spinstr='/-\|' - while ps a | awk '{print $1}' | grep -q "$pid"; do - local temp=${spinstr#?} - printf " [%c] " "$spinstr" - local spinstr=$temp${spinstr%"$temp"} - sleep $delay - printf "\\b\\b\\b\\b\\b\\b" - done - printf " \\b\\b\\b\\b" +spinner() { + local pid="${1}" + local delay=0.50 + local spinstr='/-\|' + + while ps a | awk '{print $1}' | grep -q "${pid}"; do + local temp="${spinstr#?}" + printf " [%c] " "${spinstr}" + local spinstr="${temp}${spinstr%"$temp"}" + sleep "${delay}" + printf "\\b\\b\\b\\b\\b\\b" + done + + printf " \\b\\b\\b\\b" } -removeAll(){ - # Stopping and disabling services - echo "::: Stopping and disabling services..." +removeAll() { + # Stopping and disabling services + echo "::: Stopping and disabling services..." - if [ "${PLAT}" == 'Alpine' ]; then - if [ "${VPN}" = "wireguard" ]; then - rc-service wg-quick stop - rc-update del wg-quick default &> /dev/null - elif [ "${VPN}" = "openvpn" ]; then - rc-service openvpn stop - rc-update del openvpn default &> /dev/null - fi - else - if [ "${VPN}" = "wireguard" ]; then - systemctl stop wg-quick@wg0 - systemctl disable wg-quick@wg0 &> /dev/null - elif [ "${VPN}" = "openvpn" ]; then - systemctl stop openvpn - systemctl disable openvpn &> /dev/null - fi - fi + if [[ "${PLAT}" == 'Alpine' ]]; then + if [[ "${VPN}" == "wireguard" ]]; then + rc-service wg-quick stop + rc-update del wg-quick default &> /dev/null + elif [[ "${VPN}" == "openvpn" ]]; then + rc-service openvpn stop + rc-update del openvpn default &> /dev/null + fi + else + if [[ "${VPN}" == "wireguard" ]]; then + systemctl stop wg-quick@wg0 + systemctl disable wg-quick@wg0 &> /dev/null + elif [[ "${VPN}" == "openvpn" ]]; then + systemctl stop openvpn + systemctl disable openvpn &> /dev/null + fi + fi - # Removing firewall rules. - echo "::: Removing firewall rules..." - - if [ "$USING_UFW" -eq 1 ]; then + # Removing firewall rules. + echo "::: Removing firewall rules..." + if [[ "${USING_UFW}" -eq 1 ]]; then ### Ignoring SC2154, value sourced from setupVars file - # shellcheck disable=SC2154 - ufw delete allow "${pivpnPORT}"/"${pivpnPROTO}" > /dev/null + # shellcheck disable=SC2154 + ufw delete allow "${pivpnPORT}/${pivpnPROTO}" > /dev/null ### Ignoring SC2154, value sourced from setupVars file - # shellcheck disable=SC2154 - ufw route delete allow in on "${pivpnDEV}" from "${pivpnNET}/${subnetClass}" out on "${IPv4dev}" to any > /dev/null - ufw delete allow in on "${pivpnDEV}" to any port 53 from "${pivpnNET}/${subnetClass}" >/dev/null - sed "/-I POSTROUTING -s ${pivpnNET}\\/${subnetClass} -o ${IPv4dev} -j MASQUERADE -m comment --comment ${VPN}-nat-rule/d" -i /etc/ufw/before.rules - iptables -t nat -D POSTROUTING -s "${pivpnNET}/${subnetClass}" -o "${IPv4dev}" -j MASQUERADE -m comment --comment "${VPN}-nat-rule" - ufw reload &> /dev/null + # shellcheck disable=SC2154 + ufw route delete allow in on "${pivpnDEV}" \ + from "${pivpnNET}/${subnetClass}" out on "${IPv4dev}" to any > /dev/null + ufw delete allow in on "${pivpnDEV}" to any port 53 \ + from "${pivpnNET}/${subnetClass}" > /dev/null - elif [ "$USING_UFW" -eq 0 ]; then + sed_pattern='/-I POSTROUTING' + sed_pattern="${sed_pattern} -s ${pivpnNET}\\/${subnetClass}" + sed_pattern="${sed_pattern} -o ${IPv4dev}" + sed_pattern="${sed_pattern} -j MASQUERADE" + sed_pattern="${sed_pattern} -m comment" + sed_pattern="${sed_pattern} --comment ${VPN}-nat-rule/d" + sed "${sed_pattern}" -i /etc/ufw/before.rules + unset sed_pattern - if [ "$INPUT_CHAIN_EDITED" -eq 1 ]; then - iptables -D INPUT -i "${IPv4dev}" -p "${pivpnPROTO}" --dport "${pivpnPORT}" -j ACCEPT -m comment --comment "${VPN}-input-rule" - fi + iptables \ + -t nat \ + -D POSTROUTING \ + -s "${pivpnNET}/${subnetClass}" \ + -o "${IPv4dev}" \ + -j MASQUERADE \ + -m comment \ + --comment "${VPN}-nat-rule" - if [ "$FORWARD_CHAIN_EDITED" -eq 1 ]; then - iptables -D FORWARD -d "${pivpnNET}/${subnetClass}" -i "${IPv4dev}" -o "${pivpnDEV}" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -m comment --comment "${VPN}-forward-rule" - iptables -D FORWARD -s "${pivpnNET}/${subnetClass}" -i "${pivpnDEV}" -o "${IPv4dev}" -j ACCEPT -m comment --comment "${VPN}-forward-rule" - fi + ufw reload &> /dev/null + elif [[ "${USING_UFW}" -eq 0 ]]; then + if [[ "${INPUT_CHAIN_EDITED}" -eq 1 ]]; then + iptables \ + -D INPUT \ + -i "${IPv4dev}" \ + -p "${pivpnPROTO}" \ + --dport "${pivpnPORT}" \ + -j ACCEPT \ + -m comment \ + --comment "${VPN}-input-rule" + fi - iptables -t nat -D POSTROUTING -s "${pivpnNET}/${subnetClass}" -o "${IPv4dev}" -j MASQUERADE -m comment --comment "${VPN}-nat-rule" - iptables-save > /etc/iptables/rules.v4 + if [[ "${FORWARD_CHAIN_EDITED}" -eq 1 ]]; then + iptables \ + -D FORWARD \ + -d "${pivpnNET}/${subnetClass}" \ + -i "${IPv4dev}" \ + -o "${pivpnDEV}" \ + -m conntrack \ + --ctstate RELATED,ESTABLISHED \ + -j ACCEPT \ + -m comment \ + --comment "${VPN}-forward-rule" - fi + iptables \ + -D FORWARD \ + -s "${pivpnNET}/${subnetClass}" \ + -i "${pivpnDEV}" \ + -o "${IPv4dev}" \ + -j ACCEPT \ + -m comment \ + --comment "${VPN}-forward-rule" + fi - # Disable IPv4 forwarding - if [ "${vpnStillExists}" -eq 0 ]; then - sed -i '/net.ipv4.ip_forward=1/c\#net.ipv4.ip_forward=1' /etc/sysctl.conf - sysctl -p - fi + iptables \ + -t nat \ + -D POSTROUTING \ + -s "${pivpnNET}/${subnetClass}" \ + -o "${IPv4dev}" \ + -j MASQUERADE \ + -m comment \ + --comment "${VPN}-nat-rule" - # Purge dependencies - echo "::: Purge dependencies..." + iptables-save > /etc/iptables/rules.v4 + fi - for i in "${INSTALLED_PACKAGES[@]}"; do - while true; do - read -rp "::: Do you wish to remove $i from your system? [Y/n]: " yn - case $yn in - [Yy]* ) - if [ "${PLAT}" == 'Alpine' ]; then - if [ "${i}" == 'openvpn' ]; then - deluser openvpn - rm -f /etc/rsyslog.d/30-openvpn.conf /etc/logrotate.d/openvpn - fi - else - if [ "${i}" == "wireguard-tools" ]; then - # The bullseye repo may not exist if wireguard was available at the - # time of installation. - if [ -f /etc/apt/sources.list.d/pivpn-bullseye-repo.list ]; then - echo "::: Removing Debian Bullseye repo..." + # Disable IPv4 forwarding + if [[ "${vpnStillExists}" -eq 0 ]]; then + sed -i '/net.ipv4.ip_forward=1/c\#net.ipv4.ip_forward=1' /etc/sysctl.conf + sysctl -p + fi - rm -f /etc/apt/sources.list.d/pivpn-bullseye-repo.list - rm -f /etc/apt/preferences.d/pivpn-limit-bullseye + # Purge dependencies + echo "::: Purge dependencies..." - echo "::: Updating package cache..." + for i in "${INSTALLED_PACKAGES[@]}"; do + while true; do + read -rp "::: Do you wish to remove ${i} from your system? [Y/n]: " yn - ${UPDATE_PKG_CACHE} &> /dev/null & - spinner $! - fi + case "${yn}" in + [Yy]*) + if [[ "${PLAT}" == 'Alpine' ]]; then + if [[ "${i}" == 'openvpn' ]]; then + deluser openvpn + rm -f /etc/rsyslog.d/30-openvpn.conf /etc/logrotate.d/openvpn + fi + else + if [[ "${i}" == "wireguard-tools" ]]; then + # The bullseye repo may not exist if wireguard was available at + # the time of installation. + tmp_path='/etc/apt/sources.list.d/pivpn-bullseye-repo.list' - if [ -f /etc/systemd/system/wg-quick@.service.d/override.conf ]; then - rm -f /etc/systemd/system/wg-quick@.service.d/override.conf - fi - elif [ "${i}" = "unattended-upgrades" ]; then - rm -rf /var/log/unattended-upgrades /etc/apt/apt.conf.d/*periodic - rm -rf /etc/apt/apt.conf.d/*unattended-upgrades - elif [ "${i}" = "openvpn" ]; then - if [ -f /etc/apt/sources.list.d/pivpn-openvpn-repo.list ]; then - echo "::: Removing OpenVPN software repo..." + if [[ -f "${tmp_path}" ]]; then + echo "::: Removing Debian Bullseye repo..." - rm -f /etc/apt/sources.list.d/pivpn-openvpn-repo.list + rm -f "${tmp_path}" + rm -f /etc/apt/preferences.d/pivpn-limit-bullseye - echo "::: Updating package cache..." + echo "::: Updating package cache..." - ${UPDATE_PKG_CACHE} &> /dev/null & - spinner $! - fi + ${UPDATE_PKG_CACHE} &> /dev/null & + spinner "$!" + fi - deluser openvpn - rm -f /etc/rsyslog.d/30-openvpn.conf /etc/logrotate.d/openvpn - fi - fi + tmp_path='/etc/systemd/system/wg-quick@.service.d/override.conf' - printf ":::\\tRemoving %s..." "$i" + if [[ -f "${tmp_path}" ]]; then + rm -f "${tmp_path}" + fi - "${PKG_REMOVE}" "$i" &> /dev/null & - spinner $! + unset tmp_path + elif [[ "${i}" == "unattended-upgrades" ]]; then + rm -rf /var/log/unattended-upgrades /etc/apt/apt.conf.d/*periodic + rm -rf /etc/apt/apt.conf.d/*unattended-upgrades + elif [[ "${i}" == "openvpn" ]]; then + if [[ -f /etc/apt/sources.list.d/pivpn-openvpn-repo.list ]]; then + echo "::: Removing OpenVPN software repo..." - printf "done!\\n"; - break - ;; - [Nn]* ) - printf ":::\\tSkipping %s\\n" "$i"; - break - ;; - * ) - printf "::: You must answer yes or no!\\n" - ;; - esac - done - done + rm -f /etc/apt/sources.list.d/pivpn-openvpn-repo.list - if [ "${PLAT}" != 'Alpine' ]; then - # Take care of any additional package cleaning - printf "::: Auto removing remaining dependencies..." + echo "::: Updating package cache..." - "${PKG_MANAGER}" -y autoremove &> /dev/null & - spinner $! + ${UPDATE_PKG_CACHE} &> /dev/null & + spinner "$!" + fi - printf "done!\\n"; - printf "::: Auto cleaning remaining dependencies..." + deluser openvpn + rm -f /etc/rsyslog.d/30-openvpn.conf /etc/logrotate.d/openvpn + fi + fi - "${PKG_MANAGER}" -y autoclean &> /dev/null & - spinner $! + printf ":::\\tRemoving %s..." "${i}" - printf "done!\\n"; - fi + ${PKG_REMOVE} "${i}" &> /dev/null & + spinner "$!" - if [ -f "$dnsmasqConfig" ]; then - rm -f "$dnsmasqConfig" - pihole restartdns - fi + printf "done!\\n" + break + ;; + [Nn]*) + printf ":::\\tSkipping %s\\n" "${i}" + break + ;; + *) + err "::: You must answer yes or no!" + ;; + esac + done + done - echo ":::" - echo "::: Removing VPN configuration files..." + if [[ "${PLAT}" != 'Alpine' ]]; then + # Take care of any additional package cleaning + printf "::: Auto removing remaining dependencies..." - if [ "$VPN" = "wireguard" ]; then - rm -f /etc/wireguard/wg0.conf - rm -rf /etc/wireguard/configs - rm -rf /etc/wireguard/keys + "${PKG_MANAGER}" -y autoremove &> /dev/null & + spinner "$!" + + printf "done!\\n" + printf "::: Auto cleaning remaining dependencies..." + + "${PKG_MANAGER}" -y autoclean &> /dev/null & + spinner "$!" + + printf "done!\\n" + fi + + if [[ -f "${dnsmasqConfig}" ]]; then + rm -f "${dnsmasqConfig}" + pihole restartdns + fi + + echo ":::" + echo "::: Removing VPN configuration files..." + + if [[ "${VPN}" == "wireguard" ]]; then + rm -f /etc/wireguard/wg0.conf + rm -rf /etc/wireguard/configs + rm -rf /etc/wireguard/keys ### Ignoring SC2154, value sourced from setupVars file - # shellcheck disable=SC2154 - rm -rf "$install_home/configs" - elif [ "$VPN" = "openvpn" ]; then - rm -rf /var/log/*openvpn* - rm -f /etc/openvpn/server.conf - rm -f /etc/openvpn/crl.pem - rm -rf /etc/openvpn/easy-rsa - rm -rf /etc/openvpn/ccd - rm -rf "$install_home/ovpns" - fi + # shellcheck disable=SC2154 + rm -rf "${install_home}/configs" + elif [[ "${VPN}" == "openvpn" ]]; then + rm -rf /var/log/*openvpn* + rm -f /etc/openvpn/server.conf + rm -f /etc/openvpn/crl.pem + rm -rf /etc/openvpn/easy-rsa + rm -rf /etc/openvpn/ccd + rm -rf "${install_home}/ovpns" + fi - if [ "${vpnStillExists}" -eq 0 ]; then - echo ":::" - echo "::: Removing pivpn system files..." - rm -rf "${setupConfigDir}" - rm -rf "${pivpnFilesDir}" - rm -f /var/log/*pivpn* - rm -f /etc/bash_completion.d/pivpn - unlink "${pivpnScriptDir}" - unlink /usr/local/bin/pivpn - else - if [[ ${VPN} == 'wireguard' ]]; then - othervpn='openvpn' - else - othervpn='wireguard' - fi + if [[ "${vpnStillExists}" -eq 0 ]]; then + echo ":::" + echo "::: Removing pivpn system files..." - echo ":::" - echo "::: Other VPN ${othervpn} still present, so not" - echo "::: removing pivpn system files" - rm -f "${setupConfigDir}/${VPN}/${setupVarsFile}" + rm -rf "${setupConfigDir}" + rm -rf "${pivpnFilesDir}" + rm -f /var/log/*pivpn* + rm -f /etc/bash_completion.d/pivpn - # Restore single pivpn script and bash completion for the remaining VPN - $SUDO unlink /usr/local/bin/pivpn - $SUDO ln -s -T "${pivpnFilesDir}/scripts/${othervpn}/pivpn.sh" /usr/local/bin/pivpn - $SUDO ln -s -T "${pivpnFilesDir}/scripts/${othervpn}/bash-completion" /etc/bash_completion.d/pivpn - # shellcheck disable=SC1091 - . /etc/bash_completion.d/pivpn - fi + unlink "${pivpnScriptDir}" + unlink /usr/local/bin/pivpn + else + if [[ "${VPN}" == 'wireguard' ]]; then + othervpn='openvpn' + else + othervpn='wireguard' + fi - echo ":::" - printf "::: Finished removing PiVPN from your system.\\n" - printf "::: Reinstall by simply running\\n:::\\n:::\\tcurl -L https://install.pivpn.io | bash\\n:::\\n::: at any time!\\n:::\\n" + echo ":::" + echo "::: Other VPN ${othervpn} still present, so not" + echo "::: removing pivpn system files" + rm -f "${setupConfigDir}/${VPN}/${setupVarsFile}" + + # Restore single pivpn script and bash completion for the remaining VPN + ${SUDO} unlink /usr/local/bin/pivpn + + ${SUDO} ln \ + -s \ + -T "${pivpnFilesDir}/scripts/${othervpn}/pivpn.sh" \ + /usr/local/bin/pivpn + + ${SUDO} ln \ + -s \ + -T "${pivpnFilesDir}/scripts/${othervpn}/bash-completion" \ + /etc/bash_completion.d/pivpn + + # shellcheck disable=SC1091 + . /etc/bash_completion.d/pivpn + fi + + echo ":::" + printf "::: Finished removing PiVPN from your system.\\n" + printf "::: Reinstall by simply running\\n:::\\n:::\\t" + printf "curl -L https://install.pivpn.io | " + printf "bash\\n:::\\n::: at any time!\\n:::\\n" } -askreboot(){ - printf "It is \\e[1mstrongly\\e[0m recommended to reboot after un-installation.\\n" - read -p "Would you like to reboot now? [y/n]: " -n 1 -r - echo - if [[ ${REPLY} =~ ^[Yy]$ ]]; then - printf "\\nRebooting system...\\n" - sleep 3 - reboot - fi +askreboot() { + printf "It is \\e[1mstrongly\\e[0m recommended to reboot " + printf "after un-installation.\\n" + + read -p "Would you like to reboot now? [y/n]: " -n 1 -r + + echo + + if [[ "${REPLY}" =~ ^[Yy]$ ]]; then + printf "\\nRebooting system...\\n" + sleep 3 + reboot + fi } ######### SCRIPT ########### -echo "::: Preparing to remove packages, be sure that each may be safely removed depending on your operating system." +echo -n "::: Preparing to remove packages, be sure that each may be safely " +echo "removed depending on your operating system." echo "::: (SAFE TO REMOVE ALL ON RASPBIAN)" -while true; do - read -rp "::: Do you wish to completely remove PiVPN configuration and installed packages from your system? (You will be prompted for each package) [y/n]: " yn - case $yn in - [Yy]* ) removeAll; askreboot; break;; - [Nn]* ) printf "::: Not removing anything, exiting...\\n"; break;; - esac +while true; do + echo -n "::: Do you wish to completely remove PiVPN configuration and " + echo -n "installed packages from your system? " + echo -n "(You will be prompted for each package) [y/n]: " + read -r yn + + case "${yn}" in + [Yy]*) + removeAll + askreboot + break + ;; + [Nn]*) + err "::: Not removing anything, exiting..." + break + ;; + esac done diff --git a/scripts/update.sh b/scripts/update.sh index f296e24..7c922d4 100755 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -1,130 +1,145 @@ #!/bin/bash -###Updates pivpn scripts (Not PiVPN) -###Main Vars +### Updates pivpn scripts (Not PiVPN) +### Main Vars pivpnrepo="https://github.com/pivpn/pivpn.git" pivpnlocalpath="/etc/.pivpn" pivpnscripts="/opt/pivpn/" bashcompletiondir="/etc/bash_completion.d/" # Find the rows and columns. Will default to 80x24 if it can not be detected. -screen_size=$(stty size 2>/dev/null || echo 24 80) -rows=$(echo "$screen_size" | awk '{print $1}') -columns=$(echo "$screen_size" | awk '{print $2}') +screen_size="$(stty size 2> /dev/null || echo 24 80)" +rows="$(echo "${screen_size}" | awk '{print $1}')" +columns="$(echo "${screen_size}" | awk '{print $2}')" # Divide by two so the dialogs take up half of the screen, which looks nice. -r=$(( rows / 2 )) -c=$(( columns / 2 )) +r=$((rows / 2)) +c=$((columns / 2)) # Unless the screen is tiny -r=$(( r < 20 ? 20 : r )) -c=$(( c < 70 ? 70 : c )) +r=$((r < 20 ? 20 : r)) +c=$((c < 70 ? 70 : c)) -echo "::: The updating functionality for PiVPN scripts is temporarily disabled" -echo "::: To keep the VPN (and the system) up to date, use 'apt update' and 'apt upgrade'" +# TODO: Delete this section when the updating functionality will be re-enabled +### +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + +err "::: The updating functionality for PiVPN scripts is temporarily disabled" +err "::: To keep the VPN (and the system) up to date, use:" +err " apt update; apt upgrade" exit 0 +### - chooseVPNCmd=(whiptail --backtitle "Setup PiVPN" --title "Installation mode" --separate-output --radiolist "Choose a VPN to update (press space to select):" "${r}" "${c}" 2) - VPNChooseOptions=(WireGuard "" on - OpenVPN "" off) +chooseVPNCmd=(whiptail + --backtitle "Setup PiVPN" + --title "Installation mode" + --separate-output + --radiolist "Choose a VPN to update (press space to select):" + "${r}" "${c}" 2) +VPNChooseOptions=(WireGuard "" on + OpenVPN "" off) - if VPN=$("${chooseVPNCmd[@]}" "${VPNChooseOptions[@]}" 2>&1 >/dev/tty) ; then - echo "::: Using VPN: $VPN" - VPN="${VPN,,}" - else - echo "::: Cancel selected, exiting...." - exit 1 - fi +if VPN="$("${chooseVPNCmd[@]}" "${VPNChooseOptions[@]}" 2>&1 > /dev/tty)"; then + echo "::: Using VPN: ${VPN}" + VPN="${VPN,,}" +else + err "::: Cancel selected, exiting...." + exit 1 +fi setupVars="/etc/pivpn/${VPN}/setupVars.conf" -if [ ! -f "${setupVars}" ]; then - echo "::: Missing setup vars file!" - exit 1 +if [[ ! -f "${setupVars}" ]]; then + err "::: Missing setup vars file!" + exit 1 fi # shellcheck disable=SC1090 source "${setupVars}" -scriptusage(){ - echo "::: Updates PiVPN scripts" - echo ":::" - echo "::: Usage: pivpn <-up|update> [-t|--test]" - echo ":::" - echo "::: Commands:" - echo "::: [none] Updates from master branch" - echo "::: -t, test Updates from test branch" - echo "::: -h, help Show this usage dialog" +# TODO: Uncomment this function when the updating functionality +# will be re-enabled +#err() { +# echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +#} + +scriptusage() { + echo "::: Updates PiVPN scripts" + echo ":::" + echo "::: Usage: pivpn <-up|update> [-t|--test]" + echo ":::" + echo "::: Commands:" + echo "::: [none] Updates from master branch" + echo "::: -t, test Updates from test branch" + echo "::: -h, help Show this usage dialog" } -###Functions -##Updates scripts -updatepivpnscripts(){ - ##We don't know what sort of changes users have made. - ##Lets remove first /etc/.pivpn dir then clone it back again - echo "going do update PiVPN Scripts" - if [[ -d "$pivpnlocalpath" ]]; then - if [[ -n "$pivpnlocalpath" ]]; then - rm -rf "${pivpnlocalpath}/../.pivpn" - cloneandupdate - fi - else - cloneandupdate - fi - echo "PiVPN Scripts have been updated" -# Disabling warning for SC1090 +### Functions +## Updates scripts +updatepivpnscripts() { + local branch + branch="${1}" + ## We don't know what sort of changes users have made. + ## Lets remove first /etc/.pivpn dir then clone it back again + echo -n "Going do update PiVPN Scripts" + + if [[ -z "${branch}" ]]; then + echo "from ${branch} branch" + else + echo + fi + + if [[ -d "${pivpnlocalpath}" ]] && + [[ -n "${pivpnlocalpath}" ]]; then + rm -rf "${pivpnlocalpath}/../.pivpn" + fi + + cloneandupdate "${branch}" + echo -n "PiVPN Scripts have been updated" + + if [[ -z "${branch}" ]]; then + echo "from ${branch} branch" + else + echo + fi } -##Updates scripts using test branch -updatefromtest(){ - ##We don't know what sort of changes users have made. - ##Lets remove first /etc/.pivpn dir then clone it back again - echo "PiVPN Scripts updating from test branch" - if [[ -d "$pivpnlocalpath" ]]; then - if [[ -n "$pivpnlocalpath" ]]; then - rm -rf "${pivpnlocalpath}/../.pivpn" - cloneupdttest - fi - else - cloneupdttest - fi - echo "PiVPN Scripts updated have been updated from test branch" - } +## Clone and copy pivpn scripts to /opt/pivpn +cloneandupdate() { + local branch + branch="${1}" + git clone "${pivpnrepo}" "${pivpnlocalpath}" -##Clone and copy pivpn scripts to /opt/pivpn -cloneandupdate(){ - git clone "$pivpnrepo" "$pivpnlocalpath" - cp "${pivpnlocalpath}"/scripts/*.sh "$pivpnscripts" - cp "${pivpnlocalpath}"/scripts/"$VPN"/*.sh "$pivpnscripts" - cp "${pivpnlocalpath}"/scripts/"$VPN"/bash-completion "$bashcompletiondir" -} + if [[ -z "${branch}" ]]; then + git -C "${pivpnlocalpath}" checkout "${branch}" + git -C "${pivpnlocalpath}" pull origin "${branch}" + fi -##same as cloneandupdate() but from test branch -##and falls back to master branch again after updating -cloneupdttest(){ - git clone "$pivpnrepo" "$pivpnlocalpath" - git -C "$pivpnlocalpath" checkout test - git -C "$pivpnlocalpath" pull origin test - cp "${pivpnlocalpath}"/scripts/*.sh "$pivpnscripts" - cp "${pivpnlocalpath}"/scripts/"$VPN"/*.sh "$pivpnscripts" - cp "${pivpnlocalpath}"/scripts/"$VPN"/bash-completion "$bashcompletiondir" - git -C "$pivpnlocalpath" checkout master + cp "${pivpnlocalpath}"/scripts/*.sh "${pivpnscripts}" + cp "${pivpnlocalpath}"/scripts/"${VPN}"/*.sh "${pivpnscripts}" + cp "${pivpnlocalpath}"/scripts/"${VPN}"/bash-completion "${bashcompletiondir}" + + if [[ -z "${branch}" ]]; then + git -C "${pivpnlocalpath}" checkout master + fi } ## SCRIPT -if [[ $# -eq 0 ]]; then - updatepivpnscripts +if [[ "$#" -eq 0 ]]; then + updatepivpnscripts else while true; do - case "$1" in - -t|test) - updatefromtest - exit 0 - ;; - -h|help) - scriptusage - exit 0 - ;; + case "${1}" in + -t | test) + updatepivpnscripts 'test' + exit 0 + ;; + -h | help) + scriptusage + exit 0 + ;; *) updatepivpnscripts exit 0 diff --git a/scripts/wireguard/bash-completion b/scripts/wireguard/bash-completion index eda7a14..b0ce6ea 100644 --- a/scripts/wireguard/bash-completion +++ b/scripts/wireguard/bash-completion @@ -1,18 +1,22 @@ #!/bin/bash -_pivpn() -{ - local cur opts - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - dashopts="-a -c -d -l -qr -r -h -u -up -bk -off -on" - opts="add clients debug list qrcode remove help uninstall update backup (temp) off (temp) on" - if [ "${#COMP_WORDS[@]}" -eq 2 ]; then - if [[ ${cur} == -* ]] ; then - COMPREPLY=( "$(compgen -W "${dashopts}" -- "${cur}")" ) - else - COMPREPLY=( "$(compgen -W "${opts}" -- "${cur}")" ) - fi + +_pivpn() { + local cur opts + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + dashopts="-a -c -d -l -qr -r -h -u -up -bk -off -on" + opts="add clients debug list qrcode remove help uninstall update" + opts="${opts} backup (temp) off (temp) on" + + if [[ "${#COMP_WORDS[@]}" -eq 2 ]]; then + if [[ "${cur}" == -* ]]; then + COMPREPLY=("$(compgen -W "${dashopts}" -- "${cur}")") + else + COMPREPLY=("$(compgen -W "${opts}" -- "${cur}")") fi - return 0 + fi + + return 0 } + complete -F _pivpn pivpn diff --git a/scripts/wireguard/clientSTAT.sh b/scripts/wireguard/clientSTAT.sh index 139b09d..248db38 100755 --- a/scripts/wireguard/clientSTAT.sh +++ b/scripts/wireguard/clientSTAT.sh @@ -1,94 +1,108 @@ -#!/usr/bin/env bash +#!/bin/bash # PiVPN: client status script CLIENTS_FILE="/etc/wireguard/configs/clients.txt" -if [ ! -s "$CLIENTS_FILE" ]; then - echo "::: There are no clients to list" - exit 0 +if [[ ! -s "${CLIENTS_FILE}" ]]; then + err "::: There are no clients to list" + exit 0 fi -scriptusage(){ - echo "::: List any connected clients to the server" - echo ":::" - echo "::: Usage: pivpn <-c|clients> [-b|bytes]" - echo ":::" - echo "::: Commands:" - echo "::: [none] List clients with human readable format" - echo "::: -b, bytes List clients with dotted decimal notation" - echo "::: -h, help Show this usage dialog" +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 } -hr(){ - numfmt --to=iec-i --suffix=B "$1" +scriptusage() { + echo "::: List any connected clients to the server" + echo ":::" + echo "::: Usage: pivpn <-c|clients> [-b|bytes]" + echo ":::" + echo "::: Commands:" + echo "::: [none] List clients with human readable format" + echo "::: -b, bytes List clients with dotted decimal notation" + echo "::: -h, help Show this usage dialog" } -listClients(){ - if DUMP="$(wg show wg0 dump)"; then - DUMP="$(tail -n +2 <<< "$DUMP")" - else - exit 1 - fi +hr() { + numfmt --to=iec-i --suffix=B "${1}" +} - printf "\e[1m::: Connected Clients List :::\e[0m\n" +listClients() { + if DUMP="$(wg show wg0 dump)"; then + DUMP="$(tail -n +2 <<< "${DUMP}")" + else + exit 1 + fi - { - printf "\e[4mName\e[0m \t \e[4mRemote IP\e[0m \t \e[4mVirtual IP\e[0m \t \e[4mBytes Received\e[0m \t \e[4mBytes Sent\e[0m \t \e[4mLast Seen\e[0m\n" + printf "\e[1m::: Connected Clients List :::\e[0m\n" + + { + printf "\e[4mName\e[0m \t \e[4mRemote IP\e[0m \t \e[4mVirtual IP\e[0m" + printf "\t \e[4mBytes Received\e[0m \t \e[4mBytes Sent\e[0m " + printf "\t \e[4mLast Seen\e[0m\n" while IFS= read -r LINE; do - if [ -n "${LINE}" ]; then - PUBLIC_KEY="$(awk '{ print $1 }' <<< "$LINE")" - REMOTE_IP="$(awk '{ print $3 }' <<< "$LINE")" - VIRTUAL_IP="$(awk '{ print $4 }' <<< "$LINE")" - BYTES_RECEIVED="$(awk '{ print $6 }' <<< "$LINE")" - BYTES_SENT="$(awk '{ print $7 }' <<< "$LINE")" - LAST_SEEN="$(awk '{ print $5 }' <<< "$LINE")" - CLIENT_NAME="$(grep "$PUBLIC_KEY" "$CLIENTS_FILE" | awk '{ print $1 }')" - if [ "$HR" = 1 ]; then - if [ "$LAST_SEEN" -ne 0 ]; then - printf "%s \t %s \t %s \t %s \t %s \t %s\n" "$CLIENT_NAME" "$REMOTE_IP" "${VIRTUAL_IP/\/32/}" "$(hr "$BYTES_RECEIVED")" "$(hr "$BYTES_SENT")" "$(date -d @"$LAST_SEEN" '+%b %d %Y - %T')" - else - printf "%s \t %s \t %s \t %s \t %s \t %s\n" "$CLIENT_NAME" "$REMOTE_IP" "${VIRTUAL_IP/\/32/}" "$(hr "$BYTES_RECEIVED")" "$(hr "$BYTES_SENT")" "(not yet)" - fi - else - if [ "$LAST_SEEN" -ne 0 ]; then - printf "%s \t %s \t %s \t %'d \t %'d \t %s\n" "$CLIENT_NAME" "$REMOTE_IP" "${VIRTUAL_IP/\/32/}" "$BYTES_RECEIVED" "$BYTES_SENT" "$(date -d @"$LAST_SEEN" '+%b %d %Y - %T')" - else - printf "%s \t %s \t %s \t %'d \t %'d \t %s\n" "$CLIENT_NAME" "$REMOTE_IP" "${VIRTUAL_IP/\/32/}" "$BYTES_RECEIVED" "$BYTES_SENT" "(not yet)" - fi - fi + if [[ -n "${LINE}" ]]; then + PUBLIC_KEY="$(awk '{ print $1 }' <<< "${LINE}")" + REMOTE_IP="$(awk '{ print $3 }' <<< "${LINE}")" + VIRTUAL_IP="$(awk '{ print $4 }' <<< "${LINE}")" + BYTES_RECEIVED="$(awk '{ print $6 }' <<< "${LINE}")" + BYTES_SENT="$(awk '{ print $7 }' <<< "${LINE}")" + LAST_SEEN="$(awk '{ print $5 }' <<< "${LINE}")" + CLIENT_NAME="$(grep "${PUBLIC_KEY}" "${CLIENTS_FILE}" | + awk '{ print $1 }')" + printf "%s \t %s \t %s \t " \ + "${CLIENT_NAME}" \ + "${REMOTE_IP}" \ + "${VIRTUAL_IP/\/32/}" + + if [[ "${HR}" == 1 ]]; then + printf "%s \t %s \t " \ + "$(hr "${BYTES_RECEIVED}")" \ + "$(hr "${BYTES_SENT}")" + else + printf "%s \t %s \t " "${BYTES_RECEIVED}" "${BYTES_SENT}" fi - done <<< "$DUMP" + + if [[ "${LAST_SEEN}" -ne 0 ]]; then + printf "%s" "$(date -d @"${LAST_SEEN}" '+%b %d %Y - %T')" + else + printf "(not yet)" + fi + + printf "\n" + fi + done <<< "${DUMP}" printf "\n" - } | column -t -s $'\t' + } | column -ts $'\t' - cd /etc/wireguard || return - echo "::: Disabled clients :::" - grep '\[disabled\] ### begin' wg0.conf | sed 's/#//g; s/begin//' + cd /etc/wireguard || return + echo "::: Disabled clients :::" + grep '\[disabled\] ### begin' wg0.conf | sed 's/#//g; s/begin//' } -if [[ $# -eq 0 ]]; then - HR=1 - listClients +if [[ "$#" -eq 0 ]]; then + HR=1 + listClients else - while true; do - case "$1" in - -b|bytes) - HR=0 - listClients - exit 0 - ;; - -h|help) - scriptusage - exit 0 - ;; - *) - HR=0 - listClients - exit 0 - ;; - esac - done + while true; do + case "${1}" in + -b | bytes) + HR=0 + listClients + exit 0 + ;; + -h | help) + scriptusage + exit 0 + ;; + *) + HR=0 + listClients + exit 0 + ;; + esac + done fi diff --git a/scripts/wireguard/disableCONF.sh b/scripts/wireguard/disableCONF.sh index 4fe5147..0dc4f1e 100755 --- a/scripts/wireguard/disableCONF.sh +++ b/scripts/wireguard/disableCONF.sh @@ -2,127 +2,137 @@ setupVars="/etc/pivpn/wireguard/setupVars.conf" -if [ ! -f "${setupVars}" ]; then - echo "::: Missing setup vars file!" - exit 1 +if [[ ! -f "${setupVars}" ]]; then + err "::: Missing setup vars file!" + exit 1 fi # shellcheck disable=SC1090 source "${setupVars}" -helpFunc(){ - echo "::: Disable client conf profiles" - echo ":::" - echo "::: Usage: pivpn <-off|off> [-h|--help] [-v] [ ... [] ...] " - echo ":::" - echo "::: Commands:" - echo "::: [none] Interactive mode" - echo "::: Client" - echo "::: -y,--yes Disable client(s) without confirmation" - echo "::: -v Show disabled clients only" - echo "::: -h,--help Show this help dialog" +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + +helpFunc() { + echo "::: Disable client conf profiles" + echo ":::" + echo -n "::: Usage: pivpn <-off|off> [-h|--help] [-v] " + echo "[ ... [] ...]" + echo ":::" + echo "::: Commands:" + echo "::: [none] Interactive mode" + echo "::: Client" + echo "::: -y,--yes Disable client(s) without confirmation" + echo "::: -v Show disabled clients only" + echo "::: -h,--help Show this help dialog" } # Parse input arguments -while test $# -gt 0; do - _key="$1" - case "$_key" in - -h|--help) - helpFunc - exit 0 - ;; - -y|--yes) - CONFIRM=true - ;; - -v) - DISPLAY_DISABLED=true - ;; - *) - CLIENTS_TO_CHANGE+=("$1") - ;; - esac - shift +while [[ "$#" -gt 0 ]]; do + _key="${1}" + + case "${_key}" in + -h | --help) + helpFunc + exit 0 + ;; + -y | --yes) + CONFIRM=true + ;; + -v) + DISPLAY_DISABLED=true + ;; + *) + CLIENTS_TO_CHANGE+=("${1}") + ;; + esac + + shift done cd /etc/wireguard || exit -if [ ! -s configs/clients.txt ]; then - echo "::: There are no clients to change" - exit 1 + +if [[ ! -s configs/clients.txt ]]; then + err "::: There are no clients to change" + exit 1 fi - -if [ "$DISPLAY_DISABLED" ]; then - grep '\[disabled\] ### begin' wg0.conf | sed 's/#//g; s/begin//' - exit 1 +if [[ "${DISPLAY_DISABLED}" ]]; then + grep '\[disabled\] ### begin' wg0.conf | sed 's/#//g; s/begin//' + exit 1 fi - mapfile -t LIST < <(awk '{print $1}' configs/clients.txt) -if [ "${#CLIENTS_TO_CHANGE[@]}" -eq 0 ]; then - echo -e "::\e[4m Client list \e[0m::" - len=${#LIST[@]} - COUNTER=1 - while [ $COUNTER -le "${len}" ]; do - printf "%0${#len}s) %s\r\n" "${COUNTER}" "${LIST[(($COUNTER-1))]}" - ((COUNTER++)) - done - read -r -p "Please enter the Index/Name of the Client to be removed from the list above: " CLIENTS_TO_CHANGE +if [[ "${#CLIENTS_TO_CHANGE[@]}" -eq 0 ]]; then + echo -e "::\e[4m Client list \e[0m::" + len="${#LIST[@]}" + COUNTER=1 - if [ -z "${CLIENTS_TO_CHANGE}" ]; then - echo "::: You can not leave this blank!" - exit 1 - fi + while [[ "${COUNTER}" -le "${len}" ]]; do + printf "%0${#len}s) %s\r\n" "${COUNTER}" "${LIST[(($COUNTER - 1))]}" + ((COUNTER++)) + done + + echo -n "Please enter the Index/Name of the Client to be removed " + echo -n "from the list above: " + read -r CLIENTS_TO_CHANGE + + if [[ -z "${CLIENTS_TO_CHANGE}" ]]; then + err "::: You can not leave this blank!" + exit 1 + fi fi CHANGED_COUNT=0 for CLIENT_NAME in "${CLIENTS_TO_CHANGE[@]}"; do + re='^[0-9]+$' + if [[ "${CLIENT_NAME}" =~ $re ]]; then + CLIENT_NAME="${LIST[$((CLIENT_NAME - 1))]}" + fi - re='^[0-9]+$' - if [[ ${CLIENT_NAME} =~ $re ]] ; then - CLIENT_NAME=${LIST[$((CLIENT_NAME -1))]} - fi + if ! grep -q "^${CLIENT_NAME} " configs/clients.txt; then + echo -e "::: \e[1m${CLIENT_NAME}\e[0m does not exist" + elif grep -q "#\[disabled\] ### begin ${CLIENT_NAME}" wg0.conf; then + echo -e "::: \e[1m${CLIENT_NAME}\e[0m is already disabled" + else + if [[ -n "${CONFIRM}" ]]; then + REPLY="y" + else + read -r -p "Confirm you want to disable ${CLIENT_NAME}? [Y/n] " + fi - if ! grep -q "^${CLIENT_NAME} " configs/clients.txt; then - echo -e "::: \e[1m${CLIENT_NAME}\e[0m does not exist" - elif grep -q "#\[disabled\] ### begin ${CLIENT_NAME}" wg0.conf; then - echo -e "::: \e[1m${CLIENT_NAME}\e[0m is already disabled" - else - if [ -n "$CONFIRM" ]; then - REPLY="y" - else - read -r -p "Confirm you want to disable $CLIENT_NAME? [Y/n] " - fi + if [[ "${REPLY}" =~ ^[Yy]$ ]]; then + # Disable the peer section from the server config + echo "${CLIENT_NAME}" - if [[ $REPLY =~ ^[Yy]$ ]]; then - - # Disable the peer section from the server config - echo "${CLIENT_NAME}" - sed -e "/### begin ${CLIENT_NAME}/,/end ${CLIENT_NAME}/ s/^/#\[disabled\] /" -i wg0.conf - echo "::: Updated server config" - - ((CHANGED_COUNT++)) - echo "::: Successfully disabled ${CLIENT_NAME}" - - fi - fi + sed_pattern="/### begin ${CLIENT_NAME}/," + sed_pattern="${sed_pattern}/end ${CLIENT_NAME}/ s/^/#\[disabled\] /" + sed -e "${sed_pattern}" -i wg0.conf + unset sed_pattern + echo "::: Updated server config" + ((CHANGED_COUNT++)) + echo "::: Successfully disabled ${CLIENT_NAME}" + fi + fi done # Restart WireGuard only if some clients were actually deleted -if [ "${CHANGED_COUNT}" -gt 0 ]; then - if [ "${PLAT}" == 'Alpine' ]; then - if rc-service wg-quick restart; then - echo "::: WireGuard reloaded" - else - echo "::: Failed to reload WireGuard" - fi - else - if systemctl reload wg-quick@wg0; then - echo "::: WireGuard reloaded" - else - echo "::: Failed to reload WireGuard" - fi - fi +if [[ "${CHANGED_COUNT}" -gt 0 ]]; then + if [[ "${PLAT}" == 'Alpine' ]]; then + if rc-service wg-quick restart; then + echo "::: WireGuard reloaded" + else + err "::: Failed to reload WireGuard" + fi + else + if systemctl reload wg-quick@wg0; then + echo "::: WireGuard reloaded" + else + err "::: Failed to reload WireGuard" + fi + fi fi diff --git a/scripts/wireguard/enableCONF.sh b/scripts/wireguard/enableCONF.sh index dbfd02a..87e648c 100755 --- a/scripts/wireguard/enableCONF.sh +++ b/scripts/wireguard/enableCONF.sh @@ -2,123 +2,136 @@ setupVars="/etc/pivpn/wireguard/setupVars.conf" -if [ ! -f "${setupVars}" ]; then - echo "::: Missing setup vars file!" - exit 1 +if [[ ! -f "${setupVars}" ]]; then + err "::: Missing setup vars file!" + exit 1 fi # shellcheck disable=SC1090 source "${setupVars}" -helpFunc(){ - echo "::: Enables client conf profiles" - echo ":::" - echo "::: Usage: pivpn <-on|on> [-h|--help] [-v] [ ... [] ...] " - echo ":::" - echo "::: Commands:" - echo "::: [none] Interactive mode" - echo "::: Client" - echo "::: -y,--yes Enable client(s) without confirmation" - echo "::: -v Show disabled clients only" - echo "::: -h,--help Show this help dialog" +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + +helpFunc() { + echo "::: Enables client conf profiles" + echo ":::" + echo -n "::: Usage: pivpn <-on|on> [-h|--help] [-v] " + echo "[ ... [] ...]" + echo ":::" + echo "::: Commands:" + echo "::: [none] Interactive mode" + echo "::: Client" + echo "::: -y,--yes Enable client(s) without confirmation" + echo "::: -v Show disabled clients only" + echo "::: -h,--help Show this help dialog" } # Parse input arguments -while test $# -gt 0; do - _key="$1" - case "$_key" in - -h|--help) - helpFunc - exit 0 - ;; - -y|--yes) - CONFIRM=true - ;; - -v) - DISPLAY_DISABLED=true - ;; - *) - CLIENTS_TO_CHANGE+=("$1") - ;; - esac - shift +while [[ "$#" -gt 0 ]]; do + _key="${1}" + + case "${_key}" in + -h | --help) + helpFunc + exit 0 + ;; + -y | --yes) + CONFIRM=true + ;; + -v) + DISPLAY_DISABLED=true + ;; + *) + CLIENTS_TO_CHANGE+=("${1}") + ;; + esac + + shift done cd /etc/wireguard || exit -if [ ! -s configs/clients.txt ]; then - echo "::: There are no clients to change" - exit 1 + +if [[ ! -s configs/clients.txt ]]; then + err "::: There are no clients to change" + exit 1 fi -if [ "$DISPLAY_DISABLED" ]; then - grep '\[disabled\] ### begin' wg0.conf | sed 's/#//g; s/begin//' - exit 1 +if [[ "${DISPLAY_DISABLED}" ]]; then + grep '\[disabled\] ### begin' wg0.conf | sed 's/#//g; s/begin//' + exit 1 fi mapfile -t LIST < <(awk '{print $1}' configs/clients.txt) -if [ "${#CLIENTS_TO_CHANGE[@]}" -eq 0 ]; then - echo -e "::\e[4m Client list \e[0m::" - len=${#LIST[@]} - COUNTER=1 - while [ $COUNTER -le "${len}" ]; do - printf "%0${#len}s) %s\r\n" "${COUNTER}" "${LIST[(($COUNTER-1))]}" - ((COUNTER++)) - done - read -r -p "Please enter the Index/Name of the Client to be enabled from the list above: " CLIENTS_TO_CHANGE +if [[ "${#CLIENTS_TO_CHANGE[@]}" -eq 0 ]]; then + echo -e "::\e[4m Client list \e[0m::" + len="${#LIST[@]}" + COUNTER=1 - if [ -z "${CLIENTS_TO_CHANGE}" ]; then - echo "::: You can not leave this blank!" - exit 1 - fi + while [[ "${COUNTER}" -le "${len}" ]]; do + printf "%0${#len}s) %s\r\n" "${COUNTER}" "${LIST[(($COUNTER - 1))]}" + ((COUNTER++)) + done + + echo -n "Please enter the Index/Name of the Client to be enabled " + echo -n "from the list above: " + read -r CLIENTS_TO_CHANGE + + if [[ -z "${CLIENTS_TO_CHANGE}" ]]; then + err "::: You can not leave this blank!" + exit 1 + fi fi CHANGED_COUNT=0 for CLIENT_NAME in "${CLIENTS_TO_CHANGE[@]}"; do + re='^[0-9]+$' - re='^[0-9]+$' - if [[ ${CLIENT_NAME} =~ $re ]] ; then - CLIENT_NAME=${LIST[$((CLIENT_NAME -1))]} - fi + if [[ "${CLIENT_NAME}" =~ $re ]]; then + CLIENT_NAME="${LIST[$((CLIENT_NAME - 1))]}" + fi - if ! grep -q "^${CLIENT_NAME} " configs/clients.txt; then - echo -e "::: \e[1m${CLIENT_NAME}\e[0m does not exist" - else - if [ -n "$CONFIRM" ]; then - REPLY="y" - else - read -r -p "Confirm you want to enable $CLIENT_NAME? [Y/n] " - fi + if ! grep -q "^${CLIENT_NAME} " configs/clients.txt; then + echo -e "::: \e[1m${CLIENT_NAME}\e[0m does not exist" + else + if [[ -n "${CONFIRM}" ]]; then + REPLY="y" + else + read -r -p "Confirm you want to enable ${CLIENT_NAME}? [Y/n] " + fi - if [[ $REPLY =~ ^[Yy]$ ]]; then + if [[ "${REPLY}" =~ ^[Yy]$ ]]; then + # Enable the peer section from the server config + echo "${CLIENT_NAME}" - # Enable the peer section from the server config - echo "${CLIENT_NAME}" - sed -e "/begin ${CLIENT_NAME}/,/end ${CLIENT_NAME}/ s/#\[disabled\] //" -i wg0.conf - echo "::: Updated server config" - - ((CHANGED_COUNT++)) - echo "::: Successfully enabled ${CLIENT_NAME}" - - fi - fi + sed_pattern="/begin ${CLIENT_NAME}/," + sed_pattern="${sed_pattern}/end ${CLIENT_NAME}/ s/#\[disabled\] //" + sed -e "${sed_pattern}" -i wg0.conf + unset sed_pattern + echo "::: Updated server config" + ((CHANGED_COUNT++)) + echo "::: Successfully enabled ${CLIENT_NAME}" + fi + fi done # Restart WireGuard only if some clients were actually deleted -if [ "${CHANGED_COUNT}" -gt 0 ]; then - if [ "${PLAT}" == 'Alpine' ]; then - if rc-service wg-quick restart; then - echo "::: WireGuard reloaded" - else - echo "::: Failed to reload WireGuard" - fi - else - if systemctl reload wg-quick@wg0; then - echo "::: WireGuard reloaded" - else - echo "::: Failed to reload WireGuard" - fi - fi +if [[ "${CHANGED_COUNT}" -gt 0 ]]; then + if [[ "${PLAT}" == 'Alpine' ]]; then + if rc-service wg-quick restart; then + echo "::: WireGuard reloaded" + else + err "::: Failed to reload WireGuard" + fi + else + if systemctl reload wg-quick@wg0; then + echo "::: WireGuard reloaded" + else + err "::: Failed to reload WireGuard" + fi + fi fi diff --git a/scripts/wireguard/listCONF.sh b/scripts/wireguard/listCONF.sh index c89bae3..da37b87 100755 --- a/scripts/wireguard/listCONF.sh +++ b/scripts/wireguard/listCONF.sh @@ -1,33 +1,34 @@ #!/bin/bash +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + cd /etc/wireguard/configs || exit -if [ ! -s clients.txt ]; then - echo "::: There are no clients to list" - exit 1 + +if [[ ! -s clients.txt ]]; then + err "::: There are no clients to list" + exit 1 fi printf "\e[1m::: Clients Summary :::\e[0m\n" # Present the user with a summary of the clients, fetching info from dates. { -echo -e "\e[4mClient\e[0m \t \e[4mPublic key\e[0m \t \e[4mCreation date\e[0m" - -while read -r LINE; do - CLIENT_NAME="$(awk '{print $1}' <<< "$LINE")" - - PUBLIC_KEY="$(awk '{print $2}' <<< "$LINE")" - - CREATION_DATE="$(awk '{print $3}' <<< "$LINE")" + echo -ne "\e[4mClient\e[0m \t \e[4mPublic key\e[0m \t " + echo -e "\e[4mCreation date\e[0m" + while read -r LINE; do + CLIENT_NAME="$(awk '{print $1}' <<< "${LINE}")" + PUBLIC_KEY="$(awk '{print $2}' <<< "${LINE}")" + CREATION_DATE="$(awk '{print $3}' <<< "${LINE}")" # Dates are converted from UNIX time to human readable. - CD_FORMAT="$(date -d @"$CREATION_DATE" +'%d %b %Y, %H:%M, %Z')" - - echo -e "$CLIENT_NAME \t $PUBLIC_KEY \t $CD_FORMAT" -done < clients.txt - + CD_FORMAT="$(date -d @"${CREATION_DATE}" +'%d %b %Y, %H:%M, %Z')" + echo -e "${CLIENT_NAME} \t ${PUBLIC_KEY} \t ${CD_FORMAT}" + done < clients.txt } | column -t -s $'\t' - cd /etc/wireguard || return + echo "::: Disabled clients :::" grep '\[disabled\] ### begin' wg0.conf | sed 's/#//g; s/begin//' diff --git a/scripts/wireguard/makeCONF.sh b/scripts/wireguard/makeCONF.sh index 25f5ee0..c396125 100755 --- a/scripts/wireguard/makeCONF.sh +++ b/scripts/wireguard/makeCONF.sh @@ -1,108 +1,113 @@ #!/bin/bash -######## Some vars that might be empty -# but need to be defined for checks +# Some vars that might be empty but need to be defined for checks pivpnPERSISTENTKEEPALIVE="" pivpnDNS2="" setupVars="/etc/pivpn/wireguard/setupVars.conf" +# shellcheck disable=SC2154 +userGroup="${install_user}:${install_user}" -if [ ! -f "${setupVars}" ]; then - echo "::: Missing setup vars file!" - exit 1 +if [[ ! -f "${setupVars}" ]]; then + err "::: Missing setup vars file!" + exit 1 fi # shellcheck disable=SC1090 source "${setupVars}" -helpFunc(){ - echo "::: Create a client conf profile" - echo ":::" - echo "::: Usage: pivpn <-a|add> [-n|--name ] [-h|--help]" - echo ":::" - echo "::: Commands:" - echo "::: [none] Interactive mode" - echo "::: -n,--name Name for the Client (default: '$HOSTNAME')" - echo "::: -h,--help Show this help dialog" +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + +helpFunc() { + echo "::: Create a client conf profile" + echo ":::" + echo "::: Usage: pivpn <-a|add> [-n|--name ] [-h|--help]" + echo ":::" + echo "::: Commands:" + echo "::: [none] Interactive mode" + echo "::: -n,--name Name for the Client (default: '${HOSTNAME}')" + echo "::: -h,--help Show this help dialog" } # Parse input arguments -while test $# -gt 0; do - _key="$1" - case "$_key" in - -n|--name|--name=*) - _val="${_key##--name=}" - if test "$_val" = "$_key"; then - test $# -lt 2 && echo "::: Missing value for the optional argument '$_key'." && exit 1 - _val="$2" - shift - fi - CLIENT_NAME="$_val" - ;; - -h|--help) - helpFunc - exit 0 - ;; - *) - echo "::: Error: Got an unexpected argument '$1'" - helpFunc - exit 1 - ;; - esac - shift +while [[ "$#" -gt 0 ]]; do + _key="${1}" + + case "${_key}" in + -n | --name | --name=*) + _val="${_key##--name=}" + + if [[ "${_val}" == "${_key}" ]]; then + [[ "$#" -lt 2 ]] && + err "::: Missing value for the optional argument '${_key}'." && + exit 1 + + _val="${2}" + shift + fi + + CLIENT_NAME="${_val}" + ;; + -h | --help) + helpFunc + exit 0 + ;; + *) + err "::: Error: Got an unexpected argument '${1}'" + helpFunc + exit 1 + ;; + esac + + shift done # Disabling SC2154, variables sourced externaly # shellcheck disable=SC2154 # The home folder variable was sourced from the settings file. -if [ ! -d "${install_home}/configs" ]; then - mkdir "${install_home}/configs" - chown "${install_user}":"${install_user}" "${install_home}/configs" - chmod 0750 "${install_home}/configs" +if [[ ! -d "${install_home}/configs" ]]; then + mkdir "${install_home}/configs" + chown "${userGroup}" "${install_home}/configs" + chmod 0750 "${install_home}/configs" fi cd /etc/wireguard || exit -if [ -z "${CLIENT_NAME}" ]; then - read -r -p "Enter a Name for the Client: " CLIENT_NAME +if [[ -z "${CLIENT_NAME}" ]]; then + read -r -p "Enter a Name for the Client: " CLIENT_NAME +elif [[ "${CLIENT_NAME}" =~ [^a-zA-Z0-9.@_-] ]]; then + err "Name can only contain alphanumeric characters and these symbols (.-@_)." + exit 1 +elif [[ "${CLIENT_NAME:0:1}" == "-" ]]; then + err "Name cannot start with -" + exit 1 +elif [[ "${CLIENT_NAME}" =~ ^[0-9]+$ ]]; then + err "Names cannot be integers." + exit 1 +elif [[ -z "${CLIENT_NAME}" ]]; then + err "::: You cannot leave the name blank." + exit 1 +elif [[ -f "configs/${CLIENT_NAME}.conf" ]]; then + err "::: A client with this name already exists" + exit 1 fi -if [[ "${CLIENT_NAME}" =~ [^a-zA-Z0-9.@_-] ]]; then - echo "Name can only contain alphanumeric characters and these characters (.-@_)." - exit 1 -fi - -if [[ "${CLIENT_NAME:0:1}" == "-" ]]; then - echo "Name cannot start with -" - exit 1 -fi - -if [[ "${CLIENT_NAME}" =~ ^[0-9]+$ ]]; then - echo "Names cannot be integers." - exit 1 -fi - -if [ -z "${CLIENT_NAME}" ]; then - echo "::: You cannot leave the name blank." - exit 1 -fi - -if [ -f "configs/${CLIENT_NAME}.conf" ]; then - echo "::: A client with this name already exists" - exit 1 -fi - -wg genkey | tee "keys/${CLIENT_NAME}_priv" | wg pubkey > "keys/${CLIENT_NAME}_pub" +wg genkey | + tee "keys/${CLIENT_NAME}_priv" | + wg pubkey > "keys/${CLIENT_NAME}_pub" wg genpsk | tee "keys/${CLIENT_NAME}_psk" &> /dev/null echo "::: Client Keys generated" # Find an unused number for the last octet of the client IP for i in {2..254}; do - if ! grep -q " $i$" configs/clients.txt; then - COUNT="$i" - echo "${CLIENT_NAME} $(> configs/clients.txt - break - fi + if ! grep -q " ${i}$" configs/clients.txt; then + COUNT="${i}" + echo "${CLIENT_NAME} $(< keys/"${CLIENT_NAME}"_pub) $(date +%s) ${COUNT}" | + tee -a configs/clients.txt > /dev/null + break + fi done # Disabling SC2154, variables sourced externaly @@ -110,88 +115,95 @@ done NET_REDUCED="${pivpnNET::-2}" # shellcheck disable=SC2154 -if [ "$pivpnenableipv6" == "1" ]; then -echo "[Interface] -PrivateKey = $(cat "keys/${CLIENT_NAME}_priv") -Address = ${NET_REDUCED}.${COUNT}/${subnetClass},${pivpnNETv6}${COUNT}/${subnetClassv6}" > "configs/${CLIENT_NAME}.conf" -else -echo "[Interface] -PrivateKey = $(cat "keys/${CLIENT_NAME}_priv") -Address = ${NET_REDUCED}.${COUNT}/${subnetClass}" > "configs/${CLIENT_NAME}.conf" -fi +{ + echo '[Interface]' + echo "PrivateKey = $(cat "keys/${CLIENT_NAME}_priv")" + echo -n "Address = ${NET_REDUCED}.${COUNT}/${subnetClass}" -# shellcheck disable=SC2154 -echo -n "DNS = ${pivpnDNS1}" >> "configs/${CLIENT_NAME}.conf" -if [ -n "${pivpnDNS2}" ]; then - echo ", ${pivpnDNS2}" >> "configs/${CLIENT_NAME}.conf" -else - echo >> "configs/${CLIENT_NAME}.conf" -fi -echo >> "configs/${CLIENT_NAME}.conf" + if [[ "${pivpnenableipv6}" == 1 ]]; then + echo ",${pivpnNETv6}${COUNT}/${subnetClassv6}" + else + echo + fi -# shellcheck disable=SC2154 -echo "[Peer] -PublicKey = $(cat keys/server_pub) -PresharedKey = $(cat "keys/${CLIENT_NAME}_psk") -Endpoint = ${pivpnHOST}:${pivpnPORT} -AllowedIPs = ${ALLOWED_IPS}" >> "configs/${CLIENT_NAME}.conf" + echo -n "DNS = ${pivpnDNS1}" + + if [[ -n "${pivpnDNS2}" ]]; then + echo ", ${pivpnDNS2}" + else + echo + fi + + echo + echo '[Peer]' + echo "PublicKey = $(cat keys/server_pub)" + echo "PresharedKey = $(cat "keys/${CLIENT_NAME}_psk")" + echo "Endpoint = ${pivpnHOST}:${pivpnPORT}" + echo "AllowedIPs = ${ALLOWED_IPS}" + + if [[ -n "${pivpnPERSISTENTKEEPALIVE}" ]]; then + echo "PersistentKeepalive = ${pivpnPERSISTENTKEEPALIVE}" + fi +} > "configs/${CLIENT_NAME}.conf" -if [ -n "${pivpnPERSISTENTKEEPALIVE}" ]; then - echo "PersistentKeepalive = ${pivpnPERSISTENTKEEPALIVE}" >> "configs/${CLIENT_NAME}.conf" -fi echo "::: Client config generated" -if [ "$pivpnenableipv6" == "1" ]; then -echo "### begin ${CLIENT_NAME} ### -[Peer] -PublicKey = $(cat "keys/${CLIENT_NAME}_pub") -PresharedKey = $(cat "keys/${CLIENT_NAME}_psk") -AllowedIPs = ${NET_REDUCED}.${COUNT}/32,${pivpnNETv6}${COUNT}/128 -### end ${CLIENT_NAME} ###" >> wg0.conf -else -echo "### begin ${CLIENT_NAME} ### -[Peer] -PublicKey = $(cat "keys/${CLIENT_NAME}_pub") -PresharedKey = $(cat "keys/${CLIENT_NAME}_psk") -AllowedIPs = ${NET_REDUCED}.${COUNT}/32 -### end ${CLIENT_NAME} ###" >> wg0.conf -fi +{ + echo "### begin ${CLIENT_NAME} ###" + echo '[Peer]' + echo "PublicKey = $(cat "keys/${CLIENT_NAME}_pub")" + echo "PresharedKey = $(cat "keys/${CLIENT_NAME}_psk")" + echo -n "AllowedIPs = ${NET_REDUCED}.${COUNT}/32" + + if [[ "${pivpnenableipv6}" == 1 ]]; then + echo ",${pivpnNETv6}${COUNT}/128" + else + echo + fi + + echo "### end ${CLIENT_NAME} ###" +} >> wg0.conf echo "::: Updated server config" -if [ -f /etc/pivpn/hosts.wireguard ]; then - echo "${NET_REDUCED}.${COUNT} ${CLIENT_NAME}.pivpn" >> /etc/pivpn/hosts.wireguard - if [ "$pivpnenableipv6" == "1" ]; then - echo "${pivpnNETv6}${COUNT} ${CLIENT_NAME}.pivpn" >> /etc/pivpn/hosts.wireguard - fi - if killall -SIGHUP pihole-FTL; then - echo "::: Updated hosts file for Pi-hole" - else - echo "::: Failed to reload pihole-FTL configuration" - fi +if [[ -f /etc/pivpn/hosts.wireguard ]]; then + echo "${NET_REDUCED}.${COUNT} ${CLIENT_NAME}.pivpn" | + tee -a /etc/pivpn/hosts.wireguard > /dev/null + + if [[ "${pivpnenableipv6}" == 1 ]]; then + echo "${pivpnNETv6}${COUNT} ${CLIENT_NAME}.pivpn" | + tee -a /etc/pivpn/hosts.wireguard > /dev/null + fi + + if killall -SIGHUP pihole-FTL; then + echo "::: Updated hosts file for Pi-hole" + else + err "::: Failed to reload pihole-FTL configuration" + fi fi -if [ "${PLAT}" == 'Alpine' ]; then - if rc-service wg-quick restart; then - echo "::: WireGuard reloaded" - else - echo "::: Failed to reload WireGuard" - fi +if [[ "${PLAT}" == 'Alpine' ]]; then + if rc-service wg-quick restart; then + echo "::: WireGuard reloaded" + else + err "::: Failed to reload WireGuard" + fi else - if systemctl reload wg-quick@wg0; then - echo "::: WireGuard reloaded" - else - echo "::: Failed to reload WireGuard" - fi + if systemctl reload wg-quick@wg0; then + echo "::: WireGuard reloaded" + else + err "::: Failed to reload WireGuard" + fi fi cp "configs/${CLIENT_NAME}.conf" "${install_home}/configs/${CLIENT_NAME}.conf" -chown "${install_user}":"${install_user}" "${install_home}/configs/${CLIENT_NAME}.conf" +chown "${userGroup}" "${install_home}/configs/${CLIENT_NAME}.conf" chmod 640 "${install_home}/configs/${CLIENT_NAME}.conf" echo "======================================================================" echo -e "::: Done! \e[1m${CLIENT_NAME}.conf successfully created!\e[0m" -echo "::: ${CLIENT_NAME}.conf was copied to ${install_home}/configs for easy transfer." +echo -n "::: ${CLIENT_NAME}.conf was copied to ${install_home}/configs for easy" +echo "transfer." echo "::: Please use this profile only on one device and create additional" echo -e "::: profiles for other devices. You can also use \e[1mpivpn -qr\e[0m" echo "::: to generate a QR Code you can scan with the mobile app." diff --git a/scripts/wireguard/pivpn.sh b/scripts/wireguard/pivpn.sh index 27a5138..894caeb 100755 --- a/scripts/wireguard/pivpn.sh +++ b/scripts/wireguard/pivpn.sh @@ -3,127 +3,160 @@ CHECK_PKG_INSTALLED='dpkg-query -s' if grep -qsEe "^NAME\=['\"]?Alpine[a-zA-Z ]*['\"]?$" /etc/os-release; then - CHECK_PKG_INSTALLED='apk --no-cache info -e' + CHECK_PKG_INSTALLED='apk --no-cache info -e' fi # Must be root to use this tool -if [ $EUID -ne 0 ]; then - if eval "${CHECK_PKG_INSTALLED} sudo" &> /dev/null; then - export SUDO="sudo" - else - echo "::: Please install sudo or run this as root." - exit 1 +if [[ "${EUID}" -ne 0 ]]; then + if ${CHECK_PKG_INSTALLED} sudo &> /dev/null; then + export SUDO="sudo" + else + err "::: Please install sudo or run this as root." + exit 1 fi fi scriptdir="/opt/pivpn" vpn="wireguard" -makeConf(){ - shift - $SUDO ${scriptdir}/${vpn}/makeCONF.sh "$@" - exit "$?" +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 } -listConnected(){ - shift - $SUDO ${scriptdir}/${vpn}/clientSTAT.sh "$@" - exit "$?" +makeConf() { + shift + ${SUDO} "${scriptdir}/${vpn}/makeCONF.sh" "$@" + exit "${?}" } -debug(){ - echo "::: Generating Debug Output" - $SUDO ${scriptdir}/${vpn}/pivpnDEBUG.sh | tee /tmp/debug.log - echo "::: " - echo "::: Debug output completed above." - echo "::: Copy saved to /tmp/debug.log" - echo "::: " - exit "$?" +listConnected() { + shift + ${SUDO} "${scriptdir}/${vpn}/clientSTAT.sh" "$@" + exit "${?}" } -listClients(){ - $SUDO ${scriptdir}/${vpn}/listCONF.sh - exit "$?" +debug() { + echo "::: Generating Debug Output" + + ${SUDO} "${scriptdir}/${vpn}/pivpnDEBUG.sh" | tee /tmp/debug.log + + echo "::: " + echo "::: Debug output completed above." + echo "::: Copy saved to /tmp/debug.log" + echo "::: " + exit "${?}" } -showQrcode(){ - shift - $SUDO ${scriptdir}/${vpn}/qrcodeCONF.sh "$@" - exit "$?" +listClients() { + ${SUDO} "${scriptdir}/${vpn}/listCONF.sh" + exit "${?}" } -removeClient(){ - shift - $SUDO ${scriptdir}/${vpn}/removeCONF.sh "$@" - exit "$?" +showQrcode() { + shift + ${SUDO} "${scriptdir}/${vpn}/qrcodeCONF.sh" "$@" + exit "${?}" } -disableClient(){ - shift - $SUDO ${scriptdir}/${vpn}/disableCONF.sh "$@" - exit "$?" +removeClient() { + shift + ${SUDO} "${scriptdir}/${vpn}/removeCONF.sh" "$@" + exit "${?}" } -enableClient(){ - shift - $SUDO ${scriptdir}/${vpn}/enableCONF.sh "$@" - exit "$?" +disableClient() { + shift + ${SUDO} "${scriptdir}/${vpn}/disableCONF.sh" "$@" + exit "${?}" } -uninstallServer(){ - $SUDO ${scriptdir}/uninstall.sh "${vpn}" - exit "$?" +enableClient() { + shift + ${SUDO} "${scriptdir}/${vpn}/enableCONF.sh" "$@" + exit "${?}" } -updateScripts(){ - shift - $SUDO ${scriptdir}/update.sh "$@" - exit "$?" +uninstallServer() { + ${SUDO} "${scriptdir}/uninstall.sh" "${vpn}" + exit "${?}" } -backup(){ - $SUDO ${scriptdir}/backup.sh "${vpn}" - exit "$?" +updateScripts() { + shift + ${SUDO} "${scriptdir}/update.sh" "$@" + exit "${?}" } -showHelp(){ - echo "::: Control all PiVPN specific functions!" - echo ":::" - echo "::: Usage: pivpn [option]" - echo ":::" - echo "::: Commands:" - echo "::: -a, add Create a client conf profile" - echo "::: -c, clients List any connected clients to the server" - echo "::: -d, debug Start a debugging session if having trouble" - echo "::: -l, list List all clients" - echo "::: -qr, qrcode Show the qrcode of a client for use with the mobile app" - echo "::: -r, remove Remove a client" - echo "::: -off, off Disable a client" - echo "::: -on, on Enable a client" - echo "::: -h, help Show this help dialog" - echo "::: -u, uninstall Uninstall pivpn from your system!" - echo "::: -up, update Updates PiVPN Scripts" - echo "::: -bk, backup Backup VPN configs and user profiles" - exit 0 +backup() { + ${SUDO} "${scriptdir}/backup.sh" "${vpn}" + exit "${?}" } -if [ $# = 0 ]; then - showHelp +showHelp() { + echo "::: Control all PiVPN specific functions!" + echo ":::" + echo "::: Usage: pivpn [option]" + echo ":::" + echo "::: Commands:" + echo "::: -a, add Create a client conf profile" + echo "::: -c, clients List any connected clients to the server" + echo "::: -d, debug Start a debugging session if having trouble" + echo "::: -l, list List all clients" + echo -n "::: -qr, qrcode Show the qrcode of a client for use " + echo "with the mobile app" + echo "::: -r, remove Remove a client" + echo "::: -off, off Disable a client" + echo "::: -on, on Enable a client" + echo "::: -h, help Show this help dialog" + echo "::: -u, uninstall Uninstall pivpn from your system!" + echo "::: -up, update Updates PiVPN Scripts" + echo "::: -bk, backup Backup VPN configs and user profiles" + exit 0 +} + +if [[ "$#" == 0 ]]; then + showHelp fi # Handle redirecting to specific functions based on arguments -case "$1" in -"-a" | "add" ) makeConf "$@";; -"-c" | "clients" ) listConnected "$@";; -"-d" | "debug" ) debug;; -"-l" | "list" ) listClients;; -"-qr" | "qrcode" ) showQrcode "$@";; -"-r" | "remove" ) removeClient "$@";; -"-off" | "off" ) disableClient "$@";; -"-on" | "on" ) enableClient "$@";; -"-h" | "help" ) showHelp;; -"-u" | "uninstall" ) uninstallServer;; -"-up" | "update" ) updateScripts "$@" ;; -"-bk" | "backup" ) backup ;; -* ) showHelp;; +case "${1}" in + "-a" | "add") + makeConf "$@" + ;; + "-c" | "clients") + listConnected "$@" + ;; + "-d" | "debug") + debug + ;; + "-l" | "list") + listClients + ;; + "-qr" | "qrcode") + showQrcode "$@" + ;; + "-r" | "remove") + removeClient "$@" + ;; + "-off" | "off") + disableClient "$@" + ;; + "-on" | "on") + enableClient "$@" + ;; + "-h" | "help") + showHelp + ;; + "-u" | "uninstall") + uninstallServer + ;; + "-up" | "update") + updateScripts "$@" + ;; + "-bk" | "backup") + backup + ;; + *) + showHelp + ;; esac diff --git a/scripts/wireguard/pivpnDEBUG.sh b/scripts/wireguard/pivpnDEBUG.sh index 3a68198..2ecc011 100755 --- a/scripts/wireguard/pivpnDEBUG.sh +++ b/scripts/wireguard/pivpnDEBUG.sh @@ -1,11 +1,15 @@ -#!/usr/bin/env bash +#!/bin/bash # This scripts runs as root setupVars="/etc/pivpn/wireguard/setupVars.conf" -if [ ! -f "${setupVars}" ]; then - echo "::: Missing setup vars file!" - exit 1 +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + +if [[ ! -f "${setupVars}" ]]; then + err "::: Missing setup vars file!" + exit 1 fi # shellcheck disable=SC1090 @@ -15,51 +19,76 @@ echo -e "::::\t\t\e[4mPiVPN debug\e[0m\t\t ::::" printf "=============================================\n" echo -e "::::\t\t\e[4mLatest commit\e[0m\t\t ::::" echo -n "Branch: " + git --git-dir /usr/local/src/pivpn/.git rev-parse --abbrev-ref HEAD -git --git-dir /usr/local/src/pivpn/.git log -n 1 --format='Commit: %H%nAuthor: %an%nDate: %ad%nSummary: %s' +git \ + --git-dir /usr/local/src/pivpn/.git log \ + -n 1 \ + --format='Commit: %H%nAuthor: %an%nDate: %ad%nSummary: %s' + printf "=============================================\n" echo -e "::::\t \e[4mInstallation settings\e[0m \t ::::" + # Disabling SC2154 warning, variable is sourced externaly and may vary # shellcheck disable=SC2154 -sed "s/$pivpnHOST/REDACTED/" < ${setupVars} +sed "s/${pivpnHOST}/REDACTED/" < "${setupVars}" + printf "=============================================\n" echo -e ":::: \e[4mServer configuration shown below\e[0m ::::" + cd /etc/wireguard/keys || exit cp ../wg0.conf ../wg0.tmp + # Replace every key in the server configuration with just its file name for k in *; do - sed "s#$(<"$k")#$k#" -i ../wg0.tmp + sed "s#$(< "${k}")#${k}#" -i ../wg0.tmp done + cat ../wg0.tmp rm ../wg0.tmp + printf "=============================================\n" echo -e ":::: \e[4mClient configuration shown below\e[0m ::::" + EXAMPLE="$(head -1 /etc/wireguard/configs/clients.txt | awk '{print $1}')" -if [ -n "$EXAMPLE" ]; then - cp ../configs/"$EXAMPLE".conf ../configs/"$EXAMPLE".tmp - for k in *; do - sed "s#$(<"$k")#$k#" -i ../configs/"$EXAMPLE".tmp - done - sed "s/$pivpnHOST/REDACTED/" < ../configs/"$EXAMPLE".tmp - rm ../configs/"$EXAMPLE".tmp + +if [[ -n "${EXAMPLE}" ]]; then + cp ../configs/"${EXAMPLE}".conf ../configs/"${EXAMPLE}".tmp + + for k in *; do + sed "s#$(< "${k}")#${k}#" -i ../configs/"${EXAMPLE}".tmp + done + + sed "s/${pivpnHOST}/REDACTED/" < ../configs/"${EXAMPLE}".tmp + rm ../configs/"${EXAMPLE}".tmp else - echo "::: There are no clients yet" + echo "::: There are no clients yet" fi printf "=============================================\n" -echo -e ":::: \t\e[4mRecursive list of files in\e[0m\t ::::\n::::\t\e[4m/etc/wireguard shown below\e[0m\t ::::" +echo -e ":::: \t\e[4mRecursive list of files in\e[0m\t ::::" +echo -e "::::\t\e[4m/etc/wireguard shown below\e[0m\t ::::" + ls -LR /etc/wireguard + printf "=============================================\n" echo -e "::::\t\t\e[4mSelf check\e[0m\t\t ::::" + /opt/pivpn/self_check.sh "${VPN}" + printf "=============================================\n" echo -e ":::: Having trouble connecting? Take a look at the FAQ:" echo -e ":::: \e[1mhttps://docs.pivpn.io/faq\e[0m" printf "=============================================\n" -echo -e ":::: \e[1mWARNING\e[0m: This script should have automatically masked sensitive ::::" -echo -e ":::: information, however, still make sure that \e[4mPrivateKey\e[0m, \e[4mPublicKey\e[0m ::::" -echo -e ":::: and \e[4mPresharedKey\e[0m are masked before reporting an issue. An example key ::::" -echo ":::: that you should NOT see in this log looks like this: ::::" -echo ":::: YIAoJVsdIeyvXfGGDDadHh6AxsMRymZTnnzZoAb9cxRe ::::" +echo -ne ":::: \e[1mWARNING\e[0m: This script should have " +echo -e "automatically masked sensitive ::::" +echo -ne ":::: information, however, still make sure that " +echo -e "\e[4mPrivateKey\e[0m, \e[4mPublicKey\e[0m ::::" +echo -ne ":::: and \e[4mPresharedKey\e[0m are masked before " +echo -e "reporting an issue. An example key ::::" +echo -n ":::: that you should NOT see in this log looks like this:" +echo " ::::" +echo -n ":::: YIAoJVsdIeyvXfGGDDadHh6AxsMRymZTnnzZoAb9cxRe" +echo " ::::" printf "=============================================\n" echo -e "::::\t\t\e[4mDebug complete\e[0m\t\t ::::" diff --git a/scripts/wireguard/qrcodeCONF.sh b/scripts/wireguard/qrcodeCONF.sh index 8f707f5..1f7bbd8 100755 --- a/scripts/wireguard/qrcodeCONF.sh +++ b/scripts/wireguard/qrcodeCONF.sh @@ -1,76 +1,91 @@ #!/bin/bash -helpFunc(){ - echo "::: Show the qrcode of a client for use with the mobile app" - echo ":::" - echo "::: Usage: pivpn <-qr|qrcode> [-h|--help] [Options] [] ... [] ..." - echo ":::" - echo "::: Options:" - echo "::: -a256|ansi256 Shows QR Code in ansi256 characters" - echo "::: Commands:" - echo "::: [none] Interactive mode" - echo "::: Client(s) to show" - echo "::: -h,--help Show this help dialog" +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + +helpFunc() { + echo "::: Show the qrcode of a client for use with the mobile app" + echo ":::" + echo -n "::: Usage: pivpn <-qr|qrcode> [-h|--help] [Options] " + echo "[ ... [] ...]" + echo ":::" + echo "::: Options:" + echo "::: -a256|ansi256 Shows QR Code in ansi256 characters" + echo "::: Commands:" + echo "::: [none] Interactive mode" + echo "::: Client(s) to show" + echo "::: -h,--help Show this help dialog" } # Parse input arguments encoding="ansiutf8" -while test $# -gt 0; do - _key="$1" - case "$_key" in - -h|--help) - helpFunc - exit 0 - ;; - -a256|--ansi256) - encoding="ansi256" - ;; - *) - CLIENTS_TO_SHOW+=("$1") - ;; - esac - shift + +while [[ "$#" -gt 0 ]]; do + _key="${1}" + + case "${_key}" in + -h | --help) + helpFunc + exit 0 + ;; + -a256 | --ansi256) + encoding="ansi256" + ;; + *) + CLIENTS_TO_SHOW+=("${1}") + ;; + esac + + shift done cd /etc/wireguard/configs || exit -if [ ! -s clients.txt ]; then - echo "::: There are no clients to show" - exit 1 + +if [[ ! -s clients.txt ]]; then + err "::: There are no clients to show" + exit 1 fi mapfile -t LIST < <(awk '{print $1}' clients.txt) -if [ "${#CLIENTS_TO_SHOW[@]}" -eq 0 ]; then - echo -e "::\e[4m Client list \e[0m::" - len=${#LIST[@]} - COUNTER=1 - while [ $COUNTER -le "${len}" ]; do - printf "%0${#len}s) %s\r\n" "${COUNTER}" "${LIST[(($COUNTER-1))]}" - ((COUNTER++)) - done +if [[ "${#CLIENTS_TO_SHOW[@]}" -eq 0 ]]; then + echo -e "::\e[4m Client list \e[0m::" + len="${#LIST[@]}" + COUNTER=1 - read -r -p "Please enter the Index/Name of the Client to show: " CLIENTS_TO_SHOW + while [[ "${COUNTER}" -le "${len}" ]]; do + printf "%0${#len}s) %s\r\n" "${COUNTER}" "${LIST[(($COUNTER - 1))]}" + ((COUNTER++)) + done - if [ -z "${CLIENTS_TO_SHOW}" ]; then - echo "::: You can not leave this blank!" - exit 1 - fi + echo -n "Please enter the Index/Name of the Client to show: " + read -r CLIENTS_TO_SHOW + + if [[ -z "${CLIENTS_TO_SHOW}" ]]; then + err "::: You can not leave this blank!" + exit 1 + fi fi for CLIENT_NAME in "${CLIENTS_TO_SHOW[@]}"; do - re='^[0-9]+$' - if [[ ${CLIENT_NAME:0:1} == "-" ]]; then - echo "${CLIENT_NAME} is not a valid client name or option" - exit 1 - elif [[ ${CLIENT_NAME} =~ $re ]] ; then - CLIENT_NAME=${LIST[$((CLIENT_NAME -1))]} - fi - if grep -qw "${CLIENT_NAME}" clients.txt; then - echo -e "::: Showing client \e[1m${CLIENT_NAME}\e[0m below" - echo "=====================================================================" - qrencode -t "${encoding}" < "${CLIENT_NAME}.conf" - echo "=====================================================================" - else - echo -e "::: \e[1m${CLIENT_NAME}\e[0m does not exist" - fi + re='^[0-9]+$' + + if [[ "${CLIENT_NAME:0:1}" == "-" ]]; then + err "${CLIENT_NAME} is not a valid client name or option" + exit 1 + elif [[ "${CLIENT_NAME}" =~ $re ]]; then + CLIENT_NAME="${LIST[$((CLIENT_NAME - 1))]}" + fi + + if grep -qw "${CLIENT_NAME}" clients.txt; then + echo -e "::: Showing client \e[1m${CLIENT_NAME}\e[0m below" + echo "=====================================================================" + + qrencode -t "${encoding}" < "${CLIENT_NAME}.conf" + + echo "=====================================================================" + else + echo -e "::: \e[1m${CLIENT_NAME}\e[0m does not exist" + fi done diff --git a/scripts/wireguard/removeCONF.sh b/scripts/wireguard/removeCONF.sh index ad1af7c..9f15b21 100755 --- a/scripts/wireguard/removeCONF.sh +++ b/scripts/wireguard/removeCONF.sh @@ -2,159 +2,187 @@ setupVars="/etc/pivpn/wireguard/setupVars.conf" -if [ ! -f "${setupVars}" ]; then - echo "::: Missing setup vars file!" - exit 1 +if [[ ! -f "${setupVars}" ]]; then + err "::: Missing setup vars file!" + exit 1 fi # shellcheck disable=SC1090 source "${setupVars}" -helpFunc(){ - echo "::: Remove a client conf profile" - echo ":::" - echo "::: Usage: pivpn <-r|remove> [-y|--yes] [-h|--help] [] ... [] ..." - echo ":::" - echo "::: Commands:" - echo "::: [none] Interactive mode" - echo "::: Client(s) to remove" - echo "::: -y,--yes Remove Client(s) without confirmation" - echo "::: -h,--help Show this help dialog" +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2 +} + +helpFunc() { + echo "::: Remove a client conf profile" + echo ":::" + echo -n "::: Usage: pivpn <-r|remove> [-y|--yes] [-h|--help] " + echo "[ ... [] ...]" + echo ":::" + echo "::: Commands:" + echo "::: [none] Interactive mode" + echo "::: Client(s) to remove" + echo "::: -y,--yes Remove Client(s) without confirmation" + echo "::: -h,--help Show this help dialog" } # Parse input arguments -while test $# -gt 0 -do - _key="$1" - case "$_key" in - -h|--help) - helpFunc - exit 0 - ;; - -y|--yes) - CONFIRM=true - ;; - *) - CLIENTS_TO_REMOVE+=("$1") - ;; - esac - shift +while [[ "$#" -gt 0 ]]; do + _key="${1}" + + case "${_key}" in + -h | --help) + helpFunc + exit 0 + ;; + -y | --yes) + CONFIRM=true + ;; + *) + CLIENTS_TO_REMOVE+=("${1}") + ;; + esac + + shift done cd /etc/wireguard || exit -if [ ! -s configs/clients.txt ]; then - echo "::: There are no clients to remove" - exit 1 + +if [[ ! -s configs/clients.txt ]]; then + err "::: There are no clients to remove" + exit 1 fi mapfile -t LIST < <(awk '{print $1}' configs/clients.txt) -if [ "${#CLIENTS_TO_REMOVE[@]}" -eq 0 ]; then - echo -e "::\e[4m Client list \e[0m::" - len=${#LIST[@]} - COUNTER=1 - while [ $COUNTER -le "${len}" ]; do - printf "%0${#len}s) %s\r\n" "${COUNTER}" "${LIST[(($COUNTER-1))]}" - ((COUNTER++)) - done - read -r -p "Please enter the Index/Name of the Client to be removed from the list above: " CLIENTS_TO_REMOVE +if [[ "${#CLIENTS_TO_REMOVE[@]}" -eq 0 ]]; then + echo -e "::\e[4m Client list \e[0m::" + len="${#LIST[@]}" + COUNTER=1 - if [ -z "${CLIENTS_TO_REMOVE}" ]; then - echo "::: You can not leave this blank!" - exit 1 - fi + while [[ "${COUNTER}" -le "${len}" ]]; do + printf "%0${#len}s) %s\r\n" "${COUNTER}" "${LIST[(($COUNTER - 1))]}" + ((COUNTER++)) + done + + echo -n "Please enter the Index/Name of the Client to be removed " + echo -n "from the list above: " + read -r CLIENTS_TO_REMOVE + + if [[ -z "${CLIENTS_TO_REMOVE}" ]]; then + err "::: You can not leave this blank!" + exit 1 + fi fi DELETED_COUNT=0 for CLIENT_NAME in "${CLIENTS_TO_REMOVE[@]}"; do + re='^[0-9]+$' - re='^[0-9]+$' - if [[ ${CLIENT_NAME} =~ $re ]] ; then - CLIENT_NAME=${LIST[$((CLIENT_NAME -1))]} - fi + if [[ "${CLIENT_NAME}" =~ $re ]]; then + CLIENT_NAME="${LIST[$((CLIENT_NAME - 1))]}" + fi - if ! grep -q "^${CLIENT_NAME} " configs/clients.txt; then - echo -e "::: \e[1m${CLIENT_NAME}\e[0m does not exist" + if ! grep -q "^${CLIENT_NAME} " configs/clients.txt; then + echo -e "::: \e[1m${CLIENT_NAME}\e[0m does not exist" + else + REQUESTED="$(sha256sum "configs/${CLIENT_NAME}.conf" | cut -c 1-64)" + + if [[ -n "${CONFIRM}" ]]; then + REPLY="y" else - REQUESTED="$(sha256sum "configs/${CLIENT_NAME}.conf" | cut -c 1-64)" - if [ -n "$CONFIRM" ]; then - REPLY="y" - else - read -r -p "Do you really want to delete $CLIENT_NAME? [y/N] " - fi - - if [[ $REPLY =~ ^[Yy]$ ]]; then - - # Grab the least significant octed of the client IP address - COUNT=$(grep "^${CLIENT_NAME} " configs/clients.txt | awk '{print $4}') - # The creation date of the client - CREATION_DATE="$(grep "^${CLIENT_NAME} " configs/clients.txt | awk '{print $3}')" - # And its public key - PUBLIC_KEY="$(grep "^${CLIENT_NAME} " configs/clients.txt | awk '{print $2}')" - - # Then remove the client matching the variables above - sed "\#${CLIENT_NAME} ${PUBLIC_KEY} ${CREATION_DATE} ${COUNT}#d" -i configs/clients.txt - - # Remove the peer section from the server config - sed "/### begin ${CLIENT_NAME} ###/,/### end ${CLIENT_NAME} ###/d" -i wg0.conf - echo "::: Updated server config" - - rm "configs/${CLIENT_NAME}.conf" - echo "::: Client config for ${CLIENT_NAME} removed" - - rm "keys/${CLIENT_NAME}_priv" - rm "keys/${CLIENT_NAME}_pub" - rm "keys/${CLIENT_NAME}_psk" - echo "::: Client Keys for ${CLIENT_NAME} removed" - - # Find all .conf files in the home folder of the user matching the checksum of the - # config and delete them. '-maxdepth 3' is used to avoid traversing too many folders. - # Disabling SC2154, variable sourced externaly and may vary - # shellcheck disable=SC2154 - find "${install_home}" -maxdepth 3 -type f -name '*.conf' -print0 | while IFS= read -r -d '' CONFIG; do - if sha256sum -c <<< "${REQUESTED} ${CONFIG}" &> /dev/null; then - rm "${CONFIG}" - fi - done - - ((DELETED_COUNT++)) - echo "::: Successfully deleted ${CLIENT_NAME}" - - # If using Pi-hole, remove the client from the hosts file - # Disabling SC2154, variable sourced externaly and may vary - # shellcheck disable=SC2154 - if [ -f /etc/pivpn/hosts.wireguard ]; then - NET_REDUCED="${pivpnNET::-2}" - sed -e "\#${NET_REDUCED}.${COUNT} ${CLIENT_NAME}.pivpn#d" -e "\#${pivpnNETv6}${COUNT} ${CLIENT_NAME}.pivpn#d" -i /etc/pivpn/hosts.wireguard - if killall -SIGHUP pihole-FTL; then - echo "::: Updated hosts file for Pi-hole" - else - echo "::: Failed to reload pihole-FTL configuration" - fi - fi - - else - echo "Aborting operation" - exit 1 - fi + read -r -p "Do you really want to delete ${CLIENT_NAME}? [y/N] " fi + if [[ "${REPLY}" =~ ^[Yy]$ ]]; then + # Grab the least significant octed of the client IP address + COUNT="$(grep "^${CLIENT_NAME} " configs/clients.txt | awk '{print $4}')" + # The creation date of the client + CREATION_DATE="$(grep "^${CLIENT_NAME} " configs/clients.txt | + awk '{print $3}')" + # And its public key + PUBLIC_KEY="$(grep "^${CLIENT_NAME} " configs/clients.txt | + awk '{print $2}')" + + # Then remove the client matching the variables above + sed \ + -e "\#${CLIENT_NAME} ${PUBLIC_KEY} ${CREATION_DATE} ${COUNT}#d" \ + -i \ + configs/clients.txt + + # Remove the peer section from the server config + sed_pattern="/### begin ${CLIENT_NAME} ###/," + sed_pattern="${sed_pattern}/### end ${CLIENT_NAME} ###/d" + sed -e "${sed_pattern}" -i wg0.conf + echo "::: Updated server config" + + rm "configs/${CLIENT_NAME}.conf" + echo "::: Client config for ${CLIENT_NAME} removed" + + rm "keys/${CLIENT_NAME}_priv" + rm "keys/${CLIENT_NAME}_pub" + rm "keys/${CLIENT_NAME}_psk" + echo "::: Client Keys for ${CLIENT_NAME} removed" + + # Find all .conf files in the home folder of the user matching the + # checksum of the config and delete them. '-maxdepth 3' is used to + # avoid traversing too many folders. + # Disabling SC2154, variable sourced externaly and may vary + # shellcheck disable=SC2154 + while IFS= read -r -d '' CONFIG; do + if sha256sum -c <<< "${REQUESTED} ${CONFIG}" &> /dev/null; then + rm "${CONFIG}" + fi + done < <(find "${install_home}" \ + -maxdepth 3 \ + -type f \ + -name '*.conf' \ + -print0) + + ((DELETED_COUNT++)) + echo "::: Successfully deleted ${CLIENT_NAME}" + + # If using Pi-hole, remove the client from the hosts file + # Disabling SC2154, variable sourced externaly and may vary + # shellcheck disable=SC2154 + if [[ -f /etc/pivpn/hosts.wireguard ]]; then + NET_REDUCED="${pivpnNET::-2}" + sed \ + -e "\#${NET_REDUCED}.${COUNT} ${CLIENT_NAME}.pivpn#d" \ + -e "\#${pivpnNETv6}${COUNT} ${CLIENT_NAME}.pivpn#d" \ + -i \ + /etc/pivpn/hosts.wireguard + + if killall -SIGHUP pihole-FTL; then + echo "::: Updated hosts file for Pi-hole" + else + err "::: Failed to reload pihole-FTL configuration" + fi + fi + + unset sed_pattern + else + err "Aborting operation" + exit 1 + fi + fi done # Restart WireGuard only if some clients were actually deleted -if [ "${DELETED_COUNT}" -gt 0 ]; then - if [ "${PLAT}" == 'Alpine' ]; then - if rc-service wg-quick restart; then - echo "::: WireGuard reloaded" - else - echo "::: Failed to reload WireGuard" - fi +if [[ "${DELETED_COUNT}" -gt 0 ]]; then + if [[ "${PLAT}" == 'Alpine' ]]; then + if rc-service wg-quick restart; then + echo "::: WireGuard reloaded" else - if systemctl reload wg-quick@wg0; then - echo "::: WireGuard reloaded" - else - echo "::: Failed to reload WireGuard" - fi + err "::: Failed to reload WireGuard" fi + else + if systemctl reload wg-quick@wg0; then + echo "::: WireGuard reloaded" + else + err "::: Failed to reload WireGuard" + fi + fi fi