#!/bin/bash
#
# metadata_begin
# recipe: Wireguard
# tags: centos7,centos9,centos9-stream,alma,rocky,oracle,fedora,debian10,debian11,debian12,ubuntu1804,ubuntu2004,ubuntu2204,ubuntu2404
# revision: 2
# description_ru: Wireguard VPN server
# description_en: Wireguard VPN server
# metadata_end
#

cat <<\WG >>/tmp/wg.sh
RNAME="Wireguard"

set -x

LOG_PIPE=/tmp/log.pipe.$$
mkfifo ${LOG_PIPE}
LOG_FILE=/root/${RNAME}.log
touch ${LOG_FILE}
chmod 600 ${LOG_FILE}
tee < ${LOG_PIPE} ${LOG_FILE} &
exec > ${LOG_PIPE}
exec 2> ${LOG_PIPE}

killjobs() {
    test -n "$(jobs -p)" && kill $(jobs -p) || :
}
trap killjobs INT TERM EXIT

echo
echo "=== Recipe ${RNAME} started at $(date) ==="
echo

# Переменные
DIR=/etc/wireguard
WG_SCRIPT=/root/wg_client_add.sh

# Определение IP-адресов и сетевой карты
ipv4Addr=$(ip route get 1 | grep -Po '(?<=src )[^ ]+')
ipv6Addr=$(ip -6 route get 1 | grep -Po '(?<=src )[^ ]+')
ifName=$(ip route get 1 | grep -Po '(?<=dev )[^ ]+')

# Информация об ОС
. /etc/os-release
osLike="${ID_LIKE}"
[ "${ID}" = "debian" ] && osLike="debian"
echo ${ID_LIKE} | grep -q "rhel\|fedora" && osLike="rhel"
[ "${ID}" = "fedora" ] && osLike="rhel"
unaID=$(echo ${VERSION_ID} | sed -r 's/\..+//')

# Определение пакетного менеджера
DNF="/usr/bin/yum"
[ -f /usr/bin/dnf ] && DNF="/usr/bin/dnf"
[ -f /usr/bin/apt ] && { export DEBIAN_FRONTEND=noninteractive ; DNF="apt_Installer" ; }
[ -n "${DNF}" ] || exit 1

# Финальный текст
final_text() {
cat > /root/${RNAME}-script-final.txt <<- EOF
=========================
Работа скрипта ${RNAME} успешно завершена.
Конфигурационные файлы сервера находятся в каталоге ${DIR}
Конфигурационные файлы клиентов находятся в каталоге ${DIR}/client/
Сгенерировать QR-код клиента можно командой:
qrencode -t ansiutf8 < ${CLIENT_DIR}/client.conf
Добавить клиента можно командой:
/root/wg_client_add.sh <количество_клиентов>
где <количество_клиентов> может принимать значение от 1 до 253
=========================

=========================
The ${RNAME} script completed successfully.
The configuration files are located in the directory ${DIR}
Client configuration files are located in the directory ${DIR}/client/
You can generate a clients QR-code with the command
qrencode -t ansiutf8 < ${CLIENT_DIR}/client.conf
You can add a client with the command:
/root/wg_client_add.sh <number_of_clients>
where <number_of_clients> can take a value from 1 to 253
=========================
EOF
}

# Определяем инсталятор apt и его опции. Обходим фоновые задачи установки и одновления ОС
apt_Installer() {
while ps uxaww | grep -v grep | grep -Eq 'apt|dpkg|unattended' ; do echo "waiting..." ; sleep 5 ; done && \
/usr/bin/apt -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-confdef" -q 	--allow-downgrades --allow-remove-essential --allow-change-held-packages $@
}

# Проверяем установлен ли WireGuard
([ -d "${DIR}" -a -f "${DIR}/publickey" ] && modprobe wireguard 2>/dev/null) && wgInstalled=1

