Postmortem трёх дней: семь независимых поломок, которые притворялись одной. До этого система десять месяцев стояла мёртвой.

TL;DR. В telecom-OSS Huawei SANEX9510 отвалились все компоненты сразу. Выглядело как «протухли сертификаты». На деле это был каскад из семи разных проблем в четырёх TLS-контурах: просроченные сертификаты, PKCS#12 размером 0 байт, зашифрованный пароль, у которого при копировании потерялся первый символ, private key в формате, который не понимает родной OpenSSL продукта, и в конце утёкшие соединения к Sybase IQ. Починили без ребута серверов, без рестарта БД и без сброса паролей. Ниже — как искали, где я чуть не свернул не туда и что я думаю о диагностируемости этого продукта.

Контекст, без которого история не читается. Система находится на платной поддержке у компании из tier-1 сегмента. Называть её не буду, да и дело не в ней. Часть сертификатов умерла ещё в октябре 2025 года, и десять месяцев вернуть систему в рабочее состояние не удавалось. Я поднял её за три дня, с 16 по 18 сентября. Это не упрёк: у поддержки свои регламенты и ограничения по доступам. Но цифры «десять месяцев против трёх дней» многое говорят о самом продукте: в нём выигрывает не тот, кто лучше знает документацию, а тот, кто строит карту зависимостей сам и не верит ни одному сообщению об ошибке.

Что за зверь и что у нас сломалось

SANEX — это платформа Huawei для сбора и анализа сигнального трафика (MAP, ISUP, SCCP, TCAP). У нас она работает на двух серверах плюс четыре сборщика DSS. Версия — V200R003C10SPC110, то есть продукт очень, очень легаси: Java и OpenSSL из другой эпохи, документация размазана по HedEx, отдельным инструкциям поддержки и старой переписке, а вокруг ядра за годы наросло столько обвязки, что каждый компонент живёт своей жизнью.

В аварии участвовали такие узлы (адреса изменены):

Роль

Имя

Адрес

Основной сервер (WAS + DAS + Store + Sybase IQ)

cnoss1

10.0.0.14

Резервный сервер

cnoss2

10.0.0.15

Сборщик DSS01

SANEX_DSS01

10.0.1.39 / 10.0.2.39

Сборщик DSS02

SANEX_DSS02

10.0.1.43 / 10.0.2.43

Внутри платформы данные ходят по четырём независимым защищённым контурам:

WAS  <-> DAS                  (Thrift, порт 29301)
DAS  <-> DSS                  (FTPS, забор файлов)
WAS  <-> DSS                  (SGDSS, порт 6688)
Store <-> Sybase IQ           (запись в БД, порт 2988)

У каждого контура свои конфиги, свои keystore и truststore. Снаружи всё это видно как несколько абстрактных сообщений:

Connection Interrupted
Start server failed
connet DSS faild
PKIX path building failed

И каждое из них может означать совершенно разные причины в совершенно разных местах. Про «connet» отдельно: опечатка в продакшн-логе вендора, который продаёт операторам связи. Я решил, что это фича.

Как это выглядело снаружи

В GUI сначала красные DSS, потом красный DAS, потом «Connection Interrupted Between WAS and Device». Данные не импортировались. Первая гипотеза была самая очевидная: протухли сертификаты. Она оказалась правдой, но только на четверть.

И да, сертификаты действительно умерли. Вот что нашлось:

DAS FTPS identity:   CN=GENEX CN Nastar   истёк 2025-10-18
DSS Certificate_CA:  huawei.RootCA        истёк 2026-07-18
DSS Certificate_Pub: CN=OSS               истёк 2026-07-18

У нас это не повод ругать вендора: сертификаты у всех когда-нибудь кончаются. Но в продукте нет ни одного предупреждения за 180, 90 или 30 дней. Просто в какой-то момент всё отваливается, и вы идёте искать, какой из шести сертификатов решил испортить вам день.

Дальше начинается самое интересное: починил слой, вылез следующий.

починил слой №1 -> вылез слой №2 -> починил слой №2 -> вылез слой №3 -> ...

Слой 1. DAS: заменяем identity

Начали с DAS, потому что без него остальное бессмысленно. Собрали новый serverkeystore_das.p12 с цепочкой:

mae.huawei.com  ->  Huawei Wireless Network Product CA  ->  Huawei Equipment CA

Листовой сертификат — до 2030 года. Положили на оба узла, DAS поднялся. Это был самый простой этап, и он создавал ложное ощущение, что дальше будет так же.

