#!/bin/bash

#
# Name:         install_wireguard.sh
# Version:      1.9.0
# Description:  Wireguard One-Click Installer
#
# (c) 2022-2023, Rapidseedbox, alin@rapidseedbox.com
#                              alexey@rapidseedbox.com
#

SCRIPT_VER="1.9.0"

APP="Wireguard"
APP_CATEGORY="System Services"
APP_SCOPE="system"
APP_ENABLED="yes"
#><#APP_CHECK_VER_CMD="[ -x /usr/local/sbin/boringtun ] && /usr/local/sbin/boringtun --version 2>&1 | awk "{print \$NF}""
SUPPORTED_ON="Ubuntu16.04,Ubuntu18.04,Ubuntu20.04,Ubuntu22.04"
DAEMON_PORT=51820
LIBRARY="functions4.sh"

if [ "${SUPPORT-}" = '' ]; then
    SUPPORT="https://installers.rapidseedbox.com/scripts/whmcs_client_installers/support"
fi

LOGFILE="/var/log/whmcs_client_installers/${0}.log"
[ ! -d "/var/log/whmcs_client_installers" ] && MKDIR "/var/log/whmcs_client_installers"

wget -4 -nc -q --no-check-certificate --timeout=20 --tries=5 ${SUPPORT}/${LIBRARY} >> "${LOGFILE}" 2>&1
if [ ! -f "./${LIBRARY}" ] ; then
    echo "55"
    echo "Error downloading \"${SUPPORT}/${LIBRARY}\"" >> "${LOGFILE}"
    exit 55
fi

# shellcheck source=support/functions4.sh
. "./${LIBRARY}"

SCRIPTENTRY

WG_CONF_DIR="/etc/wireguard"
WG_IPV4="10.7.0.1"
WG_IPV4_SUBNET_PFX="$( echo "${WG_IPV4}" | awk -F'.' '{print $1"."$2"."$3}' )"
WG_DEV="wg0"
WG_PORT="${DAEMON_PORT}"