# Установка основных программ и подготовка базовых репозиториев
install_soft() {
if [ "${osLike}" = "debian" ]; then
	[ $(systemctl is-active unattended-upgrades.service 2>/dev/null) = "active" ] && { systemctl stop unattended-upgrades.service && unattServ="0" ; }
	${DNF} update && ${DNF} -y install lsb-release ca-certificates debian-archive-keyring
fi
if [ "${osLike}" = "rhel" -a "${ID}" != "ol" -a "${ID}" != "fedora" ]; then
	${DNF} -y install epel-release elrepo-release
	${DNF} -y upgrade kernel*
fi
if [ "${osLike}" = "rhel" -a "${ID}" = "ol" ]; then
	sed -i 's/oracle.com/hoztnode.net/' /etc/yum/vars/ocidomain
	${DNF} -y install oracle-epel-release-el*
	${DNF} -y install kernel-uek
fi
[ "${osLike}" = "rhel" ] && \
case ${unaID} in
"7")
	${DNF} -y install policycoreutils-python
	;;
*)
	${DNF} -y install policycoreutils-python-utils 
	${DNF} -y update libmodulemd
esac
${DNF} -y install tuned 2>/dev/null
systemctl enable --now tuned 2>/dev/null
}

# Преднастройка ОС
config_system() {
[ "${osLike}" = "debian" -a ${VERSION_ID} -eq 10 ] && \
echo $(hostname -I | cut -d\  -f1) $(hostname) | tee -a /etc/hosts
}

# Определение и настройка фаервола
config_firewall() {
if [ -f /usr/sbin/ufw ]; then
	ufw disable
	systemctl stop ufw.service
	systemctl disable ufw.service
fi
[ ! -f /usr/sbin/firewalld ] && ${DNF} -y install firewalld
if [ -f /usr/sbin/firewalld ]; then
	[ "${ID}" = "debian" -a ${VERSION_ID} -eq 10 ] && sed -i 's/IndividualCalls=no/IndividualCalls=yes/' /etc/firewalld/firewalld.conf
	systemctl restart firewalld.service
	systemctl enable firewalld.service
	firewalldZone="$(firewall-cmd --get-default-zone)"
	[ ! -f /usr/lib/firewalld/services/wireguard.xml ] && \
	cat << 'EOF' > /etc/firewalld/services/wireguard.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>Wireguard</short>
  <description>WireGuard protocol server</description>
  <port protocol="udp" port="51820"/>
</service>
EOF
	firewall-cmd --reload
	firewall-cmd --permanent --zone=${firewalldZone} --add-interface ${ifName}
	firewall-cmd --permanent --zone=${firewalldZone} --add-service=ssh
	firewall-cmd --permanent --zone=${firewalldZone} --remove-service=dhcpv6-client 2>&1 >/dev/null || return 0
	firewall-cmd --permanent --zone=${firewalldZone} --add-service=wireguard
	firewall-cmd --permanent --zone=${firewalldZone} --add-masquerade
	firewall-cmd --reload
fi
}

# Настройка транзита данных
config_sysctl() {
cat << EOF > /etc/sysctl.d/01-wireguard.conf
net.ipv4.ip_forward=1
net.ipv4.conf.all.forwarding=1
net.ipv6.conf.all.forwarding=1
EOF
#sysctl -p
sysctl --system
}

# Установка Wireguard
install_wireguard() {
[ "${osLike}" = "rhel" -a "${ID}" != "ol" -a "${ID}" != "fedora" ] && ${DNF} -y install kmod-wireguard
[ "${ID}" = "debian" -a ${VERSION_ID} -eq 10 ] && ${DNF} -y install wireguard-dkms
[ "${osLike}" = "debian" ] && ${DNF} -y install wireguard
${DNF} -y install wireguard-tools
modprobe wireguard 2>/dev/null || needReboot=1
${DNF} -y install qrencode
}

# Настройка сервера Wireguard
config_wireguard_server() {
umask 077
mkdir -p ${DIR}
local KEY=$(wg genkey)
local PUB_KEY=$(echo ${KEY} | wg pubkey)
echo ${KEY} > ${DIR}/privatekey
echo ${PUB_KEY} > ${DIR}/publickey
cat << EOF > ${DIR}/wg0.conf
[Interface]
Address = 192.168.15.1/24
SaveConfig = true
ListenPort = 51820
PrivateKey = ${KEY}
EOF
systemctl enable wg-quick@wg0.service
systemctl start wg-quick@wg0.service
}

