wireguard-install/wireguard-install.sh
hwdsl2 0ed60bc874 Fix for CentOS 8
- Fix an issue with the kmod-wireguard package on CentOS 8 Stream.
2022-11-17 09:21:21 -06:00

948 lines
31 KiB
Bash
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
#
# https://github.com/hwdsl2/wireguard-install
#
# Based on the work of Nyr and contributors at:
# https://github.com/Nyr/wireguard-install
#
# Copyright (c) 2022 Lin Song <linsongui@gmail.com>
# Copyright (c) 2020-2022 Nyr
#
# Released under the MIT License, see the accompanying file LICENSE.txt
# or https://opensource.org/licenses/MIT
exiterr() { echo "Error: $1" >&2; exit 1; }
exiterr2() { exiterr "'apt-get install' failed."; }
exiterr3() { exiterr "'yum install' failed."; }
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"
}
check_os() {
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
exiterr "This installer seems to be running on an unsupported distribution.
Supported distros are Ubuntu, Debian, AlmaLinux, Rocky Linux, CentOS and Fedora."
fi
}
check_os_ver() {
if [[ "$os" == "ubuntu" && "$os_version" -lt 1804 ]]; then
exiterr "Ubuntu 18.04 or higher is required to use this installer.
This version of Ubuntu is too old and unsupported."
fi
if [[ "$os" == "debian" && "$os_version" -lt 10 ]]; then
exiterr "Debian 10 or higher is required to use this installer.
This version of Debian is too old and unsupported."
fi
if [[ "$os" == "centos" && "$os_version" -lt 7 ]]; then
exiterr "CentOS 7 or higher is required to use this installer.
This version of CentOS is too old and unsupported."
fi
}
check_nftables() {
if [ "$os" = "centos" ]; then
if grep -qs "hwdsl2 VPN script" /etc/sysconfig/nftables.conf \
|| systemctl is-active --quiet nftables 2>/dev/null; then
exiterr "This system has nftables enabled, which is not supported by this installer."
fi
fi
}
install_wget() {
# 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
) || exiterr2
fi
}
install_iproute() {
if ! hash ip 2>/dev/null; then
if [ "$auto" = 0 ]; then
echo "iproute is required to use this installer."
read -n1 -r -p "Press any key to install iproute and continue..."
fi
if [ "$os" = "debian" ] || [ "$os" = "ubuntu" ]; then
export DEBIAN_FRONTEND=noninteractive
(
set -x
apt-get -yqq update || apt-get -yqq update
apt-get -yqq install iproute2 >/dev/null
) || exiterr2
else
(
set -x
yum -y -q install iproute >/dev/null
) || exiterr3
fi
fi
}
show_start_setup() {
if [ "$auto" = 0 ]; then
echo
echo 'Welcome to this WireGuard server installer!'
echo 'GitHub: https://github.com/hwdsl2/wireguard-install'
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
show_header
echo
echo 'Starting WireGuard setup using default options.'
fi
}
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
}
detect_ip() {
# 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}')
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
fi
if ! check_ip "$ip"; then
echo "Error: Could not detect this server's IP address." >&2
echo "Abort. No changes were made." >&2
exit 1
fi
}
check_nat_ip() {
# If $ip is a private IP address, the server must be behind NAT
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
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
fi
}
show_config() {
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
}
detect_ipv6() {
# 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
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
}
select_port() {
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
}
enter_custom_dns() {
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
}
set_client_name() {
# Allow a limited set of characters to avoid conflicts
client=$(sed 's/[^0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-]/_/g' <<< "$unsanitized_client")
}
enter_client_name() {
if [ "$auto" = 0 ]; then
echo
echo "Enter a name for the first client:"
read -rp "Name [client]: " unsanitized_client
set_client_name
[[ -z "$client" ]] && client=client
else
client=client
fi
}
check_firewall() {
# 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
firewall="iptables"
fi
fi
}
abort_and_exit() {
echo "Abort. No changes were made." >&2
exit 1
}
confirm_setup() {
if [ "$auto" = 0 ]; then
printf "Do you want to continue? [Y/n] "
read -r response
case $response in
[yY][eE][sS]|[yY]|'')
:
;;
*)
abort_and_exit
;;
esac
fi
}
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
# DNS
case "$dns" in
1)
# Locate the proper resolv.conf
# Needed for systems running systemd-resolved
if grep '^nameserver' "/etc/resolv.conf" | grep -qv '127.0.0.53' ; then
resolv_conf="/etc/resolv.conf"
else
resolv_conf="/run/systemd/resolve/resolv.conf"
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')
;;
2|"")
dns="8.8.8.8, 8.8.4.4"
;;
3)
dns="1.1.1.1, 1.0.0.1"
;;
4)
dns="208.67.222.222, 208.67.220.220"
;;
5)
dns="9.9.9.9, 149.112.112.112"
;;
6)
dns="94.140.14.14, 94.140.15.15"
;;
7)
enter_custom_dns
if [ -n "$dns2" ]; then
dns="$dns1, $dns2"
else
dns="$dns1"
fi
;;
esac
}
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
}
new_client_setup() {
get_export_dir
# 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
(( octet++ ))
done
# Don't break the WireGuard configuration in case the address space is full
if [[ "$octet" -eq 255 ]]; then
exiterr "253 clients are already configured. The WireGuard internal subnet is full!"
fi
key=$(wg genkey)
psk=$(wg genpsk)
# Configure client in the server
cat << EOF >> /etc/wireguard/wg0.conf
# BEGIN_PEER $client
[Peer]
PublicKey = $(wg pubkey <<< "$key")
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
[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
if [ "$export_to_home_dir" = 1 ]; then
chown "$SUDO_USER:$SUDO_USER" "$export_dir$client".conf
fi
chmod 600 "$export_dir$client".conf
}
update_sysctl() {
mkdir -p /etc/sysctl.d
conf_fwd="/etc/sysctl.d/99-wireguard-forward.conf"
conf_opt="/etc/sysctl.d/99-wireguard-optimize.conf"
# Enable net.ipv4.ip_forward for the system
echo 'net.ipv4.ip_forward=1' > "$conf_fwd"
if [[ -n "$ip6" ]]; then
# Enable net.ipv6.conf.all.forwarding for the system
echo "net.ipv6.conf.all.forwarding=1" >> "$conf_fwd"
fi
# Optimize sysctl settings such as TCP buffer sizes
base_url="https://github.com/hwdsl2/vpn-extras/releases/download/v1.0.0"
conf_url="$base_url/sysctl-wg-$os"
[ "$auto" != 0 ] && conf_url="${conf_url}-auto"
wget -t 3 -T 30 -q -O "$conf_opt" "$conf_url" 2>/dev/null \
|| curl -m 30 -fsL "$conf_url" -o "$conf_opt" 2>/dev/null \
|| { /bin/rm -f "$conf_opt"; touch "$conf_opt"; }
# 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 >> "$conf_opt" <<'EOF'
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
EOF
fi
# Apply sysctl settings
sysctl -e -q -p "$conf_fwd"
sysctl -e -q -p "$conf_opt"
}
update_rclocal() {
ipt_cmd="systemctl restart wg-iptables.service"
if ! grep -qs "$ipt_cmd" /etc/rc.local; then
if [ ! -f /etc/rc.local ]; then
echo '#!/bin/sh' > /etc/rc.local
else
if [ "$os" = "ubuntu" ] || [ "$os" = "debian" ]; then
sed --follow-symlinks -i '/^exit 0/d' /etc/rc.local
fi
fi
cat >> /etc/rc.local <<EOF
$ipt_cmd
EOF
if [ "$os" = "ubuntu" ] || [ "$os" = "debian" ]; then
echo "exit 0" >> /etc/rc.local
fi
chmod +x /etc/rc.local
fi
}
show_header() {
cat <<'EOF'
WireGuard Script
https://github.com/hwdsl2/wireguard-install
EOF
}
show_header2() {
cat <<'EOF'
Copyright (c) 2022 Lin Song
Copyright (c) 2020-2022 Nyr
EOF
}
show_usage() {
if [ -n "$1" ]; then
echo "Error: $1" >&2
fi
show_header
show_header2
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() {
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
if [ "$(id -u)" != 0 ]; then
exiterr "This installer must be run as root. Try 'sudo bash $0'"
fi
# Detect Debian users running the script with "sh" instead of bash
if readlink /proc/$$/exe | grep -q "dash"; then
exiterr 'This installer needs to be run with "bash", not "sh".'
fi
# Detect OpenVZ 6
if [[ $(uname -r | cut -d "." -f 1) -eq 2 ]]; then
exiterr "The system is running an old kernel, which is incompatible with this installer."
fi
check_os
check_os_ver
if systemd-detect-virt -cq 2>/dev/null; then
exiterr "This system is running inside a container, which is not supported by this installer."
fi
auto=0
if [[ ! -e /etc/wireguard/wg0.conf ]]; then
check_nftables
while [ "$#" -gt 0 ]; do
case $1 in
--auto)
auto=1
shift
;;
-h|--help)
show_usage
;;
*)
show_usage "Unknown parameter: $1"
;;
esac
done
install_wget
install_iproute
show_start_setup
detect_ip
check_nat_ip
show_config
detect_ipv6
select_port
enter_client_name
new_client_dns
if [ "$auto" = 0 ]; then
echo
echo "WireGuard installation is ready to begin."
fi
check_firewall
confirm_setup
echo
echo "Installing WireGuard, please wait..."
if [[ "$os" == "ubuntu" ]]; then
export DEBIAN_FRONTEND=noninteractive
(
set -x
apt-get -yqq update || apt-get -yqq update
apt-get -yqq install wireguard qrencode $firewall >/dev/null
) || exiterr2
elif [[ "$os" == "debian" && "$os_version" -ge 11 ]]; then
export DEBIAN_FRONTEND=noninteractive
(
set -x
apt-get -yqq update || apt-get -yqq update
apt-get -yqq install wireguard qrencode $firewall >/dev/null
) || exiterr2
elif [[ "$os" == "debian" && "$os_version" -eq 10 ]]; then
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
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
) || exiterr2
elif [[ "$os" == "centos" && "$os_version" -eq 9 ]]; then
(
set -x
yum -y -q install epel-release >/dev/null
yum -y -q install wireguard-tools qrencode $firewall >/dev/null 2>&1
) || exiterr3
mkdir -p /etc/wireguard/
elif [[ "$os" == "centos" && "$os_version" -eq 8 ]]; then
(
set -x
yum -y -q install epel-release elrepo-release >/dev/null
yum -y -q --nobest install kmod-wireguard >/dev/null 2>&1
yum -y -q install wireguard-tools qrencode $firewall >/dev/null 2>&1
) || exiterr3
mkdir -p /etc/wireguard/
elif [[ "$os" == "centos" && "$os_version" -eq 7 ]]; then
(
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 2>&1
yum -y -q install kmod-wireguard wireguard-tools qrencode $firewall >/dev/null 2>&1
) || exiterr3
mkdir -p /etc/wireguard/
elif [[ "$os" == "fedora" ]]; then
(
set -x
dnf install -y wireguard-tools qrencode $firewall >/dev/null
) || exiterr "'dnf install' failed."
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
)
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")
[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
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
# 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"
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"
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.
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
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
echo "ExecStart=$ip6tables_path -t nat -A POSTROUTING -s fddd:2c4:2c4:2c4::/64 ! -d fddd:2c4:2c4:2c4::/64 -j SNAT --to $ip6
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
)
fi
update_rclocal
# 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
)
echo
qrencode -t UTF8 < "$export_dir$client".conf
echo -e '\xE2\x86\x91 That is a QR code containing the client configuration.'
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
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.'
elif [[ "$os" == "debian" && "$os_version" -eq 10 ]]; then
echo "Upgrade the kernel with \"apt-get install linux-image-$architecture\" and restart."
elif [[ "$os" == "centos" && "$os_version" -le 8 ]]; then
echo "Reboot the system to load the most recent kernel."
fi
else
echo "Finished!"
fi
echo
echo "The client configuration is available in: $export_dir$client.conf"
echo "New clients can be added by running this script again."
else
show_header
echo
echo "WireGuard is already installed."
echo
echo "Select an option:"
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
echo "$option: invalid selection."
read -rp "Option: " option
done
case "$option" in
1)
echo
echo "Provide a name for the client:"
read -rp "Name: " unsanitized_client
[ -z "$unsanitized_client" ] && abort_and_exit
set_client_name
while [[ -z "$client" ]] || grep -q "^# BEGIN_PEER $client$" /etc/wireguard/wg0.conf; do
echo "$client: invalid name."
read -rp "Name: " unsanitized_client
[ -z "$unsanitized_client" ] && abort_and_exit
set_client_name
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)
echo
qrencode -t UTF8 < "$export_dir$client".conf
echo -e '\xE2\x86\x91 That is a QR code containing the client configuration.'
echo
echo "$client added. Configuration available in: $export_dir$client.conf"
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)
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
echo "Select the client to remove:"
grep '^# BEGIN_PEER' /etc/wireguard/wg0.conf | cut -d ' ' -f 3 | nl -s ') '
read -rp "Client: " client_number
[ -z "$client_number" ] && abort_and_exit
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
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
until [[ "$remove" =~ ^[yYnN]*$ ]]; do
echo "$remove: invalid selection."
read -rp "Confirm $client removal? [y/N]: " remove
done
if [[ "$remove" =~ ^[yY]$ ]]; then
echo
echo "Removing $client..."
# The following is the right way to avoid disrupting other active connections:
# Remove from the live interface
wg set wg0 peer "$(sed -n "/^# BEGIN_PEER $client$/,\$p" /etc/wireguard/wg0.conf | grep -m 1 PublicKey | cut -d " " -f 3)" remove
# 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
echo
echo "$client removed!"
else
echo
echo "$client removal aborted!"
fi
exit
;;
4)
echo
read -rp "Confirm WireGuard removal? [y/N]: " remove
until [[ "$remove" =~ ^[yYnN]*$ ]]; do
echo "$remove: invalid selection."
read -rp "Confirm WireGuard removal? [y/N]: " remove
done
if [[ "$remove" =~ ^[yY]$ ]]; then
echo
echo "Removing WireGuard, please wait..."
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"
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"
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
ipt_cmd="systemctl restart wg-iptables.service"
if grep -qs "$ipt_cmd" /etc/rc.local; then
sed --follow-symlinks -i "/^$ipt_cmd/d" /etc/rc.local
fi
if [[ "$os" == "ubuntu" ]]; then
(
set -x
rm -rf /etc/wireguard/
apt-get remove --purge -y wireguard wireguard-tools >/dev/null
)
elif [[ "$os" == "debian" && "$os_version" -ge 11 ]]; then
(
set -x
rm -rf /etc/wireguard/
apt-get remove --purge -y wireguard wireguard-tools >/dev/null
)
elif [[ "$os" == "debian" && "$os_version" -eq 10 ]]; then
(
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
(
set -x
yum -y -q remove wireguard-tools >/dev/null
rm -rf /etc/wireguard/
)
elif [[ "$os" == "centos" && "$os_version" -le 8 ]]; then
(
set -x
yum -y -q remove kmod-wireguard wireguard-tools >/dev/null
rm -rf /etc/wireguard/
)
elif [[ "$os" == "fedora" ]]; then
(
set -x
dnf remove -y wireguard-tools >/dev/null
rm -rf /etc/wireguard/
)
fi
echo
echo "WireGuard removed!"
else
echo
echo "WireGuard removal aborted!"
fi
exit
;;
5)
exit
;;
esac
fi
}
## Defer setup until we have the complete script
wgsetup "$@"
exit 0