diff --git a/scripts/openvpn/backup.sh b/scripts/openvpn/backup.sh new file mode 100755 index 0000000..8ef6006 --- /dev/null +++ b/scripts/openvpn/backup.sh @@ -0,0 +1,36 @@ +#!/bin/bash +setupVars="/etc/pivpn/setupVars.conf" +backupdir=pivpnbackup +openvpndir=/etc/openvpn +ovpnsdir=${install_home}/ovpns +date=$(date +%Y-%m-%d-%H%M%S) + +if [ ! -f "${setupVars}" ]; then + echo "::: Missing setup vars file!" + exit 1 +fi + +source "${setupVars}" + +backup_openvpn(){ + if [[ ! -d $install_home/$backupdir ]]; then + mkdir $install_home/$backupdir + fi + cp -r $openvpndir $ovpnsdir $backupdir 2&>1 + backupzip=$date-pivpnbackup.tgz + tar -czf $backupzip -C ${install_home} $backupdir 2&>1 + echo -e "Backup crated to $install_home/$backupdir/$backupzip \nTo restore the backup, follow instructions at:\nhttps://github.com/pivpn/pivpn/wiki/FAQ#how-can-i-migrate-my-configs-to-another-pivpn-instance" +} + + +if [[ ! $EUID -eq 0 ]];then + if [[ $(dpkg-query -s sudo) ]];then + export SUDO="sudo" + else + echo "::: Please install sudo or run this as root." + exit 0 + fi +fi + +backup_openvpn + diff --git a/scripts/openvpn/bash-completion b/scripts/openvpn/bash-completion new file mode 100644 index 0000000..f317bc4 --- /dev/null +++ b/scripts/openvpn/bash-completion @@ -0,0 +1,22 @@ +_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" + opts="debug add clients list revoke uninstall help update" + 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}") ) + fi + return 0 +} +complete -F _pivpn pivpn diff --git a/scripts/openvpn/clientStat.sh b/scripts/openvpn/clientStat.sh new file mode 100755 index 0000000..2af420f --- /dev/null +++ b/scripts/openvpn/clientStat.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# PiVPN: client status script + +STATUS_LOG="/var/log/openvpn-status.log" + +function hr() { + numfmt --to=iec-i --suffix=B "$1" +} + +printf "\n" +if [ ! -f "${STATUS_LOG}" ]; then + echo "The file: $STATUS_LOG was not found!" + exit 1 +fi + +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 "\t\t\t\t\t\t\t\tBytes\t\tBytes\t\n" +printf "\e[4mName\e[0m\t\t\t\e[4mRemote IP\e[0m\t\t\e[4mVirtual IP\e[0m\t\e[4mReceived\e[0m\t\e[4mSent\e[0m\t\t\e[4mConnected Since\e[0m \n" +if grep -q "^CLIENT_LIST" "${STATUS_LOG}"; then + if [ -n "$(type -t numfmt)" ]; then + while read -r line; do + read -r -a array <<< $line + [[ ${array[0]} = CLIENT_LIST ]] || continue + printf "%s\t\t%s\t%s\t%s\t\t%s\t\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 + 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" +fi +printf "\n" diff --git a/scripts/openvpn/listOVPN.sh b/scripts/openvpn/listOVPN.sh new file mode 100755 index 0000000..ec0ffd7 --- /dev/null +++ b/scripts/openvpn/listOVPN.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# PiVPN: list clients script + +INDEX="/etc/openvpn/easy-rsa/pki/index.txt" +printf "\n" +if [ ! -f "${INDEX}" ]; then + echo "The file: $INDEX was not found!" + exit 1 +fi + +printf ": NOTE : The first entry should always be your valid server!\n" +printf "\n" +printf "\e[1m::: Certificate Status List :::\e[0m\n" +printf " ::\e[4m Status \e[0m||\e[4m Name \e[0m:: \n" + +while read -r line || [ -n "$line" ]; do + STATUS=$(echo "$line" | awk '{print $1}') + NAME=$(echo "$line" | sed -e 's:.*/CN=::') + if [ "${STATUS}" == "V" ]; then + printf " Valid :: %s\n" "$NAME" + elif [ "${STATUS}" == "R" ]; then + printf " Revoked :: %s\n" "$NAME" + else + printf " Unknown :: %s\n" "$NAME" + fi +done <${INDEX} +printf "\n" diff --git a/scripts/openvpn/makeOVPN.sh b/scripts/openvpn/makeOVPN.sh new file mode 100755 index 0000000..e0c8891 --- /dev/null +++ b/scripts/openvpn/makeOVPN.sh @@ -0,0 +1,414 @@ +#!/bin/bash +# Create OVPN Client +# Default Variable Declarations +setupVars="/etc/pivpn/setupVars.conf" +DEFAULT="Default.txt" +FILEEXT=".ovpn" +CRT=".crt" +KEY=".key" +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 +fi + +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] [-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 "::: -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) + iOS=1 + ;; + -h|--help) + helpFunc + exit 0 + ;; + nopass) + NO_PASS="1" + ;; + -b|--bitwarden) + if which bw; then + BITWARDEN="2" + else + echo "Bitwarden not found, please install bitwarden" + exit 1 + fi + + ;; + *) + echo "Error: Got an unexpected argument '$1'" + helpFunc + exit 1 + ;; + esac + shift +done + +# Functions def + +function 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 + +} + +function useBitwarden() { + + # 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" + + # 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 "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 (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 "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 + +} + +function 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 + 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 + fi + + #Escape chars in PASSWD + PASSWD=$(echo -n ${PASSWD} | sed -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') + + #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" { send -- "${PASSWD}\r" } + expect "Verifying - Enter PEM pass phrase" { send -- "${PASSWD}\r" } + expect eof +EOF + cd pki || exit + +} + +#make sure ovpns dir exists +if [ ! -d "$install_home/ovpns" ]; then + mkdir "$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 [[ -z "${NAME}" ]]; then + echo "You cannot leave the name blank." + exit 1 +fi + +# Check if name is already in use +while read -r line || [ -n "${line}" ]; do + STATUS=$(echo "$line" | awk '{print $1}') + + if [ "${STATUS}" == "V" ]; then + CERT=$(echo "$line" | sed -e 's:.*/CN=::') + if [ "${CERT}" == "${NAME}" ]; then + INUSE="1" + break + fi + fi +done <${INDEX} + +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 +fi + +# 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 + +#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 + +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 +else + keyPASS +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 +fi +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 +fi +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 +fi +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 +fi +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 +## 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 "" + + #Next append the client Public Cert + echo "" + sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' < "issued/${NAME}${CRT}" + echo "" + + #Finally, append the TA Private Key + if [ -f /etc/pivpn/TWO_POINT_FOUR ]; then + echo "" + cat "${TA}" + echo "" + else + echo "" + cat "${TA}" + echo "" + fi + + } > "${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" -export -in "issued/${NAME}${CRT}" -inkey "private/${NAME}${KEY}" -certfile ${CA} -name "${NAME}" -out "$install_home/ovpns/$NAME.ovpn12" + chown "$install_user" "$install_home/ovpns/$NAME.ovpn12" + chmod 600 "$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 -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' < "issued/${NAME}${CRT}" + echo "" + + #Then, append the client Private Key + echo "" + cat "private/${NAME}${KEY}" + echo "" + + #Finally, append the tls Private Key + echo "" + cat "${TA}" + echo "" + + } > "${NAME}${FILEEXT}" + +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_home/ovpns/$NAME$FILEEXT" +chmod 640 "/etc/openvpn/easy-rsa/pki/$NAME$FILEEXT" +chmod 640 "$install_home/ovpns/$NAME$FILEEXT" +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 "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 b/scripts/openvpn/pivpn new file mode 100755 index 0000000..de55586 --- /dev/null +++ b/scripts/openvpn/pivpn @@ -0,0 +1,106 @@ +#!/bin/bash + +# Must be root to use this tool +if [[ ! $EUID -eq 0 ]];then + if [[ $(dpkg-query -s sudo) ]];then + export SUDO="sudo" + else + echo "::: Please install sudo or run this as root." + exit 0 + fi +fi + +function makeOVPNFunc { + shift + $SUDO /opt/pivpn/makeOVPN.sh "$@" + exit 0 +} + +function listClientsFunc { + $SUDO /opt/pivpn/clientStat.sh + exit 0 +} + +function listOVPNFunc { + $SUDO /opt/pivpn/listOVPN.sh + exit 0 +} + +function debugFunc { + echo "::: Generating Debug Output" + $SUDO /opt/pivpn/pivpnDebug.sh | tee /tmp/debug.txt + echo "::: " + echo "::: Debug output completed above." + echo "::: Copy saved to /tmp/debug.txt" + echo "::: " + exit 0 +} + +function removeOVPNFunc { + shift + $SUDO /opt/pivpn/removeOVPN.sh "$@" + exit 0 +} + +function uninstallFunc { + $SUDO /opt/pivpn/uninstall.sh + exit 0 +} + +function versionFunc { + printf "\e[1mVersion 1.9\e[0m\n" +} + +function update { + + shift + $SUDO /opt/pivpn/update.sh "$@" + exit 0 + + +} + +function backup { + + $SUDO /opt/pivpn/backup.sh + exit 0 + +} + + +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 +} + +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;; +"-v" ) versionFunc;; +"-up"| "update" ) update "$@" ;; +"-bk"| "backup" ) backup;; +* ) helpFunc;; +esac diff --git a/scripts/openvpn/pivpnDebug.sh b/scripts/openvpn/pivpnDebug.sh new file mode 100755 index 0000000..847bcdc --- /dev/null +++ b/scripts/openvpn/pivpnDebug.sh @@ -0,0 +1,230 @@ +#!/usr/bin/env bash +# This scripts runs as root + +setupVars="/etc/pivpn/setupVars.conf" +ERR=0 + +if [ ! -f "${setupVars}" ]; then + echo "::: Missing setup vars file!" + exit 1 +fi + +source "${setupVars}" + +echo -e "::::\t\t\e[4mPiVPN debug\e[0m\t\t ::::" +printf "=============================================\n" +echo -e "::::\t\t\e[4mLatest commit\e[0m\t\t ::::" +git --git-dir /etc/.pivpn/.git log -n 1 +printf "=============================================\n" +echo -e "::::\t \e[4mInstallation settings\e[0m \t ::::" +# Use the wildcard so setupVars.conf.update.bak from the previous install is not shown +for filename in /etc/pivpn/*; do + if [[ "$filename" != "/etc/pivpn/setupVars.conf"* ]]; then + echo "$filename -> $(cat "$filename")" + fi +done +printf "=============================================\n" +echo -e "::::\t\e[4msetupVars file shown below\e[0m\t ::::" +sed "s/$pivpnHOST/REDACTED/" < /etc/pivpn/setupVars.conf +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 +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 :::" +ls -LR /etc/openvpn/easy-rsa/pki/ -Ireqs -Icerts_by_serial +printf "=============================================\n" +echo -e "::::\t\t\e[4mSelf check\e[0m\t\t ::::" + +if [ "$(cat /proc/sys/net/ipv4/ip_forward)" -eq 1 ]; then + echo ":: [OK] IP forwarding is enabled" +else + ERR=1 + read -r -p ":: [ERR] IP forwarding is not enabled, attempt fix now? [Y/n] " REPLY + if [[ ${REPLY} =~ ^[Yy]$ ]]; then + sed -i '/net.ipv4.ip_forward=1/s/^#//g' /etc/sysctl.conf + sysctl -p + echo "Done" + fi +fi + +if [ "$USING_UFW" -eq 0 ]; then + + if iptables -t nat -C POSTROUTING -s 10.8.0.0/24 -o "${PHYS_INT}" -j MASQUERADE &> /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]$ ]]; then + iptables -t nat -F + iptables -t nat -I POSTROUTING -s 10.8.0.0/24 -o "${PHYS_INT}" -j MASQUERADE + iptables-save > /etc/iptables/rules.v4 + echo "Done" + fi + fi + + + if [ "$INPUT_CHAIN_EDITED" -eq 1 ]; then + + if iptables -C INPUT -i "$PHYS_INT" -p "$pivpnPROTO" --dport "$pivpnPORT" -j ACCEPT &> /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]$ ]]; then + iptables -I INPUT 1 -i "$PHYS_INT" -p "$pivpnPROTO" --dport "$pivpnPORT" -j ACCEPT + iptables-save > /etc/iptables/rules.v4 + echo "Done" + fi + fi + fi + + if [ "$FORWARD_CHAIN_EDITED" -eq 1 ]; then + + if iptables -C FORWARD -s 10.8.0.0/24 -i tun0 -o "$PHYS_INT" -j ACCEPT &> /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]$ ]]; then + iptables -I FORWARD 1 -d 10.8.0.0/24 -i "$PHYS_INT" -o tun0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT + iptables -I FORWARD 2 -s 10.8.0.0/24 -i tun0 -o "$PHYS_INT" -j ACCEPT + 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 + read -r -p ":: [ERR] Ufw is not enabled, try to enable now? [Y/n] " REPLY + if [[ ${REPLY} =~ ^[Yy]$ ]]; then + ufw enable + fi + fi + + if iptables -t nat -C POSTROUTING -s 10.8.0.0/24 -o "${PHYS_INT}" -j MASQUERADE &> /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]$ ]]; then + sed "/delete these required/i *nat\n:POSTROUTING ACCEPT [0:0]\n-I POSTROUTING -s 10.8.0.0/24 -o $PHYS_INT -j MASQUERADE\nCOMMIT\n" -i /etc/ufw/before.rules + ufw reload + echo "Done" + 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]$ ]]; then + ufw insert 1 allow "$pivpnPORT"/"$pivpnPROTO" + ufw reload + echo "Done" + fi + fi + + if [ "$OLD_UFW" -eq 1 ]; then + FORWARD_POLICY="$(iptables -S FORWARD | grep '^-P' | awk '{print $3}')" + if [ "$FORWARD_POLICY" = "ACCEPT" ]; then + echo ":: [OK] Ufw forwarding policy is accept" + else + ERR=1 + read -r -p ":: [ERR] Ufw forwarding policy is not 'ACCEPT', attempt fix now? [Y/n] " REPLY + if [[ ${REPLY} =~ ^[Yy]$ ]]; then + sed -i "s/\(DEFAULT_FORWARD_POLICY=\).*/\1\"ACCEPT\"/" /etc/default/ufw + ufw reload > /dev/null + echo "Done" + fi + fi + else + if iptables -C ufw-user-forward -i tun0 -o "${PHYS_INT}" -s 10.8.0.0/24 -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]$ ]]; then + ufw route insert 1 allow in on tun0 from 10.8.0.0/24 out on "$PHYS_INT" to any + ufw reload + echo "Done" + fi + fi + fi + +fi + +if systemctl is-active -q openvpn; then + echo ":: [OK] OpenVPN is running" +else + ERR=1 + read -r -p ":: [ERR] OpenVPN is not running, try to start now? [Y/n] " REPLY + if [[ ${REPLY} =~ ^[Yy]$ ]]; then + systemctl start openvpn + echo "Done" + fi +fi + +if systemctl is-enabled -q openvpn; then + echo ":: [OK] OpenVPN is enabled (it will automatically start on reboot)" +else + ERR=1 + read -r -p ":: [ERR] OpenVPN is not enabled, try to enable now? [Y/n] " REPLY + if [[ ${REPLY} =~ ^[Yy]$ ]]; then + systemctl enable openvpn + echo "Done" + fi +fi + +# grep -w (whole word) is used so port 111940 with now match when looking for 1194 +if netstat -uanpt | grep openvpn | grep -w "${pivpnPORT}" | grep -q "${pivpnPROTO}"; then + echo ":: [OK] OpenVPN is listening on port ${pivpnPORT}/${pivpnPROTO}" +else + ERR=1 + read -r -p ":: [ERR] OpenVPN is not listening, try to restart now? [Y/n] " REPLY + if [[ ${REPLY} =~ ^[Yy]$ ]]; then + systemctl restart openvpn + 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" +fi + +printf "=============================================\n" +echo -e ":::: \e[4mSnippet of the server log\e[0m ::::" +tail -20 /var/log/openvpn.log > /tmp/snippet + +# Regular expession taken from https://superuser.com/a/202835, it will match invalid IPs +# like 123.456.789.012 but it's fine because 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 /tmp/snippet | 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 + sed -i "s/$IP/REDACTED/g" /tmp/snippet +done + +cat /tmp/snippet +rm /tmp/snippet +printf "=============================================\n" +echo -e "::::\t\t\e[4mDebug complete\e[0m\t\t ::::" + +# Telekom Hybrid Check +wget -O /tmp/hybcheck http://speedport.ip &>/dev/null +if grep -Fq "Speedport Pro" /tmp/hybcheck || grep -Fq "Speedport Hybrid" /tmp/hybcheck +then + printf ":::\t\t\t\t\t:::\n::\tTelekom Hybrid Check\t\t ::\n:::\t\t\t\t\t:::\n" + echo "Are you using Telekom Hybrid (found a hybrid compatible router)?" + echo "If yes and you have problems with the connections you can test the following:" + echo "Add 'tun-mtu 1316' in /etc/openvpn/easy-rsa/pki/Default.txt to set a hybrid compatible MTU size (new .ovpn files)." + echo "For already existing .ovpn files 'tun-mtu 1316' can also be inserted there manually." + echo "With Telekom hybrid connections, you may have to experiment a little with MTU (tun-mtu, link-mtu and mssfix)." +fi +rm /tmp/hybcheck diff --git a/scripts/openvpn/removeOVPN.sh b/scripts/openvpn/removeOVPN.sh new file mode 100755 index 0000000..0d79eaf --- /dev/null +++ b/scripts/openvpn/removeOVPN.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash +# PiVPN: revoke client script + +install_user=$( [-h|--help] [] ... [] ..." + echo ":::" + echo "::: Commands:" + echo "::: [none] Interactive mode" + echo "::: Client(s) to to revoke" + 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 + ;; + *) + CERTS_TO_REVOKE+=("$1") + ;; + esac + shift +done + +if [ ! -f "${INDEX}" ]; then + printf "The file: %s was not found\n" "$INDEX" + exit 1 +fi + +if [[ -z "${CERTS_TO_REVOKE}" ]]; then + 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 + NAME=$(echo "$line" | sed -e 's:.*/CN=::') + CERTS[$i]=${NAME} + if [ "$i" != 0 ]; then + # Prevent printing "server" certificate + printf " %s\n" "$NAME" + fi + let i=i+1 + fi + done <${INDEX} + printf "\n" + + echo "::: Please enter the Name of the client to be revoked from the list above:" + read -r NAME + + if [[ -z "${NAME}" ]]; then + echo "You can not leave this blank!" + exit 1 + fi + + for((x=1;x<=i;++x)); do + if [ "${CERTS[$x]}" = "${NAME}" ]; then + VALID=1 + fi + done + + if [ -z "${VALID}" ]; then + printf "You didn't enter a valid cert name!\n" + exit 1 + fi + + 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 "$line" | sed -e 's:.*/CN=::') + CERTS[$i]=${NAME} + let i=i+1 + fi + done <${INDEX} + + 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 + printf "You passed an invalid cert name: '"%s"'!\n" "${CERTS_TO_REVOKE[ii]}" + exit 1 + fi + done +fi + +cd /etc/openvpn/easy-rsa || exit + +install_home=$(grep -m1 "^${install_user}:" /etc/passwd | cut -d: -f6) +install_home=${install_home%/} # remove possible trailing slash +for (( ii = 0; ii < ${#CERTS_TO_REVOKE[@]}; ii++)); do + 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" + + 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 +done +printf "::: Completed!\n" diff --git a/scripts/openvpn/update.sh b/scripts/openvpn/update.sh new file mode 100755 index 0000000..fb74450 --- /dev/null +++ b/scripts/openvpn/update.sh @@ -0,0 +1,99 @@ +#/bin/bash + +###Updates pivpn scripts (Not PiVPN) +###Main Vars +pivpnrepo="https://github.com/pivpn/pivpn.git" +pivpnlocalpath="/etc/.pivpn" +pivpnscripts="/opt/pivpn/scripts" +bashcompletiondir="/etc/bash_completion.d/pivpn" + + +###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 + + sudo rm -rf $pivpnlocalpath + cloneandupdate + + else + cloneandupdate + fi + echo "PiVPN Scripts have been updated" +} + +##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 /etc/.pivpn ]]; then + + rm -rf /etc/.pivpn + cloneupdttest + + else + + cloneupdttest + + fi + echo "PiVPN Scripts updated have been updated from test branch" + } + +##Clone and copy pivpn scripts to /op/ +cloneandupdate(){ + + sudo git clone $pivpnrepo $pivpnlocalpath + sudo cp -r $pivpnlocalpath/scripts $pivpnscripts + sudo cp $pivpnlocalpath/scripts/bash-completion $bashcompletiondir + +} + +##same as cloneandupdate() but from test branch +##and falls back to master branch again after updating +cloneupdttest(){ + + sudo git clone $pivpnrepo $pivpnlocalpath + sudo git -C $pivpnlocalpath checkout test + sudo git -C $pivpnlocalpath pull origin test + sudo cp -r $pivpnlocalpath/scripts $pivpnscripts + sudo cp $pivpnlocalpath/scripts/bash-completion $bashcompletiondir + sudo git -C $pivpnlocalpath checkout master + +} + +scriptusage(){ + echo -e "Updates pivpn scripts,\n + Usage: + pivpn update | updates from master branch + pivpn update -t or --test | updates from test branch" + +} + +## SCRIPT + +if [[ $# -eq 0 ]]; then + updatepivpnscripts + +else + while true; do + case "$1" in + -t | --test | test ) + updatefromtest + exit 0 + ;; + -h | --help | help ) + scriptusage + exit 0 + ;; + * ) + updatepivpnscripts + exit 0 + ;; + esac + done +fi + diff --git a/scripts/wireguard/listCONF.sh b/scripts/wireguard/listCONF.sh new file mode 100755 index 0000000..02e5224 --- /dev/null +++ b/scripts/wireguard/listCONF.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +cd /etc/wireguard/configs +if [ ! -s clients.txt ]; then + echo "::: There are no clients to list" + exit 1 +fi + +# Present the user with a summary of the clients, fetching info from dates. +FORMATTED+=": \e[4mClient\e[0m&\e[4mCreation date\e[0m :\n" + +while read -r LINE; do + CLIENT_NAME="$(awk '{print $1}' <<< "$LINE")" + + CREATION_DATE="$(awk '{print $2}' <<< "$LINE")" + + # Dates are converted from UNIX time to human readable. + CD_FORMAT="$(date -d @"$CREATION_DATE" +'%d %b %Y, %H:%M, %Z')" + + FORMATTED+="• $CLIENT_NAME&$CD_FORMAT\n" +done < clients.txt + +echo -e "$FORMATTED" | column -t -s '&' \ No newline at end of file diff --git a/scripts/wireguard/makeCONF.sh b/scripts/wireguard/makeCONF.sh new file mode 100755 index 0000000..cbfd9b4 --- /dev/null +++ b/scripts/wireguard/makeCONF.sh @@ -0,0 +1,130 @@ +#!/bin/bash + +setupVars="/etc/pivpn/setupVars.conf" + +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 +done + +if [ ! -f "${setupVars}" ]; then + echo "::: Missing setup vars file!" + exit 1 +fi + +source "${setupVars}" + +# 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" +fi + +cd /etc/wireguard + +if [ -z "${CLIENT_NAME}" ]; then + read -r -p "Enter a Name for the Client: " CLIENT_NAME +fi + +if [[ "${CLIENT_NAME}" =~ [^a-zA-Z0-9.@_-] ]]; then + echo "Name can only contain alphanumeric characters and these characters (.-@_)." + 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" +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} $(date +%s) ${COUNT}" >> configs/clients.txt + break + fi +done + +echo -n "[Interface] +PrivateKey = $(cat "keys/${CLIENT_NAME}_priv") +Address = 10.6.0.${COUNT}/24 +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" + +echo "[Peer] +PublicKey = $(cat keys/server_pub) +PresharedKey = $(cat keys/psk) +Endpoint = ${pivpnHOST}:${pivpnPORT} +AllowedIPs = 0.0.0.0/0" >> "configs/${CLIENT_NAME}.conf" +echo "::: Client config generated" + +echo "# begin ${CLIENT_NAME} +[Peer] +PublicKey = $(cat "keys/${CLIENT_NAME}_pub") +PresharedKey = $(cat keys/psk) +AllowedIPs = 10.6.0.${COUNT}/32 +# end ${CLIENT_NAME}" >> wg0.conf +echo "::: Updated server config" + +if systemctl restart wg-quick@wg0; then + echo "::: WireGuard restarted" +else + echo "::: Failed to restart WireGuard" +fi + +cp "configs/${CLIENT_NAME}.conf" "${install_home}/configs/${CLIENT_NAME}.conf" +chown "${install_user}":"${install_user}" "${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 "::: 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." +echo "======================================================================" diff --git a/scripts/wireguard/pivpn b/scripts/wireguard/pivpn new file mode 100755 index 0000000..f4bcfd9 --- /dev/null +++ b/scripts/wireguard/pivpn @@ -0,0 +1,83 @@ +#!/bin/bash + +# Must be root to use this tool +if [ $EUID -ne 0 ];then + if dpkg-query -s sudo &> /dev/null; then + export SUDO="sudo" + else + echo "::: Please install sudo or run this as root." + exit 1 + fi +fi + +makeConf(){ + shift + $SUDO /opt/pivpn/makeCONF.sh "$@" + exit 1 +} + +listConnected(){ + $SUDO wg show + exit 1 +} + +debug(){ + $SUDO /opt/pivpn/pivpnDEBUG.sh + exit 1 +} + +listClients(){ + $SUDO /opt/pivpn/listCONF.sh + exit 1 +} + +showQrcode(){ + shift + $SUDO /opt/pivpn/qrcodeCONF.sh "$@" + exit 1 +} + +removeClient(){ + shift + $SUDO /opt/pivpn/removeCONF.sh "$@" + exit 1 +} + +uninstallServer(){ + $SUDO /opt/pivpn/uninstall.sh + exit 1 +} + +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 "::: -h, help Show this help dialog" + echo "::: -u, uninstall Uninstall pivpn from your system!" + exit 1 +} + +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 "$@";; +"-h" | "help" ) showHelp;; +"-u" | "uninstall" ) uninstallServer;; +* ) showHelp;; +esac diff --git a/scripts/wireguard/pivpnDEBUG.sh b/scripts/wireguard/pivpnDEBUG.sh new file mode 100755 index 0000000..761cc49 --- /dev/null +++ b/scripts/wireguard/pivpnDEBUG.sh @@ -0,0 +1,173 @@ +#!/usr/bin/env bash +# This scripts runs as root + +setupVars="/etc/pivpn/setupVars.conf" + +if [ ! -f "${setupVars}" ]; then + echo "::: Missing setup vars file!" + exit 1 +fi + +source "${setupVars}" + +EXAMPLE="$(head -1 /etc/wireguard/configs/clients.txt | awk '{print $1}')" +ERR=0 + +echo -e "::::\t\t\e[4mPiVPN debug\e[0m\t\t ::::" +printf "=============================================\n" +echo -e "::::\t\t\e[4mLatest commit\e[0m\t\t ::::" +git --git-dir /etc/.pivpn/.git log -n 1 +printf "=============================================\n" +echo -e "::::\t \e[4mInstallation settings\e[0m \t ::::" +sed "s/$pivpnHOST/REDACTED/" < /etc/pivpn/setupVars.conf +printf "=============================================\n" +echo -e ":::: \e[4mServer configuration shown below\e[0m ::::" +cd /etc/wireguard/keys +cp ../wg0.conf ../wg0.tmp +for k in *; do + sed "s#$(cat "$k")#$k#" -i ../wg0.tmp +done +cat ../wg0.tmp +rm ../wg0.tmp +printf "=============================================\n" +echo -e ":::: \e[4mClient configuration shown below\e[0m ::::" +if [ -n "$EXAMPLE" ]; then + cp ../configs/"$EXAMPLE".conf ../configs/"$EXAMPLE".tmp + for k in *; do + sed "s#$(cat "$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" +fi + +printf "=============================================\n" +echo -e ":::: \t\e[4mRecursive list of files in\e[0m\t ::::\n::::\e\t[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 ::::" + +if [ "$(cat /proc/sys/net/ipv4/ip_forward)" -eq 1 ]; then + echo ":: [OK] IP forwarding is enabled" +else + ERR=1 + read -r -p ":: [ERR] IP forwarding is not enabled, attempt fix now? [Y/n] " REPLY + if [[ ${REPLY} =~ ^[Yy]$ ]]; then + sed -i '/net.ipv4.ip_forward=1/s/^#//g' /etc/sysctl.conf + sysctl -p + echo "Done" + fi +fi + +if [ "$USING_UFW" -eq 0 ]; then + + if iptables -t nat -C POSTROUTING -s 10.6.0.0/24 -o "${PHYS_INT}" -j MASQUERADE &> /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]$ ]]; then + iptables -t nat -F + iptables -t nat -I POSTROUTING -s 10.6.0.0/24 -o "${PHYS_INT}" -j MASQUERADE + iptables-save > /etc/iptables/rules.v4 + iptables-restore < /etc/iptables/rules.v4 + echo "Done" + fi + fi + +else + + 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]$ ]]; then + ufw enable + fi + fi + + if iptables -t nat -C POSTROUTING -s 10.6.0.0/24 -o "${PHYS_INT}" -j MASQUERADE &> /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]$ ]]; then + sed "/delete these required/i *nat\n:POSTROUTING ACCEPT [0:0]\n-I POSTROUTING -s 10.6.0.0/24 -o $PHYS_INT -j MASQUERADE\nCOMMIT\n" -i /etc/ufw/before.rules + ufw reload + echo "Done" + fi + fi + + if iptables -C ufw-user-input -p udp --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]$ ]]; then + ufw insert 1 allow "$pivpnPORT"/udp + ufw reload + echo "Done" + fi + fi + + if iptables -C ufw-user-forward -i wg0 -o "${PHYS_INT}" -s 10.6.0.0/24 -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]$ ]]; then + ufw route insert 1 allow in on wg0 from 10.6.0.0/24 out on "$PHYS_INT" to any + ufw reload + echo "Done" + fi + fi + +fi + +if systemctl is-active -q wg-quick@wg0; then + echo ":: [OK] WireGuard is running" +else + ERR=1 + read -r -p ":: [ERR] WireGuard is not running, try to start now? [Y/n] " REPLY + if [[ ${REPLY} =~ ^[Yy]$ ]]; then + systemctl start wg-quick@wg0 + echo "Done" + fi +fi + +if systemctl is-enabled -q wg-quick@wg0; then + echo ":: [OK] WireGuard is enabled (it will automatically start on reboot)" +else + ERR=1 + read -r -p ":: [ERR] WireGuard is not enabled, try to enable now? [Y/n] " REPLY + if [[ ${REPLY} =~ ^[Yy]$ ]]; then + systemctl enable wg-quick@wg0 + echo "Done" + fi +fi + +# grep -w (whole word) is used so port 111940 with now match when looking for 1194 +if netstat -uanp | grep -w "${pivpnPORT}" | grep -q 'udp'; then + echo ":: [OK] WireGuard is listening on port ${pivpnPORT}/udp" +else + ERR=1 + read -r -p ":: [ERR] WireGuard is not listening, try to restart now? [Y/n] " REPLY + if [[ ${REPLY} =~ ^[Yy]$ ]]; then + systemctl restart wg-quick@wg0 + 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" +fi +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 ":::: WJhKKx+Uk1l1TxaH2KcEGeBdPBTp/k/Qy4EpBig5UnI= ::::" +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 new file mode 100755 index 0000000..40fce8f --- /dev/null +++ b/scripts/wireguard/qrcodeCONF.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +helpFunc(){ + echo "::: Show the qrcode of a client for use with the mobile app" + echo ":::" + echo "::: Usage: pivpn <-qr|qrcode> [-h|--help] [] ... [] ..." + echo ":::" + echo "::: Commands:" + echo "::: [none] Interactive mode" + echo "::: Client(s) to show" + 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 + ;; + *) + CLIENTS_TO_SHOW+=("$1") + ;; + esac + shift +done + +cd /etc/wireguard/configs +if [ ! -s clients.txt ]; then + echo "::: There are no clients to remove" + exit 1 +fi + +if [ "${#CLIENTS_TO_SHOW[@]}" -eq 0 ]; then + + echo -e "::\e[4m Client list \e[0m::" + LIST=($(awk '{print $1}' clients.txt)) + COUNTER=1 + while [ $COUNTER -le ${#LIST[@]} ]; do + echo "• ${LIST[(($COUNTER-1))]}" + ((COUNTER++)) + done + + read -r -p "Please enter the Name of the Client to show: " CLIENTS_TO_SHOW + + if [ -z "${CLIENTS_TO_SHOW}" ]; then + echo "::: You can not leave this blank!" + exit 1 + fi +fi + +for CLIENT_NAME in "${CLIENTS_TO_SHOW[@]}"; do + if grep -qw "${CLIENT_NAME}" clients.txt; then + echo -e "::: Showing client \e[1m${CLIENT_NAME}\e[0m below" + echo "=====================================================================" + qrencode -t ansiutf8 < "${CLIENT_NAME}.conf" + echo "=====================================================================" + else + echo -e "::: \e[1m${CLIENT_NAME}\e[0m does not exist" + fi +done \ No newline at end of file diff --git a/scripts/wireguard/removeCONF.sh b/scripts/wireguard/removeCONF.sh new file mode 100755 index 0000000..403da3c --- /dev/null +++ b/scripts/wireguard/removeCONF.sh @@ -0,0 +1,117 @@ +#!/bin/bash + +setupVars="/etc/pivpn/setupVars.conf" + +helpFunc(){ + echo "::: Remove a client conf profile" + echo ":::" + echo "::: Usage: pivpn <-r|remove> [-h|--help] [] ... [] ..." + echo ":::" + echo "::: Commands:" + echo "::: [none] Interactive mode" + echo "::: Client(s) to remove" + 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 + ;; + *) + CLIENTS_TO_REMOVE+=("$1") + ;; + esac + shift +done + +if [ ! -f "${setupVars}" ]; then + echo "::: Missing setup vars file!" + exit 1 +fi + +source "${setupVars}" + +cd /etc/wireguard +if [ ! -s configs/clients.txt ]; then + echo "::: There are no clients to remove" + exit 1 +fi + +if [ "${#CLIENTS_TO_REMOVE[@]}" -eq 0 ]; then + + echo -e "::\e[4m Client list \e[0m::" + LIST=($(awk '{print $1}' configs/clients.txt)) + COUNTER=1 + while [ $COUNTER -le ${#LIST[@]} ]; do + echo "• ${LIST[(($COUNTER-1))]}" + ((COUNTER++)) + done + + read -r -p "Please enter the Name of the Client to be removed from the list above: " CLIENTS_TO_REMOVE + + if [ -z "${CLIENTS_TO_REMOVE}" ]; then + echo "::: You can not leave this blank!" + exit 1 + fi +fi + +DELETED_COUNT=0 + +for CLIENT_NAME in "${CLIENTS_TO_REMOVE[@]}"; do + + if ! grep -qw "${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)" + read -r -p "Do you really want to delete $CLIENT_NAME? [Y/n] " + + if [[ $REPLY =~ ^[Yy]$ ]]; then + + # Grab the least significant octed of the client IP address + COUNT=$(grep "${CLIENT_NAME}" configs/clients.txt | awk '{print $3}') + # And the creation date of the client + CREATION_DATE="$(grep "${CLIENT_NAME}" configs/clients.txt | awk '{print $2}')" + + # Then remove the client matching the variables above + sed "/${CLIENT_NAME} ${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" + 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. + 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 + + echo "::: Successfully deleted ${CLIENT_NAME}" + + ((DELETED_COUNT++)) + fi + fi + +done + +# Restart WireGuard only if some clients were actually deleted +if [ "${DELETED_COUNT}" -gt 0 ]; then + if systemctl restart wg-quick@wg0; then + echo "::: WireGuard restarted" + else + echo "::: Failed to restart WireGuard" + fi +fi