WireGuard: Next-Generation VPN Praised as a Work of Art by Linus Torvalds
WireGuard is an extremely simple, high-speed modern VPN built on cutting-edge cryptography. It aims to be faster, lighter, more versatile, and less problematic than IPsec, and outperforms OpenVPN in throughput and efficiency. Designed as a general-purpose VPN that can run everywhere from embedded devices to high-performance supercomputers, it works across a wide range of environments. Originally released exclusively for Linux, it now supports cross-platform deployment across Windows, macOS, BSD, iOS, and Android. While still under active development, it is already widely regarded as one of the most secure, easiest to deploy VPN solutions available today.
Written in C by Jason Donenfeld and contributors, WireGuard is an open-source Layer 3 network tunneling tool recognized as the next-generation VPN standard. It was merged into the mainline Linux kernel starting with version 5.6 in January 2020, meaning most modern Linux distribution users can use it out of the box with no extra kernel modules required.
Official project site: https://www.wireguard.com/
Installation
If you are running Linux kernel 5.6 or newer, you already have native WireGuard support in the kernel, and only need to install the wireguard-tools utility package to get started.
Check your running kernel version with:
uname --kernel-release
Official installation documentation: https://www.wireguard.com/install/
Alternative installation opsions:
- Docker image: https://hub.docker.com/r/linuxserver/wireguard
- One-click automated installation scripts: https://github.com/hwdsl2/wireguard-install or https://github.com/angristan/wireguard-install
Key Advantages of WireGuard
- High performance with low latency: WireGuard is designed from the ground up for speed, delivering excellent throughput while maintaining strong security. Compared to legacy VPN protocols, it offers faster connection establishment, higher throughput, and lower end-to-end latency, making it ideal for performance-sensitive use cases.
- Modern, battle-tested cryptography: WireGuard uses state-of-the-art cryptographic primitives including Curve25519 for key exchange, ChaCha20 for symmetric encryption, and Poly1305 for message authentication, ensuring robust security and privacy for all data in transit.
- Simple design and easy deployment: One of WireGuard's core design principles is minimalism. It has a small codebase, minimal configuration surface, and is straightforward to understand and deploy for both end users and system administrators.
- Full cros-platform support: WireGuard runs natively on all major operating systems including Linux, Windows, macOS, Android, and iOS, allowing you to create secure VPN connections across any of your devices.
- Native Linux kernel integration: On Linux, WireGuard runs directly as an integrated kernel component which reduces overhead and improves performance compared to userspace implementations.
- Transparent layer 3 tunneling: WireGuard creates a seamless encrypted tunnel that allows devices on separate networks to communicate as if they are on the same local area network, making it ideal for building distributed private networks.
- Open source and auditable: The entire WireGuard codebase is open source, available for public review and audit. Its small size makes it much easier for the security community to validate its security properties compared to larger, more complex VPN solutions.
Important Notes
- Deep packet inspection circumvention: WireGuard does not include built-in traffic obfuscation. Instead, it focuses on delivering a solid, simple encrypted tunneling foundation, and obfuscation can be added at the application layer above WireGuard if needed.
- UDP-only by design: WireGuard exclusively uses UDP for transport by design, because TCP-over-TCP tunneling introduces catastrophic performance issues due to overlapping retransmission logic. If you need to carry WireGuard traffic over TCP connections, this can be handled by upper-layer tools such as
udptunnelorudp2rawthat encapsulate the UDP traffic into TCP.
Custom Configuration
Enable IP Forwarding
First enable persistent IPv4 forwarding by adding the configuration to sysctl:
echo "net.ipv4.ip_forward=1" | tee -a /etc/sysctl.conf
Add traffic forwarding and NAT rules to your WireGuard interface configuration for outbound traffic:
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE; iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; iptables -t nat -D POSTROUTING -o tun0 -j MASQUERADE; iptables -t nat -D POSTROUTING -o eth1 -j MASQUERADE
Automated WireGuard Server Install Script
Below is an automated installation script for Linux servers, with adjusted naming:
#!/bin/bash
# Automated WireGuard Server Installer
# Based on https://github.com/angristan/wireguard-install
RED='\033[0;31m'
ORANGE='\033[0;33m'
NC='\033[0m'
check_root() {
if [ "${EUID}" -ne 0 ]; then
echo "Error: you must run this script as root"
exit 1
fi
}
detect_virtualization() {
VIRT=$(systemd-detect-virt)
if [ "${VIRT}" == "openvz" ]; then
echo "OpenVZ virtualization is not supported"
exit 1
fi
if [ "${VIRT}" == "lxc" ]; then
echo "LXC containers are not supported by this installer."
echo "WireGuard can run on LXC if the host has the kernel module"
echo "and the container is launched with specific permissions."
exit 1
fi
}
detect_os() {
if [[ -e /etc/debian_version ]]; then
source /etc/os-release
TARGET_OS="${ID}"
if [[ ${ID} == "debian" || ${ID} == "raspbian" ]]; then
if [[ ${VERSION_ID} -lt 10 ]]; then
echo "Debian ${VERSION_ID} is not supported, please use Debian 10 Buster or newer"
exit 1
fi
TARGET_OS=debian
fi
elif [[ -e /etc/fedora-release ]]; then
source /etc/os-release
TARGET_OS="${ID}"
elif [[ -e /etc/centos-release ]]; then
source /etc/os-release
TARGET_OS=centos
elif [[ -e /etc/oracle-release ]]; then
source /etc/os-release
TARGET_OS=oracle
elif [[ -e /etc/arch-release ]]; then
TARGET_OS=arch
else
echo "This installer only supports Debian, Ubuntu, Fedora, CentOS, Oracle Linux, and Arch Linux"
exit 1
fi
}
pre_flight_check() {
check_root
detect_virtualization
detect_os
}
gather_user_input() {
echo "Welcome to the WireGuard server installer!"
echo "Source code available at: https://github.com/angristan/wireguard-install"
echo ""
echo "I will ask you a few questions to configure your server. You can press enter to accept the default value."
echo ""
PUB_IP=$(ip -4 addr | sed -ne 's|^.* inet \([^/]*\)/.* scope global.*$|\1|p' | awk '{print $1}' | head -1)
if [[ -z ${PUB_IP} ]]; then
PUB_IP=$(ip -6 addr | sed -ne 's|^.* inet6 \([^/]*\)/.* scope global.*$|\1|p' | head -1)
fi
read -rp "Public IPv4/IPv6 address of your server: " -e -i "${PUB_IP}" PUB_IP
DEFAULT_NIC="$(ip -4 route ls | grep default | grep -Po '(?<=dev )(\S+)' | head -1)"
until [[ ${PUB_NIC} =~ ^[a-zA-Z0-9_]+$ ]]; do
read -rp "Public network interface: " -e -i "${DEFAULT_NIC}" PUB_NIC
done
until [[ ${WG_IFACE} =~ ^[a-zA-Z0-9_]+$ && ${#WG_IFACE} -lt 16 ]]; do
read -rp "WireGuard interface name: " -e -i wg0 WG_IFACE
done
until [[ ${WG_IPV4} =~ ^([0-9]{1,3}\.){3} ]]; do
read -rp "WireGuard server IPv4 address: " -e -i 10.77.77.1 WG_IPV4
done
until [[ ${WG_IPV6} =~ ^([a-f0-9]{1,4}:){3,4}: ]]; do
read -rp "WireGuard server IPv6 address: " -e -i fd77:77:77::1 WG_IPV6
done
RANDOM_LISTEN_PORT=$(shuf -i 49152-65535 -n1)
until [[ ${LISTEN_PORT} =~ ^[0-9]+$ ]] && [ "${LISTEN_PORT}" -ge 1 ] && [ "${LISTEN_PORT}" -le 65535 ]; do
read -rp "WireGuard server listen port (1-65535): " -e -i "${RANDOM_LISTEN_PORT}" LISTEN_PORT
done
until [[ ${DNS1} =~ ^((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 for clients: " -e -i 94.140.14.14 DNS1
done
until [[ ${DNS2} =~ ^((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 for clients (optional): " -e -i 94.140.15.15 DNS2
if [[ ${DNS2} == "" ]]; then
DNS2="${DNS1}"
fi
done
echo ""
echo "All questions completed. We are ready to set up your WireGuard server now."
echo "You will be able to create your first client configuration after installation finishes."
read -n1 -r -p "Press any key to continue..."
}
install_wireguard() {
gather_user_input
if [[ ${TARGET_OS} == 'ubuntu' ]] || [[ ${TARGET_OS} == 'debian' && ${VERSION_ID} -gt 10 ]]; then
apt-get update
apt-get install -y wireguard iptables resolvconf qrencode
elif [[ ${TARGET_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 [[ ${TARGET_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 [[ ${TARGET_OS} == 'centos' ]]; then
yum -y install epel-release elrepo-release
if [[ ${VERSION_ID} -eq 7 ]]; then
yum -y install yum-plugin-elrepo
fi
yum -y install kmod-wireguard wireguard-tools iptables qrencode
elif [[ ${TARGET_OS} == 'oracle' ]]; then
dnf install -y oraclelinux-developer-release-el8
dnf config-manager --disable -y ol8_developer
dnf config-manager --enable -y ol8_developer_UEKR6
dnf config-manager --save -y --setopt=ol8_developer_UEKR6.includepkgs='wireguard-tools*'
dnf install -y wireguard-tools qrencode iptables
elif [[ ${TARGET_OS} == 'arch' ]]; then
pacman -S --needed --noconfirm wireguard-tools qrencode
fi
mkdir /etc/wireguard >/dev/null 2>&1
chmod 600 -R /etc/wireguard/
SERVER_PRIV=$(wg genkey)
SERVER_PUB=$(echo "${SERVER_PRIV}" | wg pubkey)
echo "PUB_IP=${PUB_IP}
PUB_NIC=${PUB_NIC}
WG_IFACE=${WG_IFACE}
WG_IPV4=${WG_IPV4}
WG_IPV6=${WG_IPV6}
LISTEN_PORT=${LISTEN_PORT}
SERVER_PRIV=${SERVER_PRIV}
SERVER_PUB=${SERVER_PUB}
DNS1=${DNS1}
DNS2=${DNS2}" >/etc/wireguard/params
echo "[Interface]
Address = ${WG_IPV4}/24,${WG_IPV6}/64
ListenPort = ${LISTEN_PORT}
PrivateKey = ${SERVER_PRIV}" >"/etc/wireguard/${WG_IFACE}.conf"
if pgrep firewalld; then
FIREWALLD_V4NET=$(echo "${WG_IPV4}" | cut -d"." -f1-3)".0"
FIREWALLD_V6NET=$(echo "${WG_IPV6}" | sed 's/:[^:]*$/:0/')
echo "PostUp = firewall-cmd --add-port ${LISTEN_PORT}/udp && firewall-cmd --add-rich-rule='rule family=ipv4 source address=${FIREWALLD_V4NET}/24 masquerade' && firewall-cmd --add-rich-rule='rule family=ipv6 source address=${FIREWALLD_V6NET}/24 masquerade'
PostDown = firewall-cmd --remove-port ${LISTEN_PORT}/udp && firewall-cmd --remove-rich-rule='rule family=ipv4 source address=${FIREWALLD_V4NET}/24 masquerade' && firewall-cmd --remove-rich-rule='rule family=ipv6 source address=${FIREWALLD_V6NET}/24 masquerade'" >>"/etc/wireguard/${WG_IFACE}.conf"
else
echo "PostUp = iptables -A FORWARD -i ${PUB_NIC} -o ${WG_IFACE} -j ACCEPT; iptables -A FORWARD -i ${WG_IFACE} -j ACCEPT; iptables -t nat -A POSTROUTING -o ${PUB_NIC} -j MASQUERADE; ip6tables -A FORWARD -i ${WG_IFACE} -j ACCEPT; ip6tables -t nat -A POSTROUTING -o ${PUB_NIC} -j MASQUERADE
PostDown = iptables -D FORWARD -i ${PUB_NIC} -o ${WG_IFACE} -j ACCEPT; iptables -D FORWARD -i ${WG_IFACE} -j ACCEPT; iptables -t nat -D POSTROUTING -o ${PUB_NIC} -j MASQUERADE; ip6tables -D FORWARD -i ${WG_IFACE} -j ACCEPT; ip6tables -t nat -D POSTROUTING -o ${PUB_NIC} -j MASQUERADE" >>"/etc/wireguard/${WG_IFACE}.conf"
fi
echo "net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1" >/etc/sysctl.d/wireguard.conf
sysctl --system
systemctl start "wg-quick@${WG_IFACE}"
systemctl enable "wg-quick@${WG_IFACE}"
add_new_client
echo "You can run this script again any time to add more clients, remove existing ones, or uninstall WireGuard."
systemctl is-active --quiet "wg-quick@${WG_IFACE}"
WG_RUNNING=$?
if [[ ${WG_RUNNING} -ne 0 ]]; then
echo -e "\n${RED}WARNING: WireGuard does not appear to be running.${NC}"
echo -e "${ORANGE}You can check status with: systemctl status wg-quick@${WG_IFACE}${NC}"
echo -e "${ORANGE}If you get an error about missing device, please reboot your server and try again.${NC}"
fi
}
add_new_client() {
ENDPOINT="${PUB_IP}:${LISTEN_PORT}"
echo ""
echo "Enter a name for your new client."
echo "Use only alphanumeric characters, underscores, or dashes, maximum 15 characters."
until [[ ${CLIENT_NAME} =~ ^[a-zA-Z0-9_-]+$ && ${CLIENT_EXISTS} == '0' && ${#CLIENT_NAME} -lt 16 ]]; do
read -rp "Client name: " -e CLIENT_NAME
CLIENT_EXISTS=$(grep -c -E "^### Client ${CLIENT_NAME}\$" "/etc/wireguard/${WG_IFACE}.conf")
if [[ ${CLIENT_EXISTS} == '1' ]]; then
echo ""
echo "A client with this name already exists, please choose a different name."
echo ""
fi
done
for OCTET in {2..254}; do
IP_EXISTS=$(grep -c "${WG_IPV4::-1}${OCTET}" "/etc/wireguard/${WG_IFACE}.conf")
if [[ ${IP_EXISTS} == '0' ]]; then
break
fi
done
if [[ ${IP_EXISTS} == '1' ]]; then
echo ""
echo "The configured WireGuard subnet can only support up to 253 clients."
exit 1
fi
BASE_V4=$(echo "$WG_IPV4" | awk -F '.' '{ print $1"."$2"."$3 }')
until [[ ${V4_EXISTS} == '0' ]]; do
read -rp "Client WireGuard IPv4: ${BASE_V4}." -e -i "${OCTET}" OCTET
CLIENT_V4="${BASE_V4}.${OCTET}"
V4_EXISTS=$(grep -c "$CLIENT_V4/24" "/etc/wireguard/${WG_IFACE}.conf")
if [[ ${V4_EXISTS} == '1' ]]; then
echo ""
echo "A client with this IPv4 already exists, please choose another."
echo ""
fi
done
BASE_V6=$(echo "$WG_IPV6" | awk -F '::' '{ print $1 }')
until [[ ${V6_EXISTS} == '0' ]]; do
read -rp "Client WireGuard IPv6: ${BASE_V6}::" -e -i "${OCTET}" OCTET
CLIENT_V6="${BASE_V6}::${OCTET}"
V6_EXISTS=$(grep -c "${CLIENT_V6}/64" "/etc/wireguard/${WG_IFACE}.conf")
if [[ ${V6_EXISTS} == '1' ]]; then
echo ""
echo "A client with this IPv6 already exists, please choose another."
echo ""
fi
done
CLIENT_PRIV=$(wg genkey)
CLIENT_PUB=$(echo "${CLIENT_PRIV}" | wg pubkey)
PSK=$(wg genpsk)
if [ -e "/home/${CLIENT_NAME}" ]; then
OUT_DIR="/home/${CLIENT_NAME}"
elif [ "${SUDO_USER}" ]; then
if [ "${SUDO_USER}" == "root" ]; then
OUT_DIR="/root"
else
OUT_DIR="/home/${SUDO_USER}"
fi
else
OUT_DIR="/root"
fi
echo "[Interface]
PrivateKey = ${CLIENT_PRIV}
Address = ${CLIENT_V4}/32,${CLIENT_V6}/128
DNS = ${DNS1},${DNS2}
[Peer]
PublicKey = ${SERVER_PUB}
PresharedKey = ${PSK}
Endpoint = ${ENDPOINT}
AllowedIPs = 0.0.0.0/0,::/0" >>"${OUT_DIR}/${WG_IFACE}-client-${CLIENT_NAME}.conf"
echo -e "\n### Client ${CLIENT_NAME}
[Peer]
PublicKey = ${CLIENT_PUB}
PresharedKey = ${PSK}
AllowedIPs = ${CLIENT_V4}/32,${CLIENT_V6}/128" >>"/etc/wireguard/${WG_IFACE}.conf"
wg syncconf "${WG_IFACE}" <(wg-quick strip "${WG_IFACE}")
echo -e "\nYour client configuration QR code:"
qrencode -t ansiutf8 -l L <"${OUT_DIR}/${WG_IFACE}-client-${CLIENT_NAME}.conf"
echo "Configuration file saved to: ${OUT_DIR}/${WG_IFACE}-client-${CLIENT_NAME}.conf"
}
revoke_client() {
TOTAL_CLIENTS=$(grep -c -E "^### Client" "/etc/wireguard/${WG_IFACE}.conf")
if [[ ${TOTAL_CLIENTS} == '0' ]]; then
echo ""
echo "You have no existing clients to revoke."
exit 1
fi
echo ""
echo "Select the client you want to revoke:"
grep -E "^### Client" "/etc/wireguard/${WG_IFACE}.conf" | cut -d ' ' -f 3 | nl -s ') '
until [[ ${CLIENT_SEL} -ge 1 && ${CLIENT_SEL} -le ${TOTAL_CLIENTS} ]]; do
if [[ ${TOTAL_CLIENTS} == '1' ]]; then
read -rp "Select client [1]: " CLIENT_SEL
else
read -rp "Select client [1-${TOTAL_CLIENTS}]: " CLIENT_SEL
fi
done
CLIENT_NAME=$(grep -E "^### Client" "/etc/wireguard/${WG_IFACE}.conf" | cut -d ' ' -f 3 | sed -n "${CLIENT_SEL}"p)
sed -i "/^### Client ${CLIENT_NAME}\$/",/^$/d "/etc/wireguard/${WG_IFACE}.conf"
rm -f "${HOME}/${WG_IFACE}-client-${CLIENT_NAME}.conf"
wg syncconf "${WG_IFACE}" <(wg-quick strip "${WG_IFACE}")
}
uninstall_wireguard() {
echo ""
read -rp "Are you sure you want to completely remove WireGuard? [y/n]: " -e -i n CONFIRM
if [[ $CONFIRM == 'y' ]]; then
detect_os
systemctl stop "wg-quick@${WG_IFACE}"
systemctl disable "wg-quick@${WG_IFACE}"
if [[ ${TARGET_OS} == 'ubuntu' ]]; then
apt-get autoremove --purge -y wireguard qrencode
elif [[ ${TARGET_OS} == 'debian' ]]; then
apt-get autoremove --purge -y wireguard qrencode
elif [[ ${TARGET_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 [[ ${TARGET_OS} == 'centos' ]]; then
yum -y remove kmod-wireguard wireguard-tools qrencode
yum -y autoremove
elif [[ ${TARGET_OS} == 'oracle' ]]; then
yum -y remove wireguard-tools qrencode
yum -y autoremove
elif [[ ${TARGET_OS} == 'arch' ]]; then
pacman -Rs --noconfirm wireguard-tools qrencode
fi
rm -rf /etc/wireguard
rm -f /etc/sysctl.d/wireguard.conf
sysctl --system
systemctl is-active --quiet "wg-quick@${WG_IFACE}"
WG_RUNNING=$?
if [[ ${WG_RUNNING} -eq 0 ]]; then
echo "WireGuard was not completely uninstalled."
exit 1
else
echo "WireGuard uninstalled successfully."
exit 0
fi
else
echo ""
echo "Uninstall cancelled."
fi
}
main_menu() {
echo "Welcome to WireGuard installer!"
echo "Source available at: https://github.com/angristan/wireguard-install"
echo ""
echo "WireGuard is already installed on this system."
echo ""
echo "What would you like to do?"
echo " 1) Add a new client"
echo " 2) Revoke an existing client"
echo " 3) Uninstall WireGuard"
echo " 4) Exit"
until [[ ${MENU_SEL} =~ ^[1-4]$ ]]; do
read -rp "Select an option [1-4]: " MENU_SEL
done
case "${MENU_SEL}" in
1)
add_new_client
;;
2)
revoke_client
;;
3)
uninstall_wireguard
;;
4)
exit 0
;;
esac
}
pre_flight_check
if [[ -e /etc/wireguard/params ]]; then
source /etc/wireguard/params
main_menu
else
install_wireguard
fi
More information about Jason Donenfeld's ISRG Radiant Award can be found here: https://www.abetterinternet.org/post/radiant-award-jason-donenfeld/