cd ~кейси/server-office.md

кейс 04LIVE · ПРАЦЮЄ У КЛІЄНТА

19 робочих місць під контролем з телефона власника

Зробив Telegram-бота з панеллю Mini App, через який власник офісу сам керує робочим сервером на 19 місць. Він бачить, хто працює, блокує вхід, додає працівників, перезапускає сервер і отримує попередження — без віддаленого робочого стола і без дзвінків адміну.

Термін
20 оновлень від заглушки до версії 1.11
Стек
PowerShell 5.1 · Telegram Bot API · Telegram Mini App · Cloudflare Tunnel · Hyper-V · PowerShell Direct · RDS · WTS API · WireGuard · PBKDF2 · HMAC-SHA256
Виконавець
Forge Logic
19робочих місць під керуванням з телефона
0відкритих вхідних портів на сервері
20оновлень, кожне з бекапом і скриптом відкату
75 мсзавантаження списку працівників — було понад 12 с
3ролі доступу: власник, адмін, менеджер
15 хвживуть пароль і архів нового працівника в панелі
19 робочих місць під контролем з телефона власника1:00
Головний екран панелі: скільки працівників на сервері
Стан сервера: процесор, пам'ять, диски, ліцензії, бекап
Керування сервером: перезавантаження з відліком

Що боліло

У власника вже був простий бот: увімкнути чи вимкнути сервер і подивитись його стан. Усе інше йшло через адміна: хто зараз працює, як заблокувати вхід, новий працівник, перезапуск. Власник хотів робити це сам, з телефона, без віддаленого робочого стола і консолі.

Вимоги замовника

  • Усе з телефона і зрозуміло людині без технічної освіти.
  • Кілька людей з різними правами. Ідея в тому, щоб власник сам вів адміністрування, а не дзвонив адміну.
  • Критичні дії (живлення, блок, нові працівники) — лише з PIN-кодом.
  • Блокування не має закривати чужу роботу: сеанс зберігається, людина продовжує з того ж місця.
  • Видалення працівника — це відключення. Дані лишаються, доступ можна повернути.

Обмеження

  • Старий бот працює до кінця тестів, і його не чіпаємо.
  • Облікову програму офісу не чіпати: бот може лише читати її стан.
  • Жодного стеження за екранами працівників і жодних скриншотів — принципова вимога.
  • Уся робота віддалена: фізичного доступу до офісу не було.
  • Кожна зміна — готовий скрипт PowerShell з бекапом, окремим скриптом відкату і перевіркою синтаксису до поставки.

Рішення

Бот живе на хості, де крутиться віртуальна машина офісу, і керує нею зсередини. Назовні він лише сам з’єднується — з Telegram і з тунелем, тому жоден порт за межу не відкритий.

Назовні бот лише сам з’єднується — з Telegram і з тунелем. Жоден порт за межу не відкритий.

Схема: телефон власника → тунель → бот на хості Hyper-V → робочий сервер і VPN працівників
  • Один процес PowerShell 5.1. Він уже є в кожному Windows Server, нічого ставити не треба. Одночасно слухає Telegram і віддає панель. Довгі дії йдуть у фоні, щоб бот не зависав.
  • Telegram Mini App. Це вебсторінка, яка відкривається всередині Telegram як застосунок. Чат лишився для коротких сповіщень і кнопок «Дозволити / Відхилити».
  • Тунель Cloudflare. Сервер сам тягне з’єднання назовні, і панель отримує HTTPS-адресу. Сама панель слухає лише локальну адресу хоста.
  • Перевірка підпису Telegram (HMAC-SHA256). Кожен запит з панелі несе криптографічну печатку від Telegram. Без токена бота її не підробити, тож чужий запит не пройде.
  • PIN з хешем PBKDF2. Хеш — це «відбиток» PIN, з якого сам PIN не відновити. PBKDF2 навмисно повільний, щоб перебір не мав сенсу. Після 5 помилок — блок.
  • PowerShell Direct. Канал між хостом Hyper-V і його віртуалкою, який взагалі не йде через мережу. Бот заходить під службовим записом, якому заборонено звичайний вхід. Пароль зашифрований DPAPI — вбудованим шифруванням Windows, прив’язаним до машини.
  • WTS API. Вбудований інтерфейс Windows, який каже, хто підключений до сервера і скільки часу. З нього беруться статуси працівників.
  • WireGuard. VPN, через який працівники підключаються. Бот додає і вимикає ключі на льоту, інші підключення не рвуться.
  • Дані в JSON-файлах і журнал дій. База даних для десятка користувачів бота — зайва деталь, яку треба обслуговувати.
  • Сторож. Задача планувальника запускає бота зі стартом системи і перевіряє його кожні 5 хв. Друга копія сама не запуститься.

Історія розробки

Бот ріс від заглушки до версії 1.11 за 20 оновлень. Кожне міняло лише файли нового бота і мало свій скрипт відкату.

ops(stub)#00

Заглушка поруч зі старим ботом

Що сталось: розробляти треба на живому сервері і не зачепити робочий бот.

Як вирішили: окремий тестовий бот, окрема папка і задача. Старий бот лише читається. Сторож і захист від другої копії — з першого дня.

sec(auth)#01

Меню, PIN і ролі

Що сталось: потрібні права доступу і захист дій.

Як вирішили: меню в чаті, PIN з хешем, видача доступу, журнал дій. Забутий PIN скидається одноразовим кодом, який видно лише на самому сервері.

feat(mini-app)#02

Mini App замість кнопок у чаті

Що сталось: меню на кнопках у чаті виглядало занадто дешево.

Як вирішили: панель Mini App за погодженим прототипом, світла й темна тема. Спершу тимчасовий тунель з випадковою адресою, потім постійна адреса.

feat(v1.1)#03

Хто і скільки працює

Що сталось: боту треба бачити, хто і скільки часу на сервері.

Як вирішили: службовий запис у віртуалці без права входу, пароль зашифрований. Список з іменами і статусами.

feat(v1.2)#04

Блок входу без втрати роботи

Що сталось: власнику треба заборонити вхід, не згубивши чужу роботу.

Як вирішили: блок одного чи всіх: 15 хв, 1 год, доба або до розпорядження. Сеанс від’єднується, але зберігається. Блок знімається сам.

feat(v1.3)#05

Робочий час

Що сталось: вхід уночі чи в неділю ніхто не бачив.

Як вирішили: графік у панелі (за замовчуванням Пн–Сб 08:00–19:00). Вхід поза ним — запит власнику «Дозволити / Відхилити». Відмова з PIN від’єднує і блокує до ранку.

fix(ui)#06

Екран не вміщався в телефон

Що сталось: головний екран і вибір терміну блоку не вміщались у телефон.

Як вирішили: компактніший інтерфейс без прокрутки.

feat(v1.4)#07

Новий працівник однією кнопкою

Що сталось: треба додавати людину без адміна.

Як вирішили: одна кнопка створює обліковий запис, VPN-ключ і підписаний ярлик підключення (підпис прибирає попередження Windows). Ліміт — 5 на добу. «Видалити» = вимкнути вхід і VPN, профіль лишається.

fix(bug)#08

«Не вдалося додати працівника»

Що сталось: перший же тест — помилка. PowerShell не розрізняє регістр змінних, і мої $n та $N виявились однією. Номер нового користувача перезаписувався.

Як вирішили: перейменував змінні. Додав автоматичну перевірку коду на такі збіги перед кожною поставкою.

feat(v1.6)#09

Власник адмініструє сам

Що сталось: власник має сам адмініструвати, включно з живленням. Рядок фільтрів доводилось гортати вбік.

Як вирішили: власник і адмін — повні права, живлення з PIN. Нова роль «менеджер»: працівники, блоки, графік. Фільтри переверстано без горизонтальної прокрутки.

feat(v1.7)#10

Одноразові запрошення

Що сталось: замовник хотів додавати людей з адресної книги. Але бот не може написати першим тому, хто його не запускав, — це правило Telegram.

Як вирішили: одноразове посилання з роллю, дійсне 24 год. У базі лише його хеш. Активні запрошення можна скасувати.

feat(v1.8)#11

Обслуговування без дзвінків адміну

Що сталось: лишались дії, за якими все одно дзвонили адміну.

Як вирішили: новий пароль, повідомлення всім на екран, перезапуск з попередженням за 1/5/10/15 хв і скасуванням. «Стан сервера»: процесор, пам’ять, диски, бекап, попередження.

fix(perf)#12

Список працівників: з 12 с до 75 мс

Що сталось: список працівників перестав вкладатись у 12 с і падав за тайм-аутом. Причина: блокування Windows перевірялось окремо для кожного користувача.

Як вирішили: один запит на всіх — 75 мс. Першу перевірку стану зсунув на хвилину після старту, щоб не заважала.

ops(live)#13

Живий тест на справжньому сервері

Що сталось: треба перевірити все на справжньому сервері, не зачепивши людей.

Як вирішили: створили через бота тестового працівника. На ньому перевірили новий пароль і розсилку, після чого видалили й почистили. Перезапуск теж пройшов вживу.

feat(v1.9)#14

Відлік до перезапуску

Що сталось: замовнику потрібен таймер до перезапуску, потім «перезапускається» і «онлайн».

Як вирішили: живий відлік у панелі. У чаті — одне повідомлення, яке оновлюється, а не потік нових. «Стан» і «Керування» винесені на головний екран.

feat(v1.10)#15

Ліцензії й облікова програма

Що сталось: термін ліцензій віддаленого доступу і роботу облікової програми ніхто не відстежував.

Як вирішили: лічильник днів і попередження за 30/14/7/3/1/0 днів. Програма — тільки читання стану і нагадування щогодини, поки вона стоїть.

fix(ui)#16

Позначка стану вибивалась з рядка

Що сталось: на вузькому екрані позначка стану вилазила за рядок.

Як вирішили: позначки в один стовпчик, коротші підписи.

sec(v1.11)#17

Секрети — не в чат

Що сталось: таймер PIN ішов далі, поки PIN ще перевірявся. Паролі й архіви приходили в чат, де лежать завжди.

Як вирішили: таймер замирає на час перевірки. Секрети тепер у вкладці «Сповіщення» панелі на 15 хв, у чат — лише нагадування. Вкладки отримали значки. Перевірено на живому сервері.

ops(release)#18

Заміна старого бота без простою

Що сталось: новий бот має замінити старий так, щоб офіс жодної хвилини не лишався без керування.

Як вирішили: пакет переносу передає токен, переносить доступи і вимикає старий бот, не видаляючи його. Відкат вмикає старий назад. Передано власнику.

Дрібніші помилки, знайдені по дорозі

  • У PowerShell список із функції записом return , $list ламав фільтрацію далі по конвеєру — запрошення й лічильник сповіщень рахувались неправильно. Тепер результат спершу кладеться в змінну.
  • Два стилі панелі мали однакову назву і зіпсували вигляд таймера. Перейменував.
  • Заборона пересилання в Telegram блокувала б передачу архіву новому працівнику. Прибрав її, захист дає 15-хвилинний термін життя.
  • Для перевірки інтерфейсу зібрав локальний стенд: справжній код бота працює проти імітації Telegram і сервера. Верстку панелі перевіряв там, до поставки.

Уроки

  • Нове розгортати поруч зі старим, а не замість нього. Окремий тестовий бот дав тестувати на живому сервері, а власник жодного дня не лишався без керування.
  • Малі оновлення з відкатом дешевші за одне велике. Помилки з додаванням працівника і тайм-аутом списку виправились наступним же оновленням, бо було видно, де саме зламалось.
  • У PowerShell назви змінних не розрізняють регістр. Таку помилку очима не видно, тому тепер її ловить автоматична перевірка.
  • Питати систему одним запитом, а не в циклі. Різниця була між тайм-аутом у 12 с і 75 мс.
  • Секрети — не в чат. Чат зберігає все назавжди, а панель показує пароль 15 хв і забуває його.
  • Обмеження платформи формують інтерфейс. Бот не може написати першим, тому замість адресної книги з’явились одноразові запрошення.
  • Одне повідомлення, що оновлюється, краще за п’ять нових. Власник бачить стан перезапуску в одному місці, і чат не засмічується.
  • Локальний стенд швидший за живий сервер. Верстку і сценарії видно за секунди, а живий сервер лишається для фінальної перевірки.

Результат

Версія 1.11 працює на живому сервері, бот передано власнику. Вживу перевірені:

  • додавання, відключення і повернення працівника;
  • новий пароль і розсилка на екрани;
  • перезапуск з відліком і стан сервера;
  • сповіщення і завантаження архіву нового працівника.

До бота є покрокова інструкція для власника.