Внутрішній устрій та нюанси роботи
Ця сторінка збирає операційні деталі, які не вписуються природно в інші
сторінки - як саме працює пошук конфіг-файлу, що саме робить (і чого не
робить) кожна команда життєвого циклу акаунта в signal2sip-gendb, і
власні внутрішні таймери демона. Для адмінів, які хочуть точно знати, що
відбувається під капотом, а не лише як почати.
Пошук конфіг-файлу
І signal2sip-daemon, і signal2sip-gendb шукають свій конфіг-файл
однаково, у такому порядку:
- Явний шлях, переданий першим аргументом.
/etc/signal2sip/signal2sip.conf, якщо він існує../signal2sip.conf(відносно поточної робочої директорії) - запасний варіант для dev-checkout’у, не для реального встановленого розгортання.
Кореневий CA-сертифікат Signal, який пінить кожен нативний TLS-клієнт у
цьому проєкті, шукається за тим самим принципом:
/etc/signal2sip/certs/signal-root-ca.pem, якщо він є, інакше
./certs/signal-root-ca.pem.
Що де живе: файл [global] проти бази даних на акаунт
У конфіг-файлі завжди лише одна секція [global] - невелика кількість
загальних для процесу налаштувань. Все інше живе в SQLCipher-базі даних,
на яку вказує сам [global] (db_path):
Ключ [global] | За замовчуванням | Що контролює |
|---|---|---|
db_path / db_key | (обов’язково) | Єдиний файл бази даних SQLCipher, у якому живуть дані всіх акаунтів, і пароль до нього. |
sip_reg_watchdog_sec | 60 | Скільки часу SIP-акаунт може лишатися незареєстрованим, перш ніж демон сам форсує нову спробу реєстрації (власний авто-retry PJSIP покриває не всі коди помилок, наприклад не 403). |
resolved_contact_ttl_sec | 86400 (1 доба) | Скільки часу кешований результат резолву e164→ACI/PNI (з реального запиту Contact Discovery) вважається дійсним, перш ніж резолвити наново. |
storage_sync_interval_sec | 43200 (12 год) | Як часто пов’язаний (linked) акаунт (той, що має справжній доступ до синхронізації контактів Signal) заново забирає список контактів із StorageService Signal. |
config_poll_interval_sec | 30 | Як часто демон перечитує конфігурацію кожного акаунта з бази даних як запасний варіант, незалежно від SIGHUP (див. нижче). |
on_account_error_cmd | (порожньо = вимкнено) | Опційна shell-команда, яка запускається, коли акаунт натрапляє на відому проблему, яку неможливо вирішити без дії адміністратора (сьогодні: Signal відхиляє облікові дані цього пристрою), або відновлюється після неї - див. PIN Signal і Registration Lock, чому це може статися навіть без проблеми на боці SIP. |
Все інше - sip_host, sip_extension, sip_password, sip_transport,
sip_srtp, sip_bridge_destination/sip_bridge_did, прапорець
enabled, і решта - живе в таблиці account бази даних, по одному рядку
на акаунт, і редагується через signal2sip-gendb <name> config set/get/list або enable/disable, ніколи вручну через файл.
Один неправильно налаштований акаунт не блокує інші
Кожен акаунт піднімається незалежно, як при старті, так і при кожному
живому перезавантаженні - помилка в шляху sip_tls_ca_file, недоступна
АТС чи будь-яка інша проблема на рівні одного акаунта заважає лише
цьому акаунту, і ніколи жодному іншому:
- Уся послідовність запуску одного акаунта виконується всередині одного
try/catch- при будь-якій помилці демон пишеaccount setup failed: ... - skipping this account, continuing with the restі переходить до наступного. Акаунт узагалі без налаштованого SIP (тільки Signal) абсолютно не постраждає, якщо в іншого акаунта зламаний транк до АТС. - SIP/АТС-сторона має власний додатковий рівень захисту: неправильний
sip_tls_ca_file(шлях до файлу, якого не існує або який неможливо прочитати) заваляє лише SIP-транспорт цього одного акаунта (у логах -TLS transport setup failed: ...) - його Signal-сторона, якщо вже підключена, продовжує працювати незалежно від цього.
Це не допоможе лише з ресурсом, який усі акаунти використовують
однаково - наприклад, неправильний шлях до кореневого CA-сертифіката
Signal (налаштування секції [global], не окремого акаунта) зламає всі
акаунти однаково, бо тут просто немає різниці “хороший акаунт / поганий
акаунт”, яку можна було б провести. Це інший тип збою, ніж помилка в
одному акаунті, і сама лише ізоляція на рівні акаунта тут не допоможе.
Як зміна конфігурації насправді доходить до запущеного демона
Два незалежні механізми, спрацьовує той, що першим:
- SIGHUP -
gendb config set/enable/disableнадсилають запущеному демонуSIGHUPна основі “найкращого зусилля” (читають його pidfile, перевіряють, що/proc/<pid>/exeдійсно веде до справжнього бінарникаsignal2sip-daemon, перш ніж сигналити - застарілий або перевикористаний PID мовчки пропускається, сигнал не надсилається). Майже миттєво, коли спрацьовує. - Запасний опитувальний цикл - незалежно від того, чи дійшов SIGHUP
до демона (демон на той момент не запущений, застарілий pidfile,
проблема з правами), демон сам перечитує
[global]плюс конфігурацію кожного увімкненого акаунта кожніconfig_poll_interval_sec(за замовчуванням 30с). Це реальна гарантія; SIGHUP - лише швидкий шлях.
Обидва шляхи запускають абсолютно ту саму логіку перезавантаження:
порівняти щойно завантажену конфігурацію з тим, що зараз працює -
акаунти, яких більше немає (вимкнені або видалені), демонтуються, нові
або щойно увімкнені - піднімаються, а вже запущений акаунт, чий
config_version змінився (автоматично збільшується кожним config set), демонтується і збирається наново з новими налаштуваннями. Акаунт,
якого зміна не стосується, продовжує працювати без жодних змін - жодні
дзвінки чи реєстрація інших акаунтів не зачіпаються.
Головний цикл демона: незалежні таймери, а не один спільний тік
Навмисно розділені, кожен зі своєї причини:
- Опитування конфігурації (
config_poll_interval_sec, за замовчуванням 30с) - див. вище. - Вотчдог SIP-реєстрації (
sip_reg_watchdog_sec, за замовчуванням 60с) - на кожен акаунт, форсує нову спробу реєстрації, якщо акаунт незареєстрований довше цього часу, покриваючи коди помилок, які власний авто-retry PJSIP не охоплює. Див. Двосторонній зв’язок про окремий вотчдог, що керує станом SIP залежно від з’єднання Signal і співпрацює з цим. - Кеш резолву контактів (
resolved_contact_ttl_sec, за замовчуванням 1 доба) - контролює, як довго кешований результат Contact Discovery для цілі вихідного дзвінка використовується повторно, перш ніж запит повториться. Навмисно тривалий, бо реальні запити CDS обмежені по частоті на акаунт. - Пересинхронізація контактів через StorageService
(
storage_sync_interval_sec, за замовчуванням 12 год) - на кожен пов’язаний (linked) акаунт, заново забирає реальний список контактів із серверів Signal. Виконується синхронно в тому самому циклі, що й усе інше (у головному циклі цього демона нічого не асинхронне) - повільний мережевий round-trip тут може затримати інші перевірки циклу максимум приблизно на 90с, але лише раз на акаунт за інтервал, а не на кожному тіку.
Життєвий цикл акаунта в signal2sip-gendb, точно
Точний ефект кожної команди життєвого циклу - що змінюється локально (у базі даних), а що на реальних серверах Signal, і чи це оборотно:
| Команда | Оборотно? | Локальна база даних | Сервери Signal |
|---|---|---|---|
register --e164 <e164> sms|voice | - | створює рядок акаунта | починає справжню сесію реєстрації з підтвердженням SMS/голосом (Flow A) |
verify <code> | - | заповнює справжню ідентичність акаунта (ACI/PNI/ключі) | завершує реєстрацію |
link | - | створює рядок акаунта | лінкує як вторинний пристрій через QR-код (Flow B) |
deactivate (псевдонім: unregister) | Так (reactivate) | не зачіпається | перемикає fetchesMessages=false - відправники не можуть достукатися до цього номера, більше нічого не змінюється |
reactivate | - | не зачіпається | перемикає fetchesMessages=true назад |
enable / disable | Так (одне іншим) | перемикає лише прапорець enabled у рядку | не зачіпається - вимкнений акаунт демон просто не завантажує |
unlink | Ні | повністю стирає рядок цього акаунта | не зачіпається - реальний акаунт Signal (і його номер) абсолютно не постраждав |
delete-account | Ні | стирається, але лише при підтвердженій успішній відповіді - див. нижче | DELETE /v1/accounts/me - знищує акаунт і кожен пов’язаний пристрій, звільняє номер для будь-чиєї нової реєстрації |
Дві речі варто підкреслити явно:
deactivate- безпечна, оборотна команда - суто серверна зміна прапорця, локальні дані ніколи не зачіпаються, іreactivateповністю це скасовує.unlinkіdelete-account- ось команди, які дійсно видаляють локальні дані. Стара назваunregisterдосі працює як псевдонім.- Відповідь сервера на
delete-accountможе бути справді неоднозначною. Власний протокол Signal документує, що цей ендпоінт іноді завершується через закриття WebSocket (код4401) замість звичайної HTTP-відповіді - signal2sip ще не розшифровує цей код закриття, тож з’єднання, що закрилося до отримання будь-якої відповіді, невідрізнюване від звичайного мережевого збою. У такому неоднозначному випадку локальні дані навмисно залишаються недоторканими, замість того щоб вгадувати - перевірте вручну (наприклад, спробуйте зареєструвати той самий e164 наново десь-інде), перш ніж самостійно запускатиunlink, щоб очистити локальний рядок.
Сигналізація адміну про проблеми з акаунтом
На відміну від звичайного падіння SIP-реєстрації (яку демон просто сам
ретраїть, суто з пам’яті), деякі збої на боці Signal неможливо
відновити без людини: сьогодні це означає спрацювання
AuthSocket::isDeauthorized() (акаунт відв’язали/видалили десь-інде,
або - коли з’явиться підтримка Registration Lock, див. PIN Signal і
Registration Lock - спробу захоплення коректно
відхилено через відсутність правильного PIN, що також заморожує
облікові дані справжнього власника як побічний ефект). Демон припиняє
ретраїти саме цей акаунт і потребує signal2sip-gendb <name> link
(або unlink, а потім link) з наступним рестартом.
Три незалежні, взаємодоповнювальні способи, якими це доходить до адміністратора:
account.last_error/last_error_at- записується в базу даних у момент, коли це стається, і автоматично очищується наступного разу, коли акаунт успішно підключається (завжди після рестарту, оскільки демон припиняє ретраїти в межах процесу). Видно черезsignal2sip-gendb list(показується як суфіксERROR (since ...)) і в списку акаунтів TUI (червоний статус “problem”) - обидва раніше не бачили цього, оскільки воно існувало лише в пам’яті запущеного демона.Рядок логу, видимий через
journalctl -p err- власне повідомлення демона в stderr позначене префіксом рівня syslog systemd<3>(LOG_ERR), тож його можна грепати/використовувати для алертів через journald без жодного додаткового налаштування (запакований systemd-юніт вже надсилає stderr у журнал).on_account_error_cmd(див. таблицю конфігурації вище) - опційний shell-хук, щоб підключити це до будь-якого реального каналу сповіщень (пошта, бот Telegram/ntfy.sh, вебхук Slack - signal2sip свідомо не обирає його за вас). Викликається як/bin/sh -c '<ваша команда>' sh <ім'я-акаунта> <e164> <тип-помилки>- ці три значення приходять як$1/$2/$3всередині вашої команди, наприклад:on_account_error_cmd=curl -s -d "signal2sip: $1 ($2) is $3" ntfy.sh/your-topic<тип-помилки>наразі -deauthorizedабоrecovered. Fire-and- forget - демон ніколи не чекає на неї і не перевіряє код завершення, тож повільний або зависаючий скрипт не може загальмувати обробку жодного іншого акаунта.
Перевірки прав та привілеїв при старті
signal2sip-daemon, signal2sip-gendb і signal2sip-tui всі
відмовляються стартувати замість того, щоб працювати з більшим
доступом, ніж їм реально потрібно:
- Під
root- явна, гучна помилка, а не тихе скидання привілеїв. Жодному з них root не потрібен: лише вихідні з’єднання, жодних привілейованих портів, немає причин довіряти процесу, що працює зі справжніми ключами Signal, більшим доступом, ніж решті системи. - Конфіг-файл належить іншому користувачу, або права не рівно
0600- він містить пароль до бази даних SQLCipher у відкритому вигляді, тож будь-що слабше за доступ лише власника на читання/запис відхиляється одразу, з точним рецептом виправлення (chmod 600 <шлях>) прямо в тексті помилки. - Файл бази даних належить іншому користувачу - та сама перевірка
власника, без конкретної вимоги до прав доступу (він і так
зашифрований, тож файл із шифротекстом, доступний для читання групі,
- цілком підтримуваний вибір для деяких розгортань; тут має значення лише власник).
Обидві перевірки виконуються до того, як вміст файлу взагалі
читається, і працюють однаково незалежно від того, чи файл вже існував,
чи щойно створюється вперше (свіжий gendb register/link на новому
розгортанні одразу пише власний бутстрапнутий конфіг з правами 0600).
signal2sip запускає демон
через власний systemd-юніт, і кожна комбінація неправильного
власника/прав (конфіг, що належить root, 0644 замість 0600, база
даних, що належить root) підтверджено відхиляється з конкретною,
дієвою помилкою вище, перш ніж все запрацювало з правильним
налаштуванням.