diff --git a/README.md b/README.md index 10a12e9..aea76f1 100644 --- a/README.md +++ b/README.md @@ -15,18 +15,14 @@ ## Архитектура ```text -Windows client - | - | HTTPS / WebSocket, mTLS - v -Control node (может работать на одном из ваших серверов) - ^ - | только исходящие соединения агентов - | -Linux agents ---- управляемые WireGuard links ---- Linux agents +Windows client -- ограниченный SSH --> главный WireGuard Hub с белым IP + ^ + | исходящие WireGuard-соединения + | + Hermes / Home / другие серверы ``` -Мониторинг и межсерверные связи разделены. Регистрация агента не создаёт сетевого туннеля и не выдаёт доступ к SSH. Каждая связь создаётся отдельно, имеет список разрешённых адресов и портов, может иметь срок действия и всегда поддерживает ручной `Disconnect`. +Hub объединяет узлы в приватной подсети, но по умолчанию запрещает транзит между ними. Каждая связь `источник → цель` включается и отключается отдельно. Поэтому можно оставить `Hermes → Home`, отключив только `Hermes → Server2`. Обратное направление не появляется автоматически. Подробности: [архитектура](docs/architecture.md), [модель безопасности](docs/security-model.md), [план разработки](docs/roadmap.md) и [контракт Linux-установщика](docs/installer-contract.md). @@ -47,44 +43,45 @@ tests/ Проект находится на стадии первого тестируемого Windows MVP. Репозиторий переименован из `mobile-server-manager`, потому что первой целевой платформой теперь является Windows. Клиент создаёт отдельный Ed25519-ключ, сохраняет профили локально и получает живые метрики серверов через системный OpenSSH. -Установочный скрипт Linux-части хранится в репозитории [`ochenstarik-ui/lightweight-server`](https://github.com/ochenstarik-ui/lightweight-server) под именем `ochenstarik-server-monitor-manager.sh`. Его копия находится в [`deploy/`](deploy/). Скрипт создаёт отдельного пользователя `ochenstarik-monitor` и SSH forced-command: ключ мониторинга не получает shell, PTY, port forwarding или право выполнять произвольные команды. +Установочный скрипт Linux-части хранится в репозитории [`ochenstarik-ui/lightweight-server`](https://github.com/ochenstarik-ui/lightweight-server) под именем `ochenstarik-server-monitor-manager.sh`. Он устанавливает режим Hub или Node, создаёт отдельного пользователя `ochenstarik-monitor` и SSH forced-command: ключ приложения не получает shell, PTY, port forwarding или право выполнять произвольные команды. ## Быстрый тест на трёх серверах 1. Запустите Windows-клиент и нажмите `SSH-ключ` → `Копировать`. -2. На каждом Ubuntu/Debian-сервере запустите одну команду: +2. На сервере с белым IP скачайте установщик и выберите режим Hub: ```bash -curl -fsSL \ - https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/agent/server-monitor-installer/ochenstarik-server-monitor-manager.sh \ - | sudo bash -s -- install +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/agent/server-monitor-installer/ochenstarik-server-monitor-manager.sh +chmod 700 ochenstarik-server-monitor-manager.sh +sudo ./ochenstarik-server-monitor-manager.sh hub ``` -3. Вставьте скопированный публичный ключ по запросу скрипта. -4. В приложении нажмите `Добавить сервер`, укажите IP/домен, текущий SSH-порт и пользователя `ochenstarik-monitor`. -5. Повторите для остальных серверов и нажмите `Обновить`. +3. Откройте выбранный UDP-порт WireGuard (по умолчанию `51820`) и создайте коды узлов: + +```bash +sudo ochenstarik-smm node-code hermes +sudo ochenstarik-smm node-code home +sudo ochenstarik-smm node-code server2 +``` + +4. На каждом вторичном сервере запустите `sudo ./ochenstarik-server-monitor-manager.sh node`, вставьте SSH-ключ приложения и код именно этого узла. Белый IP вторичным серверам не нужен. +5. В приложении добавьте главный сервер с пользователем `ochenstarik-monitor` и отметьте `Это главный Mesh Hub`. +6. Нажмите обновление в панели связей, выберите источник и цель, затем `Разрешить` или `Отключить`. Приватный ключ хранится в локальном каталоге packaged-приложения Windows. Первый SSH-host key принимается в режиме `accept-new`, затем проверяется по отдельному `known_hosts` приложения. -Для повторяемой установки без ручного ввода публичный ключ можно передать через окружение. Это публичная часть ключа, не секрет: - -```bash -curl -fsSL \ - https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/agent/server-monitor-installer/ochenstarik-server-monitor-manager.sh \ - | sudo env SERVER_MONITOR_PUBLIC_KEY='ssh-ed25519 AAAA... server-monitor-manager' \ - bash -s -- install -``` - Для предварительной проверки вместо запуска через pipe скачайте файл, выполните `bash -n` и просмотрите его содержимое. +Код Node содержит приватный ключ WireGuard узла. Считайте его секретом, используйте только на предназначенном сервере и не сохраняйте в Git или истории сообщений. + ## Принципы безопасности -- никаких общих root-паролей и приватных SSH-ключей в control node; -- на первом SSH-MVP — отдельный ключ мониторинга и pinned `known_hosts`; enrollment tokens появятся вместе с постоянным агентом; +- никаких общих root-паролей и приватных SSH-ключей на Hub; +- отдельный ключ мониторинга и pinned `known_hosts`; код регистрации Node передаётся один раз вручную; - отдельная идентичность для пользователя, устройства, агента и автоматизации; - опасные действия требуют повторного подтверждения и попадают в неизменяемый аудит; - доступ Hermes выдаётся отдельному Unix-пользователю и SSH-ключу с минимальными правами; -- отключение Link удаляет маршруты и peer-конфигурацию, а не только скрывает кнопку в интерфейсе. +- отключение Link сразу удаляет разрешённую пару из nftables на Hub, не затрагивая другие связи узла. ## Лицензия и секреты diff --git a/deploy/ochenstarik-server-monitor-manager.sh b/deploy/ochenstarik-server-monitor-manager.sh deleted file mode 100644 index eea5eac..0000000 --- a/deploy/ochenstarik-server-monitor-manager.sh +++ /dev/null @@ -1,175 +0,0 @@ -#!/usr/bin/env bash -set -Eeuo pipefail - -readonly APP_NAME="ochenstarik-server-monitor-manager" -readonly MONITOR_USER="ochenstarik-monitor" -readonly MONITOR_HOME="/var/lib/${APP_NAME}" -readonly MONITOR_COMMAND="/usr/local/libexec/ochenstarik-server-monitor" -readonly AUTHORIZED_KEYS="${MONITOR_HOME}/.ssh/authorized_keys" - -log() { printf '[+] %s\n' "$*"; } -warn() { printf '[!] %s\n' "$*" >&2; } -die() { printf '[x] %s\n' "$*" >&2; exit 1; } - -require_root() { - [[ "$EUID" -eq 0 ]] || die "Запустите скрипт через sudo" -} - -detect_system() { - [[ -r /etc/os-release ]] || die "Не найден /etc/os-release" - # shellcheck disable=SC1091 - . /etc/os-release - case "${ID:-}" in - ubuntu|debian) ;; - *) die "Поддерживаются Ubuntu и Debian; обнаружено: ${ID:-unknown}" ;; - esac - command -v systemctl >/dev/null 2>&1 || die "Требуется systemd" -} - -read_public_key() { - if [[ -n "${SERVER_MONITOR_PUBLIC_KEY:-}" ]]; then - PUBLIC_KEY="$SERVER_MONITOR_PUBLIC_KEY" - log "Публичный ключ получен из SERVER_MONITOR_PUBLIC_KEY" - else - printf '\nВ Windows-приложении нажмите «SSH-ключ» → «Копировать».\n' - if [[ -r /dev/tty ]]; then - IFS= read -r -p 'Вставьте публичный ключ: ' PUBLIC_KEY < /dev/tty - else - die "Нет интерактивного терминала. Передайте ключ через SERVER_MONITOR_PUBLIC_KEY" - fi - fi - PUBLIC_KEY="${PUBLIC_KEY//$'\r'/}" - [[ "$PUBLIC_KEY" =~ ^ssh-ed25519[[:space:]]+[A-Za-z0-9+/]+={0,3}([[:space:]].*)?$ ]] \ - || die "Ожидается публичный ключ формата ssh-ed25519 AAAA..." -} - -install_dependencies() { - apt-get update - DEBIAN_FRONTEND=noninteractive apt-get install -y \ - ca-certificates openssh-server openssh-client coreutils gawk - systemctl enable --now ssh -} - -verify_public_key() { - local key_file - key_file="$(mktemp)" - trap 'rm -f -- "${key_file:-}"' RETURN - printf '%s\n' "$PUBLIC_KEY" > "$key_file" - ssh-keygen -l -f "$key_file" >/dev/null \ - || die "ssh-keygen отклонил публичный ключ" - trap - RETURN - rm -f -- "$key_file" -} - -create_monitor_command() { - install -d -m 0755 -o root -g root "$(dirname "$MONITOR_COMMAND")" - [[ ! -L "$MONITOR_COMMAND" ]] \ - || die "Отказ от записи через символическую ссылку: $MONITOR_COMMAND" - cat > "$MONITOR_COMMAND" <<'EOF' -#!/usr/bin/env bash -set -Eeuo pipefail -export LC_ALL=C - -read_mem_value() { - awk -v key="$1" '$1 == key ":" { print $2; exit }' /proc/meminfo -} - -printf 'PROTOCOL=1\n' -printf 'HOSTNAME=%s\n' "$(hostname)" -printf 'UPTIME_SECONDS=%s\n' "$(cut -d. -f1 /proc/uptime)" -printf 'LOAD1=%s\n' "$(cut -d' ' -f1 /proc/loadavg)" -printf 'CPU_COUNT=%s\n' "$(getconf _NPROCESSORS_ONLN)" -printf 'MEM_TOTAL_KB=%s\n' "$(read_mem_value MemTotal)" -printf 'MEM_AVAILABLE_KB=%s\n' "$(read_mem_value MemAvailable)" -df -Pk / | awk 'NR == 2 { - printf "DISK_TOTAL_KB=%s\nDISK_AVAILABLE_KB=%s\n", $2, $4 -}' -printf 'KERNEL=%s\n' "$(uname -r)" -EOF - chown root:root "$MONITOR_COMMAND" - chmod 0755 "$MONITOR_COMMAND" -} - -create_monitor_user() { - if ! getent passwd "$MONITOR_USER" >/dev/null; then - useradd \ - --system \ - --create-home \ - --home-dir "$MONITOR_HOME" \ - --shell /bin/bash \ - "$MONITOR_USER" - fi - usermod --home "$MONITOR_HOME" --shell /bin/bash "$MONITOR_USER" - passwd -l "$MONITOR_USER" >/dev/null 2>&1 || true - - install -d -m 0750 -o "$MONITOR_USER" -g "$MONITOR_USER" "$MONITOR_HOME" - install -d -m 0700 -o "$MONITOR_USER" -g "$MONITOR_USER" "${MONITOR_HOME}/.ssh" - [[ ! -L "$AUTHORIZED_KEYS" ]] \ - || die "Отказ от записи через символическую ссылку: $AUTHORIZED_KEYS" - printf 'restrict,command="%s" %s\n' "$MONITOR_COMMAND" "$PUBLIC_KEY" > "$AUTHORIZED_KEYS" - chown "$MONITOR_USER:$MONITOR_USER" "$AUTHORIZED_KEYS" - chmod 0600 "$AUTHORIZED_KEYS" -} - -verify_sshd() { - sshd -t - sshd -T | grep -qi '^pubkeyauthentication yes$' \ - || die "В sshd отключена аутентификация по публичному ключу" -} - -show_status() { - local ssh_port="unknown" - if command -v sshd >/dev/null 2>&1; then - ssh_port="$(sshd -T 2>/dev/null | awk '$1 == "port" { print $2; exit }')" - fi - printf 'Пользователь: %s\n' "$MONITOR_USER" - printf 'SSH-порт: %s\n' "${ssh_port:-unknown}" - printf 'Команда: %s\n' "$MONITOR_COMMAND" - if [[ -x "$MONITOR_COMMAND" ]]; then - runuser -u "$MONITOR_USER" -- "$MONITOR_COMMAND" - else - warn "Серверная часть ещё не установлена" - fi -} - -install_server_part() { - read_public_key - install_dependencies - verify_public_key - create_monitor_command - create_monitor_user - verify_sshd - systemctl reload ssh - log "Серверная часть установлена" - log "Входящий порт не изменялся, новые правила UFW не создавались" - show_status -} - -uninstall_server_part() { - local answer - if [[ -r /dev/tty ]]; then - IFS= read -r -p 'Удалить пользователя мониторинга и forced-command? [y/N]: ' answer < /dev/tty - else - die "Для удаления требуется интерактивный терминал" - fi - [[ "$answer" =~ ^[Yy]$ ]] || { log "Отменено"; return 0; } - rm -f -- "$MONITOR_COMMAND" - if getent passwd "$MONITOR_USER" >/dev/null; then - userdel --remove "$MONITOR_USER" 2>/dev/null || userdel "$MONITOR_USER" - fi - log "Серверная часть удалена" -} - -main() { - local action="${1:-install}" - require_root - detect_system - case "$action" in - install) install_server_part ;; - status) show_status ;; - uninstall) uninstall_server_part ;; - *) die "Использование: $0 [install|status|uninstall]" ;; - esac -} - -main "$@" diff --git a/src/ServerMonitorManager.Desktop/MainPage.xaml b/src/ServerMonitorManager.Desktop/MainPage.xaml index 55b3922..4ea43ad 100644 --- a/src/ServerMonitorManager.Desktop/MainPage.xaml +++ b/src/ServerMonitorManager.Desktop/MainPage.xaml @@ -4,6 +4,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="using:ServerMonitorManager_Desktop" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> @@ -122,8 +123,8 @@ - - + + @@ -214,60 +215,71 @@ - + + x:Name="MeshStatusText" + Foreground="{ThemeResource TextFillColorSecondaryBrush}" + Text="Добавьте главный Mesh Hub" /> - + AutomationProperties.Name="Обновить список серверов и связей" + Click="RefreshMeshButton_Click"> + + - + + + + - + - - - - - - - - - - - - - - +