while : ; do

    main_rc=1
    CHECK_APP ; _rc=$?
    if [ ${_rc} -ne 0 ] ; then
        main_rc=${_rc}
        break
    fi


    # -- TUN device is required to use BoringTun if running inside a container
    if [ ! -e /dev/net/tun ] || ! ( exec 7<>/dev/net/tun ) 2> /dev/null; then
        ERROR "The system does not have the TUN device available."
        ERROR "TUN needs to be enabled before running this installer."
        break
    fi

    # -- By defalut assign the port 51820 to be used by WG and name the client file wg-rsb.conf
    client="${USERHOME}/wireguard-rsb.conf"
    qr="${USERHOME}/wireguard-qr.txt"
    qr_png="${USERHOME}/wireguard-qr.png"
    iptables_path="$( command -v iptables )"
    INFO "WireGuard listens on port ${WG_PORT}"

    INFO "WireGuard installation is ready to begin"

    # -- Install required packages
    APT_INSTALL "ca-certificates" "iptables" "qrencode" "fping" "cron" "whois" || break
    APT_INSTALL "wireguard-tools" || break

    # -- Create service diectories if needed
    if [ ! -d "${WG_CONF_DIR}" ] ; then
        MKDIR "${WG_CONF_DIR}" || break
        CHOWN_FILE 0700 "${WG_CONF_DIR}" || break
    fi
    if [ ! -d "/var/www/html/wireguard" ] ; then
        MKDIR "/var/www/html/wireguard" || break
    fi

    # -- Generate server keys
    WG_PRIV_KEY="$( wg genkey )"
    echo "${WG_PRIV_KEY}"  > "${WG_CONF_DIR}/private.key"
    CHMOD_FILE 0600 "${WG_CONF_DIR}/private.key"
    WG_PUB_KEY="$( wg pubkey <<< "${WG_PRIV_KEY}" )"
    echo "${WG_PUB_KEY}" > "${WG_CONF_DIR}/public.key"

    # -- Save WireGuard settings
    cat <<-EOF > "${WG_CONF_DIR}/params"
	SERVER_PUB_IP=${IP}
	SERVER_PUB_NIC=${DEV}
	SERVER_WG_NIC=${WG_DEV}
	SERVER_WG_IPV4=${WG_IPV4}
	SERVER_PORT=${WG_PORT}
	SERVER_PRIV_KEY=${WG_PRIV_KEY}
	SERVER_PUB_KEY=${WG_PUB_KEY}
	CLIENT_DNS_1=8.8.8.8
	CLIENT_DNS_2=8.8.4.4
	EOF
    CHMOD_FILE 0600 "${WG_CONF_DIR}/params"

    # -- Grab the BoringTun binary using wget or curl and extract into the right place
    RUN_LOG "{ wget -qO- https://wg.nyr.be/1/latest/download 2> /dev/null \
      || curl -sL https://wg.nyr.be/1/latest/download ; } \
           | tar -xz -C /usr/local/sbin/ --wildcards 'boringtun-*/boringtun' --strip-components 1"

    # -- Configure wireguard to use BoringTun
    MKDIR "/etc/systemd/system/wg-quick@${WG_DEV}.service.d" || break
    cat <<-EOF > "/etc/systemd/system/wg-quick@${WG_DEV}.service.d/boringtun.conf"
	[Service]
	Environment=WG_QUICK_USERSPACE_IMPLEMENTATION=boringtun
	Environment=WG_SUDO=1		  
	Environment=WG_LOG_LEVEL=debug
	TimeoutStartSec=60
	TimeoutStopSec=60
	EOF

    # -- Generate ${WG_DEV}.conf (server part)
    cat <<-EOF > "${WG_CONF_DIR}/${WG_DEV}.conf"
	[Interface]
	Address = ${WG_IPV4}/24
	PrivateKey = ${WG_PRIV_KEY}
	ListenPort = ${WG_PORT}
	PostUp = /bin/bash ${WG_CONF_DIR}/wg-if-updown.sh up ${WG_DEV} ${DEV}
	PostDown = /bin/bash ${WG_CONF_DIR}/wg-if-updown.sh down ${WG_DEV} ${DEV}
	EOF
    #
    # -- Generate ${WG_DEV}.conf (clients part)
    _start_addr=11
    _end_addr=15
    for _addr in $( seq ${_start_addr} ${_end_addr} ) ; do
        _psk="$( wg genpsk )"
        _key="$( wg genkey )"
        _client_id="$( printf '%03d' $(( _addr - _start_addr + 1 )) )"
        _client_conf="/var/www/html/wireguard/client-${_client_id}.conf"
        _client_qr_txt="/var/www/html/wireguard/client-${_client_id}-qr.txt"
        _client_qr_png="/var/www/html/wireguard/client-${_client_id}-qr.png"
        cat <<-EOF >> "${WG_CONF_DIR}/${WG_DEV}.conf"

		[Peer]
		# Client: client-${_client_id}
		PublicKey = $( wg pubkey <<< "${_key}" )
		PresharedKey = ${_psk}
		AllowedIPs = ${WG_IPV4_SUBNET_PFX}.${_addr}/32
		EOF
    
        cat <<-EOF > "${_client_conf}"
		[Interface]
		Address = ${WG_IPV4_SUBNET_PFX}.${_addr}/24
		DNS = 8.8.8.8, 8.8.4.4
		PrivateKey = ${_key}

		[Peer]
		PublicKey =  ${WG_PUB_KEY}
		PresharedKey = ${_psk}
		AllowedIPs = 0.0.0.0/0
		Endpoint = ${IP}:${WG_PORT}
		PersistentKeepalive = 25
		EOF
        # -- Encode the WG config file in a QR
        RUN_LOG "cat '${_client_conf}' | qrencode -t utf8 -o '${_client_qr_txt}'"
        RUN_LOG "cat '${_client_conf}' | qrencode -t png  -o '${_client_qr_png}'"
        # -- Compatibility stuff (old implementation)
        if [ "${_addr}" = "${_start_addr}" ] ; then
            LN "${_client_conf}"   "${USERHOME}/wireguard-rsb.conf"
            LN "${_client_qr_txt}" "${USERHOME}/wireguard-qr.txt"
            LN "${_client_qr_png}" "${USERHOME}/wireguard-qr.png"
        fi
    done
    #
    CHMOD_FILE 0600 "${WG_CONF_DIR}/${WG_DEV}.conf"

    # -- Create HTML info update script
    cat <<-EOF > "${WG_CONF_DIR}/wg_regenhtml.sh"
	#!/bin/bash
	export PATH=/usr/sbin:/usr/bin:/sbin:/bin
	WG_DEV="${WG_DEV}"
	export WG_DEV
	_wg_dump="\$( wg show ${WG_DEV} dump )"
	export _wg_dump
        [ -e "${WG_CONF_DIR}/rsb-wg-acct.sh" ] && /bin/bash "${WG_CONF_DIR}/rsb-wg-acct.sh"
	echo "<html><head><title>RSB: Wireguard information</title><meta http-equiv=\"refresh\" content=\"300\"><meta http-equiv=\"Cache-Control\" content=\"no-cache, no-store, must-revalidate\"/><meta http-equiv=\"Pragma\" content=\"no-cache\"/><meta http-equiv=\"Expires\" content=\"0\"/></head><body><h1>WireGuard</h1>
