Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

WireGuard: Next-Generation VPN Praised as a Work of Art by Linus Torvalds

Tech 1

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:

Key Advantages of WireGuard

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. Native Linux kernel integration: On Linux, WireGuard runs directly as an integrated kernel component which reduces overhead and improves performance compared to userspace implementations.
  6. 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.
  7. 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 udptunnel or udp2raw that 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/

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.