Last active
December 17, 2024 05:45
-
-
Save ig-rudenko/27104297935d1745884f6f0063f12601 to your computer and use it in GitHub Desktop.
Установка и управление StrongSwan VPN + FreeRaduis
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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