Слой 2. WAS: keystore размером 0 байт

WAS всё равно не стартовал нормально. Проверяю identity PKCS#12 и обнаруживаю:

WAS identity PKCS#12 = 0 bytes

Не «битая цепочка». Не «неверный пароль». Не «alias не найден». Просто пустой файл.

Компоненту, которому для работы нужна TLS-идентичность, подсунули нулевой файл, и в ответ он выдал кучу косвенных TLS-ошибок. Любая нормальная валидация на старте должна была крикнуть что-то вроде FATAL: identity keystore is empty. Здесь вместо этого вы раскручиваете цепочку через следствия.

Пересобрали keystore из server_final.pem и SI.cer, проверили: PrivateKeyEntry, CN=mae.huawei.com, срок до 2030.

Слой 3. Одна @, которая сломала truststore

Стало лучше, но не совсем. В ThriftClient.properties лежит зашифрованный TrustStorePass. Попытка его расшифровать выдавала мусор.

Правильное значение должно начинаться с @:

TrustStorePass=@0102...

А у нас первый символ потерялся при копировании через PTY. Один символ в начале непрозрачного blob’а превращает рабочую конфигурацию в нерабочую, и приложение при этом не пишет invalid encrypted secret format, а начинает сыпать ошибками про TLS. Цепочка выглядит так:

секрет кривой -> truststore не открывается -> TLS ломается -> лог пишет что-то совсем не то

Вернули @ — пароль декодировался, truststore открылся. Подтвердили CA в хранилище (Huawei Wireless Network Product CA), и сервис задышал:

29301 LISTEN
 8086 LISTEN
31943 LISTEN

iEMP System is Ok
reportDasAlarm success

WAS ↔ DAS вернулся. Обычная история «оказалось, что дело в одном символе». Но я бы ставил на то, что в вашей жизни такое будет повторяться.

Слой 4. FTPS живёт на другом keystore

Тут я открыл для себя главный сюрприз архитектуры. DAS использует для обычного TLS serverkeystore_das.p12, а для FTPS — совсем другой файл serverkeystore.p12 (настройка в ftpsCer.properties). Названия отличаются на пять символов, а смысл у файлов разный.

Чтобы не сломать то, что только что починил, я составил карту: кто какой keystore читает.

thrift4collect.properties      -> serverkeystore_das.p12
ThriftClient.properties        -> serverkeystore_das.p12
thrift4watchDog.properties     -> serverkeystore_das.p12
sybTunnelCer.properties        -> serverkeystore_das.p12
thrift4config.properties       -> serverkeystore_das.p12
thrift4procAgent.properties    -> serverkeystore_das.p12

ftpsCer.properties             -> serverkeystore.p12

Только после этого стало безопасно менять FTPS identity. Без карты можно было спокойно починить одно и заодно сломать уже восстановленную связку DAS ↔ WAS.

Старый FTPS-сертификат был мёртв (истёк ещё в октябре 2025). Сделали бэкап serverkeystore.p12.before-ftps-fix, собрали новый PKCS#12 из ключа, SI.cer и корневого сертификата, проверили: alias=1, PrivateKeyEntry=yes, срок до 2030.

Слой 5. Private key, который DSS принимает, но не читает

Из истории работ выяснилась любопытная деталь. Серверную часть обновления сертификатов делали ещё в мае 2026 года, а вторую половину, на самих сборщиках DSS, так и не сделали. Нужно было загрузить файлы через OMU File Transfer и применить их MML-командой LOD CERT. В итоге новые сертификаты уже лежали на сервере, а сервисы продолжали читать старые файлы. Загрузку на железо через LMT и проверку на стороне DSS мы делали уже вдвоём.

Теперь DSS. Заменили на сборщиках три файла: Certificate_CA.pem, Certificate_Pub.cer, Certificate_Priv.pem. Процедура штатная:

RMV CERT
LOD CERT
RST FTP
DSP FTP

Новый CA — до 2036, публичный сертификат — до 2030. Загрузили. FTP-сервис не поднялся:

Start server failed

И всё. Никакого private key decrypt failed, никакого unsupported PBKDF2 PRF, никакого key format incompatible. Сервис принял файл, но стартовать с ним отказался.

Я раскопал причину. Ключ я сгенерировал штатным OpenSSL, который поставляется вместе с SANEX (это времена 0.9.8 / 1.0.x). А оригинальный ключ Huawei был в таком виде:

PKCS#8, PBES2, PBKDF2
iterations = 1676
PRF = HMAC-SHA512
cipher = AES-256-CBC

Старый OpenSSL внутри самого продукта не умеет нормально работать с этим PRF. То есть компонент A платформы Huawei штатным инструментом не может сгенерировать то, что ожидает компонент B той же платформы. Дальше я не спорил, а пошёл на современный Ubuntu с нормальным OpenSSL:

openssl pkcs8 \
  -topk8 \
  -in CN_key_legacy.pem \
  -out Certificate_Priv.pem \
  -v2 aes-256-cbc \
  -v2prf hmacWithSHA512 \
  -iter 1676

Загрузили ключ на DSS, и наконец:

FTP service started

Доказательства, что FTPS работает по-настоящему

Статус DSP FTP = Running ничего не доказывает. Нужны реальные файлы. Через некоторое время в логах пошли события сбора:

KPI_SI_XDR_COLLECT_END,DSSID=10.0.2.39,FILECOUNT=2
XDR Close ftp successfully
KPI_SI_XDR_COLLECT_END,DSSID=10.0.1.39,FILECOUNT=1
XDR Close ftp successfully

Сеть, TLS, клиентский сертификат, FTP и передача файлов — всё работает.

Слой 6. WAS ↔ DSS: отдельный контур на порту 6688

FTPS починили, DAS работает, а в GUI всё равно Connection Interrupted Between WAS and Device. Почему? Потому что это четвёртый, отдельный контур: порт 6688, свой конфиг, свой keystore, свой truststore, свой прикладной протокол.

Нашёлся SgdssSocketSSL.properties, который указывает на var/sysagent/ssl/serverkeystore.p12 и servertruststore.jks. А рядом лежит ещё etc/dsscertificate/ с другим комплектом файлов. Без grep и трассировки не понять, какой из них реально читает процесс.

Дальше я не стал гадать, а проверил слои по очереди.

TLS руками. OpenSSL-проба: TLSv1.2, AES128-SHA256, Verify return code: 0 (ok). DSS запросил клиентский сертификат, и наш приняли. Взаимная аутентификация (mTLS) работает.

TLS из Java. Чтобы исключить «у Huawei особенная Java», повторил проверку из Java: инициализация менеджеров ключей и доверия OK, handshake OK, TLSv1.2.

KEYMANAGER_INIT=OK
TRUSTMANAGER_INIT=OK
HANDSHAKE=OK
PROTOCOL=TLSv1.2
CIPHER=TLS_RSA_WITH_AES_128_CBC_SHA256

Итого: TLS-слой полностью рабочий. Значит, если приложение при этом пишет connet DSS faild, проблема выше TLS. Вот только лог не скажет, на каком именно шаге: TCP, TLS, прикладной handshake или регистрация сессии. Все четыре сценария выглядят одинаково.

Дальше случилось то, что я до сих пор не могу объяснить иначе как чудом. Мы просто «дёрнули линки», то есть переподняли соединения между WAS и сборщиками, ничего больше не меняя. Никаких новых сертификатов, правок конфигов и перезапусков в этот момент не было. Линки встали, handshake прошёл с первой попытки, и DSS один за другим появились в GUI со статусом Online. Видимо, к этому моменту все четыре контура уже были исправны, и системе оставалось только заново поднять сессии, которые она до этого честно пыталась установить и не могла.

В итоге GUI показал все четыре DSS (по два на сборщик) в статусе Online. DAS тоже Online.

Слой 7. Финальный босс: Store и Sybase IQ

Казалось, всё. Но Store решил устроить свой раунд. Транспорт работает, файлы приходят, а в базу данные не пишутся.

Sybase IQ сам жив и слушает порт. Но соединений ESTABLISHED на нём очень много. В логах Store — ошибки логина, и это сразу подкинуло версию про пароль БД.

И вот здесь я чуть не пошёл не туда. Версия выглядела логично, и рука уже тянулась к:

ALTER USER ...
GRANT CONNECT ...

Но сначала я проверил: зашифрованный пароль ossuser в конфиге декодируется нормально. Дальше пошла серия попыток подключиться через jjs, jConnect, dbisqlc, DriverManager, SybDriver. И тут старый стек Java и Sybase устроил отдельный цирк: No suitable driver found, Nashorn Class is not defined, особенности регистрации драйвера. Диагностика legacy-стека оказалась почти отдельной аварией.