<h2>Client configurations</h2>
" > "/var/www/html/wireguard/index.html"
	while read -r _f ; do
	    _num="\$( basename "\${_f}" | sed -e 's/client-\(.*\)\.conf$/\1/' )"
	    _ts="\$( date -u '+%s' )"
	    _wg_ip="\$( awk '\$1 == "Address" {print \$NF}' "\${_f}" | sed -e 's/\/.*$//' )"
	    _info_line="\$( awk -v ip="\${_wg_ip}" '\$4 == ip"/32" && \$3 != "${WG_PORT}" && \$3 != "(none)" {print \$3" "\$4" "\$5}' <<< "\${_wg_dump}" )"
	    read -ra _info_arr <<< "\${_info_line}"
	    _style=""
	    _cc=""
	    _connected_ip=""
	    _connected_since="\${_info_arr[2]}"
	    if [ "\${_connected_since}" != "0" ] && [ -n "\${_info_arr[0]}" ] ; then
	        _connected_ip="\$( echo "\${_info_arr[0]}" | sed -e 's/:.*$//' )"
	        _cc="\$( whois "\${_connected_ip}" | grep -m1 '^country:' | awk '{print \$NF}' )"
	        _style=" style=\"border:5px double red;\""
	    fi
	    echo "
<h3>\$( basename "\${_f}" | sed -e 's/\.conf$//' )\$( if [ -n "\${_connected_ip}" ] ; then echo ", occupied by: \${_connected_ip}\$( [ -n "\${_cc}" ] && echo " (\${_cc})" ), last seen: \$( date -u -d "@\${_connected_since}" ))" ; fi )</h3>
<pre><code>\$( cat "\${_f}" )</code></pre>
<img src=\"client-\${_num}-qr.png\" alt=\"QR-\${_num}\"\${_style}>
" >> "/var/www/html/wireguard/index.html"
	done < <( find "/var/www/html/wireguard" -mindepth 1 -maxdepth 1 -name "*.conf" | sort )
	echo "<br><hr>Genereted on: \$( date -u )</body></html>" >> "/var/www/html/wireguard/index.html"
	exit 0
	EOF
    RUN_LOG "bash ${WG_CONF_DIR}/wg_regenhtml.sh 2>&1"

    # -- Create ${WG_DEV} UP/DOWN script
    cat <<-'EOF' > "/etc/wireguard/wg-if-updown.sh"
	#!/bin/bash
	export PATH=/usr/sbin:/usr/bin:/sbin:/bin
	SC_NAME="$( basename "${0}" )"
	OP="${1,,}"
	WG_DEV="${2,,}"
	DEV="${3,,}"
	case "${OP}" in
	  "down") IPT_OP="-D" ;;
	  "up")   IPT_OP="-I" ;;
	esac
	iptables -S | grep -- " ${WG_DEV} " \
	  | sed 's/^-A //' \
	  | while read -r rule ; do
	        echo "while iptables -C ${rule} ; do iptables -D ${rule} ; done" >> "/var/tmp/${SC_NAME}.$$"
	        bash "/var/tmp/${SC_NAME}.$$"
	    done
	rm -f "/var/tmp/${SC_NAME}.$$"
	iptables -t nat -S | grep -- "-A POSTROUTING -o ${DEV} -j MASQUERADE" \
	  | sed 's/^-A //' \
	  | while read -r rule ; do
	        echo "while iptables -t nat -C ${rule} ; do iptables -t nat -D ${rule} ; done" \
	            >> "/var/tmp/${SC_NAME}.$$"
	        bash "/var/tmp/${SC_NAME}.$$"
	    done
	rm -f "/var/tmp/${SC_NAME}.$$"
	iptables ${IPT_OP} FORWARD -i ${DEV} -o ${WG_DEV} -j ACCEPT
	iptables ${IPT_OP} FORWARD -i ${WG_DEV} -j ACCEPT
	iptables -t nat ${IPT_OP} POSTROUTING -o ${DEV} -j MASQUERADE
	iptables ${IPT_OP} FORWARD -i ${WG_DEV} -o ${WG_DEV} -j REJECT
	exit 0
	EOF


    # -- Enable net.ipv4.ip_forward for the system
    cat <<-EOF > "/etc/sysctl.d/99-wireguard-forward.conf"
	net.ipv4.ip_forward=1
	EOF

    # -- Enable without waiting for a reboot or service restart
    echo '1' > "/proc/sys/net/ipv4/ip_forward"

    # -- Enable and start the wireguard service
    SYSTEMD_RELOAD
    SVC_ENABLE "wg-quick@${WG_DEV}"
    SVC_START "wg-quick@${WG_DEV}"

    # -- Configure Apache2
    if [ -d "/etc/apache2/userconf-enabled" ] ; then
        cat <<-EOF > "/etc/apache2/userconf-enabled/wireguard.conf"
	RewriteEngine on
	RewriteRule ^/wireguard$ /wireguard/ [R=permanent]

	<Location /wireguard>
	   Options -Indexes
	   Order Allow,Deny
	   Allow from All
	   AuthType Digest
	   AuthName "apache2"
	   AuthDigestProvider file
	   AuthUserFile "/etc/apache2/users.password"
	   Require user user
	</Location>
	EOF
        SVC_RELOAD "apache2"
    fi

    # -- Configure `cron` job
    if [ -d "/etc/cron.d" ] ; then
        cat <<-EOF > "/etc/cron.d/wg_regenhtml"
	MAILTO=""
	PATH=/sbin:/bin:/usr/sbin:/usr/bin

	*/5 * * * * root /bin/bash /etc/wireguard/wg_regenhtml.sh
	EOF
    fi

    # -- Configure WireGuard directory URL in RSB Dashboard
    if [ -d "/var/www/dashboard" ] ; then
        RUN_LOG "ln -svf '/var/www/html/wireguard' '/var/www/dashboard/wireguard' 2>&1"
    fi

    INFO "Finished installing!"


    main_rc=0
    break

