#!/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_IPV4=${CLIENT_DOT_IPV4:-2} CLIENT_DOT_IPV6=${CLIENT_DOT_IPV6:-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_IPV4} =~ ^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ ]]; do read -rp "Client's WireGuard IPv4: ${SERVER_WG_IPV4::-1}" -e -i "${CLIENT_DOT_IPV4}" CLIENT_DOT_IPV4 done CLIENT_WG_IPV4="${SERVER_WG_IPV4::-1}${CLIENT_DOT_IPV4}" 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 "" fi done until [[ ${IPV6_EXISTS} == '0' ]]; do until [[ ${CLIENT_DOT_IPV6} =~ ^[a-f0-9]{1,4}$ ]]; do read -rp "Client's WireGuard IPv6: ${SERVER_WG_IPV6::-1}" -e -i "${CLIENT_DOT_IPV6}" CLIENT_DOT_IPV6 done CLIENT_WG_IPV6="${SERVER_WG_IPV6::-1}${CLIENT_DOT_IPV6}" IPV6_EXISTS=$(grep -c "${CLIENT_WG_IPV6}" "/etc/wireguard/${SERVER_WG_NIC}.conf") if [[ ${IPV6_EXISTS} == '1' ]]; then echo "" echo "A client with the specified IPv6 was already created, please choose another IPv6." echo "" fi done # 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