# Добавление клиента
config_client() {
umask 077
[ ! -d "${DIR}/client" ] && mkdir -p ${DIR}/client
[ $(ls ${DIR}/client | wc -l) -ge 253 ] && { echo "Created the maximum number of clients" && exit 1 ; }
local CLIENT_KEY=$(wg genkey)
CLIENT_PUB_KEY=$(echo ${CLIENT_KEY} | wg pubkey)
CLIENT_DIR=$(mktemp -d ${DIR}/client/client_XXX)
echo ${CLIENT_KEY} > ${CLIENT_DIR}/privatekey
echo ${CLIENT_PUB_KEY} > ${CLIENT_DIR}/publickey
local CLIENT_COUNT=$(ls ${DIR}/client | wc -l)
local NEW_CLIENT=$(expr ${CLIENT_COUNT} + 1)
cat << EOF > ${CLIENT_DIR}/client.conf
[Interface]
PrivateKey = ${CLIENT_KEY}
Address = 192.168.15.${NEW_CLIENT}/24
DNS = 1.1.1.1, 8.8.8.8

[Peer]
PublicKey = $(cat ${DIR}/publickey)
AllowedIPs = 0.0.0.0/0
Endpoint = ${ipv4Addr}:51820
EOF
wg set wg0 peer "${CLIENT_PUB_KEY}" allowed-ips "192.168.15.${NEW_CLIENT}"
}

# Создание скрипта для добавления клиентов
wg_client_add() {
printf "#!/bin/sh\nDIR=${DIR}\nipv4Addr=${ipv4Addr}\n" > ${WG_SCRIPT}
type config_client
type config_client | sed '1d' >> ${WG_SCRIPT}
cat <<\WGCLIENT >>${WG_SCRIPT}
modprobe wireguard 2>/dev/null || { echo "Can not load wireguard module" && exit 1 ; }
NOW_CLIENTS=$(ls ${DIR}/client | wc -l)
ADD_CLIENTS="$1"
[ -z "${ADD_CLIENTS}" ] && ADD_CLIENTS="1"
[ -n "${ADD_CLIENTS}" -a "${ADD_CLIENTS}" -gt 0 -a "${ADD_CLIENTS}" -le 253 ] 2>/dev/null || { echo "The number of clients must be an integer and have a value from 1 to 253" && exit 1 ; }
[ $((${NOW_CLIENTS} + ${ADD_CLIENTS})) -ge 254 ] && { echo "Many users. You can create $((253 - ${NOW_CLIENTS}))" && exit 1 ; }
qz=0
while [ $qz -ne ${ADD_CLIENTS} ]; do
	config_client
	qrencode -t ansiutf8 < ${CLIENT_DIR}/client.conf
	qz=$(($qz + 1))
done
WGCLIENT
chmod +x /root/wg_client_add.sh
}

# Перезапуск сервера для запуска модуля wireguard со свежим ядром
need_reboot() {
if [ -n "${needReboot}" ]; then
	local START=/root/startup.sh
cat << EOF > ${START}
#!/bin/sh
wg set wg0 peer "${CLIENT_PUB_KEY}" allowed-ips 192.168.15.2
systemctl disable run-at-startup.service
rm -rf ${START}
rm -rf /etc/systemd/system/run-at-startup.service
systemctl daemon-reload
EOF
chmod +x ${START}
cat << EOF > /etc/systemd/system/run-at-startup.service
[Unit]
Description=Run script at startup after all systemd services are loaded
After=default.target

[Service]
Type=simple
RemainAfterExit=yes
ExecStart=${START}
TimeoutStartSec=0

[Install]
WantedBy=default.target
EOF
	systemctl daemon-reload
	systemctl enable run-at-startup.service
	shutdown -r
fi
}

if [ -n "${wgInstalled}" ]; then
	[ ! -f "${WG_SCRIPT}" ] && wg_client_add
	config_client
	final_text
	exit 0
fi
install_soft
config_system
config_sysctl
install_wireguard
config_firewall

# Включаем обратно сервис самообновления Ubuntu
[ -n "${unattServ}" ] && systemctl start unattended-upgrades.service

[ ! -f ${DIR}/publickey ] && config_wireguard_server
wg_client_add
config_client
final_text
need_reboot

WG

/bin/bash /tmp/wg.sh ; rm -rf /tmp/wg.sh