В одному абзаці
Коли тримаєш інфраструктуру сам, найважче – не підняти її, а
доглядати, бо з доглядом ніколи не горить. Два локальні гіпервізори
й один VPS у публічній хмарі тримають близько трьох десятків сервісів
для родини: внутрішні застосунки за зворотними проксі, власну АТС,
продубльований DNS у локальній мережі, а частину всього цього видно
з інтернету. Підтримувати це в робочому стані – одна робота; вчасно
латати, робити резервні копії і не давати конфігурації розповзатися –
зовсім інша, і вона змагається з основною роботою та здебільшого їй
програє. Прогалину закриває такий підхід: агент із довгим контекстом
як постійний напарник; репозиторій homelab-ops, де лежить кожна
конфігурація і кожне «чому» за нею; захищений бастіон, у якого ключі
від усього іншого; і непоказна рутина – щотижневі оновлення
контейнерів, розбір CVE, переходи баз даних на нові мажорні версії, –
що йде за розкладом, з резервною копією й автоматичним відкатом, а не
подвигами по тихих вечорах. 61 день від старту. 200 комітів, і кожен –
з поясненням.
Догляд, який більше не з’їдає увагу
Особиста інфраструктура занепадає, коли догляд за нею забирає увагу. Сисадмін-практик напише будь-який конфіг із пам’яті; а от тримати в голові перелік того, що цього тижня треба залатати й перевірити, і водночас тягнути основну роботу вже не вийде. Тож перелік губиться, образи контейнерів відстають на місяці, і що довша перерва, то важче потім усе пригадати й наздогнати. Тут перелік переїжджає з голови оператора туди, де агент із довгим контекстом перечитує його на початку кожної сесії, а рутинний догляд стає на розклад, який сам робить собі резервну копію і сам себе відкочує.
- образи контейнерів відстають на місяці, а оновити їх вручну – це цілий вечір
- сканер 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, а не покладається на везіння.
Автовідкат, що спрацював через хибну тривогу перевірки стану, – сам собою аварія. Страховка в тому, що відкат іде тим самим перевіреним шляхом, що й оновлення, – 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.
- ● 01CLAUDE.mdроль агента + робочі процедури
- ● 02покрокові інструкціїдодати/прибрати/оновити сервіс · ротація секретів · відновлення · нова версія БД
- ● 039 списків дозволів sudoпо одному на хост · розбіжності щодня летять у канал сповіщень
- ● 04контрольні зрізиSUID + cron + відкриті порти · звіряються щодня
- ● 05еталонні конфігизворотний проксі · локальний DNS · unattended-upgrades · контейнери
- ● 01ноутбук1 SSH-ключ
- ● 02бастіонмежа
- ● 038 хостівпарк
- один ключ із вузькими правами
- довірений, завжди ввімкнений
- ● 01вузол SSHноутбук → бастіон → кожен хост · ключів більше ніде немає
- ● 0214 перевірок безпекиsudoers · SSH · невдалі входи · SUID · сертифікати · щодня
- ● 03~25 перевірок станудиск · RAM · сервіси · резервні копії · mesh · кворум · щодня
- ● 04щотижневе сканування CVETrivy · з огляду на відкритість · лише те, що можна виправити · звіт щосуботи
- ● 05вебхук сповіщеньсповіщає про проблеми · мовчить, коли все гаразд
- ● 01автооновлення 5 ночей на тижденьпо черзі на різних хостах · копія + відкат на кожен запуск
- ● 02щотижневе оновлення стекабекенд-стек із 13 контейнерів · pg_dumpall + перевірка стану + відкат
- ● 03deploy / remove-serviceAnsible · копія → проксі → DNS ×2 → перевірка, і те саме навпаки
- ● 04upgrade-container.ymlAnsible · оновлення закріплених тегів, а перед тим – знімок
- ◐ 05нові мажорні версії БДдамп → перевірка сумісності → оновлення → логи на очах → перевірка · по одній
28 чистих ранків із 30 по всьому парку. Єдиний червоний день –
справжній улов: правило podman exec із зірочкою, яке з’явилося під
час нічного налагодження, і його так і не прибрали; cron його помітив,
а наступна сесія відкликала. Єдиний жовтий день – разовий тайм-аут
перевірки сертифіката на публічному VPS, який минув сам при наступній
перевірці. Від cron чекають не тиші, а того, щоб оператор не міг
проґавити неспокійний ранок.
Заплановано
- ○ 01мережевий шлюз + два канали в інтернетфаєрвол L3 із відстеженням з'єднань + IDS/IPS · замінить ручне перемикання між каналами
- ○ 02поділ мережі на VLANпоки що одна локальна мережа на все · відгородити хост із Windows і розумні пристрої
- ○ 03власна фототекабібліотека + пошук за обличчями/CLIP на гостьовій машині · чекає на переїзд сховища
- ○ 04решта нових версій БДPostgres 15/17 → 18 + векторне сховище · за розкладом, по одній
- ○ 05віддалені резервні копіїспершу NAS через Samba · згодом копія в хмарному об'єктному сховищі
Найцінніші сесії – нудні
Поки що все здебільшого працює на запит: Макс називає завдання, агент виконує його від початку до кінця. Але самі завдання змінилися – на місці гасіння пожеж тепер догляд, і найцінніші сесії зараз найнудніші: щотижневе оновлення, яке написали раз і лишили працювати; лавина CVE, зведена до трьох справжніх пунктів; нова мажорна версія бази, якої сайт клієнта навіть не помітив. Проєкт рухається до того, щоб агент діяв на випередження: cron на бастіоні вже безперервно стежить за станом і здіймає тривогу, а наступний крок – щоб агент сам починав розслідування, пропонував виправлення й чекав лише на «так» від Макса. Особиста інфраструктура – полігон із малим радіусом ураження, де це коло замикають, перш ніж переносити його на клієнтські парки, на порядок більші.
61 день. Один оператор. 200 комітів, і кожен – з поясненням. ~38 сервісів, 8 хостів. Догляд, на який раніше йшов цілий тихий вечір, тепер іде за розкладом – з резервною копією, перевіркою стану й можливістю відкотитися.
Тут описано особисту інфраструктуру, яку ведуть як повноцінний проєкт з експлуатації. Ролі хостів і категорії постачальників названо за функцією; конкретні продукти, імена хостів і залізо приховано, бо цінність кейсу – у способі роботи, а не в тому, що саме купили.