Merge pull request #1572 from giulio-coa/formatting_enhancements

Reformatted the code according to google bash code style.
This commit is contained in:
4s3ti 2022-08-12 22:37:37 +02:00 committed by GitHub
commit f59c693ba6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 5861 additions and 4353 deletions

View file

@ -3,15 +3,13 @@ language: shell
addons: addons:
hosts: hosts:
- pivpn.test - pivpn.test
apt:
packages:
- shellcheck
services: services:
- docker - docker
stages: stages:
- lint - lint
- style
- test - test
branches: branches:
@ -24,11 +22,29 @@ jobs:
- stage: lint - stage: lint
name: "Shellcheck lint" name: "Shellcheck lint"
dist: focal dist: focal
addons:
apt:
packages:
- shellcheck
virt: vm virt: vm
arch: amd64 arch: amd64
script: script:
- shellcheck auto_install/install.sh - shellcheck -x auto_install/install.sh
- find scripts/ -type f | xargs shellcheck - find ciscripts/ -type f -regex '.*\.sh' -exec shellcheck -x {} \;
- find scripts/ -type f -exec shellcheck -x {} \;
- stage: style
name: "Shell Style Guide"
dist: xenial
addons:
snaps:
- shfmt
virt: vm
arch: amd64
script:
- shfmt -d -i 2 -ci -sr -bn auto_install/install.sh
- find ciscripts/ -type f -regex '.*\.sh' -exec shfmt -d -i 2 -ci -sr -bn {} \;
- find scripts/ -type f -exec shfmt -d -i 2 -ci -sr -bn {} \;
- stage: test - stage: test
name: "OpenVPN Xenial & Commands" name: "OpenVPN Xenial & Commands"

View file

@ -22,6 +22,11 @@ Please report unacceptable behavior to any project maintainer.
* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
* Use the following [commit rules](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format).
* Use the following [code style rules](https://google.github.io/styleguide/shellguide.html).
We suggest you to use `shfmt` with the options `-i 2 -ci -sr -w -bn`
### **PiVPN Website** ### **PiVPN Website**
* Yes the site is open source as well! You can find and contribute to [pivpn.io](https://github.com/pivpn/pivpn.io) directly. * Yes the site is open source as well! You can find and contribute to [pivpn.io](https://github.com/pivpn/pivpn.io) directly.

File diff suppressed because it is too large Load diff

View file

@ -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) interface=$(ip -o link \
ipaddress=$(ip addr show "$interface" | grep -o -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}/[0-9]{2}") | 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}') gateway=$(ip route show | awk '/default/ {print $3}')
hostname="pivpn.test" hostname="pivpn.test"
common(){ err() {
sed -i "s/INTERFACE/$interface/g" "$vpnconfig" echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
sed -i "s|IPADDRESS|$ipaddress|g" "$vpnconfig"
sed -i "s/GATEWAY/$gateway/g" "$vpnconfig"
} }
openvpn(){ common() {
vpnconfig="ciscripts/ci_openvpn.conf" sed -i "s/INTERFACE/${interface}/g" "${vpnconfig}"
twofour=1 sed -i "s|IPADDRESS|${ipaddress}|g" "${vpnconfig}"
common sed -i "s/GATEWAY/${gateway}/g" "${vpnconfig}"
sed -i "s/2POINT4/$twofour/g" "$vpnconfig"
cat $vpnconfig
exit 0
} }
wireguard(){ openvpn() {
vpnconfig="ciscripts/ci_wireguard.conf" vpnconfig="ciscripts/ci_openvpn.conf"
common twofour=1
cat $vpnconfig common
exit 0 sed -i "s/2POINT4/${twofour}/g" "${vpnconfig}"
cat "${vpnconfig}"
exit 0
} }
if [ $# -lt 1 ]; then wireguard() {
echo "specifiy a VPN protocol to prepare" vpnconfig="ciscripts/ci_wireguard.conf"
exit 1 common
cat "${vpnconfig}"
exit 0
}
if [[ "$#" -lt 1 ]]; then
err "specifiy a VPN protocol to prepare"
exit 1
else else
chmod +x auto_install/install.sh chmod +x auto_install/install.sh
sudo hostnamectl set-hostname $hostname sudo hostnamectl set-hostname "${hostname}"
cat /etc/os-release cat /etc/os-release
while true; do
case "$1" in while true; do
-o | --openvpn ) openvpn case "${1}" in
;; -o | --openvpn)
-w | --wireguard ) wireguard openvpn
;; ;;
* ) echo "unknown vpn protocol"; exit 1 -w | --wireguard)
;; wireguard
esac ;;
done *)
err "unknown vpn protocol"
exit 1
;;
esac
done
fi fi

View file

@ -1,10 +1,11 @@
#!/bin/bash #!/bin/bash
if command -v systemctl > /dev/null; then if command -v systemctl > /dev/null; then
systemctl status openvpn systemctl status openvpn
elif command -v rc-service > /dev/null; then elif command -v rc-service > /dev/null; then
rc-service openvpn status rc-service openvpn status
fi fi
pivpn add -n foo pivpn add -n foo
pivpn -qr foo pivpn -qr foo
pivpn -bk pivpn -bk

View file

@ -2,111 +2,125 @@
# PiVPN: Backup Script # PiVPN: Backup Script
# Find the rows and columns. Will default to 80x24 if it can not be detected. # 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) screen_size="$(stty size 2> /dev/null || echo 24 80)"
rows=$(echo "$screen_size" | awk '{print $1}') rows="$(echo "${screen_size}" | awk '{print $1}')"
columns=$(echo "$screen_size" | awk '{print $2}') columns="$(echo "${screen_size}" | awk '{print $2}')"
# Divide by two so the dialogs take up half of the screen, which looks nice. # Divide by two so the dialogs take up half of the screen, which looks nice.
r=$(( rows / 2 )) r=$((rows / 2))
c=$(( columns / 2 )) c=$((columns / 2))
# Unless the screen is tiny # Unless the screen is tiny
r=$(( r < 20 ? 20 : r )) r=$((r < 20 ? 20 : r))
c=$(( c < 70 ? 70 : c )) c=$((c < 70 ? 70 : c))
backupdir=pivpnbackup backupdir=pivpnbackup
date=$(date +%Y%m%d-%H%M%S) date="$(date +%Y%m%d-%H%M%S)"
setupVarsFile="setupVars.conf" setupVarsFile="setupVars.conf"
setupConfigDir="/etc/pivpn" setupConfigDir="/etc/pivpn"
CHECK_PKG_INSTALLED='dpkg-query -s' 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 if VPN="$("${chooseVPNCmd[@]}" "${VPNChooseOptions[@]}" 2>&1 \
# an argument, otherwise ask the user which one he wants to remove > /dev/tty)"; then
if [ $# -ge 1 ]; then echo "::: Backing up VPN: ${VPN}"
VPN="$1" VPN="${VPN,,}"
echo "::: Backing up VPN: $VPN" else
else err "::: Cancel selected, exiting...."
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) exit 1
VPNChooseOptions=(WireGuard "" on fi
OpenVPN "" off) fi
if VPN=$("${chooseVPNCmd[@]}" "${VPNChooseOptions[@]}" 2>&1 >/dev/tty) ; then setupVars="${setupConfigDir}/${VPN}/${setupVarsFile}"
echo "::: Backing up VPN: $VPN"
VPN="${VPN,,}"
else
echo "::: Cancel selected, exiting...."
exit 1
fi
fi
setupVars="${setupConfigDir}/${VPN}/${setupVarsFile}"
else else
if [[ -r "${setupConfigDir}/wireguard/${setupVarsFile}" ]]; then
if [ -r "${setupConfigDir}/wireguard/${setupVarsFile}" ]; then setupVars="${setupConfigDir}/wireguard/${setupVarsFile}"
setupVars="${setupConfigDir}/wireguard/${setupVarsFile}" elif [[ -r "${setupConfigDir}/openvpn/${setupVarsFile}" ]]; then
elif [ -r "${setupConfigDir}/openvpn/${setupVarsFile}" ]; then setupVars="${setupConfigDir}/openvpn/${setupVarsFile}"
setupVars="${setupConfigDir}/openvpn/${setupVarsFile}" fi
fi
fi fi
if [ ! -f "${setupVars}" ]; then if [[ ! -f "${setupVars}" ]]; then
echo "::: Missing setup vars file!" err "::: Missing setup vars file!"
exit 1 exit 1
fi fi
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "${setupVars}" source "${setupVars}"
if [ "${PLAT}" == 'Alpine' ]; then if [[ "${PLAT}" == 'Alpine' ]]; then
CHECK_PKG_INSTALLED='apk --no-cache info -e' CHECK_PKG_INSTALLED='apk --no-cache info -e'
fi fi
checkbackupdir(){ err() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
# Disabling shellcheck error $install_home sourced from $setupVars
# shellcheck disable=SC2154
if [[ ! -d $install_home/$backupdir ]]; then
mkdir -p "$install_home"/"$backupdir"
fi
} }
backup_openvpn(){ checkbackupdir() {
# Disabling shellcheck error $install_home sourced from $setupVars
openvpndir=/etc/openvpn # shellcheck disable=SC2154
ovpnsdir=${install_home}/ovpns mkdir -p "${install_home}/${backupdir}"
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"
} }
backup_wireguard(){ backup_openvpn() {
openvpndir=/etc/openvpn
ovpnsdir="${install_home}/ovpns"
backupzip="${date}-pivpnovpnbackup.tgz"
wireguarddir=/etc/wireguard checkbackupdir
configsdir=${install_home}/configs # shellcheck disable=SC2210
checkbackupdir tar czpf "${install_home}/${backupdir}/${backupzip}" "${openvpndir}" \
backupzip=$date-pivpnwgbackup.tgz "${ovpnsdir}" > /dev/null 2>&1
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"
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 backup_wireguard() {
if eval "${CHECK_PKG_INSTALLED} sudo" &> /dev/null; then wireguarddir=/etc/wireguard
export SUDO="sudo" configsdir="${install_home}/configs"
else backupzip="${date}-pivpnwgbackup.tgz"
echo "::: Please install sudo or run this as root."
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 exit 1
fi fi
fi fi
if [[ "${VPN}" == "wireguard" ]]; then if [[ "${VPN}" == "wireguard" ]]; then
backup_wireguard backup_wireguard
else else
backup_openvpn backup_openvpn
fi fi

View file

@ -1,21 +1,25 @@
#!/bin/bash #!/bin/bash
_pivpn()
{ _pivpn() {
local cur prev opts local cur prev opts
COMPREPLY=() COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}" cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}" prev="${COMP_WORDS[COMP_CWORD - 1]}"
dashopts="-a -c -d -l -r -h -u -up -bk" dashopts="-a -c -d -l -r -h -u -up -bk"
opts="debug add clients list revoke uninstall help update backup" opts="debug add clients list revoke uninstall help update backup"
if [ "${#COMP_WORDS[@]}" -eq 2 ]; then
if [[ ${cur} == -* ]] ; then if [[ "${#COMP_WORDS[@]}" -eq 2 ]]; then
COMPREPLY=( "$(compgen -W "${dashopts}" -- "${cur}")" ) if [[ "${cur}" == -* ]]; then
else COMPREPLY=("$(compgen -W "${dashopts}" -- "${cur}")")
COMPREPLY=( "$(compgen -W "${opts}" -- "${cur}")" ) else
fi COMPREPLY=("$(compgen -W "${opts}" -- "${cur}")")
elif [[ ( "$prev" == "add" || "$prev" == "-a" ) && "${#COMP_WORDS[@]}" -eq 3 ]]; then
COMPREPLY=( "$(compgen -W "nopass" -- "${cur}")" )
fi 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 complete -F _pivpn pivpn

View file

@ -1,84 +1,97 @@
#!/usr/bin/env bash #!/bin/bash
# PiVPN: client status script # PiVPN: client status script
STATUS_LOG="/var/log/openvpn-status.log" STATUS_LOG="/var/log/openvpn-status.log"
if [ ! -f "${STATUS_LOG}" ]; then if [[ ! -f "${STATUS_LOG}" ]]; then
echo "The file: $STATUS_LOG was not found!" err "The file: ${STATUS_LOG} was not found!"
exit 1 exit 1
fi fi
scriptusage(){ err() {
echo "::: List any connected clients to the server" echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
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(){ scriptusage() {
numfmt --to=iec-i --suffix=B "$1" 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(){ listClients() {
printf ": NOTE : The output below is NOT real-time!\n" printf ": NOTE : The output below is NOT real-time!\n"
printf ": : It may be off by a few minutes.\n" printf ": : It may be off by a few minutes.\n"
printf "\n" printf "\n"
printf "\e[1m::: Client Status List :::\e[0m\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 grep -q "^CLIENT_LIST" "${STATUS_LOG}"; then
if [ -n "$(type -t numfmt)" ]; then if [[ -n "$(type -t numfmt)" ]]; then
if [ "$HR" = 1 ]; then while read -r line; do
while read -r line; do read -r -a array <<< "${line}"
read -r -a array <<< "$line"
[[ ${array[0]} = CLIENT_LIST ]] || continue [[ "${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 printf "%s \t %s \t " "${array[1]}" "${array[2]}"
else printf "%s \t " "${array[3]}"
while read -r line; do
read -r -a array <<< "$line" if [[ "${HR}" == 1 ]]; then
[[ ${array[0]} = CLIENT_LIST ]] || continue printf "%s \t %s" "$(hr "${array[4]}")" "$(hr "${array[5]}")"
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]}" else
done <$STATUS_LOG printf "%'d \t %'d" "${array[4]}" "${array[5]}"
fi 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} printf " \t %s %s %s " "${array[7]}" "${array[8]}" "${array[10]}"
fi 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 else
printf "\nNo Clients Connected!\n" printf "\nNo Clients Connected!\n"
fi fi
printf "\n" printf "\n"
} | column -t -s $'\t' } | column -t -s $'\t'
} }
if [[ $# -eq 0 ]]; then if [[ "$#" -eq 0 ]]; then
HR=1 HR=1
listClients listClients
else else
while true; do while true; do
case "$1" in case "${1}" in
-b|bytes) -b | bytes)
HR=0 HR=0
listClients listClients
exit 0 exit 0
;; ;;
-h|help) -h | help)
scriptusage scriptusage
exit 0 exit 0
;; ;;
*) *)
HR=0 HR=0
listClients listClients
exit 0 exit 0
;; ;;
esac esac
done done
fi fi

View file

@ -1,42 +1,57 @@
#!/usr/bin/env bash #!/bin/bash
# PiVPN: list clients script # 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" INDEX="/etc/openvpn/easy-rsa/pki/index.txt"
if [ ! -f "${INDEX}" ]; then
echo "The file: $INDEX was not found!" err() {
exit 1 echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
}
if [[ ! -f "${INDEX}" ]]; then
err "The file: ${INDEX} was not found!"
exit 1
fi fi
EASYRSA="/etc/openvpn/easy-rsa/easyrsa" EASYRSA="/etc/openvpn/easy-rsa/easyrsa"
if [ ! -f "${EASYRSA}" ]; then
echo "The file: $EASYRSA was not found!" if [[ ! -f "${EASYRSA}" ]]; then
exit 1 err "The file: ${EASYRSA} was not found!"
exit 1
fi 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 "\\n"
printf "\\e[1m::: Certificate Status List :::\\e[0m\\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 while read -r line || [[ -n "${line}" ]]; do
STATUS=$(echo "$line" | awk '{print $1}') STATUS="$(echo "${line}" | awk '{print $1}')"
NAME=$(echo "$line" | awk -FCN= '{print $2}') 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 -) 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 if [[ "${STATUS}" == "V" ]]; then
printf "Valid \t %s \t %s\\n" "$(echo -e "$NAME")" "$EXPD" printf "Valid"
elif [ "${STATUS}" == "R" ]; then elif [[ "${STATUS}" == "R" ]]; then
printf "Revoked \t %s \t %s\\n" "$(echo -e "$NAME")" "$EXPD" printf "Revoked"
elif [ "${STATUS}" == "E" ]; then elif [[ "${STATUS}" == "E" ]]; then
printf "Expired \t %s \t %s\\n" "$(echo -e "$NAME")" "$EXPD" printf "Expired"
else else
printf "Unknown \t %s \t %s\\n" "$(echo -e "$NAME")" "$EXPD" printf "Unknown"
fi fi
done <${INDEX} printf " \t %s \t %s\\n" "$(echo -e "${NAME}")" "${EXPD}"
printf "\\n" done < "${INDEX}"
printf "\\n"
} | column -t -s $'\t' } | column -t -s $'\t'

View file