Поэтому я решил не трогать базу и перезапустить только Store. После рестарта в свежем логе появилось:

dbSpaceFlag: true
SI_DB_main
usage=1
totalSize=7.13T

Это значит, что Store подключился, аутентифицировался, выполнил SQL и прочитал метаданные. Версия «неверный пароль» оказалась неверной.

Что было на самом деле. До рестарта у Store было около 204 соединений с IQ. IQ запущен с -gm 300, то есть до лимита рукой подать. После рестарта осталось около 113, и система ожила.

Если бы я в панике сделал ALTER USER, то «починил» бы то, что не было причиной, и добавил бы к аварии ещё одну проблему.

Доказательство, что данные идут

В свежем логе Store пошли вызовы процедур по всем протоколам:

Procedure call success
   MAP / ISUP / SCCP / TCAP / DAS

И реальные пути файлов (/data/sgdss/map/ss_tdr/..., /data/sgdss/isup/cdr/... и другие). Цепочка закрыта: файл пришёл, Store его увидел, процедура вызвалась, данные ушли в IQ.

Чего мы сознательно не делали

Это, как мне кажется, главный результат. Система восстановлена без «тяжёлой артиллерии»:

не перезагружали cnoss1 и cnoss2
не перезапускали Sybase IQ
не сбрасывали пароль БД, не делали ALTER USER и GRANT CONNECT
не пересобирали SANEX
не запускали replace_encryption.sh (он деструктивный)
не меняли crypto work keys

Сводная таблица

Проблема

Реальная причина

Что сделали

DAS TLS

устаревший identity

новый serverkeystore_das.p12

WAS identity

PKCS#12 был 0 байт

пересобрали keystore

WAS truststore

потерялась @ в TrustStorePass

вернули символ

DAS FTPS

просрочен клиентский сертификат

новый PKCS#12

DSS CA/Pub

просрочены сертификаты

заменили через RMV/LOD CERT

DSS private key

несовместимое шифрование PKCS#8

пересобрали современным OpenSSL

WAS ↔ DSS

отдельный контур на 6688

проверили mTLS, довели до Online

Store ↔ IQ

накопление соединений

рестарт только Store

Бонус: тихая поломка, которая жила с июля

Пока всё чинилось, в GUI висела ещё одна авария: Failed to import DAS data to database, Alarm ID 20, тип данных DAS_DIRSIZE. К сертификатам она отношения не имела, и ей уже было больше месяца.

В store.log повторялось одно и то же:

Procedure call failed ... Others Errs:Table 'PB_DAS_DIRSIZE' not found
moveFile -> /data/sgdss/ebak/das/dirsize/

DAS исправно формировал файлы со статистикой размеров каталогов. Store пытался их импортировать, не находил таблицу и откладывал файл в error backup. Первые такие записи датированы 30 июля 2026 года.

Диагностика была короткой. Места на диске достаточно, монитор БД показывает норму, файл обычный ASCII. Read-only запрос под ossuser подтвердил, что в SI_DB_main такой таблицы просто нет:

select name, type, uid from sysobjects where name = 'PB_DAS_DIRSIZE';
-- (0 rows)

Штатный DDL нашёлся в установочном пакете DAS: script/dbscripts/table/das_dirsize.sql. Я создал только эту одну таблицу, с тем же владельцем, что у соседних (ossuser). DROP TABLE, полный create_table_customize.sql и любые installer- и upgrade-скрипты не запускал: на базе с неполной схемой я не хотел гадать, что ещё они заденут.

CREATE TABLE PB_DAS_DIRSIZE
(
   STARTTIME     NUMERIC(10) NOT NULL,
   DASIP         VARCHAR(20) NULL,
   DIR_NAME      VARCHAR(50) NULL,
   FILE_NUM      NUMERIC(10) NULL,
   DIR_SIZE      NUMERIC(10) NULL,
   DATA_DIR_SIZE NUMERIC(10) NULL,
   RESERVE1      VARCHAR(30) NULL,
   RESERVE2      VARCHAR(30) NULL,
   RESERVE3      NUMERIC(11,1) NULL,
   RESERVE4      NUMERIC(11,1) NULL
);

Первый же новый файл прошёл: Procedure call success, потом delete data file. Следующие циклы, каждые пять минут, тоже без ошибок, Alarm ID 20 исчез.

Как таблица пропала из базы, мы не знаем. Известно другое: авария висела больше месяца, и на её фоне история с сертификатами уже не выглядит единственной проблемой этой системы.

