Зонд переживає перезапуск і знає, куди підключатись
Дві помилки, які видно лише на живому розгортанні. Сервер віддавав зондам свою адресу прослуховування замість тієї, за якою до нього дістаються: у посвідченні опинялось ":9443". Реєстрація проходила, а підключитись після перезапуску зонд не міг ніколи. Порожнє значення тепер означає «лишись на адресі, якою прийшов». Запрошення одноразове, але живе в змінних оточення й лишається назавжди — кожен рестарт контейнера падав із «запрошення недійсне», хоча посвідчення поруч робоче. Наявне посвідчення тепер важить більше. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
6843bd3280
commit
55e10cbd08
3 changed files with 102 additions and 5 deletions
67
HISTORY.md
67
HISTORY.md
|
|
@ -2931,3 +2931,70 @@ NAT цілком може слухати SSH на 2222.
|
|||
другий вибір список не закрився, «Доступи 2»
|
||||
клік по підкладці форма закривається
|
||||
```
|
||||
|
||||
## Перше розгортання на живому сервері
|
||||
|
||||
Debian 13, 4 ядра, 4 ГБ. Образи зібрались із першого разу — приємна
|
||||
несподіванка для коду, який жодного разу не збирався. Далі знайшлось
|
||||
пʼять речей, і чотири з них не знайшлись би ніде, крім живого запуску.
|
||||
|
||||
### memlock
|
||||
|
||||
`ulimits: memlock: -1` для Dragonfly дозволений не всюди: у контейнерній
|
||||
віртуалізації ядро відмовляє, і контейнер не стартує взагалі з
|
||||
«error setting rlimit type 8». Швидкість того не варта — обмеження знято.
|
||||
|
||||
### TLS на голому IP
|
||||
|
||||
Домену ще немає, і Caddy нескінченно просив Let's Encrypt видати
|
||||
сертифікат на IP-адресу, чого той не робить. Сайт лишався без TLS.
|
||||
|
||||
Тепер порожня `ACME_EMAIL` означає самопідписаний сертифікат — одна
|
||||
директива `tls {$NETPULSE_TLS}` покриває обидва випадки.
|
||||
|
||||
Далі зʼясувалось друге: рукостискання обривалось «internal error» навіть
|
||||
із готовим сертифікатом. SNI не містить IP-адрес — стандарт дозволяє там
|
||||
лише доменні імена, тож браузер не каже нічого, і Caddy не має за чим
|
||||
обрати сертифікат. Лікується `default_sni`.
|
||||
|
||||
### Команда з README не працювала
|
||||
|
||||
```
|
||||
docker compose run --rm api netpulse-user …
|
||||
```
|
||||
|
||||
У образу `ENTRYPOINT` — тож імʼя команди йшло аргументом до
|
||||
`netpulse-api`, і замість заведення власника піднімався ще один сервер.
|
||||
Потрібен `--entrypoint netpulse-user`.
|
||||
|
||||
Заразом виявилось, що свіжа база не має жодного кабінету, а користувач
|
||||
без кабінету нікуди не належить — те саме коло, заради розриву якого
|
||||
існує ця команда, лише рівнем вище. Додано `-create-tenant`.
|
||||
|
||||
### Зонд не міг підключитись після реєстрації
|
||||
|
||||
У посвідченні опинилось `"endpoint": ":9443"`. Сервер віддавав зондам
|
||||
свою адресу **прослуховування** замість тієї, за якою до нього
|
||||
дістаються. Помітно це стає лише після перезапуску: реєстрація проходить,
|
||||
а підключитись зонд більше не може ніколи.
|
||||
|
||||
Порожнє значення тепер означає «лишись на тій адресі, якою прийшов» — і
|
||||
це правильна поведінка за замовчуванням: зонд дійшов, отже, вона робоча.
|
||||
|
||||
### Зонд не переживав перезапуск
|
||||
|
||||
Запрошення одноразове, а живе в змінних оточення — тобто лишається
|
||||
назавжди. Кожен рестарт контейнера намагався зареєструватись повторно й
|
||||
падав із «запрошення недійсне», хоча посвідчення поруч і цілком робоче.
|
||||
Тепер наявне посвідчення важить більше за запрошення.
|
||||
|
||||
### Підсумок
|
||||
|
||||
```
|
||||
27 міграцій, база UTF8
|
||||
власник заведений, вхід працює
|
||||
147 профілів збору, 5 шаблонів
|
||||
зонд online, переживає перезапуск
|
||||
хост «перевірка звʼязку» → up, 5 зразків ICMP, rtt 16.73 мс
|
||||
icmp-basic сам створив 2 правила сповіщень
|
||||
```
|
||||
|
|
|
|||
|
|
@ -63,6 +63,26 @@ func run() error {
|
|||
Arch: runtime.GOARCH,
|
||||
}
|
||||
|
||||
// Наявне посвідчення важить більше за запрошення.
|
||||
//
|
||||
// Запрошення одноразове, а живе воно в змінних оточення чи в unit-
|
||||
// файлі — тобто лишається на місці назавжди. Кожен перезапуск зонда
|
||||
// намагався б зареєструватись повторно й падав із «запрошення
|
||||
// недійсне», хоча посвідчення поруч і цілком робоче.
|
||||
//
|
||||
// Знайдено живим розгортанням: контейнер із NETPULSE_ENROLL у .env
|
||||
// піднявся один раз, а після першого ж restart уже не вставав.
|
||||
saved, err := config.LoadIdentity(cfg.IdentityPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if saved != nil && cfg.EnrollToken != "" {
|
||||
fmt.Fprintf(os.Stderr,
|
||||
"netpulse-agent: посвідчення вже є (%s), запрошення проігноровано\n",
|
||||
cfg.IdentityPath)
|
||||
cfg.EnrollToken = ""
|
||||
}
|
||||
|
||||
// Реєстрація перед усім іншим.
|
||||
//
|
||||
// Посвідчення зберігається на диск одразу: одноразовий токен згорає
|
||||
|
|
@ -77,10 +97,7 @@ func run() error {
|
|||
fmt.Fprintf(os.Stderr, "netpulse-agent: зареєстровано як %q, посвідчення в %s\n",
|
||||
id.AgentName, cfg.IdentityPath)
|
||||
} else if cfg.AgentID == "" || cfg.Token == "" {
|
||||
id, err := config.LoadIdentity(cfg.IdentityPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id := saved
|
||||
if id == nil {
|
||||
return fmt.Errorf(
|
||||
"зонд не зареєстрований: посвідчення не знайдено (%s). "+
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ func run() error {
|
|||
logLevel = flag.String("log-level", envOr("NETPULSE_LOG_LEVEL", "info"), "debug|info|warn|error")
|
||||
gitRoot = flag.String("git-root", envOr("NETPULSE_GIT_ROOT", "/var/lib/netpulse/git"),
|
||||
"корінь сховища версій конфігів; порожньо — без Git")
|
||||
controlEndpoint = flag.String("control-endpoint", os.Getenv("NETPULSE_CONTROL_ENDPOINT"),
|
||||
"адреса, за якою зонди мають підключатись; порожньо — та, якою вони прийшли")
|
||||
)
|
||||
flag.Parse()
|
||||
|
||||
|
|
@ -113,7 +115,18 @@ func run() error {
|
|||
npv1.RegisterAgentServiceServer(srv, svc)
|
||||
// Реєстрація йде БЕЗ токена зонда, тож окремим сервісом: інакше
|
||||
// довелося б робити виняток усередині інтерсептора автентифікації.
|
||||
npv1.RegisterEnrollmentServiceServer(srv, grpcapi.NewEnrollService(st, *listen))
|
||||
// Адреса для зондів — не та, на якій ми слухаємо.
|
||||
//
|
||||
// Раніше сюди йшов -listen, і зонд зберігав у посвідченні ":9443"
|
||||
// або "0.0.0.0:9443" — адресу, за якою до сервера не достукатись
|
||||
// нізвідки. Помітно це стає лише після перезапуску зонда: реєстрація
|
||||
// проходить, а підключитись він більше не може ніколи.
|
||||
//
|
||||
// Порожнє значення означає «лишись на тій адресі, якою прийшов» — і
|
||||
// це правильна поведінка за замовчуванням: зонд дійшов, отже, вона
|
||||
// робоча. Прапорець потрібен лише там, де реєстрацію й роботу
|
||||
// свідомо розводять на різні адреси.
|
||||
npv1.RegisterEnrollmentServiceServer(srv, grpcapi.NewEnrollService(st, *controlEndpoint))
|
||||
|
||||
lis, err := net.Listen("tcp", *listen)
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue