first commit
This commit is contained in:
113
README.md
Normal file
113
README.md
Normal file
@@ -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 <username> # Создать новый профиль
|
||||||
|
./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` - файл лога (создается автоматически)
|
||||||
|
|
||||||
89
confvars
Normal file
89
confvars
Normal file
@@ -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
|
||||||
638
profiler.sh
Normal file
638
profiler.sh
Normal file
@@ -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 <UserID> # создать новый профиль
|
||||||
|
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 '<ca>'
|
||||||
|
cat "$PKIDIR/ca.crt"
|
||||||
|
echo -e '</ca>\n<cert>'
|
||||||
|
cat "$PKIDIR/issued/${client_name}.crt"
|
||||||
|
echo -e '</cert>\n<key>'
|
||||||
|
cat "$PKIDIR/private/${client_name}.key"
|
||||||
|
echo -e '</key>\n<tls-auth>'
|
||||||
|
cat "$PKIDIR/ta.key"
|
||||||
|
echo -e '</tls-auth>'
|
||||||
|
} >> "$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
|
||||||
11
split_routes.txt
Normal file
11
split_routes.txt
Normal file
@@ -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
|
||||||
74
templates/server.txt
Normal file
74
templates/server.txt
Normal file
@@ -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
|
||||||
23
templates/user.txt
Normal file
23
templates/user.txt
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user