done


# Cleanup if error
if [ ${main_rc} -ne 0 -a ${main_rc} -ne 111 -a ${main_rc} -ne 117 ] ; then
    ERROR "'${APP}': Installation failed."
    #
    # Cleanup tasks here
    #
    for _svc in "wg-iptables" "wg-quick@${WG_DEV}" ; do
        SVC_ISACTIVE  "${_svc}" && SVC_STOP    "${_svc}"
        SVC_ISENABLED "${_svc}" && SVC_DISABLE "${_svc}"
    done
    #
    APT_REMOVE "wireguard-tools"
    RM_DIRS  "/etc/wireguard" "/etc/systemd/system/wg-quick@${WG_DEV}.service.d" 
    SYSTEMD_RELOAD
    RM_FILES "/usr/local/sbin/boringtun"
    RM_FILES "/etc/sysctl.d/99-wireguard-forward.conf" \
        && RUN_LOG "sysctl --system 2> /dev/null"
    if [ -d "/var/www/html/wireguard" ] ; then
        RM_DIRS "/var/www/html/wireguard"
    fi
    if [ -e "/etc/apache2/userconf-enabled/wireguard.conf" ] ; then
        RM_FILES "/etc/apache2/userconf-enabled/wireguard.conf"
        SVC_RELOAD "apache2"
    fi
    #
fi


SCRIPTEXIT
