Особисті ШІ-операції – багатоагентний напарник з експлуатації

Особисту інфраструктуру одного адміністратора доглянуто не гірше за промислову, бо постійним напарником тут працює агент із довгим контекстом: кожна конфігурація лежить у репозиторії, кожен ключ – на захищеному бастіоні, а рутина, з якою ніколи не горить (щотижневі оновлення, розбір CVE, переходи баз даних на нові мажорні версії), іде за розкладом – з резервною копією й автоматичним відкатом, а не подвигами по тихих вечорах.

час на побудову
61 день розробки · триває
вперше опубліковано
востаннє оновлено

В одному абзаці

Коли тримаєш інфраструктуру сам, найважче – не підняти її, а доглядати, бо з доглядом ніколи не горить. Два локальні гіпервізори й один VPS у публічній хмарі тримають близько трьох десятків сервісів для родини: внутрішні застосунки за зворотними проксі, власну АТС, продубльований DNS у локальній мережі, а частину всього цього видно з інтернету. Підтримувати це в робочому стані – одна робота; вчасно латати, робити резервні копії і не давати конфігурації розповзатися – зовсім інша, і вона змагається з основною роботою та здебільшого їй програє. Прогалину закриває такий підхід: агент із довгим контекстом як постійний напарник; репозиторій homelab-ops, де лежить кожна конфігурація і кожне «чому» за нею; захищений бастіон, у якого ключі від усього іншого; і непоказна рутина – щотижневі оновлення контейнерів, розбір CVE, переходи баз даних на нові мажорні версії, – що йде за розкладом, з резервною копією й автоматичним відкатом, а не подвигами по тихих вечорах. 61 день від старту. 200 комітів, і кожен – з поясненням.

0 днів
від першого коміту
0 комітів
і кожен – з поясненням
0 сервісів
на 8 хостах
0 ночей/тиждень
автооновлення по черзі, з відкатом

Догляд, який більше не з’їдає увагу

Особиста інфраструктура занепадає, коли догляд за нею забирає увагу. Сисадмін-практик напише будь-який конфіг із пам’яті; а от тримати в голові перелік того, що цього тижня треба залатати й перевірити, і водночас тягнути основну роботу вже не вийде. Тож перелік губиться, образи контейнерів відстають на місяці, і що довша перерва, то важче потім усе пригадати й наздогнати. Тут перелік переїжджає з голови оператора туди, де агент із довгим контекстом перечитує його на початку кожної сесії, а рутинний догляд стає на розклад, який сам робить собі резервну копію і сам себе відкочує.

як змінюється цикл обслуговування 61 день, один оператор
без напарника
  • образи контейнерів відстають на місяці, а оновити їх вручну – це цілий вечір
  • сканер CVE видає ~1900 знахідок, і до жодної руки так і не доходять
  • перехід бази даних на нову мажорну версію – ритуал, від якого душа в п'ятах, тож його відкладають роками
  • секрети осідають просто в compose-файлах, що лежать у git
  • процедуру відновлення щоразу винаходять заново посеред аварії
з напарником
  • стеки оновлюються щотижня по черзі: перед кожним запуском – резервна копія, а за збою – автоматичний відкат
  • лавину CVE сортують за тим, чи видно образ ззовні і чи є виправлення, – і з 1886 сирих знахідок лишаються три образи, які справді варто чіпати
  • нова мажорна версія щоразу йде тим самим переліком кроків: дамп → перевірка сумісності → оновлення → логи на очах → перевірка → дамп лишається
  • усі секрети винесено з git, і щотижня їх звіряють із контрольним зрізом
  • покрокові інструкції для операцій, які вже раз ламалися

Межі, в яких агента можна лишити самого

Два архітектурні рішення, незмінні з першого тижня, перетворюють звичайне «вікно з агентом, відкрите, поки я працюю» на напарника, якого можна безпечно лишити працювати за розкладом без нагляду. Єдиний SSH-ключ на ноутбуці відчиняє рівно один хост – LXC-бастіон, а вже бастіон тримає окремі ключі до кожного з решти хостів. Тож якщо витече репозиторій, нападник закріпиться лише на бастіоні, а решта парку лишиться недоторканою. А ще на кожному хості є користувач luna з вузькими правами і переліком дозволених sudo-команд, який лежить у репозиторії. Агент працює всередині цієї межі, а все небезпечне лишається за нею, на обліковому записі самого оператора; щоденне порівняння показує будь-яке розходження вже наступного ранку. Саме завдяки цій межі щотижневе завдання в cron може перезапустити стек із 13 контейнерів чи перебудувати базу даних о 02:00, і людині не треба при цьому чергувати.

Кожна робоча конфігурація – sudoers, ключі для розгортання, unattended-upgrades, профілі контейнерів, маршрути зворотного проксі, записи локального DNS – має в репозиторії еталонний файл, а поруч – щоденну перевірку на розбіжності. Перш ніж запропонувати зміну, агент щоразу читає CLAUDE.md, потрібний файл пам’яті й еталон цільового хоста. Так знання на кшталт «записи локального DNS живуть у масиві hosts основного конфігу, а не в допоміжному списку – переплутаєш, і при наступному перезавантаженні конфігу їх мовчки затре» чи «sudo-rs на цій гостьовій машині не розуміє шаблонів із зірочкою для підкоманд» живуть поза будь-якою окремою сесією, і агент підхоплює їх у кожній новій.

де лежать ключі три очевидні варіанти · обрано один
критерій ключі на ноутбуці (один перехід) ключі на бастіоні (два переходи) обрано ключі на кожному хості (агент усередині)
радіус ураження, якщо витече репозиторій увесь парк лише бастіон репозиторію на хості немає
працює з нового ноутбука треба перевипускати ключ так – один ключ до бастіона прив'язано до хоста
де видно, що запускав агент логи розкидані усе в журналах бастіона розкидано
що стримує руйнівні операції в агента є sudo дозволи luna + еталон на кожному хості по-своєму
cron працює, коли ноутбук спить ні так так

Симулятор радіуса ураження

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

доступно хостів: –аудит: –відновлення: –

працює у вашому браузері · 0 мережевих запитів · щоразу той самий результат

Три сесії зсередини

Три сесії з останніх двох тижнів, одна за одною: агент будує щотижневе оновлення без нагляду для бекенд-стека з 13 контейнерів, розбирає сканування CVE з майже двома тисячами знахідок і переводить живу базу даних на нову мажорну версію. Щоразу він сам проходить усе коло – розібратися, вирішити, зробити, перевірити, звітувати, – і кожен сценарій закінчується рядком success.

Що агент читає, перш ніж узятися до роботи