Что я думаю о диагностируемости продукта

Сертификаты — обычная эксплуатационная работа. Раздражает другое.

Нет карты зависимостей. Четыре TLS-контура, и ни одного документа вида «процесс → конфиг → keystore → truststore → порт → пир». Я рисовал её сам, по grep и трассировке.

Имена файлов ничего не говорят. serverkeystore.p12, serverkeystore_das.p12, serverkeystore_dss.p12, servertruststore.jks, servertrustkeystore.jks. Хотелось бы что-то вроде das_ftps_client_identity.p12. Я, признаюсь, пьяным придумал бы лучше.

Логи не говорят, что случилось. Start server failed при несовместимом формате ключа. connet DSS faild вместо «TCP OK, TLS OK, прикладной handshake упал». Нужно ведь всего ничего: файл, alias, сертификат, поле с паролем, стадия TLS, пир.

Нет валидации на старте. Пустой keystore и битый зашифрованный секрет — ровно то, что можно поймать до запуска сервиса.

Нет инвентаря сертификатов. Должна быть одна страница: компонент, сертификат, назначение, срок, издатель, путь, статус. Вместо этого find, grep, keytool, openssl и MML по четырём компонентам.

Старый OpenSSL внутри продукта. Когда встроенный инструмент не может сделать формат, который ожидает соседний компонент той же системы, это уже не legacy, а технический долг.

Что нужно сделать, чтобы это не повторилось

Честно: я сам не уверен, что руки дойдут до всего сразу, но вот список.

  1. Мониторинг сертификатов. Инвентарь (путь, subject, issuer, notAfter, оставшиеся дни, компонент, назначение) и алерты за 180, 90, 30 и 7 дней.

  2. Мониторинг соединений Store и IQ. Общее число соединений, соединения Store, скорость роста, пул (активные, простаивающие, ожидающие), число потоков. При -gm 300 разумные пороги: warning около 210, critical около 255.

  3. Отдельный health-check на каждый контур. WAS ↔ DAS: порт, TLS, срок сертификата, состояние приложения. DAS ↔ DSS: FTPS, логин, время последнего файла, последний XDR collect. WAS ↔ DSS: порт 6688, mTLS, прикладная сессия.

И чек-лист на следующую аварию, в порядке «от простого к сложному»:

 1. GUI alarms             9. DSS FTP
 2. Process state         10. Collect success
 3. Listener state        11. WASLNK
 4. Cert expiry           12. Store
 5. Keystore load         13. IQ connections
 6. Truststore load       14. Procedure call success
 7. Manual TLS            15. GUI Online
 8. Java TLS              16. Topology

Что осталось нерешённым

Корневая причина последней проблемы пока не доказана: почему Store накопил около 204 соединений при настройках пула initialSize=5, maxActive=63, maxIdle=15. Рабочие версии:

  • утечка соединений;

  • зависшие соединения в пуле;

  • несколько пулов или несколько процессов, каждый со своим;

  • незакрытые сессии рабочих потоков.

Но это именно версии, а не факты. Так что остаточный риск остаётся: проблема может повториться даже при полностью исправных сертификатах.

И ещё одно наблюдение. Часть объектов на топологии осталась серой или оранжевой. Это не поломка: у них нет свежей телеметрии (Load = 0, Report Time = 1970-01-...), и интерфейс показывает это как Unknown. Лучше бы он писал честно: No telemetry.

Итог

SANEX9510 удалось восстановить не потому, что нашёлся «тот самый неправильный сертификат». Нужно было методично раскопать несколько независимых security- и прикладных контуров, каждый из которых умел сломаться по-своему и сообщить об этом максимально бесполезно.

Задача, которая в нормальном продукте выглядела бы как «обновить цепочку, перезапустить, проверить», превратилась в расследование по старой Java, старому OpenSSL, Sybase, MML и четырём транспортным путям. Зато без ребута, без сброса пароля БД и без пересборки платформы. Три дня против десяти месяцев говорят не о том, что кто-то умнее, а о том, как устроен такой продукт: пока не нарисуешь карту зависимостей руками, он будет отвечать «failed» и больше ничего.

Если у вас есть опыт эксплуатации SANEX или похожих систем Huawei, расскажите в комментариях: как вы отслеживаете сроки сертификатов и встречали ли вы такое накопление соединений у Store? Особенно интересно, нашёл ли кто-нибудь первопричину.