wireguard-install/wireguard-install.sh
2020-08-23 09:39:16 +00:00

445 lines
15 KiB
Bash

#!/bin/bash
# Secure WireGuard server installer for Debian, Ubuntu, CentOS, Fedora and Arch Linux
# https://github.com/angristan/wireguard-install
function isRoot() {
if [ "${EUID}" -ne 0 ]; then
echo "You need to run this script as root"
exit 1
fi
}
function checkVirt() {
if [ "$(systemd-detect-virt)" == "openvz" ]; then
echo "OpenVZ is not supported"
exit 1
fi
if [ "$(systemd-detect-virt)" == "lxc" ]; then
echo "LXC is not supported (yet)."
echo "WireGuard can technically run in an LXC container,"
echo "but the kernel module has to be installed on the host,"
echo "the container has to be run with some specific parameters"
echo "and only the tools need to be installed in the container."
exit 1
fi
}
function checkOS() {
# Check OS version
if [[ -e /etc/debian_version ]]; then
source /etc/os-release
OS="${ID}" # debian or ubuntu
if [[ -e /etc/debian_version ]]; then
if [[ ${ID} == "debian" || ${ID} == "raspbian" ]]; then
if [[ ${VERSION_ID} -ne 10 ]]; then
echo "Your version of Debian (${VERSION_ID}) is not supported. Please use Debian 10 Buster"
exit 1
fi
fi
fi
elif [[ -e /etc/fedora-release ]]; then
source /etc/os-release
OS="${ID}"
elif [[ -e /etc/centos-release ]]; then
OS=centos
elif [[ -e /etc/arch-release ]]; then
OS=arch
else
echo "Looks like you aren't running this installer on a Debian, Ubuntu, Fedora, CentOS or Arch Linux system"
exit 1
fi
}
function initialCheck() {
isRoot
checkVirt
checkOS
}
function installQuestions() {
echo "Welcome to the WireGuard installer!"
echo "The git repository is available at: https://github.com/angristan/wireguard-install"
echo ""
echo "I need to ask you a few questions before starting the setup."
echo "You can leave the default options and just press enter if you are ok with them."
echo ""
# Detect public IPv4 or IPv6 address and pre-fill for the user
SERVER_PUB_IP=${SERVER_PUB_IP:-$(ip -4 addr | sed -ne 's|^.* inet \([^/]*\)/.* scope global.*$|\1|p' | head -1)}
if [[ -z ${SERVER_PUB_IP} ]]; then
# Detect public IPv6 address
SERVER_PUB_IP=$(ip -6 addr | sed -ne 's|^.* inet6 \([^/]*\)/.* scope global.*$|\1|p' | head -1)
fi
APPROVE_IP=${APPROVE_IP:-n}
if [[ ${APPROVE_IP} =~ n ]]; then
read -rp "IPv4 or IPv6 public address: " -e -i "${SERVER_PUB_IP}" SERVER_PUB_IP
fi
# Detect public interface and pre-fill for the user
SERVER_NIC=${SERVER_NIC:-$(ip -4 route ls | grep default | grep -Po '(?<=dev )(\S+)' | head -1)}
if [[ -z $SERVER_NIC ]]; then
SERVER_NIC=$(ip -6 route show default | sed -ne 's/^default .* dev \([^ ]*\) .*$/\1/p')
fi
APPROVE_NIC=${APPROVE_NIC:-n}
until [[ ${SERVER_PUB_NIC} =~ ^[a-zA-Z0-9_]+$ || ${APPROVE_NIC} =~ n ]]; do
read -rp "Public interface: " -e -i "${SERVER_NIC}" SERVER_PUB_NIC
done
until [[ ${SERVER_WG_NIC} =~ ^[a-zA-Z0-9_]+$ ]]; do
read -rp "WireGuard interface name: " -e -i wg0 SERVER_WG_NIC
done
until [[ ${SERVER_WG_IPV4} =~ ^([0-9]{1,3}\.){3} ]]; do
read -rp "Server's WireGuard IPv4: " -e -i 10.66.66.1 SERVER_WG_IPV4
done
until [[ ${SERVER_WG_IPV6} =~ ^([a-f0-9]{1,4}:){3,4}: ]]; do
read -rp "Server's WireGuard IPv6: " -e -i fd42:42:42::1 SERVER_WG_IPV6
done
# Generate random number within private ports range
RANDOM_PORT=$(shuf -i49152-65535 -n1)
until [[ ${SERVER_PORT} =~ ^[0-9]+$ ]] && [ "${SERVER_PORT}" -ge 1 ] && [ "${SERVER_PORT}" -le 65535 ]; do
read -rp "Server's WireGuard port [1-65535]: " -e -i "${RANDOM_PORT}" SERVER_PORT
done
# Adguard DNS by default
until [[ ${CLIENT_DNS_1} =~ ^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ ]]; do
read -rp "First DNS resolver to use for the clients: " -e -i 176.103.130.130 CLIENT_DNS_1
done
until [[ ${CLIENT_DNS_2} =~ ^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ ]]; do
read -rp "Second DNS resolver to use for the clients (optional): " -e -i 176.103.130.131 CLIENT_DNS_2
if [[ ${CLIENT_DNS_2} == "" ]]; then
CLIENT_DNS_2="${CLIENT_DNS_1}"
fi
done
echo ""
echo "Okay, that was all I needed. We are ready to setup your WireGuard server now."
echo "You will be able to generate a client at the end of the installation."
APPROVE_INSTALL=${APPROVE_INSTALL:-n}
if [[ $APPROVE_INSTALL =~ n ]]; then
read -n1 -r -p "Press any key to continue..."
fi
}
function installWireGuard() {
if [[ ${AUTO_INSTALL} == "y" ]]; then
# Set default choices so that no questions will be asked.
APPROVE_INSTALL=${APPROVE_INSTALL:-y}
APPROVE_IP=${APPROVE_IP:-y}
APPROVE_NIC=${APPROVE_NIC:-y}
SERVER_WG_NIC=${SERVER_WG_NIC:-wg0}
SERVER_WG_IPV4=${SERVER_WG_IPV4:-10.66.66.1}
SERVER_WG_IPV6=${SERVER_WG_IPV6:-fd42:42:42::1}
SERVER_PORT=${SERVER_PORT:-$(shuf -i49152-65535 -n1)}
CLIENT_DNS_1=${CLIENT_DNS_1:-176.103.130.130}
CLIENT_DNS_2=${CLIENT_DNS_2:-176.103.130.131}
CLIENT_NAME=${CLIENT_NAME:-client}
CLIENT_DOT=${CLIENT_DOT:-2}
# Behind NAT, we'll default to the publicly reachable IPv4.
SERVER_PUB_IP=${SERVER_PUB_IP:-$(curl https://ifconfig.co)}
else
# Run setup questions first
installQuestions
fi
# Install WireGuard tools and module
if [[ ${OS} == 'ubuntu' ]]; then
apt-get update
apt-get install -y wireguard iptables resolvconf qrencode
elif [[ ${OS} == 'debian' ]]; then
if ! grep -rqs "^deb .* buster-backports" /etc/apt/; then
echo "deb http://deb.debian.org/debian buster-backports main" >/etc/apt/sources.list.d/backports.list
apt-get update
fi
apt update
apt-get install -y iptables resolvconf qrencode
apt-get install -y -t buster-backports wireguard
elif [[ ${OS} == 'fedora' ]]; then
if [[ ${VERSION_ID} -lt 32 ]]; then
dnf install -y dnf-plugins-core
dnf copr enable -y jdoss/wireguard
dnf install -y wireguard-dkms
fi
dnf install -y wireguard-tools iptables qrencode
elif [[ ${OS} == 'centos' ]]; then
curl -Lo /etc/yum.repos.d/wireguard.repo https://copr.fedorainfracloud.org/coprs/jdoss/wireguard/repo/epel-7/jdoss-wireguard-epel-7.repo
yum -y install epel-release kernel kernel-devel kernel-headers
yum -y install wireguard-dkms wireguard-tools iptables qrencode
elif [[ ${OS} == 'arch' ]]; then
pacman -S --noconfirm linux-headers
pacman -S --noconfirm wireguard-tools iptables qrencode
fi
# Make sure the directory exists (this does not seem the be the case on fedora)
mkdir /etc/wireguard >/dev/null 2>&1
chmod 600 -R /etc/wireguard/
SERVER_PRIV_KEY=$(wg genkey)
SERVER_PUB_KEY=$(echo "${SERVER_PRIV_KEY}" | wg pubkey)
# Save WireGuard settings
echo "SERVER_PUB_IP=${SERVER_PUB_IP}
SERVER_PUB_NIC=${SERVER_PUB_NIC}
SERVER_WG_NIC=${SERVER_WG_NIC}
SERVER_WG_IPV4=${SERVER_WG_IPV4}
SERVER_WG_IPV6=${SERVER_WG_IPV6}
SERVER_PORT=${SERVER_PORT}
SERVER_PRIV_KEY=${SERVER_PRIV_KEY}
SERVER_PUB_KEY=${SERVER_PUB_KEY}
CLIENT_DNS_1=${CLIENT_DNS_1}
CLIENT_DNS_2=${CLIENT_DNS_2}" >/etc/wireguard/params
# Add server interface
echo "[Interface]
Address = ${SERVER_WG_IPV4}/24,${SERVER_WG_IPV6}/64
ListenPort = ${SERVER_PORT}
PrivateKey = ${SERVER_PRIV_KEY}" >"/etc/wireguard/${SERVER_WG_NIC}.conf"
if pgrep firewalld; then
FIREWALLD_IPV4_ADDRESS=$(echo "${SERVER_WG_IPV4}" | cut -d"." -f1-3)".0"
FIREWALLD_IPV6_ADDRESS=$(echo "${SERVER_WG_IPV6}" | sed 's/:[^:]*$/:0/')
echo "PostUp = firewall-cmd --add-port ${SERVER_PORT}/udp && firewall-cmd --add-rich-rule='rule family=ipv4 source address=${FIREWALLD_IPV4_ADDRESS}/24 masquerade' && firewall-cmd --add-rich-rule='rule family=ipv6 source address=${FIREWALLD_IPV6_ADDRESS}/24 masquerade'
PostDown = firewall-cmd --remove-port ${SERVER_PORT}/udp && firewall-cmd --remove-rich-rule='rule family=ipv4 source address=${FIREWALLD_IPV4_ADDRESS}/24 masquerade' && firewall-cmd --remove-rich-rule='rule family=ipv6 source address=${FIREWALLD_IPV6_ADDRESS}/24 masquerade'" >>"/etc/wireguard/${SERVER_WG_NIC}.conf"
else
echo "PostUp = iptables -A FORWARD -i ${SERVER_WG_NIC} -j ACCEPT; iptables -t nat -A POSTROUTING -o ${SERVER_PUB_NIC} -j MASQUERADE; ip6tables -A FORWARD -i ${SERVER_WG_NIC} -j ACCEPT; ip6tables -t nat -A POSTROUTING -o ${SERVER_PUB_NIC} -j MASQUERADE
PostDown = iptables -D FORWARD -i ${SERVER_WG_NIC} -j ACCEPT; iptables -t nat -D POSTROUTING -o ${SERVER_PUB_NIC} -j MASQUERADE; ip6tables -D FORWARD -i ${SERVER_WG_NIC} -j ACCEPT; ip6tables -t nat -D POSTROUTING -o ${SERVER_PUB_NIC} -j MASQUERADE" >>"/etc/wireguard/${SERVER_WG_NIC}.conf"
fi
# Enable routing on the server
echo "net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1" >/etc/sysctl.d/wg.conf
sysctl --system
systemctl start "wg-quick@${SERVER_WG_NIC}"
systemctl enable "wg-quick@${SERVER_WG_NIC}"
# Check if WireGuard is running
systemctl is-active --quiet "wg-quick@${SERVER_WG_NIC}"
WG_RUNNING=$?
# WireGuard might not work if we updated the kernel. Tell the user to reboot
if [[ ${WG_RUNNING} -ne 0 ]]; then
echo -e "\nWARNING: WireGuard does not seem to be running."
echo "You can check if WireGuard is running with: systemctl status wg-quick@${SERVER_WG_NIC}"
echo "If you get something like \"Cannot find device ${SERVER_WG_NIC}\", please reboot!"
fi
newClient
echo "If you want to add more clients, you simply need to run this script another time!"
}
function newClient() {
ENDPOINT="${SERVER_PUB_IP}:${SERVER_PORT}"
echo ""
echo "Tell me a name for the client."
echo "The name must consist of alphanumeric character. It may also include an underscore or a dash."
until [[ ${CLIENT_NAME} =~ ^[a-zA-Z0-9_-]+$ && ${CLIENT_EXISTS} == '0' ]]; do
read -rp "Client name: " -e CLIENT_NAME
CLIENT_EXISTS=$(grep -c -E "^### Client ${CLIENT_NAME}\$" "/etc/wireguard/${SERVER_WG_NIC}.conf")
if [[ ${CLIENT_EXISTS} == '1' ]]; then
echo ""
echo "A client with the specified name was already created, please choose another name."
echo ""
exit 1
fi
done
for DOT_IP in {2..254}; do
DOT_EXISTS=$(grep -c "${SERVER_WG_IPV4::-1}${DOT_IP}" "/etc/wireguard/${SERVER_WG_NIC}.conf")
if [[ ${DOT_EXISTS} == '0' ]]; then
break
fi
done
if [[ ${DOT_EXISTS} == '1' ]]; then
echo ""
echo "The subnet configured supports only 253 clients."
exit 1
fi
until [[ ${IPV4_EXISTS} == '0' ]]; do
until [[ ${CLIENT_DOT} =~ ^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ ]]; do
read -rp "Client's WireGuard Host ID (valid for both IPv4 and IPv6): ${SERVER_WG_IPV4::-1}" -e -i "${CLIENT_DOT}" CLIENT_DOT
done
CLIENT_WG_IPV4="${SERVER_WG_IPV4::-1}${CLIENT_DOT}"
IPV4_EXISTS=$(grep -c "$CLIENT_WG_IPV4" "/etc/wireguard/${SERVER_WG_NIC}.conf")
if [[ ${IPV4_EXISTS} == '1' ]]; then
echo ""
echo "A client with the specified IPv4 was already created, please choose another IPv4."
echo ""
exit 1
fi
done
CLIENT_WG_IPV6="${SERVER_WG_IPV6::-1}${CLIENT_DOT}"
# Generate key pair for the client
CLIENT_PRIV_KEY=$(wg genkey)
CLIENT_PUB_KEY=$(echo "${CLIENT_PRIV_KEY}" | wg pubkey)
CLIENT_PRE_SHARED_KEY=$(wg genpsk)
# Home directory of the user, where the client configuration will be written
if [ -e "/home/${CLIENT_NAME}" ]; then # if $1 is a user name
HOME_DIR="/home/${CLIENT_NAME}"
elif [ "${SUDO_USER}" ]; then # if not, use SUDO_USER
HOME_DIR="/home/${SUDO_USER}"
else # if not SUDO_USER, use /root
HOME_DIR="/root"
fi
# Create client file and add the server as a peer
echo "[Interface]
PrivateKey = ${CLIENT_PRIV_KEY}
Address = ${CLIENT_WG_IPV4}/32,${CLIENT_WG_IPV6}/128
DNS = ${CLIENT_DNS_1},${CLIENT_DNS_2}
[Peer]
PublicKey = ${SERVER_PUB_KEY}
PresharedKey = ${CLIENT_PRE_SHARED_KEY}
Endpoint = ${ENDPOINT}
AllowedIPs = 0.0.0.0/0,::/0" >>"${HOME_DIR}/${SERVER_WG_NIC}-client-${CLIENT_NAME}.conf"
# Add the client as a peer to the server
echo -e "\n### Client ${CLIENT_NAME}
[Peer]
PublicKey = ${CLIENT_PUB_KEY}
PresharedKey = ${CLIENT_PRE_SHARED_KEY}
AllowedIPs = ${CLIENT_WG_IPV4}/32,${CLIENT_WG_IPV6}/128" >>"/etc/wireguard/${SERVER_WG_NIC}.conf"
systemctl restart "wg-quick@${SERVER_WG_NIC}"
echo -e "\nHere is your client config file as a QR Code:"
qrencode -t ansiutf8 -l L <"${HOME_DIR}/${SERVER_WG_NIC}-client-${CLIENT_NAME}.conf"
echo "It is also available in ${HOME_DIR}/${SERVER_WG_NIC}-client-${CLIENT_NAME}.conf"
}
function revokeClient() {
NUMBER_OF_CLIENTS=$(grep -c -E "^### Client" "/etc/wireguard/${SERVER_WG_NIC}.conf")
if [[ ${NUMBER_OF_CLIENTS} == '0' ]]; then
echo ""
echo "You have no existing clients!"
exit 1
fi
echo ""
echo "Select the existing client you want to revoke"
grep -E "^### Client" "/etc/wireguard/${SERVER_WG_NIC}.conf" | cut -d ' ' -f 3 | nl -s ') '
until [[ ${CLIENT_NUMBER} -ge 1 && ${CLIENT_NUMBER} -le ${NUMBER_OF_CLIENTS} ]]; do
if [[ ${CLIENT_NUMBER} == '1' ]]; then
read -rp "Select one client [1]: " CLIENT_NUMBER
else
read -rp "Select one client [1-${NUMBER_OF_CLIENTS}]: " CLIENT_NUMBER
fi
done
# match the selected number to a client name
CLIENT_NAME=$(grep -E "^### Client" "/etc/wireguard/${SERVER_WG_NIC}.conf" | cut -d ' ' -f 3 | sed -n "${CLIENT_NUMBER}"p)
# remove [Peer] block matching $CLIENT_NAME
sed -i "/^### Client ${CLIENT_NAME}\$/,/^$/d" "/etc/wireguard/${SERVER_WG_NIC}.conf"
# remove generated client file
rm -f "${HOME}/${SERVER_WG_NIC}-client-${CLIENT_NAME}.conf"
# restart wireguard to apply changes
systemctl restart "wg-quick@${SERVER_WG_NIC}"
}
function uninstallWg() {
checkOS
systemctl stop "wg-quick@${SERVER_WG_NIC}"
systemctl disable "wg-quick@${SERVER_WG_NIC}"
if [[ ${OS} == 'ubuntu' ]]; then
apt-get autoremove --purge -y wireguard qrencode
elif [[ ${OS} == 'debian' ]]; then
apt-get autoremove --purge -y wireguard qrencode
elif [[ ${OS} == 'fedora' ]]; then
dnf remove -y wireguard-tools qrencode
if [[ ${VERSION_ID} -lt 32 ]]; then
dnf remove -y wireguard-dkms
dnf copr disable -y jdoss/wireguard
fi
dnf autoremove -y
elif [[ ${OS} == 'centos' ]]; then
yum -y remove wireguard-dkms wireguard-tools qrencode
rm -f "/etc/yum.repos.d/wireguard.repo"
yum -y autoremove
elif [[ ${OS} == 'arch' ]]; then
pacman -Rs --noconfirm wireguard-tools qrencode
fi
rm -rf /etc/wireguard
rm -f /etc/sysctl.d/wg.conf
# Reload sysctl
sysctl --system
# Check if WireGuard is running
systemctl is-active --quiet "wg-quick@${SERVER_WG_NIC}"
WG_RUNNING=$?
if [[ ${WG_RUNNING} -eq 0 ]]; then
echo "WireGuard failed to uninstall properly."
exit 1
else
echo "WireGuard uninstalled successfully."
exit 0
fi
}
function manageMenu() {
echo "Welcome to WireGuard-install!"
echo "The git repository is available at: https://github.com/angristan/wireguard-install"
echo ""
echo "It looks like WireGuard is already installed."
echo ""
echo "What do you want to do?"
echo " 1) Add a new user"
echo " 2) Revoke existing user"
echo " 3) Uninstall WireGuard"
echo " 4) Exit"
until [[ ${MENU_OPTION} =~ ^[1-4]$ ]]; do
read -rp "Select an option [1-4]: " MENU_OPTION
done
case "${MENU_OPTION}" in
1)
newClient
;;
2)
revokeClient
;;
3)
uninstallWg
;;
4)
exit 0
;;
esac
}
# Check for root, virt, OS...
initialCheck
# Check if WireGuard is already installed and load params
if [[ -e /etc/wireguard/params ]]; then
source /etc/wireguard/params
manageMenu
else
installWireGuard
fi