@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
# Create OVPN Client # Create OVPN Client
# Default Variable Declarations # Default Variable Declarations
setupVars="/etc/pivpn/openvpn/setupVars.conf" setupVars="/etc/pivpn/openvpn/setupVars.conf"
@ -10,429 +11,497 @@ CA="ca.crt"
TA="ta.key" TA="ta.key"
INDEX="/etc/openvpn/easy-rsa/pki/index.txt" INDEX="/etc/openvpn/easy-rsa/pki/index.txt"
if [ ! -f "${setupVars}" ]; then if [[ ! -f "${setupVars}" ]]; then
echo "::: Missing setup vars file!" err "::: Missing setup vars file!"
exit 1 exit 1
fi fi
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "${setupVars}" source "${setupVars}"
helpFunc() { err() {
echo "::: Create a client ovpn profile, optional nopass" echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
echo ":::"
echo "::: Usage: pivpn <-a|add> [-n|--name <arg>] [-p|--password <arg>]|[nopass] [-d|--days <number>] [-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"
} }
if [ -z "$HELP_SHOWN" ]; then helpFunc() {
helpFunc echo "::: Create a client ovpn profile, optional nopass"
echo echo ":::"
echo "HELP_SHOWN=1" >> "$setupVars" echo -n "::: Usage: pivpn <-a|add> [-n|--name <arg>] "
echo -n "[-p|--password <arg>]|[nopass] [-d|--days <number>] "
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 fi
# Parse input arguments # Parse input arguments
while test $# -gt 0; do while [[ "$#" -gt 0 ]]; do
_key="$1" _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'
if [ "${PLAT}" == 'Alpine' ]; then case "${_key}" in
echo 'You can download it through the following commands:' -n | --name | --name=*)
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' _val="${_key##--name=}"
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
exit 1 if [[ "${_val}" == "${_key}" ]]; then
fi [[ "$#" -lt 2 ]] \
&& err "Missing value for the optional argument '${_key}'." \
&& exit 1
;; _val="${2}"
-o|--ovpn) shift
GENOVPNONLY=1 fi
;;
*) NAME="${_val}"
echo "Error: Got an unexpected argument '$1'" ;;
helpFunc -p | --password | --password=*)
exit 1 _val="${_key##--password=}"
;;
esac if [[ "${_val}" == "${_key}" ]]; then
shift [[ "$#" -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 done
# Functions def # Functions def
function keynoPASS() { keynoPASS() {
# Build the client key
#Build the client key expect << EOF
expect << EOF
set timeout -1 set timeout -1
set env(EASYRSA_CERT_EXPIRE) "${DAYS}" set env(EASYRSA_CERT_EXPIRE) "${DAYS}"
spawn ./easyrsa build-client-full "${NAME}" nopass spawn ./easyrsa build-client-full "${NAME}" nopass
expect eof expect eof
EOF EOF
cd pki || exit
cd pki || exit
} }
function useBitwarden() { useBitwarden() {
# login and unlock vault
printf "****Bitwarden Login****"
printf "\n"
# login and unlock vault SESSION_KEY="$(bw login --raw)"
printf "****Bitwarden Login****" export BW_SESSION="${SESSION_KEY}"
printf "\n"
SESSION_KEY=$(bw login --raw)
export BW_SESSION=$SESSION_KEY
printf "Successfully Logged in!"
printf "\n"
# ask user for username printf "Successfully Logged in!"
printf "Enter the username: " 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 read -r NAME
done
# check name # ask user for length of password
until [[ "$NAME" =~ ^[a-zA-Z0-9.@_-]+$ && ${NAME::1} != "." && ${NAME::1} != "-" ]]; do printf "Please enter the length of characters you want your password to be "
echo "Name can only contain alphanumeric characters and these characters (.-@_). The name also cannot start with a dot (.) or a dash (-). Please try again." printf "(minimum 12): "
# ask user for username again read -r LENGTH
printf "Enter the username: "
read -r NAME
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 # 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 read -r LENGTH
done
# check length printf "Creating a PiVPN item for your vault..."
until [[ "$LENGTH" -gt 11 && "$LENGTH" -lt 129 ]]; do printf "\n"
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
# 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 if [[ -z "${PASSWD}" ]]; then
stty -echo err "You left the password blank"
while true; do err "If you don't want a password, please run:"
printf "Enter the password for the client: " err "pivpn add nopass"
read -r PASSWD exit 1
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 fi
fi
#Escape chars in PASSWD if [[ "${#PASSWD}" -lt 4 ]] || [[ "${#PASSWD}" -gt 1024 ]]; then
PASSWD_UNESCAPED="${PASSWD}" err "Password must be between from 4 to 1024 characters"
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' -e 's/\}/\\\}/g') exit 1
fi
#Build the client key and then encrypt the key # Escape chars in PASSWD
PASSWD_UNESCAPED="${PASSWD}"
expect << EOF PASSWD="${PASSWD//\\/\\\\}"
set timeout -1 PASSWD="${PASSWD//\//\\\/}"
set env(EASYRSA_CERT_EXPIRE) "${DAYS}" PASSWD="${PASSWD//\$/\\\$}"
spawn ./easyrsa build-client-full "${NAME}" PASSWD="${PASSWD//!/\\!}"
expect "Enter PEM pass phrase" { sleep 0.1; send -- "${PASSWD}\r" } PASSWD="${PASSWD//\./\\\.}"
expect "Verifying - Enter PEM pass phrase" { sleep 0.1; send -- "${PASSWD}\r" } PASSWD="${PASSWD//\'/\\\'}"
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//\}/\\\}}"
# 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 EOF
cd pki || exit cd pki || exit
} }
#make sure ovpns dir exists #make sure ovpns dir exists
# Disabling warning for SC2154, var sourced externaly # Disabling warning for SC2154, var sourced externaly
# shellcheck disable=SC2154 # shellcheck disable=SC2154
if [ ! -d "$install_home/ovpns" ]; then if [[ ! -d "${install_home}/ovpns" ]]; then
mkdir "$install_home/ovpns" mkdir "${install_home}/ovpns"
chown "$install_user":"$install_user" "$install_home/ovpns" chown "${install_user}:${install_user}" "${install_home}/ovpns"
chmod 0750 "$install_home/ovpns" chmod 0750 "${install_home}/ovpns"
fi fi
#bitWarden #bitWarden
if [[ "${BITWARDEN}" =~ "2" ]]; then if [[ "${BITWARDEN}" =~ "2" ]]; then
useBitwarden 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
fi fi
if [[ -z "${NAME}" ]]; then if [[ -z "${NAME}" ]]; then
echo "You cannot leave the name blank." printf "Enter a Name for the Client: "
exit 1 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 fi
if [ "${GENOVPNONLY}" == "1" ]; then if [[ "${GENOVPNONLY}" == 1 ]]; then
# Generate .ovpn configuration file # Generate .ovpn configuration file
cd /etc/openvpn/easy-rsa/pki || exit cd /etc/openvpn/easy-rsa/pki || exit
else else
# Check if name is already in use # Check if name is already in use
while read -r line || [ -n "${line}" ]; do while read -r line || [[ -n "${line}" ]]; do
STATUS=$(echo "$line" | awk '{print $1}') STATUS=$(echo "${line}" | awk '{print $1}')
if [ "${STATUS}" == "V" ]; then if [[ "${STATUS}" == "V" ]]; then
# Disabling SC2001 as ${variable//search/replace} doesn't go well with regexp # Disabling SC2001 as ${variable//search/replace}
# shellcheck disable=SC2001 # doesn't go well with regexp
CERT=$(echo "$line" | sed -e 's:.*/CN=::') # shellcheck disable=SC2001
if [ "${CERT}" == "${NAME}" ]; then CERT="$(echo "${line}" | sed -e 's:.*/CN=::')"
INUSE="1"
break
fi
fi
done <${INDEX}
if [ "${INUSE}" == "1" ]; then if [[ "${CERT}" == "${NAME}" ]]; then
printf "\n!! This name is already in use by a Valid Certificate." INUSE="1"
printf "\nPlease choose another name or revoke this certificate first.\n" break
exit 1 fi
fi fi
done < "${INDEX}"
# Check if name is reserved if [[ "${INUSE}" == 1 ]]; then
if [ "${NAME}" == "ta" ] || [ "${NAME}" == "server" ] || [ "${NAME}" == "ca" ]; then err "!! This name is already in use by a Valid Certificate."
echo "Sorry, this is in use by the server and cannot be used by clients." err "Please choose another name or revoke this certificate first."
exit 1 exit 1
fi # 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 # As of EasyRSA 3.0.6, by default certificates last 1080 days,
if [ -z "${DAYS}" ]; then # see https://github.com/OpenVPN/easy-rsa/blob/6b7b6bf1f0d3c9362b5618ad18c66677351cacd1/easyrsa3/vars.example
read -r -e -p "How many days should the certificate last? " -i 1080 DAYS if [[ -z "${DAYS}" ]]; then
fi 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 if [[ ! "${DAYS}" =~ ^[0-9]+$ ]] \
#The CRL lasts 3650 days so it doesn't make much sense that certificates would last longer || [[ "${DAYS}" -lt 1 ]] \
echo "Please input a valid number of days, between 1 and 3650 inclusive." || [[ "${DAYS}" -gt 3650 ]]; then
exit 1 # The CRL lasts 3650 days so it doesn't make much sense
fi # 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 [[ "${NO_PASS}" =~ "1" ]]; then
if [[ -n "${PASSWD}" ]]; then if [[ -n "${PASSWD}" ]]; then
echo "Both nopass and password arguments passed to the script. Please use either one." err "Both nopass and password arguments passed to the script. Please use either one."
exit 1 exit 1
else
keynoPASS
fi
else else
keyPASS keynoPASS
fi fi
else
keyPASS
fi
fi fi
#1st Verify that clients Public Key Exists #1st Verify that clients Public Key Exists
if [ ! -f "issued/${NAME}${CRT}" ]; then if [[ ! -f "issued/${NAME}${CRT}" ]]; then
echo "[ERROR]: Client Public Key Certificate not found: $NAME$CRT" err "[ERROR]: Client Public Key Certificate not found: ${NAME}${CRT}"
exit exit
fi 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 #Then, verify that there is a private key for that client
if [ ! -f "private/${NAME}${KEY}" ]; then if [[ ! -f "private/${NAME}${KEY}" ]]; then
echo "[ERROR]: Client Private Key not found: $NAME$KEY" err "[ERROR]: Client Private Key not found: ${NAME}${KEY}"
exit exit
fi fi
echo "Client's Private Key found: $NAME$KEY"
echo "Client's Private Key found: ${NAME}${KEY}"
#Confirm the CA public key exists #Confirm the CA public key exists
if [ ! -f "${CA}" ]; then if [[ ! -f "${CA}" ]]; then
echo "[ERROR]: CA Public Key not found: $CA" err "[ERROR]: CA Public Key not found: ${CA}"
exit exit
fi fi
echo "CA public Key found: $CA"
echo "CA public Key found: ${CA}"
#Confirm the tls key file exists #Confirm the tls key file exists
if [ ! -f "${TA}" ]; then if [[ ! -f "${TA}" ]]; then
echo "[ERROR]: tls Private Key not found: $TA" err "[ERROR]: tls Private Key not found: ${TA}"
exit exit
fi 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 ## 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/ ## 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 # Generates the .ovpn file WITHOUT the client private key
echo "<ca>" {
cat "${CA}" # Start by populating with the default file
echo "</ca>" cat "${DEFAULT}"
#Next append the client Public Cert # Now, append the CA Public Cert
echo "<cert>" echo "<ca>"
sed -n -e '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' < "issued/${NAME}${CRT}" cat "${CA}"
echo "</cert>" echo "</ca>"
#Finally, append the tls Private Key # Next append the client Public Cert
echo "<tls-auth>" echo "<cert>"
cat "${TA}" sed -n \
echo "</tls-auth>" -e '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' \
< "issued/${NAME}${CRT}"
echo "</cert>"
} > "${NAME}${FILEEXT}" if [[ "${iOS}" != 1 ]]; then
# Then, append the client Private Key
# 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 "<ca>"
cat "${CA}"
echo "</ca>"
#Next append the client Public Cert
echo "<cert>"
sed -n -e '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' < "issued/${NAME}${CRT}"
echo "</cert>"
#Then, append the client Private Key
echo "<key>" echo "<key>"
cat "private/${NAME}${KEY}" cat "private/${NAME}${KEY}"
echo "</key>" echo "</key>"
fi
#Finally, append the tls Private Key # Finally, append the tls Private Key
if [ "$TWO_POINT_FOUR" -eq 1 ]; then if [[ "${iOS}" != 1 ]] && [[ "${TWO_POINT_FOUR}" -eq 1 ]]; then
echo "<tls-crypt>" echo "<tls-crypt>"
cat "${TA}" cat "${TA}"
echo "</tls-crypt>" echo "</tls-crypt>"
else else
echo "<tls-auth>" echo "<tls-auth>"
cat "${TA}" cat "${TA}"
echo "</tls-auth>" echo "</tls-auth>"
fi 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 fi
cidrToMask(){ cidrToMask() {
# Source: https://stackoverflow.com/a/20767392 # Source: https://stackoverflow.com/a/20767392
set -- $(( 5 - ($1 / 8) )) 255 255 255 255 $(( (255 << (8 - ($1 % 8))) & 255 )) 0 0 0 set -- $((5 - (${1} / 8))) \
shift "$1" 255 255 255 255 \
echo "${1-0}"."${2-0}"."${3-0}"."${4-0}" $(((255 << (8 - (${1} % 8))) & 255)) \
0 0 0
shift "${1}"
echo "${1-0}.${2-0}.${3-0}.${4-0}"
} }
#disabling SC2514, variable sourced externaly #disabling SC2514, variable sourced externaly
@ -441,37 +510,44 @@ NET_REDUCED="${pivpnNET::-2}"
# Find an unused number for the last octet of the client IP # Find an unused number for the last octet of the client IP
for i in {2..254}; do for i in {2..254}; do
# find returns 0 if the folder is empty, so we create the 'ls -A [...]' # 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 # exception to stop at the first static IP (10.8.0.2). Otherwise it would
# cycle to the end without finding and available octet. # cycle to the end without finding and available octet.
# disabling SC2514, variable sourced externaly # disabling SC2514, variable sourced externaly
# shellcheck disable=SC2154 # shellcheck disable=SC2154
if [ -z "$(ls -A /etc/openvpn/ccd)" ] || ! find /etc/openvpn/ccd -type f -exec grep -q "${NET_REDUCED}.${i}" {} +; then if [[ -z "$(ls -A /etc/openvpn/ccd)" ]] \
COUNT="${i}" || ! find /etc/openvpn/ccd -type f \
echo "ifconfig-push ${NET_REDUCED}.${i} $(cidrToMask "$subnetClass")" >> /etc/openvpn/ccd/"${NAME}" -exec grep -q "${NET_REDUCED}.${i}" {} +; then
break COUNT="${i}"
fi echo -n "ifconfig-push ${NET_REDUCED}.${i}" >> /etc/openvpn/ccd/"${NAME}"
cidrToMask "${subnetClass}" >> /etc/openvpn/ccd/"${NAME}"
break
fi
done done
if [ -f /etc/pivpn/hosts.openvpn ]; then if [[ -f /etc/pivpn/hosts.openvpn ]]; then
echo "${NET_REDUCED}.${COUNT} ${NAME}.pivpn" >> /etc/pivpn/hosts.openvpn echo "${NET_REDUCED}.${COUNT} ${NAME}.pivpn" >> /etc/pivpn/hosts.openvpn
if killall -SIGHUP pihole-FTL; then
echo "::: Updated hosts file for Pi-hole" if killall -SIGHUP pihole-FTL; then
else echo "::: Updated hosts file for Pi-hole"
echo "::: Failed to reload pihole-FTL configuration" else
fi err "::: Failed to reload pihole-FTL configuration"
fi
fi fi
# Copy the .ovpn profile to the home directory for convenient remote access # 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" dest_path="${install_home}/ovpns/${NAME}${FILEEXT}"
chown "$install_user":"$install_user" "$install_home/ovpns/$NAME$FILEEXT" cp "/etc/openvpn/easy-rsa/pki/${NAME}${FILEEXT}" "${dest_path}"
chmod 640 "/etc/openvpn/easy-rsa/pki/$NAME$FILEEXT" chown "${install_user}:${install_user}" "${dest_path}"
chmod 640 "$install_home/ovpns/$NAME$FILEEXT" chmod 640 "/etc/openvpn/easy-rsa/pki/${NAME}${FILEEXT}"
chmod 640 "${dest_path}"
unset dest_path
printf "\n\n" printf "\n\n"
printf "========================================================\n" printf "========================================================\n"
printf "\e[1mDone! %s successfully created!\e[0m \n" "$NAME$FILEEXT" printf "\e[1mDone! %s successfully created!\e[0m \n" "${NAME}${FILEEXT}"
printf "%s was copied to:\n" "$NAME$FILEEXT" printf "%s was copied to:\n" "${NAME}${FILEEXT}"
printf " %s/ovpns\n" "$install_home" printf " %s/ovpns\n" "${install_home}"
printf "for easy transfer. Please use this profile only on one\n" printf "for easy transfer. Please use this profile only on one\n"
printf "device and create additional profiles for other devices.\n" printf "device and create additional profiles for other devices.\n"
printf "========================================================\n\n" printf "========================================================\n\n"

View file

