Skip to content

Instantly share code, notes, and snippets.

@ig-rudenko
Last active December 17, 2024 05:45
Show Gist options
  • Save ig-rudenko/27104297935d1745884f6f0063f12601 to your computer and use it in GitHub Desktop.
Save ig-rudenko/27104297935d1745884f6f0063f12601 to your computer and use it in GitHub Desktop.
Установка и управление StrongSwan VPN + FreeRaduis
#!/bin/bash
RED='\033[0;31m'
ORANGE='\033[0;33m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m'
SERVER=""
SERVER_INPUT_TYPE=""
CLIENTS_POOL="10.10.10.0/24"
function isRoot() {
if [ "${EUID}" -ne 0 ]; then
echo -e "${RED}❌ Вы должны быть root!${NC}";
exit 1
fi
}
function checkStrongSwanAlreadyInstalled() {
if [ -e /etc/strongswan.conf ]; then
echo -e "${GREEN}✅ StrongSwan уже установлен!${NC}";
exit 1;
fi
}
function inputServerDomainOrIp() {
read -rp ">> Введите домен или IP-адрес сервера: " SERVER;
checkDomainOrIp "$SERVER";
}
# Функция для проверки IP-адреса или домена
function checkDomainOrIp() {
local input="$1";
if [[ $input =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
# Проверка формата IPv4
SERVER_INPUT_TYPE="IP";
elif [[ $input =~ ^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
# Проверка формата домена
SERVER_INPUT_TYPE="DOMAIN";
else
echo -e "${RED}❌ Неверный формат IP-адреса или домена${NC}";
exit 1;
fi
}
function inputClientsPool() {
local user_input="";
read -rp ">> Введите подсеть клиентов, либо по умолчанию будет 10.10.10.0/24: " user_input;
checkClientsPool "$user_input";
}
# Функция для проверки строчки подсети.
function checkClientsPool() {
local input="$1";
if [[ $input = "" ]]; then
CLIENTS_POOL="10.10.10.0/24";
elif [[ $input =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\/[0-9]{1,2}$ ]]; then
# Проверка формата IPv4
CLIENTS_POOL="$1";
else
echo -e "${RED}❌ Неверный формат подсети!${NC}";
exit 1;
fi;
}
function installStrongSwan() {
echo -e "${BLUE} Установка StrongSwan...${NC}";
sudo apt update 1>/dev/null 2>&1;
sudo apt install -y \
strongswan \
strongswan-pki \
libcharon-extra-plugins \
libcharon-extauth-plugins \
libstrongswan-extra-plugins \
libtss2-tcti-tabrmd0 1>/dev/null 2>&1;
echo -e "${GREEN}✅ StrongSwan установлен${NC}";
}
function createCaCerts() {
mkdir -p ~/pki/{cacerts,certs,private};
chmod 700 ~/pki;
pki --gen --type rsa --size 4096 --outform pem > ~/pki/private/ca-key.pem;
echo -e "${GREEN}✅ Корневой ключ создан!${NC}";
pki --self --ca --lifetime 3650 \
--in ~/pki/private/ca-key.pem \
--type rsa --dn "CN=VPN root CA" \
--outform pem > ~/pki/cacerts/ca-cert.pem;
echo -e "${GREEN}✅ Корневой сертификат центра сертификации создан!${NC}";
}
function createVpnCerts() {
mkdir -p ~/pki/{cacerts,certs,private};
chmod 700 ~/pki;
pki --gen --type rsa --size 4096 \
--outform pem > ~/pki/private/server-key.pem;
echo -e "${GREEN}✅ Закрытый ключ для VPN-сервера создан!${NC}";
if [[ "$SERVER_INPUT_TYPE" == "DOMAIN" ]]; then
pki --pub --in ~/pki/private/server-key.pem --type rsa \
| pki --issue --lifetime 1825 \
--cacert ~/pki/cacerts/ca-cert.pem \
--cakey ~/pki/private/ca-key.pem \
--dn "CN=$SERVER" \
--san "$SERVER" \
--flag serverAuth \
--flag ikeIntermediate \
--outform pem > ~/pki/certs/server-cert.pem;
elif [[ "$SERVER_INPUT_TYPE" == "IP" ]]; then
pki --pub --in ~/pki/private/server-key.pem --type rsa \
| pki --issue --lifetime 1825 \
--cacert ~/pki/cacerts/ca-cert.pem \
--cakey ~/pki/private/ca-key.pem \
--dn "CN=$SERVER" \
--san "@$SERVER" \
--san "$SERVER" \
--flag serverAuth \
--flag ikeIntermediate \
--outform pem > ~/pki/certs/server-cert.pem;
fi
echo -e "${GREEN}✅ Сертификат для VPN-сервера создан!${NC}";
}
function copyCertsToIpsecFolder() {
cp -r ~/pki/* /etc/ipsec.d/;
echo -e "${GREEN}✅ Сертификаты скопированы в /etc/ipsec.d/${NC}";
}
function createIpsecProfile() {
cp /etc/ipsec.conf "/etc/ipsec.conf.backup_$(date +%Y.%m.%d-%H:%M)";
local SYMBOL=""
if [[ $SERVER_INPUT_TYPE == "DOMAIN" ]]; then
SYMBOL="@";
fi;
echo "
config setup
charondebug=\"ike 1, knl 1, cfg 0\"
uniqueids=no
conn ikev2-vpn
auto=add
compress=no
type=tunnel
keyexchange=ikev2
fragmentation=yes
forceencaps=yes
dpdaction=clear
dpddelay=300s
rekey=no
left=%any
leftid=$SYMBOL$SERVER
leftcert=server-cert.pem
leftsendcert=always
leftsubnet=0.0.0.0/0
right=%any
rightid=%any
rightauth=eap-radius
rightsourceip=%radius
rightdns=8.8.8.8,8.8.4.4
rightsendcert=never
eap_identity=%identity
ike=aes256gcm16-aes256gcm12-aes128gcm16-aes128gcm12-sha256-sha1-modp2048-modp4096-modp1024,aes256-aes128-sha256-sha1-modp2048-modp4096-modp1024,3des-sha1-modp1024!
esp=aes128gcm12-aes128gcm16-aes256gcm12-aes256gcm16-modp2048-modp4096-modp1024,aes128-aes256-sha1-sha256-modp2048-modp4096-modp1024,aes128-sha1-modp2048,aes128-sha1-modp1024,3des-sha1-modp1024,aes128-aes256-sha1-sha256,aes128-sha1,3des-sha1!
" > /etc/ipsec.conf;
echo ': RSA "server-key.pem"' > /etc/ipsec.secrets
echo -e "${GREEN}✅ Конфигурация IPSec создана!${NC}";
}
function configUfw() {
ufw allow OpenSSH 1>/dev/null;
yes | ufw enable 1>/dev/null;
echo -e "${GREEN}✅ UFW включен${NC}";
ufw allow 500,4500/udp 1>/dev/null;
# Определяем имя сетевого интерфейса
local interface_name=$(ip route | grep default | awk '{print $5}');
if [[ -z "$interface_name" ]]; then
echo -e "${RED}❌ Не удалось определить имя сетевого интерфейса.${NC}";
return 1;
fi
# Создаём резервную копию оригинального файла
local ufw_file="/etc/ufw/before.rules";
if [[ ! -f "$ufw_file" ]]; then
echo -e "${RED}❌ Файл $ufw_file не найден.${NC}";
return 1;
fi
cp "$ufw_file" "$ufw_file.bak" || {
echo -e "${RED}❌ Не удалось создать резервную копию файла.${NC}";
return 1;
}
# Добавляем блок конфигурации *nat и *mangle
sed -i "/^# Don't delete these required lines/i \
# \n\
# Маршрутизация трафика между VPN-клиентами и Интернетом.\n\
*nat\n\
-A POSTROUTING -s $CLIENTS_POOL -o $interface_name -m policy --pol ipsec --dir out -j ACCEPT\n\
-A POSTROUTING -s $CLIENTS_POOL -o $interface_name -j MASQUERADE\n\
COMMIT\n\
# \n\
# \n\
*mangle\n\
# Регулировка максимального размера сегмента пакета, чтобы предотвратить потенциальные проблемы с определенными VPN-клиентами\n\
-A FORWARD --match policy --pol ipsec --dir in -s $CLIENTS_POOL -o $interface_name -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360\n\
COMMIT\n" "$ufw_file";
# Добавляем блок конфигурации для ESP после строк с определением цепочек
sed -i "/End required lines/a \
# \n\
# Пересылать трафик ESP (инкапсулированной полезной нагрузки) чтобы клиенты VPN могли подключиться.\n\
-A ufw-before-forward --match policy --pol ipsec --dir in --proto esp -s $CLIENTS_POOL -j ACCEPT\n\
-A ufw-before-forward --match policy --pol ipsec --dir out --proto esp -d $CLIENTS_POOL -j ACCEPT\n" /etc/ufw/before.rules;
echo -e "${GREEN}✅ Конфигурация успешно добавлена в $ufw_file.${NC}";
echo "
# Enable FORWARD
net/ipv4/ip_forward=1
# Block sending and receiving ICMP redirect packets
net/ipv4/conf/all/accept_redirects=0
net/ipv4/conf/all/send_redirects=0
# Turn off Path MTU discovery
net/ipv4/ip_no_pmtu_disc=1
" >> /etc/ufw/sysctl.conf;
echo -e "${GREEN}✅ Forwarding включен${NC}";
ufw reload 1>/dev/null;
echo -e "${GREEN}✅ UFW перезагружен${NC}";
}
function configurateFreeRadius() {
apt install -y freeradius 1>/dev/null 2>&1;
echo "
charon {
load_modular = yes
plugins {
eap-radius {
accounting = yes
eap_start = no
servers {
radius {
address = 127.0.0.1
secret = testing123
auth_port = 1812 # default
acct_port = 1813 # default
}
}
}
}
}" > /etc/strongswan.d/eap-radius.conf;
echo -e "${GREEN}✅ Модуль Radius успешно добавлен в StrongSwan.${NC}";
generateNewFreeRadiusSecret;
}
function generateNewFreeRadiusSecret() {
local new_secret=$(dd if=/dev/random bs=1 count=24 2>/dev/null | base64);
local file="/etc/freeradius/3.0/clients.conf" # Путь к вашему файлу
# Обновляем secret только в блоке localhost
awk -v new_secret="$new_secret" '
BEGIN { in_block = 0; }
/^client localhost {/ { in_block = 1; }
/^}/ { if (in_block) in_block = 0; }
in_block && /^\s*secret\s*=/ {
sub(/secret\s*=\s*.*/, "secret = " new_secret);
}
{ print; }
' "$file" > "${file}.tmp" && mv "${file}.tmp" "$file"
# Обновим secret в файле для StrongSwan.
local strongswan_file="/etc/strongswan.d/eap-radius.conf";
sed -i "s/secret = .*/secret = $new_secret/" "$strongswan_file";
echo -e "${GREEN}✅ Пароль FreeRadius для localhost успешно обновлён${NC}"
}
# Функция для отображения списка пользователей
function listUsers() {
local file="/etc/freeradius/3.0/mods-config/files/authorize";
# Проверяем, существует ли файл
if [[ ! -f $file ]]; then
echo -e "${RED}❌ Ошибка: Файл $file не найден.${NC}";
exit 1;
fi
echo -e " ${CYAN}Список пользователей${NC}"
echo "┌─────────────────┬───────────────────┐"
echo "│ IP-адрес │ Имя пользователя │"
echo "└─────────────────┼───────────────────┘"
# Извлекаем имена пользователей и IP-адреса
# Извлекаем и форматируем данные
# awk '/\S+-Password/ {user=$1} /Framed-IP-Address/ {ip=$NF; gsub(",", "", ip); printf " %-15s │ %s\n", ip, user}' "$file";
awk '
/\S+-Password/ {user=$1}
/Framed-IP-Address/ {
ip=$NF; gsub(",", "", ip)
if (user ~ /^#/) {
user=substr(user, 2) "\033[0;31m commented \033[0m"
}
printf " %-15s │ %s\n", ip, user
}' "$file"
echo "──────────────────┴────────────────────";
}
# Функция для добавления пользователя в файл users
function addVPNUser() {
echo -e "${CYAN}Создание нового пользователя${NC}";
echo "";
local file="/etc/freeradius/3.0/mods-config/files/authorize";
# Проверяем, существует ли файл
if [[ ! -f $file ]]; then
echo -e "${RED}❌ Ошибка: Файл $file не найден.${NC}";
exit 1;
fi
# Запрашиваем данные у пользователя
read -rp "Введите имя пользователя: " username;
read -ersp "Введите пароль: " password1;
read -ersp "Повторите пароль: " password2;
read -rp "Введите IP-адрес пользователя: " ip_address;
if [[ "$password1" != "$password2" ]]; then
echo -e "${RED}❌ Ошибка: Пароли не совпадают.${NC}"
exit 1;
fi;
# Проверяем, что имя пользователя, пароль и IP-адрес не пустые
if [[ -z "$username" || -z "$password1" || -z "$ip_address" ]]; then
echo -e "${RED}❌ Ошибка: Имя пользователя, пароль и IP-адрес не могут быть пустыми.${NC}";
exit 1;
fi
# Формируем запись
entry=$(cat <<EOF
$username Cleartext-Password := "$password1"
MS-CHAP-Use-NTLM-Auth := 0,
Service-Type = Framed-User,
Framed-Protocol = PPP,
Framed-IP-Address = $ip_address,
Framed-IP-Netmask = 255.255.255.255,
Framed-Pool = "office",
Filter-Id = "100000/100000",
Reply-Message = "Accepted from local file"
# End $username
EOF
)
# Добавляем запись в начало файла
echo "$entry" | cat - "$file" > temp && mv temp "$file";
echo -e "${GREEN}✅ Пользователь $username успешно добавлен в файл $file.${NC}";
}
# Функция для обновления пароля пользователя в файле users
function updateUserPassword() {
echo -e "${CYAN}Обновление пароля пользователя${NC}";
echo "";
local file="/etc/freeradius/3.0/mods-config/files/authorize";
# Проверяем, существует ли файл
if [[ ! -f $file ]]; then
echo -e "${RED}❌ Ошибка: Файл $file не найден.${NC}";
exit 1;
fi
# Запрашиваем данные у пользователя
read -rp "Введите имя пользователя: " username;
read -ersp "Введите новый пароль: " password1;
read -ersp "Повторите новый пароль: " password2;
if [[ "$password1" != "$password2" ]]; then
echo -e "${RED}❌ Ошибка: Пароли не совпадают.${NC}"
exit 1;
fi;
# Проверяем, что имя пользователя, пароль и IP-адрес не пустые
if [[ -z "$username" || -z "$password1" ]]; then
echo -e "${RED}❌ Ошибка: Имя пользователя и пароль не могут быть пустыми.${NC}";
exit 1;
fi
# Проверяем, существует ли пользователь в файле
if ! grep -q "^$username Cleartext-Password" "$file"; then
echo -e "${RED}❌ Ошибка: Пользователь $username не найден в файле $file.${NC}";
exit 1;
fi
# Обновляем пароль пользователя
sed -i "/^$username Cleartext-Password/s/\".*\"/\"$password1\"/" "$file";
echo -e "${GREEN}✅ Пароль для пользователя $username успешно обновлён.${NC}";
}
# Функция для удаления данных пользователя из файла users
function deleteUserBlock() {
echo -e "${RED} ВНИМАНИЕ!${NC}";
echo -e "${CYAN}Удаление пользователя!${NC}";
echo "";
local file="/etc/freeradius/3.0/mods-config/files/authorize";
# Проверяем, существует ли файл
if [[ ! -f $file ]]; then
echo -e "${RED}❌ Ошибка: Файл $file не найден.${NC}";
exit 1
fi
# Запрашиваем имя пользователя
read -rp "Введите имя пользователя для удаления: " username;
# Проверяем, что имя пользователя не пустое
if [[ -z "$username" ]]; then
echo -e "${RED}❌ Ошибка: Имя пользователя не может быть пустым.${NC}";
exit 1;
fi
# Проверяем, существует ли пользователь в файле
if ! grep -q "^$username Cleartext-Password" "$file"; then
echo -e "${RED}❌ Ошибка: Пользователь $username не найден в файле $file.${NC}";
exit 1;
fi
# Удаляем блок данных пользователя
sed -i "/^$username Cleartext-Password/,/# End $username/d" "$file";
echo -e "${GREEN}✅ Пользователь $username успешно удален.${NC}";
}
function reloadStrongSwan() {
systemctl reload strongswan-starter;
echo -e "${GREEN}✅ StrongSwan перезагружен${NC}"
}
function reloadFreeRadius() {
systemctl reload freeradius;
echo -e "${GREEN}✅ FreeRadius перезагружен${NC}"
}
function restartFreeRadius() {
systemctl reload freeradius;
echo -e "${GREEN}✅ FreeRadius перезапущен${NC}"
}
function manageMenu() {
echo "Добро пожаловать в скрипт управления StrongSwan VPN!"
echo ""
echo "Что вы хотите сделать?"
echo " 1) Установить StrongSwan VPN + FreeRadius"
echo " 2) Пересоздать VPN сертификат"
echo ""
echo " 3) Посмотреть пользователей"
echo " 4) Добавить пользователя VPN"
echo " 5) Обновить пароль пользователя VPN"
echo " 6) Удалить пользователя VPN"
echo ""
echo " 7) Обновить пароль FreeRadius"
echo " 8) Перезапустить все сервисы"
echo " 0) Exit"
until [[ ${MENU_OPTION} =~ ^[0-8]$ ]]; do
read -rp "Select an option [0-8]: " MENU_OPTION
done
case "${MENU_OPTION}" in
1) # Установить StrongSwan VPN + FreeRadius
checkStrongSwanAlreadyInstalled;
inputServerDomainOrIp;
inputClientsPool;
installStrongSwan;
createCaCerts;
createVpnCerts;
copyCertsToIpsecFolder;
createIpsecProfile;
configUfw;
configurateFreeRadius;
reloadFreeRadius;
restartFreeRadius;
echo -e "\n${CYAN}Для подключения вы должны скопировать CA сертификат на ваше устройство:${NC}\n";
cat /etc/ipsec.d/cacerts/ca-cert.pem;
;;
2) # Пересоздать VPN сертификат
inputServerDomainOrIp;
createVpnCerts
copyCertsToIpsecFolder;
reloadStrongSwan;
;;
3) # Посмотреть пользователей
listUsers;
;;
4) # Добавить пользователя VPN
addVPNUser;
reloadFreeRadius;
;;
5) # Обновить пароль пользователя VPN
updateUserPassword;
reloadFreeRadius;
;;
6) # Удалить пользователя VPN
deleteUserBlock;
reloadFreeRadius;
;;
7) # Обновить пароль FreeRadius
generateNewFreeRadiusSecret;
restartFreeRadius;
reloadStrongSwan;
;;
8) # Перезапустить все
systemctl restart freeradius ipsec strongswan-starter;
echo -e "${GREEN}✅ StrongSwan, FreeRadius и IPsec перезапущены${NC}"
;;
0) # Exit
exit 0
;;
esac
}
isRoot;
manageMenu;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment