Обрано побайтову точність архіву замість маскування — без неї відновлення з архіву не працює. Наслідок прийнято свідомо: доступ до дзеркала рівносильний доступу до всіх паролів у мережі. Маскування лишається там, де конфіг читає людина: план відкату й стенограми завдань. Записано, щоб не переобговорювати. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
522 lines
34 KiB
Markdown
522 lines
34 KiB
Markdown
# Розгортання NetPulse
|
||
|
||
Один хост, `docker compose`, автоматичний TLS. Такого розгортання
|
||
вистачає до кількох тисяч хостів на моніторингу; розносити служби по
|
||
машинах має сенс тоді, коли впирається БД, а не застосунок.
|
||
|
||
## Що з чого складається
|
||
|
||
| Служба | Роль | Порт |
|
||
| ----------- | ------------------------------------------------ | ----- |
|
||
| `db` | PostgreSQL 16 + TimescaleDB — усі дані | — |
|
||
| `cache` | DragonflyDB — черги й тимчасові стани | — |
|
||
| `migrate` | накочування схеми; відпрацьовує і зупиняється | — |
|
||
| `api` | HTTP API + вшитий інтерфейс | 8080 |
|
||
| `collector` | gRPC-колектор, до якого підключаються зонди | 9443 |
|
||
| `proxy` | Caddy: сертифікати, HTTPS, проксі до двох служб | 80/443/9443 |
|
||
|
||
Історія конфігів живе в Git на томі `git-data`, спільному для `api` й
|
||
`collector`: перший читає її для порівняння версій, другий пише під час
|
||
бекапу.
|
||
|
||
Назовні дивиться лише `proxy`. `api` і `collector` портів не публікують:
|
||
до них ходять через нього.
|
||
|
||
## Перший запуск
|
||
|
||
```sh
|
||
git clone <репозиторій> netpulse && cd netpulse
|
||
cp deploy/.env.example .env
|
||
```
|
||
|
||
Заповнити `.env`. Три значення обовʼязкові й генеруються так:
|
||
|
||
```sh
|
||
openssl rand -base64 24 # POSTGRES_PASSWORD
|
||
echo "np1=$(openssl rand -hex 32)" # NETPULSE_DEK
|
||
openssl rand -base64 48 # NETPULSE_JWT_SECRET
|
||
```
|
||
|
||
Далі:
|
||
|
||
```sh
|
||
docker compose up -d --build
|
||
docker compose run --rm --entrypoint netpulse-user cli \
|
||
-tenant default -login admin -role owner -name "Адміністратор"
|
||
```
|
||
|
||
Пароль команда спитає інтерактивно — щоб він не осів в історії оболонки
|
||
й у списку процесів.
|
||
|
||
Інтерфейс — на `https://<NETPULSE_DOMAIN>`.
|
||
|
||
## Пісочниця: повна установка, яку можна перевірити
|
||
|
||
Твердження «нова інсталяція піднімається сама» довго було доведене
|
||
міркуванням, а не запуском. Сухий прогін проходив, окремі кроки
|
||
перевірялись на живій базі — а повної установки з нуля не робив ніхто,
|
||
бо ніде: єдина доступна машина була бойовим стендом на 4 ГБ, і другий
|
||
повний стек поклав би робочу систему.
|
||
|
||
`./netpulse sandbox` ставить NetPulse **по-справжньому**, з тим самим
|
||
`docker-compose.yml`, тими самими міграціями й тією самою
|
||
самоперевіркою, але в окремому проєкті `compose` з власними томами й
|
||
портами на `127.0.0.1`. Це не імітація: вона або справді піднімає стек і
|
||
заходить у нього справжнім паролем, або зупиняється й каже, на чому.
|
||
|
||
```sh
|
||
./netpulse sandbox # підняти й лишити, щоб подивитись
|
||
./netpulse sandbox once # підняти, перевірити, знести все
|
||
./netpulse sandbox upgrade # другий режим: оновлення з версії на версію
|
||
./netpulse sandbox check # та сама перевірка ще раз
|
||
./netpulse sandbox status # що вона зараз займає
|
||
./netpulse sandbox down # знести все (обидва режими), включно з томами
|
||
./netpulse sandbox logs [служба] # журнали
|
||
```
|
||
|
||
Режимів два, і вони перевіряють різне: `sandbox` — установку з нуля,
|
||
`sandbox upgrade` — шлях «стояла стара версія, стала нова». Другий
|
||
описано нижче окремим розділом.
|
||
|
||
**Розробникові перед випуском** — `./netpulse sandbox once`. Він робить
|
||
повну установку з нуля, проганяє всі твердження самоперевірки (вхід
|
||
справжнім паролем, кабінет назвався, сім переліків із міграцій
|
||
непорожні, дванадцять ендпоїнтів відповідають, зонд зареєструвався в
|
||
колекторі) і прибирає за собою до останнього тому. Ненульовий код
|
||
виходу означає, що цю збірку клієнтові віддавати не можна.
|
||
|
||
**Клієнтові — щоб подивитись до того, як ставити** — `./netpulse
|
||
sandbox` без підкоманди. Після установки вона друкує адресу
|
||
`https://localhost:<порт>`, логін і пароль; система жива, у ній є
|
||
кабінет, локальний зонд і всі довідники. Подивились — `./netpulse
|
||
sandbox down`, і на машині не лишається нічого.
|
||
|
||
### Скільки це коштує
|
||
|
||
| Що | Скільки |
|
||
| -------------- | ------------------------------------------------------------ |
|
||
| Пам'ять у роботі | ~550–900 МБ на всі шість служб |
|
||
| Стеля пам'яті | ~2.05 ГБ — жорстке обмеження з накладки, вище не підніметься |
|
||
| Пік при збірці | ще ~1–2 ГБ, поки збираються образи Go |
|
||
| Диск: томи | ~250 МБ (порожня база зі схемою, внутрішній CA Caddy, посвідчення зонда) |
|
||
| Диск: образи | ~2 ГБ, **спільні** з бойовою інсталяцією — якщо вони вже зібрані, пісочниця не додає нічого |
|
||
| Порти | 18080, 18081, 18082 (або наступна вільна трійка), усі на `127.0.0.1` |
|
||
|
||
Точні цифри після запуску показує `./netpulse sandbox status` — вони
|
||
виміряні, а не оцінені.
|
||
|
||
### Коли її запускати НЕ можна
|
||
|
||
Установник перевіряє це сам і **відмовляється**, а не пробує:
|
||
|
||
- **вільно менше 3 ГБ пам'яті** (4 ГБ, якщо образи ще треба зібрати).
|
||
Міряється `MemAvailable`, а не вся пам'ять: на машині, де вже працює
|
||
бойовий стек, «4 ГБ встановлено» не має жодного стосунку до того,
|
||
скільки з них можна взяти;
|
||
- **вільно менше 3 ГБ диска** (8 ГБ без готових образів);
|
||
- **на машині працює бойова інсталяція NetPulse.** Пісочниця не зіпсує
|
||
їй ані даних, ані портів — вона в іншому проєкті `compose`. Але
|
||
пам'ять і диск у них спільні, і два Postgres не вміщуються там, де
|
||
ледве вміщується один. Обійти можна прапорцем `--alongside`, і це
|
||
свідоме рішення, а не формальність;
|
||
- **docker compose старший за 2.24.4** — див. нижче;
|
||
- **пісочниця вже стоїть.** Друга поверх першої поділила б із нею томи,
|
||
і прогін «з нуля» перестав би бути прогоном з нуля.
|
||
|
||
Правильне місце для пісочниці — машина розробника або окрема віртуалка.
|
||
|
||
### Що буде при обриві
|
||
|
||
Пісочниця, яка лишила по собі том на 250 МБ і контейнер, що тримає
|
||
порт, — це та сама шкода, від якої вона мала захистити. Тому:
|
||
|
||
- `Ctrl-C`, `SIGTERM`, `SIGHUP` перехоплюються і прибирають усе;
|
||
- будь-яка зупинка установки (`ЗУПИНКА на кроці …`) теж прибирає все;
|
||
- у накладці стоїть `restart: "no"` — забута пісочниця **не воскресає**
|
||
після перезавантаження хоста;
|
||
- прибирання йде трьома ешелонами: `compose down -v`, потім пряме
|
||
`docker rm`/`docker volume rm` за міткою проєкту (ловить те, що
|
||
лишилось від обірваного `up`), потім видалення `.env.sandbox`;
|
||
- `--keep` лишає уламки для розбору журналів — але тільки при **невдачі
|
||
установки**, не при сигналі: перерваний прогін лишає по собі не стенд,
|
||
а половину стенду.
|
||
|
||
Чого перехопити неможливо: `kill -9` і зникнення живлення. Саме тому
|
||
джерелом правди про залишки є не файл-позначка, а сам docker — і
|
||
`./netpulse install` та `./netpulse check` при кожному запуску кажуть,
|
||
якщо на машині висить забута пісочниця.
|
||
|
||
### Чого пісочниця НЕ доводить
|
||
|
||
Це найважливіший абзац розділу. Зелена перевірка доводить тільки те, що
|
||
вона перевіряє — цей проєкт уже платив за протилежне припущення.
|
||
|
||
- **Let's Encrypt і DNS.** Пісочниця стоїть на `localhost` із
|
||
самопідписаним сертифікатом і навмисно не читає `netpulse.conf`:
|
||
справжній `DOMAIN` звідти відправив би її по сертифікат для адреси,
|
||
яка веде на бойовий стенд, і витрачені спроби списались би з тижневої
|
||
квоти домену.
|
||
- **Прийом SNMP-трапів і правило `DOCKER-USER`.** Порт 162/udp назовні
|
||
не виставляється взагалі: він не має автентифікації, і відкривати
|
||
його заради перевірки означало б купити перевірку ціною дірки.
|
||
- **Розрахунок `shared_buffers` із пам'яті хоста.** У пісочниці він
|
||
заданий числом — це умова того, щоб вона нічого не поклала.
|
||
- **Поведінка під навантаженням.** База порожня, хостів на моніторингу
|
||
немає, історії немає.
|
||
|
||
### Чому потрібен compose 2.24.4
|
||
|
||
Списки `ports` при накладанні compose-файлів **додаються**, а не
|
||
замінюються. Щоб зсунуті порти пісочниці не стали *додатком* до базових
|
||
80/443/9443, накладка перевизначає їх тегом `!override`, а він
|
||
з'явився у docker compose 2.24.4. На старішій версії пісочниця
|
||
відмовляється працювати — бо мовчки зайняти порти бойового проксі
|
||
гірше, ніж не запуститись.
|
||
|
||
Вимога знімається одним рядком у `docker-compose.yml`: якщо зробити
|
||
базові порти змінними —
|
||
|
||
```yaml
|
||
ports:
|
||
- "${NETPULSE_BIND:-0.0.0.0}:${NETPULSE_PORT_HTTP:-80}:80"
|
||
- "${NETPULSE_BIND:-0.0.0.0}:${NETPULSE_PORT_HTTPS:-443}:443"
|
||
- "${NETPULSE_BIND:-0.0.0.0}:${NETPULSE_PORT_GRPC:-9443}:9443"
|
||
```
|
||
|
||
— то пісочниці вистачить власного `.env.sandbox`, накладка портів стане
|
||
непотрібною, а мінімальна версія compose лишиться 2.0, як у решті
|
||
установника.
|
||
|
||
## Пісочниця, режим другий: оновлення з версії на версію
|
||
|
||
Установку з нуля клієнт робить один раз, а оновлення — щоразу. І
|
||
ламається воно частіше: міграції котяться не на порожню базу, а на ту, де
|
||
вже лежать чужі дані; конфігурація змінюється; зонд у мережі клієнта
|
||
лишається старим, а колектор стає новим. Досі цього не перевіряло нічого,
|
||
тобто перевіряв перший клієнт.
|
||
|
||
```sh
|
||
./netpulse sandbox upgrade # попередня версія обереться сама
|
||
./netpulse sandbox upgrade --from REF # попередня версія — цей коміт або тег
|
||
./netpulse sandbox upgrade --keep # лишити стенд після прогону
|
||
```
|
||
|
||
Прогін іде п'ятьма етапами, і кожен друкує свою назву — падіння називає
|
||
етап, а не лише крок:
|
||
|
||
| Етап | Що відбувається |
|
||
| ---- | --------------- |
|
||
| **0** | вибір попередньої версії й розгортання її дерева |
|
||
| **А** | попередня версія ставиться **з нуля** тим самим `install` і проходить ту саму самоперевірку |
|
||
| **Б** | у базу наливаються локації, 24 хости, доба ICMP і метрик із кроком 5 хв, конфіги NCM, тригер і алерти; знімаються кількості «до» |
|
||
| **В** | дамп, збірка нової версії, **міграції поверх наявних даних**, перезапуск служб, перевірка старого зонда проти нового колектора, оновлення зонда |
|
||
| **Г** | та сама `selfcheck`, що й після чистої установки |
|
||
| **Д** | кількості «після» проти «до», по рядках |
|
||
|
||
За замовчуванням стенд зноситься разом із томами: це ворота випуску, а не
|
||
стенд для розглядання. `--keep` лишає все на місці — і тоді прибрати
|
||
**обов'язково**: `./netpulse sandbox down`.
|
||
|
||
### Що таке «попередня версія», якщо версіонування ще немає
|
||
|
||
Питання не риторичне: від відповіді залежить, чого вартий прогін. Тегів
|
||
немає, реєстру немає, образ завжди `netpulse/server:dev`.
|
||
|
||
**Обрано коміт git.** Коміт — це повне дерево: Go-код, міграції,
|
||
`docker-compose.yml`, `Caddyfile`, `Dockerfile`. З нього збираються
|
||
справжні старі образи, тобто попередня версія тут не описана, а
|
||
**виконується**. Це відтворювано: той самий ref дасть той самий стенд і
|
||
за півроку.
|
||
|
||
І це не тимчасове рішення. Тег у git — теж ref, тому в день першого тегу
|
||
тут не зміниться жодного рядка: `--from v0.1.0` запрацює сам.
|
||
|
||
Чому не інакше:
|
||
|
||
* **Тег.** Найправильніше — і неможливе сьогодні: перевірка оновлення є
|
||
вхідним квитком *до* першого тегу (ROADMAP, Етап 13). Вимагати тег
|
||
означало б вимагати те, заради чого вона й пишеться.
|
||
* **Збережений дамп бази.** Найдешевше й доводить найменше. Дамп — це
|
||
схема з даними, але не бінарники: він не запускає старий колектор і
|
||
старий зонд, тобто не бачить двох названих класів поломки з трьох.
|
||
Гірше інше: дамп старіє мовчки. Його зробила версія, яку вже ніхто не
|
||
збере, і коли прогін почервоніє, розрізнити «зламався код» і «протух
|
||
дамп» буде нічим.
|
||
|
||
Типовий ref обчислюється, а не вписаний числом: береться найновіший
|
||
коміт, у якого міграцій **менше**, ніж у HEAD. Причина та сама, що й у
|
||
решті установника: зелений прогін, який нічого не перевірив, гірший за
|
||
відсутність прогону. Оновлення без жодної нової міграції доводить лише
|
||
те, що служби перезапустились, — і мовчки видається за доказ, що міграції
|
||
котяться поверх даних. Якщо в парі версій нових міграцій немає, прогін
|
||
про це кричить — і в момент вибору, і в підсумку.
|
||
|
||
**Перед випуском беріть `--from` явно** — із тією версією, з якої
|
||
клієнти оновлюватимуться насправді. Автоматичний вибір — це «найближча
|
||
попередня», а не «та, що справді стоїть».
|
||
|
||
### Ізоляція: чому це не може зачепити нічого чужого
|
||
|
||
| Що | Як розведено |
|
||
| -- | ------------ |
|
||
| Проєкт compose | `netpulse-sandbox-upgrade` — окремі контейнери, мережа й **томи** |
|
||
| Порти | від **18300** на `127.0.0.1`; режим установки з нуля перебирає 18080…18272 і сюди не дістає |
|
||
| `.env` | власний `.env.sandbox-upgrade`; бойовий `.env` не читається й не пишеться |
|
||
| `netpulse.conf` | не читається взагалі (див. `read_conf`) |
|
||
| Образи | власні теги `netpulse/*:sandbox-prev` і `:sandbox-new`; спільний тег `:dev` не чіпається жодного разу |
|
||
| Файрвол хоста | правило `DOCKER-USER` не додається: `TRAPS_FROM` порожній |
|
||
| Репозиторій | дерево розгортається через `git archive` — він **лише читає**; ані `worktree`, ані `checkout` |
|
||
|
||
Окремі теги образів — не дрібниця. Без них обидві версії називались би
|
||
`netpulse/server:dev`, друга перетерла б першу, а обірваний прогін лишив
|
||
би цей тег указувати на **стару** збірку — на очах у бойової інсталяції,
|
||
яка ділить із пісочницею реєстр образів.
|
||
|
||
Дерево попередньої версії лежить у `.sandbox-prev/` поруч із проєктом (не
|
||
в `/tmp`: там часто tmpfs, і другий примірник вихідних текстів ліг би в
|
||
оперативну пам'ять). З контексту збірки його виключає `.dockerignore`.
|
||
|
||
`./netpulse sandbox down` прибирає **обидва** режими одразу — контейнери,
|
||
томи, мережу, `.env`, дерево й власні теги образів. Пам'ятати, у якому
|
||
режимі стенд піднімали, не треба.
|
||
|
||
### Що саме звіряється в кінці
|
||
|
||
Дві різні вимоги, тому й два правила:
|
||
|
||
* **`exact`** — рядки, які налила сама пісочниця (хости `sb-sw-*`, їхні
|
||
проби, ряди метрик, конфіги, тригер, алерти). Їхня кількість не має
|
||
права змінитись **ані в який бік**: поменшало — оновлення знищило,
|
||
побільшало — роздвоїло. Обидва однаково погані й обидва беззвучні.
|
||
* **`min`** — усе інше (кабінети, користувачі, довідники, аудит). Тут
|
||
дозволено лише рости: система під час оновлення жива.
|
||
|
||
Кожен `exact`-запит звужено до рядків пісочниці, бо локальний зонд весь
|
||
цей час пише свою телеметрію. Алерти рахуються по `alr.alerts` **і**
|
||
`alr.alerts_history` одразу: фоновий такт переносить погашені в історію,
|
||
і рядок, що переїхав, — це не втрачений рядок.
|
||
|
||
### Чого цей режим НЕ доводить
|
||
|
||
* **Гілку «наявна інсталяція» в міграторі.** База пісочниці народжується
|
||
чистою, тому `public.netpulse_install.fresh` назавжди `true`, і мігратор
|
||
іде гілкою «видати паролі ролям». Перехід стенду, зробленого до 0063
|
||
(`deploy/RLS-EXISTING-INSTALL.md`), лишається неперевіреним.
|
||
* **Зміни в самому установнику між версіями.** Обидва етапи веде один і
|
||
той самий `./netpulse` — вимірювальний прилад має бути тим самим на
|
||
обох кінцях вимірювання, інакше різниця в приладі читається як різниця
|
||
у виробі. Під перевіркою тут те, що установник ставить, а не він сам.
|
||
* **Оновлення через кілька версій підряд.** Перевіряється рівно один
|
||
стрибок: з обраного ref у HEAD.
|
||
* Усе те, чого не доводить і перший режим: Let's Encrypt і DNS, трапи на
|
||
162/udp, поведінка під навантаженням.
|
||
|
||
## Підключення зонда
|
||
|
||
В інтерфейсі: **Зонди → Додати зонд**. Видане запрошення (`np_enr_…`)
|
||
одноразове — після реєстрації воно згоряє.
|
||
|
||
На машині, де стоятиме зонд:
|
||
|
||
```sh
|
||
docker run -d --name netpulse-agent --restart unless-stopped \
|
||
--cap-add NET_RAW \
|
||
-v netpulse-agent:/var/lib/netpulse \
|
||
netpulse/agent:dev \
|
||
-server netpulse.example.com:9443 \
|
||
-enroll np_enr_… \
|
||
-name "Зонд у Львові" \
|
||
-modules icmp,snmp,topology,ncm
|
||
```
|
||
|
||
Зонд обміняє запрошення на постійний токен і збереже його в
|
||
`/var/lib/netpulse/agent.json` (права 0600). Наступні запуски токена вже
|
||
не потребують — том із посвідченням має пережити перестворення
|
||
контейнера, інакше кожен старт вимагатиме нового запрошення.
|
||
|
||
Усі зʼєднання зонда вихідні: у мережі клієнта не треба відкривати
|
||
жодного порту.
|
||
|
||
## Бекап
|
||
|
||
Три речі, і всі три обовʼязкові:
|
||
|
||
1. **База** — усе, крім секретів у відкритому вигляді.
|
||
2. **`NETPULSE_DEK`** — без нього паролі SSH і SNMP-community з бекапу не
|
||
розшифрувати. У БД лежить лише шифротекст.
|
||
3. **`NETPULSE_JWT_SECRET`** — без нього після відновлення всі активні
|
||
сесії відваляться. Не смертельно, але користувачі помітять.
|
||
|
||
```sh
|
||
docker compose exec -T db \
|
||
pg_dump -U netpulse -d netpulse -Fc --no-owner \
|
||
> netpulse-$(date +%F).dump
|
||
```
|
||
|
||
Формат `-Fc` (custom), а не простий SQL: він стискається і дозволяє
|
||
відновлювати вибірково.
|
||
|
||
Ключі зберігати **окремо від дампа** — інакше сенс шифрування секретів
|
||
зникає: той, хто дістав бекап, дістав і ключ до нього.
|
||
|
||
Том `git-data` бекапити не обовʼязково: тіла конфігів лежать
|
||
зашифрованими в базі, і репозиторій повністю відтворюється з неї —
|
||
|
||
```sh
|
||
docker compose run --rm --entrypoint netpulse-gitsync api
|
||
```
|
||
|
||
Зворотне невірно: з репозиторію базу не відновити. Тому джерелом істини
|
||
лишається дамп, а Git — похідне сховище, яке коштує один запуск команди.
|
||
|
||
### Автоматично, щодня
|
||
|
||
```cron
|
||
15 3 * * * cd /opt/netpulse && docker compose exec -T db pg_dump -U netpulse -d netpulse -Fc --no-owner > /var/backups/netpulse-$(date +\%F).dump && find /var/backups -name 'netpulse-*.dump' -mtime +30 -delete
|
||
```
|
||
|
||
## Відновлення
|
||
|
||
TimescaleDB вимагає рамки навколо відновлення: без неї фонові процеси
|
||
агрегації втручаються в наливання даних і дамп лягає пошкодженим.
|
||
|
||
```sh
|
||
docker compose stop api collector
|
||
|
||
docker compose exec -T db psql -U netpulse -d postgres -c \
|
||
'DROP DATABASE IF EXISTS netpulse; CREATE DATABASE netpulse;'
|
||
|
||
docker compose exec -T db psql -U netpulse -d netpulse -c \
|
||
'CREATE EXTENSION IF NOT EXISTS timescaledb;'
|
||
|
||
docker compose exec -T db psql -U netpulse -d netpulse -c \
|
||
'SELECT timescaledb_pre_restore();'
|
||
|
||
docker compose exec -T db pg_restore -U netpulse -d netpulse --no-owner \
|
||
< netpulse-2026-08-25.dump
|
||
|
||
docker compose exec -T db psql -U netpulse -d netpulse -c \
|
||
'SELECT timescaledb_post_restore();'
|
||
|
||
docker compose up -d api collector
|
||
```
|
||
|
||
У `.env` має лежати **той самий** `NETPULSE_DEK`, що й на момент дампа.
|
||
Інакше застосунок підніметься, але кожна спроба скористатись збереженим
|
||
паролем поверне помилку розшифрування — і виглядатиме це як зламані
|
||
креденшели, а не як втрачений ключ.
|
||
|
||
Перевірка після відновлення:
|
||
|
||
```sh
|
||
docker compose exec -T db psql -U netpulse -d netpulse -c \
|
||
'SELECT count(*) FROM core.devices;'
|
||
curl -sf https://<NETPULSE_DOMAIN>/healthz && echo OK
|
||
```
|
||
|
||
## Оновлення
|
||
|
||
```sh
|
||
git pull
|
||
docker compose up -d --build
|
||
```
|
||
|
||
`migrate` відпрацює першим і не дасть піднятись API, якщо схема не
|
||
накотилась. Міграції йдуть по одній у транзакції; уже застосований файл
|
||
зі зміненою контрольною сумою зупиняє весь запуск — це захист від
|
||
мовчазного розходження схеми з кодом.
|
||
|
||
Відкат схеми не передбачений: зворотні міграції на даних телеметрії
|
||
коштують дорожче, ніж відновлення з бекапу.
|
||
|
||
Перед тим як віддавати оновлення клієнтові, цей самий шлях треба
|
||
прогнати в ізоляції: `./netpulse sandbox upgrade --from <версія, що
|
||
стоїть у клієнта>`. Він ставить стару версію з нуля, наливає в неї дані,
|
||
оновлює й окремо звіряє, що дані пережили. Розділ «Пісочниця, режим
|
||
другий» вище.
|
||
|
||
## Ізоляція кабінетів (RLS)
|
||
|
||
**Нова інсталяція вже під політиками — робити нічого не треба.**
|
||
`netpulse-migrate` на чистій базі сам видає паролі ролям `netpulse_app`
|
||
і `netpulse_worker`, і застосунок з першої секунди ходить роллю без
|
||
`BYPASSRLS`.
|
||
|
||
Ізоляція тримається на двох незалежних рубежах: політика RLS у базі й
|
||
предикат `tenant_id` у кожному запиті коду. Другий потрібен окремо, бо
|
||
на гіпертаблицях RLS не працює взагалі — TimescaleDB не поєднує його зі
|
||
стисненням, а туди йде вся телеметрія.
|
||
|
||
**Інсталяціям, старшим за 0063,** застосунок і далі ходить роллю
|
||
`netpulse` — тобто суперкористувачем, який політики обходить, — і другий
|
||
рубіж вмикається окремою оборотною процедурою:
|
||
**deploy/RLS-EXISTING-INSTALL.md**. Вона не змінює даних. Робити її
|
||
разом з оновленням версії не варто: ламатись у них різне, і розбирати
|
||
доведеться одночасно.
|
||
|
||
Свій випадок видно одним запитом:
|
||
|
||
```sh
|
||
docker compose exec -T db psql -U netpulse -d netpulse -c "SELECT fresh FROM public.netpulse_install"
|
||
```
|
||
|
||
## Зміна ключа шифрування
|
||
|
||
Ключі перелічуються через кому, новий — першим:
|
||
|
||
```
|
||
NETPULSE_DEK=np2=<новий hex>,np1=<старий hex>
|
||
```
|
||
|
||
Нові секрети шифруються першим ключем, старі читаються своїм. Прибирати
|
||
старий ключ можна лише після того, як усі секрети перезаписані.
|
||
|
||
## Чому саме так
|
||
|
||
**Два образи, а не пʼять.** `api`, `collector`, `migrate`, `netpulse-user`
|
||
і `netpulse-secret` — з одного модуля, з половиною спільного коду. Один
|
||
образ гарантує, що API і колектор ходять у схему БД однією версією; окремі
|
||
образи дають їм можливість розʼїхатись саме там, де це найдорожче.
|
||
|
||
Зонд — окремо: він їде в чужу мережу, і DSN, ключі шифрування та команди
|
||
заведення користувачів не повинні бути в тому образі навіть як
|
||
невикористані файли.
|
||
|
||
**Міграції окремою службою.** API піднімається в кількох примірниках;
|
||
накочування схеми зі старту означало б гонку між ними.
|
||
|
||
**TLS на проксі, а не в застосунку.** Прострочений сертифікат на системі,
|
||
яка сама має повідомляти про проблеми, — найгірший спосіб дізнатись про
|
||
проблему. Caddy оновлює його сам.
|
||
|
||
## Без домену
|
||
|
||
Домен потрібен для справжнього сертифіката: Let's Encrypt не видає їх на
|
||
IP-адреси. Поки домену немає, лишіть `ACME_EMAIL` порожнім — Caddy
|
||
випише самопідписаний, і система працюватиме одразу, з попередженням у
|
||
браузері.
|
||
|
||
Коли домен зʼявиться: замінити `NETPULSE_DOMAIN`, вписати `ACME_EMAIL` і
|
||
`docker compose restart proxy`. Caddyfile змонтований, тож перезбирати
|
||
образи не треба — але й `up -d` без `restart` його не перечитає.
|
||
|
||
## Дашборд на телевізор
|
||
|
||
В інтерфейсі: **Дашборд → На телевізор → Видати посилання**. Отриману
|
||
адресу відкривають на екрані в диспетчерській — вона не потребує входу.
|
||
|
||
Телевізор нікуди не залогиниш: сесія протермінується, браузер оновиться,
|
||
і зранку на стіні висітиме форма входу замість карти мережі — рівно
|
||
тоді, коли на неї дивляться.
|
||
|
||
Що дає посилання і чого не дає:
|
||
|
||
- **тільки читання** цього дашборда: його плитки, активні алерти й
|
||
метрики тих хостів, які на ньому показані;
|
||
- метрики чужого хоста за ним не дістати навіть підбором ідентифікатора;
|
||
- решта кабінету — інвентар, конфіги, налаштування, секрети —
|
||
недоступна;
|
||
- відкликається одним рухом, старе посилання одразу мертве.
|
||
|
||
Токен показується один раз. Видати нове можна будь-коли — попереднє при
|
||
цьому перестає працювати.
|