@ -3,15 +3,15 @@
CHECK_PKG_INSTALLED='dpkg-query -s' CHECK_PKG_INSTALLED='dpkg-query -s'
if grep -qsEe "^NAME\=['\"]?Alpine[a-zA-Z ]*['\"]?$" /etc/os-release; then 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 fi
# Must be root to use this tool # Must be root to use this tool
if [[ ! $EUID -eq 0 ]]; then if [[ "${EUID}" -ne 0 ]]; then
if eval "${CHECK_PKG_INSTALLED} sudo" &> /dev/null; then if ${CHECK_PKG_INSTALLED} sudo &> /dev/null; then
export SUDO="sudo" export SUDO="sudo"
else else
echo "::: Please install sudo or run this as root." err "::: Please install sudo or run this as root."
exit 1 exit 1
fi fi
fi fi
@ -19,88 +19,113 @@ fi
scriptDir="/opt/pivpn" scriptDir="/opt/pivpn"
vpn="openvpn" vpn="openvpn"
function makeOVPNFunc { err() {
shift echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
$SUDO ${scriptDir}/${vpn}/makeOVPN.sh "$@"
exit "$?"
} }
function listClientsFunc { makeOVPNFunc() {
shift shift
$SUDO ${scriptDir}/${vpn}/clientStat.sh "$@" ${SUDO} "${scriptDir}/${vpn}/makeOVPN.sh" "$@"
exit "$?" exit "${?}"
} }
function listOVPNFunc { listClientsFunc() {
$SUDO ${scriptDir}/${vpn}/listOVPN.sh shift
exit "$?" ${SUDO} "${scriptDir}/${vpn}/clientStat.sh" "$@"
exit "${?}"
} }
function debugFunc { listOVPNFunc() {
echo "::: Generating Debug Output" ${SUDO} "${scriptDir}/${vpn}/listOVPN.sh"
$SUDO ${scriptDir}/${vpn}/pivpnDebug.sh | tee /tmp/debug.log exit "${?}"
echo "::: "
echo "::: Debug output completed above."
echo "::: Copy saved to /tmp/debug.log"
echo "::: "
exit "$?"
} }
function removeOVPNFunc { debugFunc() {
shift echo "::: Generating Debug Output"
$SUDO ${scriptDir}/${vpn}/removeOVPN.sh "$@"
exit "$?" ${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 { removeOVPNFunc() {
$SUDO ${scriptDir}/uninstall.sh "${vpn}" shift
exit "$?" ${SUDO} "${scriptDir}/${vpn}/removeOVPN.sh" "$@"
exit "${?}"
} }
function update { uninstallFunc() {
shift ${SUDO} "${scriptDir}/uninstall.sh" "${vpn}"
$SUDO ${scriptDir}/update.sh "$@" exit "${?}"
exit "$?"
} }
function backup { update() {
$SUDO ${scriptDir}/backup.sh "${vpn}" shift
exit "$?" ${SUDO} "${scriptDir}/update.sh" "$@"
exit "${?}"
} }
backup() {
function helpFunc { ${SUDO} "${scriptDir}/backup.sh" "${vpn}"
echo "::: Control all PiVPN specific functions!" exit "${?}"
echo ":::"
echo "::: Usage: pivpn <command> [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() {
helpFunc echo "::: Control all PiVPN specific functions!"
echo ":::"
echo "::: Usage: pivpn <command> [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 fi
# Handle redirecting to specific functions based on arguments # Handle redirecting to specific functions based on arguments
case "$1" in case "${1}" in
"-a" | "add" ) makeOVPNFunc "$@";; "-a" | "add")
"-c" | "clients" ) listClientsFunc "$@";; makeOVPNFunc "$@"
"-d" | "debug" ) debugFunc;; ;;
"-l" | "list" ) listOVPNFunc;; "-c" | "clients")
"-r" | "revoke" ) removeOVPNFunc "$@";; listClientsFunc "$@"
"-h" | "help" ) helpFunc;; ;;
"-u" | "uninstall" ) uninstallFunc;; "-d" | "debug")
"-up"| "update" ) update "$@" ;; debugFunc
"-bk"| "backup" ) backup;; ;;
* ) helpFunc;; "-l" | "list")
listOVPNFunc
;;
"-r" | "revoke")
removeOVPNFunc "$@"
;;
"-h" | "help")
helpFunc
;;
"-u" | "uninstall")
uninstallFunc
;;
"-up" | "update")
update "$@"
;;
"-bk" | "backup")
backup
;;
*)
helpFunc
;;
esac esac

View file

@ -1,11 +1,15 @@
#!/usr/bin/env bash #!/bin/bash
# This scripts runs as root # This scripts runs as root
setupVars="/etc/pivpn/openvpn/setupVars.conf" setupVars="/etc/pivpn/openvpn/setupVars.conf"
if [ ! -f "${setupVars}" ]; then err() {
echo "::: Missing setup vars file!" echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
exit 1 }
if [[ ! -f "${setupVars}" ]]; then
err "::: Missing setup vars file!"
exit 1
fi fi
# shellcheck disable=SC1090 # shellcheck disable=SC1090
@ -15,41 +19,62 @@ echo -e "::::\t\t\e[4mPiVPN debug\e[0m\t\t ::::"
printf "=============================================\n" printf "=============================================\n"
echo -e "::::\t\t\e[4mLatest commit\e[0m\t\t ::::" echo -e "::::\t\t\e[4mLatest commit\e[0m\t\t ::::"
echo -n "Branch: " echo -n "Branch: "
git --git-dir /usr/local/src/pivpn/.git rev-parse --abbrev-ref HEAD 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" printf "=============================================\n"
echo -e "::::\t \e[4mInstallation settings\e[0m \t ::::" echo -e "::::\t \e[4mInstallation settings\e[0m \t ::::"
# shellcheck disable=SC2154 # shellcheck disable=SC2154
sed "s/$pivpnHOST/REDACTED/" < ${setupVars} sed "s/${pivpnHOST}/REDACTED/" < "${setupVars}"
printf "=============================================\n" printf "=============================================\n"
echo -e ":::: \e[4mServer configuration shown below\e[0m ::::" echo -e ":::: \e[4mServer configuration shown below\e[0m ::::"
cat /etc/openvpn/server.conf cat /etc/openvpn/server.conf
printf "=============================================\n" printf "=============================================\n"
echo -e ":::: \e[4mClient template file shown below\e[0m ::::" 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" 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 ls -LR /etc/openvpn/easy-rsa/pki/ -Ireqs -Icerts_by_serial
printf "=============================================\n" printf "=============================================\n"
echo -e "::::\t\t\e[4mSelf check\e[0m\t\t ::::" echo -e "::::\t\t\e[4mSelf check\e[0m\t\t ::::"
/opt/pivpn/self_check.sh "${VPN}" /opt/pivpn/self_check.sh "${VPN}"
printf "=============================================\n" printf "=============================================\n"
echo -e ":::: Having trouble connecting? Take a look at the FAQ:" echo -e ":::: Having trouble connecting? Take a look at the FAQ:"
echo -e ":::: \e[1mhttps://docs.pivpn.io/faq\e[0m" echo -e ":::: \e[1mhttps://docs.pivpn.io/faq\e[0m"
printf "=============================================\n" printf "=============================================\n"
if [ "${PLAT}" != 'Alpine' ]; then if [[ "${PLAT}" != 'Alpine' ]]; then
echo -e ":::: \e[4mSnippet of the server log\e[0m ::::" echo -e ":::: \e[4mSnippet of the server log\e[0m ::::"
OVPNLOG="$(tail -n 20 /var/log/openvpn.log)" OVPNLOG="$(tail -n 20 /var/log/openvpn.log)"
# Regular expession taken from https://superuser.com/a/202835, it will match invalid IPs # Regular expession taken from https://superuser.com/a/202835,
# like 123.456.789.012 but it's fine since the log only contains valid ones. # it will match invalid IPs like 123.456.789.012 but it's fine
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)") # since the log only contains valid ones.
for IP in "${IPS_TO_HIDE[@]}"; do declare -a IPS_TO_HIDE=("$(echo "${OVPNLOG}" \
OVPNLOG="${OVPNLOG//"$IP"/REDACTED}" | grepcidr -v 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 \
done | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' \
| uniq)")
echo "$OVPNLOG" for IP in "${IPS_TO_HIDE[@]}"; do
printf "=============================================\n" OVPNLOG="${OVPNLOG//"$IP"/REDACTED}"
done
echo "${OVPNLOG}"
printf "=============================================\n"
fi fi
echo -e "::::\t\t\e[4mDebug complete\e[0m\t\t ::::" echo -e "::::\t\t\e[4mDebug complete\e[0m\t\t ::::"

View file

@ -1,172 +1,195 @@
#!/usr/bin/env bash #!/bin/bash
# PiVPN: revoke client script # PiVPN: revoke client script
setupVars="/etc/pivpn/openvpn/setupVars.conf" setupVars="/etc/pivpn/openvpn/setupVars.conf"
INDEX="/etc/openvpn/easy-rsa/pki/index.txt" INDEX="/etc/openvpn/easy-rsa/pki/index.txt"
if [ ! -f "${setupVars}" ]; then if [[ ! -f "${setupVars}" ]]; then
echo "::: Missing setup vars file!" err "::: Missing setup vars file!"
exit 1 exit 1
fi fi
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "${setupVars}" source "${setupVars}"
err() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
}
helpFunc() { helpFunc() {
echo "::: Revoke a client ovpn profile" echo "::: Revoke a client ovpn profile"
echo ":::" echo ":::"
echo "::: Usage: pivpn <-r|revoke> [-y|--yes] [-h|--help] [<client-1>] ... [<client-n>] ..." echo -n "::: Usage: pivpn <-r|revoke> [-y|--yes] [-h|--help] "
echo ":::" echo "[<client-1> ... [<client-2>] ...]"
echo "::: Commands:" echo ":::"
echo "::: [none] Interactive mode" echo "::: Commands:"
echo "::: <client> Client(s) to to revoke" echo "::: [none] Interactive mode"
echo "::: -y,--yes Remove Client(s) without confirmation" echo "::: <client> Client(s) to to revoke"
echo "::: -h,--help Show this help dialog" echo "::: -y,--yes Remove Client(s) without confirmation"
echo "::: -h,--help Show this help dialog"
} }
# Parse input arguments # Parse input arguments
while test $# -gt 0; do while [[ "$#" -gt 0 ]]; do
_key="$1" _key="${1}"
case "$_key" in
-h|--help) case "${_key}" in
helpFunc -h | --help)
exit 0 helpFunc
;; exit 0
-y|--yes) ;;
CONFIRM=true -y | --yes)
;; CONFIRM=true
*) ;;
CERTS_TO_REVOKE+=("$1") *)
;; CERTS_TO_REVOKE+=("${1}")
esac ;;
shift esac
shift
done done
if [ ! -f "${INDEX}" ]; then if [[ ! -f "${INDEX}" ]]; then
printf "The file: %s was not found\n" "$INDEX" err "The file: ${INDEX} was not found"
exit 1 exit 1
fi fi
# Disabling SC2128, just checking if variable is empty or not # Disabling SC2128, just checking if variable is empty or not
# shellcheck disable=SC2128 # shellcheck disable=SC2128
if [[ -z "${CERTS_TO_REVOKE}" ]]; then if [[ -z "${CERTS_TO_REVOKE}" ]]; then
printf "\n" printf "\n"
printf " ::\e[4m Certificate List \e[0m:: \n" printf " ::\e[4m Certificate List \e[0m:: \n"
i=0 i=0
while read -r line || [ -n "$line" ]; do while read -r line || [[ -n "${line}" ]]; do
STATUS=$(echo "$line" | awk '{print $1}') 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=1 if [[ "${STATUS}" == "V" ]]; then
len=${#CERTS[@]} # Disabling SC2001 warning, suggested method doesn't work with regexp
while [ $i -le "${len}" ]; do # shellcheck disable=SC2001
printf "%0${#len}s) %s\r\n" ${i} "${CERTS[(($i))]}" NAME=$(echo "${line}" | sed -e 's:.*/CN=::')
((i++))
done
printf "\n"
echo -n "::: Please enter the Index/Name of the client to be revoked from the list above: " if [[ "${i}" != 0 ]]; then
read -r NAME # Prevent printing "server" certificate
CERTS["${i}"]=$(echo -e "${NAME}")
fi
if [[ -z "${NAME}" ]]; then ((i++))
echo "You can not leave this blank!"
exit 1
fi fi
done < "${INDEX}"
re='^[0-9]+$' i=1
if [[ ${NAME} =~ $re ]] ; then len="${#CERTS[@]}"
NAME=${CERTS[$((NAME))]} 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 fi
done
for((x=1;x<=i;++x)); do if [[ -z "${VALID}" ]]; then
if [ "${CERTS[$x]}" = "${NAME}" ]; then err "You didn't enter a valid cert name!"
VALID=1 exit 1
fi fi
done
if [ -z "${VALID}" ]; then CERTS_TO_REVOKE=("${NAME}")
printf "You didn't enter a valid cert name!\n"
exit 1
fi
CERTS_TO_REVOKE=( "${NAME}" )
else else
i=0 i=0
while read -r line || [ -n "$line" ]; do while read -r line || [[ -n "${line}" ]]; do
STATUS=$(echo "$line" | awk '{print $1}') 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}
for (( ii = 0; ii < ${#CERTS_TO_REVOKE[@]}; ii++)); do if [[ "${STATUS}" == "V" ]]; then
VALID=0 NAME=$(echo -e "${line}" | sed -e 's:.*/CN=::')
for((x=1;x<=i;++x)); do CERTS["${i}"]="${NAME}"
if [ "${CERTS[$x]}" = "${CERTS_TO_REVOKE[ii]}" ]; then ((i++))
VALID=1 fi
fi done < "${INDEX}"
done
if [ "${VALID}" != 1 ]; then for ((ii = 0; ii < ${#CERTS_TO_REVOKE[@]}; ii++)); do
printf "You passed an invalid cert name: '%s'! \n" "${CERTS_TO_REVOKE[ii]}" VALID=0
exit 1
fi for ((x = 1; x <= i; ++x)); do
if [[ "${CERTS[$x]}" == "${CERTS_TO_REVOKE[ii]}" ]]; then
VALID=1
fi
done done
if [[ "${VALID}" != 1 ]]; then
err "You passed an invalid cert name: '${CERTS_TO_REVOKE[ii]}'!"
exit 1
fi
done
fi fi
cd /etc/openvpn/easy-rsa || exit cd /etc/openvpn/easy-rsa || exit
for (( ii = 0; ii < ${#CERTS_TO_REVOKE[@]}; ii++)); do for ((ii = 0; ii < ${#CERTS_TO_REVOKE[@]}; ii++)); do
if [ -n "$CONFIRM" ]; then if [[ -n "${CONFIRM}" ]]; then
REPLY="y" REPLY="y"
else else
read -r -p "Do you really want to revoke '${CERTS_TO_REVOKE[ii]}'? [y/N] " read -r -p "Do you really want to revoke '${CERTS_TO_REVOKE[ii]}'? [y/N] "
fi fi
if [[ $REPLY =~ ^[Yy]$ ]]; then
printf "\n::: Revoking certificate '%s'. \n" "${CERTS_TO_REVOKE[ii]}" if [[ "${REPLY}" =~ ^[Yy]$ ]]; then
./easyrsa --batch revoke "${CERTS_TO_REVOKE[ii]}" printf "\n::: Revoking certificate '%s'. \n" "${CERTS_TO_REVOKE[ii]}"
./easyrsa gen-crl
printf "\n::: Certificate revoked, and CRL file updated.\n" ./easyrsa --batch revoke "${CERTS_TO_REVOKE[ii]}"
printf "::: Removing certs and client configuration for this profile.\n" ./easyrsa gen-crl
rm -rf "pki/reqs/${CERTS_TO_REVOKE[ii]}.req"
rm -rf "pki/private/${CERTS_TO_REVOKE[ii]}.key" printf "\n::: Certificate revoked, and CRL file updated.\n"
rm -rf "pki/issued/${CERTS_TO_REVOKE[ii]}.crt" printf "::: Removing certs and client configuration for this profile.\n"
# Disabling SC2154 $pivpnNET sourced externally rm -rf "pki/reqs/${CERTS_TO_REVOKE[ii]}.req"
# shellcheck disable=SC2154 rm -rf "pki/private/${CERTS_TO_REVOKE[ii]}.key"
# Grab the client IP address rm -rf "pki/issued/${CERTS_TO_REVOKE[ii]}.crt"
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}") # Disabling SC2154 $pivpnNET sourced externally
rm -rf /etc/openvpn/ccd/"${CERTS_TO_REVOKE[ii]}" # shellcheck disable=SC2154
# Grab the client IP address
# disablung warning SC2154, $install_home sourced externally NET_REDUCED="${pivpnNET::-2}"
# shellcheck disable=SC2154 STATIC_IP="$(grep -v "^#" /etc/openvpn/ccd/"${CERTS_TO_REVOKE[ii]}" \
rm -rf "${install_home}/ovpns/${CERTS_TO_REVOKE[ii]}.ovpn" | grep -w ifconfig-push \
rm -rf "/etc/openvpn/easy-rsa/pki/${CERTS_TO_REVOKE[ii]}.ovpn" | grep -oE "${NET_REDUCED}\.[0-9]{1,3}")"
cp /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn/crl.pem rm -rf /etc/openvpn/ccd/"${CERTS_TO_REVOKE[ii]}"
# If using Pi-hole, remove the client from the hosts file # disablung warning SC2154, $install_home sourced externally
if [ -f /etc/pivpn/hosts.openvpn ]; then # shellcheck disable=SC2154
sed "\#${STATIC_IP} ${CERTS_TO_REVOKE[ii]}.pivpn#d" -i /etc/pivpn/hosts.openvpn rm -rf "${install_home}/ovpns/${CERTS_TO_REVOKE[ii]}.ovpn"
if killall -SIGHUP pihole-FTL; then rm -rf "/etc/openvpn/easy-rsa/pki/${CERTS_TO_REVOKE[ii]}.ovpn"
echo "::: Updated hosts file for Pi-hole" cp /etc/openvpn/easy-rsa/pki/crl.pem /etc/openvpn/crl.pem
else
echo "::: Failed to reload pihole-FTL configuration" # If using Pi-hole, remove the client from the hosts file
fi if [[ -f /etc/pivpn/hosts.openvpn ]]; then
fi 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
fi
done done
printf "::: Completed!\n" printf "::: Completed!\n"

View file

@ -3,53 +3,69 @@
CHECK_PKG_INSTALLED='dpkg-query -s' CHECK_PKG_INSTALLED='dpkg-query -s'
if grep -qsEe "^NAME\=['\"]?Alpine[a-zA-Z ]*['\"]?$" /etc/os-release; then 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 fi
# Must be root to use this tool # Must be root to use this tool
if [ $EUID -ne 0 ]; then if [[ "${EUID}" -ne 0 ]]; then
if eval "${CHECK_PKG_INSTALLED} sudo" &> /dev/null; then if ${CHECK_PKG_INSTALLED} sudo &> /dev/null; then
export SUDO="sudo" export SUDO="sudo"
else else
echo "::: Please install sudo or run this as root." err "::: Please install sudo or run this as root."
exit 1 exit 1
fi fi
fi fi
scriptDir="/opt/pivpn" scriptDir="/opt/pivpn"
uninstallServer(){ err() {
$SUDO ${scriptDir}/uninstall.sh echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
exit "$?"
} }
backup(){ uninstallServer() {
$SUDO ${scriptDir}/backup.sh ${SUDO} "${scriptDir}/uninstall.sh"
exit "$?" exit "${?}"
} }
showHelp(){ backup() {
echo "::: To pass off to the pivpn command for each protocol" ${SUDO} "${scriptDir}/backup.sh"
echo ":::" exit "${?}"
echo "::: Usage: pivpn wg <command> [option]"
echo "::: Usage: pivpn ovpn <command> [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() {
showHelp echo "::: To pass off to the pivpn command for each protocol"
echo ":::"
echo "::: Usage: pivpn wg <command> [option]"
echo "::: Usage: pivpn ovpn <command> [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 fi
# Handle redirecting to specific functions based on arguments # Handle redirecting to specific functions based on arguments
case "$1" in case "${1}" in
wg ) "${scriptDir}/wireguard/pivpn.sh" "${@:2}";; wg)
ovpn ) "${scriptDir}/openvpn/pivpn.sh" "${@:2}";; "${scriptDir}/wireguard/pivpn.sh" "${@:2}"
"-h" | "help" ) showHelp;; ;;
"-u" | "uninstall" ) uninstallServer;; ovpn)
"-bk" | "backup" ) backup ;; "${scriptDir}/openvpn/pivpn.sh" "${@:2}"
* ) showHelp;; ;;
"-h" | "help")
showHelp
;;
"-u" | "uninstall")
uninstallServer
;;
"-bk" | "backup")
backup
;;
*)
showHelp
;;
esac esac