Перед будь-якою операцією – привітання. На початку кожної сесії агент підвантажує два файли: CLAUDE.md пояснює, хто він такий і за якими правилами працює («обережний сисадмін, робить резервну копію перед руйнівними операціями, питає дозволу перед усім, чого не відкотити, за замовчуванням говорить українською»), а тека memory/* – це зібраний за попередні сесії перелік підводних каменів, файл за файлом. Саме привітання нічим не примітне; важить те, що все подальше агент починає, уже прочитавши обидва файли.

Далі – три розмови, і всі про догляд: про ту непоказну половину роботи, яка змагається з основною і здебільшого їй програє. Агент пише щотижневе оновлення без нагляду для стека з 13 контейнерів, які оновлюються тільки разом; розбирає сканування CVE з такою сирою цифрою, що в оператора опустилися б руки; і переводить живу базу даних на нову мажорну версію так, що сайт цього навіть не помічає. Щоразу агент сам проходить усе коло: розібратися, вирішити, зробити, перевірити, звітувати.

Одну річ про довіру варто сказати одразу, бо тільки завдяки їй усе це можна показати публічно: до серверів агент дістається лише через LXC-бастіон. SSH-ключі до кожного хоста лежать там і більше ніде, тож навіть якби цю сесію в терміналі перехопили, жоден ключ до хоста не витік би разом із нею. З тієї ж причини cron може запускати ці оновлення, поки всі сплять.

Стек, який оновлюється тільки цілком, тепер оновлюється сам

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

Задум агента окупається саме там, де зазвичай зрізають кути. Спершу він робить резервну копію – логічний дамп плюс архів томів, – а тоді керує юнітами systemd, а не стартовим скриптом стека: у cron пароль вводити нікому, і наївний шлях мовчки обривається на півдорозі, а обидва стеки лишаються лежати. Остаточно переходить на нову версію агент лише тоді, коли всі 13 контейнерів справні, а студія відповідає 200; за будь-якого збою спрацьовує гілка rescue і повертає останній коміт, про який точно відомо, що він працював. Ця оболонка – копія, спроба, перевірка стану, автоматичний відкат – і є вся суть.

Для бізнесу це означає, що стек із 13 контейнерів тепер оновлюється сам і нікому не треба пильнувати перезапуск о 02:00, а єдиного разу, коли оновлення таки зламалося, система за кілька хвилин сама повернулася до робочого стану, і вранці оператора не зустріла аварія. Цей збій не вигаданий: перший справжній запуск попередньої версії потрапив саме в описану вище пастку з cron і sudo. Тому показаний тут варіант перевіряє стан і працює через systemd, а не покладається на везіння.

сеанс звʼязку 02:14 · bastion-lxc

Автовідкат, що спрацював через хибну тривогу перевірки стану, – сам собою аварія. Страховка в тому, що відкат іде тим самим перевіреним шляхом, що й оновлення, – checkout потрібного коміту, тільки у зворотний бік, – а не саморобним скриптом відновлення, який пишуть похапцем о другій ночі.

А що плейбук лежить у репозиторії, наступний такий стек у парку отримає ту саму оболонку за ціною одного нового файлу змінних. І цей підхід переноситься далі: «спершу копія, потім перевірка стану, потім нова версія» – та сама дисципліна, якої клієнт хотів би довкола кожної зміни без нагляду, тільки налагоджена тут, де радіус ураження – одна родина.

Як лавина CVE перетворюється на три рішення

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

Завдання агента – перетворити цю лавину на короткий список рішень, і сортує він за двома питаннями: чи можна до образу дістатися ззовні і чи цю вразливість узагалі можна виправити. Спершу агент відкидає все, для чого виправлення ще не випустили, а решту зважує за тим, наскільки образ відкритий: до образів на публічному VPS планка значно суворіша, ніж до внутрішніх, захованих за mesh-мережею та автентифікацією на проксі. Так 1886 сирих знахідок стискаються до трьох образів, які справді варто чіпати цього вечора.

Чесним наступне сканування тримає звичка записувати, чому решту лишили. Здебільшого це постійні дрібні зміни в базових образах, версії яких закріпили самі автори скоординованих стеків; якщо їх латати, випуск зламається, тож їх записують як «прийнято з причиною» (accept-with-reason), а не тихо ігнорують. Суботній звіт наступного тижня до них уже не повертається – показує лише те, що змінилося. «Прийнято з причиною» – той самий хід, що й перевірки на розбіжності в інших місцях репозиторію: мета – не нуль знахідок, а нуль неперевірених. Записане прийняття ризику – це рішення; проігнороване попередження – це борг.

Для бізнесу це означає, що щотижневий звіт несе сигнал, а не шум. Оператор латає 3 образи, а не 28, і одним реченням може пояснити, чому інші 25 безпечно не чіпати. Саме тут різниця між продуманою безпекою і червоною стіною, яку будь-яка притомна людина швидко навчиться прогортати не дивлячись.

Нова мажорна версія бази даних, якої сайт навіть не помітив

Перехід бази даних на нову мажорну версію відкладають роками, і небезпідставно: з усієї рутини в нього найбільший радіус ураження, а якщо щось піде не так, сайт клієнта віддаватиме 500 кожному відвідувачеві. «Обережно» тут означає не сміливість, а незмінний перелік кроків, який щоразу проходить однаково, тож від нервів оператора не залежить нічого.

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

Крок, який людина в поспіху пропускає, – подивитися, як стартує рушій. Агент стежить за журналом запуску й читає його, перш ніж робити висновки: чистий рядок «ready for connections» чи цикл аварійного відновлення або список таблиць, що не піднялися, – це різниця між тим, щоб лишити нову версію, і відкатом, і в журналі її видно на цілу хвилину раніше, ніж перший відвідувач помітив би повільну сторінку. Остаточно переходить на нову версію агент лише тоді, коли живий сайт відповідає 200, а сторінка з даними з бази відкривається.

Для бізнесу це означає, що оновлення, під яке фрилансер заклав би в кошторис технічну перерву, стає запланованою операцією, за якою стежать і яку можна відкотити, – і сайт клієнта пройшов крізь неї, не спіткнувшись. Єдиний слід, який бачить власник, – рядок у журналі змін і дамп, що зберігається сім днів про всяк випадок. Справжній результат тут – сам перелік кроків, а не MariaDB 11: його один раз записано як покрокову інструкцію, і кожне наступне оновлення – Postgres, векторне сховище, наступний клієнт – отримує ту саму оболонку. Номер версії змінюється; порядок дій – ні.

Парк серверів, бастіон і механізми догляду

Два локальні гіпервізори і VPS у публічній хмарі, на якому живуть чотири публічні сайти; ~38 сервісів на 8 хостах; власна АТС із SIP-транком та IP-телефоном; продубльований DNS у локальній мережі; вихідний тунель, через який окремі внутрішні сервіси видно ззовні; і локальний сервер резервних копій, що знімає знімки гостьових машин на гіпервізорах і публічного VPS.

репозиторій першоджерело · 200 комітів · 61 день
  1. ● 01
    CLAUDE.md
    роль агента + робочі процедури
  2. ● 02
    покрокові інструкції
    додати/прибрати/оновити сервіс · ротація секретів · відновлення · нова версія БД
  3. ● 03
    9 списків дозволів sudo
    по одному на хост · розбіжності щодня летять у канал сповіщень
  4. ● 04
    контрольні зрізи
    SUID + cron + відкриті порти · звіряються щодня
  5. ● 05
    еталонні конфіги
    зворотний проксі · локальний DNS · unattended-upgrades · контейнери
як тримається межа один ключ на вході, усе інше – за ним
  1. ● 01
    ноутбук
    1 SSH-ключ
  2. ● 02
    бастіон
    межа
  3. ● 03
    8 хостів
    парк
  • один ключ із вузькими правами
  • довірений, завжди ввімкнений
бастіон – очі агента і його межа легкий LXC · завжди увімкнений · звідси агент працює
  1. ● 01
    вузол SSH
    ноутбук → бастіон → кожен хост · ключів більше ніде немає
  2. ● 02
    14 перевірок безпеки
    sudoers · SSH · невдалі входи · SUID · сертифікати · щодня
  3. ● 03
    ~25 перевірок стану
    диск · RAM · сервіси · резервні копії · mesh · кворум · щодня
  4. ● 04
    щотижневе сканування CVE
    Trivy · з огляду на відкритість · лише те, що можна виправити · звіт щосуботи
  5. ● 05
    вебхук сповіщень
    сповіщає про проблеми · мовчить, коли все гаразд
механізми догляду догляд за розкладом
  1. ● 01
    автооновлення 5 ночей на тиждень
    по черзі на різних хостах · копія + відкат на кожен запуск
  2. ● 02
    щотижневе оновлення стека
    бекенд-стек із 13 контейнерів · pg_dumpall + перевірка стану + відкат
  3. ● 03
    deploy / remove-service
    Ansible · копія → проксі → DNS ×2 → перевірка, і те саме навпаки
  4. ● 04
    upgrade-container.yml
    Ansible · оновлення закріплених тегів, а перед тим – знімок
  5. ◐ 05
    нові мажорні версії БД
    дамп → перевірка сумісності → оновлення → логи на очах → перевірка · по одній
cron бастіона · дні без розбіжностей останні 30 ранків
93% зелених
зелених 28 нестабільних 1 червоних 1

28 чистих ранків із 30 по всьому парку. Єдиний червоний день – справжній улов: правило podman exec із зірочкою, яке з’явилося під час нічного налагодження, і його так і не прибрали; cron його помітив, а наступна сесія відкликала. Єдиний жовтий день – разовий тайм-аут перевірки сертифіката на публічному VPS, який минув сам при наступній перевірці. Від cron чекають не тиші, а того, щоб оператор не міг проґавити неспокійний ранок.

Заплановано

  1. ○ 01
    мережевий шлюз + два канали в інтернет
    фаєрвол L3 із відстеженням з'єднань + IDS/IPS · замінить ручне перемикання між каналами
  2. ○ 02
    поділ мережі на VLAN
    поки що одна локальна мережа на все · відгородити хост із Windows і розумні пристрої
  3. ○ 03
    власна фототека
    бібліотека + пошук за обличчями/CLIP на гостьовій машині · чекає на переїзд сховища
  4. ○ 04
    решта нових версій БД
    Postgres 15/17 → 18 + векторне сховище · за розкладом, по одній
  5. ○ 05
    віддалені резервні копії
    спершу NAS через Samba · згодом копія в хмарному об'єктному сховищі

Найцінніші сесії – нудні

Поки що все здебільшого працює на запит: Макс називає завдання, агент виконує його від початку до кінця. Але самі завдання змінилися – на місці гасіння пожеж тепер догляд, і найцінніші сесії зараз найнудніші: щотижневе оновлення, яке написали раз і лишили працювати; лавина CVE, зведена до трьох справжніх пунктів; нова мажорна версія бази, якої сайт клієнта навіть не помітив. Проєкт рухається до того, щоб агент діяв на випередження: cron на бастіоні вже безперервно стежить за станом і здіймає тривогу, а наступний крок – щоб агент сам починав розслідування, пропонував виправлення й чекав лише на «так» від Макса. Особиста інфраструктура – полігон із малим радіусом ураження, де це коло замикають, перш ніж переносити його на клієнтські парки, на порядок більші.

61 день. Один оператор. 200 комітів, і кожен – з поясненням. ~38 сервісів, 8 хостів. Догляд, на який раніше йшов цілий тихий вечір, тепер іде за розкладом – з резервною копією, перевіркою стану й можливістю відкотитися.


Тут описано особисту інфраструктуру, яку ведуть як повноцінний проєкт з експлуатації. Ролі хостів і категорії постачальників названо за функцією; конкретні продукти, імена хостів і залізо приховано, бо цінність кейсу – у способі роботи, а не в тому, що саме купили.