wireguard-install/wireguard-install.sh

849 lines
30 KiB
Bash
Raw Normal View History

2020-04-30 20:26:38 +02:00
#!/bin/bash
#
2022-05-20 06:51:41 +02:00
# https://github.com/hwdsl2/wireguard-install
#
# Based on the work of Nyr and contributors at:
2020-04-30 20:26:38 +02:00
# https://github.com/Nyr/wireguard-install
#
2022-05-20 06:51:41 +02:00
# Copyright (c) 2022 Lin Song <linsongui@gmail.com>
# Copyright (c) 2020-2022 Nyr
#
# Released under the MIT License.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2020-04-30 20:26:38 +02:00
check_ip() {
IP_REGEX='^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
printf '%s' "$1" | tr -d '\n' | grep -Eq "$IP_REGEX"
}
abort_and_exit() {
echo "Abort. No changes were made." >&2
exit 1
}
get_export_dir() {
export_to_home_dir=0
export_dir=~/
if [ -n "$SUDO_USER" ] && getent group "$SUDO_USER" >/dev/null 2>&1; then
user_home_dir=$(getent passwd "$SUDO_USER" 2>/dev/null | cut -d: -f6)
if [ -d "$user_home_dir" ] && [ "$user_home_dir" != "/" ]; then
export_dir="$user_home_dir/"
export_to_home_dir=1
fi
fi
}
2022-10-02 06:28:19 +02:00
find_public_ip() {
ip_url1="http://ipv4.icanhazip.com"
ip_url2="http://ip1.dynupdate.no-ip.com"
# Get public IP and sanitize with grep
get_public_ip=$(grep -m 1 -oE '^[0-9]{1,3}(\.[0-9]{1,3}){3}$' <<< "$(wget -T 10 -t 1 -4qO- "$ip_url1" || curl -m 10 -4Ls "$ip_url1")")
if ! check_ip "$get_public_ip"; then
get_public_ip=$(grep -m 1 -oE '^[0-9]{1,3}(\.[0-9]{1,3}){3}$' <<< "$(wget -T 10 -t 1 -4qO- "$ip_url2" || curl -m 10 -4Ls "$ip_url2")")
fi
}
update_sysctl() {
# Enable net.ipv4.ip_forward for the system
echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-wireguard-forward.conf
if [[ -n "$ip6" ]]; then
# Enable net.ipv6.conf.all.forwarding for the system
echo "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.d/99-wireguard-forward.conf
fi
# Optimize sysctl settings such as TCP buffer sizes
cat > /etc/sysctl.d/99-wireguard-optimize.conf <<'EOF'
kernel.msgmnb = 65536
kernel.msgmax = 65536
net.core.wmem_max = 16777216
net.core.rmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 87380 16777216
EOF
# Enable TCP BBR congestion control if kernel version >= 4.20
if modprobe -q tcp_bbr \
&& printf '%s\n%s' "4.20" "$(uname -r)" | sort -C -V; then
cat >> /etc/sysctl.d/99-wireguard-optimize.conf <<'EOF'
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
EOF
fi
# Apply sysctl settings
sysctl -e -q -p /etc/sysctl.d/99-wireguard-forward.conf
sysctl -e -q -p /etc/sysctl.d/99-wireguard-optimize.conf
}
new_client_dns() {
if [ "$auto" = 0 ]; then
echo
echo "Select a DNS server for the client:"
echo " 1) Current system resolvers"
echo " 2) Google Public DNS"
echo " 3) Cloudflare DNS"
echo " 4) OpenDNS"
echo " 5) Quad9"
echo " 6) AdGuard DNS"
echo " 7) Custom"
read -rp "DNS server [2]: " dns
until [[ -z "$dns" || "$dns" =~ ^[1-7]$ ]]; do
echo "$dns: invalid selection."
read -rp "DNS server [2]: " dns
done
else
dns=2
fi
2020-04-30 20:26:38 +02:00
# DNS
case "$dns" in
1)
2020-04-30 20:26:38 +02:00
# Locate the proper resolv.conf
# Needed for systems running systemd-resolved
if grep '^nameserver' "/etc/resolv.conf" | grep -qv '127.0.0.53' ; then
2020-04-30 20:26:38 +02:00
resolv_conf="/etc/resolv.conf"
else
resolv_conf="/run/systemd/resolve/resolv.conf"
2020-04-30 20:26:38 +02:00
fi
# Extract nameservers and provide them in the required format
dns=$(grep -v '^#\|^;' "$resolv_conf" | grep '^nameserver' | grep -v '127.0.0.53' | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}' | xargs | sed -e 's/ /, /g')
2020-04-30 20:26:38 +02:00
;;
2|"")
2020-05-21 22:36:47 +02:00
dns="8.8.8.8, 8.8.4.4"
2020-04-30 20:26:38 +02:00
;;
3)
2020-05-21 22:36:47 +02:00
dns="1.1.1.1, 1.0.0.1"
2020-04-30 20:26:38 +02:00
;;
4)
dns="208.67.222.222, 208.67.220.220"
;;
5)
2020-05-21 22:36:47 +02:00
dns="9.9.9.9, 149.112.112.112"
2020-04-30 20:26:38 +02:00
;;
6)
dns="94.140.14.14, 94.140.15.15"
2020-04-30 20:26:38 +02:00
;;
7)
read -rp "Enter primary DNS server: " dns1
until check_ip "$dns1"; do
echo "Invalid DNS server."
read -rp "Enter primary DNS server: " dns1
done
read -rp "Enter secondary DNS server (Enter to skip): " dns2
until [ -z "$dns2" ] || check_ip "$dns2"; do
echo "Invalid DNS server."
read -rp "Enter secondary DNS server (Enter to skip): " dns2
done
if [ -n "$dns2" ]; then
dns="$dns1, $dns2"
else
dns="$dns1"
fi
;;
2020-04-30 20:26:38 +02:00
esac
}
new_client_setup() {
get_export_dir
2020-04-30 20:26:38 +02:00
# Given a list of the assigned internal IPv4 addresses, obtain the lowest still
# available octet. Important to start looking at 2, because 1 is our gateway.
octet=2
while grep AllowedIPs /etc/wireguard/wg0.conf | cut -d "." -f 4 | cut -d "/" -f 1 | grep -q "$octet"; do
2020-05-21 19:18:29 +02:00
(( octet++ ))
2020-04-30 20:26:38 +02:00
done
# Don't break the WireGuard configuration in case the address space is full
if [[ "$octet" -eq 255 ]]; then
2020-05-21 19:18:29 +02:00
echo "253 clients are already configured. The WireGuard internal subnet is full!"
2022-05-27 22:57:52 +02:00
exit 1
2020-04-30 20:26:38 +02:00
fi
key=$(wg genkey)
psk=$(wg genpsk)
# Configure client in the server
cat << EOF >> /etc/wireguard/wg0.conf
# BEGIN_PEER $client
[Peer]
2022-10-02 06:58:08 +02:00
PublicKey = $(wg pubkey <<< "$key")
2020-04-30 20:26:38 +02:00
PresharedKey = $psk
AllowedIPs = 10.7.0.$octet/32$(grep -q 'fddd:2c4:2c4:2c4::1' /etc/wireguard/wg0.conf && echo ", fddd:2c4:2c4:2c4::$octet/128")
# END_PEER $client
EOF
# Create client configuration
cat << EOF > "$export_dir$client".conf
2020-04-30 20:26:38 +02:00
[Interface]
Address = 10.7.0.$octet/24$(grep -q 'fddd:2c4:2c4:2c4::1' /etc/wireguard/wg0.conf && echo ", fddd:2c4:2c4:2c4::$octet/64")
DNS = $dns
PrivateKey = $key
[Peer]
PublicKey = $(grep PrivateKey /etc/wireguard/wg0.conf | cut -d " " -f 3 | wg pubkey)
PresharedKey = $psk
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = $(grep '^# ENDPOINT' /etc/wireguard/wg0.conf | cut -d " " -f 3):$(grep ListenPort /etc/wireguard/wg0.conf | cut -d " " -f 3)
PersistentKeepalive = 25
EOF
2022-09-25 03:54:01 +02:00
if [ "$export_to_home_dir" = 1 ]; then
chown "$SUDO_USER:$SUDO_USER" "$export_dir$client".conf
fi
chmod 600 "$export_dir$client".conf
2020-04-30 20:26:38 +02:00
}
2022-10-02 06:31:08 +02:00
show_usage() {
if [ -n "$1" ]; then
echo "Error: $1" >&2
fi
cat 1>&2 <<EOF
Usage: bash $0 [options]
Options:
--auto auto install WireGuard using default options
-h, --help show this help message and exit
To customize install options, run this script without arguments.
EOF
exit 1
}
wgsetup() {
# Detect Debian users running the script with "sh" instead of bash
if readlink /proc/$$/exe | grep -q "dash"; then
echo 'This installer needs to be run with "bash", not "sh".'
exit 1
fi
# Detect OpenVZ 6
if [[ $(uname -r | cut -d "." -f 1) -eq 2 ]]; then
echo "The system is running an old kernel, which is incompatible with this installer."
exit 1
fi
# Detect OS
# $os_version variables aren't always in use, but are kept here for convenience
if grep -qs "ubuntu" /etc/os-release; then
os="ubuntu"
os_version=$(grep 'VERSION_ID' /etc/os-release | cut -d '"' -f 2 | tr -d '.')
elif [[ -e /etc/debian_version ]]; then
os="debian"
os_version=$(grep -oE '[0-9]+' /etc/debian_version | head -1)
elif [[ -e /etc/almalinux-release || -e /etc/rocky-release || -e /etc/centos-release ]]; then
os="centos"
os_version=$(grep -shoE '[0-9]+' /etc/almalinux-release /etc/rocky-release /etc/centos-release | head -1)
elif [[ -e /etc/fedora-release ]]; then
os="fedora"
os_version=$(grep -oE '[0-9]+' /etc/fedora-release | head -1)
else
echo "This installer seems to be running on an unsupported distribution.
Supported distros are Ubuntu, Debian, AlmaLinux, Rocky Linux, CentOS and Fedora."
exit 1
fi
if [[ "$os" == "ubuntu" && "$os_version" -lt 1804 ]]; then
echo "Ubuntu 18.04 or higher is required to use this installer.
This version of Ubuntu is too old and unsupported."
exit 1
fi
if [[ "$os" == "debian" && "$os_version" -lt 10 ]]; then
echo "Debian 10 or higher is required to use this installer.
This version of Debian is too old and unsupported."
exit 1
fi
if [[ "$os" == "centos" && "$os_version" -lt 7 ]]; then
echo "CentOS 7 or higher is required to use this installer.
This version of CentOS is too old and unsupported."
exit 1
fi
# Detect environments where $PATH does not include the sbin directories
if ! grep -q sbin <<< "$PATH"; then
echo '$PATH does not include sbin. Try using "su -" instead of "su".'
exit 1
fi
if [ "$(id -u)" != 0 ]; then
echo "This installer must be run as root. Try 'sudo bash $0'"
exit 1
fi
if systemd-detect-virt -cq 2>/dev/null; then
echo "This system is running inside a container, which is not supported by this installer."
exit 1
fi
2022-10-02 06:28:19 +02:00
auto=0
2020-04-30 20:26:38 +02:00
if [[ ! -e /etc/wireguard/wg0.conf ]]; then
if [ "$os" = "centos" ]; then
if grep -qs "hwdsl2 VPN script" /etc/sysconfig/nftables.conf \
|| systemctl is-active --quiet nftables 2>/dev/null; then
echo "This system has nftables enabled, which is not supported by this installer."
exit 1
fi
fi
while [ "$#" -gt 0 ]; do
case $1 in
--auto)
auto=1
shift
;;
2022-10-02 06:31:08 +02:00
-h|--help)
show_usage
;;
*)
2022-10-02 06:31:08 +02:00
show_usage "Unknown parameter: $1"
;;
esac
done
2021-08-16 20:22:06 +02:00
# Detect some Debian minimal setups where neither wget nor curl are installed
if ! hash wget 2>/dev/null && ! hash curl 2>/dev/null; then
if [ "$auto" = 0 ]; then
echo "Wget is required to use this installer."
read -n1 -r -p "Press any key to install Wget and continue..."
fi
export DEBIAN_FRONTEND=noninteractive
(
set -x
apt-get -yqq update || apt-get -yqq update
apt-get -yqq install wget >/dev/null
) || exit 1
2021-08-16 20:22:06 +02:00
fi
if [ "$auto" = 0 ]; then
echo
echo 'Welcome to this WireGuard VPN server installer!'
echo
echo 'I need to ask you a few questions before starting setup.'
echo 'You can use the default options and just press enter if you are OK with them.'
else
echo
echo 'Starting WireGuard setup using default options.'
fi
# If system has a single IPv4, it is selected automatically.
if [[ $(ip -4 addr | grep inet | grep -vEc '127(\.[0-9]{1,3}){3}') -eq 1 ]]; then
ip=$(ip -4 addr | grep inet | grep -vE '127(\.[0-9]{1,3}){3}' | cut -d '/' -f 1 | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}')
2020-04-30 20:26:38 +02:00
else
# Use the IP address on the default route
ip=$(ip -4 route get 1 | sed 's/ uid .*//' | awk '{print $NF;exit}' 2>/dev/null)
if ! check_ip "$ip"; then
find_public_ip
ip_match=0
if [ -n "$get_public_ip" ]; then
ip_list=$(ip -4 addr | grep inet | grep -vE '127(\.[0-9]{1,3}){3}' | cut -d '/' -f 1 | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}')
while IFS= read -r line; do
if [ "$line" = "$get_public_ip" ]; then
ip_match=1
ip="$line"
fi
done <<< "$ip_list"
fi
if [ "$ip_match" = 0 ]; then
if [ "$auto" = 0 ]; then
echo
echo "Which IPv4 address should be used?"
number_of_ip=$(ip -4 addr | grep inet | grep -vEc '127(\.[0-9]{1,3}){3}')
ip -4 addr | grep inet | grep -vE '127(\.[0-9]{1,3}){3}' | cut -d '/' -f 1 | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}' | nl -s ') '
read -rp "IPv4 address [1]: " ip_number
until [[ -z "$ip_number" || "$ip_number" =~ ^[0-9]+$ && "$ip_number" -le "$number_of_ip" ]]; do
echo "$ip_number: invalid selection."
read -rp "IPv4 address [1]: " ip_number
done
[[ -z "$ip_number" ]] && ip_number=1
else
ip_number=1
fi
ip=$(ip -4 addr | grep inet | grep -vE '127(\.[0-9]{1,3}){3}' | cut -d '/' -f 1 | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}' | sed -n "$ip_number"p)
fi
fi
2020-04-30 20:26:38 +02:00
fi
# If $ip is a private IP address, the server must be behind NAT
2022-09-25 03:54:01 +02:00
if printf '%s' "$ip" | grep -qE '^(10|127|172\.(1[6-9]|2[0-9]|3[0-1])|192\.168|169\.254)\.'; then
find_public_ip
2022-10-02 06:28:19 +02:00
if ! check_ip "$get_public_ip"; then
if [ "$auto" = 0 ]; then
echo
echo "This server is behind NAT. What is the public IPv4 address?"
read -rp "Public IPv4 address: " public_ip
until check_ip "$public_ip"; do
echo "Invalid input."
read -rp "Public IPv4 address: " public_ip
done
else
echo "Error: Could not detect this server's public IP." >&2
echo "Abort. No changes were made." >&2
exit 1
fi
else
public_ip="$get_public_ip"
fi
2020-04-30 20:26:38 +02:00
fi
if [ "$auto" != 0 ]; then
echo
printf '%s' "Server IP: "
[ -n "$public_ip" ] && printf '%s\n' "$public_ip" || printf '%s\n' "$ip"
echo "Port: UDP/51820"
echo "Client name: client"
echo "Client DNS: Google Public DNS"
fi
2020-04-30 20:26:38 +02:00
# If system has a single IPv6, it is selected automatically
if [[ $(ip -6 addr | grep -c 'inet6 [23]') -eq 1 ]]; then
ip6=$(ip -6 addr | grep 'inet6 [23]' | cut -d '/' -f 1 | grep -oE '([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}')
fi
# If system has multiple IPv6, ask the user to select one
if [[ $(ip -6 addr | grep -c 'inet6 [23]') -gt 1 ]]; then
if [ "$auto" = 0 ]; then
echo
echo "Which IPv6 address should be used?"
number_of_ip6=$(ip -6 addr | grep -c 'inet6 [23]')
ip -6 addr | grep 'inet6 [23]' | cut -d '/' -f 1 | grep -oE '([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}' | nl -s ') '
read -rp "IPv6 address [1]: " ip6_number
until [[ -z "$ip6_number" || "$ip6_number" =~ ^[0-9]+$ && "$ip6_number" -le "$number_of_ip6" ]]; do
echo "$ip6_number: invalid selection."
read -rp "IPv6 address [1]: " ip6_number
done
[[ -z "$ip6_number" ]] && ip6_number=1
else
ip6_number=1
fi
2020-04-30 20:26:38 +02:00
ip6=$(ip -6 addr | grep 'inet6 [23]' | cut -d '/' -f 1 | grep -oE '([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}' | sed -n "$ip6_number"p)
fi
if [ "$auto" = 0 ]; then
echo
echo "What port should WireGuard listen to?"
read -rp "Port [51820]: " port
until [[ -z "$port" || "$port" =~ ^[0-9]+$ && "$port" -le 65535 ]]; do
echo "$port: invalid port."
read -rp "Port [51820]: " port
done
[[ -z "$port" ]] && port=51820
else
port=51820
fi
if [ "$auto" = 0 ]; then
echo
echo "Enter a name for the first client:"
read -rp "Name [client]: " unsanitized_client
# Allow a limited set of characters to avoid conflicts
client=$(sed 's/[^0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-]/_/g' <<< "$unsanitized_client")
[[ -z "$client" ]] && client=client
else
client=client
fi
2020-05-21 19:18:29 +02:00
new_client_dns
if [ "$auto" = 0 ]; then
echo
echo "WireGuard installation is ready to begin."
fi
2021-09-03 12:04:22 +02:00
# Install a firewall if firewalld or iptables are not already available
if ! systemctl is-active --quiet firewalld.service && ! hash iptables 2>/dev/null; then
if [[ "$os" == "centos" || "$os" == "fedora" ]]; then
firewall="firewalld"
# We don't want to silently enable firewalld, so we give a subtle warning
# If the user continues, firewalld will be installed and enabled during setup
echo
echo "Note: firewalld, which is required to manage routing tables, will also be installed."
elif [[ "$os" == "debian" || "$os" == "ubuntu" ]]; then
# iptables is way less invasive than firewalld so no warning is given
firewall="iptables"
fi
2020-04-30 20:26:38 +02:00
fi
if [ "$auto" = 0 ]; then
read -n1 -r -p "Press any key to continue..."
fi
2020-04-30 20:26:38 +02:00
# Install WireGuard
# Set up the WireGuard kernel module
echo
echo "Installing WireGuard, please wait..."
if [[ "$os" == "ubuntu" ]]; then
# Ubuntu
export DEBIAN_FRONTEND=noninteractive
(
set -x
apt-get -yqq update || apt-get -yqq update
apt-get -yqq install wireguard qrencode $firewall >/dev/null
) || exit 1
elif [[ "$os" == "debian" && "$os_version" -ge 11 ]]; then
# Debian 11 or higher
export DEBIAN_FRONTEND=noninteractive
(
set -x
apt-get -yqq update || apt-get -yqq update
apt-get -yqq install wireguard qrencode $firewall >/dev/null
) || exit 1
elif [[ "$os" == "debian" && "$os_version" -eq 10 ]]; then
# Debian 10
if ! grep -qs '^deb .* buster-backports main' /etc/apt/sources.list /etc/apt/sources.list.d/*.list; then
echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
2020-04-30 20:26:38 +02:00
fi
export DEBIAN_FRONTEND=noninteractive
(
set -x
apt-get -yqq update || apt-get -yqq update
# Try to install kernel headers for the running kernel and avoid a reboot. This
# can fail, so it's important to run separately from the other apt-get command.
apt-get -yqq install linux-headers-"$(uname -r)" >/dev/null
)
# There are cleaner ways to find out the $architecture, but we require an
# specific format for the package name and this approach provides what we need.
architecture=$(dpkg --get-selections 'linux-image-*-*' | cut -f 1 | grep -oE '[^-]*$' -m 1)
# linux-headers-$architecture points to the latest headers. We install it
# because if the system has an outdated kernel, there is no guarantee that old
# headers were still downloadable and to provide suitable headers for future
# kernel updates.
(
set -x
apt-get -yqq install linux-headers-"$architecture" >/dev/null
apt-get -yqq install wireguard qrencode $firewall >/dev/null
) || exit 1
elif [[ "$os" == "centos" && "$os_version" -eq 9 ]]; then
# CentOS 9
(
set -x
yum -y -q install epel-release >/dev/null
yum -y -q install wireguard-tools qrencode $firewall >/dev/null
) || exit 1
mkdir -p /etc/wireguard/
elif [[ "$os" == "centos" && "$os_version" -eq 8 ]]; then
# CentOS 8
(
set -x
yum -y -q install epel-release elrepo-release >/dev/null
yum -y -q install kmod-wireguard wireguard-tools qrencode $firewall >/dev/null
) || exit 1
mkdir -p /etc/wireguard/
elif [[ "$os" == "centos" && "$os_version" -eq 7 ]]; then
# CentOS 7
(
set -x
yum -y -q install epel-release https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm >/dev/null
yum -y -q install yum-plugin-elrepo >/dev/null
yum -y -q install kmod-wireguard wireguard-tools qrencode $firewall >/dev/null
) || exit 1
mkdir -p /etc/wireguard/
elif [[ "$os" == "fedora" ]]; then
# Fedora
(
set -x
dnf install -y wireguard-tools qrencode $firewall >/dev/null
) || exit 1
mkdir -p /etc/wireguard/
fi
# If firewalld was just installed, enable it
if [[ "$firewall" == "firewalld" ]]; then
(
set -x
systemctl enable --now firewalld.service >/dev/null 2>&1
)
2020-04-30 20:26:38 +02:00
fi
# Generate wg0.conf
cat << EOF > /etc/wireguard/wg0.conf
# Do not alter the commented lines
# They are used by wireguard-install
# ENDPOINT $([[ -n "$public_ip" ]] && echo "$public_ip" || echo "$ip")
2020-04-30 20:26:38 +02:00
[Interface]
Address = 10.7.0.1/24$([[ -n "$ip6" ]] && echo ", fddd:2c4:2c4:2c4::1/64")
PrivateKey = $(wg genkey)
ListenPort = $port
EOF
chmod 600 /etc/wireguard/wg0.conf
update_sysctl
2020-04-30 20:26:38 +02:00
if systemctl is-active --quiet firewalld.service; then
# Using both permanent and not permanent rules to avoid a firewalld
# reload.
firewall-cmd -q --add-port="$port"/udp
firewall-cmd -q --zone=trusted --add-source=10.7.0.0/24
firewall-cmd -q --permanent --add-port="$port"/udp
firewall-cmd -q --permanent --zone=trusted --add-source=10.7.0.0/24
2020-04-30 20:26:38 +02:00
# Set NAT for the VPN subnet
firewall-cmd -q --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.7.0.0/24 ! -d 10.7.0.0/24 -j SNAT --to "$ip"
firewall-cmd -q --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -s 10.7.0.0/24 ! -d 10.7.0.0/24 -j SNAT --to "$ip"
2020-04-30 20:26:38 +02:00
if [[ -n "$ip6" ]]; then
firewall-cmd -q --zone=trusted --add-source=fddd:2c4:2c4:2c4::/64
firewall-cmd -q --permanent --zone=trusted --add-source=fddd:2c4:2c4:2c4::/64
firewall-cmd -q --direct --add-rule ipv6 nat POSTROUTING 0 -s fddd:2c4:2c4:2c4::/64 ! -d fddd:2c4:2c4:2c4::/64 -j SNAT --to "$ip6"
firewall-cmd -q --permanent --direct --add-rule ipv6 nat POSTROUTING 0 -s fddd:2c4:2c4:2c4::/64 ! -d fddd:2c4:2c4:2c4::/64 -j SNAT --to "$ip6"
2020-04-30 20:26:38 +02:00
fi
else
# Create a service to set up persistent iptables rules
iptables_path=$(command -v iptables)
ip6tables_path=$(command -v ip6tables)
# nf_tables is not available as standard in OVZ kernels. So use iptables-legacy
# if we are in OVZ, with a nf_tables backend and iptables-legacy is available.
2020-05-21 19:18:29 +02:00
if [[ $(systemd-detect-virt) == "openvz" ]] && readlink -f "$(command -v iptables)" | grep -q "nft" && hash iptables-legacy 2>/dev/null; then
iptables_path=$(command -v iptables-legacy)
ip6tables_path=$(command -v ip6tables-legacy)
fi
2020-04-30 20:26:38 +02:00
echo "[Unit]
Before=network.target
[Service]
Type=oneshot
ExecStart=$iptables_path -t nat -A POSTROUTING -s 10.7.0.0/24 ! -d 10.7.0.0/24 -j SNAT --to $ip
ExecStart=$iptables_path -I INPUT -p udp --dport $port -j ACCEPT
ExecStart=$iptables_path -I FORWARD -s 10.7.0.0/24 -j ACCEPT
ExecStart=$iptables_path -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
ExecStop=$iptables_path -t nat -D POSTROUTING -s 10.7.0.0/24 ! -d 10.7.0.0/24 -j SNAT --to $ip
ExecStop=$iptables_path -D INPUT -p udp --dport $port -j ACCEPT
ExecStop=$iptables_path -D FORWARD -s 10.7.0.0/24 -j ACCEPT
ExecStop=$iptables_path -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT" > /etc/systemd/system/wg-iptables.service
if [[ -n "$ip6" ]]; then
2020-05-05 16:47:52 +02:00
echo "ExecStart=$ip6tables_path -t nat -A POSTROUTING -s fddd:2c4:2c4:2c4::/64 ! -d fddd:2c4:2c4:2c4::/64 -j SNAT --to $ip6
2020-04-30 20:26:38 +02:00
ExecStart=$ip6tables_path -I FORWARD -s fddd:2c4:2c4:2c4::/64 -j ACCEPT
ExecStart=$ip6tables_path -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
ExecStop=$ip6tables_path -t nat -D POSTROUTING -s fddd:2c4:2c4:2c4::/64 ! -d fddd:2c4:2c4:2c4::/64 -j SNAT --to $ip6
ExecStop=$ip6tables_path -D FORWARD -s fddd:2c4:2c4:2c4::/64 -j ACCEPT
ExecStop=$ip6tables_path -D FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT" >> /etc/systemd/system/wg-iptables.service
fi
echo "RemainAfterExit=yes
[Install]
WantedBy=multi-user.target" >> /etc/systemd/system/wg-iptables.service
(
set -x
systemctl enable --now wg-iptables.service >/dev/null 2>&1
)
2020-04-30 20:26:38 +02:00
fi
# Generates the custom client.conf
new_client_setup
# Enable and start the wg-quick service
(
set -x
systemctl enable --now wg-quick@wg0.service >/dev/null 2>&1
)
2020-04-30 20:26:38 +02:00
echo
qrencode -t UTF8 < "$export_dir$client".conf
2020-05-20 23:36:47 +02:00
echo -e '\xE2\x86\x91 That is a QR code containing the client configuration.'
2020-04-30 20:26:38 +02:00
echo
# If the kernel module didn't load, system probably had an outdated kernel
# We'll try to help, but will not force a kernel upgrade upon the user
if ! modprobe -nq wireguard; then
2020-05-21 19:18:29 +02:00
echo "Warning!"
echo "Installation was finished, but the WireGuard kernel module could not load."
if [[ "$os" == "ubuntu" && "$os_version" -eq 1804 ]]; then
echo 'Upgrade the kernel and headers with "apt-get install linux-generic" and restart.'
2020-05-21 19:18:29 +02:00
elif [[ "$os" == "debian" && "$os_version" -eq 10 ]]; then
echo "Upgrade the kernel with \"apt-get install linux-image-$architecture\" and restart."
2020-05-21 19:18:29 +02:00
elif [[ "$os" == "centos" && "$os_version" -le 8 ]]; then
2020-05-20 23:36:47 +02:00
echo "Reboot the system to load the most recent kernel."
2020-05-21 19:18:29 +02:00
fi
2020-04-30 20:26:38 +02:00
else
2020-05-21 19:18:29 +02:00
echo "Finished!"
2020-04-30 20:26:38 +02:00
fi
echo
echo "The client configuration is available in: $export_dir$client.conf"
2020-05-20 23:36:47 +02:00
echo "New clients can be added by running this script again."
2020-04-30 20:26:38 +02:00
else
2022-05-20 06:58:16 +02:00
echo
2020-05-20 23:36:47 +02:00
echo "WireGuard is already installed."
2020-04-30 20:26:38 +02:00
echo
2020-05-20 23:36:47 +02:00
echo "Select an option:"
2020-05-21 19:18:29 +02:00
echo " 1) Add a new client"
echo " 2) List existing clients"
echo " 3) Remove an existing client"
echo " 4) Remove WireGuard"
echo " 5) Exit"
read -rp "Option: " option
until [[ "$option" =~ ^[1-5]$ ]]; do
2020-04-30 20:26:38 +02:00
echo "$option: invalid selection."
read -rp "Option: " option
2020-04-30 20:26:38 +02:00
done
case "$option" in
1)
echo
2020-05-20 23:36:47 +02:00
echo "Provide a name for the client:"
read -rp "Name: " unsanitized_client
[ -z "$unsanitized_client" ] && abort_and_exit
2020-04-30 20:26:38 +02:00
# Allow a limited set of characters to avoid conflicts
client=$(sed 's/[^0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-]/_/g' <<< "$unsanitized_client")
2020-05-21 19:18:29 +02:00
while [[ -z "$client" ]] || grep -q "^# BEGIN_PEER $client$" /etc/wireguard/wg0.conf; do
2020-05-20 23:36:47 +02:00
echo "$client: invalid name."
read -rp "Name: " unsanitized_client
[ -z "$unsanitized_client" ] && abort_and_exit
2020-04-30 20:26:38 +02:00
client=$(sed 's/[^0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-]/_/g' <<< "$unsanitized_client")
done
new_client_dns
new_client_setup
# Append new client configuration to the WireGuard interface
wg addconf wg0 <(sed -n "/^# BEGIN_PEER $client/,/^# END_PEER $client/p" /etc/wireguard/wg0.conf)
2020-04-30 20:26:38 +02:00
echo
qrencode -t UTF8 < "$export_dir$client".conf
echo -e '\xE2\x86\x91 That is a QR code containing the client configuration.'
2020-04-30 20:26:38 +02:00
echo
echo "$client added. Configuration available in: $export_dir$client.conf"
2020-04-30 20:26:38 +02:00
exit
;;
2)
echo
echo "Checking for existing client(s)..."
number_of_clients=$(grep -c '^# BEGIN_PEER' /etc/wireguard/wg0.conf)
if [[ "$number_of_clients" = 0 ]]; then
echo
echo "There are no existing clients!"
exit
fi
echo
grep '^# BEGIN_PEER' /etc/wireguard/wg0.conf | cut -d ' ' -f 3 | nl -s ') '
if [ "$number_of_clients" = 1 ]; then
printf '\n%s\n' "Total: 1 client"
elif [ -n "$number_of_clients" ]; then
printf '\n%s\n' "Total: $number_of_clients clients"
fi
;;
3)
2020-04-30 20:26:38 +02:00
number_of_clients=$(grep -c '^# BEGIN_PEER' /etc/wireguard/wg0.conf)
if [[ "$number_of_clients" = 0 ]]; then
echo
2020-05-20 23:36:47 +02:00
echo "There are no existing clients!"
2020-04-30 20:26:38 +02:00
exit
fi
echo
2020-05-20 23:36:47 +02:00
echo "Select the client to remove:"
2020-04-30 20:26:38 +02:00
grep '^# BEGIN_PEER' /etc/wireguard/wg0.conf | cut -d ' ' -f 3 | nl -s ') '
read -rp "Client: " client_number
[ -z "$client_number" ] && abort_and_exit
2020-04-30 20:26:38 +02:00
until [[ "$client_number" =~ ^[0-9]+$ && "$client_number" -le "$number_of_clients" ]]; do
echo "$client_number: invalid selection."
read -rp "Client: " client_number
[ -z "$client_number" ] && abort_and_exit
2020-04-30 20:26:38 +02:00
done
client=$(grep '^# BEGIN_PEER' /etc/wireguard/wg0.conf | cut -d ' ' -f 3 | sed -n "$client_number"p)
echo
read -rp "Confirm $client removal? [y/N]: " remove
2020-04-30 20:26:38 +02:00
until [[ "$remove" =~ ^[yYnN]*$ ]]; do
echo "$remove: invalid selection."
read -rp "Confirm $client removal? [y/N]: " remove
2020-04-30 20:26:38 +02:00
done
if [[ "$remove" =~ ^[yY]$ ]]; then
echo
echo "Removing $client..."
2020-04-30 20:26:38 +02:00
# The following is the right way to avoid disrupting other active connections:
# Remove from the live interface
2020-05-21 19:18:29 +02:00
wg set wg0 peer "$(sed -n "/^# BEGIN_PEER $client$/,\$p" /etc/wireguard/wg0.conf | grep -m 1 PublicKey | cut -d " " -f 3)" remove
2020-04-30 20:26:38 +02:00
# Remove from the configuration file
sed -i "/^# BEGIN_PEER $client$/,/^# END_PEER $client$/d" /etc/wireguard/wg0.conf
get_export_dir
wg_file="$export_dir$client.conf"
if [ -f "$wg_file" ]; then
echo "Removing $wg_file..."
rm -f "$wg_file"
fi
2020-04-30 20:26:38 +02:00
echo
2020-05-20 23:36:47 +02:00
echo "$client removed!"
2020-04-30 20:26:38 +02:00
else
echo
2020-05-20 23:36:47 +02:00
echo "$client removal aborted!"
2020-04-30 20:26:38 +02:00
fi
exit
;;
4)
2020-04-30 20:26:38 +02:00
echo
read -rp "Confirm WireGuard removal? [y/N]: " remove
2020-04-30 20:26:38 +02:00
until [[ "$remove" =~ ^[yYnN]*$ ]]; do
echo "$remove: invalid selection."
read -rp "Confirm WireGuard removal? [y/N]: " remove
2020-04-30 20:26:38 +02:00
done
if [[ "$remove" =~ ^[yY]$ ]]; then
echo
echo "Removing WireGuard, please wait..."
2020-04-30 20:26:38 +02:00
port=$(grep '^ListenPort' /etc/wireguard/wg0.conf | cut -d " " -f 3)
if systemctl is-active --quiet firewalld.service; then
ip=$(firewall-cmd --direct --get-rules ipv4 nat POSTROUTING | grep '\-s 10.7.0.0/24 '"'"'!'"'"' -d 10.7.0.0/24' | grep -oE '[^ ]+$')
# Using both permanent and not permanent rules to avoid a firewalld reload.
firewall-cmd -q --remove-port="$port"/udp
firewall-cmd -q --zone=trusted --remove-source=10.7.0.0/24
firewall-cmd -q --permanent --remove-port="$port"/udp
firewall-cmd -q --permanent --zone=trusted --remove-source=10.7.0.0/24
firewall-cmd -q --direct --remove-rule ipv4 nat POSTROUTING 0 -s 10.7.0.0/24 ! -d 10.7.0.0/24 -j SNAT --to "$ip"
firewall-cmd -q --permanent --direct --remove-rule ipv4 nat POSTROUTING 0 -s 10.7.0.0/24 ! -d 10.7.0.0/24 -j SNAT --to "$ip"
2020-04-30 20:26:38 +02:00
if grep -qs 'fddd:2c4:2c4:2c4::1/64' /etc/wireguard/wg0.conf; then
ip6=$(firewall-cmd --direct --get-rules ipv6 nat POSTROUTING | grep '\-s fddd:2c4:2c4:2c4::/64 '"'"'!'"'"' -d fddd:2c4:2c4:2c4::/64' | grep -oE '[^ ]+$')
firewall-cmd -q --zone=trusted --remove-source=fddd:2c4:2c4:2c4::/64
firewall-cmd -q --permanent --zone=trusted --remove-source=fddd:2c4:2c4:2c4::/64
firewall-cmd -q --direct --remove-rule ipv6 nat POSTROUTING 0 -s fddd:2c4:2c4:2c4::/64 ! -d fddd:2c4:2c4:2c4::/64 -j SNAT --to "$ip6"
firewall-cmd -q --permanent --direct --remove-rule ipv6 nat POSTROUTING 0 -s fddd:2c4:2c4:2c4::/64 ! -d fddd:2c4:2c4:2c4::/64 -j SNAT --to "$ip6"
2020-04-30 20:26:38 +02:00
fi
else
systemctl disable --now wg-iptables.service
rm -f /etc/systemd/system/wg-iptables.service
fi
systemctl disable --now wg-quick@wg0.service
rm -f /etc/sysctl.d/99-wireguard-forward.conf /etc/sysctl.d/99-wireguard-optimize.conf
if [ ! -f /usr/sbin/openvpn ] && [ ! -f /usr/sbin/ipsec ] \
&& [ ! -f /usr/local/sbin/ipsec ]; then
echo 0 > /proc/sys/net/ipv4/ip_forward
echo 0 > /proc/sys/net/ipv6/conf/all/forwarding
fi
if [[ "$os" == "ubuntu" ]]; then
# Ubuntu
(
set -x
rm -rf /etc/wireguard/
apt-get remove --purge -y wireguard wireguard-tools >/dev/null
)
elif [[ "$os" == "debian" && "$os_version" -ge 11 ]]; then
# Debian 11 or higher
(
set -x
rm -rf /etc/wireguard/
apt-get remove --purge -y wireguard wireguard-tools >/dev/null
)
elif [[ "$os" == "debian" && "$os_version" -eq 10 ]]; then
# Debian 10
(
set -x
rm -rf /etc/wireguard/
apt-get remove --purge -y wireguard wireguard-dkms wireguard-tools >/dev/null
)
elif [[ "$os" == "centos" && "$os_version" -eq 9 ]]; then
# CentOS 9
(
set -x
yum -y -q remove wireguard-tools >/dev/null
rm -rf /etc/wireguard/
)
elif [[ "$os" == "centos" && "$os_version" -le 8 ]]; then
# CentOS 8 or 7
(
set -x
yum -y -q remove kmod-wireguard wireguard-tools >/dev/null
rm -rf /etc/wireguard/
)
elif [[ "$os" == "fedora" ]]; then
# Fedora
(
set -x
dnf remove -y wireguard-tools >/dev/null
rm -rf /etc/wireguard/
)
2020-04-30 20:26:38 +02:00
fi
echo
echo "WireGuard removed!"
else
echo
2020-05-20 23:36:47 +02:00
echo "WireGuard removal aborted!"
2020-04-30 20:26:38 +02:00
fi
exit
;;
5)
2020-04-30 20:26:38 +02:00
exit
;;
esac
fi
}
## Defer setup until we have the complete script
wgsetup "$@"
exit 0