View file

@ -1,217 +1,340 @@
#!/bin/bash #!/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 # dual protocol, VPN type supplied as $1
VPN=$1 VPN="${1}"
setupVars="/etc/pivpn/${VPN}/setupVars.conf" setupVars="/etc/pivpn/${VPN}/setupVars.conf"
ERR=0 ERR=0
if [ ! -f "${setupVars}" ]; then err() {
echo "::: Missing setup vars file!" echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
exit 1 }
if [[ ! -f "${setupVars}" ]]; then
err "::: Missing setup vars file!"
exit 1
fi fi
# SC1090 disabled as setupVars file differs from system to system # SC1090 disabled as setupVars file differs from system to system
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "${setupVars}" source "${setupVars}"
if [ "$VPN" = "wireguard" ]; then if [[ "${VPN}" == "wireguard" ]]; then
VPN_SERVICE="wg-quick@wg0" VPN_PRETTY_NAME="WireGuard"
VPN_SERVICE="wg-quick@wg0"
if [ "${PLAT}" == 'Alpine' ]; then if [[ "${PLAT}" == 'Alpine' ]]; then
VPN_SERVICE='wg-quick' VPN_SERVICE='wg-quick'
fi fi
elif [[ "${VPN}" == "openvpn" ]]; then
VPN_PRETTY_NAME="WireGuard" VPN_SERVICE="openvpn"
elif [ "$VPN" = "openvpn" ]; then VPN_PRETTY_NAME="OpenVPN"
VPN_SERVICE="openvpn"
VPN_PRETTY_NAME="OpenVPN"
fi fi
if [ "$(</proc/sys/net/ipv4/ip_forward)" -eq 1 ]; then if [[ "$(< /proc/sys/net/ipv4/ip_forward)" -eq 1 ]]; then
echo ":: [OK] IP forwarding is enabled" echo ":: [OK] IP forwarding is enabled"
else else
ERR=1 ERR=1
read -r -p ":: [ERR] IP forwarding is not enabled, attempt fix now? [Y/n] " REPLY read -r \
if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then -p ":: [ERR] IP forwarding is not enabled, attempt fix now? [Y/n] " \
sed -i '/net.ipv4.ip_forward=1/s/^#//g' /etc/sysctl.conf REPLY
sysctl -p
echo "Done" if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then
fi sed -i '/net.ipv4.ip_forward=1/s/^#//g' /etc/sysctl.conf
sysctl -p
echo "Done"
fi
fi fi
if [ "$USING_UFW" -eq 0 ]; then if [[ "${USING_UFW}" -eq 0 ]]; then
# 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
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 if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then
# shellcheck disable=SC2154 iptables \
if iptables -t nat -C POSTROUTING -s "${pivpnNET}/${subnetClass}" -o "${IPv4dev}" -j MASQUERADE -m comment --comment "${VPN}-nat-rule" &> /dev/null; then -t nat \
echo ":: [OK] Iptables MASQUERADE rule set" -I POSTROUTING \
else -s "${pivpnNET}/${subnetClass}" \
ERR=1 -o "${IPv4dev}" \
read -r -p ":: [ERR] Iptables MASQUERADE rule is not set, attempt fix now? [Y/n] " REPLY -j MASQUERADE \
if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then -m comment \
iptables -t nat -I POSTROUTING -s "${pivpnNET}/${subnetClass}" -o "${IPv4dev}" -j MASQUERADE -m comment --comment "${VPN}-nat-rule" --comment "${VPN}-nat-rule"
iptables-save > /etc/iptables/rules.v4
echo "Done"
fi
fi
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 if [[ "${INPUT_CHAIN_EDITED}" -eq 1 ]]; then
# shellcheck disable=SC2154 # Disabled SC Warnings for SC2154, values
if iptables -C INPUT -i "${IPv4dev}" -p "${pivpnPROTO}" --dport "${pivpnPORT}" -j ACCEPT -m comment --comment "${VPN}-input-rule" &> /dev/null; then # for variables are sourced from setupVars
echo ":: [OK] Iptables INPUT rule set" # shellcheck disable=SC2154
else if iptables \
ERR=1 -C INPUT \
read -r -p ":: [ERR] Iptables INPUT rule is not set, attempt fix now? [Y/n] " REPLY -i "${IPv4dev}" \
if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then -p "${pivpnPROTO}" \
iptables -I INPUT 1 -i "${IPv4dev}" -p "${pivpnPROTO}" --dport "${pivpnPORT}" -j ACCEPT -m comment --comment "${VPN}-input-rule" --dport "${pivpnPORT}" \
iptables-save > /etc/iptables/rules.v4 -j ACCEPT \
echo "Done" -m comment \
fi --comment "${VPN}-input-rule" &> /dev/null; then
fi echo ":: [OK] Iptables INPUT rule set"
fi 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 iptables-save > /etc/iptables/rules.v4
# shellcheck disable=SC2154 echo "Done"
if iptables -C FORWARD -s "${pivpnNET}/${subnetClass}" -i "${pivpnDEV}" -o "${IPv4dev}" -j ACCEPT -m comment --comment "${VPN}-forward-rule" &> /dev/null; then fi
echo ":: [OK] Iptables FORWARD rule set" fi
else fi
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
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 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 if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then
echo ":: [OK] Ufw is enabled" ufw enable
else fi
ERR=1 fi
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 iptables -t nat -C POSTROUTING -s "${pivpnNET}/${subnetClass}" -o "${IPv4dev}" -j MASQUERADE -m comment --comment "${VPN}-nat-rule" &> /dev/null; then if iptables \
echo ":: [OK] Iptables MASQUERADE rule set" -t nat \
else -C POSTROUTING \
ERR=1 -s "${pivpnNET}/${subnetClass}" \
read -r -p ":: [ERR] Iptables MASQUERADE rule is not set, attempt fix now? [Y/n] " REPLY -o "${IPv4dev}" \
if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then -j MASQUERADE \
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 -m comment \
ufw reload --comment "${VPN}-nat-rule" &> /dev/null; then
echo "Done" echo ":: [OK] Iptables MASQUERADE rule set"
fi else
fi 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 if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then
echo ":: [OK] Ufw input rule set" sed_pattern='/delete these required/i'
else sed_pattern="${sed_pattern} *nat\n:POSTROUTING ACCEPT [0:0]\n"
ERR=1 sed_pattern="${sed_pattern} -I POSTROUTING"
read -r -p ":: [ERR] Ufw input rule is not set, attempt fix now? [Y/n] " REPLY sed_pattern="${sed_pattern} -s ${pivpnNET}/${subnetClass}"
if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then sed_pattern="${sed_pattern} -o ${IPv4dev}"
ufw insert 1 allow "${pivpnPORT}"/"${pivpnPROTO}" sed_pattern="${sed_pattern} -j MASQUERADE"
ufw reload sed_pattern="${sed_pattern} -m comment"
echo "Done" sed_pattern="${sed_pattern} --comment ${VPN}-nat-rule\n"
fi sed_pattern="${sed_pattern}COMMIT\n"
fi
if iptables -C ufw-user-forward -i "${pivpnDEV}" -o "${IPv4dev}" -s "${pivpnNET}/${subnetClass}" -j ACCEPT &> /dev/null; then sed "${sed_pattern}" -i /etc/ufw/before.rules
echo ":: [OK] Ufw forwarding rule set" ufw reload
else echo "Done"
ERR=1 unset sed_pattern
read -r -p ":: [ERR] Ufw forwarding rule is not set, attempt fix now? [Y/n] " REPLY fi
if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then fi
ufw route insert 1 allow in on "${pivpnDEV}" from "${pivpnNET}/${subnetClass}" out on "${IPv4dev}" to any
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]$ ]] || [[ -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 fi
if [ "${PLAT}" == 'Alpine' ]; then if [[ "${PLAT}" == 'Alpine' ]]; then
if [ "$(rc-service "${VPN_SERVICE}" status | sed -E -e 's/.*status\: (.*)/\1/')" == 'started' ]; then if [[ "$(rc-service "${VPN_SERVICE}" status \
echo ":: [OK] ${VPN_PRETTY_NAME} is running" | sed -E -e 's/.*status\: (.*)/\1/')" == 'started' ]]; then
else echo ":: [OK] ${VPN_PRETTY_NAME} is running"
ERR=1 else
read -r -p ":: [ERR] ${VPN_PRETTY_NAME} is not running, try to start now? [Y/n] " REPLY 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 if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then
rc-service -s "${VPN_SERVICE}" restart rc-service -s "${VPN_SERVICE}" restart
rc-service -N "${VPN_SERVICE}" start rc-service -N "${VPN_SERVICE}" start
echo "Done"
fi
fi
echo "Done" if rc-update show default \
fi | grep -sEe "\s*${VPN_SERVICE} .*" &> /dev/null; then
fi 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 if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then
echo ":: [OK] ${VPN_PRETTY_NAME} is enabled (it will automatically start on reboot)" rc-update add "${VPN_SERVICE}" default
else echo "Done"
ERR=1 fi
read -r -p ":: [ERR] ${VPN_PRETTY_NAME} is not enabled, try to enable now? [Y/n] " REPLY fi
if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then
rc-update add "${VPN_SERVICE}" default
echo "Done"
fi
fi
else else
if systemctl is-active -q "${VPN_SERVICE}"; then if systemctl is-active -q "${VPN_SERVICE}"; then
echo ":: [OK] ${VPN_PRETTY_NAME} is running" echo ":: [OK] ${VPN_PRETTY_NAME} is running"
else else
ERR=1 ERR=1
read -r -p ":: [ERR] ${VPN_PRETTY_NAME} is not running, try to start now? [Y/n] " REPLY echo -n ":: [ERR] ${VPN_PRETTY_NAME} is not running, "
if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then echo -n "try to start now? [Y/n] "
systemctl start "${VPN_SERVICE}" read -r REPLY
echo "Done"
fi
fi
if systemctl is-enabled -q "${VPN_SERVICE}"; then if [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then
echo ":: [OK] ${VPN_PRETTY_NAME} is enabled (it will automatically start on reboot)" systemctl start "${VPN_SERVICE}"
else echo "Done"
ERR=1 fi
read -r -p ":: [ERR] ${VPN_PRETTY_NAME} is not enabled, try to enable now? [Y/n] " REPLY fi
if [[ ${REPLY} =~ ^[Yy]$ ]] || [[ -z ${REPLY} ]]; then
systemctl enable "${VPN_SERVICE}" if systemctl is-enabled -q "${VPN_SERVICE}"; then
echo "Done" echo ":: [OK] ${VPN_PRETTY_NAME} is enabled "
fi echo "(it will automatically start on reboot)"
fi 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 fi
# grep -w (whole word) is used so port 11940 won't match when looking for 1194 # grep -w (whole word) is used so port 11940 won't match when looking for 1194
if netstat -antu | grep -wqE "${pivpnPROTO}.*${pivpnPORT}"; then 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 else
ERR=1 ERR=1
read -r -p ":: [ERR] ${VPN_PRETTY_NAME} is not listening, try to restart now? [Y/n] " REPLY 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 [[ "${REPLY}" =~ ^[Yy]$ ]] || [[ -z "${REPLY}" ]]; then
if [ "${PLAT}" == 'Alpine' ]; then if [[ "${PLAT}" == 'Alpine' ]]; then
rc-service -s "${VPN_SERVICE}" restart rc-service -s "${VPN_SERVICE}" restart
rc-service -N "${VPN_SERVICE}" start rc-service -N "${VPN_SERVICE}" start
else else
systemctl restart "${VPN_SERVICE}" systemctl restart "${VPN_SERVICE}"
fi fi
echo "Done" echo "Done"
fi fi
fi fi
if [ "$ERR" -eq 1 ]; then if [[ "${ERR}" -eq 1 ]]; then
echo -e "[INFO] Run \e[1mpivpn -d\e[0m again to see if we detect issues" echo -e "[INFO] Run \e[1mpivpn -d\e[0m again to see if we detect issues"
fi fi

View file

@ -1,20 +1,22 @@
#!/usr/bin/env bash #!/bin/bash
# PiVPN: Uninstall Script # PiVPN: Uninstall Script
### FIXME: global: config storage, refactor all scripts to adhere to the storage ### FIXME:
### FIXME: use variables where appropriate, reduce magic numbers by 99.9%, at least. ### 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. # 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) screen_size="$(stty size 2> /dev/null || echo 24 80)"
rows=$(echo "$screen_size" | awk '{print $1}') rows="$(echo "${screen_size}" | awk '{print $1}')"
columns=$(echo "$screen_size" | awk '{print $2}') columns="$(echo "${screen_size}" | awk '{print $2}')"
# Divide by two so the dialogs take up half of the screen, which looks nice. # Divide by two so the dialogs take up half of the screen, which looks nice.
r=$(( rows / 2 )) r=$((rows / 2))
c=$(( columns / 2 )) c=$((columns / 2))
# Unless the screen is tiny # Unless the screen is tiny
r=$(( r < 20 ? 20 : r )) r=$((r < 20 ? 20 : r))
c=$(( c < 70 ? 70 : c )) c=$((c < 70 ? 70 : c))
PKG_MANAGER="apt-get" PKG_MANAGER="apt-get"
PKG_REMOVE="${PKG_MANAGER} -y remove --purge" PKG_REMOVE="${PKG_MANAGER} -y remove --purge"
@ -24,295 +26,391 @@ setupConfigDir="/etc/pivpn"
pivpnFilesDir="/usr/local/src/pivpn" pivpnFilesDir="/usr/local/src/pivpn"
pivpnScriptDir="/opt/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 if [[ "${PLAT}" == 'Alpine' ]]; then
PKG_MANAGER='apk' PKG_MANAGER='apk'
PKG_REMOVE="${PKG_MANAGER} --no-cache --purge del -r" PKG_REMOVE="${PKG_MANAGER} --no-cache --purge del -r"
fi fi
UPDATE_PKG_CACHE="${PKG_MANAGER} update" UPDATE_PKG_CACHE="${PKG_MANAGER} update"
if [ -r "${setupConfigDir}/wireguard/${setupVarsFile}" ] && [ -r "${setupConfigDir}/openvpn/${setupVarsFile}" ]; then if [[ -r "${setupConfigDir}/wireguard/${setupVarsFile}" ]] \
vpnStillExists=1 && [[ -r "${setupConfigDir}/openvpn/${setupVarsFile}" ]]; then
vpnStillExists=1
# Two protocols have been installed, check if the script has passed # Two protocols have been installed, check if the script has passed
# an argument, otherwise ask the user which one he wants to remove # an argument, otherwise ask the user which one he wants to remove
if [ $# -ge 1 ]; then if [[ "$#" -ge 1 ]]; then
VPN="$1" VPN="${1}"
echo "::: Uninstalling VPN: $VPN" echo "::: Uninstalling VPN: ${VPN}"
else 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) chooseVPNCmd=(whiptail
VPNChooseOptions=(WireGuard "" on --backtitle "Setup PiVPN"
OpenVPN "" off) --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 if VPN="$("${chooseVPNCmd[@]}" "${VPNChooseOptions[@]}" 2>&1 \
echo "::: Uninstalling VPN: $VPN" > /dev/tty)"; then
VPN="${VPN,,}" echo "::: Uninstalling VPN: ${VPN}"
else VPN="${VPN,,}"
echo "::: Cancel selected, exiting...." else
exit 1 err "::: Cancel selected, exiting...."
fi exit 1
fi fi
fi
setupVars="${setupConfigDir}/${VPN}/${setupVarsFile}" setupVars="${setupConfigDir}/${VPN}/${setupVarsFile}"
else else
vpnStillExists=0 vpnStillExists=0
if [ -r "${setupConfigDir}/wireguard/${setupVarsFile}" ]; then if [[ -r "${setupConfigDir}/wireguard/${setupVarsFile}" ]]; then
setupVars="${setupConfigDir}/wireguard/${setupVarsFile}" setupVars="${setupConfigDir}/wireguard/${setupVarsFile}"
elif [ -r "${setupConfigDir}/openvpn/${setupVarsFile}" ]; then elif [[ -r "${setupConfigDir}/openvpn/${setupVarsFile}" ]]; then
setupVars="${setupConfigDir}/openvpn/${setupVarsFile}" setupVars="${setupConfigDir}/openvpn/${setupVarsFile}"
fi fi
fi fi
if [ ! -f "${setupVars}" ]; then if [[ ! -f "${setupVars}" ]]; then
echo "::: Missing setup vars file!" err "::: Missing setup vars file!"
exit 1 exit 1
fi fi
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "${setupVars}" source "${setupVars}"
err() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
}
### FIXME: introduce global lib ### FIXME: introduce global lib
spinner(){ spinner() {
local pid=$1 local pid="${1}"
local delay=0.50 local delay=0.50
local spinstr='/-\|' local spinstr='/-\|'
while ps a | awk '{print $1}' | grep -q "$pid"; do
local temp=${spinstr#?} while ps a | awk '{print $1}' | grep -q "${pid}"; do
printf " [%c] " "$spinstr" local temp="${spinstr#?}"
local spinstr=$temp${spinstr%"$temp"} printf " [%c] " "${spinstr}"
sleep $delay local spinstr="${temp}${spinstr%"$temp"}"
printf "\\b\\b\\b\\b\\b\\b" sleep "${delay}"
done printf "\\b\\b\\b\\b\\b\\b"
printf " \\b\\b\\b\\b" done
printf " \\b\\b\\b\\b"
} }
removeAll(){ removeAll() {
# Stopping and disabling services # Stopping and disabling services
echo "::: Stopping and disabling services..." echo "::: Stopping and disabling services..."
if [ "${PLAT}" == 'Alpine' ]; then if [[ "${PLAT}" == 'Alpine' ]]; then
if [ "${VPN}" = "wireguard" ]; then if [[ "${VPN}" == "wireguard" ]]; then
rc-service wg-quick stop rc-service wg-quick stop
rc-update del wg-quick default &> /dev/null rc-update del wg-quick default &> /dev/null
elif [ "${VPN}" = "openvpn" ]; then elif [[ "${VPN}" == "openvpn" ]]; then
rc-service openvpn stop rc-service openvpn stop
rc-update del openvpn default &> /dev/null rc-update del openvpn default &> /dev/null
fi fi
else else
if [ "${VPN}" = "wireguard" ]; then if [[ "${VPN}" == "wireguard" ]]; then
systemctl stop wg-quick@wg0 systemctl stop wg-quick@wg0
systemctl disable wg-quick@wg0 &> /dev/null systemctl disable wg-quick@wg0 &> /dev/null
elif [ "${VPN}" = "openvpn" ]; then elif [[ "${VPN}" == "openvpn" ]]; then
systemctl stop openvpn systemctl stop openvpn
systemctl disable openvpn &> /dev/null systemctl disable openvpn &> /dev/null
fi fi
fi fi
# Removing firewall rules. # Removing firewall rules.
echo "::: Removing firewall rules..." echo "::: Removing firewall rules..."
if [ "$USING_UFW" -eq 1 ]; then
if [[ "${USING_UFW}" -eq 1 ]]; then
### Ignoring SC2154, value sourced from setupVars file ### Ignoring SC2154, value sourced from setupVars file
# shellcheck disable=SC2154 # shellcheck disable=SC2154
ufw delete allow "${pivpnPORT}"/"${pivpnPROTO}" > /dev/null ufw delete allow "${pivpnPORT}/${pivpnPROTO}" > /dev/null
### Ignoring SC2154, value sourced from setupVars file ### Ignoring SC2154, value sourced from setupVars file
# shellcheck disable=SC2154 # shellcheck disable=SC2154
ufw route delete allow in on "${pivpnDEV}" from "${pivpnNET}/${subnetClass}" out on "${IPv4dev}" to any > /dev/null ufw route delete allow in on "${pivpnDEV}" \
ufw delete allow in on "${pivpnDEV}" to any port 53 from "${pivpnNET}/${subnetClass}" >/dev/null from "${pivpnNET}/${subnetClass}" out on "${IPv4dev}" to any > /dev/null
sed "/-I POSTROUTING -s ${pivpnNET}\\/${subnetClass} -o ${IPv4dev} -j MASQUERADE -m comment --comment ${VPN}-nat-rule/d" -i /etc/ufw/before.rules ufw delete allow in on "${pivpnDEV}" to any port 53 \
iptables -t nat -D POSTROUTING -s "${pivpnNET}/${subnetClass}" -o "${IPv4dev}" -j MASQUERADE -m comment --comment "${VPN}-nat-rule" from "${pivpnNET}/${subnetClass}" > /dev/null
ufw reload &> /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 \
iptables -D INPUT -i "${IPv4dev}" -p "${pivpnPROTO}" --dport "${pivpnPORT}" -j ACCEPT -m comment --comment "${VPN}-input-rule" -t nat \
fi -D POSTROUTING \
-s "${pivpnNET}/${subnetClass}" \
-o "${IPv4dev}" \
-j MASQUERADE \
-m comment \
--comment "${VPN}-nat-rule"
if [ "$FORWARD_CHAIN_EDITED" -eq 1 ]; then ufw reload &> /dev/null
iptables -D FORWARD -d "${pivpnNET}/${subnetClass}" -i "${IPv4dev}" -o "${pivpnDEV}" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -m comment --comment "${VPN}-forward-rule" elif [[ "${USING_UFW}" -eq 0 ]]; then
iptables -D FORWARD -s "${pivpnNET}/${subnetClass}" -i "${pivpnDEV}" -o "${IPv4dev}" -j ACCEPT -m comment --comment "${VPN}-forward-rule" if [[ "${INPUT_CHAIN_EDITED}" -eq 1 ]]; then
fi 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-save > /etc/iptables/rules.v4 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 iptables \
if [ "${vpnStillExists}" -eq 0 ]; then -t nat \
sed -i '/net.ipv4.ip_forward=1/c\#net.ipv4.ip_forward=1' /etc/sysctl.conf -D POSTROUTING \
sysctl -p -s "${pivpnNET}/${subnetClass}" \
fi -o "${IPv4dev}" \
-j MASQUERADE \
-m comment \
--comment "${VPN}-nat-rule"
# Purge dependencies iptables-save > /etc/iptables/rules.v4
echo "::: Purge dependencies..." fi
for i in "${INSTALLED_PACKAGES[@]}"; do # Disable IPv4 forwarding
while true; do if [[ "${vpnStillExists}" -eq 0 ]]; then
read -rp "::: Do you wish to remove $i from your system? [Y/n]: " yn sed -i '/net.ipv4.ip_forward=1/c\#net.ipv4.ip_forward=1' /etc/sysctl.conf
case $yn in sysctl -p
[Yy]* ) fi
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..."
rm -f /etc/apt/sources.list.d/pivpn-bullseye-repo.list # Purge dependencies
rm -f /etc/apt/preferences.d/pivpn-limit-bullseye 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 & case "${yn}" in
spinner $! [Yy]*)
fi 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 if [[ -f "${tmp_path}" ]]; then
rm -f /etc/systemd/system/wg-quick@.service.d/override.conf echo "::: Removing Debian Bullseye repo..."
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..."
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 & ${UPDATE_PKG_CACHE} &> /dev/null &
spinner $! spinner "$!"
fi fi
deluser openvpn tmp_path='/etc/systemd/system/wg-quick@.service.d/override.conf'
rm -f /etc/rsyslog.d/30-openvpn.conf /etc/logrotate.d/openvpn
fi
fi
printf ":::\\tRemoving %s..." "$i" if [[ -f "${tmp_path}" ]]; then
rm -f "${tmp_path}"
fi
"${PKG_REMOVE}" "$i" &> /dev/null & unset tmp_path
spinner $! 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"; rm -f /etc/apt/sources.list.d/pivpn-openvpn-repo.list
break
;;
[Nn]* )
printf ":::\\tSkipping %s\\n" "$i";
break
;;
* )
printf "::: You must answer yes or no!\\n"
;;
esac
done
done
if [ "${PLAT}" != 'Alpine' ]; then echo "::: Updating package cache..."
# Take care of any additional package cleaning
printf "::: Auto removing remaining dependencies..."
"${PKG_MANAGER}" -y autoremove &> /dev/null & ${UPDATE_PKG_CACHE} &> /dev/null &
spinner $! spinner "$!"
fi
printf "done!\\n"; deluser openvpn
printf "::: Auto cleaning remaining dependencies..." rm -f /etc/rsyslog.d/30-openvpn.conf /etc/logrotate.d/openvpn
fi
fi
"${PKG_MANAGER}" -y autoclean &> /dev/null & printf ":::\\tRemoving %s..." "${i}"
spinner $!
printf "done!\\n"; ${PKG_REMOVE} "${i}" &> /dev/null &
fi spinner "$!"
if [ -f "$dnsmasqConfig" ]; then printf "done!\\n"
rm -f "$dnsmasqConfig" break
pihole restartdns ;;
fi [Nn]*)
printf ":::\\tSkipping %s\\n" "${i}"
break
;;
*)
err "::: You must answer yes or no!"
;;
esac
done
done
echo ":::" if [[ "${PLAT}" != 'Alpine' ]]; then
echo "::: Removing VPN configuration files..." # Take care of any additional package cleaning
printf "::: Auto removing remaining dependencies..."
if [ "$VPN" = "wireguard" ]; then "${PKG_MANAGER}" -y autoremove &> /dev/null &
rm -f /etc/wireguard/wg0.conf spinner "$!"
rm -rf /etc/wireguard/configs
rm -rf /etc/wireguard/keys 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 ### Ignoring SC2154, value sourced from setupVars file
# shellcheck disable=SC2154 # shellcheck disable=SC2154
rm -rf "$install_home/configs" rm -rf "${install_home}/configs"
elif [ "$VPN" = "openvpn" ]; then elif [[ "${VPN}" == "openvpn" ]]; then
rm -rf /var/log/*openvpn* rm -rf /var/log/*openvpn*
rm -f /etc/openvpn/server.conf rm -f /etc/openvpn/server.conf
rm -f /etc/openvpn/crl.pem rm -f /etc/openvpn/crl.pem
rm -rf /etc/openvpn/easy-rsa rm -rf /etc/openvpn/easy-rsa
rm -rf /etc/openvpn/ccd rm -rf /etc/openvpn/ccd
rm -rf "$install_home/ovpns" rm -rf "${install_home}/ovpns"
fi fi
if [ "${vpnStillExists}" -eq 0 ]; then if [[ "${vpnStillExists}" -eq 0 ]]; then
echo ":::" echo ":::"
echo "::: Removing pivpn system files..." 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
echo ":::" rm -rf "${setupConfigDir}"
echo "::: Other VPN ${othervpn} still present, so not" rm -rf "${pivpnFilesDir}"
echo "::: removing pivpn system files" rm -f /var/log/*pivpn*
rm -f "${setupConfigDir}/${VPN}/${setupVarsFile}" rm -f /etc/bash_completion.d/pivpn
# Restore single pivpn script and bash completion for the remaining VPN unlink "${pivpnScriptDir}"
$SUDO unlink /usr/local/bin/pivpn unlink /usr/local/bin/pivpn
$SUDO ln -s -T "${pivpnFilesDir}/scripts/${othervpn}/pivpn.sh" /usr/local/bin/pivpn else
$SUDO ln -s -T "${pivpnFilesDir}/scripts/${othervpn}/bash-completion" /etc/bash_completion.d/pivpn if [[ "${VPN}" == 'wireguard' ]]; then
# shellcheck disable=SC1091 othervpn='openvpn'
. /etc/bash_completion.d/pivpn else
fi othervpn='wireguard'
fi
echo ":::" echo ":::"
printf "::: Finished removing PiVPN from your system.\\n" echo "::: Other VPN ${othervpn} still present, so not"
printf "::: Reinstall by simply running\\n:::\\n:::\\tcurl -L https://install.pivpn.io | bash\\n:::\\n::: at any time!\\n:::\\n" 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 \
-sT "${pivpnFilesDir}/scripts/${othervpn}/pivpn.sh" \
/usr/local/bin/pivpn
${SUDO} ln \
-sT "${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(){ askreboot() {
printf "It is \\e[1mstrongly\\e[0m recommended to reboot after un-installation.\\n" printf "It is \\e[1mstrongly\\e[0m recommended to reboot "
read -p "Would you like to reboot now? [y/n]: " -n 1 -r printf "after un-installation.\\n"
echo
if [[ ${REPLY} =~ ^[Yy]$ ]]; then read -p "Would you like to reboot now? [y/n]: " -n 1 -r
printf "\\nRebooting system...\\n"
sleep 3 echo
reboot
fi if [[ "${REPLY}" =~ ^[Yy]$ ]]; then
printf "\\nRebooting system...\\n"
sleep 3
reboot
fi
} }
######### SCRIPT ########### ######### 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)" 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;; while true; do
esac 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 done

View file

@ -1,130 +1,145 @@
#!/bin/bash #!/bin/bash
###Updates pivpn scripts (Not PiVPN) ### Updates pivpn scripts (Not PiVPN)
###Main Vars ### Main Vars
pivpnrepo="https://github.com/pivpn/pivpn.git" pivpnrepo="https://github.com/pivpn/pivpn.git"
pivpnlocalpath="/etc/.pivpn" pivpnlocalpath="/etc/.pivpn"
pivpnscripts="/opt/pivpn/" pivpnscripts="/opt/pivpn/"
bashcompletiondir="/etc/bash_completion.d/" bashcompletiondir="/etc/bash_completion.d/"
# Find the rows and columns. Will default to 80x24 if it can not be detected. # 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) screen_size="$(stty size 2> /dev/null || echo 24 80)"
rows=$(echo "$screen_size" | awk '{print $1}') rows="$(echo "${screen_size}" | awk '{print $1}')"
columns=$(echo "$screen_size" | awk '{print $2}') columns="$(echo "${screen_size}" | awk '{print $2}')"
# Divide by two so the dialogs take up half of the screen, which looks nice. # Divide by two so the dialogs take up half of the screen, which looks nice.
r=$(( rows / 2 )) r=$((rows / 2))
c=$(( columns / 2 )) c=$((columns / 2))
# Unless the screen is tiny # Unless the screen is tiny
r=$(( r < 20 ? 20 : r )) r=$((r < 20 ? 20 : r))
c=$(( c < 70 ? 70 : c )) c=$((c < 70 ? 70 : c))
echo "::: The updating functionality for PiVPN scripts is temporarily disabled" # TODO: Delete this section when the updating functionality will be re-enabled
echo "::: To keep the VPN (and the system) up to date, use 'apt update' and 'apt upgrade'" ###
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 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) chooseVPNCmd=(whiptail
VPNChooseOptions=(WireGuard "" on --backtitle "Setup PiVPN"
OpenVPN "" off) --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 if VPN="$("${chooseVPNCmd[@]}" "${VPNChooseOptions[@]}" 2>&1 > /dev/tty)"; then
echo "::: Using VPN: $VPN" echo "::: Using VPN: ${VPN}"
VPN="${VPN,,}" VPN="${VPN,,}"
else else
echo "::: Cancel selected, exiting...." err "::: Cancel selected, exiting...."
exit 1 exit 1
fi fi
setupVars="/etc/pivpn/${VPN}/setupVars.conf" setupVars="/etc/pivpn/${VPN}/setupVars.conf"
if [ ! -f "${setupVars}" ]; then if [[ ! -f "${setupVars}" ]]; then
echo "::: Missing setup vars file!" err "::: Missing setup vars file!"
exit 1 exit 1
fi fi
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "${setupVars}" source "${setupVars}"
scriptusage(){ # TODO: Uncomment this function when the updating functionality
echo "::: Updates PiVPN scripts" # will be re-enabled
echo ":::" #err() {
echo "::: Usage: pivpn <-up|update> [-t|--test]" # echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
echo ":::" #}
echo "::: Commands:"
echo "::: [none] Updates from master branch" scriptusage() {
echo "::: -t, test Updates from test branch" echo "::: Updates PiVPN scripts"
echo "::: -h, help Show this usage dialog" 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 ### Functions
##Updates scripts ## Updates scripts
updatepivpnscripts(){ updatepivpnscripts() {
##We don't know what sort of changes users have made. local branch
##Lets remove first /etc/.pivpn dir then clone it back again branch="${1}"
echo "going do update PiVPN Scripts" ## We don't know what sort of changes users have made.
if [[ -d "$pivpnlocalpath" ]]; then ## Lets remove first /etc/.pivpn dir then clone it back again
if [[ -n "$pivpnlocalpath" ]]; then echo -n "Going do update PiVPN Scripts"
rm -rf "${pivpnlocalpath}/../.pivpn"
cloneandupdate if [[ -z "${branch}" ]]; then
fi echo "from ${branch} branch"
else else
cloneandupdate echo
fi fi
echo "PiVPN Scripts have been updated"
# Disabling warning for SC1090 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 ## Clone and copy pivpn scripts to /opt/pivpn
updatefromtest(){ cloneandupdate() {
##We don't know what sort of changes users have made. local branch
##Lets remove first /etc/.pivpn dir then clone it back again branch="${1}"
echo "PiVPN Scripts updating from test branch" git clone "${pivpnrepo}" "${pivpnlocalpath}"
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 if [[ -z "${branch}" ]]; then
cloneandupdate(){ git -C "${pivpnlocalpath}" checkout "${branch}"
git clone "$pivpnrepo" "$pivpnlocalpath" git -C "${pivpnlocalpath}" pull origin "${branch}"
cp "${pivpnlocalpath}"/scripts/*.sh "$pivpnscripts" fi
cp "${pivpnlocalpath}"/scripts/"$VPN"/*.sh "$pivpnscripts"
cp "${pivpnlocalpath}"/scripts/"$VPN"/bash-completion "$bashcompletiondir"
}
##same as cloneandupdate() but from test branch cp "${pivpnlocalpath}"/scripts/*.sh "${pivpnscripts}"
##and falls back to master branch again after updating cp "${pivpnlocalpath}"/scripts/"${VPN}"/*.sh "${pivpnscripts}"
cloneupdttest(){ cp "${pivpnlocalpath}"/scripts/"${VPN}"/bash-completion "${bashcompletiondir}"
git clone "$pivpnrepo" "$pivpnlocalpath"
git -C "$pivpnlocalpath" checkout test if [[ -z "${branch}" ]]; then
git -C "$pivpnlocalpath" pull origin test git -C "${pivpnlocalpath}" checkout master
cp "${pivpnlocalpath}"/scripts/*.sh "$pivpnscripts" fi
cp "${pivpnlocalpath}"/scripts/"$VPN"/*.sh "$pivpnscripts"
cp "${pivpnlocalpath}"/scripts/"$VPN"/bash-completion "$bashcompletiondir"
git -C "$pivpnlocalpath" checkout master
} }
## SCRIPT ## SCRIPT
if [[ $# -eq 0 ]]; then if [[ "$#" -eq 0 ]]; then
updatepivpnscripts updatepivpnscripts
else else
while true; do while true; do
case "$1" in case "${1}" in
-t|test) -t | test)
updatefromtest updatepivpnscripts 'test'
exit 0 exit 0
;; ;;
-h|help) -h | help)
scriptusage scriptusage
exit 0 exit 0
;; ;;
*) *)
updatepivpnscripts updatepivpnscripts
exit 0 exit 0

View file

@ -1,18 +1,22 @@
#!/bin/bash #!/bin/bash
_pivpn()
{ _pivpn() {
local cur opts local cur opts
COMPREPLY=() COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}" cur="${COMP_WORDS[COMP_CWORD]}"
dashopts="-a -c -d -l -qr -r -h -u -up -bk -off -on" 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" opts="add clients debug list qrcode remove help uninstall update"
if [ "${#COMP_WORDS[@]}" -eq 2 ]; then opts="${opts} backup (temp) off (temp) on"
if [[ ${cur} == -* ]] ; then
COMPREPLY=( "$(compgen -W "${dashopts}" -- "${cur}")" ) if [[ "${#COMP_WORDS[@]}" -eq 2 ]]; then
else if [[ "${cur}" == -* ]]; then
COMPREPLY=( "$(compgen -W "${opts}" -- "${cur}")" ) COMPREPLY=("$(compgen -W "${dashopts}" -- "${cur}")")
fi else
COMPREPLY=("$(compgen -W "${opts}" -- "${cur}")")
fi fi
return 0 fi
return 0
} }
complete -F _pivpn pivpn complete -F _pivpn pivpn

View file

@ -1,94 +1,108 @@
#!/usr/bin/env bash #!/bin/bash
# PiVPN: client status script # PiVPN: client status script
CLIENTS_FILE="/etc/wireguard/configs/clients.txt" CLIENTS_FILE="/etc/wireguard/configs/clients.txt"
if [ ! -s "$CLIENTS_FILE" ]; then if [[ ! -s "${CLIENTS_FILE}" ]]; then
echo "::: There are no clients to list" err "::: There are no clients to list"
exit 0 exit 0
fi fi
scriptusage(){ err() {
echo "::: List any connected clients to the server" echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
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(){ scriptusage() {
numfmt --to=iec-i --suffix=B "$1" 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(){ hr() {
if DUMP="$(wg show wg0 dump)"; then numfmt --to=iec-i --suffix=B "${1}"
DUMP="$(tail -n +2 <<< "$DUMP")" }
else
exit 1
fi
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[1m::: Connected Clients 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[4mLast Seen\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 while IFS= read -r LINE; do
if [ -n "${LINE}" ]; then if [[ -n "${LINE}" ]]; then
PUBLIC_KEY="$(awk '{ print $1 }' <<< "$LINE")" PUBLIC_KEY="$(awk '{ print $1 }' <<< "${LINE}")"
REMOTE_IP="$(awk '{ print $3 }' <<< "$LINE")" REMOTE_IP="$(awk '{ print $3 }' <<< "${LINE}")"
VIRTUAL_IP="$(awk '{ print $4 }' <<< "$LINE")" VIRTUAL_IP="$(awk '{ print $4 }' <<< "${LINE}")"
BYTES_RECEIVED="$(awk '{ print $6 }' <<< "$LINE")" BYTES_RECEIVED="$(awk '{ print $6 }' <<< "${LINE}")"
BYTES_SENT="$(awk '{ print $7 }' <<< "$LINE")" BYTES_SENT="$(awk '{ print $7 }' <<< "${LINE}")"
LAST_SEEN="$(awk '{ print $5 }' <<< "$LINE")" LAST_SEEN="$(awk '{ print $5 }' <<< "${LINE}")"
CLIENT_NAME="$(grep "$PUBLIC_KEY" "$CLIENTS_FILE" | awk '{ print $1 }')" CLIENT_NAME="$(grep "${PUBLIC_KEY}" "${CLIENTS_FILE}" \
if [ "$HR" = 1 ]; then | awk '{ print $1 }')"
if [ "$LAST_SEEN" -ne 0 ]; then printf "%s \t %s \t %s \t " \
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')" "${CLIENT_NAME}" \
else "${REMOTE_IP}" \
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)" "${VIRTUAL_IP/\/32/}"
fi
else if [[ "${HR}" == 1 ]]; then
if [ "$LAST_SEEN" -ne 0 ]; then printf "%s \t %s \t " \
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')" "$(hr "${BYTES_RECEIVED}")" \
else "$(hr "${BYTES_SENT}")"
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)" else
fi printf "%s \t %s \t " "${BYTES_RECEIVED}" "${BYTES_SENT}"
fi
fi 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" printf "\n"
} | column -t -s $'\t' } | column -ts $'\t'
cd /etc/wireguard || return cd /etc/wireguard || return
echo "::: Disabled clients :::"
grep '\[disabled\] ### begin' wg0.conf | sed 's/#//g; s/begin//'
echo "::: Disabled clients :::"
grep '\[disabled\] ### begin' wg0.conf | sed 's/#//g; s/begin//'
} }
if [[ $# -eq 0 ]]; then if [[ "$#" -eq 0 ]]; then
HR=1 HR=1
listClients listClients
else else
while true; do while true; do
case "$1" in case "${1}" in
-b|bytes) -b | bytes)
HR=0 HR=0
listClients listClients
exit 0 exit 0
;; ;;
-h|help) -h | help)
scriptusage scriptusage
exit 0 exit 0
;; ;;
*) *)
HR=0 HR=0
listClients listClients
exit 0 exit 0
;; ;;
esac esac
done done
fi fi

View file

@ -2,127 +2,137 @@
setupVars="/etc/pivpn/wireguard/setupVars.conf" setupVars="/etc/pivpn/wireguard/setupVars.conf"
if [ ! -f "${setupVars}" ]; then if [[ ! -f "${setupVars}" ]]; then
echo "::: Missing setup vars file!" err "::: Missing setup vars file!"
exit 1 exit 1
fi fi
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "${setupVars}" source "${setupVars}"
helpFunc(){ err() {
echo "::: Disable client conf profiles" echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
echo ":::" }
echo "::: Usage: pivpn <-off|off> [-h|--help] [-v] [<client-1> ... [<client-2>] ...] "
echo ":::" helpFunc() {
echo "::: Commands:" echo "::: Disable client conf profiles"
echo "::: [none] Interactive mode" echo ":::"
echo "::: <client> Client" echo -n "::: Usage: pivpn <-off|off> [-h|--help] [-v] "
echo "::: -y,--yes Disable client(s) without confirmation" echo "[<client-1> ... [<client-2>] ...]"
echo "::: -v Show disabled clients only" echo ":::"
echo "::: -h,--help Show this help dialog" echo "::: Commands:"
echo "::: [none] Interactive mode"
echo "::: <client> 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 # Parse input arguments
while test $# -gt 0; do while [[ "$#" -gt 0 ]]; do
_key="$1" _key="${1}"
case "$_key" in
-h|--help) case "${_key}" in
helpFunc -h | --help)
exit 0 helpFunc
;; exit 0
-y|--yes) ;;
CONFIRM=true -y | --yes)
;; CONFIRM=true
-v) ;;
DISPLAY_DISABLED=true -v)
;; DISPLAY_DISABLED=true
*) ;;
CLIENTS_TO_CHANGE+=("$1") *)
;; CLIENTS_TO_CHANGE+=("${1}")
esac ;;
shift esac
shift
done done
cd /etc/wireguard || exit cd /etc/wireguard || exit
if [ ! -s configs/clients.txt ]; then
echo "::: There are no clients to change" if [[ ! -s configs/clients.txt ]]; then
exit 1 err "::: There are no clients to change"
exit 1
fi fi
if [[ "${DISPLAY_DISABLED}" ]]; then
if [ "$DISPLAY_DISABLED" ]; then grep '\[disabled\] ### begin' wg0.conf | sed 's/#//g; s/begin//'
grep '\[disabled\] ### begin' wg0.conf | sed 's/#//g; s/begin//' exit 1
exit 1
fi fi
mapfile -t LIST < <(awk '{print $1}' configs/clients.txt) 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 while [[ "${COUNTER}" -le "${len}" ]]; do
echo "::: You can not leave this blank!" printf "%0${#len}s) %s\r\n" "${COUNTER}" "${LIST[(($COUNTER - 1))]}"
exit 1 ((COUNTER++))
fi 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 fi
CHANGED_COUNT=0 CHANGED_COUNT=0
for CLIENT_NAME in "${CLIENTS_TO_CHANGE[@]}"; do 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 ! grep -q "^${CLIENT_NAME} " configs/clients.txt; then
if [[ ${CLIENT_NAME} =~ $re ]] ; then echo -e "::: \e[1m${CLIENT_NAME}\e[0m does not exist"
CLIENT_NAME=${LIST[$((CLIENT_NAME -1))]} elif grep -q "#\[disabled\] ### begin ${CLIENT_NAME}" wg0.conf; then
fi 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 if [[ "${REPLY}" =~ ^[Yy]$ ]]; then
echo -e "::: \e[1m${CLIENT_NAME}\e[0m does not exist" # Disable the peer section from the server config
elif grep -q "#\[disabled\] ### begin ${CLIENT_NAME}" wg0.conf; then echo "${CLIENT_NAME}"
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 sed_pattern="/### begin ${CLIENT_NAME}/,"
sed_pattern="${sed_pattern}/end ${CLIENT_NAME}/ s/^/#\[disabled\] /"
# Disable the peer section from the server config sed -e "${sed_pattern}" -i wg0.conf
echo "${CLIENT_NAME}" unset sed_pattern
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
echo "::: Updated server config"
((CHANGED_COUNT++))
echo "::: Successfully disabled ${CLIENT_NAME}"
fi
fi
done done
# Restart WireGuard only if some clients were actually deleted # Restart WireGuard only if some clients were actually deleted
if [ "${CHANGED_COUNT}" -gt 0 ]; then if [[ "${CHANGED_COUNT}" -gt 0 ]]; then
if [ "${PLAT}" == 'Alpine' ]; then if [[ "${PLAT}" == 'Alpine' ]]; then
if rc-service wg-quick restart; then if rc-service wg-quick restart; then
echo "::: WireGuard reloaded" echo "::: WireGuard reloaded"
else else
echo "::: Failed to reload WireGuard" err "::: Failed to reload WireGuard"
fi fi
else else
if systemctl reload wg-quick@wg0; then if systemctl reload wg-quick@wg0; then
echo "::: WireGuard reloaded" echo "::: WireGuard reloaded"
else else
echo "::: Failed to reload WireGuard" err "::: Failed to reload WireGuard"
fi fi
fi fi
fi fi

View file

@ -2,123 +2,136 @@
setupVars="/etc/pivpn/wireguard/setupVars.conf" setupVars="/etc/pivpn/wireguard/setupVars.conf"
if [ ! -f "${setupVars}" ]; then if [[ ! -f "${setupVars}" ]]; then
echo "::: Missing setup vars file!" err "::: Missing setup vars file!"
exit 1 exit 1
fi fi
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "${setupVars}" source "${setupVars}"
helpFunc(){ err() {
echo "::: Enables client conf profiles" echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
echo ":::" }
echo "::: Usage: pivpn <-on|on> [-h|--help] [-v] [<client-1> ... [<client-2>] ...] "
echo ":::" helpFunc() {
echo "::: Commands:" echo "::: Enables client conf profiles"
echo "::: [none] Interactive mode" echo ":::"
echo "::: <client> Client" echo -n "::: Usage: pivpn <-on|on> [-h|--help] [-v] "
echo "::: -y,--yes Enable client(s) without confirmation" echo "[<client-1> ... [<client-2>] ...]"
echo "::: -v Show disabled clients only" echo ":::"
echo "::: -h,--help Show this help dialog" echo "::: Commands:"
echo "::: [none] Interactive mode"
echo "::: <client> 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 # Parse input arguments
while test $# -gt 0; do while [[ "$#" -gt 0 ]]; do
_key="$1" _key="${1}"
case "$_key" in
-h|--help) case "${_key}" in
helpFunc -h | --help)
exit 0 helpFunc
;; exit 0
-y|--yes) ;;
CONFIRM=true -y | --yes)
;; CONFIRM=true
-v) ;;
DISPLAY_DISABLED=true -v)
;; DISPLAY_DISABLED=true
*) ;;
CLIENTS_TO_CHANGE+=("$1") *)
;; CLIENTS_TO_CHANGE+=("${1}")
esac ;;
shift esac
shift
done done
cd /etc/wireguard || exit cd /etc/wireguard || exit
if [ ! -s configs/clients.txt ]; then
echo "::: There are no clients to change" if [[ ! -s configs/clients.txt ]]; then
exit 1 err "::: There are no clients to change"
exit 1
fi fi
if [ "$DISPLAY_DISABLED" ]; then if [[ "${DISPLAY_DISABLED}" ]]; then
grep '\[disabled\] ### begin' wg0.conf | sed 's/#//g; s/begin//' grep '\[disabled\] ### begin' wg0.conf | sed 's/#//g; s/begin//'
exit 1 exit 1
fi fi
mapfile -t LIST < <(awk '{print $1}' configs/clients.txt) 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 while [[ "${COUNTER}" -le "${len}" ]]; do
echo "::: You can not leave this blank!" printf "%0${#len}s) %s\r\n" "${COUNTER}" "${LIST[(($COUNTER - 1))]}"
exit 1 ((COUNTER++))
fi 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 fi
CHANGED_COUNT=0 CHANGED_COUNT=0
for CLIENT_NAME in "${CLIENTS_TO_CHANGE[@]}"; do for CLIENT_NAME in "${CLIENTS_TO_CHANGE[@]}"; do
re='^[0-9]+$'
re='^[0-9]+$' if [[ "${CLIENT_NAME}" =~ $re ]]; then
if [[ ${CLIENT_NAME} =~ $re ]] ; then CLIENT_NAME="${LIST[$((CLIENT_NAME - 1))]}"
CLIENT_NAME=${LIST[$((CLIENT_NAME -1))]} fi
fi
if ! grep -q "^${CLIENT_NAME} " configs/clients.txt; then if ! grep -q "^${CLIENT_NAME} " configs/clients.txt; then
echo -e "::: \e[1m${CLIENT_NAME}\e[0m does not exist" echo -e "::: \e[1m${CLIENT_NAME}\e[0m does not exist"
else else
if [ -n "$CONFIRM" ]; then if [[ -n "${CONFIRM}" ]]; then
REPLY="y" REPLY="y"
else else
read -r -p "Confirm you want to enable $CLIENT_NAME? [Y/n] " read -r -p "Confirm you want to enable ${CLIENT_NAME}? [Y/n] "
fi 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 sed_pattern="/begin ${CLIENT_NAME}/,"
echo "${CLIENT_NAME}" sed_pattern="${sed_pattern}/end ${CLIENT_NAME}/ s/#\[disabled\] //"
sed -e "/begin ${CLIENT_NAME}/,/end ${CLIENT_NAME}/ s/#\[disabled\] //" -i wg0.conf sed -e "${sed_pattern}" -i wg0.conf
echo "::: Updated server config" unset sed_pattern
((CHANGED_COUNT++))
echo "::: Successfully enabled ${CLIENT_NAME}"
fi
fi
echo "::: Updated server config"
((CHANGED_COUNT++))
echo "::: Successfully enabled ${CLIENT_NAME}"
fi
fi
done done
# Restart WireGuard only if some clients were actually deleted # Restart WireGuard only if some clients were actually deleted
if [ "${CHANGED_COUNT}" -gt 0 ]; then if [[ "${CHANGED_COUNT}" -gt 0 ]]; then
if [ "${PLAT}" == 'Alpine' ]; then if [[ "${PLAT}" == 'Alpine' ]]; then
if rc-service wg-quick restart; then if rc-service wg-quick restart; then
echo "::: WireGuard reloaded" echo "::: WireGuard reloaded"
else else
echo "::: Failed to reload WireGuard" err "::: Failed to reload WireGuard"
fi fi
else else
if systemctl reload wg-quick@wg0; then if systemctl reload wg-quick@wg0; then
echo "::: WireGuard reloaded" echo "::: WireGuard reloaded"
else else
echo "::: Failed to reload WireGuard" err "::: Failed to reload WireGuard"
fi fi
fi fi
fi fi

View file

@ -1,33 +1,34 @@
#!/bin/bash #!/bin/bash
err() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
}
cd /etc/wireguard/configs || exit cd /etc/wireguard/configs || exit
if [ ! -s clients.txt ]; then
echo "::: There are no clients to list" if [[ ! -s clients.txt ]]; then
exit 1 err "::: There are no clients to list"
exit 1
fi fi
printf "\e[1m::: Clients Summary :::\e[0m\n" printf "\e[1m::: Clients Summary :::\e[0m\n"
# Present the user with a summary of the clients, fetching info from dates. # 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" 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")"
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. # Dates are converted from UNIX time to human readable.
CD_FORMAT="$(date -d @"$CREATION_DATE" +'%d %b %Y, %H:%M, %Z')" CD_FORMAT="$(date -d @"${CREATION_DATE}" +'%d %b %Y, %H:%M, %Z')"
echo -e "${CLIENT_NAME} \t ${PUBLIC_KEY} \t ${CD_FORMAT}"
echo -e "$CLIENT_NAME \t $PUBLIC_KEY \t $CD_FORMAT" done < clients.txt
done < clients.txt
} | column -t -s $'\t' } | column -t -s $'\t'
cd /etc/wireguard || return cd /etc/wireguard || return
echo "::: Disabled clients :::" echo "::: Disabled clients :::"
grep '\[disabled\] ### begin' wg0.conf | sed 's/#//g; s/begin//' grep '\[disabled\] ### begin' wg0.conf | sed 's/#//g; s/begin//'

View file

@ -1,108 +1,113 @@
#!/bin/bash #!/bin/bash
######## Some vars that might be empty # Some vars that might be empty but need to be defined for checks
# but need to be defined for checks
pivpnPERSISTENTKEEPALIVE="" pivpnPERSISTENTKEEPALIVE=""
pivpnDNS2="" pivpnDNS2=""
setupVars="/etc/pivpn/wireguard/setupVars.conf" setupVars="/etc/pivpn/wireguard/setupVars.conf"
# shellcheck disable=SC2154
userGroup="${install_user}:${install_user}"
if [ ! -f "${setupVars}" ]; then if [[ ! -f "${setupVars}" ]]; then
echo "::: Missing setup vars file!" err "::: Missing setup vars file!"
exit 1 exit 1
fi fi
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "${setupVars}" source "${setupVars}"
helpFunc(){ err() {
echo "::: Create a client conf profile" echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
echo ":::" }
echo "::: Usage: pivpn <-a|add> [-n|--name <arg>] [-h|--help]"
echo ":::" helpFunc() {
echo "::: Commands:" echo "::: Create a client conf profile"
echo "::: [none] Interactive mode" echo ":::"
echo "::: -n,--name Name for the Client (default: '$HOSTNAME')" echo "::: Usage: pivpn <-a|add> [-n|--name <arg>] [-h|--help]"
echo "::: -h,--help Show this help dialog" 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 # Parse input arguments
while test $# -gt 0; do while [[ "$#" -gt 0 ]]; do
_key="$1" _key="${1}"
case "$_key" in
-n|--name|--name=*) case "${_key}" in
_val="${_key##--name=}" -n | --name | --name=*)
if test "$_val" = "$_key"; then _val="${_key##--name=}"
test $# -lt 2 && echo "::: Missing value for the optional argument '$_key'." && exit 1
_val="$2" if [[ "${_val}" == "${_key}" ]]; then
shift [[ "$#" -lt 2 ]] \
fi && err "::: Missing value for the optional argument '${_key}'." \
CLIENT_NAME="$_val" && exit 1
;;
-h|--help) _val="${2}"
helpFunc shift
exit 0 fi
;;
*) CLIENT_NAME="${_val}"
echo "::: Error: Got an unexpected argument '$1'" ;;
helpFunc -h | --help)
exit 1 helpFunc
;; exit 0
esac ;;
shift *)
err "::: Error: Got an unexpected argument '${1}'"
helpFunc
exit 1
;;
esac
shift
done done
# Disabling SC2154, variables sourced externaly # Disabling SC2154, variables sourced externaly
# shellcheck disable=SC2154 # shellcheck disable=SC2154
# The home folder variable was sourced from the settings file. # The home folder variable was sourced from the settings file.
if [ ! -d "${install_home}/configs" ]; then if [[ ! -d "${install_home}/configs" ]]; then
mkdir "${install_home}/configs" mkdir "${install_home}/configs"
chown "${install_user}":"${install_user}" "${install_home}/configs" chown "${userGroup}" "${install_home}/configs"
chmod 0750 "${install_home}/configs" chmod 0750 "${install_home}/configs"
fi fi
cd /etc/wireguard || exit cd /etc/wireguard || exit
if [ -z "${CLIENT_NAME}" ]; then if [[ -z "${CLIENT_NAME}" ]]; then
read -r -p "Enter a Name for the Client: " CLIENT_NAME 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 fi
if [[ "${CLIENT_NAME}" =~ [^a-zA-Z0-9.@_-] ]]; then wg genkey \
echo "Name can only contain alphanumeric characters and these characters (.-@_)." | tee "keys/${CLIENT_NAME}_priv" \
exit 1 | wg pubkey > "keys/${CLIENT_NAME}_pub"
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 genpsk | tee "keys/${CLIENT_NAME}_psk" &> /dev/null wg genpsk | tee "keys/${CLIENT_NAME}_psk" &> /dev/null
echo "::: Client Keys generated" echo "::: Client Keys generated"
# Find an unused number for the last octet of the client IP # Find an unused number for the last octet of the client IP
for i in {2..254}; do for i in {2..254}; do
if ! grep -q " $i$" configs/clients.txt; then if ! grep -q " ${i}$" configs/clients.txt; then
COUNT="$i" COUNT="${i}"
echo "${CLIENT_NAME} $(<keys/"${CLIENT_NAME}"_pub) $(date +%s) ${COUNT}" >> configs/clients.txt echo "${CLIENT_NAME} $(< keys/"${CLIENT_NAME}"_pub) $(date +%s) ${COUNT}" \
break | tee -a configs/clients.txt > /dev/null
fi break
fi
done done
# Disabling SC2154, variables sourced externaly # Disabling SC2154, variables sourced externaly
@ -110,88 +115,95 @@ done
NET_REDUCED="${pivpnNET::-2}" NET_REDUCED="${pivpnNET::-2}"
# shellcheck disable=SC2154 # shellcheck disable=SC2154
if [ "$pivpnenableipv6" == "1" ]; then {
echo "[Interface] echo '[Interface]'
PrivateKey = $(cat "keys/${CLIENT_NAME}_priv") echo "PrivateKey = $(cat "keys/${CLIENT_NAME}_priv")"
Address = ${NET_REDUCED}.${COUNT}/${subnetClass},${pivpnNETv6}${COUNT}/${subnetClassv6}" > "configs/${CLIENT_NAME}.conf" echo -n "Address = ${NET_REDUCED}.${COUNT}/${subnetClass}"
else
echo "[Interface]
PrivateKey = $(cat "keys/${CLIENT_NAME}_priv")
Address = ${NET_REDUCED}.${COUNT}/${subnetClass}" > "configs/${CLIENT_NAME}.conf"
fi
# shellcheck disable=SC2154 if [[ "${pivpnenableipv6}" == 1 ]]; then
echo -n "DNS = ${pivpnDNS1}" >> "configs/${CLIENT_NAME}.conf" echo ",${pivpnNETv6}${COUNT}/${subnetClassv6}"
if [ -n "${pivpnDNS2}" ]; then else
echo ", ${pivpnDNS2}" >> "configs/${CLIENT_NAME}.conf" echo
else fi
echo >> "configs/${CLIENT_NAME}.conf"
fi
echo >> "configs/${CLIENT_NAME}.conf"
# shellcheck disable=SC2154 echo -n "DNS = ${pivpnDNS1}"
echo "[Peer]
PublicKey = $(cat keys/server_pub) if [[ -n "${pivpnDNS2}" ]]; then
PresharedKey = $(cat "keys/${CLIENT_NAME}_psk") echo ", ${pivpnDNS2}"
Endpoint = ${pivpnHOST}:${pivpnPORT} else
AllowedIPs = ${ALLOWED_IPS}" >> "configs/${CLIENT_NAME}.conf" 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" echo "::: Client config generated"
if [ "$pivpnenableipv6" == "1" ]; then {
echo "### begin ${CLIENT_NAME} ### echo "### begin ${CLIENT_NAME} ###"
[Peer] echo '[Peer]'
PublicKey = $(cat "keys/${CLIENT_NAME}_pub") echo "PublicKey = $(cat "keys/${CLIENT_NAME}_pub")"
PresharedKey = $(cat "keys/${CLIENT_NAME}_psk") echo "PresharedKey = $(cat "keys/${CLIENT_NAME}_psk")"
AllowedIPs = ${NET_REDUCED}.${COUNT}/32,${pivpnNETv6}${COUNT}/128 echo -n "AllowedIPs = ${NET_REDUCED}.${COUNT}/32"
### end ${CLIENT_NAME} ###" >> wg0.conf
else if [[ "${pivpnenableipv6}" == 1 ]]; then
echo "### begin ${CLIENT_NAME} ### echo ",${pivpnNETv6}${COUNT}/128"
[Peer] else
PublicKey = $(cat "keys/${CLIENT_NAME}_pub") echo
PresharedKey = $(cat "keys/${CLIENT_NAME}_psk") fi
AllowedIPs = ${NET_REDUCED}.${COUNT}/32
### end ${CLIENT_NAME} ###" >> wg0.conf echo "### end ${CLIENT_NAME} ###"
fi } >> wg0.conf
echo "::: Updated server config" echo "::: Updated server config"
if [ -f /etc/pivpn/hosts.wireguard ]; then if [[ -f /etc/pivpn/hosts.wireguard ]]; then
echo "${NET_REDUCED}.${COUNT} ${CLIENT_NAME}.pivpn" >> /etc/pivpn/hosts.wireguard echo "${NET_REDUCED}.${COUNT} ${CLIENT_NAME}.pivpn" \
if [ "$pivpnenableipv6" == "1" ]; then | tee -a /etc/pivpn/hosts.wireguard > /dev/null
echo "${pivpnNETv6}${COUNT} ${CLIENT_NAME}.pivpn" >> /etc/pivpn/hosts.wireguard
fi if [[ "${pivpnenableipv6}" == 1 ]]; then
if killall -SIGHUP pihole-FTL; then echo "${pivpnNETv6}${COUNT} ${CLIENT_NAME}.pivpn" \
echo "::: Updated hosts file for Pi-hole" | tee -a /etc/pivpn/hosts.wireguard > /dev/null
else fi
echo "::: Failed to reload pihole-FTL configuration"
fi if killall -SIGHUP pihole-FTL; then
echo "::: Updated hosts file for Pi-hole"
else
err "::: Failed to reload pihole-FTL configuration"
fi
fi fi
if [ "${PLAT}" == 'Alpine' ]; then if [[ "${PLAT}" == 'Alpine' ]]; then
if rc-service wg-quick restart; then if rc-service wg-quick restart; then
echo "::: WireGuard reloaded" echo "::: WireGuard reloaded"
else else
echo "::: Failed to reload WireGuard" err "::: Failed to reload WireGuard"
fi fi
else else
if systemctl reload wg-quick@wg0; then if systemctl reload wg-quick@wg0; then
echo "::: WireGuard reloaded" echo "::: WireGuard reloaded"
else else
echo "::: Failed to reload WireGuard" err "::: Failed to reload WireGuard"
fi fi
fi fi
cp "configs/${CLIENT_NAME}.conf" "${install_home}/configs/${CLIENT_NAME}.conf" 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" chmod 640 "${install_home}/configs/${CLIENT_NAME}.conf"
echo "======================================================================" echo "======================================================================"
echo -e "::: Done! \e[1m${CLIENT_NAME}.conf successfully created!\e[0m" 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 "::: 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 -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 "::: to generate a QR Code you can scan with the mobile app."

View file

@ -3,127 +3,160 @@
CHECK_PKG_INSTALLED='dpkg-query -s' CHECK_PKG_INSTALLED='dpkg-query -s'
if grep -qsEe "^NAME\=['\"]?Alpine[a-zA-Z ]*['\"]?$" /etc/os-release; then 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 fi
# Must be root to use this tool # Must be root to use this tool
if [ $EUID -ne 0 ]; then if [[ "${EUID}" -ne 0 ]]; then
if eval "${CHECK_PKG_INSTALLED} sudo" &> /dev/null; then if ${CHECK_PKG_INSTALLED} sudo &> /dev/null; then
export SUDO="sudo" export SUDO="sudo"
else else
echo "::: Please install sudo or run this as root." err "::: Please install sudo or run this as root."
exit 1 exit 1
fi fi
fi fi
scriptdir="/opt/pivpn" scriptdir="/opt/pivpn"
vpn="wireguard" vpn="wireguard"
makeConf(){ err() {
shift echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
$SUDO ${scriptdir}/${vpn}/makeCONF.sh "$@"
exit "$?"
} }
listConnected(){ makeConf() {
shift shift
$SUDO ${scriptdir}/${vpn}/clientSTAT.sh "$@" ${SUDO} "${scriptdir}/${vpn}/makeCONF.sh" "$@"
exit "$?" exit "${?}"
} }
debug(){ listConnected() {
echo "::: Generating Debug Output" shift
$SUDO ${scriptdir}/${vpn}/pivpnDEBUG.sh | tee /tmp/debug.log ${SUDO} "${scriptdir}/${vpn}/clientSTAT.sh" "$@"
echo "::: " exit "${?}"
echo "::: Debug output completed above."
echo "::: Copy saved to /tmp/debug.log"
echo "::: "
exit "$?"
} }
listClients(){ debug() {
$SUDO ${scriptdir}/${vpn}/listCONF.sh echo "::: Generating Debug Output"
exit "$?"
${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(){ listClients() {
shift ${SUDO} "${scriptdir}/${vpn}/listCONF.sh"
$SUDO ${scriptdir}/${vpn}/qrcodeCONF.sh "$@" exit "${?}"
exit "$?"
} }
removeClient(){ showQrcode() {
shift shift
$SUDO ${scriptdir}/${vpn}/removeCONF.sh "$@" ${SUDO} "${scriptdir}/${vpn}/qrcodeCONF.sh" "$@"
exit "$?" exit "${?}"
} }
disableClient(){ removeClient() {
shift shift
$SUDO ${scriptdir}/${vpn}/disableCONF.sh "$@" ${SUDO} "${scriptdir}/${vpn}/removeCONF.sh" "$@"
exit "$?" exit "${?}"
} }
enableClient(){ disableClient() {
shift shift
$SUDO ${scriptdir}/${vpn}/enableCONF.sh "$@" ${SUDO} "${scriptdir}/${vpn}/disableCONF.sh" "$@"
exit "$?" exit "${?}"
} }
uninstallServer(){ enableClient() {
$SUDO ${scriptdir}/uninstall.sh "${vpn}" shift
exit "$?" ${SUDO} "${scriptdir}/${vpn}/enableCONF.sh" "$@"
exit "${?}"
} }
updateScripts(){ uninstallServer() {
shift ${SUDO} "${scriptdir}/uninstall.sh" "${vpn}"
$SUDO ${scriptdir}/update.sh "$@" exit "${?}"
exit "$?"
} }
backup(){ updateScripts() {
$SUDO ${scriptdir}/backup.sh "${vpn}" shift
exit "$?" ${SUDO} "${scriptdir}/update.sh" "$@"
exit "${?}"
} }
showHelp(){ backup() {
echo "::: Control all PiVPN specific functions!" ${SUDO} "${scriptdir}/backup.sh" "${vpn}"
echo ":::" exit "${?}"
echo "::: Usage: pivpn <command> [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
} }
if [ $# = 0 ]; then showHelp() {
showHelp echo "::: Control all PiVPN specific functions!"
echo ":::"
echo "::: Usage: pivpn <command> [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 fi
# Handle redirecting to specific functions based on arguments # Handle redirecting to specific functions based on arguments
case "$1" in case "${1}" in
"-a" | "add" ) makeConf "$@";; "-a" | "add")
"-c" | "clients" ) listConnected "$@";; makeConf "$@"
"-d" | "debug" ) debug;; ;;
"-l" | "list" ) listClients;; "-c" | "clients")
"-qr" | "qrcode" ) showQrcode "$@";; listConnected "$@"
"-r" | "remove" ) removeClient "$@";; ;;
"-off" | "off" ) disableClient "$@";; "-d" | "debug")
"-on" | "on" ) enableClient "$@";; debug
"-h" | "help" ) showHelp;; ;;
"-u" | "uninstall" ) uninstallServer;; "-l" | "list")
"-up" | "update" ) updateScripts "$@" ;; listClients
"-bk" | "backup" ) backup ;; ;;
* ) showHelp;; "-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 esac

View file

@ -1,11 +1,15 @@
#!/usr/bin/env bash #!/bin/bash
# This scripts runs as root # This scripts runs as root
setupVars="/etc/pivpn/wireguard/setupVars.conf" setupVars="/etc/pivpn/wireguard/setupVars.conf"
if [ ! -f "${setupVars}" ]; then err() {
echo "::: Missing setup vars file!" echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
exit 1 }
if [[ ! -f "${setupVars}" ]]; then
err "::: Missing setup vars file!"
exit 1
fi fi
# shellcheck disable=SC1090 # shellcheck disable=SC1090
@ -15,51 +19,75 @@ echo -e "::::\t\t\e[4mPiVPN debug\e[0m\t\t ::::"
printf "=============================================\n" printf "=============================================\n"
echo -e "::::\t\t\e[4mLatest commit\e[0m\t\t ::::" echo -e "::::\t\t\e[4mLatest commit\e[0m\t\t ::::"
echo -n "Branch: " echo -n "Branch: "
git --git-dir /usr/local/src/pivpn/.git rev-parse --abbrev-ref HEAD 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" printf "=============================================\n"
echo -e "::::\t \e[4mInstallation settings\e[0m \t ::::" echo -e "::::\t \e[4mInstallation settings\e[0m \t ::::"
# Disabling SC2154 warning, variable is sourced externaly and may vary # Disabling SC2154 warning, variable is sourced externaly and may vary
# shellcheck disable=SC2154 # shellcheck disable=SC2154
sed "s/$pivpnHOST/REDACTED/" < ${setupVars} sed "s/${pivpnHOST}/REDACTED/" < "${setupVars}"
printf "=============================================\n" printf "=============================================\n"
echo -e ":::: \e[4mServer configuration shown below\e[0m ::::" echo -e ":::: \e[4mServer configuration shown below\e[0m ::::"
cd /etc/wireguard/keys || exit cd /etc/wireguard/keys || exit
cp ../wg0.conf ../wg0.tmp cp ../wg0.conf ../wg0.tmp
# Replace every key in the server configuration with just its file name # Replace every key in the server configuration with just its file name
for k in *; do for k in *; do
sed "s#$(<"$k")#$k#" -i ../wg0.tmp sed "s#$(< "${k}")#${k}#" -i ../wg0.tmp
done done
cat ../wg0.tmp cat ../wg0.tmp
rm ../wg0.tmp rm ../wg0.tmp
printf "=============================================\n" printf "=============================================\n"
echo -e ":::: \e[4mClient configuration shown below\e[0m ::::" echo -e ":::: \e[4mClient configuration shown below\e[0m ::::"
EXAMPLE="$(head -1 /etc/wireguard/configs/clients.txt | awk '{print $1}')" EXAMPLE="$(head -1 /etc/wireguard/configs/clients.txt | awk '{print $1}')"
if [ -n "$EXAMPLE" ]; then
cp ../configs/"$EXAMPLE".conf ../configs/"$EXAMPLE".tmp if [[ -n "${EXAMPLE}" ]]; then
for k in *; do cp ../configs/"${EXAMPLE}".conf ../configs/"${EXAMPLE}".tmp
sed "s#$(<"$k")#$k#" -i ../configs/"$EXAMPLE".tmp
done for k in *; do
sed "s/$pivpnHOST/REDACTED/" < ../configs/"$EXAMPLE".tmp sed "s#$(< "${k}")#${k}#" -i ../configs/"${EXAMPLE}".tmp
rm ../configs/"$EXAMPLE".tmp done
sed "s/${pivpnHOST}/REDACTED/" < ../configs/"${EXAMPLE}".tmp
rm ../configs/"${EXAMPLE}".tmp
else else
echo "::: There are no clients yet" echo "::: There are no clients yet"
fi fi
printf "=============================================\n" 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 ls -LR /etc/wireguard
printf "=============================================\n" printf "=============================================\n"
echo -e "::::\t\t\e[4mSelf check\e[0m\t\t ::::" echo -e "::::\t\t\e[4mSelf check\e[0m\t\t ::::"
/opt/pivpn/self_check.sh "${VPN}" /opt/pivpn/self_check.sh "${VPN}"
printf "=============================================\n" printf "=============================================\n"
echo -e ":::: Having trouble connecting? Take a look at the FAQ:" echo -e ":::: Having trouble connecting? Take a look at the FAQ:"
echo -e ":::: \e[1mhttps://docs.pivpn.io/faq\e[0m" echo -e ":::: \e[1mhttps://docs.pivpn.io/faq\e[0m"
printf "=============================================\n" printf "=============================================\n"
echo -e ":::: \e[1mWARNING\e[0m: This script should have automatically masked sensitive ::::" echo -ne ":::: \e[1mWARNING\e[0m: This script should have "
echo -e ":::: information, however, still make sure that \e[4mPrivateKey\e[0m, \e[4mPublicKey\e[0m ::::" echo -e "automatically masked sensitive ::::"
echo -e ":::: and \e[4mPresharedKey\e[0m are masked before reporting an issue. An example key ::::" echo -ne ":::: information, however, still make sure that "
echo ":::: that you should NOT see in this log looks like this: ::::" echo -e "\e[4mPrivateKey\e[0m, \e[4mPublicKey\e[0m ::::"
echo ":::: YIAoJVsdIeyvXfGGDDadHh6AxsMRymZTnnzZoAb9cxRe ::::" 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" printf "=============================================\n"
echo -e "::::\t\t\e[4mDebug complete\e[0m\t\t ::::" echo -e "::::\t\t\e[4mDebug complete\e[0m\t\t ::::"

View file

@ -1,76 +1,91 @@
#!/bin/bash #!/bin/bash
helpFunc(){ err() {
echo "::: Show the qrcode of a client for use with the mobile app" echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
echo ":::" }
echo "::: Usage: pivpn <-qr|qrcode> [-h|--help] [Options] [<client-1>] ... [<client-n>] ..."
echo ":::" helpFunc() {
echo "::: Options:" echo "::: Show the qrcode of a client for use with the mobile app"
echo "::: -a256|ansi256 Shows QR Code in ansi256 characters" echo ":::"
echo "::: Commands:" echo -n "::: Usage: pivpn <-qr|qrcode> [-h|--help] [Options] "
echo "::: [none] Interactive mode" echo "[<client-1> ... [<client-2>] ...]"
echo "::: <client> Client(s) to show" echo ":::"
echo "::: -h,--help Show this help dialog" echo "::: Options:"
echo "::: -a256|ansi256 Shows QR Code in ansi256 characters"
echo "::: Commands:"
echo "::: [none] Interactive mode"
echo "::: <client> Client(s) to show"
echo "::: -h,--help Show this help dialog"
} }
# Parse input arguments # Parse input arguments
encoding="ansiutf8" encoding="ansiutf8"
while test $# -gt 0; do
_key="$1" while [[ "$#" -gt 0 ]]; do
case "$_key" in _key="${1}"
-h|--help)
helpFunc case "${_key}" in
exit 0 -h | --help)
;; helpFunc
-a256|--ansi256) exit 0
encoding="ansi256" ;;
;; -a256 | --ansi256)
*) encoding="ansi256"
CLIENTS_TO_SHOW+=("$1") ;;
;; *)
esac CLIENTS_TO_SHOW+=("${1}")
shift ;;
esac
shift
done done
cd /etc/wireguard/configs || exit cd /etc/wireguard/configs || exit
if [ ! -s clients.txt ]; then
echo "::: There are no clients to show" if [[ ! -s clients.txt ]]; then
exit 1 err "::: There are no clients to show"
exit 1
fi fi
mapfile -t LIST < <(awk '{print $1}' clients.txt) mapfile -t LIST < <(awk '{print $1}' clients.txt)
if [ "${#CLIENTS_TO_SHOW[@]}" -eq 0 ]; then
echo -e "::\e[4m Client list \e[0m::" if [[ "${#CLIENTS_TO_SHOW[@]}" -eq 0 ]]; then
len=${#LIST[@]} echo -e "::\e[4m Client list \e[0m::"
COUNTER=1 len="${#LIST[@]}"
while [ $COUNTER -le "${len}" ]; do COUNTER=1
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 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 -n "Please enter the Index/Name of the Client to show: "
echo "::: You can not leave this blank!" read -r CLIENTS_TO_SHOW
exit 1
fi if [[ -z "${CLIENTS_TO_SHOW}" ]]; then
err "::: You can not leave this blank!"
exit 1
fi
fi fi
for CLIENT_NAME in "${CLIENTS_TO_SHOW[@]}"; do for CLIENT_NAME in "${CLIENTS_TO_SHOW[@]}"; do
re='^[0-9]+$' re='^[0-9]+$'
if [[ ${CLIENT_NAME:0:1} == "-" ]]; then
echo "${CLIENT_NAME} is not a valid client name or option" if [[ "${CLIENT_NAME:0:1}" == "-" ]]; then
exit 1 err "${CLIENT_NAME} is not a valid client name or option"
elif [[ ${CLIENT_NAME} =~ $re ]] ; then exit 1
CLIENT_NAME=${LIST[$((CLIENT_NAME -1))]} elif [[ "${CLIENT_NAME}" =~ $re ]]; then
fi CLIENT_NAME="${LIST[$((CLIENT_NAME - 1))]}"
if grep -qw "${CLIENT_NAME}" clients.txt; then fi
echo -e "::: Showing client \e[1m${CLIENT_NAME}\e[0m below"
echo "=====================================================================" if grep -qw "${CLIENT_NAME}" clients.txt; then
qrencode -t "${encoding}" < "${CLIENT_NAME}.conf" echo -e "::: Showing client \e[1m${CLIENT_NAME}\e[0m below"
echo "=====================================================================" echo "====================================================================="
else
echo -e "::: \e[1m${CLIENT_NAME}\e[0m does not exist" qrencode -t "${encoding}" < "${CLIENT_NAME}.conf"
fi
echo "====================================================================="
else
echo -e "::: \e[1m${CLIENT_NAME}\e[0m does not exist"
fi
done done

View file

@ -2,159 +2,182 @@
setupVars="/etc/pivpn/wireguard/setupVars.conf" setupVars="/etc/pivpn/wireguard/setupVars.conf"
if [ ! -f "${setupVars}" ]; then if [[ ! -f "${setupVars}" ]]; then
echo "::: Missing setup vars file!" err "::: Missing setup vars file!"
exit 1 exit 1
fi fi
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "${setupVars}" source "${setupVars}"
helpFunc(){ err() {
echo "::: Remove a client conf profile" echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
echo ":::" }
echo "::: Usage: pivpn <-r|remove> [-y|--yes] [-h|--help] [<client-1>] ... [<client-n>] ..."
echo ":::" helpFunc() {
echo "::: Commands:" echo "::: Remove a client conf profile"
echo "::: [none] Interactive mode" echo ":::"
echo "::: <client> Client(s) to remove" echo -n "::: Usage: pivpn <-r|remove> [-y|--yes] [-h|--help] "
echo "::: -y,--yes Remove Client(s) without confirmation" echo "[<client-1> ... [<client-2>] ...]"
echo "::: -h,--help Show this help dialog" echo ":::"
echo "::: Commands:"
echo "::: [none] Interactive mode"
echo "::: <client> Client(s) to remove"
echo "::: -y,--yes Remove Client(s) without confirmation"
echo "::: -h,--help Show this help dialog"
} }
# Parse input arguments # Parse input arguments
while test $# -gt 0 while [[ "$#" -gt 0 ]]; do
do _key="${1}"
_key="$1"
case "$_key" in case "${_key}" in
-h|--help) -h | --help)
helpFunc helpFunc
exit 0 exit 0
;; ;;
-y|--yes) -y | --yes)
CONFIRM=true CONFIRM=true
;; ;;
*) *)
CLIENTS_TO_REMOVE+=("$1") CLIENTS_TO_REMOVE+=("${1}")
;; ;;
esac esac
shift
shift
done done
cd /etc/wireguard || exit cd /etc/wireguard || exit
if [ ! -s configs/clients.txt ]; then
echo "::: There are no clients to remove" if [[ ! -s configs/clients.txt ]]; then
exit 1 err "::: There are no clients to remove"
exit 1
fi fi
mapfile -t LIST < <(awk '{print $1}' configs/clients.txt) 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 while [[ "${COUNTER}" -le "${len}" ]]; do
echo "::: You can not leave this blank!" printf "%0${#len}s) %s\r\n" "${COUNTER}" "${LIST[(($COUNTER - 1))]}"
exit 1 ((COUNTER++))
fi 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 fi
DELETED_COUNT=0 DELETED_COUNT=0
for CLIENT_NAME in "${CLIENTS_TO_REMOVE[@]}"; do for CLIENT_NAME in "${CLIENTS_TO_REMOVE[@]}"; do
re='^[0-9]+$'
re='^[0-9]+$' if [[ "${CLIENT_NAME}" =~ $re ]]; then
if [[ ${CLIENT_NAME} =~ $re ]] ; then CLIENT_NAME="${LIST[$((CLIENT_NAME - 1))]}"
CLIENT_NAME=${LIST[$((CLIENT_NAME -1))]} fi
fi
if ! grep -q "^${CLIENT_NAME} " configs/clients.txt; then if ! grep -q "^${CLIENT_NAME} " configs/clients.txt; then
echo -e "::: \e[1m${CLIENT_NAME}\e[0m does not exist" 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 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 [ -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
fi 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 done
# Restart WireGuard only if some clients were actually deleted # Restart WireGuard only if some clients were actually deleted
if [ "${DELETED_COUNT}" -gt 0 ]; then if [[ "${DELETED_COUNT}" -gt 0 ]]; then
if [ "${PLAT}" == 'Alpine' ]; then if [[ "${PLAT}" == 'Alpine' ]]; then
if rc-service wg-quick restart; then if rc-service wg-quick restart; then
echo "::: WireGuard reloaded" echo "::: WireGuard reloaded"
else
echo "::: Failed to reload WireGuard"
fi
else else
if systemctl reload wg-quick@wg0; then err "::: Failed to reload WireGuard"
echo "::: WireGuard reloaded"
else
echo "::: Failed to reload WireGuard"
fi
fi fi
else
if systemctl reload wg-quick@wg0; then
echo "::: WireGuard reloaded"
else
err "::: Failed to reload WireGuard"
fi
fi
fi fi