#!/bin/bash # --- Конфигурационные переменные --- script_path=$(realpath "$0") # Получаем абсолютный путь к скрипту RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLINK='\033[5m' # Анимация мигания текста NC='\033[0m' LOG_LEVEL=2 # 0=error, 1=warn, 2=info, 3=debug BACKUP_DIR="/backup" SERVER_IP=$(ip -4 addr show scope global | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -n1) LOG_FILE="/var/log/server_setup_$(date +%Y%m%d_%H%M%S).log" ERROR_LOG="/var/log/server_setup_errors.log" # Глобальная переменная для лога ошибок SECURITY_MODE="" # Явно инициализируем пустым значением # Настройки APT для скриптов (в начале файла, после переменных) export DEBIAN_FRONTEND=noninteractive export APT_LISTCHANGES_FRONTEND=none apt-get() { command apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -qq "$@" } # Визуальный вывод предупреждений msg() { local level=$1 local message=$2 case $level in error) echo -e "${RED}[✖]${NC} $message" >&2 ;; warn) [ "$LOG_LEVEL" -ge 1 ] && echo -e "${YELLOW}[⚠]${NC} $message" ;; info) [ "$LOG_LEVEL" -ge 2 ] && echo -e "${GREEN}[✔]${NC} $message" ;; debug) [ "$LOG_LEVEL" -ge 3 ] && echo -e "[DEBUG] $message" ;; custom|*) echo -e "$message" # Для произвольного форматирования с эмодзи и цветами ;; esac } # Запрет на запуск нескольких экземпляров скрипта LOCK_FILE="/var/lock/server_setup.lock" ROLLBACK_FILE="/var/backups/server_setup_rollback.sh" acquire_lock_with_rollback() { # Проверка блокировки с анализом процесса exec 200>"$LOCK_FILE" if ! flock -n 200; then local lock_pid=$(flock -n "$LOCK_FILE" -c "echo \$PPID") # Проверка "живости" процесса if [ -n "$lock_pid" ] && [ -d "/proc/$lock_pid" ]; then msg error "${RED}Обнаружен запущенный экземпляр скрипта (PID $lock_pid)${NC}" # Проверяем, есть ли файл отката if [ -f "$ROLLBACK_FILE" ]; then if read -p "Выполнить откат предыдущих изменений? (y/N): " choice && [[ "$choice" =~ [yY] ]]; then msg warn "Выполняю откат изменений..." if ! bash "$ROLLBACK_FILE"; then msg error "Ошибка при откате изменений" exit 1 fi rm -f "$ROLLBACK_FILE" "$LOCK_FILE" exit 0 fi fi # Запрос на перезагрузку if read -p "Перезагрузить систему для сброса блокировки? (y/N): " choice && [[ "$choice" =~ [yY] ]]; then countdown=5 msg warn "Перезагрузка через $countdown секунд (Ctrl+C для отмены)..." while ((countdown > 0)); do echo -ne "${RED}$countdown...${NC} " ((countdown--)) sleep 1 done echo shutdown -r now else exit 1 fi else msg warn "Обнаружена мёртвая блокировка (PID $lock_pid), очищаем..." rm -f "$LOCK_FILE" exec 200>"$LOCK_FILE" if ! flock -n 200; then msg error "Не удалось получить блокировку после очистки" exit 1 fi fi fi # Инициализация механизма отката с проверкой if ! { echo "#!/bin/bash" > "$ROLLBACK_FILE" && \ echo "# Автоматически сгенерированный скрипт отката" >> "$ROLLBACK_FILE" && \ chmod +x "$ROLLBACK_FILE"; }; then msg error "Ошибка инициализации механизма отката" exit 1 fi # Регистрация обработчиков с проверкой trap 'add_rollback_command' EXIT || { msg error "Ошибка регистрации обработчика EXIT" exit 1 } trap 'rm -f "$LOCK_FILE"; exit' SIGINT SIGTERM || { msg error "Ошибка регистрации обработчиков сигналов" exit 1 } # Дополнительная проверка через 5 секунд ( sleep 5 if [ ! -f "$LOCK_FILE" ] || [ ! -w "$LOCK_FILE" ]; then msg error "Проблема с файлом блокировки после инициализации" exit 1 fi ) & } release_lock() { flock -u 200 rm -f "$LOCK_FILE" } # Защита от автоматического запуска if [[ ! -t 0 ]]; then msg error "Скрипт требует интерактивного ввода. Запустите его вручную." exit 1 fi # Инициализация SECURITY_VALIDATED="TRUE" export SECURITY_VALIDATED STRICT_MODE="NO" #SECURITY_MODE="${1:-standard}" # standard/paranoid # Функция безопасного выполнения команд safe_exec() { local cmd="$*" local max_retries=3 local delay=15 # Увеличенная задержка между попытками local attempt=0 local timeout=300 # 5 минут на выполнение local timestamp=$(date '+%Y-%m-%d %H:%M:%S') ##### ПРОВЕРКА НА ПРИГОДДНОСТЬ apt-get() { command apt-get \ -o Acquire::http::Timeout=10 \ -o Acquire::https::Timeout=10 \ -o Acquire::Retries=3 \ -o Acquire::http::Pipeline-Depth=5 \ -o Acquire::Queue-Mode=access \ "$@" } #################### while [ $attempt -lt $max_retries ]; do if timeout $timeout bash -c "$cmd" >>"$LOG_FILE" 2>>"$ERROR_LOG"; then return 0 else echo "[$timestamp] Попытка $((attempt+1))/$max_retries: $cmd" >> "$ERROR_LOG" # Специальная обработка ошибок apt if [[ "$cmd" == *apt* ]]; then msg warn "Очистка окружения apt после ошибки..." rm -rf /var/lib/apt/lists/* apt-get clean dpkg --configure -a fi sleep $delay ((attempt++)) fi done msg error "Ошибка выполнения (после $max_retries попыток): ${RED}$cmd${NC}" echo "[$timestamp] Критическая ошибка: $cmd" >> "$ERROR_LOG" if [[ "$STRICT_MODE" == "YES" ]]; then exit 1 fi return 1 } # Система обработки ошибок установки пакетов critical_section() { local cmd="$1" local error_msg="${2:-"Critical error executing: $cmd"}" local max_retries="${3:-1}" local attempt=0 while [ $attempt -lt $max_retries ]; do if safe_exec "$cmd"; then return 0 fi attempt=$((attempt + 1)) [ $attempt -lt $max_retries ] && sleep 5 done msg error "$error_msg" [ "$STRICT_MODE" = "YES" ] && exit 1 return 1 } # --- Настройка вывода --- exec > >(tee -a "$LOG_FILE") exec 2>&1 # Проверяем, что скрипт запущен единожды acquire_lock_with_rollback trap release_lock EXIT # Валидация входных параметров validate_parameters() { while getopts ":m:" opt; do case $opt in m) case $OPTARG in standard|advanced|paranoid) SECURITY_MODE="$OPTARG" msg info "Режим установлен через параметр: ${YELLOW}$SECURITY_MODE${NC}" ;; *) msg error "Недопустимый режим безопасности: $OPTARG" exit 1 ;; esac ;; \?) msg error "Недопустимый параметр: -$OPTARG" exit 1 ;; :) msg error "Параметр -$OPTARG требует значения" exit 1 ;; esac done shift $((OPTIND -1)) # Оставшиеся аргументы (если нужны) OTHER_ARGS=("$@") } # Валидация входных параметров if [[ $# -gt 0 ]]; then validate_parameters "$@" fi # Проверка существования и возраста пароля пользователя check_password_change() { # Если имя пользователя не указано, используем текущего local username="${1:-$(whoami)}" local max_age_days=90 # Проверка существования пользователя #if ! id "$username" &>/dev/null; then # echo "Ошибка: Пользователь $username не существует" >&2 # return 1 #fi # Получение информации о пароле local shadow_entry if ! shadow_entry=$(sudo grep "^$username:" /etc/shadow 2>/dev/null); then msg warn "Не удалось получить информацию о пароле для $username" >&2 return 3 fi local days_since_epoch=$(cut -d: -f3 <<< "$shadow_entry") local current_days=$(( $(date +%s) / 86400 )) # Обработка случаев, когда пароль никогда не менялся или заблокирован if [[ -z "$days_since_epoch" || "$days_since_epoch" == "!" || "$days_since_epoch" == "*" ]]; then msg error "ВНИМАНИЕ: Пароль пользователя $username никогда не менялся или отсутствует!" >&2 msg warn "Установите новый пароль:" passwd "$username" return 2 fi # Проверка возраста пароля local days_diff=$(( current_days - days_since_epoch )) local last_change_date=$(date -d "1970-01-01 + $days_since_epoch days" "+%Y-%m-%d") if [ "$days_diff" -gt "$max_age_days" ]; then msg error "ВНИМАНИЕ: Пароль пользователя $username не менялся $days_diff дней (последнее изменение: $last_change_date)" >&2 msg warn "Установите новый пароль:" passwd "$username" return 4 fi msg warn "Пароль пользователя $username менялся $days_diff дней назад (последнее изменение: $last_change_date)" return 0 } check_password_change # Очистка перед началом apt_cleanup() { rm -rf /var/lib/apt/lists/* apt-get clean dpkg --configure -a } apt_cleanup # Проверка интернет-соединения check_internet_connection() { local max_retries=5 local delay=5 local attempt=0 while [ $attempt -lt $max_retries ]; do # Проверка DNS if ! nslookup deb.debian.org >/dev/null 2>&1; then msg warn "Проблема с DNS, попытка $((attempt+1))/$max_retries..." sleep $delay ((attempt++)) continue fi # Проверка соединения if ping -c1 -W2 deb.debian.org &>/dev/null; then return 0 fi msg warn "Нет интернет-соединения, попытка $((attempt+1))/$max_retries..." sleep $delay ((attempt++)) done msg error "Не удалось установить интернет-соединение" return 1 } # Функция для восстановления сетевого подключения repair_network() { msg info "Попытка восстановления сети..." # 1. Сначала проверяем базовую связность if ping -c1 -W2 1.1.1.1 &>/dev/null; then msg info "Сетевая связность есть, проблема может быть в DNS" # Только исправляем DNS echo "nameserver 1.1.1.1" > /etc/resolv.conf echo "nameserver 8.8.8.8" >> /etc/resolv.conf return 0 fi # 2. Проверка и восстановление DNS msg warn "Проблема с сетью, проверяем DNS и маршруты..." echo "nameserver 1.1.1.1" > /etc/resolv.conf echo "nameserver 8.8.8.8" >> /etc/resolv.conf # 3. Проверка маршрутов local default_gw=$(ip route | awk '/default/ {print $3}') if [ -z "$default_gw" ]; then msg error "Шлюз по умолчанию не найден!" return 1 fi # 4. Проверяем, существует ли уже маршрут if ! ip route | grep -q "default via $default_gw"; then msg info "Добавляем маршрут по умолчанию через $default_gw" ip route add default via "$default_gw" || { msg error "Не удалось добавить маршрут!" return 1 } else msg info "Маршрут по умолчанию уже существует" fi # 5. Проверка интерфейса local main_iface=$(ip route | awk '/default/ {print $5}') if [ -n "$main_iface" ]; then msg info "Проверяем интерфейс $main_iface" ip link set "$main_iface" up dhclient -r "$main_iface" # Освобождаем старый lease dhclient "$main_iface" # Запрашиваем новый IP fi # 6. Финальная проверка if ping -c2 -W5 1.1.1.1 &>/dev/null; then msg info "Сеть восстановлена" return 0 else msg error "Не удалось восстановить сеть" return 1 fi } # Проверка сети перед выполнением if ! repair_network; then msg error "Критическая ошибка сети. Требуется ручное вмешательство." exit 1 fi ################################## ТЕСТ Автоматического отката при критических ошибках setup_rollback() { local backup_dir="/var/backups/server_setup_$(date +%Y%m%d)" mkdir -p "$backup_dir" # Копируем критичные конфиги cp -a /etc/ssh "$backup_dir" cp -a /etc/ufw "$backup_dir" # Регистрируем обработчик для отката trap "rollback_changes '$backup_dir'" EXIT ERR } rollback_changes() { local backup_dir="$1" msg error "ОШИБКА! Восстанавливаю резервные копии..." #cp -a "$backup_dir/ssh" /etc/ #cp -a "$backup_dir/ufw" /etc/ msg info "Откат завершен." msg warn "Сервер будет перезагружен через 10 секунд. Ctrl+C для отмены." for i in {10..1}; do echo -n "$i " sleep 1 done echo shutdown -r now } ################################# # Функция проверки интернета check_internet_connection() { local max_retries=5 local delay=5 local attempt=0 local test_urls=("wikipedia.org" "1.1.1.1" "deb.debian.org") while [ $attempt -lt $max_retries ]; do for url in "${test_urls[@]}"; do # Проверяем и DNS и ping вместе if ping -c1 -W2 "$url" &>/dev/null; then return 0 fi done msg warn "Проблема с интернетом, попытка $((attempt+1))/$max_retries..." sleep $delay ((attempt++)) done msg error "Не удалось установить интернет-соединение" return 1 } # Прогресс-бар для длительных операций show_progress() { local pid=$1 local msg=$2 local delay=0.75 local spin='-\|/' local i=0 while kill -0 "$pid" 2>/dev/null; do i=$(( (i+1) %4 )) printf "\r[%s] %s" "${spin:$i:1}" "$msg" sleep "$delay" done printf "\r%s\r" " " } ########## ТЕСТ АВТО ОТКАТА В СЛУЧАЕ ОИШБОК setup_rollback ########## # GPG-проверка подписи скрипта verify_script_signature() { local temp_pub=$(mktemp) local temp_sig=$(mktemp) # Ссылки на ваши ресурсы local pubkey_url="https://your-url/security.pub" local sig_url="https://your-url/ultimate_server_setup.sh.sig" # Загрузка ключа и подписи if ! wget -qO "$temp_pub" "$pubkey_url"; then msg error "Ошибка загрузки GPG-ключа" return 1 fi if ! wget -qO "$temp_sig" "$sig_url"; then msg error "Ошибка загрузки подписи скрипта" return 1 fi # Импорт ключа if ! gpg --import "$temp_pub"; then msg error "Ошибка импорта GPG-ключа" return 1 fi # Проверка подписи if ! gpg --verify "$temp_sig" "$0"; then msg error "ПРОВЕРКА ПОДПИСИ НЕ УДАЛАСЬ! Скрипт мог быть изменен!" return 1 fi # Очистка rm -f "$temp_pub" "$temp_sig" return 0 } validate_environment() { SECURITY_VALIDATED="TRUE" # Проверка подписи в paranoid режиме if [ "$SECURITY_MODE" = "paranoid" ]; then if ! verify_script_signature; then SECURITY_VALIDATED="FALSE" msg error "Проверка подписи не пройдена!" exit 1 fi fi # Проверка целостности критичных файлов local critical_files=("/etc/passwd" "/etc/shadow" "/etc/sudoers") for file in "${critical_files[@]}"; do if [ ! -f "$file" ]; then SECURITY_VALIDATED="FALSE" msg error "Критичный файл отсутствует: $file" fi done export SECURITY_VALIDATED } # Добавляет временные правила UFW для доступа к Debian репозиториям add_temp_debian_repo_rules() { # Получаем IP-адреса через DNS local debian_ips=$(dig +short deb.debian.org security.debian.org | grep -E '^[0-9.]' | sort -u) # Добавляем резервные сети debian_ips+=" 151.101.0.0/16 199.232.0.0/16" # Применяем правила UFW для каждого IP/подсети for ip in $debian_ips; do ufw allow out proto tcp to "$ip" port 80 comment "Temp Debian Repo HTTP" ufw allow out proto tcp to "$ip" port 443 comment "Temp Debian Repo HTTPS" ufw allow out proto udp to "$ip" port 53 comment "Temp Debian Repo DNS" done sudo ufw reload } # Удаляет временные правила UFW для Debian репозиториев remove_temp_debian_repo_rules() { while read -r rule_num; do ufw --force delete "$rule_num" done < <(ufw status numbered | grep "Temp Debian Repo" | awk -F'[][]' '{print $2}') sudo ufw reload } # --- Установка режима безопасности --- set_security_mode() { # Если режим передан как параметр - используем его if [[ -n "$1" ]]; then case "$1" in standard|advanced|paranoid) SECURITY_MODE="$1" msg info "Режим установлен через параметр: ${YELLOW}$SECURITY_MODE${NC}" return 0 ;; *) msg error "Недопустимый режим безопасности: $1" return 1 ;; esac fi # Иначе интерактивный выбор local max_attempts=5 local attempts=0 while (( attempts < max_attempts )); do echo -e "\n${GREEN}Доступные режимы безопасности:${NC}" echo "1) Стандартный (базовая защита)" echo "2) Продвинутый (доп. изоляция)" echo "3) Paranoid (максимальная безопасность)" read -p "Выберите номер режима (1-3): " choice # Очистка ввода choice=$(echo "$choice" | tr -cd '0-9') case "$choice" in 1) SECURITY_MODE="standard" msg info "Выбран режим: ${YELLOW}$SECURITY_MODE${NC}" return 0 ;; 2) SECURITY_MODE="advanced" msg info "Выбран режим: ${YELLOW}$SECURITY_MODE${NC}" return 0 ;; 3) SECURITY_MODE="paranoid" msg info "Выбран режим: ${YELLOW}$SECURITY_MODE${NC}" return 0 ;; *) msg error "Недопустимый выбор. Пожалуйста, введите число от 1 до 3." ((attempts++)) if (( attempts < max_attempts )); then msg warn "Осталось попыток: $((max_attempts - attempts))" sleep 1 else msg error "Превышено максимальное количество попыток. Установлен стандартный режим." SECURITY_MODE="standard" return 0 fi ;; esac done } # Установка режима безопасности #set_security_mode "$1" validate_environment # Вызов проверки в Paranoid Mode if [ "$SECURITY_MODE" = "paranoid" ]; then verify_script_signature || exit 1 fi # Добавляем стандартизацию: validate_variables() { # Проверка всех критических переменных local required_vars=( NEW_USER SSH_PORT SECURITY_MODE SERVER_IP BACKUP_DIR ) for var in "${required_vars[@]}"; do if [ -z "${!var}" ]; then msg error "Критическая переменная $var не установлена" exit 1 fi done } #validate_variables() # Функция подтверждения пользователя с рекомендациями user_confirm() { local prompt="$1" local warning="${2:-}" #if [ "$SECURITY_MODE" = "paranoid" ]; then msg custom "🔐 ${YELLOW}Подтвердите Ваше действие:${NC} $warning" #fi while true; do read -p "$prompt (y/N): " choice case "$choice" in [yY]) return 0 ;; [nN]) return 1 ;; *) msg error "Пожалуйста, введите только Y (да) или N (нет)" continue ;; esac done } # Проверка аппаратных возможностей для Paranoid Mode check_hardware_capabilities() { if [ "$SECURITY_MODE" != "Paranoid" ]; then return 0 fi local errors=0 # Проверка AES-NI if ! grep -q aes /proc/cpuinfo; then msg warn "Аппаратное ускорение AES (AES-NI) не обнаружено" ((errors++)) fi # Проверка TPM if [ ! -d /sys/class/tpm/tpm0 ]; then msg warn "Аппаратный TPM не обнаружен" ((errors++)) fi # Проверка Secure Boot if [ ! -d /sys/firmware/efi ]; then msg warn "UEFI Secure Boot не поддерживается" ((errors++)) fi if [ $errors -gt 2 ]; then msg error "Система не соответствует требованиям Paranoid Mode" return 1 fi return 0 } # Проверка дистрибутива и его версии check_system_requirements() { msg info "=== Проверка системных требований ===" # Проверка дистрибутива if [ ! -f /etc/os-release ]; then msg error "Не удалось определить дистрибутив!" exit 1 fi source /etc/os-release declare -A MIN_VERSIONS=( ["ubuntu"]="20.04" ["debian"]="10" ) if [[ -z "${MIN_VERSIONS[$ID]}" ]]; then msg error "Неподдерживаемый дистрибутив: ${ID}" msg warn "Поддерживаются только: ${!MIN_VERSIONS[*]}" exit 1 fi if [[ "$(printf '%s\n' "${MIN_VERSIONS[$ID]}" "$VERSION_ID" | sort -V | head -n1)" != "${MIN_VERSIONS[$ID]}" ]]; then msg error "Требуется ${PRETTY_NAME} версии не ниже ${MIN_VERSIONS[$ID]} (текущая: ${VERSION_ID})" exit 1 fi msg info "Дистрибутив: ${PRETTY_NAME} ${VERSION_ID}" # Проверка оборудования local requirements=( "memory:512:$(free -m | awk '/Mem:/ {print $2}'):MB" "disk:10:$(df -BG --output=size / | tail -1 | tr -d 'G'):GB" "cores:1:$(nproc):ядер" ) for spec in "${requirements[@]}"; do IFS=':' read -r name min actual unit <<< "$spec" if [ "$actual" -lt "$min" ]; then msg error "Недостаточно ${name}: ${actual}${unit} (требуется ≥${min}${unit})" exit 1 fi msg info "${name^}: ${actual}${unit} (требуется ≥${min}${unit})" done # Дополнительные требования для Paranoid Mode #if [ "$SECURITY_MODE" = "paranoid" ]; then msg custom "${YELLOW}⚠ Дополнительные рекомендации для Paranoid Mode:${NC} - 2GB+ RAM (имеется: $(free -m | awk '/Mem:/ {print $2}')MB) - Поддержка TPM 2.0 (рекомендуется) - Аппаратная поддержка AES-NI - Поддержка UEFI Secure Boot" #fi } check_system_requirements # ЗАПУСКАЕМ СКРИПТ ИНТЕРАКТИВНО ////////////////////////////// if [[ -z "$SECURITY_MODE" ]]; then set_security_mode || { msg error "Не удалось установить режим безопасности" exit 1 } fi # Политика работы с репозиториями: # force - всегда использовать официальные # verify - проверять и запрашивать подтверждение # off - не проверять REPO_POLICY="verify" [ "$SECURITY_MODE" = "paranoid" ] && REPO_POLICY="force" # Очистка возможных конфигов rm -f /etc/apt/apt.conf.d/00proxy 2>/dev/null rm -f /etc/apt/apt.conf.d/00mirror 2>/dev/null _backup_repos() { local backup_dir="/var/backups/apt" mkdir -p "$backup_dir" local timestamp=$(date +%Y%m%d_%H%M%S) if ! cp -a /etc/apt/sources.list "$backup_dir/sources.list.$timestamp.bak"; then msg error "Ошибка резервного копирования sources.list" return 1 fi if [ -d "/etc/apt/sources.list.d" ]; then if ! cp -a /etc/apt/sources.list.d "$backup_dir/sources.list.d.$timestamp.bak"; then msg error "Ошибка резервного копирования sources.list.d" return 1 fi fi msg info "Резервные копии репозиториев сохранены в $backup_dir" return 0 } _check_mirror_availability() { local mirror=$1 local timeout=5 if ! curl -m $timeout -sSf "$mirror/Release" >/dev/null; then return 1 fi if ! curl -m $timeout -sSf "$mirror/Release.gpg" >/dev/null; then return 1 fi return 0 } setup_repositories() { msg custom "\n${GREEN}=== Настройка системы Debian ===${NC}" # 1. Резервное копирование if ! _backup_repos; then msg error "Не удалось создать резервную копию репозиториев" return 1 fi # 2. Устанавливаем официальные репозитории local OFFICIAL_SOURCES=( "deb http://deb.debian.org/debian bookworm main contrib non-free" "deb http://security.debian.org/debian-security bookworm-security main contrib non-free" "deb http://deb.debian.org/debian bookworm-updates main contrib non-free" "deb http://deb.debian.org/debian bookworm-backports main contrib non-free" ) # 3. Записываем новые репозитории { echo "# Официальные репозитории Debian, сгенерировано $(date)" printf "%s\n" "${OFFICIAL_SOURCES[@]}" } > /etc/apt/sources.list || { msg error "Ошибка записи в /etc/apt/sources.list" return 1 } # 4. Очищаем кастомные репозитории if [ -d "/etc/apt/sources.list.d" ]; then rm -rf /etc/apt/sources.list.d/* 2>/dev/null fi # 5. Проверка доступности зеркал if ! _check_mirror_availability "http://deb.debian.org/debian"; then msg warn "Основное зеркало недоступно, пробуем альтернативы..." local ALTERNATIVE_MIRRORS=( "http://ftp.us.debian.org/debian" "http://ftp.de.debian.org/debian" "http://ftp.uk.debian.org/debian" ) for mirror in "${ALTERNATIVE_MIRRORS[@]}"; do if _check_mirror_availability "$mirror"; then sed -i "s|http://deb.debian.org/debian|$mirror|" /etc/apt/sources.list msg info "Используем альтернативное зеркало: $mirror" break fi done fi # 6. Обновление кеша пакетов msg info "Обновление списка пакетов..." if ! apt-get clean || ! rm -rf /var/lib/apt/lists/*; then msg warn "Не удалось очистить кеш пакетов" fi # Применение обновления пакетов if ! apt-get update; then msg error "Ошибка обновления индексов пакетов" return 1 fi msg info "Официальные репозитории успешно настроены" sleep 2 msg info "Обновление системы" sleep 2 apt-get upgrade -y msg info "Система успешно обновлена" sleep 4 return 0 # Применение обновления системы if ! apt-get upgrade; then msg error "Ошибка обновления системы" return 1 fi msg info "Система успешно обновлена" return 0 } _install_single_package() { local pkg=$1 local max_retries=3 local attempt=0 local dist_codename=$(lsb_release -cs) while [ $attempt -lt $max_retries ]; do # Основная попытка установки if apt-get install -y --no-install-recommends "$pkg"; then return 0 fi # Для security-пакетов пробуем security-репозиторий if [[ "$pkg" =~ ^(ufw|fail2ban|ca-certificates)$ ]]; then msg warn "Пробуем security-репозиторий для $pkg..." if apt-get install -y -t "${dist_codename}-security" "$pkg"; then return 0 fi fi # Попытка из backports if grep -q "backports" /etc/apt/sources.list; then msg warn "Пробуем backports для $pkg..." if apt-get install -y -t "${dist_codename}-backports" "$pkg"; then return 0 fi fi ((attempt++)) sleep 5 done msg error "Не удалось установить $pkg после $max_retries попыток" return 1 } install_packages() { local packages=("$@") local pkg # Сначала пробуем установить все вместе if apt-get install -y --no-install-recommends "${packages[@]}"; then return 0 fi # Если не получилось, устанавливаем по одному msg warn "Пакетная установка не удалась, пробуем по одному..." for pkg in "${packages[@]}"; do if ! _install_single_package "$pkg"; then [ "$STRICT_MODE" = "YES" ] && return 1 fi done return 0 } restore_repos() { local backup_dir="/var/backups/apt" local log_file="/var/log/repo_restore.log" [ -d "$backup_dir" ] || { msg error "Директория бэкапов $backup_dir не существует" return 1 } local latest_backup=$(find "$backup_dir" -name "sources.list.*" -printf "%T@ %p\n" 2>/dev/null | sort -n | tail -1 | cut -d' ' -f2-) [ -z "$latest_backup" ] && { msg error "Не найдены резервные копии репозиториев" return 1 } msg warn "Восстанавливаем репозитории из $latest_backup..." echo "$(date) - Восстановление из $latest_backup" >> "$log_file" if ! cp -f "$latest_backup" /etc/apt/sources.list; then msg error "Ошибка восстановления sources.list" echo "$(date) - Ошибка восстановления" >> "$log_file" return 1 fi if [ -d "/etc/apt/sources.list.d" ]; then rm -rf /etc/apt/sources.list.d/* local listd_backup="${latest_backup//sources.list/sources.list.d}" [ -d "$listd_backup" ] && { cp -rf "$listd_backup"/* /etc/apt/sources.list.d/ echo "$(date) - Восстановлены sources.list.d" >> "$log_file" } fi if ! apt-get update; then msg error "Ошибка обновления индексов после восстановления" return 1 fi msg info "Репозитории успешно восстановлены из резервной копии" return 0 } install_package() { local pkg=$1 local max_retries=3 local attempt=0 local delay=5 local repo_file="/etc/apt/sources.list.d/${pkg}.list" # Улучшенная проверка доступности пакета if ! apt-cache show "$pkg" &>/dev/null && [ ! -f "$repo_file" ]; then msg warn "Пакет $pkg не найден в репозиториях" return 1 fi # Попытки установки while [ $attempt -lt $max_retries ]; do # Обновление только если есть специфический repo_file if [ -f "$repo_file" ]; then apt-get update -o Dir::Etc::sourcelist="$repo_file" \ -o Dir::Etc::sourceparts="-" \ -o APT::Get::List-Cleanup="0" fi # Основная попытка установки if apt-get install -y --no-install-recommends "$pkg"; then return 0 fi # Попытка исправления зависимостей msg warn "Попытка $((attempt+1)): исправление зависимостей для $pkg..." if apt-get -f install -y; then if apt-get install -y --no-install-recommends "$pkg"; then return 0 fi fi # Для security-пакетов пробуем security-репозиторий if [[ "$pkg" =~ ^(ufw|fail2ban|ca-certificates)$ ]]; then msg warn "Пробуем security-репозиторий для $pkg..." if apt-get install -y -t "bookworm-security" "$pkg"; then return 0 fi fi # Попытка из backports (если доступно) if grep -q "backports" /etc/apt/sources.list && [[ "$pkg" != *-backports ]]; then msg warn "Пробуем backports для $pkg..." if apt-get install -y -t "bookworm-backports" "$pkg"; then return 0 fi fi sleep $delay ((attempt++)) done msg error "Все попытки установки $pkg провалились" return 1 } check_dependencies() { local base_pkgs=( sudo curl wget git ufw fail2ban ca-certificates apt-transport-https gnupg iproute2 coreutils gpg systemd-boot ) local missing=() local pkg # Проверяем наличие базовых пакетов for pkg in "${base_pkgs[@]}"; do if ! dpkg -l | grep -q "^ii $pkg"; then missing+=("$pkg") fi done if [ ${#missing[@]} -gt 0 ]; then msg warn "Отсутствуют базовые пакеты: ${missing[*]}" msg info "Установка..." sleep 3 if ! install_packages "${missing[@]}"; then msg error "Не удалось установить необходимые пакеты" return 1 fi fi # Дополнительные проверки if (( BASH_VERSINFO[0] < 4 )); then msg error "Требуется bash версии 4 или выше" return 1 fi msg info "Все зависимости удовлетворены" sleep 2 return 0 } # --- Основной поток выполнения --- # 1. Дополнительная проверка репозиториев if ! setup_repositories; then msg error "Не удалось настроить репозитории" exit 1 fi # 2. Проверка и установка зависимостей if ! check_dependencies; then msg error "Проблемы с зависимостями" exit 1 fi msg info "Система готова к работе" sleep 4 # Проверка зависимостей check_dependencies() { # Базовые пакеты для всех режимов local base_pkgs=( coreutils # содержит tee, date, и другие базовые утилиты gpg iproute2 # содержит ip curl wget sed openssl ca-certificates bash # для проверки версии bash ) # Дополнительные пакеты для продвинутого режима local advanced_pkgs=( fail2ban ufw ) # Дополнительные пакеты для paranoid режима local paranoid_pkgs=( auditd selinux-basics selinux-policy-default aide rkhunter chkrootkit tpm2-tools libtpm2-pkcs11-1 ) # Формируем итоговый список пакетов local all_pkgs=("${base_pkgs[@]}") case "$SECURITY_MODE" in advanced) all_pkgs+=("${advanced_pkgs[@]}") ;; paranoid) all_pkgs+=("${advanced_pkgs[@]}" "${paranoid_pkgs[@]}") ;; esac # Проверяем наличие пакетов local missing=() for pkg in "${all_pkgs[@]}"; do if ! dpkg -l | grep -q "^ii $pkg"; then missing+=("$pkg") fi done if [ ${#missing[@]} -gt 0 ]; then msg error "Отсутствуют обязательные пакеты:" for m in "${missing[@]}"; do msg error " - $m" done if [[ "$SECURITY_MODE" == "paranoid" ]] || user_confirm "Установить недостающие пакеты автоматически?"; then msg info "Установка недостающих пакетов..." sleep 3 apt update && apt install -y --no-install-recommends "${missing[@]}" || { msg error "Ошибка установки пакетов" exit 1 } msg info "Необходимые пакеты успешно установлены" sleep 3 else msg error "Установите пакеты вручную и перезапустите скрипт:" msg error "sudo apt-get update && sudo apt-get install -y ${missing[*]}" exit 1 fi fi # Проверка версии bash local min_bash=4 if (( BASH_VERSINFO[0] < min_bash )); then msg error "Требуется bash версии $min_bash или выше" exit 1 fi msg info "Все зависимости проверены" } check_dependencies # --- Проверка root и логирования --- if [ "$(id -u)" -ne 0 ]; then msg error "Ошибка: скрипт требует root-прав!" exit 1 fi validate_input() { local prompt="$1" local pattern="$2" local error_msg="${3:-"Некорректный ввод!"}" local max_attempts=5 local attempts=0 while (( attempts < max_attempts )); do read -p "$prompt " input if [[ "$input" =~ $pattern ]]; then echo "$input" return 0 else msg error "$error_msg" ((attempts++)) sleep 1 # <- Критично важно! fi done msg error "Превышено максимальное количество попыток" exit 1 } # Ошибка в виртуализации error_cleanup() { # Проверяем существование TPM перед очисткой #if [ -d /sys/class/tpm/tpm0 ] && command -v tpm2_clear >/dev/null; then if [ -d "/sys/class/tpm/tpm0" ] && command -v tpm2_clear >/dev/null; then if tpm2_clear; then msg error "${RED}TPM был очищен из-за критической ошибки${NC}" else msg error "${RED}Не удалось очистить TPM${NC}" fi fi exit 1 } # Проверка места перед обновлением check_disk_space() { local required=($1) local available=$(df --output=avail -BG / | tail -1 | tr -d 'G') if [ "$available" -lt "$required" ]; then msg error "Недостаточно места на диске. Требуется: ${required}G, доступно: ${available}G" return 1 fi return 0 } # Использование: check_disk_space 10 || { msg warn "Недостаточно места для резервного копирования" [ "$STRICT_MODE" = "YES" ] && exit 1 } setup_backup() { msg custom "${GREEN}=== Настройка системы резервного копирования ===${NC}" mkdir -p $BACKUP_DIR/{etc,ssh,ufw,fail2ban,xray} chmod 700 $BACKUP_DIR # Копирование критичных конфигов if [ -f /etc/ssh/sshd_config ]; then cp -a /etc/ssh/sshd_config "$BACKUP_DIR/ssh/" || { msg error "Ошибка копирования sshd_config" exit 1 } else msg warn "Файл /etc/ssh/sshd_config не найден" fi critical_files=( "/etc/ufw/user.rules" "/etc/fail2ban/jail.local" "/etc/passwd" "/etc/shadow" ) for file in "${critical_files[@]}"; do if [ ! -f "$file" ]; then msg warn "Критичный файл не найден: $file" [ "$STRICT_MODE" = "YES" ] && exit 1 fi done # Настройка автоматического бэкапа cat > /etc/cron.d/backup <> $BACKUP_DIR/backup.log 2>&1 0 4 * * * root find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete EOL # Установка инструментов шифрования install_package "openssl" install_package "gnupg" msg info "Система резервного копирования настроена" } # Настройка пользователя create_new_user() { msg custom "\n${GREEN}=== Настройка пользователя ===${NC}" NEW_USER=$(validate_input "Введите имя нового пользователя: ") if ! grep -q '^sudo:' /etc/group; then groupadd sudo msg warn "Создана группа sudo" fi adduser --gecos "" "$NEW_USER" usermod -aG sudo "$NEW_USER" # Запретить вход в систему для пользователя root if ! user_confirm "Запретить вход в систему для пользователя root?"; then return 0 fi usermod -s /usr/sbin/nologin root } create_new_user # --- Настройка sudo --- msg custom "\n${GREEN}=== Настройка sudo ===${NC}" echo "$NEW_USER ALL=(ALL) PASSWD: ALL, !/bin/su, !/usr/bin/passwd root" > /etc/sudoers.d/secure # Настройка PATH в .bashrc echo 'export PATH="$PATH:/usr/sbin:/sbin"' >> /home/"$NEW_USER"/.bashrc chmod 440 /etc/sudoers.d/secure msg info "Новый администратор $NEW_USER успешно установлен." # Создаем .ssh директорию (если её нет) mkdir -p /home/$NEW_USER/.ssh # Устанавливаем БЕЗОПАСНЫЕ права #chmod 700 /home/$NEW_USER/.ssh #chmod 600 /home/$NEW_USER/.ssh/authorized_keys chown -R $NEW_USER:$NEW_USER /home/$NEW_USER/.ssh sleep 3 # Валидация SSH-порта validate_ssh_port() { local port while true; do read -p "Введите SSH порт (1024-65535, рекомендуется не 22): " port #port=${port:-22} # Значение по умолчанию # Проверка что это число if [[ ! "$port" =~ ^[0-9]+$ ]]; then msg error "Порт должен быть числом!" continue fi # Проверка диапазона if ((port < 1024 || port > 65535)); then msg error "Порт должен быть между 1024 и 65535" continue fi # Проверка занятости порта if netstat -tuln | grep -q ":$port "; then msg warn "Порт $port уже используется!" if ! user_confirm "Продолжить с этим портом?"; then continue fi fi echo "$port" break done } SSH_PORT=$(validate_ssh_port) # Настройка SSH msg custom "\n${GREEN}=== Настройка SSH ===${NC}" sed -i "s/#Port 22/Port $SSH_PORT/" /etc/ssh/sshd_config sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config sed -i 's/#PermitEmptyPasswords no/PermitEmptyPasswords no/' /etc/ssh/sshd_config sed -i 's/#ChallengeResponseAuthentication no/ChallengeResponseAuthentication no/' /etc/ssh/sshd_config sed -i 's/#UsePAM yes/UsePAM yes/' /etc/ssh/sshd_config cat >> /etc/ssh/sshd_config <> /etc/profile echo 'readonly TMOUT' >> /etc/profile echo 'export TMOUT' >> /etc/profile # Ограничиваем алгоритмы шифрования if [ "$SECURITY_MODE" = "paranoid" ]; then cat >> /etc/ssh/sshd_config <> /etc/ssh/sshd_config msg info "Доступ ограничен для IP: $USER_IP" # Обновление UFW ufw delete allow $SSH_PORT/tcp 2>/dev/null ufw allow from $USER_IP to any port $SSH_PORT proto tcp msg info "UFW обновлен для разрешения только с $USER_IP" break else msg error "Неверный формат IP. Попробуйте ещё раз." fi done else msg warn "Доступ по SSH не ограничен по IP" msg warn "Рекомендуется использовать VPN или jump-сервер" fi fi ############################# УСТАНОВКА DNS-CRYPT ############################# # Установка и настройка Cloudflare WARP setup_warp() { # Запрос на установку с предупреждением msg custom "\n${YELLOW}=== Cloudflare WARP ===${NC}" msg warn "Внимание: Сервис может быть недоступен в вашей стране!" msg warn "Для работы WARP требуется аккаунт Cloudflare (бесплатный)." msg info "WARP обеспечит:" msg info "- Шифрование интернет-трафика" msg info "- Защиту от MITM-атак" msg info "- Возможность обхода некоторых ограничений" if ! user_confirm "Установить Cloudflare WARP? (y/N) " "N"; then msg info "Пропуск установки Cloudflare WARP" return 0 fi # Проверка доступности Cloudflare if ! curl -s --connect-timeout 5 https://cloudflare.com >/dev/null; then msg warn "Cloudflare недоступен. WARP может не работать!" if ! user_confirm "Всё равно продолжить?" "N"; then return 0 fi fi # Установка репозитория и ключа curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | sudo gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg echo "deb [arch=amd64 signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ bookworm main" | sudo tee /etc/apt/sources.list.d/cloudflare-client.list safe_exec apt update && safe_exec apt install -y cloudflare-warp # Запуск и ожидание инициализации WARP systemctl enable --now warp-svc sleep 5 # Увеличиваем время ожидания # Проверка статуса сервиса if ! systemctl is-active --quiet warp-svc; then msg error "Сервис WARP не запустился!" journalctl -u warp-svc -n 10 --no-pager return 1 fi # Регистрация (новый синтаксис) if ! warp-cli registration status | grep -q "Active"; then warp-cli registration create || { msg error "Ошибка регистрации WARP" msg warn "1. Откройте в браузере: https://1.1.1.1/" msg warn "2. Выполните вручную: warp-cli registration create" return 1 } fi # Настройка параметров (новый синтаксис) warp-cli connect || msg warn "Не удалось подключиться" warp-cli set-mode proxy || msg warn "Не удалось установить режим прокси" warp-cli set-families restricted || msg warn "Не удалось ограничить сети" warp-cli set-proxy-port 40000 || msg warn "Не удалось установить порт" # Проверка порта if ss -tuln | grep -q ':40000 '; then msg warn "Порт 40000 занят! Выберите другой порт." return 1 fi # Разрешение порта в UFW ufw allow 40000/tcp >/dev/null 2>&1 msg info "Cloudflare Warp настроен (прокси на порту 40000)" } # Списки блокировок для DNS фильтров # Заменяем текущий блок создания списков на этот: # Функция создания базовых списков setup_dns_lists() { if [ "$SECURITY_MODE" = "paranoid" ]; then default_answer="Y" else default_answer="N" fi if user_confirm "Установить базовые списки блокировки? (Рекомендуется для paranoid режима)" "$default_answer"; then msg info "Доступные варианты блокировки:" msg info "1. Минимальный (соцсети и реклама)" msg info "2. Средний (минимальный + трекеры)" msg info "3. Полный (все выше + опасные домены)" local choice while true; do read -p "Выберите уровень блокировки (1-3, по умолчанию 2): " choice choice=${choice:-2} case $choice in 1) # Минимальный список cat > /etc/dnscrypt-proxy/blacklist.txt <<'EOL' # Соцсети и реклама facebook.com instagram.com tiktok.com twitter.com youtube.com ads.* ad.* tracking.* analytics.* EOL ;; 2) # Средний список cat > /etc/dnscrypt-proxy/blacklist.txt <<'EOL' # Соцсети, реклама и трекеры facebook.com instagram.com tiktok.com twitter.com youtube.com ads.* ad.* track.* tracking.* analytics.* metrics.* doubleclick.net googleadservices.com googletagmanager.com EOL ;; 3) # Полный список cat > /etc/dnscrypt-proxy/blacklist.txt <<'EOL' # Полная защита: соцсети, реклама, трекеры, опасные домены facebook.com instagram.com tiktok.com twitter.com youtube.com ads.* ad.* track.* tracking.* analytics.* metrics.* doubleclick.net googleadservices.com googletagmanager.com malware.* phishing.* spam.* scam.* exploit.* coin-miner.* cryptocurrency.* EOL ;; *) msg warn "Неверный выбор, используем уровень 2" continue ;; esac break done # Базовый whitelist (исключения) cat > /etc/dnscrypt-proxy/whitelist.txt <<'EOL' # Исключения из блокировки *.microsoft.com *.windowsupdate.com *.ubuntu.com *.debian.org *.github.com *.gitlab.com EOL # IP-блокировка (известные вредоносные сети) cat > /etc/dnscrypt-proxy/ip-blacklist.txt <<'EOL' # Вредоносные IP-сети 5.5.5.0/24 10.0.0.0/8 192.168.0.0/16 45.58.0.0/17 # Известная ботнет-сеть 185.56.0.0/16 # Вредоносные хосты EOL # Установка прав chmod 600 /etc/dnscrypt-proxy/*.txt chown nobody:nogroup /etc/dnscrypt-proxy/*.txt msg info "Установлены списки блокировки уровня $choice" else # Создаем пустые файлы, если пользователь отказался touch /etc/dnscrypt-proxy/{blacklist,whitelist,ip-blacklist}.txt chmod 600 /etc/dnscrypt-proxy/*.txt chown nobody:nogroup /etc/dnscrypt-proxy/*.txt msg info "Списки блокировки не установлены (используются пустые файлы)" fi } check_dnscrypt_config() { local config_file=$1 local work_dir="/etc/dnscrypt-proxy" # Создаем временную копию в рабочей директории local temp_check="${work_dir}/temp-check.toml" cp "$config_file" "$temp_check" chown nobody:nogroup "$temp_check" chmod 644 "$temp_check" # Проверяем с явным указанием рабочей директории if ! (cd "$work_dir" && /usr/sbin/dnscrypt-proxy -check -config "$temp_check"); then rm -f "$temp_check" return 1 fi rm -f "$temp_check" return 0 } get_dns_stamp() { local server=$1 local stamp="" # Резервные stamp-идентификаторы declare -A fallback_stamps=( ["cloudflare"]="sdns://AgcAAAAAAAAADjEwNC4xNi4yNDguMjQ5ABJjbG91ZGZsYXJlLWRucy5jb20KL2Rucy1xdWVyeQ" ["quad9-doh-ip4-filter-pri"]="sdns://AgMAAAAAAAAABzEuMC4wLjGgENk8mGSlIfMGXMOlIlCcKvq7AVgcrZxtjon911-ep1cgYWx0YS1kbnMucXVhZDkubmV0Ci9kbnMtcXVlcnk" ["cloudflare-security"]="sdns://AgMAAAAAAAAABzEuMC4wLjIABzEuMC4wLjIKL2Rucy1xdWVyeQ" ["dns.sb"]="sdns://AgcAAAAAAAAADzE4NS4yMjIuMjIyLjIyMiAOp5Svj-oV-Fz-65-8H2VKHLKJ0egmfEgrdPeAQlUFFA8xODUuMjIyLjIyMi4yMjIKL2Rucy1xdWVyeQ" ) # Пытаемся получить актуальный stamp из GitHub echo >&2 "[ℹ] Пытаемся получить актуальный stamp для $server из GitHub..." if ! curl -s --max-time 5 "https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md" >/dev/null; then echo >&2 "[⚠] Внимание: Не удалось подключиться к GitHub. Причины:" echo >&2 " 1. Нет интернет-соединения" echo >&2 " 2. GitHub временно недоступен" echo >&2 " 3. Проблемы с DNS" echo >&2 " Используем резервный stamp из локального кэша" else stamp=$(curl -s --max-time 5 "https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md" | awk -v server="$server" ' BEGIN {IGNORECASE=1} /^## / {current_section=$2; next} current_section ~ server && /^sdns:\/\// {print; exit} ' | head -n1) fi # Если не получили stamp из GitHub, используем резервный if [ -z "$stamp" ]; then echo >&2 "[ℹ] Используем резервный stamp для $server" stamp="${fallback_stamps[$server]}" if [ -z "$stamp" ]; then echo >&2 "[✖] Критическая ошибка: Нет резервного stamp для $server" return 1 fi fi # Финалная проверка формата if [[ ! "$stamp" =~ ^sdns://[A-Za-z0-9+/]+[=]{0,2}$ ]]; then echo >&2 "[✖] Ошибка: Некорректный формат stamp для $server" return 1 fi echo "$stamp" } setup_dnscrypt_service() { # Основной конфиг сервиса cat > /etc/systemd/system/dnscrypt-proxy.service <<'EOL' [Unit] Description=DNSCrypt Proxy After=network.target Documentation=https://github.com/DNSCrypt/dnscrypt-proxy StartLimitIntervalSec=30 StartLimitBurst=5 [Service] Type=simple User=nobody Group=nogroup WorkingDirectory=/etc/dnscrypt-proxy RuntimeDirectory=dnscrypt-proxy RuntimeDirectoryMode=0750 ExecStart=/usr/sbin/dnscrypt-proxy -config /etc/dnscrypt-proxy/dnscrypt-proxy.toml ExecReload=/bin/kill -HUP $MAINPID Restart=on-failure RestartSec=5s TimeoutStartSec=30 TimeoutStopSec=5 # Security ProtectSystem=full ProtectHome=true PrivateTmp=true PrivateDevices=true ProtectKernelTunables=true ProtectKernelModules=true ProtectControlGroups=true NoNewPrivileges=true CapabilityBoundingSet=CAP_NET_BIND_SERVICE MemoryDenyWriteExecute=true LockPersonality=true RestrictAddressFamilies=AF_INET AF_INET6 RestrictNamespaces=true RestrictRealtime=true SystemCallFilter=@system-service SystemCallArchitectures=native DevicePolicy=closed DeviceAllow=/dev/null rw DeviceAllow=/dev/urandom r UMask=0077 # Directories ReadWritePaths=/etc/dnscrypt-proxy /var/log/dnscrypt-proxy StateDirectory=dnscrypt-proxy CacheDirectory=dnscrypt-proxy LogsDirectory=dnscrypt-proxy ConfigurationDirectory=dnscrypt-proxy [Install] WantedBy=multi-user.target EOL # Обязательные действия после создания service файла: mkdir -p /etc/systemd/system/dnscrypt-proxy.service.d cat > /etc/systemd/system/dnscrypt-proxy.service.d/override.conf <<'EOL' [Service] WorkingDirectory=/etc/dnscrypt-proxy ReadWritePaths=/etc/dnscrypt-proxy /var/log/dnscrypt-proxy # Доп. параметры для paranoid режима ProtectControlGroups=true RestrictNamespaces=true RestrictRealtime=true RemoveIPC=true UMask=0077 EOL systemctl reset-failed dnscrypt-proxy sleep 5 # Дополнительные настройки для paranoid режима if [ "$SECURITY_MODE" = "paranoid" ]; then if ! cat >> /etc/systemd/system/dnscrypt-proxy.service <<'EOL' ProtectKernelTunables=yes ProtectKernelModules=yes ProtectControlGroups=yes RestrictRealtime=yes RemoveIPC=yes RestrictNamespaces=uts ipc pid net UMask=0077 EOL then msg error "Не удалось добавить paranoid-настройки" return 1 fi fi } # Установка и настройки DNS защиты setup_dns_protection() { msg custom "\n${GREEN}=== Настройка DNS защиты ===${NC}" # Запрос на установку msg info "dnscrypt-proxy обеспечит:" msg info "- Шифрование DNS-запросов" msg info "- Защиту от MITM-атак" msg info "- Использование CloudFlare DNS или Quad9 (фильтрация вредоносных доменов)" if ! user_confirm "Установить пакет dnscrypt-proxy?"; then msg warn "DNS защита не будет настроена" return 0 fi msg info "=== Информация о системе ===" msg info "Версия системы: $(uname -a)" msg info "Версия Go: $(go version 2>/dev/null || echo "не установлен")" msg info "Версия curl: $(curl --version 2>/dev/null | head -n1 || echo "не установлен")" msg info "===========================" # Генерация случайного порта (исключая 53) while true; do DPORT=$((RANDOM % 64511 + 10000)) [[ $DPORT -ne 53 ]] && break done # Проверка порта 53 if ss -uln | grep -q ':53 ' && [ "$(ss -uln sport = :53 | wc -l)" -gt 1 ]; then export DNS_PORT=$DPORT msg info "Для интеграции с systemd-resolved используем порт: $DNS_PORT" fi # Устанавливаем рабочий каталог DNSCRYPT_WORKDIR="/etc/dnscrypt-proxy" DNSCRYPT_WORKDIR_LOG="/var/log/dnscrypt-proxy" mkdir -p "$DNSCRYPT_WORKDIR" "$DNSCRYPT_WORKDIR_LOG" chown -R nobody:nogroup "$DNSCRYPT_WORKDIR" "$DNSCRYPT_WORKDIR_LOG" chmod 755 "$DNSCRYPT_WORKDIR" chmod 750 "$DNSCRYPT_WORKDIR_LOG" local installed=0 trap 'rm -rf "$temp_dir"' EXIT INT TERM # 1. Попытка установки из репозиториев if install_package "dnscrypt-proxy"; then installed=1 else # 2. Попытка установки из .deb msg warn "Поиск последней версии в Debian Sid..." deb_url="http://ftp.debian.org/debian/pool/main/d/dnscrypt-proxy/" latest_deb=$(curl -s "$deb_url" | grep -oP 'dnscrypt-proxy_.*?amd64\.deb' | sort -V | tail -n1) if [ -n "$latest_deb" ]; then msg info "Найдена версия: $latest_deb" temp_dir=$(mktemp -d) cd "$temp_dir" || { rm -rf "$temp_dir"; return 1; } if wget "$deb_url$latest_deb" -O dnscrypt-proxy.deb 2>/dev/null && \ dpkg -i dnscrypt-proxy.deb && \ apt-get install -f -y; then # Добавьте эту проверку if [ -f "/usr/sbin/dnscrypt-proxy" ] && /usr/sbin/dnscrypt-proxy --version; then installed=1 msg info "Установлено: $(/usr/sbin/dnscrypt-proxy --version)" else msg warn "Пакет установился, но не работает корректно" fi fi rm -rf "$temp_dir" fi fi # 3. Установка из исходников (если предыдущие способы не сработали) if [ "$installed" -eq 0 ]; then msg warn "Установка из исходников..." if [ -f "/usr/sbin/dnscrypt-proxy" ]; then rm -f "/usr/sbin/dnscrypt-proxy" fi # Проверяем и устанавливаем Go if ! command -v go >/dev/null; then msg info "Установка Go..." if ! apt-get install -y --no-install-recommends golang || ! apt-get install -y --no-install-recommends golang-go; then msg error "Ошибка установки Go" return 1 fi export PATH="$PATH:/usr/lib/go/bin" fi # Проверяем версию Go go_min_version=1.16 if ! go version | awk '{print $3}' | sed 's/go//' | awk -v min="$go_min_version" '$1 < min {exit 1}'; then msg error "Требуется Go версии не ниже $go_min_version" msg info "Попытка установки последней версии Go..." if ! apt-get install -y --no-install-recommends golang-go; then msg error "Не удалось установить подходящую версию Go" return 1 fi fi # Скачиваем исходники msg info "Поиск последней версии на GitHub..." latest_version=$(curl -s https://api.github.com/repos/DNSCrypt/dnscrypt-proxy/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') if [ -z "$latest_version" ]; then msg error "Не удалось определить версию" return 1 fi msg info "Скачивание $latest_version..." temp_dir=$(mktemp -d) cd "$temp_dir" || { rm -rf "$temp_dir"; return 1; } if ! wget "https://github.com/DNSCrypt/dnscrypt-proxy/archive/refs/tags/${latest_version}.tar.gz" -O dnscrypt-proxy.tar.gz; then msg error "Ошибка загрузки" rm -rf "$temp_dir" return 1 fi tar xzf dnscrypt-proxy.tar.gz || { rm -rf "$temp_dir"; return 1; } # Компиляция msg info "Компиляция..." cd "dnscrypt-proxy-${latest_version#v}/dnscrypt-proxy" || { rm -rf "$temp_dir"; return 1; } # Исправление версии Go в go.mod (если требуется) if [ -f "go.mod" ]; then sed -i 's/go 1.*/go 1.20/' go.mod fi if ! go build -o dnscrypt-proxy; then msg error "Ошибка компиляции" rm -rf "$temp_dir" return 1 fi # Установка msg info "Установка..." install -Dm755 dnscrypt-proxy /usr/sbin/dnscrypt-proxy install -Dm644 example-dnscrypt-proxy.toml /etc/dnscrypt-proxy/dnscrypt-proxy.toml chown nobody:nogroup /etc/dnscrypt-proxy/dnscrypt-proxy.toml chmod 644 /etc/dnscrypt-proxy/dnscrypt-proxy.toml if [ ! -f "/etc/dnscrypt-proxy/dnscrypt-proxy.toml" ]; then msg error "Конфигурационный файл не найден!" if [ -f "/usr/share/dnscrypt-proxy/example-dnscrypt-proxy.toml" ]; then cp /usr/share/dnscrypt-proxy/example-dnscrypt-proxy.toml /etc/dnscrypt-proxy/dnscrypt-proxy.toml chown nobody:nogroup /etc/dnscrypt-proxy/dnscrypt-proxy.toml chmod 644 /etc/dnscrypt-proxy/dnscrypt-proxy.toml else msg error "Пример конфига не найден в /usr/share/dnscrypt-proxy/" return 1 fi fi rm -rf "$temp_dir" installed=1 msg info "Установка dnscrypt-proxy завершена (версия $latest_version)" fi ########################## if ! [ -f "/usr/sbin/dnscrypt-proxy" ]; then msg error "Основной бинарник не найден в /usr/sbin/dnscrypt-proxy" return 1 fi # Проверка installed перед настройкой systemd if [ "$installed" -eq 1 ]; then if /usr/sbin/dnscrypt-proxy --version 2>/dev/null; then msg info "Версия dnscrypt-proxy: $(/usr/sbin/dnscrypt-proxy --version)" else msg warn "Не удалось проверить версию dnscrypt-proxy" fi # Управление службами systemd (для всех способов установки) if command -v systemctl >/dev/null; then # Остановка systemd-resolved if systemctl is-active --quiet systemd-resolved; then systemctl stop systemd-resolved sleep 3 systemctl disable systemd-resolved msg info "systemd-resolved отключен" fi # Настройка dnscrypt-proxy if [ -f "/lib/systemd/system/dnscrypt-proxy.service" ]; then systemctl daemon-reload >/dev/null systemctl enable dnscrypt-proxy >/dev/null systemctl restart dnscrypt-proxy >/dev/null msg info "Служба dnscrypt-proxy настроена и запущена" else msg warn "Файл службы dnscrypt-proxy не найден, необходимо настроить вручную" fi else msg warn "Systemd не обнаружен, необходимо настроить службу вручную" fi # Настройка конфигурации mkdir -p /etc/dnscrypt-proxy chown nobody:nogroup /etc/dnscrypt-proxy # Настройка логов chown nobody:nogroup /var/log/dnscrypt-proxy # Выбор серверов с фильтрацией # Список доступных серверов с описанием available_servers=( "1:cloudflare:Быстрый DNS от Cloudflare" "2:quad9-doh-ip4-filter-pri:Quad9 с фильтрацией угроз" "3:cloudflare-security:Cloudflare с защитой от угроз" "4:dns.sb:Безлогирующий DNS-сервер" ) # Показываем меню выбора msg info "Доступные DNS-серверы:" for server in "${available_servers[@]}"; do IFS=':' read -r num name desc <<< "$server" msg info "$num. $name - $desc" done # Запрос выбора while true; do read -p "Укажите номера серверов через пробел (первый - основной): " choices # Проверяем ввод if [[ ! "$choices" =~ ^[1-4]([[:space:]]+[1-4])*$ ]]; then msg warn "Неверный ввод. Используйте номера 1-4 через пробел." continue fi # Преобразуем ввод в массив серверов selected_servers=() for choice in $choices; do for server in "${available_servers[@]}"; do IFS=':' read -r num name _ <<< "$server" if [ "$num" == "$choice" ]; then selected_servers+=("$name") break fi done done [ ${#selected_servers[@]} -gt 0 ] && break msg warn "Не выбрано ни одного сервера" done msg info "Выбраны серверы (в порядке приоритета): ${selected_servers[*]}" # Формируем список серверов для server_names server_names_list=$(printf "'%s'," "${selected_servers[@]}" | sed 's/,$//') # Создаем временный конфиг с базовой структурой temp_conf=$(mktemp) || { msg error "Не удалось создать временный файл"; return 1; } cat > "$temp_conf" <&2 "[✔] Добавляем сервер: $server" cat >> "$temp_conf" <&2 "[✖] Не удалось получить stamp для $server, сервер пропущен" fi done # Обновляем server_names if [ ${#valid_servers[@]} -gt 0 ]; then # Формируем строку с серверами servers=$(printf "'%s', " "${valid_servers[@]}" | sed 's/, $//') # Обновляем конфиг sed -i "/server_names = /c\server_names = [$servers]" "$temp_conf" else echo >&2 "[✖] Нет валидных серверов для добавления" rm -f "$temp_conf" return 1 fi # Формируем правильный список серверов для TOML if [ ${#valid_servers[@]} -gt 0 ]; then # Создаем временный файл для sed tmp_sed=$(mktemp) # Формируем строку с серверами printf -v servers "'%s', " "${valid_servers[@]}" servers=${servers%, } # Удаляем последнюю запятую # Создаем правильную замену для sed cat > "$tmp_sed" <&2 "[✖] Нет валидных серверов для добавления" return 1 fi # Проверяем, что хотя бы один сервер добавлен if [ ${#valid_servers[@]} -eq 0 ]; then msg error "Не удалось получить stamp'ы ни для одного сервера!" msg info "Возможные причины:" msg info "1. Отсутствует интернет-соединение" msg info "2. Проблемы с доступом к GitHub" msg info "3. Выбранные серверы недоступны" msg info "4. Нет резервных stamp для выбранных серверов" rm -f "$temp_conf" return 1 fi # Обновляем server_names только с валидными серверами #sed -i "s/server_names = .*/server_names = ['$(IFS="', '"; echo "${valid_servers[*]}")']/" "$temp_conf" servers_list=$(printf "'%s', " "${valid_servers[@]}" | sed 's/, $//') sed -i "s|server_names = .*|server_names = [$servers_list]|" "$temp_conf" echo "Установлено значение: server_names = ['$(IFS="', '"; echo "${valid_servers[*]}")']" # Применяем новый конфиг cp "$temp_conf" /etc/dnscrypt-proxy/dnscrypt-proxy.toml chown nobody:nogroup /etc/dnscrypt-proxy/dnscrypt-proxy.toml chmod 644 /etc/dnscrypt-proxy/dnscrypt-proxy.toml rm -rf "$temp_conf_dir" msg info "\n=== Этап: Настройка systemd ===" # Cоздание systemd сервиса setup_dnscrypt_service if ! grep -q 'listen_addresses' /etc/dnscrypt-proxy/dnscrypt-proxy.toml; then sed -i "s/^# listen_addresses/listen_addresses = ['127.0.0.1:$DNS_PORT']\n# listen_addresses/" /etc/dnscrypt-proxy/dnscrypt-proxy.toml fi sleep 2 # Применяем конфиг if ! install -m 644 -o nobody -g nogroup "$temp_conf" "/etc/dnscrypt-proxy/dnscrypt-proxy.toml"; then msg error "Ошибка применения конфигурации" rm -f "$temp_conf" return 1 fi rm -f "$temp_conf" msg info "Конфигурация успешно применена" # Убедимся, что рабочий каталог правильный cd /etc/dnscrypt-proxy || { msg error "Ошибка перехода в рабочий каталог"; return 1; } sed -i "s/^listen_addresses = .*/listen_addresses = ['127.0.0.1:$DNS_PORT']/" /etc/dnscrypt-proxy/dnscrypt-proxy.toml # Фиксируем рабочий каталог для dnscrypt-proxy mkdir -p /etc/systemd/system/dnscrypt-proxy.service.d cat > /etc/systemd/system/dnscrypt-proxy.service.d/workingdir.conf < /etc/systemd/resolved.conf.d/dnscrypt.conf </dev/null rm -f /etc/resolv.conf ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf # 3. Перезапускаем resolved systemctl enable systemd-resolved systemctl restart systemd-resolved msg info "systemd-resolved настроен для использования dnscrypt-proxy" # Активация resolved systemctl enable systemd-resolved systemctl restart systemd-resolved sleep 3 ############################################# # 1. Настройка списков блокировки setup_dns_lists # Если файла нет — создаём if [ ! -f /etc/resolv.conf ]; then touch /etc/resolv.conf msg info "Файл /etc/resolv.conf создан" fi echo "nameserver 127.0.0.1" > /etc/resolv.conf # Пытаемся заблокировать файл от изменений if chattr +i /etc/resolv.conf 2>/dev/null; then msg info "DNS-настройки успешно заблокированы" else msg warn "Не удалось заблокировать /etc/resolv.conf" fi # 4. Применение изменений systemctl enable dnscrypt-proxy systemctl stop dnscrypt-proxy 2>/dev/null systemctl daemon-reload || { msg error "Ошибка перезагрузки systemd" return 1 } sleep 2 systemctl restart systemd-resolved # 5. Запуск службы if ! systemctl start dnscrypt-proxy; then msg warn "Первая попытка запуска не удалась, пробуем снова..." systemctl stop dnscrypt-proxy 2>/dev/null sleep 2 systemctl start dnscrypt-proxy || { msg error "Не удалось запустить dnscrypt-proxy" journalctl -u dnscrypt-proxy -n 50 --no-pager return 1 } fi # 6. Финальная проверка sleep 4 # Даем время для запуска if ! systemctl is-active --quiet dnscrypt-proxy; then msg error "Ошибка запуска dnscrypt-proxy!" journalctl -u dnscrypt-proxy -n 20 --no-pager return 1 else msg info "DNS защита успешно настроена с использованием серверов: ${selected_servers[*]}" return 0 fi else msg error "Не удалось установить dnscrypt-proxy ни одним из способов!" return 1 fi msg info "Проверка работы системы DNS..." if ! resolvectl query google.com | grep -q 'google.com'; then msg warn "Проблема с systemd-resolved, проверяем прямое соединение..." if ! dig @127.0.0.1 -p $DNS_PORT google.com +short; then msg error "Ошибка в работе dnscrypt-proxy" return 1 else msg info "dnscrypt-proxy работает, проблема в интеграции со systemd-resolved" fi else msg info "Система DNS работает корректно через systemd-resolved и dnscrypt-proxy" fi } # --- Настройка DNS защиты --- if [ "$SECURITY_MODE" = "paranoid" ]; then setup_dns_protection setup_warp fi ############################# УСТАНОВКА DNS-CRYPT ############################# # Добавляем security-репозиторий (если ещё не добавлен) if ! grep -q "debian-security" /etc/apt/sources.list; then echo "deb http://security.debian.org/debian-security $(lsb_release -sc)-security main" | tee -a /etc/apt/sources.list fi system_auto_update() { msg custom "\n${GREEN}=== Настройка автоматических обновлений ===${NC}" if ! user_confirm "Настроить автоматическое обновление системы?"; then systemctl disable --now unattended-upgrades 2>/dev/null || true msg info "Автоматические обновления отключены" return 0 fi # Установка необходимых пакетов if ! command -v unattended-upgrades &> /dev/null; then msg error "unattended-upgrades не установлен!" sleep 2 msg warn "Устанавливаем unattended-upgrades..." # Обновляем список пакетов сначала if ! apt-get update; then msg error "Ошибка при обновлении списка пакетов" [ "$STRICT_MODE" = "YES" ] && return 1 fi # Устанавливаем пакеты по отдельности #for pkg in unattended-upgrades; do if ! apt-get install -y "$pkg"; then msg error "Не удалось установить пакет $pkg" # Проверяем доступен ли пакет в репозиториях if ! apt-cache show "$pkg" &> /dev/null; then msg warn "Пакет $pkg не найден в репозиториях" fi [ "$STRICT_MODE" = "YES" ] && return 1 fi #done fi # Определение дистрибутива local distro_id distro_codename if [ -f /etc/os-release ]; then distro_id=$(grep '^ID=' /etc/os-release | cut -d= -f2 | tr -d '"') distro_codename=$(grep '^VERSION_CODENAME=' /etc/os-release | cut -d= -f2) else distro_id="debian" # Fallback для старых систем distro_codename=$(lsb_release -cs) fi # 1. Выбор времени обновления local update_time while true; do read -p "Введите время для автоматических обновлений (ЧЧ:ММ) [по умолчанию 04:00]: " update_time update_time=${update_time:-"04:00"} if [[ $update_time =~ ^([0-1][0-9]|2[0-3]):[0-5][0-9]$ ]]; then break else msg error "Неверный формат времени. Используйте ЧЧ:ММ (например, 04:00)" fi done # 2. Выбор типа обновлений local origins if user_confirm "Включать только обновления безопасности (рекомендуется)?"; then origins=( "${distro_id}:${distro_codename}-security" "${distro_id}-security:${distro_codename}" ) else origins=( "${distro_id}:${distro_codename}" "${distro_id}:${distro_codename}-security" "${distro_id}:${distro_codename}-updates" ) fi # 3. Настройка автоматической перезагрузки local automatic_reboot="false" if user_confirm "Разрешить автоматическую перезагрузку при необходимости?"; then automatic_reboot="true" install_package "needrestart" fi # 4. Черный список пакетов local blacklist_config="" if user_confirm "Добавить пакеты в черный список автообновлений?"; then read -p "Введите названия пакетов через пробел: " blacklist_pkgs if [ -n "$blacklist_pkgs" ]; then blacklist_config="Unattended-Upgrade::Package-Blacklist {\n" for pkg in $blacklist_pkgs; do blacklist_config+=" \"$pkg\";\n" done blacklist_config+="};\n" fi fi # Генерация конфигурационного файла cat > /etc/apt/apt.conf.d/50unattended-upgrades <> /etc/apt/apt.conf.d/50unattended-upgrades fi # Активация службы if [[ -f /lib/systemd/system/unattended-upgrades.service ]]; then systemctl enable --now unattended-upgrades elif [[ -f /lib/systemd/system/unattended-upgrades.timer ]]; then systemctl enable --now unattended-upgrades.timer else msg warn "Создаем минимальную службу unattended-upgrades..." cat > /etc/systemd/system/unattended-upgrades.service < /etc/cron.d/unattended-upgrades chmod 644 /etc/cron.d/unattended-upgrades fi # Финальная проверка активности sleep 2 # Даем время для активации if systemctl is-active --quiet unattended-upgrades* || \ (grep -qi 'debian' /etc/os-release && [ -f /etc/cron.d/unattended-upgrades ]); then msg info "Служба автоматических обновлений успешно активирована" else msg warn "Автоматические обновления настроены, но не активированы как служба" msg warn "Обновления будут выполняться при каждом запуске apt" fi } system_auto_update # --- Настройка Fail2Ban --- msg custom "\n${GREEN}=== Настройка Fail2Ban ===${NC}" touch /var/log/auth.log chmod 700 /var/log/auth.log cat > /etc/fail2ban/jail.d/ssh.conf </dev/null; then msg info "Установка SELinux..." safe_exec apt install -y selinux-basics selinux-policy-default auditd fi current_status=$(getenforce 2>/dev/null) case "$current_status" in "Disabled") msg info "Активация SELinux (требуется перезагрузка)..." mkdir -p /etc/selinux echo "SELINUX=enforcing" > /etc/selinux/config echo "SELINUXTYPE=default" >> /etc/selinux/config msg warn "SELinux будет активирован после перезагрузки" sleep 3 touch /.autorelabel ;; "Permissive") msg info "Перевод SELinux в enforcing режим..." sed -i 's/SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config setenforce 1 ;; "Enforcing") msg info "SELinux уже в enforcing режиме" ;; *) msg warn "Неизвестное состояние SELinux" ;; esac # Настройка дополнительных компонентов if [ "$current_status" != "Disabled" ]; then # Установка утилит для разработки политик (если нужно) if ! command -v sealert >/dev/null; then safe_exec apt install -y policycoreutils-python-utils setools-console fi # Генерация политик if [ -f /var/log/audit/audit.log ]; then msg info "Генерация политик SELinux..." sealert -a /var/log/audit/audit.log | audit2allow -M mypolicy semodule -i mypolicy.pp else msg warn "Файл аудита не найден, политики не сгенерированы" fi # Рекомендации msg custom "${YELLOW}Рекомендации по SELinux:${NC} • Проверьте логи аудита: ${BLUE}ausearch -m AVC -ts recent${NC} • Для диагностики: ${BLUE}sealert -a /var/log/audit/audit.log${NC} • ${RED}После первой активации система может потребовать перезагрузки${NC}" fi fi ########################################################################## # Единая функция настройки GRUB configure_grub() { local grub_file="/etc/default/grub" local changes_made=0 # Проверяем существование файла if [ ! -f "$grub_file" ]; then msg warn "Файл $grub_file не найден, настройки GRUB не применены" return 1 fi # Создаем резервную копию cp "$grub_file" "${grub_file}.bak" # 1. Настройка module_blacklist (базовая защита) if ! grep -q 'module_blacklist=' "$grub_file"; then sed -i 's/GRUB_CMDLINE_LINUX=.*/GRUB_CMDLINE_LINUX="module_blacklist=usb_storage,thunderbolt,firewire_core"/' "$grub_file" changes_made=1 msg info "Добавлены module_blacklist параметры" fi # 2. Дополнительные параметры для параноидального режима if [ "$SECURITY_MODE" = "paranoid" ]; then if ! grep -q "nosmt" "$grub_file"; then sed -i '/GRUB_CMDLINE_LINUX=/ s/"$/ nosmt"/' "$grub_file" changes_made=1 msg info "Добавлен параметр nosmt" fi if ! grep -q "mitigations=auto" "$grub_file"; then sed -i '/GRUB_CMDLINE_LINUX=/ s/"$/ mitigations=auto"/' "$grub_file" changes_made=1 msg info "Добавлен параметр mitigations" fi fi # Обновляем GRUB только если были изменения if [ "$changes_made" -eq 1 ]; then msg info "Применение изменений GRUB..." for i in {1..3}; do if update-grub; then msg info "GRUB успешно обновлен" return 0 else msg warn "Ошибка обновления GRUB (попытка $i/3)" # Восстанавливаем backup при ошибке cp "${grub_file}.bak" "$grub_file" sleep 2 fi done msg error "Не удалось обновить GRUB после 3 попыток" [ "$STRICT_MODE" = "YES" ] && exit 1 return 1 else msg info "Все параметры GRUB уже настроены" return 0 fi } ########################################################################## # --- Настройка аппаратной защиты --- if [ "$SECURITY_MODE" = "paranoid" ]; then msg custom "\n${GREEN}=== Настройка аппаратной защиты ===${NC}" sleep 2 # 0. Обязательно обновляем кеш пакетов if ! apt-get update; then msg error "Ошибка обновления списков пакетов" [ "$STRICT_MODE" = "YES" ] && exit 1 return 0 fi # 1. Определяем систему инициализации RAM-диска INITRAMFS_TOOL="" if command -v update-initramfs >/dev/null && dpkg -l initramfs-tools >/dev/null 2>&1; then INITRAMFS_TOOL="initramfs-tools" msg info "Обнаружен initramfs-tools" elif command -v dracut >/dev/null && dpkg -l dracut-core >/dev/null 2>&1; then INITRAMFS_TOOL="dracut" msg info "Обнаружен dracut" else # Попытка установить один из инструментов с проверкой доступности msg warn "Инструмент initramfs не обнаружен, пробуем установить..." if apt-cache show initramfs-tools >/dev/null 2>&1; then if apt-get install -y --no-install-recommends initramfs-tools; then INITRAMFS_TOOL="initramfs-tools" msg info "Успешно установлен initramfs-tools" fi fi if [ -z "$INITRAMFS_TOOL" ] && apt-cache show dracut-core >/dev/null 2>&1; then if apt-get install -y --no-install-recommends dracut-core; then INITRAMFS_TOOL="dracut" msg info "Успешно установлен dracut-core" fi fi if [ -z "$INITRAMFS_TOOL" ]; then msg error "Не удалось установить ни initramfs-tools, ни dracut-core!" msg warn "Защита на уровне initramfs будет недоступна" [ "$STRICT_MODE" = "YES" ] && exit 1 return 0 fi fi # 2. Настройка чёрного списка модулей (с резервным копированием) if [ ! -f "/etc/modprobe.d/paranoid.conf.bak" ]; then cp /etc/modprobe.d/paranoid.conf /etc/modprobe.d/paranoid.conf.bak 2>/dev/null || true fi cat > /etc/modprobe.d/paranoid.conf <> /etc/initramfs-tools/modules msg info "Добавлен usb-storage в /etc/initramfs-tools/modules" fi ;; "dracut") if [ ! -f "/etc/dracut.conf.d/usb-storage.conf" ]; then echo 'add_drivers+="usb-storage"' > /etc/dracut.conf.d/usb-storage.conf msg info "Создан конфиг для dracut: /etc/dracut.conf.d/usb-storage.conf" fi ;; esac # 3. Обновление initramfs с обработкой ошибок case "$INITRAMFS_TOOL" in "initramfs-tools") if ! update-initramfs -u -k all; then msg error "Ошибка обновления initramfs-tools" [ "$STRICT_MODE" = "YES" ] && exit 1 else msg info "initramfs успешно обновлён с учётом чёрного списка модулей" fi ;; "dracut") if ! dracut --force --regenerate-all; then msg error "Ошибка выполнения dracut" [ "$STRICT_MODE" = "YES" ] && exit 1 else msg info "dracut успешно пересобран с учётом чёрного списка модулей" fi ;; *) msg error "Неизвестный инструмент initramfs: $INITRAMFS_TOOL" [ "$STRICT_MODE" = "YES" ] && exit 1 ;; esac # 4. Дополнительная защита (с проверкой изменений) configure_grub # 5. Проверка результата case "$INITRAMFS_TOOL" in "initramfs-tools") if ! lsinitramfs /boot/initrd.img-$(uname -r) | grep -q 'usb-storage'; then msg warn "Модуль usb-storage не обнаружен в initramfs" else msg info "usb-storage успешно добавлен в initramfs-tools" fi ;; "dracut") if ! lsinitrd /boot/initramfs-$(uname -r).img | grep -q 'usb-storage'; then msg warn "Модуль usb-storage не обнаружен в dracut" else msg info "usb-storage успешно добавлен в dracut" fi ;; esac fi # Блокировка DMA if [ "$SECURITY_MODE" = "paranoid" ]; then echo "blacklist ioatdma" >> /etc/modprobe.d/blacklist.conf dmesg | grep -q 'DMAR:' && echo "rd.driver.blacklist=vfio-pci" >> /etc/default/grub fi # Отключение DMA if [ "$SECURITY_MODE" = "paranoid" ]; then echo "blacklist firewire-ohci" >> /etc/modprobe.d/blacklist.conf echo "blacklist thunderbolt" >> /etc/modprobe.d/blacklist.conf fi # Защита от атак через PCIe if [ "$SECURITY_MODE" = "paranoid" ]; then echo "iommu=on" >> /etc/default/grub update-grub fi # Безопасное управление IPMI secure_ipmi() { if [[ "$SECURITY_MODE" != "paranoid" ]]; then return 0 fi msg info "Настройка безопасного доступа к IPMI..." # Установка ipmitool if ! command -v ipmitool >/dev/null 2>&1; then msg warn "IPMI tools не установлены. Установка..." if ! safe_exec apt-get install -y ipmitool; then msg error "Ошибка установки ipmitool" return 1 fi fi # Проверка доступности IPMI if ! ipmitool lan print >/dev/null 2>&1; then msg warn "IPMI интерфейс не обнаружен" return 0 fi # Генерация и сохранение пароля local ipmi_pass=$(openssl rand -base64 16 | tr -d '=' | tr '+/' '_-') local ipmi_backup_file="/root/ipmi_backup_$(date +%Y%m%d).info" # Сохраняем пароль в защищённый файл { echo "IPMI Backup Information - $(date)" echo "Username: ADMIN" echo "Password: $ipmi_pass" echo "Access restricted to localhost only" echo "Generated by secure_server_setup script" } > "$ipmi_backup_file" chmod 600 "$ipmi_backup_file" # Настройка безопасности local success=true ipmitool user set password 2 "$ipmi_pass" || success=false ipmitool lan set 1 access on || success=false ipmitool lan set 1 ipsrc static || success=false ipmitool lan set 1 ipaddr 127.0.0.1 || success=false ipmitool lan set 1 netmask 255.255.255.255 || success=false ipmitool lan set 1 auth ADMIN MD5,PASSWORD || success=false ipmitool lan set 1 cipher_privs aaaaaaaaaaaaaaa || success=false if ! $success; then msg error "Ошибка настройки IPMI. Проверьте вручную." return 1 fi # Очистка логов if ! ipmitool sel clear || ! ipmitool sel time set now; then msg warn "Не удалось очистить логи IPMI" fi # Важная информация для пользователя msg warn "IPMI настроен в безопасном режиме. Доступ только через localhost." msg custom "${RED}ВАЖНО: Пароль IPMI сохранён в ${BLUE}$ipmi_backup_file${NC}" # Запрос на подтверждение сохранения пароля echo -e "\n${YELLOW}================================================" echo -e "Скопируйте содержимое файла $ipmi_backup_file" echo -e "в безопасное место (например, менеджер паролей)." echo -e "После подтверждения файл будет автоматически удалён." echo -e "================================================${NC}\n" while true; do read -p "Вы сохранили пароль IPMI в безопасном месте? (y/N): " confirm case "$confirm" in [yY]|[yY][eE][sS]|[дД]|[дД][аА]) if shred -u "$ipmi_backup_file"; then msg info "Резервный файл с паролем IPMI был уничтожен" else msg error "Не удалось удалить файл! Удалите его вручную:" msg error "shred -u $ipmi_backup_file" fi break ;; *) msg warn "Файл НЕ был удалён. Обязательно сохраните его содержимое:" msg warn "cat $ipmi_backup_file" msg warn "и удалите вручную после сохранения:" msg warn "shred -u $ipmi_backup_file" break ;; esac done return 0 } # Защита от холодных атак полным шифрованим RAM (требует ядра с memfd_secret) if [ "$SECURITY_MODE" = "paranoid" ]; then echo "memfd_secret=1" >> /etc/default/grub update-grub fi ########################################################### НАЧАЛО НАСТРОЙКИ TPM # Обработка виртуальных сред с улучшенной логикой setup_virtualization_security() { if [ "$VIRT_TYPE" = "none" ]; then return 0 fi local warning_shown=false # Параноидные настройки if [ "$SECURITY_MODE" = "paranoid" ]; then # Удаление гостевых агентов с проверкой зависимостей if [ "${#AGENT_PKGS[@]}" -gt 0 ]; then msg debug "Определены гостевые агенты для удаления: ${AGENT_PKGS[*]}" if user_confirm "Отключить гостевые агенты (${AGENT_PKGS[*]}) в режиме паранойи?"; then for agent in "${AGENT_PKGS[@]}"; do if dpkg -l | grep -q "^ii $agent"; then # Проверка зависимостей local deps=$(apt-cache rdepends --installed --important "$agent" | tail -n +2 | wc -l) if [ "$deps" -gt 0 ]; then msg warn "Пакет $agent имеет $deps критических зависимостей! Их удаление сломает систему. Пропускаем удаление " #msg info "Рекомендуется вручную отключить сервис:" #msg info "sudo systemctl disable --now $agent" sudo systemctl disable --now $agent #msg info "sudo apt-mark hold $agent" sudo apt-mark hold $agent continue fi sleep 3 msg info "Отключаем у провайдера возможность изменить пароль для root" systemctl stop "${agent//-/_}"* 2>/dev/null systemctl disable "${agent//-/_}"* 2>/dev/null if safe_exec "apt purge -y $agent"; then msg info "Удалён: $agent" else msg warn "Не удалось полностью удалить $agent" fi # 3. Настройка PAM sed -i 's/\(pam_unix.so .*\)/\1 no_change/' /etc/pam.d/common-password # 4. Создание файла-флага touch /.no-root-password-change chattr +i /.no-root-password-change # 5. Удаление vmanager systemctl stop vmanager-agent 2>/dev/null systemctl disable vmanager-agent 2>/dev/null systemctl mask vmanager-agent 2>/dev/null # Проверка наличия пакета перед удалением if dpkg -l | grep -q vmanager-agent; then apt purge -y vmanager-agent fi # 6. Очистка остатков rm -rf /etc/vmanager /usr/lib/vmanager /var/lib/vmanager # 7. Блокировка через crontab (если vmanager пытается восстановиться) (crontab -l | grep -v vmanager) | crontab - fi done warning_shown=true fi fi # Блокировка портов с проверкой declare -A ports=([5900]="VNC" [10000]="VirtualBox" [902]="VMware") for port in "${!ports[@]}"; do if ! ss -tulnp | grep -q ":$port"; then ufw deny out "$port/tcp" && \ msg info "Заблокирован порт $port (${ports[$port]})" fi done ############################ if user_confirm "Создать скрипт для восстановления блокировки портов и отключения гостевых агентов при загрузке?"; then # Создаем расширенный скрипт cat > /usr/local/bin/paranoid-boot.sh <<'EOF' #!/bin/bash # Ждем инициализации UFW while ! systemctl is-active --quiet ufw; do sleep 0.5 done # 1. Блокировка опасных портов declare -A ports=([5900]="VNC" [10000]="VirtualBox" [902]="VMware") for port in "${!ports[@]}"; do if ! ufw status | grep -Pq "^\s*${port}/tcp\s+DENY\s+"; then ufw deny "$port/tcp" >/dev/null 2>&1 logger -t paranoid-boot "Заблокирован порт $port (${ports[$port]})" fi done # 2. Отключение гостевых агентов declare -A AGENTS=( [qemu-guest-agent]="QEMU" [open-vm-tools]="VMware" [virtualbox-guest-utils]="VirtualBox" [lxd-agent]="LXD" [zabbix-agent]="Zabbix" [vmanager-agent]="VManager" ) for agent in "${!AGENTS[@]}"; do # 2.1. Остановка и отключение сервисов systemctl stop "$agent" 2>/dev/null systemctl disable "$agent" 2>/dev/null # Поиск всех связанных сервисов (например, для open-vm-tools) for service in $(systemctl list-unit-files --no-legend | grep -E "${agent//-/}" | awk '{print $1}'); do systemctl stop "$service" 2>/dev/null systemctl disable "$service" 2>/dev/null logger -t paranoid-boot "Отключен сервис $service (${AGENTS[$agent]})" done # 2.2. Удаление пакетов (если возможно) if dpkg -l | grep -q "^ii $agent"; then # Проверка критических зависимостей deps=$(apt-cache rdepends --installed --important "$agent" | tail -n +2 | wc -l) if [ "$deps" -eq 0 ]; then apt purge -y "$agent" >/dev/null 2>&1 logger -t paranoid-boot "Удален пакет $agent (${AGENTS[$agent]})" else apt-mark hold "$agent" >/dev/null 2>&1 logger -t paranoid-boot "Заблокирован пакет $agent (${AGENTS[$agent]})" fi fi # 2.3. Специальная обработка VManager if [[ "$agent" == "vmanager-agent" ]]; then # Маскировка сервиса systemctl mask vmanager-agent 2>/dev/null # Удаление остатков rm -rf /etc/vmanager /usr/lib/vmanager /var/lib/vmanager # Очистка cron if crontab -l | grep -q vmanager; then (crontab -l | grep -v vmanager) | crontab - fi fi done # 3. Защита пароля root (однократное выполнение) if [ ! -f /.no-root-password-change ]; then # Настройка PAM sed -i 's/\(pam_unix.so .*\)/\1 no_change/' /etc/pam.d/common-password # Создание файла-флага touch /.no-root-password-change chattr +i /.no-root-password-change logger -t paranoid-boot "Защита пароля root активирована" fi EOF chmod +x /usr/local/bin/paranoid-boot.sh # Создаем systemd сервис cat > /etc/systemd/system/paranoid-boot.service < /etc/systemd/system/swtpm-fallback.service <<'EOF' [Unit] Description=SWTPM Fallback Service After=network.target [Service] Type=forking ExecStart=/usr/bin/swtpm socket --tpmstate dir=/var/lib/swtpm --ctrl type=unixio,path=/var/run/swtpm.sock --daemon User=tss Group=tss Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable --now swtpm-fallback.service >/dev/null 2>&1 && \ msg warn "Создана fallback-служба для TPM" } create_fallback_unit() { cat > /etc/systemd/system/swtpm-fallback.service <<'EOF' [Unit] Description=SWTPM (Fallback Mode) After=network.target [Service] Type=forking PIDFile=/run/swtpm.pid ExecStart=/bin/sh -c "/usr/bin/swtpm socket --tpmstate dir=/var/lib/swtpm --ctrl type=unixio,path=/var/lib/swtpm/socket/swtpm.sock --daemon --pid file=/run/swtpm.pid" User=tss Group=tss Restart=on-failure [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable --now swtpm-fallback 2>/dev/null && \ msg warn "Активирован fallback-режим TPM" } start_swtpm_fallback() { # Запуск вручную с проверкой if ! pgrep -x swtpm >/dev/null; then nohup /usr/bin/swtpm socket --tpmstate dir=/var/lib/swtpm \ --ctrl type=unixio,path=/var/lib/swtpm/socket/swtpm.sock \ --log level=20 >/var/log/swtpm.log 2>&1 & sleep 3 fi if pgrep -x swtpm >/dev/null; then # Создаём временный unit-файл для управления процессом create_fallback_unit return 1 fi return 2 } # Функция проверки службы swtpm ensure_swtpm_service() { local service_file="/etc/systemd/system/swtpm.service" local socket_dir="/run/swtpm" # Останавливаем существующий сервис systemctl stop swtpm.service 2>/dev/null pkill -9 -x swtpm 2>/dev/null # Очищаем старые файлы rm -rf "$socket_dir" rm -f /var/run/swtpm.sock # Создаем директорию для сокета в /run mkdir -p "$socket_dir" chown -R tss:tss "$socket_dir" chmod 700 "$socket_dir" # Создаем unit-файл с обновленной конфигурацией cat > "$service_file" << EOF [Unit] Description=SWTPM TPM Emulator After=network.target ConditionPathExists=/var/lib/swtpm ConditionPathExists=/usr/bin/swtpm [Service] Type=forking ExecStart=/usr/bin/swtpm socket --tpmstate dir=/var/lib/swtpm --ctrl type=unixio,path=$socket_dir/swtpm.sock --daemon --log level=20 User=tss Group=tss RuntimeDirectory=swtpm RuntimeDirectoryMode=0700 Restart=on-failure RestartSec=5 TimeoutStartSec=30 Environment=TPM2TOOLS_TCTI=swtpm: # Жёсткие требования безопасности ProtectSystem=strict ProtectHome=read-only PrivateTmp=true NoNewPrivileges=true [Install] WantedBy=multi-user.target EOF # Перезагружаем конфигурацию systemd systemctl daemon-reload # Проверяем и исправляем права доступа chown -R tss:tss /var/lib/swtpm chmod 700 /var/lib/swtpm chown -R tss:tss "$socket_dir" chmod 700 "$socket_dir" # Запускаем сервис systemctl enable swtpm --now 2>&1 | grep -v "Created symlink" # Проверяем статус if systemctl is-active swtpm; then msg info "SWTPM сервис успешно запущен" return 0 else msg warn "SWTPM сервис не запустился, проверяем логи..." diagnose_tpm_failure return 1 fi } # Проверка установки необходимых пакетов TPM check_tpm_packages_installed() { local pkg missing=() for pkg in swtpm swtpm-tools libtpms0; do if ! dpkg -l | grep -q "^ii $pkg"; then missing+=("$pkg") msg warn "⚠ Пакет $pkg не установлен" fi done [ ${#missing[@]} -eq 0 ] && return 0 || return 1 } attempt_libtpms0_install() { msg info "🔎 Поиск последней версии libtpms0 в Debian Sid..." # Определяем архитектуру local arch=$(dpkg --print-architecture) local deb_url="http://ftp.debian.org/debian/pool/main/libt/libtpms/" # Ищем последний .deb пакет local latest_deb=$(curl -s "$deb_url" | grep -oP "libtpms0_.*?${arch}\.deb" | sort -Vr | head -n1) if [ -z "$latest_deb" ]; then msg warn "Не удалось определить последнюю версию в Sid" return 1 fi msg info "Найдена последняя версия: $latest_deb" local temp_dir=$(mktemp -d) # Скачиваем и устанавливаем if wget -q "$deb_url$latest_deb" -O "$temp_dir/libtpms0.deb"; then msg info "Установка пакета вручную..." # Устанавливаем с игнорированием зависимостей (их доставим позже) if dpkg --force-depends -i "$temp_dir/libtpms0.deb"; then # Фиксируем зависимости apt-get install -f -y >/dev/null 2>&1 rm -rf "$temp_dir" # Проверяем результат if dpkg -l | grep -q "^ii libtpms0"; then msg info "libtpms0 успешно установлен из Sid!" return 0 fi fi fi msg warn "Ошибка установки из .deb пакета" rm -rf "$temp_dir" return 1 } # Настройки SWTPM/vTPM configure_tpm() { local max_attempts=3 local attempt=1 local vtpm_dir="/var/lib/swtpm" local log_file="/var/log/swtpm_setup.log" local success=0 # Усиленная проверка зависимостей if ! command -v swtpm_setup >/dev/null || ! command -v swtpm >/dev/null; then msg error "Не найдены команды swtpm_setup или swtpm" return 2 fi # Полноценная очистка msg info "Очистка окружения TPM..." systemctl stop swtpm swtpm-socket-fallback 2>/dev/null pkill -9 -x "swtpm" rm -rf "$vtpm_dir"/* 2>/dev/null mkdir -p "$vtpm_dir" || { msg error "Не удалось создать $vtpm_dir" return 2 } # Права и владелец chown -R tss:tss "$vtpm_dir" || { msg warn "Не удалось изменить владельца $vtpm_dir" } find "$vtpm_dir" -type d -exec chmod 0750 {} \; find "$vtpm_dir" -type f -exec chmod 0640 {} \; # Настройка состояния TPM for ((attempt=1; attempt<=max_attempts; attempt++)); do msg info "Попытка настройки TPM ($attempt/$max_attempts)..." if ! swtpm_setup --tpm-state "$vtpm_dir" \ --create-ek-cert \ --create-platform-cert \ --overwrite > "$log_file" 2>&1; then msg warn "Ошибка swtpm_setup (попытка $attempt)" grep -i -E "error|fail|warning" "$log_file" | tail -n 5 >&2 sleep $((attempt * 2)) continue fi if grep -q "Successfully authored TPM state" "$log_file"; then msg info "Состояние TPM успешно создано" success=1 break else msg warn "Не удалось создать состояние TPM" fi done if [ $success -ne 1 ]; then msg error "Не удалось инициализировать TPM после $max_attempts попыток" return 2 fi # Улучшенный блок запуска (ЗАМЕНЯЕМ старый код на этот) local attempts=3 while ((attempts-- > 0)); do if ensure_swtpm_service; then msg info "TPM успешно запущен через systemd" return 0 fi sleep $((3 - attempts)) done if pgrep -x swtpm >/dev/null; then msg warn "TPM работает в fallback-режиме (см. диагностику)" diagnose_tpm_issue return 1 else msg error "Не удалось запустить TPM ни одним методом" diagnose_tpm_issue return 2 fi } # Глубокая диагностика diagnose_tpm_failure() { local socket_dir="/run/swtpm" msg custom "\n${YELLOW}=== Диагностика TPM ===${NC}" # Проверка установленных пакетов msg info "Проверка установленных пакетов..." dpkg -l | grep -E 'swtpm|libtpms|tpm2' | while read -r line; do msg info "$line" done # Проверка прав доступа msg info "\nПроверка прав доступа..." ls -la /var/lib/swtpm/ 2>/dev/null || msg error "Директория /var/lib/swtpm не существует" ls -la "$socket_dir" 2>/dev/null || msg error "Директория $socket_dir не существует" # Проверка пользователя и группы msg info "\nПроверка пользователя и группы..." id tss 2>/dev/null || msg error "Пользователь tss не существует" getent group tss 2>/dev/null || msg error "Группа tss не существует" # Проверка конфигурации systemd msg info "\nПроверка конфигурации systemd..." systemctl cat swtpm.service # Проверка логов msg info "\nПоследние логи сервиса..." journalctl -u swtpm -n 20 --no-pager # Проверка зависимостей msg info "\nПроверка зависимостей..." ldd $(which swtpm) 2>/dev/null || msg error "Не удалось найти swtpm" # Проверка сокета msg info "\nПроверка сокета..." ls -l "$socket_dir/swtpm.sock" 2>/dev/null || msg error "Сокет не найден" # Проверка процессов msg info "\nПроверка процессов..." ps aux | grep -i swtpm | grep -v grep # Проверка системных ресурсов msg info "\nПроверка системных ресурсов..." df -h /var/lib/swtpm df -h /run free -m # Проверка SELinux/AppArmor msg info "\nПроверка SELinux/AppArmor..." if command -v sestatus >/dev/null 2>&1; then sestatus | grep -i tpm fi if command -v aa-status >/dev/null 2>&1; then aa-status | grep -i tpm fi # Проверка и исправление проблем msg info "\nПопытка исправления проблем..." # 1. Проверка и исправление прав доступа if [ -d "/var/lib/swtpm" ]; then chown -R tss:tss /var/lib/swtpm chmod 700 /var/lib/swtpm msg info "Права доступа исправлены" fi # 2. Проверка и создание директории для сокета if [ ! -d "$socket_dir" ]; then mkdir -p "$socket_dir" chown tss:tss "$socket_dir" chmod 700 "$socket_dir" msg info "Создана директория для сокета" fi # 3. Проверка и перезапуск сервиса systemctl daemon-reload systemctl restart swtpm.service sleep 2 # 4. Проверка результата if systemctl is-active swtpm.service; then msg info "Сервис успешно перезапущен" else msg error "Не удалось перезапустить сервис" journalctl -u swtpm.service -n 10 --no-pager fi } # Установка TPM attempt_tpm_install() { local max_retries=3 local retry local pkg_status=0 local temp_dir # Полноценная очистка как в оригинале msg info "Очистка окружения TPM..." apt-get purge -y swtpm* libtpms* >/dev/null 2>&1 apt-get autoremove -y --purge >/dev/null 2>&1 apt-get clean >/dev/null 2>&1 rm -rf /var/lib/apt/lists/* >/dev/null 2>&1 apt-get update >/dev/null 2>&1 rm -rf /var/lib/swtpm/* 2>/dev/null # Все методы установки libtpms0 как в оригинале local libtpms_attempts=( "1) Стандартный репозиторий|apt-get install -y libtpms0" "2) Backports|apt-get install -t $(lsb_release -cs)-backports -y libtpms0" "3) Принудительная установка|apt-get download libtpms0 && dpkg --force-all -i libtpms0*.deb" "4) Debian Sid (unstable)|attempt_libtpms0_install" "5) Ручная сборка|install_libtpms_from_source" ) msg info "Установка libtpms0 (${#libtpms_attempts[@]} методов)..." for attempt in "${libtpms_attempts[@]}"; do IFS='|' read -r desc cmd <<< "$attempt" msg info "Метод: $desc" if eval "$cmd" >/dev/null 2>&1; then if dpkg -l | grep -q "^ii libtpms0"; then msg info "libtpms0 установлен ($desc)" pkg_status=1 break else # Проверка наличия библиотеки вручную if [ -f /usr/lib/x86_64-linux-gnu/libtpms.so.0 ]; then msg warn "Библиотека libtpms0 найдена, но пакет не зарегистрирован" pkg_status=1 break fi fi fi done if [ $pkg_status -ne 1 ]; then msg error "Не удалось установить libtpms0" return 2 fi # Все основные методы установки как в оригинале local main_attempts=( "1) Полная установка|apt-get install -y --no-install-recommends swtpm swtpm-tools libtpms0" "2) Исправление зависимостей|apt-get install -f -y swtpm swtpm-tools libtpms0" "3) Universe репозиторий|add-apt-repository -y universe && apt-get update && apt-get install -y swtpm swtpm-tools libtpms0" "4) Минимальная установка|apt-get install -y swtpm swtpm-tools" ) for ((retry=0; retry/dev/null 2>&1; then # Полноценная проверка пакетов как в оригинале local installed=() local missing=() for pkg in swtpm swtpm-tools libtpms0; do if dpkg -l | grep -q "^ii $pkg"; then installed+=("$pkg") else missing+=("$pkg") fi done if [ ${#installed[@]} -ge 2 ]; then msg info "Установленные пакеты: ${installed[*]}" # Настройка TPM с обработкой всех статусов configure_tpm case $? in 0) msg info "TPM полностью настроен" return 0 ;; 1) msg warn "TPM настроен, но требует ручного запуска" return 1 ;; *) msg warn "Ошибка настройки TPM" continue ;; esac fi fi done msg warn "Пауза перед повторной попыткой..." sleep 5 apt-get autoremove -y >/dev/null 2>&1 apt-get update >/dev/null 2>&1 done # Полноценный анализ ошибок как в оригинале msg error "Детальный анализ состояния TPM:" for pkg in swtpm swtpm-tools libtpms0; do if dpkg -l | grep -q "^ii $pkg"; then msg info "Пакет установлен: $pkg" else msg warn "Пакет отсутствует: $pkg" fi done if [ -n "$(which swtpm)" ]; then msg info "Бинарные файлы найдены: $(which swtpm)" else msg error "Бинарные файлы swtpm не найдены" fi msg warn "Рекомендации:" msg warn "1. Проверьте интернет-соединение и репозитории" msg warn "2. Попробуйте вручную: apt-get install -y swtpm swtpm-tools libtpms0" msg warn "3. Для виртуальных машин vTPM не является критическим компонентом" return 2 } # Дополнительная функция для установки из исходников install_libtpms_from_source() { msg info "Сборка libtpms из исходников..." local temp_dir=$(mktemp -d) apt-get install -y build-essential autoconf automake libtool >/dev/null 2>&1 git clone https://github.com/stefanberger/libtpms.git "$temp_dir" >/dev/null 2>&1 if [ -d "$temp_dir" ]; then cd "$temp_dir" || return 1 ./autogen.sh >/dev/null 2>&1 ./configure --prefix=/usr >/dev/null 2>&1 make >/dev/null 2>&1 make install >/dev/null 2>&1 if [ -f /usr/lib/x86_64-linux-gnu/libtpms.so.0 ]; then msg info "libtpms успешно собран и установлен" return 0 fi fi msg warn "⚠ Ошибка сборки libtpms" rm -rf "$temp_dir" return 1 } ### Дополнительные функции безопасности из скрипта №2 ### secure_ipmi() { if [ -f /usr/bin/ipmitool ]; then msg info "Настройка безопасности IPMI..." # Отключаем анонимный доступ ipmitool user set name 2 '' 2>/dev/null ipmitool user disable 2 2>/dev/null # Меняем пароль по умолчанию if [ -n "$IPMI_PASSWORD" ]; then ipmitool user set password 1 "$IPMI_PASSWORD" 2>/dev/null fi # Настраиваем безопасные параметры ipmitool lan set 1 auth ADMIN MD5 2>/dev/null ipmitool lan set 1 access on 2>/dev/null msg info "Базовые настройки безопасности IPMI применены" else msg warn "IPMI tools не установлены, настройки не применены" fi } setup_luks() { if [ "$VIRT_TYPE" = "none" ] && [ "$SECURITY_MODE" = "paranoid" ]; then if ! grep -q "luks" /etc/crypttab 2>/dev/null; then if user_confirm "Настроить LUKS шифрование для дисков (не связанное с TPM)?"; then if install_package "cryptsetup"; then msg info "Настройка LUKS шифрования..." # Здесь должна быть логика настройки LUKS # Это пример - реальная реализация зависит от вашей системы local root_dev=$(df / --output=source | tail -1 | sed 's/[0-9]*$//') if [ -b "$root_dev" ]; then cryptsetup luksFormat --type luks2 "$root_dev" msg warn "${RED}СОХРАНИТЕ КЛЮЧ ШИФРОВАНИЯ В БЕЗОПАСНОЕ МЕСТО${NC}" fi else msg error "Не удалось установить cryptsetup" fi fi fi fi } ### АППАРАТНАЯ ЗАЩИТА ### # Проверка виртуализации detect_virtualization() { VIRT_TYPE="none" AGENT_PKGS=() SECURE_BOOT="false" # Проверка Secure Boot if [ -d /sys/firmware/efi ] && [ "$(bootctl status | grep -c "Secure Boot: enabled")" -gt 0 ]; then SECURE_BOOT="true" fi # Основная проверка if command -v systemd-detect-virt >/dev/null; then VIRT_TYPE=$(systemd-detect-virt) case "$VIRT_TYPE" in kvm) AGENT_PKGS=("qemu-guest-agent") ;; # QEMU/KVM vmware) AGENT_PKGS=("open-vm-tools") ;; # VMware oracle) AGENT_PKGS=("virtualbox-guest-utils") ;; # VirtualBox xen) AGENT_PKGS=("xen-tools" "xen-utils") ;; # Xen microsoft) AGENT_PKGS=("hyperv-daemons") ;; # Hyper-V none) ;; # Физический сервер *) msg error "Неизвестный тип виртуализации: $VIRT_TYPE" return 1 ;; esac else msg warn "systemd-detect-virt не найден, используется базовое определение" fi # Дополнительные проверки if grep -q 'container=lxc' /proc/1/environ 2>/dev/null; then VIRT_TYPE="lxc" elif [ -f /.dockerenv ]; then VIRT_TYPE="docker" elif [ -f /proc/vz/vzquota ] && [ "$VIRT_TYPE" = "none" ]; then VIRT_TYPE="openvz" fi return 0 } if ! detect_virtualization; then msg error "Ошибка определения виртуализации" exit 1 fi setup_tpm() { msg custom "\n${GREEN}=== Настройка TPM ===${NC}" [ "$SECURITY_MODE" = "paranoid" ] || msg info "Режим $SECURITY_MODE - некоторые функции TPM будут ограничены" # Установка обязательных пакетов if ! dpkg -l tpm2-tools >/dev/null 2>&1; then msg info "Установка вспомогательных пакетов TPM..." apt-get install -y tpm2-tools tpm2-abrmd libtss2-esys0 fi # Создаем группу tss если не существует if ! getent group tss >/dev/null; then groupadd tss msg info "Создана группа tss" fi # Создаем директорию для TPM с правильными правами mkdir -p /var/lib/swtpm/socket chown -R tss:tss /var/lib/swtpm chmod 700 /var/lib/swtpm chmod 700 /var/lib/swtpm/socket # Создаем конфигурацию для временных файлов cat > /etc/tmpfiles.d/swtpm.conf << 'EOF' d /var/lib/swtpm/socket 0700 tss tss - EOF systemd-tmpfiles --create # Для виртуальных машин if [ "$VIRT_TYPE" != "none" ]; then if [ ! -f /etc/rc.local ]; then cat > /etc/rc.local </dev/null 2>&1 fi msg info "Обнаружена виртуализация: ${VIRT_TYPE^^}" [ "$SECURE_BOOT" = "true" ] && msg info "Secure Boot активирован" if [ "$SECURITY_MODE" = "paranoid" ]; then if ! user_confirm "Эмуляция vTPM в $VIRT_TYPE менее безопасна. Продолжить? (Рекомендуется)"; then return 0 fi fi # Заменяем старую проверку на новую унифицированную if attempt_tpm_install; then case $? in 0) msg info "vTPM полностью настроен и работает" ;; 1) msg warn "vTPM настроен, но требует ручного запуска" diagnose_tpm_failure # Диагностика ;; *) msg warn "Не удалось полностью настроить vTPM, но это не критично для виртуальной среды" diagnose_tpm_failure # Диагностика return 1 ;; esac return 0 else msg warn "Не удалось настроить vTPM, но это не критично для виртуальной среды" diagnose_tpm_failure # Диагностика return 0 fi fi # Для физических серверов if lsmod | grep -q tpm && [ -e /dev/tpm0 ]; then tpm_version=$(cat /sys/class/tpm/tpm0/tpm_version_major 2>/dev/null) [ "$tpm_version" -eq 2 ] && msg info "Обнаружен аппаратный TPM 2.0" if ! install_package "tpm2-tools tpm2-abrmd"; then msg error "Не удалось установить инструменты для работы с TPM" return 1 fi # Проверка производителя local manufacturer=$(tpm2_getcap properties-fixed 2>/dev/null | awk -F'"' '/TPM2_PT_MANUFACTURER/{print $2}') if [ -n "$manufacturer" ]; then msg info "Производитель TPM: ${GREEN}$manufacturer${NC}" else msg warn "Не удалось определить производителя TPM" fi # Дополнительные настройки if [ "$VIRT_TYPE" = "none" ]; then # LUKS с TPM if user_confirm "Использовать TPM для автоматической разблокировки дисков?"; then setup_tpm_luks fi # SSH с TPM if user_confirm "Использовать TPM для защиты SSH-ключей?"; then setup_tpm_ssh fi if [ "$SECURITY_MODE" = "paranoid" ]; then secure_ipmi if ! grep -q "clevis" /etc/crypttab 2>/dev/null; then setup_luks else msg info "LUKS уже настроен с поддержкой TPM" fi fi fi else msg warn "Аппаратный TPM не обнаружен (рекомендуется для paranoid режима)" fi protect_tpm_hardware return 0 } # Оригинальные дополнительные функции для TPM (без изменений) setup_tpm_luks() { if ! install_package "clevis-tpm2"; then msg error "Не удалось установить clevis-tpm2" return 1 fi local root_dev=$(df / --output=source | tail -1 | sed 's/[0-9]*$//') if [ -b "$root_dev" ]; then if clevis luks bind -d "$root_dev" tpm2 '{"hash":"sha256","key":"aes"}'; then update-initramfs -u -k all msg info "Диски настроены для разблокировки TPM" # Резервный ключ cryptsetup luksAddKey "$root_dev" /root/luks-backup.key chmod 400 /root/luks-backup.key msg warn "${RED}СОХРАНИТЕ РЕЗЕРВНЫЙ КЛЮЧ: /root/luks-backup.key${NC}" else msg error "Ошибка настройки LUKS с TPM" fi else msg error "Не удалось определить корневое устройство" fi } setup_tpm_ssh() { if install_package "tpm2-pkcs11 libtpm2-pkcs11-1"; then echo "PKCS11Provider /usr/lib/x86_64-linux-gnu/pkcs11/libtpm2_pkcs11.so" >> /etc/ssh/sshd_config systemctl restart sshd msg info "SSH теперь использует TPM-модуль" else msg error "Не удалось установить пакеты для TPM-PKCS11" fi } protect_tpm_hardware() { [ -e /dev/tpm0 ] || return 0 find /lib/modules/$(uname -r)/ -type f -writable -exec chattr +i {} + 2>/dev/null [ -w /sys/class/tpm/tpm0/device/driver_override ] && echo "deny" > /sys/class/tpm/tpm0/device/driver_override } # Проверка настройки TPM (без изменений) if [ "${SKIP_TPM:-0}" != "1" ]; then DNS_BACKUP=$(mktemp) cp /etc/resolv.conf "$DNS_BACKUP" || { msg error "Не удалось сохранить DNS-настройки" exit 1 } if setup_tpm; then msg info "Настройка TPM завершена успешно" rm -f "$DNS_BACKUP" else msg warn "Восстанавливаем оригинальные DNS" mv "$DNS_BACKUP" /etc/resolv.conf || { msg error "Критическая ошибка: не удалось восстановить DNS" exit 1 } fi fi #################################################### # Настройка безопасности виртуализации if ! setup_virtualization_security; then msg error "Ошибка настройки безопасности виртуализации" exit 1 fi if [ "$VIRT_TYPE" = "none" ]; then if ! user_confirm "${RED}ВНИМАНИЕ: При переустановке без резервного ключа данные будут утеряны! Продолжить?${NC}"; then # Выйти из скрипта с ошибкой, если это основной поток exit 1 # Или return 1, если это внутри функции fi fi # Затем уже вызываем setup_tpm #if ! setup_tpm; then # error_cleanup #fi # Финализация msg info "Проверка состояния безопасности..." if [ "$VIRT_TYPE" != "none" ]; then msg custom "${YELLOW}Итоговые рекомендации для виртуальной среды:${NC} • Регулярно проверяйте логи виртуальной машины • Ограничьте доступ к гипервизору • ${RED}Обновите инструменты виртуализации до последней версии${NC}" fi sleep 3 # Установка auditd с резервными вариантами install_auditd() { # Определяем доступный пакет (сохраняем вашу логику) if apt-cache show auditd &>/dev/null; then PKG_NAME="auditd" elif apt-cache show audit &>/dev/null; then PKG_NAME="audit" else msg warn "Пакет auditd/audit не найден в основных репозиториях" # Попытка найти в backports if apt-cache -t bookworm-backports show auditd &>/dev/null; then msg info "Найден auditd в backports" if apt-get install -y -t bookworm-backports auditd; then return 0 fi fi msg warn "Пропускаем установку auditd" return 1 fi # Основная установка (ваш код с улучшенной обработкой ошибок) if ! install_package "$PKG_NAME"; then msg warn "Стандартная установка не удалась, пробуем принудительно..." apt-get -f install -y --no-install-recommends "$PKG_NAME" || { msg error "Не удалось установить $PKG_NAME" return 1 } fi # Настройка правил (ваш код) cat > /etc/audit/rules.d/server-hardening.rules </dev/null || \ msg warn "Не удалось запустить auditd (но пакет установлен)" return 0 } # Установка lynis с проверенными ссылками install_lynis() { # Попытка 1: Установка из официальных репозиториев if install_package "lynis"; then echo "0 3 * * 0 root lynis audit system --quiet" > /etc/cron.d/lynis-audit return 0 fi msg warn "Официальный репозиторий недоступен, пробуем альтернативы..." # Попытка 2: Установка из GitHub (последняя версия) local latest_version=$(curl -s https://api.github.com/repos/CISOfy/lynis/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') if [ -n "$latest_version" ]; then msg info "Установка Lynis $latest_version из GitHub..." local temp_dir=$(mktemp -d) if curl -sSL "https://github.com/CISOfy/lynis/archive/refs/tags/${latest_version}.tar.gz" | \ tar xz -C "$temp_dir" --strip-components=1; then mkdir -p /opt/lynis cp -r "$temp_dir"/* /opt/lynis/ ln -sf /opt/lynis/lynis /usr/local/bin/lynis rm -rf "$temp_dir" echo "0 3 * * 0 root /usr/local/bin/lynis audit system --quiet" > /etc/cron.d/lynis-audit return 0 fi fi # Попытка 3: Локальный резервный вариант msg warn "Используем встроенную резервную копию Lynis..." if [ -f "/tmp/lynis-backup.tar.gz" ]; then # Предварительно загруженный архив mkdir -p /opt/lynis tar xzf /tmp/lynis-backup.tar.gz -C /opt/lynis ln -sf /opt/lynis/lynis /usr/local/bin/lynis return 0 fi msg error "Все методы установки Lynis провалились" return 1 } # Защита ядра (исправленная версия без дублирования) apply_kernel_protections() { # Создаем временный файл для проверки local temp_conf=$(mktemp) # Добавляем только отсутствующие параметры grep -q "kernel.kptr_restrict" /etc/sysctl.conf || \ echo "kernel.kptr_restrict=2" >> "$temp_conf" grep -q "kernel.dmesg_restrict" /etc/sysctl.conf || \ echo "kernel.dmesg_restrict=1" >> "$temp_conf" grep -q "kernel.printk" /etc/sysctl.conf || \ echo "kernel.printk=3 3 3 3" >> "$temp_conf" grep -q "kernel.randomize_va_space" /etc/sysctl.conf || \ echo "kernel.randomize_va_space=2" >> "$temp_conf" grep -q "kernel.perf_event_paranoid" /etc/sysctl.conf || \ echo "kernel.perf_event_paranoid=3" >> "$temp_conf" grep -q "kernel.yama.ptrace_scope" /etc/sysctl.conf || \ echo "kernel.yama.ptrace_scope=2" >> "$temp_conf" grep -q "kernel.unprivileged_bpf_disabled" /etc/sysctl.conf || \ echo "kernel.unprivileged_bpf_disabled=1" >> "$temp_conf" # Добавляем только новые параметры if [ -s "$temp_conf" ]; then cat "$temp_conf" >> /etc/sysctl.conf msg info "Применяем новые параметры ядра:" cat "$temp_conf" sysctl -p else msg info "Все параметры ядра уже настроены" fi rm -f "$temp_conf" } # Использование в скрипте: if [ "$SECURITY_MODE" = "paranoid" ]; then msg custom "\n${GREEN}=== Настройка системы аудита ===${NC}" # Запрос на установку msg info "Пакеты auditd и lynis обеспечат:" msg info "- Мониторинг безопасности системы в реальном времени (auditd)" msg info "- Автоматизированный аудит безопасности и рекомендации по hardening (lynis)" msg info "- Обнаружение подозрительной активности и уязвимостей" if ! user_confirm "Установить пакеты auditd и lynis?"; then msg warn "Система менее безопасна" return 0 fi sleep 3 install_auditd install_lynis apply_kernel_protections fi # Дополнительная изоляцию процессов if [ "$SECURITY_MODE" = "paranoid" ]; then # Установка дополнительных пакетов изоляции install_package "firejail" install_package "apparmor-profiles" # Настройка namespaces echo "kernel.unprivileged_userns_clone=0" >> /etc/sysctl.conf echo "kernel.kexec_load_disabled=1" >> /etc/sysctl.conf sysctl -p fi if [ "$SECURITY_MODE" = "paranoid" ]; then # Блокировка IPv6 sysctl -w net.ipv6.conf.all.disable_ipv6=1 sysctl -w net.ipv6.conf.default.disable_ipv6=1 # Защита от ARP spoofing echo "net.ipv4.conf.all.arp_ignore = 1" >> /etc/sysctl.conf echo "net.ipv4.conf.all.arp_announce = 2" >> /etc/sysctl.conf # Защита от ICMP атак echo "net.ipv4.icmp_echo_ignore_all = 1" >> /etc/sysctl.conf fi # --- Настройка резервного копирования --- setup_backup # --- Мониторинг целостности файлов --- if [ "$SECURITY_MODE" = "paranoid" ]; then if user_confirm "Установить среду обнаружения вторжений?"; then msg warn "Может нагружать систему!" install_package "aide" # Инициализация AIDE с универсальным методом msg info "Инициализация базы AIDE..." if [ -x "/usr/bin/aideinit" ]; then aideinit -y -f elif [ -x "/usr/sbin/aideinit" ]; then /usr/sbin/aideinit -y -f else # Альтернативный метод для разных версий if [ -f "/etc/aide/aide.conf" ]; then aide --config /etc/aide/aide.conf --init mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db else msg error "Не найден файл конфигурации /etc/aide/aide.conf" [ "$STRICT_MODE" = "YES" ] && exit 1 fi fi # Настройка ежедневной проверки if [ ! -f "/etc/cron.daily/aide" ]; then echo '#!/bin/sh /usr/bin/aide --check ' > /etc/cron.daily/aide chmod 755 /etc/cron.daily/aide msg info "Настроена ежедневная проверка AIDE" fi fi fi # --- Защита от руткитов --- if [ "$SECURITY_MODE" = "paranoid" ]; then rkhunter --propupd echo "0 2 * * * root /usr/bin/rkhunter --cronjob --update --quiet" > /etc/cron.d/rkhunter fi # --- Дополнительная защита сети --- if [ "$SECURITY_MODE" = "paranoid" ]; then # Защита от спуфинга echo "net.ipv4.conf.all.rp_filter=1" >> /etc/sysctl.conf echo "net.ipv4.conf.default.rp_filter=1" >> /etc/sysctl.conf # Защита от SYN-флуда safe_exec echo 'net.ipv4.tcp_syncookies=1' >> /etc/sysctl.conf safe_exec echo "net.ipv4.tcp_max_syn_backlog = 2048" >> /etc/sysctl.conf sysctl -p fi # --- Управление MAC-адресом --- setup_mac_address() { msg custom "\n${GREEN}=== Настройка MAC-адреса ===${NC}" # Определение активного интерфейса (не только eth0) local active_iface=$(ip route show default | awk '/default/ {print $5}') [ -z "$active_iface" ] && active_iface="eth0" local options=("Автогенерация MAC-адреса" "Ручной ввод" "Оставить текущий") PS3="Выберите способ настройки MAC-адреса: " select opt in "${options[@]}"; do case $opt in "Автогенерация MAC-адреса") NEW_MAC=$(printf '02:%02x:%02x:%02x:%02x:%02x' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256))) msg info "Сгенерирован MAC-адрес: ${YELLOW}$NEW_MAC${NC}" break ;; "Ручной ввод") while true; do read -p "Введите MAC-адрес (формат XX:XX:XX:XX:XX:XX): " NEW_MAC if [[ $NEW_MAC =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then msg info "Установлен MAC-адрес: ${YELLOW}$NEW_MAC${NC}" break else msg error "Неверный формат MAC-адреса!" fi done break ;; "Оставить текущий") NEW_MAC=$(ip link show $active_iface | grep ether | awk '{print $2}') msg info "Текущий MAC-адрес сохранен: ${YELLOW}$NEW_MAC${NC}" break ;; *) msg error "Некорректный выбор" ;; esac done # Применение MAC-адреса if [ -n "$NEW_MAC" ]; then # Сохраняем текущие настройки сети для восстановления local old_ip=$(ip -4 addr show $active_iface | grep -oP '(?<=inet\s)\d+(\.\d+){3}') local old_gw=$(ip route | grep default | awk '{print $3}') # Пытаемся сменить MAC без отключения интерфейса if ! ip link set dev $active_iface address "$NEW_MAC" 2>/dev/null; then msg warn "Не удалось изменить MAC без отключения интерфейса" return 1 fi # Совместимость с systemd-networkd cat > /etc/systemd/network/10-mac-address.link < /etc/network/if-up.d/fixmac < "$TMP_RULES" # ===== 2. Устанавливаем политики по умолчанию ===== ufw default deny incoming # Блокируем все входящие ufw default deny outgoing # Блокируем все исходящие # ===== 3. Восстанавливаем ВСЕ старые правила ===== while read -r rule_line; do rule_num=$(echo "$rule_line" | grep -oP '\[\s*\K\d+') rule_action=$(echo "$rule_line" | awk '{print $2}') # ALLOW/DENY/REJECT rule_rest=$(echo "$rule_line" | awk -F'[][]' '{print $3}' | sed 's/^ *//g') # Вставляем правило с тем же действием (ALLOW/DENY/REJECT) ufw insert 1 "$rule_action" "$rule_rest" done < "$TMP_RULES" rm -f "$TMP_RULES" # ===== 4. Добавляем новые правила безопасности ===== # --- SSH --- ufw allow in "$SSH_PORT/tcp" ufw limit in "$SSH_PORT/tcp" # Защита от брутфорса # --- Разрешаем обязательные исходящие --- for port in "${ALLOW_OUTGOING_PORTS[@]}"; do if [[ $port -eq 53 ]]; then ufw allow out "$port/udp" # DNS (UDP) else ufw allow out "$port/tcp" # HTTP/HTTPS (TCP) fi done # --- Блокируем опасные входящие --- for port in "${DENY_INCOMING_PORTS[@]}"; do ufw deny in "$port/tcp" ufw deny in "$port/udp" done # --- Блокировка IPv6 (если не используется) --- ufw reject from ::/0 to any # --- Запрет исходящего UDP (кроме DNS) --- ufw deny out to any proto udp ufw allow out 53/tcp # DNS исключение ufw allow out 53/udp # DNS исключение # --- Запрет системных портов --- ufw deny out 1:1023/tcp ufw deny out 1:1023/udp # --- Блокировка Tor --- for port in "${DENY_OUTGOING_TOR_PORTS[@]}"; do ufw deny out "$port/tcp" ufw deny out "$port/udp" done # ===== 5. Проверка и активация ===== if ! ufw --force enable; then echo -e "\e[31mОшибка активации UFW!\e[0m" >&2 exit 1 fi # Проверяем SSH if ! ufw status | grep -q "$SSH_PORT/tcp"; then echo -e "\e[31mSSH-порт $SSH_PORT не разрешён!\e[0m" >&2 exit 1 fi # Вывод результата echo -e "\n\e[32m=== Фаервол настроен! ===\e[0m" echo -e "\e[33mТекущие правила:\e[0m" ufw status numbered } setup_ufw_service if ! user_confirm "Закончить установку скрипта?"; then msg info "Завершить установку скрипта на этом этапе" # Удаляем себя в конце rm -f "$script_path" msg info "Установочный скрипт удалён." # Очистка истории команд clean_history() { history -c shred -u ~/.bash_history 2>/dev/null ln -s /dev/null ~/.bash_history } clean_history exit 0 fi #systemctl restart ssh # --- Функция полного вайпа диска --- secure_disk_wipe() { if [[ "$SECURITY_MODE" != "Paranoid" ]]; then return 0 fi msg custom "\n${RED}=== Функция экстренного уничтожения данных ===${NC}" msg warn "${YELLOW}⚠ ВНИМАНИЕ: Эта операция НЕОБРАТИМО уничтожит все данные!${NC}" if ! user_confirm "Вы действительно хотите выполнить полный wipe диска?"; then msg info "Отмена операции wipe диска" return 0 fi # Определение основного диска local root_disk=$(df / --output=source | tail -1 | sed 's/[0-9]*$//') local wipe_methods=("Быстрый (нулями)" "Полный (3 прохода)" "Сверхнадежный (7 проходов)") PS3="Выберите метод очистки: " select method in "${wipe_methods[@]}"; do case $method in "Быстрый (нулями)") msg warn "Начало быстрой очистки диска $root_disk нулями..." dd if=/dev/zero of=$root_disk bs=1M status=progress break ;; "Полный (3 прохода)") msg warn "Начало полной очистки диска $root_disk (3 прохода)..." for i in {1..3}; do msg info "Проход $i/3: Запись случайных данных..." dd if=/dev/urandom of=$root_disk bs=1M status=progress done break ;; "Сверхнадежный (7 проходов)") msg warn "Начало сверхнадежной очистки диска $root_disk (7 проходов)..." for i in {1..7}; do case $i in 1|3|5|7) pattern="/dev/urandom" ;; 2|4|6) pattern="/dev/zero" ;; esac msg info "Проход $i/7: Используется $pattern..." dd if=$pattern of=$root_disk bs=1M status=progress done break ;; *) msg error "Некорректный выбор" ;; esac done # После завершения - выключение системы msg error "Очистка завершена. Система будет выключена." shutdown -h now } # Добавление вызова функции в раздел параноидных функций if [ "$SECURITY_MODE" = "paranoid" ]; then if user_confirm "Активировать функцию экстренного уничтожения данных?"; then secure_disk_wipe fi fi # Полная изоляция сервера server_isolation() { msg custom "\n${GREEN}=== Полная изоляция сервера (Paranoid Mode) ===${NC}" msg info "\nЭта настройка позволит вам:\n\ ${YELLOW}1. Отключить все методы удаленного доступа кроме SSH\n\ 2. Заблокировать графические интерфейсы (VNC, X11)\n\ 3. Отключить панели управления провайдера\n\ 4. Удалить агенты мониторинга\n\ 5. Применить дополнительные меры защиты ядра\n\ ${RED}Внимание! После применения этих изменений восстановление возможно только через физический доступ к серверу!${NC}\n" # 1. Блокировка консоли провайдера msg error "Отключаем все методы удаленного доступа кроме SSH" # Отключаем serial console systemctl stop serial-getty@ttyS0.service 2>/dev/null || true systemctl disable serial-getty@ttyS0.service 2>/dev/null || true systemctl mask serial-getty@ttyS0.service 2>/dev/null || true # Отключаем все дополнительные TTY for i in {1..6}; do systemctl stop getty@tty${i}.service 2>/dev/null || true systemctl disable getty@tty${i}.service 2>/dev/null || true systemctl mask getty@tty${i}.service 2>/dev/null || true done # 2. Блокировка VNC и других графических интерфейсов msg error "Блокируем графические интерфейсы" # Игнорируем ошибки отсутствия пакетов apt-get purge -y xserver-xorg* vnc* *x11* *vmanager* *nvidia_modprobe* *nvidia_modprobe//kmod* 2>/dev/null || true apt-get autoremove -y 2>/dev/null || true # 3. Запрет управления через панель провайдера msg error "Блокируем сервисы управления провайдера" # Общие сервисы панелей управления pkill -9 -f 'vmanager|solusvm|virtuozzo|hypervm|openvz' 2>/dev/null || true systemctl stop -f 'vmanager*' 'solusvm*' 'virtuozzo*' 2>/dev/null || true systemctl disable 'vmanager*' 'solusvm*' 'virtuozzo*' 2>/dev/null || true systemctl mask 'vmanager*' 'solusvm*' 'virtuozzo*' 2>/dev/null || true # 4. Удаление скриптов и агентов провайдера msg error "Удаляем агенты мониторинга провайдера" rm -rf /usr/local/vmanager /usr/local/solusvm /etc/vagent /etc/solusvm 2>/dev/null || true crontab -l | grep -v 'vmanager\|solusvm' | crontab - 2>/dev/null || true # 5. Дополнительные меры защиты msg error "Устанавливаем дополнительные защиты" # Запрещаем загрузку новых модулей ядра #echo "kernel.modules_disabled=1" >> /etc/sysctl.conf # Тем самым ломаем едро #echo "kernel.modules_disabled=0" >> /etc/sysctl.conf # Разрешить загрузку модулей # Запрещаем доступ к /dev/mem и /dev/kmem #echo "kernel.kexec_load_disabled=1" >> /etc/sysctl.conf # Уже есть в скрипте echo "kernel.sysrq=0" >> /etc/sysctl.conf sysctl -p 2>/dev/null || true # 6. Физическая изоляция (если возможно) msg warn "Замечание: Полная физическая изоляция требует аппаратных изменений" msg custom "${RED}✖ ВНИМАНИЕ!${NC} ${YELLOW}🔒 1. Вы потеряете доступ через все интерфейсы кроме SSH${NC} ${YELLOW}⚠️ 2. Восстановление возможно только через физический доступ${NC} ${YELLOW}💾 3. Резервные копии сохранены в ${BLUE}$BACKUP_DIR${NC}" if ! user_confirm "Вы подтверждаете эти изменения?" \ "${RED}ЭТО НЕОБРАТИМЫЕ ИЗМЕНЕНИЯ!${NC}\n\ ${YELLOW}После применения вы не сможете отменить их удалённо!${NC}"; then msg warn "Отмена изоляции сервера" return 1 fi msg info "Изоляция сервера успешно завершена!" msg warn "Рекомендуется перезагрузить сервер для применения всех изменений" } if [ "$SECURITY_MODE" = "paranoid" ]; then if user_confirm "Активировать функцию полной изоляции сервера?"; then server_isolation fi fi if [ "$SECURITY_MODE" = "paranoid" ]; then msg custom "${RED}=== SHIELD FULL - АБСОЛЮТНАЯ ИЗОЛЯЦИЯ ===${NC} ${YELLOW}⚠ВНИМАНИЕ! Активация этого режима приведёт к:${NC} ${YELLOW}1. Потере доступа через single-user mode${NC} ${YELLOW}2. Невозможности подключения live-USB${NC} ${YELLOW}3. Блокировке изменений в GRUB без пароля${NC} ${YELLOW}4. Полному отключению KVM-доступа${NC} ${RED}✖После активации восстановление доступа возможно только через физическое вмешательство!${NC}" # Проверяем доступность chattr check_chattr() { if ! command -v chattr >/dev/null 2>&1; then msg warn "Утилита chattr недоступна, некоторые функции защиты будут отключены" return 1 fi # Проверяем, поддерживается ли файловой системой local test_file="/tmp/chattr_test_$(date +%s)" touch "$test_file" if ! chattr +i "$test_file" 2>/dev/null; then msg warn "Файловая система не поддерживает chattr, некоторые функции защиты будут отключены" rm -f "$test_file" return 1 fi chattr -i "$test_file" rm -f "$test_file" return 0 } generate_emergency_token() { local token_file="/root/.emergency_token" # Если токен уже существует - используем его if [[ -f "$token_file" ]]; then # Дешифруем существующий токен emergency_token=$(base64 -d < "$token_file" | openssl enc -aes-256-cbc -d -pbkdf2 -pass pass:"$(cat /etc/machine-id)") msg warn "Обнаружен существующий emergency token (переиспользуем)" else # Генерируем новый токен emergency_token=$(openssl rand -hex 16) # Шифруем и сохраняем echo "$emergency_token" | openssl enc -aes-256-cbc -salt -pbkdf2 -pass pass:"$(cat /etc/machine-id)" | base64 -w0 > "$token_file" chmod 600 "$token_file" chattr +i "$token_file" 2>/dev/null fi # Возвращаем токен для использования в других частях скрипта echo "$emergency_token" } # Проверка целостности токена verify_emergency_token() { local token_file="/root/.emergency_token" if [[ ! -f "$token_file" ]]; then msg error "Emergency token не найден!" return 1 fi # Проверяем, что файл не изменялся if [[ "$(stat -c %a "$token_file")" != "600" ]]; then msg error "Небезопасные права доступа к токену!" return 1 fi # Пытаемся дешифровать if ! base64 -d < "$token_file" | openssl enc -aes-256-cbc -d -pbkdf2 -pass pass:"$(cat /etc/machine-id)" >/dev/null 2>&1; then msg error "Токен повреждён или изменён!" return 1 fi return 0 } # Использование: if ! verify_emergency_token; then msg warn "Обнаружена проблема с emergency token" emergency_token=$(generate_emergency_token) # Регенерируем fi shield_full() { # === Подтверждение активации === if ! user_confirm "Активировать SHIELD FULL режим?"; then msg warn "Отмена" return 0 fi # 1. Блокировка recovery-режимов if user_confirm "Заблокировать recovery-режимы?"; then msg info "Блокируем recovery-режимы..." sed -i 's/^GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT="norescue nosingle"/' /etc/default/grub update-grub fi # 2. Защита GRUB (дополнительное подтверждение) if user_confirm "Установить пароль на GRUB?"; then msg warn "При утере пароля восстановление станет невозможным!" msg info "2. Настраиваем защиту GRUB..." # 2.1 Устанавливаем права (только владелец-root может читать/запускать/изменять) chmod 700 /usr/sbin/grub* /usr/bin/grub* 2>/dev/null || { msg error "Ошибка изменения прав GRUB!" [ "$STRICT_MODE" = "YES" ] && exit 1 } local password="impossible_password_$(openssl rand -hex 32)" local grub_passwd=$(printf "%s\n%s\n" "$password" "$password" | grub-mkpasswd-pbkdf2 2>/dev/null | awk -F" " '/grub.pbkdf2/{print $NF}') cat > /boot/grub/custom.cfg </dev/null systemctl mask ipmi 2>/dev/null apt purge -y ipmitool openipmi *vnc* *rdp* 2>/dev/null # Удаляем все возможные интерфейсы управления systemctl list-units --type=service --all | grep -E '(ipmi|kvm|vpro|idrac|ilo)' | awk '{print $1}' | xargs -r -n1 systemctl mask fi msg info "Правила применяются..." sleep 3 msg info "Сервис перезапускается..." systemctl daemon-reload sleep 4 # 4. Криптографические токены if user_confirm "\n${YELLOW}Настроить Emergency-доступ через криптографические токены?${NC}"; then echo "Это потребует наличия физического токена (YubiKey и аналоги)" msg info "Настраиваем PKCS#11..." apt install -y opensc-pkcs11 yubikey-manager echo "auth required pam_pkcs11.so" >> /etc/pam.d/sshd echo "PubkeyAuthOptions touch-required" >> /etc/ssh/sshd_config msg error "Токен должен быть подключён для любого доступа!" msg warn "Сохраните резервные ключи!" fi # 5. Финальные инструкции msg custom "${RED}=== КРИТИЧЕСКИЕ ИНСТРУКЦИИ ===${NC} ${YELLOW}Требуется физическое отключение IPMI в дата-центре${NC} ${YELLOW}Заблокируйте USB-порты аппаратно${NC} ${BLUE}====================================${NC} ${YELLOW}=== ИНСТРУКЦИИ ПО ВОССТАНОВЛЕНИЮ ===${NC} ${RED}1. Сохраните Emergency-код!${NC} ${YELLOW}2. Emergency-код: ${GREEN}$emergency_token${NC} ${YELLOW}3. Резервные копии: ${GREEN}$BACKUP_DIR${NC} ${YELLOW}Без этих действий система может стать НЕДОСТУПНОЙ!${NC}" } shield_full encrypt_home_partition() { local home_dev=$(df /home --output=source | tail -1) # Запрос пароля while true; do read -sp "Введите пароль для шифрования /home: " luks_pass echo read -sp "Подтвердите пароль: " luks_pass_confirm echo if [ "$luks_pass" != "$luks_pass_confirm" ]; then msg error "Пароли не совпадают!" elif [ -z "$luks_pass" ]; then msg error "Пароль не может быть пустым!" else break fi done # Очистка переменных unset luks_pass_confirm # Процесс шифрования msg warn "Шифрование раздела /home..." if ! echo -n "$luks_pass" | cryptsetup -v luksFormat "$home_dev" --key-file=-; then msg error "Ошибка шифрования раздела!" return 1 fi echo -n "$luks_pass" | cryptsetup open "$home_dev" home_encrypted --key-file=- # Создание файловой системы mkfs.ext4 -m 0 /dev/mapper/home_encrypted mount /dev/mapper/home_encrypted /mnt rsync -aHAXv --progress /home/ /mnt/ umount /mnt # Настройка автоматического монтирования mkdir -p /etc/luks-keys dd if=/dev/urandom of=/etc/luks-keys/home.key bs=512 count=8 chmod 0400 /etc/luks-keys/home.key echo -n "$luks_pass" | cryptsetup luksAddKey "$home_dev" /etc/luks-keys/home.key --key-file=- echo "home_encrypted $home_dev /etc/luks-keys/home.key luks,discard" >> /etc/crypttab echo "/dev/mapper/home_encrypted /home ext4 defaults 0 2" >> /etc/fstab msg info "Раздел /home успешно зашифрован" } configure_grub_for_luks() { msg warn "Настройка GRUB для работы с LUKS..." # Критически важные настройки echo "GRUB_ENABLE_CRYPTODISK=y" >> /etc/default/grub echo "LUKS_ENABLED=yes" >> /etc/default/grub # Дополнительные параметры для initramfs echo "CRYPTSETUP=y" >> /etc/cryptsetup-initramfs/conf-hook echo "KEYFILE_PATTERN=/etc/luks-keys/*.key" >> /etc/cryptsetup-initramfs/conf-hook # ВРЕМЕННОЕ Разрешение # Initramfs может не содержать необходимых модулей для расшифровки # Убедитесь, что в initramfs добавлены крипто-модули echo "dm_crypt" >> /etc/initramfs-tools/modules # Обновление конфигураций update-initramfs -u -k all update-grub sed -i 's/GRUB_DISABLE_OS_PROBER=.*/GRUB_DISABLE_OS_PROBER=true/' /etc/default/grub # Проверка if ! grep -q "GRUB_ENABLE_CRYPTODISK=y" /etc/default/grub; then msg error "Ошибка: настройки GRUB для LUKS не применены!" return 1 fi msg info "GRUB успешно настроен для работы с LUKS" msg error "ВНИМАНИЕ: Сохраните recovery-ключи!" msg custom "${YELLOW}Emergency key: ${GREEN}$(openssl rand -hex 16)${NC}" if [ "$VIRT_TYPE" = "none" ]; then msg custom "${RED}=== РЕКОМЕНДАЦИЯ ДЛЯ ФИЗИЧЕСКИХ СЕРВЕРОВ ===${NC} ${YELLOW}Для полной защиты рекомендуется:${NC} ${YELLOW}⚠️1. Использовать TPM-модуль (если доступен)${NC} ${YELLOW}⚠️2. Настроить безопасную загрузку (Secure Boot)${NC}" fi } ### КРИПТОГРАФИЯ ### setup_luks() { msg custom "${YELLOW}=== LUKS Disk Encryption ===${NC}" # Проверка существующих зашифрованных разделов if [ "$(lsblk -o TYPE | grep -c crypt)" -gt 0 ]; then msg info "Обнаружены существующие зашифрованные разделы" # Даже если разделы уже есть, предлагаем настроить GRUB if user_confirm "${YELLOW}Настроить GRUB для работы с существующими зашифрованными разделами?${NC}"; then msg error "ВНИМАНИЕ: Полное шифрование root-раздела требует переустановки системы!" msg warn "Этот скрипт может настроить только шифрование дополнительных разделов." configure_grub_for_luks fi return 0 fi # Основное подтверждение if ! user_confirm "${YELLOW}Включить поддержку LUKS (требует предварительного шифрования диска)?${NC}"; then return 0 fi # Установка пакетов (только если пользователь согласился) safe_exec apt install -y cryptsetup cryptsetup-initramfs # Шифрование /home раздела if df --output=target | grep -q "/home" && ! grep -q "/home" /etc/crypttab; then msg warn "Рекомендуется зашифровать /home раздел" if user_confirm "Зашифровать /home раздел (данные будут перенесены)?"; then encrypt_home_partition fi fi # Настройка GRUB if grep -q "crypt" /etc/crypttab || grep -q "home_encrypted" /etc/fstab; then configure_grub_for_luks else msg warn "Не выполнено шифрование разделов. Настройка GRUB не требуется." fi } setup_serial_console() { msg custom "${RED}=== Serial Console Access ===${NC} ${YELLOW}ЭТА ФУНКЦИЯ ТРЕБУЕТ:${NC} 🔧 ${YELLOW}1. Физического доступа к серверу${NC} 🔌 ${YELLOW}2. Настроенного оборудования (кабель null-modem)${NC} 📡 ${YELLOW}3. Знания скорости передачи (обычно 115200 baud)${NC} ${RED}🚨 ВНИМАНИЕ: Неправильная настройка может сделать сервер недоступным!${NC}" if ! user_confirm "Вы точно понимаете, для чего вам это нужно?"; then msg info "Serial Console не будет настроен" return 0 fi msg custom "${RED}⚠ ПОСЛЕДНЕЕ ПРЕДУПРЕЖДЕНИЕ ⚠${NC}" msg warn "Это может сделать сервер НЕДОСТУПНЫМ без физического доступа!" if ! user_confirm "ПОДТВЕРДИТЕ настройку Serial Console"; then msg info "Настройка отменена" return 0 fi # Настройка GRUB sed -i 's/^GRUB_CMDLINE_LINUX=.*/GRUB_CMDLINE_LINUX="console=tty0 console=ttyS0,115200n8"/' /etc/default/grub echo "GRUB_TERMINAL=serial" >> /etc/default/grub echo "GRUB_SERIAL_COMMAND=\"serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1\"" >> /etc/default/grub # Настройка Getty cat > /etc/systemd/system/serial-console.service <<'EOL' [Unit] Description=Serial Console After=systemd-user-sessions.service [Service] ExecStart=-/sbin/agetty -o '-p -- \\u' --keep-baud 115200,38400,9600 ttyS0 vt102 Restart=always RestartSec=0 [Install] WantedBy=multi-user.target EOL systemctl enable serial-console.service systemctl start serial-console.service update-grub msg info "Serial Console активирован на ttyS0 (115200 baud)" msg custom "${RED}ЗАПИШИТЕ эти параметры:${NC} ${YELLOW}Скорость: 115200${NC} ${YELLOW}Пароль: ваш текущий root-пароль${NC} ${YELLOW}Оборудование: кабель null-modem${NC}" } # Механизм самоуничтожения setup_self_destruct() { if [[ "$SECURITY_MODE" != "Paranoid" ]]; then return 0 fi msg custom "\n${YELLOW}⚙️ 1. Механизм самоуничтожения (Emergency Wipe)${NC} ${RED}▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔${NC} ${YELLOW}При активации при обнаружении:${NC} ${RED}☠ ${YELLOW}• 2 из 3 признаков взлома${NC} ${RED}⚡ ${YELLOW}• Изменение MAC-адреса или USB-устройств${NC} ${RED}⏻ ${YELLOW}• Физическое вскрытие корпуса${NC} ${RED}⚠ ${RED}Последствие: Сервер станет НЕРАБОТОСПОСОБЕН${NC}" if ! user_confirm "${RED}Активировать механизм самоуничтожения?${NC}"; then msg info "${YELLOW}Механизм самоуничтожения не активирован${NC}" return 0 fi # Сохраняем оригинальный MAC-адрес для проверки ip link show eth0 | grep ether | awk '{print $2}' > /etc/network/original_mac chmod 400 /etc/network/original_mac # Установка скрипта cat > /usr/local/bin/self_destruct <<'EOL' #!/bin/bash ### PHYSICAL BREACH DESTRUCTION PROTOCOL ### # 1. Многофакторная проверка BREACH_CONFIRMED=0 # Проверка 1: Датчик вскрытия корпуса if [[ -f "/sys/class/hwmon/hwmon0/breach" ]] && [[ $(cat /sys/class/hwmon/hwmon0/breach) -eq 1 ]]; then BREACH_CONFIRMED=$((BREACH_CONFIRMED+1)) logger -t self_destruct "Физическое вскрытие корпуса обнаружено" fi # Проверка 2: Неизвестное USB-устройство if lsusb | grep -q "Unknown"; then BREACH_CONFIRMED=$((BREACH_CONFIRMED+1)) logger -t self_destruct "Обнаружено неизвестное USB-устройство" fi # Проверка 3: Изменение MAC-адреса ORIGINAL_MAC="$(cat /etc/network/original_mac 2>/dev/null)" CURRENT_MAC="$(ip link show eth0 | grep ether | awk '{print $2}')" if [[ "$ORIGINAL_MAC" != "$CURRENT_MAC" ]]; then BREACH_CONFIRMED=$((BREACH_CONFIRMED+1)) logger -t self_destruct "Обнаружено изменение MAC-адреса" fi # Минимум 2 подтверждения из 3 if [[ $BREACH_CONFIRMED -lt 2 ]]; then exit 0 fi # 2. Процедура уничтожения с логированием logger -t self_destruct "PHYSICAL BREACH CONFIRMED - INITIATING DESTRUCTION" # Шаг 1: Уничтожение ключей for keyfile in /etc/ssh/*_key /etc/ssl/private/*; do if [[ -f "$keyfile" ]]; then shred -u -z -n 10 "$keyfile" fi done # Шаг 2: Очистка диска DISK=$(df / --output=source | tail -1) if [[ -e "$DISK" ]]; then dd if=/dev/urandom of="$DISK" bs=1M count=100 status=none sync fi # Шаг 3: Выключение echo 1 > /proc/sys/kernel/sysrq echo o > /proc/sysrq-trigger EOL # Защита скрипта chmod 700 /usr/local/bin/self_destruct chown root:root /usr/local/bin/self_destruct if ! chattr +i /usr/local/bin/self_destruct 2>/dev/null; then msg warn "Не удалось заблокировать self_destruct от изменений" fi # Проверка целостности sha256sum /usr/local/bin/self_destruct > /var/lib/self_destruct.sha256 chmod 400 /var/lib/self_destruct.sha256 chattr +i /var/lib/self_destruct.sha256 2>/dev/null # Настройка cron cat > /etc/cron.d/self_destruct_monitor </dev/null msg info "Механизм Самоуничтожения Активирован!" msg custom "${YELLOW}⚡Защита будет срабатывать при:${NC} ${YELLOW}- 2 из 3 признаков взлома${NC} ${YELLOW}- Проверка каждые 5 минут${NC}" # Добавляем в скрипт self_destruct секцию вайпа диска cat >> /usr/local/bin/self_destruct <<'EOL' # Wipe диска при обнаружении взлома if [[ $BREACH_CONFIRMED -ge 2 ]]; then logger -t self_destruct "INITIATING DISK WIPE PROCEDURE" # Определяем корневой диск ROOT_DISK=$(df / --output=source | tail -1 | sed 's/[0-9]*$//') # Быстрый wipe нулями dd if=/dev/zero of=$ROOT_DISK bs=1M count=1000 status=none # Дополнительно: стираем ключевые разделы for dev in /dev/sd* /dev/nvme*; do [ -b "$dev" ] && dd if=/dev/urandom of=$dev bs=1M count=100 status=none done sync fi EOL } # Вызов функции if [[ "$SECURITY_MODE" = "paranoid" ]]; then setup_self_destruct fi msg custom "\n${RED}▄︻デ═══ FINAL LOCK ════︻▄${NC} ${YELLOW}Это необратимый режим полной блокировки:${NC} ${RED}☠ ${YELLOW}• Полная блокировка всех учётных записей${NC} ${RED}💀 ${YELLOW}• Замена ${BLUE}/bin/login${YELLOW} на самоуничтожающую заглушку${NC} ${RED}⚰️ ${YELLOW}• Невозможность входа ЛЮБЫМИ способами${NC} ${RED}⚡ ${YELLOW}• Автоматическое выключение при попытке входа${NC} ${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC} ${RED}🚨 ПОСЛЕДСТВИЯ:${NC} ${RED}• Система станет НЕДОСТУПНОЙ${NC} ${RED}• Восстановление ТОЛЬКО переустановкой${NC} ${RED}• Все данные будут БЕЗВОЗВРАТНО утеряны${NC}" if user_confirm "${RED}▸ Активировать FINAL LOCK? (невозможно отменить!)${NC}"; then # Многоуровневое подтверждение msg custom "${RED}#######${BLINK}⛔ ОКОНЧАТЕЛЬНОЕ ПОДТВЕРЖДЕНИЕ ⛔${NC}${RED}#######${NC} ${YELLOW}Для активации FINAL LOCK требуется:${NC} 1. Ввести кодовую фразу ${RED}DESTROY-ALL-ACCESS${NC} 2. Подтвердить физическое наличие сервера 3. Ввести emergency-токен" # Проверка кодовой фразы read -p "$(echo -e "${RED}Введите 'DESTROY-ALL-ACCESS': ${NC}")" confirm if [ "$confirm" != "DESTROY-ALL-ACCESS" ]; then msg error "Неверная кодовая фраза! Активация отменена." exit 1 fi # Проверка emergency-токена read -sp "$(echo -e "${YELLOW}Введите emergency-токен: ${NC}")" user_emergency_token if [ "$user_emergency_token" != "$emergency_token" ]; then msg error "\nНеверный emergency-токен! Активация отменена." exit 1 fi # Удаляем себя в конце rm -f "$script_path" msg info "Установочный скрипт удалён." # Очистка истории команд clean_history() { history -c shred -u ~/.bash_history 2>/dev/null ln -s /dev/null ~/.bash_history } clean_history exit 0 ######################################### ЗДЕСЬ КОНЕЦ СКРИПТА ######################################### # Финальное предупреждение confirm_destructive_action() { msg custom "${RED} ▄︻デ═══ ИНИЦИАЦИЯ FINAL LOCK ═══︻▄ ${BLINK}ВСЕ ДАННЫЕ БУДУТ НЕОБРАТИМО УТЕРЯНЫ${NC}" for i in {1..3}; do read -p "[${i}/3] Введите 'DESTROY' для подтверждения: " cmd [[ "$cmd" != "DESTROY" ]] && return 1 done return 0 } confirm_destructive_action # 1. Блокировка критичных файлов msg info "Блокировка системных файлов..." critical_files=( /etc/passwd /etc/shadow /etc/ssh/* /etc/sudoers /etc/sudoers.d/* ) for file in "${critical_files[@]}"; do if [ -e "$file" ]; then chattr +i "$file" 2>/dev/null && \ msg info "Заблокирован: $file" || \ msg warn "Не удалось заблокировать: $file" fi done # 2. Замена /bin/login msg info "Установка самоуничтожающего login..." mv /bin/login /bin/login.original 2>/dev/null cat > /bin/login <<'EOL' #!/bin/bash ### FINAL LOCK PROTOCOL ### echo -e "\n\033[1;31m▓▓▓ SYSTEM PERMANENTLY LOCKED ▓▓▓\033[0m" > /dev/console logger -t FINAL_LOCK "SYSTEM LOCK ACTIVATED - INITIATING DESTRUCTION" # Уничтожение ключей shred -uz /etc/ssh/*_key /etc/ssh/*_key.pub 2>/dev/null [ -f /etc/crypttab ] && shred -uz /etc/crypttab # Очистка истории history -a [ -f /root/.bash_history ] && shred -uz /root/.bash_history # Физическое отключение sync echo 1 > /proc/sys/kernel/sysrq echo o > /proc/sysrq-trigger exit 1 EOL chmod 755 /bin/login chattr +i /bin/login 2>/dev/null # 3. Дополнительные меры msg info "Активация дополнительных защит..." echo "vm.panic_on_oom=1" >> /etc/sysctl.conf echo "kernel.panic=5" >> /etc/sysctl.conf sysctl -p # Результат msg custom "${RED}#######${BLINK}🚨 SYSTEM PERMANENTLY LOCKED 🚨${NC}${RED}#######${NC}" msg warn "Сервер будет немедленно перезагружен!" sleep 3 reboot -f else msg info "Активация FINAL LOCK отменена" fi # 3. Рекомендации по аппаратной защите msg custom "${RED}#########################################${NC} ${RED}=== АППАРАТНЫЕ МЕРЫ ===${NC} ${YELLOW}Для полной безопасности сервера необходимо:${NC} ${RED} - Физически выпаять IPMI-чип${NC} ${RED} - Физическое отключение KVM${NC} ${RED} - Залить USB-порты эпоксидной смолой${NC} ${RED} - Установить замок на корпус с датчиком вскрытия${NC} ${RED} - Подключить HSM-модуль для автоматического wipe при вскрытии${NC}" fi if [ "$SECURITY_MODE" = "paranoid" ]; then # Защита памяти (Hardened Kernel) msg info "Устанавливаем защищённое ядро..." safe_exec apt install -y linux-image-hardened # AppArmor для всех процессов msg info "Включаем принудительный режим AppArmor..." safe_exec apt install -y apparmor-profiles apparmor-utils aa-enforce /etc/apparmor.d/* systemctl restart apparmor if [ -f "/proc/hsm" ]; then msg info "Обнаружен HSM-модуль" else msg warn "HSM не найден (рекомендуется для Paranoid Mode)" fi fi # Изоляция процессов if [ "$SECURITY_MODE" = "paranoid" ]; then msg info "Настройка cgroup2 для strict resource isolation..." echo "cgroup_no_v1=all" >> /etc/default/grub echo "systemd.unified_cgroup_hierarchy=1" >> /etc/default/grub update-grub fi # Автоматические обновления безопасности if [ "$SECURITY_MODE" != "Disabled" ]; then # Работает во всех режимах, кроме Disabled msg custom "${GREEN}=== ЗАЩИТА ОТ ZERO-DAY УЯЗВИМОСТЕЙ ===${NC}" # Установка unattended-upgrades if ! command -v unattended-upgrade >/dev/null; then msg warn "Устанавливаем unattended-upgrades..." safe_exec apt install -y unattended-upgrades apt-listchanges fi # Настройка конфигурации cat > /etc/apt/apt.conf.d/50unattended-upgrades <<'EOL' Unattended-Upgrade::Origins-Pattern { "origin=*Debian,archive=stable"; "origin=*Ubuntu,archive=*"; }; Unattended-Upgrade::Package-Blacklist { // Исключения (если нужны) }; Unattended-Upgrade::Automatic-Reboot "true"; Unattended-Upgrade::Automatic-Reboot-Time "04:00"; Unattended-Upgrade::MinimalSteps "true"; EOL # Включение ежедневных обновлений systemctl enable --now unattended-upgrades msg info "Автообновления безопасности активированы (с перезагрузкой при необходимости)" msg custom "${GREEN}=== РЕКОМЕНДАЦИИ ПО ОБНОВЛЕНИЯМ ===${NC} ${YELLOW}1. Логи автообновлений: ${BLUE}/var/log/unattended-upgrades/${NC} ${YELLOW}2. Ручное обновление: ${BLUE}apt update && apt upgrade -y${NC} ${YELLOW}3. Проверка уязвимостей: ${BLUE}lynis audit system${NC}" fi # Ручные проверки уязвимостей if user_confirm "Добавить ежедневную проверку уязвимостей?"; then # Установка инструментов аудита safe_exec apt install -y lynis chkrootkit # Настройка cron-задания cat > /etc/cron.daily/security-audit <<'EOL' #!/bin/bash echo "=== Сканирование на уязвимости $(date) ===" >> /var/log/security-audit.log lynis audit system --quick --no-colors >> /var/log/security-audit.log chkrootkit -q >> /var/log/security-audit.log rkhunter --check --sk --quiet >> /var/log/security-audit.log EOL chmod +x /etc/cron.daily/security-audit # Регулярная проверка на руткиты echo "0 5 * * * root rkhunter --check --sk --report-warnings-only" >> /etc/cron.d/rkhunter msg info "Настроена ежедневная проверка руткитов в 05:00" fi # Дополнительный мониторинг целостности (AIDE + OSSEC) if user_confirm "Реализовать мониторинг целостности в реальном времени?"; then # AIDE (периодические проверки) safe_exec apt install -y aide aideinit cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db echo "0 3 * * * root /usr/bin/aide --check" > /etc/cron.d/aide # OSSEC (реальный времени) safe_exec apt install -y ossec-hids cat > /var/ossec/etc/ossec.conf < 43200 /etc,/usr/bin,/usr/sbin /var/ossec/etc/shared/rootkit_files.txt EOL systemctl restart ossec fi # Финализация SSH msg custom "${GREEN}=== Проверка конфигурации SSH ===${NC}" if ! sshd -t; then msg error "Критическая ошибка в конфигурации SSH!" msg warn "Проверьте настройки вручную." else msg info "Конфигурация SSH проверена и применена" fi # --- Завершение --- msg custom "${GREEN}🎉 НАСТРОЙКА ЗАВЕРШЕНА!${NC}" msg custom "${BLUE}════════ Рекомендуемые действия ════════${NC} ${GREEN}🔑 1. Сохранить резервные копии SSH-ключей ${GREEN}🖥️ 2. Настроить SSH-клиент для подключения ${BLUE}ssh -p $SSH_PORT $NEW_USER@$SERVER_IP${NC} ${GREEN}📄 3. Проверить логи безопасности: ${BLUE}/var/log/auth.log${NC}" if [ "$SECURITY_MODE" = "paranoid" ]; then #emergency_token=$(openssl rand -hex 16) #echo "$emergency_token" > /root/.emergency_token #chmod 600 /root/.emergency_token # Генерация единого emergency token generate_emergency_token() { local token_file="/root/.emergency_token" # Если токен уже существует - используем его if [[ -f "$token_file" ]]; then # Дешифруем существующий токен emergency_token=$(base64 -d < "$token_file" | openssl enc -aes-256-cbc -d -pbkdf2 -pass pass:"$(cat /etc/machine-id)") msg warn "Обнаружен существующий emergency token (переиспользуем)" else # Генерируем новый токен emergency_token=$(openssl rand -hex 16) # Шифруем и сохраняем echo "$emergency_token" | openssl enc -aes-256-cbc -salt -pbkdf2 -pass pass:"$(cat /etc/machine-id)" | base64 -w0 > "$token_file" chmod 600 "$token_file" chattr +i "$token_file" 2>/dev/null fi # Возвращаем токен для использования в других частях скрипта echo "$emergency_token" } if [ "$SECURITY_MODE" = "paranoid" ]; then emergency_token=$(generate_emergency_token) fi msg custom "${RED}🚨 КРИТИЧЕСКИЕ ДАННЫЕ${NC} ${RED}• Изменения НЕОБРАТИМЫ без физического доступа${NC} ${RED}• Все ключи должны храниться в аппаратном HSM${NC} ${RED}ЗАПИШИТЕ ЭТИ ДАННЫЕ В БЕЗОПАСНОЕ МЕСТО:${NC} ${GREEN}• SSH порт: ${BLUE}$SSH_PORT${NC} ${GREEN}• Резервные копии: ${BLUE}$BACKUP_DIR${NC} ${GREEN}• Дата настройки: ${BLUE}$(date)${NC} ${RED}• Emergency token: ${BLINK}$emergency_token${NC}" fi # Финальная проверка post_install_checks() { local errors=0 # Проверка SSH if ! sshd -t; then msg error "Ошибка конфигурации SSH" ((errors++)) fi # Проверка брандмауэра if ! ufw status | grep -q "Status: active"; then msg error "UFW не активирован" ((errors++)) fi # Проверка резервных копий if [ ! -f "$BACKUP_DIR/sshd_config" ]; then msg warn "Резервные копии не созданы" ((errors++)) fi # Проверка MAC-адреса CURRENT_MAC=$(ip link show eth0 | grep ether | awk '{print $2}') SAVED_MAC=$(grep MACAddress /etc/network/mac-address.conf 2>/dev/null | cut -d= -f2) if [[ "$CURRENT_MAC" != "$SAVED_MAC" ]]; then msg error "MAC-адрес изменился! Текущий: $CURRENT_MAC, ожидаемый: $SAVED_MAC" ((errors++)) fi [ $errors -eq 0 ] || msg warn "Обнаружены проблемы: $errors" return $errors } post_install_checks # Запрос на перезагрузку msg custom "${YELLOW} 🔄 ТРЕБУЕТСЯ ПЕРЕЗАГРУЗКА${NC} ${YELLOW}Для применения всех изменений необходима перезагрузка${NC}" if user_confirm "${RED}Выполнить перезагрузку сейчас?${NC}"; then msg info "${GREEN}Инициирую перезагрузку...${NC}" msg warn "${YELLOW}Подключение будет разорвано через 5 секунд${NC}" for i in {5..1}; do echo -ne "${RED}$i...${NC} " sleep 1 done shutdown -r now else msg warn "${YELLOW}⚠ Обязательно перезагрузите сервер вручную!${NC}" msg info "Команда для перезагрузки: ${GREEN}sudo shutdown -r now${NC}" fi # Очистка истории команд clean_history() { history -c shred -u ~/.bash_history 2>/dev/null ln -s /dev/null ~/.bash_history } exit 0