commit 641c4cb9dd009f070d743cd37ca395d61943e7f2 Author: Антон Date: Wed Jan 14 22:27:53 2026 +0300 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..5f7ee64 --- /dev/null +++ b/README.md @@ -0,0 +1,113 @@ +# OpenVPN Profiler - Обновленная версия + +## Основные улучшения + +### 1. Поддержка массивов для маршрутов и DNS +- Теперь можно задавать любое количество маршрутов для SPLIT туннелирования +- Поддержка любого количества DNS серверов +- Обратная совместимость со старым форматом + +### 2. Улучшенная безопасность +- Безопасная обработка шаблонов (экранирование спецсимволов) +- Строгий режим выполнения скрипта +- Проверка зависимостей + +### 3. Логирование +- Все действия логируются в файл `profiler.log` +- Подробные сообщения об ошибках + +### 4. Гибкость конфигурации +- Поддержка CIDR формата для маршрутов (192.168.0.0/24) +- Возможность загрузки маршрутов из файла +- Автоматическое определение IP адресов с fallback + +## Установка обновлений + +1. **Создайте резервную копию текущей конфигурации:** + + ```bash + cp -r templates/ templates.backup/ + cp profiler.sh profiler.sh.backup + cp confvars confvars.backup + ``` + +2. **Используйте скрипт миграции (если нужно сохранить старые настройки):** + + ```bash + bash + chmod +x migrate_config.sh + ./migrate_config.sh + ``` + +3. **Или скопируйте новые файлы вручную:** + + ```bash + cp templates/server.txt templates/server.txt.new + cp templates/user.txt templates/user.txt.new + cp profiler.sh profiler.sh.new + cp confvars confvars.new + + # Просмотрите изменения и примените их + diff templates/server.txt templates/server.txt.new + diff profiler.sh profiler.sh.new + ``` + +## Использование новых возможностей + +1. **Конфигурация SPLIT туннелирования с массивами:** + + ```bash + # В файле confvars + TTUNNETS=( + "192.168.5.0/24" + "10.0.0.0/8" + "172.16.0.0/12" + ) + + # Или используйте файл со списком маршрутов + TTUNNETS_FILE="split_routes.txt" + ``` + +2. **Конфигурация DNS с массивами:** + + ```bash + TDNS=( + "192.168.5.253" + "8.8.8.8" + "1.1.1.1" + ) + ``` + +## Новые функции скрипта + +1. Безопасная подстановка в шаблоны - защита от спецсимволов +2. Расширенное логирование - все действия записываются в лог +3. Проверка зависимостей - автоматическая проверка необходимых программ +4. Обработка ошибок - информативные сообщения об ошибках +5. Поддержка CIDR - автоматическое преобразование CIDR в маску + +## Обратная совместимость + +Скрипт поддерживает старый формат конфигурации. Если вы используете старые переменные `TTUNNET1-5` и `TDNS1-3`, они будут автоматически преобразованы в массивы. + +# Команды + +```bash +./profiler.sh build # Создать новый профиль +./profiler.sh list # Показать список профилей +./profiler.sh revoke # Отозвать профиль +./profiler.sh init # Инициализировать PKI +./profiler.sh clear # Очистить PKI +./profiler.sh srvconf # Создать конфигурацию сервера +./profiler.sh help # Показать справку +``` + +# Файлы + +- `profiler.sh` - основной скрипт +- `confvars` - конфигурация +- `templates/server.txt` - шаблон сервера +- `templates/user.txt` - шаблон пользователя +- `split_routes.txt` - пример файла с маршрутами +- `profiler.log` - файл лога (создается автоматически) + diff --git a/confvars b/confvars new file mode 100644 index 0000000..a811f0a --- /dev/null +++ b/confvars @@ -0,0 +1,89 @@ +### Конфигурация клиента и сервера + +# Указать протокол: ( tcp | udp ) +TPROTO=tcp + +# Указать публичный порт VPN сервера +TPORT=443 + +### Конфигурация сервера + +# Указать сеть VPN клиентов +TSERNET="172.20.1.0" + +# Указать маску сети VPN клиентов +TSERMASK="255.255.255.0" + +# Указать тип тоннеля ( FULL | SPLIT ) +TTUNTYPE=SPLIT + +# Список сетей для SPLIT туннелирования (один CIDR на строку в кавычках) +# Формат: "сеть/маска" или "сеть маска" +TTUNNETS=( + "192.168.5.0/24" + "10.0.0.0/24" + # "172.16.0.0/12" # пример закомментированной записи +) + +# Альтернативно: можно использовать файл со списком маршрутов +# TTUNNETS_FILE="split_routes.txt" + +# Список DNS серверов +TDNS=( + "172.20.1.1" + # "8.8.8.8" # пример стандартного значения + # "1.1.1.1" # пример стандартного значения +) + +# Разрешить трафик между VPN клиентами ( YES | NO ) +TC2C=NO + +# Разрешить множественные подключения по одному сертификату ( YES | NO ) +TDCN=YES + +# Отслеживать список отозванных сертификатов ( YES | NO ) +TREVO=NO + +# Использовать скрипты обработки событий: подключение и отключение пользователя ( YES | NO ) +T_CONNSCRIPTS=NO + +# Путь до исполняемого файла скрипта с учетом вызывающего бинарника +# Пример: "/opt/ovpn/venv/bin/python /etc/opеnvpn/user_conn.py" + +T_CONNSCRIPT_STRING="/opt/ovpn/venv/bin/python /etc/opеnvpn/user_conn.py" +T_DISCONNSCRIPT_STRING="/opt/ovpn/venv/bin/python /etc/opеnvpn/user_disconn.py" + +# Активировать интерфейс управления ( YES | NO ) +T_MGMT=NO + +# Назначить адрес для интерфейса управления +T_MGMT_ADDR="127.0.0.1" + +# Назначить порт для интерфейса управления +T_MGMT_PORT=7505 + +### Конфигурация PKI + +# Указать FQDN для SERVER сертификата +FQDN_SERVER=ovpn-srv + +# Указать FQDN для SERVER сертификата +FQDN_CA=ovpn-ca + +# Статический публичный IP (если не задан, определяется автоматически) +# EXTIP_CONFIG="62.176.10.113" + +# Секция настройки окружения EasyRSA +export EASYRSA_DN="cn_only" +export EASYRSA_REQ_COUNTRY="RU" +export EASYRSA_REQ_PROVINCE="Moscow" +export EASYRSA_REQ_CITY="Moscow" +export EASYRSA_REQ_ORG="SomeORG" +export EASYRSA_REQ_EMAIL="info@someorg.local" +export EASYRSA_REQ_OU="IT" +export EASYRSA_KEY_SIZE=2048 +export EASYRSA_CA_EXPIRE=3650 +export EASYRSA_CERT_EXPIRE=3649 +export EASYRSA_CERT_RENEW=30 +export EASYRSA_CRL_DAYS=3649 +export EASYRSA_BATCH=1 diff --git a/profiler.sh b/profiler.sh new file mode 100644 index 0000000..00131ba --- /dev/null +++ b/profiler.sh @@ -0,0 +1,638 @@ +#!/bin/bash +set -euo pipefail +IFS=$'\n\t' + +# Базовые переменные +BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LOG_FILE="${BASE_DIR}/profiler.log" +STAGE="${BASE_DIR}/staging" +CLIENTCONF="${BASE_DIR}/client-config" +EASYRSADIR="${BASE_DIR}/easy-rsa" +PKIDIR="${EASYRSADIR}/pki" +export EASYRSA_PKI="$PKIDIR" + +# Функция логирования +log() { + local level="$1" + local message="$2" + local timestamp=$(date '+%Y-%m-%d %H:%M:%S') + echo "${timestamp} - ${level}: ${message}" | tee -a "$LOG_FILE" >&2 +} + +print() { + printf "%s\n" "$*" || exit 1 +} + +usage() { + print " +USAGE: ./profiler COMMAND [option] + +Список доступных КОМАНД и [опций]: + + build # создать новый профиль + list # отобразить список созданных профилей + revoke # отозвать созданный профиль + init # выполнить инициализацию окружения PKI + clear # выполнить очистку окружения PKI + srvconf # создать файл конфигурации сервера +" +} + +# Функция для безопасной подстановки в шаблоны +safe_sed() { + local pattern="$1" + local replacement="$2" + local file="$3" + + # Экранируем специальные символы для sed + replacement=$(printf '%s\n' "$replacement" | sed -e 's/[\/&]/\\&/g') + sed -i "s/$pattern/$replacement/g" "$file" +} + +# Функция преобразования CIDR в маску +cidr_to_netmask() { + local cidr=$1 + local mask="" + + for i in {1..4}; do + if [ "$cidr" -ge 8 ]; then + mask+="255" + cidr=$((cidr - 8)) + else + local mask_byte=$((256 - 2**(8 - cidr))) + mask+="$mask_byte" + cidr=0 + fi + [ $i -lt 4 ] && mask+="." + done + + echo "$mask" +} + +# Функция генерации строк push "route" для SPLIT туннелирования +generate_split_routes() { + local routes=("$@") + + for route in "${routes[@]}"; do + [[ -z "$route" || "$route" =~ ^[[:space:]]*# ]] && continue + + if [[ "$route" == *"/"* ]]; then + local ip=$(echo "$route" | cut -d'/' -f1) + local cidr_mask=$(echo "$route" | cut -d'/' -f2) + local mask=$(cidr_to_netmask "$cidr_mask") + echo "push \"route $ip $mask\"" + else + echo "push \"route $route\"" + fi + done +} + +# Функция генерации DNS опций +generate_dns_options() { + local dns_servers=("$@") + local output="" + + for dns in "${dns_servers[@]}"; do + # Пропускаем пустые строки и комментарии + [[ -z "$dns" || "$dns" =~ ^[[:space:]]*# ]] && continue + output+="push \"dhcp-option DNS $dns\"\n" + done + + echo -e "$output" +} + +# Функция получения сетевой информации +get_network_info() { + local ifid ifaddr extip + + # Получаем имя сетевого интерфейса + ifid=$(ip -f inet route 2>/dev/null | grep default | head -1 | awk '{print $5}' || echo "") + if [ -z "$ifid" ]; then + log "WARN" "Не удалось определить сетевой интерфейс" + return 1 + fi + + # Получаем адрес интерфейса + ifaddr=$(ip -f inet addr show "$ifid" 2>/dev/null | grep -oP 'inet \K[\d.]+' | head -1 || echo "") + + # Получаем публичный IP адрес (пробуем несколько сервисов) + local services=("curlmyip.ru" "ifconfig.me" "icanhazip.com" "api.ipify.org") + for service in "${services[@]}"; do + extip=$(curl -s --connect-timeout 3 "$service" 2>/dev/null | grep -oE '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' | head -1) + [ -n "$extip" ] && break + done + + # Используем статический IP из конфигурации, если задан + if [ -n "${EXTIP_CONFIG:-}" ]; then + extip="$EXTIP_CONFIG" + log "INFO" "Используется статический IP из конфигурации: $extip" + fi + + echo "$ifid,$ifaddr,$extip" +} + +# Функция загрузки конфигурации +load_config() { + local config_file="${BASE_DIR}/confvars" + + if [ ! -f "$config_file" ]; then + log "ERROR" "Файл конфигурации не найден: $config_file" + return 1 + fi + + # Загружаем конфигурацию + source "$config_file" + + # Проверяем обязательные переменные + local required_vars=("TPROTO" "TPORT" "TSERNET" "TSERMASK" "TTUNTYPE" "FQDN_SERVER") + for var in "${required_vars[@]}"; do + if [ -z "${!var:-}" ]; then + log "ERROR" "Не задана обязательная переменная: $var" + return 1 + fi + done + + # Обратная совместимость: преобразование старых форматов в массивы + if ! declare -p TTUNNETS &>/dev/null 2>&1; then + TTUNNETS=() + [ -n "${TTUNNET1:-}" ] && TTUNNETS+=("$TTUNNET1") + [ -n "${TTUNNET2:-}" ] && TTUNNETS+=("$TTUNNET2") + [ -n "${TTUNNET3:-}" ] && TTUNNETS+=("$TTUNNET3") + [ -n "${TTUNNET4:-}" ] && TTUNNETS+=("$TTUNNET4") + [ -n "${TTUNNET5:-}" ] && TTUNNETS+=("$TTUNNET5") + log "INFO" "Используется старый формат TTUNNET, преобразован в массив" + fi + + if ! declare -p TDNS &>/dev/null 2>&1; then + TDNS=() + [ -n "${TDNS1:-}" ] && TDNS+=("$TDNS1") + [ -n "${TDNS2:-}" ] && TDNS+=("$TDNS2") + [ -n "${TDNS3:-}" ] && TDNS+=("$TDNS3") + log "INFO" "Используется старый формат TDNS, преобразован в массив" + fi + + # Загрузка маршрутов из файла, если указано + if [ -n "${TTUNNETS_FILE:-}" ] && [ -f "${TTUNNETS_FILE}" ]; then + log "INFO" "Загружаем маршруты из файла: $TTUNNETS_FILE" + mapfile -t TTUNNETS < <(grep -v '^#' "${TTUNNETS_FILE}" | grep -v '^$') + fi + + log "INFO" "Конфигурация загружена успешно" + return 0 +} + +# Проверка зависимостей +check_dependencies() { + local deps=("openvpn" "curl" "ip") + local missing_deps=() + + for dep in "${deps[@]}"; do + if ! command -v "$dep" &>/dev/null; then + missing_deps+=("$dep") + fi + done + + if [ ${#missing_deps[@]} -gt 0 ]; then + log "ERROR" "Отсутствуют зависимости: ${missing_deps[*]}" + return 1 + fi + + return 0 +} + +build() { + local uservar="$1" + + log "INFO" "Начало сборки профиля" + + # Загрузка конфигурации + load_config || exit 1 + + # Проверка зависимостей + check_dependencies || exit 1 + + # Получение сетевой информации + local network_info + network_info=$(get_network_info) + IFS=',' read -r IFID IFADDR EXTIP <<< "$network_info" + + log "INFO" "Сетевой интерфейс: $IFID, Адрес: $IFADDR, Публичный IP: ${EXTIP:-не определен}" + + # Проверка наличия шаблона пользователя + local utemp="${BASE_DIR}/templates/user.txt" + if [ ! -f "$utemp" ]; then + log "ERROR" "Шаблон пользователя не найден: $utemp" + exit 1 + fi + + # Проверка инициализации PKI + if [ ! -f "$PKIDIR/index.txt" ]; then + log "ERROR" "PKI окружение не инициализировано. Выполните './profiler init'" + exit 1 + fi + + # Создание директорий + mkdir -p "$STAGE" "$CLIENTCONF" + + # Подготовка шаблона пользователя + log "INFO" "Подготовка шаблона пользователя" + cp "$utemp" "$STAGE/user.conf" + + # Обработка протокола + if [ "$TPROTO" == "tcp" ]; then + safe_sed "TCL" "tls-client" "$STAGE/user.conf" + log "INFO" "Используется TCP протокол" + else + safe_sed "TCL" "#tls-client" "$STAGE/user.conf" + log "INFO" "Используется UDP протокол" + fi + + safe_sed "TPROTO" "$TPROTO" "$STAGE/user.conf" + safe_sed "TREMOTE" "${EXTIP:-$TREMOTE}" "$STAGE/user.conf" + safe_sed "TPORT" "$TPORT" "$STAGE/user.conf" + + # Проверка инкремента + if [ ! -f "$PKIDIR/increment.txt" ]; then + echo 1 > "$PKIDIR/increment.txt" + log "INFO" "Создан файл инкремента" + fi + + local id=$(cat "$PKIDIR/increment.txt") + + # Запрос имени пользователя, если не передано + if [ -z "$uservar" ]; then + read -p 'Введите имя пользователя латиницей или его UserID: ' uservar + if [ -z "$uservar" ]; then + log "ERROR" "Имя пользователя не может быть пустым" + exit 1 + fi + fi + + local client_name="${id}-${uservar}" + + # Генерация клиентских сертификатов + if [ ! -f "$EASYRSADIR/easyrsa" ]; then + log "ERROR" "Компоненты easy-rsa 3 не найдены" + exit 1 + fi + + log "INFO" "Генерация сертификатов для клиента: $client_name" + "$EASYRSADIR/easyrsa" build-client-full "$client_name" nopass + + # Обновление инкремента + echo $((id + 1)) > "$PKIDIR/increment.txt" + + # Создание конфигурационного файла клиента + local client_config="$CLIENTCONF/${client_name}.ovpn" + cp "$STAGE/user.conf" "$client_config" + + # Добавление ключей и сертификатов + { + echo -e '' + cat "$PKIDIR/ca.crt" + echo -e '\n' + cat "$PKIDIR/issued/${client_name}.crt" + echo -e '\n' + cat "$PKIDIR/private/${client_name}.key" + echo -e '\n' + cat "$PKIDIR/ta.key" + echo -e '' + } >> "$client_config" + + log "INFO" "Профиль успешно создан: $client_config" + echo "==================================================================" + echo "Профиль: $client_name создан успешно!" + echo "Файл: $client_config" + echo "==================================================================" +} + +list() { + log "INFO" "Отображение списка профилей" + + if [ ! -d "$CLIENTCONF" ]; then + log "WARN" "Директория клиентских конфигураций не найдена" + echo "Нет созданных профилей" + return 0 + fi + + local profiles=("$CLIENTCONF"/*.ovpn) + + if [ ${#profiles[@]} -eq 0 ] || [ ! -f "${profiles[0]}" ]; then + echo "Нет созданных профилей" + return 0 + fi + + echo "" + echo "Список действующих профилей:" + echo "=============================" + + for profile in "${profiles[@]}"; do + local name=$(basename "$profile" .ovpn) + local size=$(stat -c%s "$profile" 2>/dev/null || stat -f%z "$profile" 2>/dev/null) + local date=$(stat -c%y "$profile" 2>/dev/null || stat -f%Sm "$profile" 2>/dev/null) + printf "%-20s | %10s | %s\n" "$name" "$((size/1024)) KB" "$date" + done + + echo "=============================" + echo "Всего профилей: ${#profiles[@]}" + echo "" +} + +revoke() { + log "INFO" "Начало процедуры отзыва профиля" + + if [ ! -f "$EASYRSADIR/easyrsa" ]; then + log "ERROR" "Компоненты easy-rsa 3 не найдены" + exit 1 + fi + + read -p 'Введите ID профиля (для получения списка используйте команду list): ' profile_id + + if [ -z "$profile_id" ]; then + log "ERROR" "ID профиля не может быть пустым" + exit 1 + fi + + # Проверка существования профиля + local profile_file="$CLIENTCONF/${profile_id}.ovpn" + if [ ! -f "$profile_file" ]; then + log "ERROR" "Профиль $profile_id не найден" + exit 1 + fi + + # Отзыв сертификата + log "INFO" "Отзыв сертификата: $profile_id" + "$EASYRSADIR/easyrsa" revoke "$profile_id" + + # Удаление конфигурационного файла + rm -f "$profile_file" + log "INFO" "Удален файл конфигурации: $profile_file" + + # Обновление списка отозванных сертификатов + "$EASYRSADIR/easyrsa" gen-crl + log "INFO" "Список отозванных сертификатов обновлен" + + echo "Профиль $profile_id успешно отозван" +} + +init() { + log "INFO" "Инициализация PKI окружения" + + # Проверка зависимостей + check_dependencies || exit 1 + + # Загрузка конфигурации + load_config || exit 1 + + # Проверка, не была ли выполнена инициализация ранее + if [ -f "$PKIDIR/ca.crt" ]; then + log "ERROR" "Инициализация PKI уже была выполнена ранее" + exit 1 + fi + + # Создание директорий + mkdir -p "$STAGE" "$CLIENTCONF" "$PKIDIR" + + # Инициализация PKI + log "INFO" "Создание CA сертификата" + "$EASYRSADIR/easyrsa" init-pki + "$EASYRSADIR/easyrsa" --req-cn="$FQDN_CA" build-ca nopass + + if [ ! -f "$PKIDIR/ca.crt" ]; then + log "ERROR" "Не удалось создать корневой сертификат" + exit 1 + fi + + log "INFO" "Создание сертификата сервера: $FQDN_SERVER" + "$EASYRSADIR/easyrsa" build-server-full "$FQDN_SERVER" nopass + + log "INFO" "Создание DH ключа" + "$EASYRSADIR/easyrsa" gen-dh + + log "INFO" "Создание TLS ключа" + openvpn --genkey secret "$PKIDIR/ta.key" + + log "INFO" "Создание списка отозванных сертификатов" + "$EASYRSADIR/easyrsa" gen-crl + chmod 644 "$PKIDIR/crl.pem" + + # Копирование ключей и сертификатов + local openvpn_dir="/etc/openvpn" + + log "INFO" "Копирование ключей в $openvpn_dir" + + cp "$PKIDIR/issued/$FQDN_SERVER.crt" "$openvpn_dir/server.crt" + cp "$PKIDIR/ca.crt" "$openvpn_dir/ca.crt" + cp "$PKIDIR/dh.pem" "$openvpn_dir/dh.pem" + cp "$PKIDIR/ta.key" "$openvpn_dir/ta.key" + cp "$PKIDIR/private/$FQDN_SERVER.key" "$openvpn_dir/server.key" + + # Проверка копирования + local files_to_check=("server.crt" "ca.crt" "dh.pem" "ta.key" "server.key") + for file in "${files_to_check[@]}"; do + if [ ! -f "$openvpn_dir/$file" ]; then + log "ERROR" "Не удалось скопировать: $file" + exit 1 + fi + done + + log "INFO" "Инициализация PKI успешно завершена" + echo "==================================================================" + echo "PKI окружение успешно инициализировано!" + echo "Сертификаты скопированы в /etc/openvpn/" + echo "==================================================================" +} + +clear() { + log "WARN" "Запрос на очистку PKI окружения" + + read -p 'ВНИМАНИЕ: Все PKI данные будут удалены. Для продолжения введите "YES": ' answer + + if [ "$answer" != "YES" ]; then + log "INFO" "Очистка PKI отменена пользователем" + exit 0 + fi + + # Очистка директорий + rm -rf "$CLIENTCONF"/* 2>/dev/null || true + rm -rf "$STAGE"/* 2>/dev/null || true + + # Инициализация новой PKI + "$EASYRSADIR/easyrsa" init-pki + + log "INFO" "PKI окружение очищено и переинициализировано" + echo "PKI окружение успешно очищено" +} + +srvconf() { + log "INFO" "Создание конфигурации сервера" + + # Загрузка конфигурации + load_config || exit 1 + + # Получение сетевой информации + local network_info + network_info=$(get_network_info) + IFS=',' read -r IFID IFADDR EXTIP <<< "$network_info" + + # Проверка наличия шаблона сервера + local stemp="${BASE_DIR}/templates/server.txt" + if [ ! -f "$stemp" ]; then + log "ERROR" "Шаблон сервера не найден: $stemp" + exit 1 + fi + + # Создание директории staging + mkdir -p "$STAGE" + + # Копирование шаблона + cp "$stemp" "$STAGE/server.conf" + + log "INFO" "Заполнение параметров конфигурации сервера" + + # Обработка протокола + if [ "$TPROTO" == "tcp" ]; then + safe_sed "TCL" "tls-server" "$STAGE/server.conf" + safe_sed "TUDP" "# explicit-exit-notify 1" "$STAGE/server.conf" + log "INFO" "Используется TCP протокол" + else + safe_sed "TCL" "#tls-server" "$STAGE/server.conf" + safe_sed "TUDP" "explicit-exit-notify 1" "$STAGE/server.conf" + log "INFO" "Используется UDP протокол" + fi + + # Базовые подстановки + safe_sed "TPROTO" "$TPROTO" "$STAGE/server.conf" + safe_sed "TLADDR" "$IFADDR" "$STAGE/server.conf" + safe_sed "TPORT" "$TPORT" "$STAGE/server.conf" + safe_sed "TSERNET" "$TSERNET" "$STAGE/server.conf" + safe_sed "TSERMASK" "$TSERMASK" "$STAGE/server.conf" + + # Режим туннеля + if [ "$TTUNTYPE" == "FULL" ]; then + safe_sed "TTUNTYPE" "push \"redirect-gateway def1 bypass-dhcp\"" "$STAGE/server.conf" + safe_sed "TSPLIT_ROUTES" "# Full tunneling mode - все маршруты через VPN" "$STAGE/server.conf" + log "INFO" "Режим: FULL tunneling" + else + safe_sed "TTUNTYPE" "# Split tunneling mode" "$STAGE/server.conf" + split_routes=$(generate_split_routes "${TTUNNETS[@]}") + + if [ -n "$split_routes" ]; then + # Создаем временный файл с маршрутами + echo "$split_routes" > "$STAGE/split_routes.tmp" + # Заменяем маркер содержимым файла + sed -i "/TSPLIT_ROUTES/r $STAGE/split_routes.tmp" "$STAGE/server.conf" + sed -i "/TSPLIT_ROUTES/d" "$STAGE/server.conf" + rm -f "$STAGE/split_routes.tmp" + else + sed -i "s/TSPLIT_ROUTES/# No split routes configured/" "$STAGE/server.conf" + fi + + log "INFO" "Режим: SPLIT tunneling, маршрутов: ${#TTUNNETS[@]}" + fi + + # DNS серверы + local dns_options=$(generate_dns_options "${TDNS[@]}") + safe_sed "TDNS_OPTIONS" "$dns_options" "$STAGE/server.conf" + log "INFO" "Настроено DNS серверов: ${#TDNS[@]}" + + # Клиент-клиент соединения + if [ "$TC2C" == "YES" ]; then + safe_sed "TC2C" "client-to-client" "$STAGE/server.conf" + log "INFO" "Разрешены соединения между клиентами" + else + safe_sed "TC2C" "# client-to-client disabled" "$STAGE/server.conf" + fi + + # Множественные подключения по одному сертификату + if [ "$TDCN" == "YES" ]; then + safe_sed "TDCN" "duplicate-cn" "$STAGE/server.conf" + log "INFO" "Разрешены множественные подключения по одному сертификату" + else + safe_sed "TDCN" "# duplicate-cn disabled" "$STAGE/server.conf" + fi + + # Проверка отозванных сертификатов + if [ "$TREVO" == "YES" ]; then + safe_sed "TREVO" "crl-verify $PKIDIR/crl.pem" "$STAGE/server.conf" + log "INFO" "Активирована проверка списка отозванных сертификатов" + else + safe_sed "TREVO" "# crl-verify disabled" "$STAGE/server.conf" + fi + + # Определение ОС и копирование конфигурации + local os_info=$(uname -a) + local target_conf + + if [[ "$os_info" == *"Alpine"* ]]; then + target_conf="/etc/openvpn/openvpn.conf" + log "INFO" "Обнаружена Alpine Linux" + else + target_conf="/etc/openvpn/server.conf" + log "INFO" "Обнаружена другая дистрибутив Linux" + fi + + # Использовать скрипты для обработки событий подключения и отключения пользователя + if [ "$T_CONNSCRIPTS" == "YES" ]; then + safe_sed "T_SCRIPTSEC" "script-security 2" "$STAGE/server.conf" + safe_sed "T_CONNSCRIPT" "client-connect \"$T_CONNSCRIPT_STRING\"" "$STAGE/server.conf" + safe_sed "T_DISCONNSCRIPT" "client-disconnect \"$T_DISCONNSCRIPT_STRING\"" "$STAGE/server.conf" + log "INFO" "Настройка скриптов подключения и отключения пользователя применена" + else + safe_sed "T_SCRIPTSEC" "" "$STAGE/server.conf" + safe_sed "T_CONNSCRIPT" "" "$STAGE/server.conf" + safe_sed "T_DISCONNSCRIPT" "" "$STAGE/server.conf" + fi + + # Использовать Management Interface + if [ "$T_MGMT" == "YES" ]; then + safe_sed "T_MGMT_CONF" "management $T_MGMT_ADDR $T_MGMT_PORT" "$STAGE/server.conf" + log "INFO" "Management Interface активирован" + else + safe_sed "T_MGMT_CONF" "" "$STAGE/server.conf" + fi + + # Копирование конфигурации + cp "$STAGE/server.conf" "$target_conf" + + if [ $? -eq 0 ]; then + log "INFO" "Конфигурация сервера скопирована в: $target_conf" + echo "==================================================================" + echo "Конфигурация сервера успешно создана!" + echo "Файл: $target_conf" + echo "Для применения изменений перезапустите OpenVPN:" + echo " systemctl restart openvpn@server # для systemd" + echo " или service openvpn restart # для SysVinit" + echo "==================================================================" + else + log "ERROR" "Не удалось скопировать конфигурацию в $target_conf" + exit 1 + fi +} + +# Обработка команд +cmd="${1:-}" +shift 2>/dev/null || true + +case "$cmd" in + build) + build "${1:-}" ;; + list) + list ;; + revoke) + revoke ;; + init) + init ;; + clear) + clear ;; + srvconf) + srvconf ;; + help|-h|--help|--usage|"") + usage ;; + *) + echo "Неизвестная команда: $cmd" + usage + exit 1 ;; +esac + +exit 0 diff --git a/split_routes.txt b/split_routes.txt new file mode 100644 index 0000000..1b3c256 --- /dev/null +++ b/split_routes.txt @@ -0,0 +1,11 @@ +# Пример файла со списком маршрутов для SPLIT туннелирования +# Формат: CIDR (192.168.0.0/24) или адрес с маской (192.168.0.0 255.255.255.0) +# Пустые строки и строки начинающиеся с # игнорируются + +10.0.0.0/8 +172.16.0.0/12 +192.168.0.0/16 + +# Дополнительные маршруты +# 192.168.10.0/24 +# 192.168.20.0/24 diff --git a/templates/server.txt b/templates/server.txt new file mode 100644 index 0000000..e420637 --- /dev/null +++ b/templates/server.txt @@ -0,0 +1,74 @@ +local TLADDR +dev tun +proto TPROTO +TCL +port TPORT + +# Keys +ca /etc/openvpn/ca.crt +cert /etc/openvpn/server.crt +key /etc/openvpn/server.key +dh /etc/openvpn/dh.pem +tls-auth /etc/openvpn/ta.key 0 + +# Network topology +topology subnet +server TSERNET TSERMASK + +ifconfig-pool-persist /etc/openvpn/ipp.txt + +log /etc/openvpn/openvpn.log +log-append /etc/openvpn/openvpn.log + +verb 3 + +# Use Extended Status Output +status /etc/openvpn/openvpn-status.log 5 +status-version 2 + + +# FULL Tunneling +TTUNTYPE + +# SPLIT Tunneling +TSPLIT_ROUTES + +# DNS Configuration +TDNS_OPTIONS + +# Client-to-client communication +TC2C + +user nobody +group nogroup + +# Allow same profile on multiple devices simultaneously +TDCN + +# data protection +data-ciphers CHACHA20-POLY1305:AES-256-GCM:AES-256-CBC +data-ciphers-fallback AES-256-CBC +auth SHA256 + +keepalive 10 120 + +persist-key +persist-tun + +# check evocation list +TREVO + +# for UDP only +TUDP + +# Script Scurity Lever +T_SCRIPTSEC + +# Client Connect Script +T_CONNSCRIPT + +# Client Disconnect Script +T_DISCONNSCRIPT + +# Enable Management Interface +T_MGMT_CONF \ No newline at end of file diff --git a/templates/user.txt b/templates/user.txt new file mode 100644 index 0000000..dd781dc --- /dev/null +++ b/templates/user.txt @@ -0,0 +1,23 @@ +client +dev tun +windows-driver wintun +proto TPROTO +remote TREMOTE TPORT +resolv-retry infinite +nobind +user nobody +group nobody +persist-key +persist-tun + +TCL + +mute-replay-warnings + +remote-cert-tls server +data-ciphers CHACHA20-POLY1305:AES-256-GCM:AES-256-CBC +data-ciphers-fallback AES-256-CBC +auth SHA256 +verb 3 + +key-direction 1