diff --git a/extras/ikev2setup.sh b/extras/ikev2setup.sh index d4dd567..ff84c25 100755 --- a/extras/ikev2setup.sh +++ b/extras/ikev2setup.sh @@ -67,14 +67,19 @@ check_os() { ;; [Aa]lpine) os_type=alpine - [ "$in_container" != "1" ] && exiterr "This script only supports Alpine Linux in a Docker container." ;; *) - exiterr "This script only supports Ubuntu, Debian, CentOS/RHEL 7/8 and Amazon Linux 2." + echo "Error: This script only supports one of the following OS:" >&2 + echo " Ubuntu, Debian, CentOS/RHEL, Rocky Linux, AlmaLinux," >&2 + echo " Amazon Linux 2 and Alpine Linux" >&2 + exit 1 ;; esac if [ "$os_type" = "alpine" ]; then - os_ver=$(. /etc/os-release && printf '%s' "$VERSION_ID") + os_ver=$(. /etc/os-release && printf '%s' "$VERSION_ID" | cut -d '.' -f 1,2) + if [ "$os_ver" != "3.14" ]; then + exiterr "This script only supports Alpine Linux 3.14." + fi else os_ver=$(sed 's/\..*//' /etc/debian_version | tr -dc 'A-Za-z0-9') fi diff --git a/extras/quickstart.sh b/extras/quickstart.sh index 2927838..6abd69d 100755 --- a/extras/quickstart.sh +++ b/extras/quickstart.sh @@ -1,6 +1,7 @@ #!/bin/sh # -# Quick start script to set up an IPsec VPN server with IPsec/L2TP, Cisco IPsec and IKEv2 +# Quick start script to set up an IPsec VPN server on Ubuntu, Debian, +# CentOS/RHEL, Rocky Linux, AlmaLinux, Amazon Linux 2 and Alpine Linux # Works on any dedicated server or virtual private server (VPS) # # DO NOT RUN THIS SCRIPT ON YOUR PC OR MAC! @@ -61,23 +62,38 @@ check_os() { [Rr]aspbian) os_type=raspbian ;; + [Aa]lpine) + os_type=alpine + ;; *) - exiterr "This script only supports Ubuntu, Debian, CentOS/RHEL 7/8 and Amazon Linux 2." + echo "Error: This script only supports one of the following OS:" >&2 + echo " Ubuntu, Debian, CentOS/RHEL, Rocky Linux, AlmaLinux," >&2 + echo " Amazon Linux 2 and Alpine Linux" >&2 + exit 1 ;; esac - os_ver=$(sed 's/\..*//' /etc/debian_version | tr -dc 'A-Za-z0-9') - if [ "$os_ver" = "8" ] || [ "$os_ver" = "jessiesid" ]; then - exiterr "Debian 8 or Ubuntu < 16.04 is not supported." - fi - if { [ "$os_ver" = "10" ] || [ "$os_ver" = "11" ]; } && [ ! -e /dev/ppp ]; then - exiterr "/dev/ppp is missing. Debian 11 or 10 users, see: https://git.io/vpndebian10" + if [ "$os_type" = "alpine" ]; then + os_ver=$(. /etc/os-release && printf '%s' "$VERSION_ID" | cut -d '.' -f 1,2) + if [ "$os_ver" != "3.14" ]; then + exiterr "This script only supports Alpine Linux 3.14." + fi + else + os_ver=$(sed 's/\..*//' /etc/debian_version | tr -dc 'A-Za-z0-9') + if [ "$os_ver" = "8" ] || [ "$os_ver" = "jessiesid" ]; then + exiterr "Debian 8 or Ubuntu < 16.04 is not supported." + fi + if { [ "$os_ver" = "10" ] || [ "$os_ver" = "11" ]; } && [ ! -e /dev/ppp ]; then + exiterr "/dev/ppp is missing. Debian 11 or 10 users, see: https://git.io/vpndebian10" + fi fi fi } check_iface() { def_iface=$(route 2>/dev/null | grep -m 1 '^default' | grep -o '[^ ]*$') - [ -z "$def_iface" ] && def_iface=$(ip -4 route list 0/0 2>/dev/null | grep -m 1 -Po '(?<=dev )(\S+)') + if [ "$os_type" != "alpine" ]; then + [ -z "$def_iface" ] && def_iface=$(ip -4 route list 0/0 2>/dev/null | grep -m 1 -Po '(?<=dev )(\S+)') + fi def_state=$(cat "/sys/class/net/$def_iface/operstate" 2>/dev/null) check_wl=0 if [ -n "$def_state" ] && [ "$def_state" != "down" ]; then @@ -120,7 +136,7 @@ wait_for_apt() { done } -install_wget() { +install_pkgs() { if ! command -v wget >/dev/null 2>&1; then if [ "$os_type" = "ubuntu" ] || [ "$os_type" = "debian" ] || [ "$os_type" = "raspbian" ]; then wait_for_apt @@ -133,13 +149,19 @@ install_wget() { set -x apt-get -yqq install wget >/dev/null ) || exiterr "'apt-get install wget' failed." - else + elif [ "$os_type" != "alpine" ]; then ( set -x yum -y -q install wget >/dev/null ) || exiterr "'yum install wget' failed." fi fi + if [ "$os_type" = "alpine" ]; then + ( + set -x + apk add -U -q bash net-tools wget sed grep + ) || exiterr "'apk add' failed." + fi } get_setup_url() { @@ -149,6 +171,8 @@ get_setup_url() { sh_file="vpnsetup_centos.sh" elif [ "$os_type" = "amzn" ]; then sh_file="vpnsetup_amzn.sh" + elif [ "$os_type" = "alpine" ]; then + sh_file="vpnsetup_alpine.sh" fi setup_url="$base_url/$sh_file" } @@ -183,7 +207,7 @@ quickstart() { check_os check_iface check_iptables - install_wget + install_pkgs get_setup_url run_setup } diff --git a/extras/vpnuninstall.sh b/extras/vpnuninstall.sh index 83563a2..aa5499e 100755 --- a/extras/vpnuninstall.sh +++ b/extras/vpnuninstall.sh @@ -1,7 +1,7 @@ #!/bin/bash # # Script to uninstall IPsec VPN on Ubuntu, Debian, CentOS/RHEL, -# Rocky Linux, AlmaLinux and Amazon Linux 2 +# Rocky Linux, AlmaLinux, Amazon Linux 2 and Alpine Linux # # DO NOT RUN THIS SCRIPT ON YOUR PC OR MAC! # @@ -53,8 +53,14 @@ check_os() { [Rr]aspbian) os_type=raspbian ;; + [Aa]lpine) + os_type=alpine + ;; *) - exiterr "This script only supports Ubuntu, Debian, CentOS/RHEL 7/8 and Amazon Linux 2." + echo "Error: This script only supports one of the following OS:" >&2 + echo " Ubuntu, Debian, CentOS/RHEL, Rocky Linux, AlmaLinux," >&2 + echo " Amazon Linux 2 and Alpine Linux" >&2 + exit 1 ;; esac fi @@ -70,7 +76,9 @@ check_libreswan() { check_iface() { def_iface=$(route 2>/dev/null | grep -m 1 '^default' | grep -o '[^ ]*$') - [ -z "$def_iface" ] && def_iface=$(ip -4 route list 0/0 2>/dev/null | grep -m 1 -Po '(?<=dev )(\S+)') + if [ "$os_type" != "alpine" ]; then + [ -z "$def_iface" ] && def_iface=$(ip -4 route list 0/0 2>/dev/null | grep -m 1 -Po '(?<=dev )(\S+)') + fi def_state=$(cat "/sys/class/net/$def_iface/operstate" 2>/dev/null) if [ -n "$def_state" ] && [ "$def_state" != "down" ]; then check_wl=0 @@ -146,6 +154,8 @@ remove_xl2tpd() { if [ "$os_type" = "ubuntu" ] || [ "$os_type" = "debian" ] || [ "$os_type" = "raspbian" ]; then export DEBIAN_FRONTEND=noninteractive apt-get -yqq purge xl2tpd >/dev/null + elif [ "$os_type" = "alpine" ]; then + apk del -q xl2tpd else yum -y -q remove xl2tpd >/dev/null fi @@ -155,7 +165,11 @@ update_sysctl() { if grep -qs "hwdsl2 VPN script" /etc/sysctl.conf; then bigecho "Updating sysctl settings..." conf_bk "/etc/sysctl.conf" - sed --follow-symlinks -i '/# Added by hwdsl2 VPN script/,+17d' /etc/sysctl.conf + if [ "$os_type" = "alpine" ]; then + sed -i '/# Added by hwdsl2 VPN script/,+17d' /etc/sysctl.conf + else + sed --follow-symlinks -i '/# Added by hwdsl2 VPN script/,+17d' /etc/sysctl.conf + fi echo 0 > /proc/sys/net/ipv4/ip_forward fi } @@ -164,13 +178,18 @@ update_rclocal() { if grep -qs "hwdsl2 VPN script" /etc/rc.local; then bigecho "Updating rc.local..." conf_bk "/etc/rc.local" - sed --follow-symlinks -i '/# Added by hwdsl2 VPN script/,+4d' /etc/rc.local + if [ "$os_type" = "alpine" ]; then + sed -i '/# Added by hwdsl2 VPN script/,+4d' /etc/rc.local + else + sed --follow-symlinks -i '/# Added by hwdsl2 VPN script/,+4d' /etc/rc.local + fi fi } update_iptables_rules() { use_nft=0 - if [ "$os_type" = "ubuntu" ] || [ "$os_type" = "debian" ] || [ "$os_type" = "raspbian" ]; then + if [ "$os_type" = "ubuntu" ] || [ "$os_type" = "debian" ] || [ "$os_type" = "raspbian" ] \ + || [ "$os_type" = "alpine" ]; then IPT_FILE=/etc/iptables.rules IPT_FILE2=/etc/iptables/rules.v4 else diff --git a/extras/vpnupgrade.sh b/extras/vpnupgrade.sh index 70358be..20dce58 100755 --- a/extras/vpnupgrade.sh +++ b/extras/vpnupgrade.sh @@ -1,7 +1,7 @@ #!/bin/sh # # Script to update Libreswan on Ubuntu, Debian, CentOS/RHEL, Rocky Linux, -# AlmaLinux and Amazon Linux 2 +# AlmaLinux, Amazon Linux 2 and Alpine Linux # # The latest version of this script is available at: # https://github.com/hwdsl2/setup-ipsec-vpn @@ -64,31 +64,59 @@ check_os() { [Rr]aspbian) os_type=raspbian ;; + [Aa]lpine) + os_type=alpine + ;; *) - exiterr "This script only supports Ubuntu, Debian, CentOS/RHEL 7/8 and Amazon Linux 2." + echo "Error: This script only supports one of the following OS:" >&2 + echo " Ubuntu, Debian, CentOS/RHEL, Rocky Linux, AlmaLinux," >&2 + echo " Amazon Linux 2 and Alpine Linux" >&2 + exit 1 ;; esac - os_ver=$(sed 's/\..*//' /etc/debian_version | tr -dc 'A-Za-z0-9') - if [ "$os_ver" = "8" ] || [ "$os_ver" = "jessiesid" ]; then - exiterr "Debian 8 or Ubuntu < 16.04 is not supported." + if [ "$os_type" = "alpine" ]; then + os_ver=$(. /etc/os-release && printf '%s' "$VERSION_ID" | cut -d '.' -f 1,2) + if [ "$os_ver" != "3.14" ]; then + exiterr "This script only supports Alpine Linux 3.14." + fi + else + os_ver=$(sed 's/\..*//' /etc/debian_version | tr -dc 'A-Za-z0-9') + if [ "$os_ver" = "8" ] || [ "$os_ver" = "jessiesid" ]; then + exiterr "Debian 8 or Ubuntu < 16.04 is not supported." + fi fi fi } check_libreswan() { - case $SWAN_VER in - 3.32|4.[1-5]) - true - ;; - *) + if [ "$os_type" != "alpine" ]; then + case $SWAN_VER in + 3.32|4.[1-5]) + true + ;; + *) cat 1>&2 <&2 </dev/null 2>&1; then if [ "$os_type" = "ubuntu" ] || [ "$os_type" = "debian" ] || [ "$os_type" = "raspbian" ]; then export DEBIAN_FRONTEND=noninteractive @@ -116,13 +144,19 @@ install_wget() { set -x apt-get -yqq install wget >/dev/null ) || exiterr "'apt-get install wget' failed." - else + elif [ "$os_type" != "alpine" ]; then ( set -x yum -y -q install wget >/dev/null ) || exiterr "'yum install wget' failed." fi fi + if [ "$os_type" = "alpine" ]; then + ( + set -x + apk add -U -q bash wget sed grep + ) || exiterr "'apk add' failed." + fi } get_setup_url() { @@ -132,6 +166,8 @@ get_setup_url() { sh_file="vpnupgrade_centos.sh" elif [ "$os_type" = "amzn" ]; then sh_file="vpnupgrade_amzn.sh" + elif [ "$os_type" = "alpine" ]; then + sh_file="vpnupgrade_alpine.sh" fi setup_url="$base_url/$sh_file" } @@ -158,7 +194,7 @@ vpnupgrade() { check_vz check_os check_libreswan - install_wget + install_pkgs get_setup_url run_setup } diff --git a/extras/vpnupgrade_alpine.sh b/extras/vpnupgrade_alpine.sh new file mode 100755 index 0000000..40346fc --- /dev/null +++ b/extras/vpnupgrade_alpine.sh @@ -0,0 +1,311 @@ +#!/bin/bash +# +# Script to update Libreswan on Alpine Linux +# +# The latest version of this script is available at: +# https://github.com/hwdsl2/setup-ipsec-vpn +# +# Copyright (C) 2021 Lin Song +# +# This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 +# Unported License: http://creativecommons.org/licenses/by-sa/3.0/ +# +# Attribution required: please include my name in any derivative and let me +# know how you have improved it! + +# Specify which Libreswan version to install. See: https://libreswan.org +SWAN_VER=4.5 + +### DO NOT edit below this line ### + +export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" +[ -n "$VPN_UPDATE_SWAN_VER" ] && SWAN_VER="$VPN_UPDATE_SWAN_VER" + +exiterr() { echo "Error: $1" >&2; exit 1; } +exiterr2() { exiterr "'apk add' failed."; } +bigecho() { echo "## $1"; } + +check_root() { + if [ "$(id -u)" != 0 ]; then + exiterr "Script must be run as root. Try 'sudo bash $0'" + fi +} + +check_vz() { + if [ -f /proc/user_beancounters ]; then + exiterr "OpenVZ VPS is not supported." + fi +} + +check_os() { + os_type=$(lsb_release -si 2>/dev/null) + os_arch=$(uname -m | tr -dc 'A-Za-z0-9_-') + [ -z "$os_type" ] && [ -f /etc/os-release ] && os_type=$(. /etc/os-release && printf '%s' "$ID") + case $os_type in + [Aa]lpine) + os_type=alpine + ;; + *) + exiterr "This script only supports Alpine Linux." + ;; + esac + os_ver=$(. /etc/os-release && printf '%s' "$VERSION_ID" | cut -d '.' -f 1,2) + if [ "$os_ver" != "3.14" ]; then + exiterr "This script only supports Alpine Linux 3.14." + fi +} + +check_libreswan() { + case $SWAN_VER in + 4.5) + true + ;; + *) +cat 1>&2 </dev/null) + swan_ver_old=$(printf '%s' "$ipsec_ver" | sed -e 's/.*Libreswan U\?//' -e 's/\( (\|\/K\).*//') + if ! printf '%s' "$ipsec_ver" | grep -q "Libreswan"; then +cat 1>&2 <<'EOF' +Error: This script requires Libreswan already installed. + See: https://github.com/hwdsl2/setup-ipsec-vpn +EOF + exit 1 + fi + + if [ "$swan_ver_old" = "$SWAN_VER" ]; then +cat </dev/null + trap 'finish $? $((dlo+1))' EXIT + mkdir -p /opt/src + cd /opt/src || exit 1 +} + +install_pkgs() { + bigecho "Installing required packages..." + ( + set -x + apk add -U -q bash bind-tools coreutils openssl wget iproute2 sed grep \ + libcap-ng libcurl libevent linux-pam musl nspr nss nss-tools \ + bison flex gcc make libc-dev bsd-compat-headers linux-pam-dev nss-dev \ + libcap-ng-dev libevent-dev curl-dev nspr-dev uuidgen openrc + ) || exiterr2 +} + +get_libreswan() { + bigecho "Downloading Libreswan..." + swan_file="libreswan-$SWAN_VER.tar.gz" + swan_url1="https://github.com/libreswan/libreswan/archive/v$SWAN_VER.tar.gz" + swan_url2="https://download.libreswan.org/$swan_file" + ( + set -x + wget -t 3 -T 30 -q -O "$swan_file" "$swan_url1" || wget -t 3 -T 30 -q -O "$swan_file" "$swan_url2" + ) || exit 1 + /bin/rm -rf "/opt/src/libreswan-$SWAN_VER" + tar xzf "$swan_file" && /bin/rm -f "$swan_file" +} + +install_libreswan() { + bigecho "Compiling and installing Libreswan, please wait..." + cd "libreswan-$SWAN_VER" || exit 1 + sed -i '28s/stdlib\.h/sys\/types.h/' include/fd.h +cat > Makefile.inc.local <<'EOF' +WERROR_CFLAGS=-w -s +USE_DNSSEC=false +USE_DH2=true +FINALNSSDIR=/etc/ipsec.d +USE_GLIBC_KERN_FLIP_HEADERS=true +EOF + NPROCS=$(grep -c ^processor /proc/cpuinfo) + [ -z "$NPROCS" ] && NPROCS=1 + ( + set -x + make "-j$((NPROCS+1))" -s base >/dev/null && make -s install-base >/dev/null + ) + + cd /opt/src || exit 1 + /bin/rm -rf "/opt/src/libreswan-$SWAN_VER" + if ! /usr/local/sbin/ipsec --version 2>/dev/null | grep -qF "$SWAN_VER"; then + exiterr "Libreswan $SWAN_VER failed to build." + fi +} + +update_config() { + bigecho "Updating VPN configuration..." + IKE_NEW=" ike=aes256-sha2,aes128-sha2,aes256-sha1,aes128-sha1,aes256-sha2;modp1024,aes128-sha1;modp1024" + PHASE2_NEW=" phase2alg=aes_gcm-null,aes128-sha1,aes256-sha1,aes256-sha2_512,aes128-sha2,aes256-sha2" + + if uname -m | grep -qi '^arm'; then + if ! modprobe -q sha512; then + PHASE2_NEW=" phase2alg=aes_gcm-null,aes128-sha1,aes256-sha1,aes128-sha2,aes256-sha2" + fi + fi + + dns_state=0 + DNS_SRV1=$(grep "modecfgdns1=" /etc/ipsec.conf | head -n 1 | cut -d '=' -f 2) + DNS_SRV2=$(grep "modecfgdns2=" /etc/ipsec.conf | head -n 1 | cut -d '=' -f 2) + [ -n "$DNS_SRV1" ] && dns_state=2 + [ -n "$DNS_SRV1" ] && [ -n "$DNS_SRV2" ] && dns_state=1 + [ "$(grep -c "modecfgdns1=" /etc/ipsec.conf)" -gt "1" ] && dns_state=3 + + sed -i".old-$(date +%F-%T)" \ + -e "s/^[[:space:]]\+auth=/ phase2=/" \ + -e "s/^[[:space:]]\+forceencaps=/ encapsulation=/" \ + -e "s/^[[:space:]]\+ike-frag=/ fragmentation=/" \ + -e "s/^[[:space:]]\+sha2_truncbug=/ sha2-truncbug=/" \ + -e "s/^[[:space:]]\+sha2-truncbug=yes/ sha2-truncbug=no/" \ + -e "s/^[[:space:]]\+ike=.\+/$IKE_NEW/" \ + -e "s/^[[:space:]]\+phase2alg=.\+/$PHASE2_NEW/" /etc/ipsec.conf + + if [ "$dns_state" = "1" ]; then + sed -i -e "s/^[[:space:]]\+modecfgdns1=.\+/ modecfgdns=\"$DNS_SRV1 $DNS_SRV2\"/" \ + -e "/modecfgdns2=/d" /etc/ipsec.conf + elif [ "$dns_state" = "2" ]; then + sed -i "s/^[[:space:]]\+modecfgdns1=.\+/ modecfgdns=$DNS_SRV1/" /etc/ipsec.conf + fi + + sed -i "/ikev2=never/d" /etc/ipsec.conf + sed -i "/conn shared/a \ ikev2=never" /etc/ipsec.conf + + if grep -qs ike-frag /etc/ipsec.d/ikev2.conf; then + sed -i 's/^[[:space:]]\+ike-frag=/ fragmentation=/' /etc/ipsec.d/ikev2.conf + fi +} + +restart_ipsec() { + bigecho "Restarting IPsec service..." + mkdir -p /run/pluto + service ipsec restart >/dev/null 2>&1 +} + +show_setup_complete() { +cat <&2 + echo " Ubuntu, Debian, CentOS/RHEL, Rocky Linux, AlmaLinux," >&2 + echo " Amazon Linux 2 and Alpine Linux" >&2 + exit 1 ;; esac - os_ver=$(sed 's/\..*//' /etc/debian_version | tr -dc 'A-Za-z0-9') - if [ "$os_ver" = "8" ] || [ "$os_ver" = "jessiesid" ]; then - exiterr "Debian 8 or Ubuntu < 16.04 is not supported." - fi - if { [ "$os_ver" = "10" ] || [ "$os_ver" = "11" ]; } && [ ! -e /dev/ppp ]; then - exiterr "/dev/ppp is missing. Debian 11 or 10 users, see: https://git.io/vpndebian10" + if [ "$os_type" = "alpine" ]; then + os_ver=$(. /etc/os-release && printf '%s' "$VERSION_ID" | cut -d '.' -f 1,2) + if [ "$os_ver" != "3.14" ]; then + exiterr "This script only supports Alpine Linux 3.14." + fi + else + os_ver=$(sed 's/\..*//' /etc/debian_version | tr -dc 'A-Za-z0-9') + if [ "$os_ver" = "8" ] || [ "$os_ver" = "jessiesid" ]; then + exiterr "Debian 8 or Ubuntu < 16.04 is not supported." + fi + if { [ "$os_ver" = "10" ] || [ "$os_ver" = "11" ]; } && [ ! -e /dev/ppp ]; then + exiterr "/dev/ppp is missing. Debian 11 or 10 users, see: https://git.io/vpndebian10" + fi fi fi } check_iface() { def_iface=$(route 2>/dev/null | grep -m 1 '^default' | grep -o '[^ ]*$') - [ -z "$def_iface" ] && def_iface=$(ip -4 route list 0/0 2>/dev/null | grep -m 1 -Po '(?<=dev )(\S+)') + if [ "$os_type" != "alpine" ]; then + [ -z "$def_iface" ] && def_iface=$(ip -4 route list 0/0 2>/dev/null | grep -m 1 -Po '(?<=dev )(\S+)') + fi def_state=$(cat "/sys/class/net/$def_iface/operstate" 2>/dev/null) check_wl=0 if [ -n "$def_state" ] && [ "$def_state" != "down" ]; then @@ -174,7 +189,7 @@ wait_for_apt() { done } -install_wget() { +install_pkgs() { if ! command -v wget >/dev/null 2>&1; then if [ "$os_type" = "ubuntu" ] || [ "$os_type" = "debian" ] || [ "$os_type" = "raspbian" ]; then wait_for_apt @@ -187,13 +202,19 @@ install_wget() { set -x apt-get -yqq install wget >/dev/null ) || exiterr "'apt-get install wget' failed." - else + elif [ "$os_type" != "alpine" ]; then ( set -x yum -y -q install wget >/dev/null ) || exiterr "'yum install wget' failed." fi fi + if [ "$os_type" = "alpine" ]; then + ( + set -x + apk add -U -q bash net-tools wget sed grep + ) || exiterr "'apk add' failed." + fi } get_setup_url() { @@ -203,6 +224,8 @@ get_setup_url() { sh_file="vpnsetup_centos.sh" elif [ "$os_type" = "amzn" ]; then sh_file="vpnsetup_amzn.sh" + elif [ "$os_type" = "alpine" ]; then + sh_file="vpnsetup_alpine.sh" fi setup_url="$base_url/$sh_file" } @@ -237,7 +260,7 @@ vpnsetup() { check_creds check_dns check_iptables - install_wget + install_pkgs get_setup_url run_setup } diff --git a/vpnsetup_alpine.sh b/vpnsetup_alpine.sh new file mode 100755 index 0000000..3659977 --- /dev/null +++ b/vpnsetup_alpine.sh @@ -0,0 +1,546 @@ +#!/bin/bash +# +# Script for automatic setup of an IPsec VPN server on Alpine Linux +# +# DO NOT RUN THIS SCRIPT ON YOUR PC OR MAC! +# +# The latest version of this script is available at: +# https://github.com/hwdsl2/setup-ipsec-vpn +# +# Copyright (C) 2021 Lin Song +# +# This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 +# Unported License: http://creativecommons.org/licenses/by-sa/3.0/ +# +# Attribution required: please include my name in any derivative and let me +# know how you have improved it! + +# ===================================================== + +# Define your own values for these variables +# - IPsec pre-shared key, VPN username and password +# - All values MUST be placed inside 'single quotes' +# - DO NOT use these special characters within values: \ " ' + +YOUR_IPSEC_PSK='' +YOUR_USERNAME='' +YOUR_PASSWORD='' + +# Important notes: https://git.io/vpnnotes +# Setup VPN clients: https://git.io/vpnclients +# IKEv2 guide: https://git.io/ikev2 + +# ===================================================== + +export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" +SYS_DT=$(date +%F-%T | tr ':' '_') + +exiterr() { echo "Error: $1" >&2; exit 1; } +exiterr2() { exiterr "'apk add' failed."; } +conf_bk() { /bin/cp -f "$1" "$1.old-$SYS_DT" 2>/dev/null; } +bigecho() { echo "## $1"; } + +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_root() { + if [ "$(id -u)" != 0 ]; then + exiterr "Script must be run as root. Try 'sudo bash $0'" + fi +} + +check_vz() { + if [ -f /proc/user_beancounters ]; then + exiterr "OpenVZ VPS is not supported." + fi +} + +check_os() { + os_type=$(lsb_release -si 2>/dev/null) + os_arch=$(uname -m | tr -dc 'A-Za-z0-9_-') + [ -z "$os_type" ] && [ -f /etc/os-release ] && os_type=$(. /etc/os-release && printf '%s' "$ID") + case $os_type in + [Aa]lpine) + os_type=alpine + ;; + *) + exiterr "This script only supports Alpine Linux." + ;; + esac + os_ver=$(. /etc/os-release && printf '%s' "$VERSION_ID" | cut -d '.' -f 1,2) + if [ "$os_ver" != "3.14" ]; then + exiterr "This script only supports Alpine Linux 3.14." + fi +} + +check_iface() { + def_iface=$(route 2>/dev/null | grep -m 1 '^default' | grep -o '[^ ]*$') + def_state=$(cat "/sys/class/net/$def_iface/operstate" 2>/dev/null) + if [ -n "$def_state" ] && [ "$def_state" != "down" ]; then + if ! uname -m | grep -qi -e '^arm' -e '^aarch64'; then + case $def_iface in + wl*) + exiterr "Wireless interface '$def_iface' detected. DO NOT run this script on your PC or Mac!" + ;; + esac + fi + NET_IFACE="$def_iface" + else + eth0_state=$(cat "/sys/class/net/eth0/operstate" 2>/dev/null) + if [ -z "$eth0_state" ] || [ "$eth0_state" = "down" ]; then + exiterr "Could not detect the default network interface." + fi + NET_IFACE=eth0 + fi +} + +check_creds() { + [ -n "$YOUR_IPSEC_PSK" ] && VPN_IPSEC_PSK="$YOUR_IPSEC_PSK" + [ -n "$YOUR_USERNAME" ] && VPN_USER="$YOUR_USERNAME" + [ -n "$YOUR_PASSWORD" ] && VPN_PASSWORD="$YOUR_PASSWORD" + + if [ -z "$VPN_IPSEC_PSK" ] && [ -z "$VPN_USER" ] && [ -z "$VPN_PASSWORD" ]; then + bigecho "VPN credentials not set by user. Generating random PSK and password..." + VPN_IPSEC_PSK=$(LC_CTYPE=C tr -dc 'A-HJ-NPR-Za-km-z2-9' /dev/null | head -c 20) + VPN_USER=vpnuser + VPN_PASSWORD=$(LC_CTYPE=C tr -dc 'A-HJ-NPR-Za-km-z2-9' /dev/null | head -c 16) + fi + + if [ -z "$VPN_IPSEC_PSK" ] || [ -z "$VPN_USER" ] || [ -z "$VPN_PASSWORD" ]; then + exiterr "All VPN credentials must be specified. Edit the script and re-enter them." + fi + + if printf '%s' "$VPN_IPSEC_PSK $VPN_USER $VPN_PASSWORD" | LC_ALL=C grep -q '[^ -~]\+'; then + exiterr "VPN credentials must not contain non-ASCII characters." + fi + + case "$VPN_IPSEC_PSK $VPN_USER $VPN_PASSWORD" in + *[\\\"\']*) + exiterr "VPN credentials must not contain these special characters: \\ \" '" + ;; + esac +} + +check_dns() { + if { [ -n "$VPN_DNS_SRV1" ] && ! check_ip "$VPN_DNS_SRV1"; } \ + || { [ -n "$VPN_DNS_SRV2" ] && ! check_ip "$VPN_DNS_SRV2"; } then + exiterr "The DNS server specified is invalid." + fi +} + +start_setup() { + bigecho "VPN setup in progress... Please be patient." + # shellcheck disable=SC2154 + trap 'dlo=$dl;dl=$LINENO' DEBUG 2>/dev/null + trap 'finish $? $((dlo+1))' EXIT + mkdir -p /opt/src + cd /opt/src || exit 1 +} + +install_setup_pkgs() { + bigecho "Installing packages required for setup..." + ( + set -x + apk add -U -q bash bind-tools coreutils openssl wget iproute2 sed grep + ) || exiterr2 +} + +detect_ip() { + bigecho "Trying to auto discover IP of this server..." + public_ip=${VPN_PUBLIC_IP:-''} + check_ip "$public_ip" || public_ip=$(dig @resolver1.opendns.com -t A -4 myip.opendns.com +short) + check_ip "$public_ip" || public_ip=$(wget -t 3 -T 15 -qO- http://ipv4.icanhazip.com) + check_ip "$public_ip" || exiterr "Cannot detect this server's public IP. Define it as variable 'VPN_PUBLIC_IP' and re-run this script." +} + +install_vpn_pkgs() { + bigecho "Installing packages required for the VPN..." + ( + set -x + apk add -U -q libcap-ng libcurl libevent linux-pam musl nspr nss nss-tools \ + bison flex gcc make libc-dev bsd-compat-headers linux-pam-dev nss-dev \ + libcap-ng-dev libevent-dev curl-dev nspr-dev uuidgen openrc xl2tpd + ) || exiterr2 +} + +install_fail2ban() { + bigecho "Installing Fail2Ban to protect SSH..." + ( + set -x + apk add -U -q fail2ban + ) || exiterr2 +} + +get_ikev2_script() { + bigecho "Downloading IKEv2 script..." + ikev2_url="https://github.com/hwdsl2/setup-ipsec-vpn/raw/master/extras/ikev2setup.sh" + ( + set -x + wget -t 3 -T 30 -q -O ikev2.sh "$ikev2_url" + ) || /bin/rm -f ikev2.sh + [ -s ikev2.sh ] && chmod +x ikev2.sh && ln -s /opt/src/ikev2.sh /usr/bin 2>/dev/null +} + +check_libreswan() { + SWAN_VER=4.5 + ipsec_ver=$(/usr/local/sbin/ipsec --version 2>/dev/null) + swan_ver_old=$(printf '%s' "$ipsec_ver" | sed -e 's/.*Libreswan U\?//' -e 's/\( (\|\/K\).*//') + [ "$swan_ver_old" = "$SWAN_VER" ] +} + +get_libreswan() { + if ! check_libreswan; then + bigecho "Downloading Libreswan..." + swan_file="libreswan-$SWAN_VER.tar.gz" + swan_url1="https://github.com/libreswan/libreswan/archive/v$SWAN_VER.tar.gz" + swan_url2="https://download.libreswan.org/$swan_file" + ( + set -x + wget -t 3 -T 30 -q -O "$swan_file" "$swan_url1" || wget -t 3 -T 30 -q -O "$swan_file" "$swan_url2" + ) || exit 1 + /bin/rm -rf "/opt/src/libreswan-$SWAN_VER" + tar xzf "$swan_file" && /bin/rm -f "$swan_file" + else + bigecho "Libreswan $SWAN_VER is already installed, skipping..." + fi +} + +install_libreswan() { + if ! check_libreswan; then + bigecho "Compiling and installing Libreswan, please wait..." + cd "libreswan-$SWAN_VER" || exit 1 + sed -i '28s/stdlib\.h/sys\/types.h/' include/fd.h +cat > Makefile.inc.local <<'EOF' +WERROR_CFLAGS=-w -s +USE_DNSSEC=false +USE_DH2=true +FINALNSSDIR=/etc/ipsec.d +USE_GLIBC_KERN_FLIP_HEADERS=true +EOF + NPROCS=$(grep -c ^processor /proc/cpuinfo) + [ -z "$NPROCS" ] && NPROCS=1 + ( + set -x + make "-j$((NPROCS+1))" -s base >/dev/null && make -s install-base >/dev/null + ) + + cd /opt/src || exit 1 + /bin/rm -rf "/opt/src/libreswan-$SWAN_VER" + if ! /usr/local/sbin/ipsec --version 2>/dev/null | grep -qF "$SWAN_VER"; then + exiterr "Libreswan $SWAN_VER failed to build." + fi + fi +} + +create_vpn_config() { + bigecho "Creating VPN configuration..." + + L2TP_NET=${VPN_L2TP_NET:-'192.168.42.0/24'} + L2TP_LOCAL=${VPN_L2TP_LOCAL:-'192.168.42.1'} + L2TP_POOL=${VPN_L2TP_POOL:-'192.168.42.10-192.168.42.250'} + XAUTH_NET=${VPN_XAUTH_NET:-'192.168.43.0/24'} + XAUTH_POOL=${VPN_XAUTH_POOL:-'192.168.43.10-192.168.43.250'} + DNS_SRV1=${VPN_DNS_SRV1:-'8.8.8.8'} + DNS_SRV2=${VPN_DNS_SRV2:-'8.8.4.4'} + DNS_SRVS="\"$DNS_SRV1 $DNS_SRV2\"" + [ -n "$VPN_DNS_SRV1" ] && [ -z "$VPN_DNS_SRV2" ] && DNS_SRVS="$DNS_SRV1" + + # Create IPsec config + conf_bk "/etc/ipsec.conf" +cat > /etc/ipsec.conf < /etc/ipsec.secrets < /etc/xl2tpd/xl2tpd.conf < /etc/ppp/options.xl2tpd <> /etc/ppp/options.xl2tpd < /etc/ppp/chap-secrets < /etc/ipsec.d/passwd <> /etc/sysctl.conf </dev/null 2>&1 + iptables-save > "$IPT_FILE.old-$SYS_DT" + $ipi 1 -p udp --dport 1701 -m policy --dir in --pol none -j DROP + $ipi 2 -m conntrack --ctstate INVALID -j DROP + $ipi 3 -m conntrack --ctstate "$res" -j ACCEPT + $ipi 4 -p udp -m multiport --dports 500,4500 -j ACCEPT + $ipi 5 -p udp --dport 1701 -m policy --dir in --pol ipsec -j ACCEPT + $ipi 6 -p udp --dport 1701 -j DROP + $ipf 1 -m conntrack --ctstate INVALID -j DROP + $ipf 2 -i "$NET_IFACE" -o ppp+ -m conntrack --ctstate "$res" -j ACCEPT + $ipf 3 -i ppp+ -o "$NET_IFACE" -j ACCEPT + $ipf 4 -i ppp+ -o ppp+ -j ACCEPT + $ipf 5 -i "$NET_IFACE" -d "$XAUTH_NET" -m conntrack --ctstate "$res" -j ACCEPT + $ipf 6 -s "$XAUTH_NET" -o "$NET_IFACE" -j ACCEPT + $ipf 7 -s "$XAUTH_NET" -o ppp+ -j ACCEPT + iptables -A FORWARD -j DROP + $ipp -s "$XAUTH_NET" -o "$NET_IFACE" -m policy --dir out --pol none -j MASQUERADE + $ipp -s "$L2TP_NET" -o "$NET_IFACE" -j MASQUERADE + echo "# Modified by hwdsl2 VPN script" > "$IPT_FILE" + iptables-save >> "$IPT_FILE" + fi +} + +enable_on_boot() { + bigecho "Enabling services on boot..." + mkdir -p /etc/network/if-pre-up.d +cat > /etc/network/if-pre-up.d/iptablesload <<'EOF' +#!/bin/sh +iptables-restore < /etc/iptables.rules +exit 0 +EOF + chmod +x /etc/network/if-pre-up.d/iptablesload + + for svc in fail2ban ipsec xl2tpd; do + rc-update add "$svc" >/dev/null + done + + if ! grep -qs "hwdsl2 VPN script" /etc/rc.local; then + if [ -f /etc/rc.local ]; then + conf_bk "/etc/rc.local" + else + echo '#!/bin/sh' > /etc/rc.local + fi +cat >> /etc/rc.local <<'EOF' + +# Added by hwdsl2 VPN script +(sleep 15 +service ipsec restart +service xl2tpd restart +echo 1 > /proc/sys/net/ipv4/ip_forward)& +EOF + fi +} + +start_services() { + bigecho "Starting services..." + sysctl -e -q -p + + chmod +x /etc/rc.local + chmod 600 /etc/ipsec.secrets* /etc/ppp/chap-secrets* /etc/ipsec.d/passwd* + + mkdir -p /run/pluto + service fail2ban restart >/dev/null 2>&1 + service ipsec restart >/dev/null 2>&1 + service xl2tpd restart >/dev/null 2>&1 +} + +show_vpn_info() { +cat <