Harden installer menu and security defaults (#14)
This commit is contained in:
parent
3994102cd1
commit
ef83559f7d
13 changed files with 220 additions and 95 deletions
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
3
.github/workflows/bash-syntax.yml
vendored
3
.github/workflows/bash-syntax.yml
vendored
|
|
@ -29,6 +29,9 @@ jobs:
|
|||
- name: Test step 2 IPv4 firewall mode
|
||||
run: bash tests/test-step2-ipv4-firewall.sh
|
||||
|
||||
- name: Test security invariants
|
||||
run: bash tests/test-security-invariants.sh
|
||||
|
||||
- name: Test master language selection
|
||||
run: bash tests/test-master-language.sh
|
||||
|
||||
|
|
|
|||
25
README.md
25
README.md
|
|
@ -32,33 +32,17 @@ chmod 700 ./*.sh
|
|||
sudo ./ochenstarik-server-install.sh
|
||||
```
|
||||
|
||||
The first wizard prompt selects the dialog language. English is the default; Russian, Spanish, German, French, Portuguese, Simplified Chinese, Japanese, Arabic, and Hindi are also available. The wizard then presents steps 1–8 in order. Each step can be installed, skipped, or used to exit the wizard.
|
||||
The first wizard prompt selects the dialog language. English is the default; Russian, Spanish, German, French, Portuguese, Simplified Chinese, Japanese, Arabic, and Hindi are also available. The wizard then opens the main menu with steps 1–8. Select any step, run it, and the wizard returns to the main menu.
|
||||
|
||||
Keep the current SSH session open while changing the SSH port. Test the new login in a second terminal before disconnecting.
|
||||
|
||||
## Short wizard-only installation
|
||||
|
||||
If `raw.githubusercontent.com` is reachable from the server:
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y curl ca-certificates
|
||||
curl -4 -fLO \
|
||||
--retry 5 \
|
||||
--retry-delay 10 \
|
||||
--connect-timeout 30 \
|
||||
https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-install.sh
|
||||
chmod 700 ochenstarik-server-install.sh
|
||||
sudo ./ochenstarik-server-install.sh
|
||||
```
|
||||
|
||||
The wizard downloads any missing module from the `main` branch and validates it with `bash -n` before execution.
|
||||
The wizard intentionally requires all module scripts to be present locally. Use the full archive or `git clone`; a single downloaded wizard file will not fetch missing modules from the mutable `main` branch.
|
||||
|
||||
## Modules
|
||||
|
||||
| Script | Purpose | Standalone use |
|
||||
| --- | --- | --- |
|
||||
| `ochenstarik-server-install.sh` | Unified wizard with language selection and sequential module installation | Recommended |
|
||||
| `ochenstarik-server-install.sh` | Unified wizard with language selection and a main menu for all modules | Recommended |
|
||||
| `ochenstarik-server-1.sh` | Timezone, terminal locale, program groups, and a 2 GiB swap file | Yes |
|
||||
| `ochenstarik-server-2.sh` | Base packages, SSH port, IPv4/IPv6 mode, UFW, and port management | Yes |
|
||||
| `ochenstarik-server-user-3.sh` | Administrative user, SSH key permissions, SSH migration, sudo, and fail2ban | Run after step 2 |
|
||||
|
|
@ -69,10 +53,9 @@ The wizard downloads any missing module from the `main` branch and validates it
|
|||
| `ochenstarik-server-ai-agents-8.sh` | Installs selected AI agents: Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT, or Pi Coding Agent | Optional |
|
||||
| `ochenstarik-server-uninstall.sh` | Removes project-managed settings so installation can start again | Use carefully |
|
||||
|
||||
Every module can be downloaded and run independently:
|
||||
Every module can be run independently from the full archive or cloned repository:
|
||||
|
||||
```bash
|
||||
curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/SCRIPT_NAME.sh
|
||||
chmod 700 SCRIPT_NAME.sh
|
||||
bash -n SCRIPT_NAME.sh
|
||||
sudo ./SCRIPT_NAME.sh
|
||||
|
|
|
|||
25
SECURITY.md
Normal file
25
SECURITY.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
The `main` branch is the active development line. For production use, prefer a reviewed release archive or a pinned commit instead of running scripts directly from a mutable branch.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report security issues privately through GitHub private vulnerability reporting when it is enabled for this repository. If that channel is unavailable, open a minimal GitHub issue that does not include exploit details or secrets, and request a private contact channel.
|
||||
|
||||
Include:
|
||||
|
||||
- affected script and commit SHA;
|
||||
- expected and actual behavior;
|
||||
- impact and required privileges;
|
||||
- safe reproduction steps;
|
||||
- whether any secret, host, or user data may have been exposed.
|
||||
|
||||
## Installer Trust
|
||||
|
||||
These scripts can run as `root`, change firewall rules, create users, configure SSH, and install third-party software. Do not execute an installer until you have reviewed the exact commit or release archive you intend to run.
|
||||
|
||||
## Secrets
|
||||
|
||||
Do not paste private keys, API tokens, Telegram bot tokens, VPN links, or server passwords into public issues, pull requests, screenshots, or logs.
|
||||
|
|
@ -32,23 +32,9 @@ chmod 700 ./*.sh
|
|||
sudo ./ochenstarik-server-install.sh
|
||||
```
|
||||
|
||||
Первым запросом мастер предлагает выбрать язык диалога и только затем показывает этапы 1–8 по порядку. На каждом этапе можно выбрать установку, пропуск или завершение. В первом этапе пустой ввод устанавливает все рекомендуемые наборы программ. Во время изменения SSH-порта не закрывайте текущую сессию — сначала проверьте новый вход во втором терминале.
|
||||
Первым запросом мастер предлагает выбрать язык диалога и только затем открывает главное меню со всеми этапами 1–8. Выберите нужный этап, установите его, и после завершения мастер вернётся обратно в главное меню. В первом этапе пустой ввод устанавливает все рекомендуемые наборы программ. Во время изменения SSH-порта не закрывайте текущую сессию — сначала проверьте новый вход во втором терминале.
|
||||
|
||||
Если нужен только мастер без скачивания полного архива:
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y curl ca-certificates
|
||||
curl -4 -fLO \
|
||||
--retry 5 \
|
||||
--retry-delay 10 \
|
||||
--connect-timeout 30 \
|
||||
https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-install.sh
|
||||
chmod 700 ochenstarik-server-install.sh
|
||||
sudo ./ochenstarik-server-install.sh
|
||||
```
|
||||
|
||||
При запуске одного мастера недостающие этапы будут загружены автоматически из основной ветки и проверены командой `bash -n`.
|
||||
Мастер намеренно требует, чтобы все скрипты этапов уже лежали рядом локально. Используйте полный архив или `git clone`; одиночный файл мастера больше не подкачивает недостающие этапы из изменяемой ветки `main`.
|
||||
|
||||
## Что входит в проект
|
||||
|
||||
|
|
@ -385,12 +371,12 @@ zstd -dc /ПУТЬ/К/АРХИВУ.tar.zst | tar --acls --xattrs --numeric-owner
|
|||
|
||||
## Полная установка
|
||||
|
||||
Рекомендуемый способ — единый мастер. Он показывает этапы по порядку и перед каждым предлагает установить его, пропустить либо завершить работу. Если остальные файлы находятся рядом, мастер использует их. Если какой-либо этап отсутствует, мастер безопасно загрузит его из основной ветки, проверит `bash -n` и только потом запустит.
|
||||
Рекомендуемый способ — единый мастер. Он показывает главное меню со всеми этапами, запускает выбранный пункт и после завершения возвращается обратно в меню. Если какой-либо этап отсутствует рядом с мастером, запуск будет остановлен: скачайте полный архив проекта или используйте `git clone`.
|
||||
|
||||
```bash
|
||||
curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-install.sh
|
||||
chmod 700 ochenstarik-server-install.sh
|
||||
bash -n ochenstarik-server-install.sh
|
||||
cd lightweight-server
|
||||
chmod 700 ./*.sh
|
||||
bash -n ./*.sh
|
||||
sudo ./ochenstarik-server-install.sh
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -278,13 +278,11 @@ apply_ip_mode_and_firewall() {
|
|||
if [[ "$IP_MODE" == ipv4 ]]; then
|
||||
restore_ipv6_stack_if_managed
|
||||
set_ufw_ipv6_setting yes
|
||||
ufw reload >/dev/null 2>&1 || true
|
||||
delete_managed_ufw_rules
|
||||
else
|
||||
enable_ipv6_systemwide
|
||||
[[ "$IP_MODE" != ipv6 ]] || verify_ipv6_connectivity
|
||||
set_ufw_ipv6_setting yes
|
||||
ufw reload >/dev/null 2>&1 || true
|
||||
delete_managed_ufw_rules
|
||||
fi
|
||||
|
||||
|
|
@ -295,6 +293,7 @@ apply_ip_mode_and_firewall() {
|
|||
allow_rule_for_selected_families "$rule"
|
||||
done
|
||||
ufw --force enable
|
||||
ufw reload >/dev/null 2>&1 || true
|
||||
[[ ! -L "$IP_FAMILY_CONFIG" ]] \
|
||||
|| die "Отказ от записи через символическую ссылку: $IP_FAMILY_CONFIG"
|
||||
printf 'IP_MODE=%s\n' "$IP_MODE" > "$IP_FAMILY_CONFIG"
|
||||
|
|
|
|||
38
ochenstarik-server-backup-7.sh
Normal file → Executable file
38
ochenstarik-server-backup-7.sh
Normal file → Executable file
|
|
@ -146,6 +146,35 @@ rotate_backups() {
|
|||
done
|
||||
}
|
||||
|
||||
read_backup_config() {
|
||||
local line key value
|
||||
BACKUP_ROOT=""
|
||||
[[ -f "$CONFIG_FILE" && ! -L "$CONFIG_FILE" ]] \
|
||||
|| die "Не найден безопасный файл настроек: $CONFIG_FILE"
|
||||
[[ "$(stat -c %u "$CONFIG_FILE")" == 0 ]] \
|
||||
|| die "Файл настроек должен принадлежать root: $CONFIG_FILE"
|
||||
[[ "$(stat -c %a "$CONFIG_FILE")" =~ ^[0-7]*[0-5][0-5]$ ]] \
|
||||
|| die "Файл настроек не должен быть доступен для записи группе или всем: $CONFIG_FILE"
|
||||
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
line="${line//$'\r'/}"
|
||||
[[ -n "$line" && "$line" != \#* ]] || continue
|
||||
[[ "$line" == *=* ]] || die "Некорректная строка в $CONFIG_FILE: $line"
|
||||
key="${line%%=*}"
|
||||
value="${line#*=}"
|
||||
case "$key" in
|
||||
BACKUP_ROOT)
|
||||
[[ "$value" =~ ^/[A-Za-z0-9._/-]+$ ]] \
|
||||
|| die "Некорректный BACKUP_ROOT в $CONFIG_FILE"
|
||||
BACKUP_ROOT="$value"
|
||||
;;
|
||||
*)
|
||||
die "Неизвестный ключ в $CONFIG_FILE: $key"
|
||||
;;
|
||||
esac
|
||||
done < "$CONFIG_FILE"
|
||||
}
|
||||
|
||||
create_backup() {
|
||||
local backup_kind="$1" keep_count="$2" timestamp target_dir
|
||||
local archive temporary checksum checksum_temporary exclude_backup_root
|
||||
|
|
@ -226,12 +255,7 @@ create_backup() {
|
|||
}
|
||||
|
||||
[[ "$EUID" -eq 0 ]] || die "Запустите команду от имени root"
|
||||
[[ -f "$CONFIG_FILE" && ! -L "$CONFIG_FILE" ]] \
|
||||
|| die "Не найден безопасный файл настроек: $CONFIG_FILE"
|
||||
[[ "$(stat -c %u "$CONFIG_FILE")" == 0 ]] \
|
||||
|| die "Файл настроек должен принадлежать root: $CONFIG_FILE"
|
||||
# shellcheck source=/dev/null
|
||||
source "$CONFIG_FILE"
|
||||
read_backup_config
|
||||
[[ "${BACKUP_ROOT:-}" == /* && "$BACKUP_ROOT" != / ]] \
|
||||
|| die "Некорректный BACKUP_ROOT в $CONFIG_FILE"
|
||||
mkdir -p -m 700 -- "$BACKUP_ROOT"/{initial,daily,weekly,monthly}
|
||||
|
|
@ -363,7 +387,7 @@ done
|
|||
|
||||
install -d -o root -g root -m 700 "$CONFIG_DIR" "$BACKUP_ROOT"
|
||||
[[ ! -L "$CONFIG_FILE" ]] || die "Отказ от записи через символическую ссылку: $CONFIG_FILE"
|
||||
printf 'BACKUP_ROOT=%q\n' "$BACKUP_ROOT" > "$CONFIG_FILE"
|
||||
printf 'BACKUP_ROOT=%s\n' "$BACKUP_ROOT" > "$CONFIG_FILE"
|
||||
chown root:root "$CONFIG_FILE"
|
||||
chmod 600 "$CONFIG_FILE"
|
||||
|
||||
|
|
|
|||
73
ochenstarik-server-install.sh
Normal file → Executable file
73
ochenstarik-server-install.sh
Normal file → Executable file
|
|
@ -1,7 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
readonly REPO_RAW_BASE="https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main"
|
||||
readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
|
||||
declare -a STEP_FILES=(
|
||||
|
|
@ -33,8 +32,11 @@ declare -A MASTER_TEXT=()
|
|||
load_master_text() {
|
||||
MASTER_TEXT=(
|
||||
[intro_title]="Unified lightweight-server installer"
|
||||
[intro_body]="Steps are shown in order. Each step can be installed, skipped, or used to exit the installer. Keep the current SSH session open while changing the SSH port and test the new login in another terminal."
|
||||
[intro_body]="Select any installation step from the main menu. After a step finishes, the installer returns to this menu. Keep the current SSH session open while changing the SSH port and test the new login in another terminal."
|
||||
[step]="Step %s of %s: %s"
|
||||
[main_menu]="Main menu"
|
||||
[select_step]="Enter a step number to install it, or 0 to exit: "
|
||||
[exit]="Exit"
|
||||
[install]="Install / run this step"
|
||||
[skip]="Skip and continue to the next step"
|
||||
[finish]="Exit the installer"
|
||||
|
|
@ -56,8 +58,9 @@ load_master_text() {
|
|||
case "$UI_LANG" in
|
||||
ru) MASTER_TEXT+=(
|
||||
[intro_title]="Единый мастер установки lightweight-server"
|
||||
[intro_body]="Этапы будут показаны по порядку. Каждый можно установить, пропустить или завершить мастер. Не закрывайте текущую SSH-сессию при изменении порта и проверьте новый вход во втором терминале."
|
||||
[intro_body]="Выберите любой этап установки в главном меню. После завершения этапа мастер вернётся обратно в это меню. Не закрывайте текущую SSH-сессию при изменении порта и проверьте новый вход во втором терминале."
|
||||
[step]="Этап %s из %s: %s" [install]="Установить / запустить этот этап"
|
||||
[main_menu]="Главное меню" [select_step]="Введите номер этапа для установки или 0 для выхода: " [exit]="Выход"
|
||||
[skip]="Пропустить и перейти к следующему" [finish]="Завершить мастер"
|
||||
[action]="Выберите действие: " [invalid]="Введите 1, 2 или 3" [interrupted]="Ввод прерван"
|
||||
[failed]="Этап завершился с ошибкой." [retry]="Запустить этот этап повторно"
|
||||
|
|
@ -208,31 +211,37 @@ ensure_step_script() {
|
|||
local filename="$1" result_variable="$2" target
|
||||
target="${SCRIPT_DIR}/${filename}"
|
||||
|
||||
if [[ -e "$target" ]]; then
|
||||
[[ -f "$target" && ! -L "$target" ]] \
|
||||
|| die "Отказ от запуска: $target должен быть обычным файлом"
|
||||
else
|
||||
command -v curl >/dev/null 2>&1 \
|
||||
|| die "Не найден curl: установите его командой apt-get install -y curl ca-certificates"
|
||||
[[ -d "$SCRIPT_DIR" && ! -L "$SCRIPT_DIR" && -w "$SCRIPT_DIR" ]] \
|
||||
|| die "Каталог $SCRIPT_DIR недоступен для безопасной загрузки"
|
||||
|
||||
log "Файл ${filename} отсутствует; загружаю его из основного репозитория"
|
||||
TEMP_FILE="$(mktemp "${SCRIPT_DIR}/.ochenstarik-server-download.XXXXXX")"
|
||||
chmod 600 "$TEMP_FILE"
|
||||
curl -fL --retry 5 --retry-delay 5 --connect-timeout 30 \
|
||||
--proto '=https' --tlsv1.2 "${REPO_RAW_BASE}/${filename}" -o "$TEMP_FILE"
|
||||
bash -n "$TEMP_FILE" || die "Загруженный файл ${filename} не прошёл проверку Bash"
|
||||
chmod 700 "$TEMP_FILE"
|
||||
mv -- "$TEMP_FILE" "$target"
|
||||
TEMP_FILE=""
|
||||
fi
|
||||
|| die "Не найден локальный файл ${filename}. Скачайте полный архив проекта или выполните git clone, затем запустите мастер из каталога со всеми скриптами."
|
||||
|
||||
chmod 700 "$target"
|
||||
bash -n "$target" || die "Синтаксическая ошибка в $target"
|
||||
printf -v "$result_variable" '%s' "$target"
|
||||
}
|
||||
|
||||
choose_main_menu_action() {
|
||||
local result_variable="$1" answer index
|
||||
while :; do
|
||||
printf '\n============================================================\n'
|
||||
printf '%s\n' "$(master_msg main_menu)"
|
||||
for index in "${!STEP_FILES[@]}"; do
|
||||
printf ' %s) %s\n' "$((index + 1))" "${STEP_TITLES[index]}"
|
||||
done
|
||||
printf ' 0) %s\n' "$(master_msg exit)"
|
||||
read -rp "$(master_msg select_step)" answer || die "$(master_msg interrupted)"
|
||||
answer="${answer:-}"
|
||||
if [[ "$answer" == 0 ]]; then
|
||||
printf -v "$result_variable" '%s' 0
|
||||
return 0
|
||||
fi
|
||||
if [[ "$answer" =~ ^[0-9]+$ ]] && ((10#$answer >= 1 && 10#$answer <= ${#STEP_FILES[@]})); then
|
||||
printf -v "$result_variable" '%s' "$((10#$answer))"
|
||||
return 0
|
||||
fi
|
||||
warn "Введите число от 0 до ${#STEP_FILES[@]}"
|
||||
done
|
||||
}
|
||||
|
||||
choose_step_action() {
|
||||
local step_number="$1" title="$2" result_variable="$3" answer
|
||||
while :; do
|
||||
|
|
@ -284,21 +293,15 @@ printf '\n%s\n\n%s\n' "$(master_msg intro_title)" "$(master_msg intro_body)"
|
|||
completed=0
|
||||
skipped=0
|
||||
|
||||
for index in "${!STEP_FILES[@]}"; do
|
||||
step_number="$((index + 1))"
|
||||
action=""
|
||||
choose_step_action "$step_number" "${STEP_TITLES[index]}" action
|
||||
case "$action" in
|
||||
2)
|
||||
skipped="$((skipped + 1))"
|
||||
continue
|
||||
;;
|
||||
3)
|
||||
while :; do
|
||||
menu_action=""
|
||||
choose_main_menu_action menu_action
|
||||
if [[ "$menu_action" == 0 ]]; then
|
||||
print_summary "$completed" "$skipped"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
fi
|
||||
step_number="$menu_action"
|
||||
index="$((step_number - 1))"
|
||||
script_path=""
|
||||
ensure_step_script "${STEP_FILES[index]}" script_path
|
||||
while :; do
|
||||
|
|
@ -319,5 +322,3 @@ for index in "${!STEP_FILES[@]}"; do
|
|||
esac
|
||||
done
|
||||
done
|
||||
|
||||
print_summary "$completed" "$skipped"
|
||||
|
|
|
|||
45
ochenstarik-server-panel-warp-6.sh
Normal file → Executable file
45
ochenstarik-server-panel-warp-6.sh
Normal file → Executable file
|
|
@ -112,6 +112,26 @@ wait_for_tcp_listener() {
|
|||
die "${description} не открыл TCP-порт ${port}"
|
||||
}
|
||||
|
||||
choose_public_access() {
|
||||
local answer
|
||||
printf '\nДоступ к панели 3x-ui и подпискам:\n'
|
||||
printf ' 1) Приватный режим: не открывать порты панели и подписок в UFW (рекомендуется)\n'
|
||||
printf ' 2) Публичный режим: открыть порты панели и подписок всему Интернету\n'
|
||||
while :; do
|
||||
read -rp 'Режим доступа [1]: ' answer
|
||||
answer="${answer:-1}"
|
||||
case "$answer" in
|
||||
1) XUI_PUBLIC_ACCESS=no; return 0 ;;
|
||||
2)
|
||||
warn "Публичный доступ к панели опасен. Используйте сложный путь, новые учётные данные, TLS и 2FA"
|
||||
XUI_PUBLIC_ACCESS=yes
|
||||
return 0
|
||||
;;
|
||||
*) warn "Введите 1 или 2" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
allow_ufw_port() {
|
||||
local port="$1" description="$2" mode
|
||||
mode="$(read_ip_mode)"
|
||||
|
|
@ -265,6 +285,7 @@ forbidden_common=("22" "80" "443")
|
|||
PANEL_PORT=""
|
||||
SUBSCRIPTION_PORT=""
|
||||
WARP_PORT=""
|
||||
XUI_PUBLIC_ACCESS=no
|
||||
XUI_WEB_PATH="/"
|
||||
XUI_SUB_PATH="/sub/"
|
||||
choose_port PANEL_PORT "Порт панели 3x-ui" "$DEFAULT_PANEL_PORT" "${forbidden_common[@]}"
|
||||
|
|
@ -272,11 +293,17 @@ choose_port SUBSCRIPTION_PORT "Порт подписок 3x-ui" "$DEFAULT_SUBSCR
|
|||
"${forbidden_common[@]}" "$PANEL_PORT"
|
||||
choose_port WARP_PORT "Локальный proxy-порт WARP" "$DEFAULT_WARP_PORT" \
|
||||
"${forbidden_common[@]}" "$PANEL_PORT" "$SUBSCRIPTION_PORT"
|
||||
choose_public_access
|
||||
|
||||
printf '\nБудут использованы порты:\n'
|
||||
printf ' 3x-ui panel: %s/tcp\n' "$PANEL_PORT"
|
||||
printf ' 3x-ui subscriptions: %s/tcp\n' "$SUBSCRIPTION_PORT"
|
||||
printf ' WARP local proxy: 127.0.0.1:%s/tcp\n\n' "$WARP_PORT"
|
||||
if [[ "$XUI_PUBLIC_ACCESS" == yes ]]; then
|
||||
printf ' UFW: порты панели и подписок будут открыты публично\n\n'
|
||||
else
|
||||
printf ' UFW: порты панели и подписок не будут открыты публично\n\n'
|
||||
fi
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
log "Установка системных зависимостей"
|
||||
|
|
@ -306,13 +333,22 @@ install_cloudflare_warp
|
|||
log "Настройка UFW"
|
||||
allow_ufw_port 80 "HTTP"
|
||||
allow_ufw_port 443 "HTTPS"
|
||||
allow_ufw_port "$PANEL_PORT" "панель 3x-ui"
|
||||
allow_ufw_port "$SUBSCRIPTION_PORT" "подписки 3x-ui"
|
||||
if [[ "$XUI_PUBLIC_ACCESS" == yes ]]; then
|
||||
allow_ufw_port "$PANEL_PORT" "панель 3x-ui"
|
||||
allow_ufw_port "$SUBSCRIPTION_PORT" "подписки 3x-ui"
|
||||
else
|
||||
log "UFW: порты панели и подписок оставлены закрытыми для публичного Интернета"
|
||||
fi
|
||||
ufw --force enable
|
||||
|
||||
for port in 80 443 "$PANEL_PORT" "$SUBSCRIPTION_PORT"; do
|
||||
for port in 80 443; do
|
||||
verify_ufw_port "$port"
|
||||
done
|
||||
if [[ "$XUI_PUBLIC_ACCESS" == yes ]]; then
|
||||
for port in "$PANEL_PORT" "$SUBSCRIPTION_PORT"; do
|
||||
verify_ufw_port "$port"
|
||||
done
|
||||
fi
|
||||
|
||||
wait_for_tcp_listener "$PANEL_PORT" "Панель 3x-ui"
|
||||
wait_for_tcp_listener "$SUBSCRIPTION_PORT" "Сервис подписок 3x-ui"
|
||||
|
|
@ -323,4 +359,7 @@ printf 'Панель 3x-ui: http(s)://<server-ip>:%s%s\n' "$PANEL_PORT"
|
|||
printf 'Подписки 3x-ui: http(s)://<server-ip>:%s%s\n' "$SUBSCRIPTION_PORT" "$XUI_SUB_PATH"
|
||||
printf 'Локальный WARP proxy: socks5://127.0.0.1:%s\n' "$WARP_PORT"
|
||||
printf '\nПорт WARP не открыт публично в UFW: локальный proxy не требует аутентификации.\n'
|
||||
if [[ "$XUI_PUBLIC_ACCESS" != yes ]]; then
|
||||
printf 'Порты панели и подписок не открыты публично. Используйте SSH tunnel, management VPN или повторный запуск с явным публичным режимом.\n'
|
||||
fi
|
||||
printf 'Настройте TLS, сложный путь панели, новые учётные данные и двухфакторную аутентификацию в 3x-ui.\n'
|
||||
|
|
|
|||
23
ochenstarik-server-uninstall.sh
Normal file → Executable file
23
ochenstarik-server-uninstall.sh
Normal file → Executable file
|
|
@ -218,12 +218,29 @@ remove_panel_and_warp() {
|
|||
}
|
||||
|
||||
read_backup_root() {
|
||||
local value="/var/backups/ochenstarik-server"
|
||||
local value="/var/backups/ochenstarik-server" line key parsed_value
|
||||
if [[ -f "$BACKUP_CONFIG" && ! -L "$BACKUP_CONFIG" ]]; then
|
||||
[[ "$(stat -c '%u' "$BACKUP_CONFIG")" == 0 ]] \
|
||||
|| die "Файл $BACKUP_CONFIG должен принадлежать root"
|
||||
value="$(bash -c 'set -efu; source "$1"; printf "%s" "$BACKUP_ROOT"' \
|
||||
_ "$BACKUP_CONFIG")"
|
||||
[[ "$(stat -c '%a' "$BACKUP_CONFIG")" =~ ^[0-7]*[0-5][0-5]$ ]] \
|
||||
|| die "Файл $BACKUP_CONFIG не должен быть доступен для записи группе или всем"
|
||||
parsed_value=""
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
line="${line//$'\r'/}"
|
||||
[[ -n "$line" && "$line" != \#* ]] || continue
|
||||
[[ "$line" == *=* ]] || die "Некорректная строка в $BACKUP_CONFIG: $line"
|
||||
key="${line%%=*}"
|
||||
value="${line#*=}"
|
||||
case "$key" in
|
||||
BACKUP_ROOT)
|
||||
[[ "$value" =~ ^/[A-Za-z0-9._/-]+$ ]] \
|
||||
|| die "Некорректный BACKUP_ROOT в $BACKUP_CONFIG"
|
||||
parsed_value="$value"
|
||||
;;
|
||||
*) die "Неизвестный ключ в $BACKUP_CONFIG: $key" ;;
|
||||
esac
|
||||
done < "$BACKUP_CONFIG"
|
||||
value="${parsed_value:-$value}"
|
||||
fi
|
||||
[[ "$value" == /* && "$value" != / ]] || die "Опасный путь резервных копий: $value"
|
||||
case "$value" in
|
||||
|
|
|
|||
5
tests/test-master-language.sh
Normal file → Executable file
5
tests/test-master-language.sh
Normal file → Executable file
|
|
@ -22,5 +22,10 @@ choose_dialog_language <<< '2' >/dev/null
|
|||
[[ "${#STEP_TITLES[@]}" == 8 ]]
|
||||
[[ "${STEP_FILES[7]}" == 'ochenstarik-server-ai-agents-8.sh' ]]
|
||||
[[ "${STEP_TITLES[7]}" == 'AI-агенты на выбор для обычного пользователя' ]]
|
||||
[[ "$(master_msg main_menu)" == 'Главное меню' ]]
|
||||
choose_main_menu_action selected_step <<< '8' >/dev/null
|
||||
[[ "$selected_step" == 8 ]]
|
||||
choose_main_menu_action selected_step <<< '0' >/dev/null
|
||||
[[ "$selected_step" == 0 ]]
|
||||
|
||||
printf 'Master language selection tests passed.\n'
|
||||
|
|
|
|||
32
tests/test-security-invariants.sh
Executable file
32
tests/test-security-invariants.sh
Executable file
|
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.."
|
||||
|
||||
if grep -q 'REPO_RAW_BASE' ochenstarik-server-install.sh ||
|
||||
grep -q 'raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main' ochenstarik-server-install.sh; then
|
||||
printf '[x] Master installer must not fetch missing modules from mutable main\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if grep -q 'source "$CONFIG_FILE"' ochenstarik-server-backup-7.sh; then
|
||||
printf '[x] Backup runner must parse config keys instead of sourcing root config\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if grep -Eq 'bash -c .*source|source "\$1"' ochenstarik-server-uninstall.sh; then
|
||||
printf '[x] Uninstaller must parse backup config keys instead of sourcing root config\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q 'XUI_PUBLIC_ACCESS=no' ochenstarik-server-panel-warp-6.sh; then
|
||||
printf '[x] 3x-ui panel must be private by default\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q 'Приватный режим' ochenstarik-server-panel-warp-6.sh; then
|
||||
printf '[x] Step 6 must offer a private access mode for 3x-ui\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '[+] Security invariants look correct\n'
|
||||
|
|
@ -41,4 +41,9 @@ if [[ "$ipv4_block" != *'delete_managed_ufw_rules'* ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$ipv4_block" == *'ufw reload'* ]]; then
|
||||
printf '[x] IPv4 mode must not reload UFW before stale managed rules are deleted\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '[+] Step 2 IPv4 firewall behavior looks correct\n'
|
||||
|
|
|
|||
Loading…
Reference in a new issue