Commit 8037b7c0 authored by Vitaly Lipatov's avatar Vitaly Lipatov

memory: update infrastructure references and project notes

parent e27dce06
...@@ -6,7 +6,16 @@ PVE-хост в кластере MIAC. CPU: **AMD Opteron 6164 HE** (2010, K10, ...@@ -6,7 +6,16 @@ PVE-хост в кластере MIAC. CPU: **AMD Opteron 6164 HE** (2010, K10,
## Changelog ## Changelog
### 2026-06-26 — с border сняты два VPN-гостя ### 2026-07-19 — создан CT 480 (petrosvyaz.dgw.etersoft.ru)
- **Что:** Клонирован CT 694 (dgw.etersoft.ru) → CT 480. IP 91.232.225.250, routing через Петровсвязь: `ip rule from 91.232.225.250 lookup petrosvyaz` (persistent в `/etc/net/ifaces/ether3/ipv4rule`). PTR добавлен в обратную зону. На egw route-update.sh обработал petrosvyaz группу: google-dns маршруты (8.8.8.8, 8.8.4.4) через 91.232.225.250. Masquerade НЕ нужен — Петровсвязь маршрутизирует ответы до .250 обратно через priv.
- **Зачем:** Изоляция трафика через Петровсвязь — вместо кастомных маршрутов на priv, отдельный контейнер с policy routing. [ses_08605dc7cffeh, ses_077433517ffe]
### 2026-07-15 — запущен CT 706 (gr.egw), восстановлен gvpn Greek-эгресс
- **Что:** CT 706 `ikev2.gr.egw.etersoft.ru` (.139) был остановлен → gvpn (CT 752 на enceladus) терял default-gateway и был offline. `pct start 706` + initiate с parentglobal → SA up, gvpn эгрессит `94.69.12.186` (Греция).
- **Зачем:** CT 706 = IPsec-responder для Greek-эгресса gvpn (аналог fr.egw=CT704). onboot:1, переживает ребут border.
- **Подробности:** [[reference_ikev2_gr_tunnel]].
### 2026-06-26 — с border сняты два VPN-гестя
- **Что:** CT 258 (vpn.eterfund.ru) и VM 292 (vpn.office.etersoft.ru) мигрированы на enceladus (Ryzen, AES-NI). С border ушли полностью. - **Что:** CT 258 (vpn.eterfund.ru) и VM 292 (vpn.office.etersoft.ru) мигрированы на enceladus (Ryzen, AES-NI). С border ушли полностью.
- **Зачем:** Opteron 6164 HE без AES-NI делал OpenVPN-крипту софтовой → упор в ядро. Подробности: [[project_vpn_migrate_enceladus_aesni]]. - **Зачем:** Opteron 6164 HE без AES-NI делал OpenVPN-крипту софтовой → упор в ядро. Подробности: [[project_vpn_migrate_enceladus_aesni]].
- **evpn (CT 264) ОСТАЛСЯ на border** — его не переносили (IKEv2/strongSwan, см. [[lesson_evpn_ikev2_strongswan_cert]]). - **evpn (CT 264) ОСТАЛСЯ на border** — его не переносили (IKEv2/strongSwan, см. [[lesson_evpn_ikev2_strongswan_cert]]).
...@@ -42,6 +42,26 @@ ...@@ -42,6 +42,26 @@
## Changelog ## Changelog
### 2026-07-19 — eterweb.ru: истёк→освобождён→ПЕРЕРЕГИСТРИРОВАН обратно + фикс сертификата gnucheva
**Инцидент: `eterweb.ru` истёк и был освобождён из реестра `.ru`.**
- whois (TCI) первое время: `No entries found` — домен **полностью отсутствовал** (истёк, освобождён). TLD `.ru` отдавал NXDOMAIN+SOA зоны `.ru`, публичные резолверы — NXDOMAIN на все `*.eterweb.ru`. Наши ns1/ns4 хранили зону, но без делегирования мир её не видел.
- **Восстановлено пользователем в тот же день** (~19:36Z): перерегистрация через регистратора `DOMAINSHOP-RU`, `paid-till: 2027-07-19`. Делегирование `state: REGISTERED, DELEGATED, UNVERIFIED`.
- **Набор NS изменился** при перерегистрации: теперь `ns1/ns2.etersoft.ru` + `ns3/ns4.etersoft.com` (раньше было `ns1/ns4.etersoft.ru`). Все 4 синхронно отдают зону (SOA serial `2018011404`, знают `vgnucheva.eterweb.ru`→.9). ТLD `.ru` подхватывает делегирование с задержкой (минуты–десятки минут) — публичные резолверы сразу после регистрации ещё NXDOMAIN.
- **Влияние было:** все `*.eterweb.ru` публично мертвы ~7 недель; сертификаты LE с SAN `*.eterweb.ru` протухли (renew падал на DNS NXDOMAIN): `eterweb.ru` (16.05), `vgucheva.eterweb.ru` (09.05), `anastasia-nikolaeva.eterweb.ru` (09.05), `vgnucheva.eterweb.ru` (30.05).
- **После возврата домена:** renew этих сертификатов снова заработает автоматически, как только TLD распространит делегирование. Ничего удалять не нужно.
- **2026-07-20:** как только DNS `*.eterweb.ru` ожил публично (TLD `.ru` разнёс NS-делегирование), вручную перевыпущены `certbot renew --cert-name {eterweb.ru, vgucheva.eterweb.ru, anastasia-nikolaeva.eterweb.ru}` — все три до 17.10.2026, HTTPS ожил (anastasia-nikolaeva 200, остальные 302-forward). **Грабля:** certbot 0.31 `renew` ставит `random delay` до ~480 c перед каждым cert → пакетный renew 3 сертификатов занял ~10 мин; для скорости можно `--no-random-sleep-on-renew` (но лишает anti-load jitter).
**Фикс: сертификат `vgnucheva.eterweb.ru` (varvara.gnucheva.com и др.).**
- Root cause: в SAN было `vgnucheva.eterweb.ru` (NXDOMAIN публично из-за потери зоны) → роняло весь сертификат → истёк 30.05.2026.
- Перевыпустил с 6 доменами **без** `vgnucheva.eterweb.ru`: `certbot certonly --webroot -w /var/spool/nginx/tmp/client -d gnucheva.com -d gnucheva.ru -d varvara.gnucheva.com -d varvara.gnucheva.ru -d varya.gnucheva.com -d varya.gnucheva.ru --cert-name vgnucheva.eterweb.ru` (cert-name оставлен для сохранения путей).
- Новый cert: `notAfter=2026-10-17`. `--dry-run` auto-renew прошёл.
- certbot при `certonly` **стёр `renew_hook`** из renewal-conf → восстановил вручную (`renew_hook = serv nginx reload`), бэкап `.bak-hook-*`. **Грабли:** всегда проверять renew_hook после ручного certonly.
- Сайт `https://varvara.gnucheva.com` — HTTP/2 200.
- Тестовый challenge-файл `z-claude-test` в webroot создан для диагностики и **удалён**.
**Прочее из mass-renew-failure (04:49, 23 серта):** у каждого своя причина (не только eterweb). Например `ximper.ru` — домен указывает на .173 (другой хост), `www.ximper.ru` NXDOMAIN. Это отдельная задача массового разбора.
### 2026-06-27 — массовые 503 на soulibre.ru (ДОКАЗАННАЯ причина) ### 2026-06-27 — массовые 503 на soulibre.ru (ДОКАЗАННАЯ причина)
- **Симптом:** soulibre.ru плавающе отдаёт 503, реально **1 685 200 ответов 503/сутки** (~60% запросов). Даже 1 запрос в 6 сек ловит 503; wikilivres.ru (тот же движок/хост) и etersoft.ru — чисто. - **Симптом:** soulibre.ru плавающе отдаёт 503, реально **1 685 200 ответов 503/сутки** (~60% запросов). Даже 1 запрос в 6 сек ловит 503; wikilivres.ru (тот же движок/хост) и etersoft.ru — чисто.
- **503 рисует nginx, НЕ бэкенд.** Apache в CT 167 за сутки отдал **0 ответов 503** (200/404/302/301/304), mod_status Load 0.5 — перегрузки нет. Бэкенд ни при чём (важно: первая гипотеза про «маленький CT 167 захлёбывается» оказалась неверной). - **503 рисует nginx, НЕ бэкенд.** Apache в CT 167 за сутки отдал **0 ответов 503** (200/404/302/301/304), mod_status Load 0.5 — перегрузки нет. Бэкенд ни при чём (важно: первая гипотеза про «маленький CT 167 захлёбывается» оказалась неверной).
......
...@@ -10,3 +10,57 @@ ...@@ -10,3 +10,57 @@
- Модули `nf_conntrack_pptp`+`nf_nat_pptp` в `/etc/modules-load.d/pptp-nat.conf`. - Модули `nf_conntrack_pptp`+`nf_nat_pptp` в `/etc/modules-load.d/pptp-nat.conf`.
- FORWARD policy ACCEPT, без state/INVALID-правил (GRE не дропается файрволом — проблема была именно в хелпере). - FORWARD policy ACCEPT, без state/INVALID-правил (GRE не дропается файрволом — проблема была именно в хелпере).
- Можно откатить: убрать `*raw` секцию (или восстановить из .pre-pptp-helper), удалить modules-load файл. - Можно откатить: убрать `*raw` секцию (или восстановить из .pre-pptp-helper), удалить modules-load файл.
## BIRD (маршрутизация, AS198324)
**Текущее состояние (с 2026-07-05): BIRD2 2.15.1-alt2** — единый dual-stack демон `bird` (пакет `bird2`), работает от **`_bird`**. bird1 (bird+bird6 1.6.8) снят.
- Конфиг: `/etc/bird/bird.conf` + include `/etc/bird/bird.d/*.conf` (disabled-апстримы). Сокет `/run/bird/bird.ctl`. Юнит `bird.service` (`ExecStart=bird -u _bird -g _bird -f`, `ExecStartPre=bird -p` — parse-gate, `After=network.target`). Логи → syslog (`journalctl -u bird`).
- BGP-пиры: **Petrosvyaz AS50538 — активный** (`petrosvyaz4`+`petrosvyaz6`, два отдельных сеанса на разных адресах, НЕ слиты в MP-BGP), ~1.09M v4 + ~250K v6; Prometey/Ekvant/Antifilter — `disabled` в include (failover).
- bird экспортирует master4/master6 в **kernel table 5** (iproute2 `common`). `ip rule` `10000: from all lookup common` — весь трафик по table 5. Source-policy (rule 1000 prometey/petrosvyaz) — статика, bird не трогает.
- Caps: bird от `_bird` удерживает CAP_NET_ADMIN через `-u` (юнит стартует от root, дропает привилегии, CapEff=0x3c00). file-caps/AmbientCapabilities не нужны.
- В `.claude/skills/network/SKILL.md` priv помечен «BIRD2» — теперь корректно. (iBGP/AS65000+`route-update.sh` — это igw, другая машина.)
### Миграция bird1→bird2 — ВЫПОЛНЕНО 2026-07-05 (Etersoft#10177, comment 179505)
- Cutover: `epm remove bird bird6``epm install bird2` (bird2 `Conflicts: bird`), уложить конфиг, parse-check, `systemctl enable --now bird`. Маршруты в table 5 на время свопа держит `persist`.
- **Cleanup при переводе**: выкинуты мёртвые таблицы (testable/maintable/antifilter — kernel-таблиц 6/8 в ядре не существовало) и неиспользуемые фильтры; пиры переименованы.
- **Грабли BIRD1→BIRD2**: (1) `preference N;` нельзя на уровне protocol → `preference = N;` внутри import-фильтра; (2) `include` — только ПОСЛЕ определений функций/фильтров (символы top-down).
- **Логирование → syslog** (не файл): bird2 от `_bird` **падает в цикле**, если `/var/log/bird.log` не доступен на запись (а от bird1 он root-owned). С syslog этого режима отказа нет.
- **Урок: `learn;` в kernel-протоколе при `import none` — БЕССМЫСЛЕНЕН и вреден.** Чужих маршрутов в table 5 нет (0), но `learn` заставляет bird2 вычитывать ВСЮ kernel table 5 каждый scan. После cutover из-за `persist` table 5 оказалась удвоенной (bird1 metric0 + bird2 metric32 ≈ 2.18M) → `learn` вычитывал 2M → переполнение netlink (`Kernel dropped some netlink messages`) → `I/O loop ~8с` → спам `Netlink: No route to host`. **Фикс: убрать `learn;` из k4/k6** → спам прекратился сразу.
- **Удвоение v4 в table 5** (persist-артефакт cutover) — очищается само при ребуте (kernel-таблицы сбрасываются, bird2 стартует fresh). Flush 2.18M iproute2 за проход не осиливает.
- v6 под bird1 в kernel table 5 застревал на ~5342 (bird6 со своим `learn` тоже страдал netlink'ом); под bird2 v6 корректно наполняется до ~250K.
- Файлы на priv: `/root/bird2-staging/` (`bird.conf`, `bird.d/`, `README.md` — инструкция cutover+откат, `bird1-backup/`, `rollback/bird+bird6-1.6.8-alt3.rpm`). Артефакт+бэкапы локально: `~/tmp/claude/priv-bird-baseline/bird2/`. План: `/home/lav/.claude/plans/encapsulated-napping-stallman.md`.
### Reboot-safety (проверено 2026-07-05, к установке сетевой карты) — ОК
- bird.service enabled + parse-gate; интерфейсы ONBOOT (ether3=109.235.223.53/31).
- **ip rule персистентны через etcnet**: `/etc/net/ifaces/{vmbr0,ether3,ether1}/{ipv4rule,ipv6rule}` (главное `from all lookup common pref 10000` на vmbr0 + source-policy).
- rt_tables в `/etc/iproute2/rt_tables` (common=5, prometey=2, petrosvyaz=4).
- **main-default персистентен**: `ether3/ipv4route: default via 109.235.223.52` — страхует паузу сходимости BGP после загрузки (~30–60с, после неё table 5 перепишется clean).
## 2026-07-07 — 3-й канал enp8s0f0: GlobalNet (DATA-IX пиинг + транзит) — бага #19190
Канал через GlobalNet — **НЕ обычный аплинк**, а tagged trunk с ДВУМЯ VLAN (бага Etersoft#19190 — туда пишем всё). Провайдер: Антон Корбин (GlobalNet). Наш MAC `8c:dc:d4:b5:ff:90` (enp8s0f0, sub-if наследуют). Физически — Intel 82599ES SFP+, модуль FIBO FT-S10-W2720LD (10G BiDi 1270/1330); **порт 23 (10G) — завтра**.
- **VLAN 3210 — DATA-IX (AS50952)**: `enp8s0f0.3210`, мы `178.18.227.15/22` + `2a03:5f80:4::227:15/64`; RS1 `178.18.224.100`/`2a03:5f80:4::224:100`, RS2 `178.18.227.100`/`2a03:5f80:4::227:100`; BFD 1000/5.
- **VLAN 3211 — GlobalNet транзит (AS31500)**: `enp8s0f0.3211`, мы `94.124.183.151/31` + `2001:b28:7b0c:feed:94:124:183:151/127`; пир `94.124.183.150`.
- Свой блок `91.232.225.0/24` (AS198324) анонсим на RS + GlobalNet → SNAT офиса `.1` (не меняется).
- **etcnet применён** (2026-07-07): старый Prometey (85.235.192.190/30) снесён (бэкап `/root/etcnet-backup-enp8s0f0-20260707`), source-policy удалён. VLAN'ы `.3210`/`.3211` подняты. Source-policy ip rules: `from 178.18.227.15 lookup main pref 500`, `from 94.124.183.151 lookup main pref 500` (persistent в etcnet ipv4rule) — без них table 5 BGP-маршруты через Petrosvyaz перебивали прямые scope-link.
- **BIRD2** (2026-07-07): `/etc/bird/bird.d/globalnet.conf` загружен (`birdc configure`, не рестарт). Все 6 сессий Established: `dix_rs1/2_v4/v6` (DATA-IX, BFD Up, 1000×5) + `gbl_v4/v6` (GlobalNet, полная таблица ~1M v4 + ~250K v6). IX local_pref=150, transit local_pref=100. Export: только `91.232.225.0/24` + `2a03:5a00:c::/48`.
- **iptables**: BGP peer-ACCEPT :179 + BFD :3784 на sub-if — **live** (инкрементально), generic REJECT :179 на .3210/.3211. ⚠ НЕ persist в `/etc/sysconfig/iptables` — после ребута потеряются.
- **Открыто**: persist iptables; сверить local_pref Petrosvyaz vs GlobalNet; мониторинг BGP в InfluxDB.
### firewall pre-stage (enp8s0f0, до пиинга)
Завтра к priv подключают 3-й внешний канал через доп. NIC `enp8s0f0` (сейчас DOWN, MAC 8c:dc:d4:b5:ff:90; enp8s0f1 — близнец). Распространил на него все WAN-правила ether1/ether3, иначе канал = дыра (:1080 к защищённым прокси .120/.139/.140, BGP-сканы, сервисные порты, обход eterban/anti-spoof). Источник истины файрвола — `/etc/sysconfig/iptables`.
**Сделано:**
- Бэкап: `/etc/sysconfig/iptables.bak-20260707-pre-enp8s0f0`.
- **15 статических правил** добавлены в `/etc/sysconfig/iptables` (twin к ether3, без ipset-зависимости → безопасно в файле): nat POSTROUTING SNAT (.1), INPUT REJECT :179+8006+3128+5900:5999+5404,5405+5876+19999+46555, FORWARD :1080 DROP + 5 anti-spoof (10/8,172.16/12,192.168/16,100.64/10,169.254/16). `iptables-restore --test` = OK.
- Live: те же 15 + **4 eterban-правила enp8s0f0** (FORWARD eterban_1 REJECT @top, nat PREROUTING eterban_white/eterban_1/firehol DNAT→.67) — применены инкрементально (без flush, eterban-динамика ether1/ether3/vmbr0 не тронута). Live=19 enp8s0f0, файл=15.
- **ВНИМАНИЕ — eterban enp8s0f0 НЕ персистентен через ребут.** eterban знает только 2 WAN (`i_interface`/`i_interface2` в `/etc/eterban/settings.ini`, switcher.py хардкодит 2 слота), его правила dynamic и НЕ лежат в файле (ipset-зависимость → `iptables.service` упал бы при загрузке). См. [[lesson_eterban_priv_two_wan_slots]]. После ребута enp8s0f0 потеряет 4 eterban-правила (останутся 15 статических). Фикс = расширить eterban на 3-й интерфейс (build-запрос через Kanban → alt-packaging-agents) ИЛИ sidecar systemd-сервис `After=eterban.service`.
- Live-позиции статических правил = appended (мои скрипт twjn-lookup споткнулся: `iptables -S` не умеет `--line-numbers`), но **функционально эквивалентно** twin-позициям в файле (ESTABLISHED не матчит NEW; .68/.69 :1080 ACCEPT интерфейс-агностичны и стоят раньше). Файл = source of truth, при ребуте встанут twin-позиции.
**⚠ Открыто (после поднятия канала):**
1. **BGP peer-ACCEPT :179 для enp8s0f0** — нужен IP пира + IP priv на линке. Пока только generic REJECT :179. Добавить `-I INPUT -s <peer> -d <priv_ip> -i enp8s0f0 -p tcp --dport 179 -j ACCEPT` ПЕРЕД REJECT, + BIRD-пир.
2. **SNAT source** — стоит `91.232.225.1` (как ether1/ether3). Если у канала свой публичный IP и .1 не анонсится — поменять `--to-source`.
3. Поднять enp8s0f0 в etcnet (IP/маршрут) — отдельная задача; потом проверить счётчики `iptables -L FORWARD -n -v | grep enp8s0f0` и внешний тест :1080/8006 через новый путь (tcpdump на .140 при стуке через новый канал).
4. Решить персистентность eterban (см. выше) ДО ребута.
# roundcube.eterhost.ru (CT 366, 10.20.30.66)
## 2026-07-10 — обновление до Roundcube 1.7.0
Обновлено через epm. Breaking changes и исправления:
**Apache** (`/etc/httpd2/conf/sites-enabled/roundcube.eterhost.ru.conf`):
- DocumentRoot: `/usr/share/roundcube``/usr/share/roundcube/public_html` (mandatory в 1.7.0)
- Directory: аналогично
**Патч authres_status** — плагин `pimlie/authres_status` v0.6.3 не обновлён для 1.7.0:
- `/usr/share/roundcube/plugins/authres_status/authres_status.php`
- `src="plugins/authres_status/images/"``src="static.php/plugins/authres_status/images/"`
- `login_history` (выключен) — та же проблема
**Nginx** (на 91.232.225.23, `/etc/nginx/sites-enabled.d/roundcube.eterhost.ru.conf`):
- `location /installer.php { return 403; }` — блокировка установщика
- `listen 443 ssl http2``listen 443 ssl` + `http2 on` (deprecation fix)
**Статика**: `public_html/` содержит только index.php, static.php, installer.php, .htaccess. Вся статика (JS, CSS, шрифты) отдаётся через `static.php`. Прямых файлов для nginx нет. Кеш: `cache-control: public, max-age=604800` (7 дней).
**Плагины**: contextmenu оставлен (встроенное меню 1.7.0 — только список писем). Дубликат image_paster убран. zipdownload отключён (500 на AJAX).
**Безопасность**: directory listing отключён, внутренние файлы не доступны, installer.php заблокирован nginx.
## 2026-07-09 — проверка исходящей связности
- По запросу проверен `ping -c 4 ya.ru` от `root@10.20.30.66`.
- Результат: 4/4 ответа, потерь нет; RTT 13.772–14.003 ms (средний 13.900 ms).
- На момент проверки исходящий IPv4-трафик из CT проходит; изменений на сервере и NAT-шлюзе не выполнялось.
---
# VPN: dvpn/fvpn/gvpn.eterfund.ru — прогресс (2026-07-13)
## Созданные контейнеры (enceladus)
| Контейнер | VMID | IP | Default route | VPN pool | OpenVPN | ocserv |
|---|---|---|---|---|---|---|
| dvpn.eterfund.ru | CT 750 | 91.232.225.182 | via 91.232.225.12 (dgw) | 10.236.0.0/16 | ✅ | ✅ |
| fvpn.eterfund.ru | CT 751 | 91.232.225.183 | via 91.232.225.140 (fr.egw) | 10.237.0.0/16 | ✅ | ✅ |
| gvpn.eterfund.ru | CT 752 | 91.232.225.184 | via 91.232.225.139 (gr.egw) | 10.238.0.0/16 | ✅ | ✅ |
## Сделано
- Клонированы с CT 258 (vpn.eterfund)
- Сеть настроена через /etc/net/ifaces/breth0/ (ipv4address, ipv4route, options)
- IPv6 адреса: ::182/::183/::184 (последняя компонента как IPv4)
- OpenVPN запущен и работает (порт 1195, UDP)
- ocserv запущен и работает (порт 443, TCP/UDP)
- PKI и пароли ocserv скопированы с CT 258
- DNS записи добавлены на ns1
- Let's Encrypt сертификаты получены (dvpn/fvpn/gvpn.eterfund.ru)
- ntpd отключён (не нужен в LXC)
- Старый azbyka отключён и удалён
- iptables на хосте: SNAT добавлен для .182/.183/.184
## Нужно доделать
1. **Мониторинг** — telegraf не установлен на CT 750/751/752
2. **Баг #16295** — отписаться о создании
3. **certbot auto-renew** — настроить на всех трёх
## Грабли
- Клонирование LXC НЕ копирует SNAT-правила на хосте → нужно добавить вручную: `iptables -t nat -A POSTROUTING -s <IP> -j SNAT --to-source <IP>`
- ntpd в контейнере падает с segfault (не нужен в LXC, отключить)
- IPv6 адреса копируются с оригинала → dadfailed конфликт
## Ссылки
- План: `.mimocode/plans/1783569058720-calm-eagle.md`
- Баг: https://bugs.etersoft.ru/show_bug.cgi?id=16295
--- ---
name: project-dchat-telemt-greek-tunnel-down name: project-dchat-telemt-greek-tunnel-down
description: "d.chat.eterfund.ru (telemt Telegram MTProxy на divserver) не работает лежит IPsec-туннель divserver↔parentglobal к греческому exit. Диагноз готов, фикс отложен 2026-06-05" description: "d.chat.eterfund.ru (telemt MTProxy на divserver) Greek exit parentglobal. Туннель divserver↔parentglobal падал 2026-06-05 (фикс 06-06) и РЕЦИДИВ 2026-07-14 (rekey-fail, нет self-heal). Финальный фикс 2026-07-15: runtime re-initiate + watchdog-timer на parentglobal (divserver + site-to-site). РАБОТАЕТ"
metadata: metadata:
node_type: memory node_type: memory
type: project type: project
...@@ -42,5 +42,22 @@ IPsec-туннель **divserver↔parentglobal (10.43.0.0/30) лежит**: ...@@ -42,5 +42,22 @@ IPsec-туннель **divserver↔parentglobal (10.43.0.0/30) лежит**:
Связано: [[lesson-telemt-middle-proxy-egress-match]] (egress=registered IP для ME), [[reference-remote-ssh-access]], [[lesson-anyssh-ru-in-91232225]], [[lesson-etcnet-hooks]]. Связано: [[lesson-telemt-middle-proxy-egress-match]] (egress=registered IP для ME), [[reference-remote-ssh-access]], [[lesson-anyssh-ru-in-91232225]], [[lesson-etcnet-hooks]].
## Рецидив 2026-07-14 → финальный фикс 2026-07-15 (watchdog)
Персистентность 2026-06-06 оказалась **недостаточной**: `start_action=start` срабатывает ТОЛЬКО при загрузке конфига в charon, а **после провала rekey SA умирает и не переинициируется**. strongSwan на parentglobal крутился с 2026-05-26 без рестарта; последний CHILD_SA `div{278}` — 14 июля 06:48, в 12:33 «restarting CHILD_SA div» провалился → тишина до ручного initiate. telemt висел в `No ME connections` (~3 суток ретраев).
**Runtime-фикс 2026-07-15:**
1. `ssh -p10337 etersoft@anyssh.ru 'sudo swanctl --initiate --ike divserver --child div'` (child = **`div`**, НЕ `s2s`) → SA up.
2. divserver responder был полностью здоров (conn `parentglobal` загружен, ipsec1.service active, policy-route table 201 на месте) — проблема была только в отсутствии инициации с parentglobal.
3. `ssh root@div.eterhost.ru 'systemctl restart telemt'` → ME-pool reinit, STUN `94.69.12.186`, 48→126 ESTAB к TG-DC, d.chat обслуживает клиентов.
**Self-heal (настоящий фикс) — watchdog-timer на parentglobal** (как autoheal у fr-туннеля, но systemd, не docker):
- `/usr/local/bin/ikev2-keepalive.sh` — каждые 2 мин (timer) проверяет `swanctl --list-sas | grep -q '^<ike>:.*ESTABLISHED'`; если 0 → `swanctl --initiate --ike <ike> --child <child>`. Логирует в syslog тегом `ikev2-keepalive`.
- Покрывает **оба** туннеля parentglobal: `divserver`/child `div` (этот telemt-эгресс) и `site-to-site`/child `s2s` (gvpn Greek-эгресс, [[reference_ikev2_gr_tunnel]] — у него 2026-07-15 был тот же гэп).
- Юниты: `/etc/systemd/system/ikev2-keepalive.{service,timer}`, `systemctl enable --now ikev2-keepalive.timer`. OnBootSec=2min, OnUnitActiveSec=2min.
- Проверка паттерна: `swanctl --list-sas | grep -c '^divserver:.*ESTABLISHED'` = 1 (up); при падении = 0 → триггер. ✓
Доступ к parentglobal: `ssh -p10337 etersoft@anyssh.ru` (sudo). divserver: `ssh root@div.eterhost.ru`.
## Побочное (отдельно) ## Побочное (отдельно)
Сертификат `/etc/ssl/chat.eterfund.ru/fullchain.pem` на divserver (и на .43/.140) имеет SAN только `chat.eterfund.ru`, без `d.chat.eterfund.ru` → в браузере ошибка имени. Для telemt fake-TLS неважно. Cert раздаётся централизованно на все 3 узла (одинаковый, LE, выпуск 2026-05-08). nginx vhost (`server_name chat.eterfund.ru d.chat.eterfund.ru`) проксирует на web.matrix.etersoft.ru — это камуфляж-бэкенд. Сертификат `/etc/ssl/chat.eterfund.ru/fullchain.pem` на divserver (и на .43/.140) имеет SAN только `chat.eterfund.ru`, без `d.chat.eterfund.ru` → в браузере ошибка имени. Для telemt fake-TLS неважно. Cert раздаётся централизованно на все 3 узла (одинаковый, LE, выпуск 2026-05-08). nginx vhost (`server_name chat.eterfund.ru d.chat.eterfund.ru`) проксирует на web.matrix.etersoft.ru — это камуфляж-бэкенд.
---
name: reference-bugs-watcher
description: "bugs-watcher real-time «кто на каком баге» дашборд для Bugzilla; где живёт, архитектура, краш-баги 2026-07-25"
metadata:
node_type: memory
type: reference
originSessionId: 30f3090f-9a53-41d9-8b87-cab8e7e27db1
---
# bugs-watcher (https://bugs.etersoft.ru/bugs-watcher/)
Real-time дашборд «кто сейчас работает над каким багом» для Bugzilla. Автор: David Dobryakov (kantegory).
## Где живёт
- Хост: **bugs.etersoft.ru** = 91.232.225.24 (CNAME bugzilla.etersoft.ru), SSH **`ssh -p32 root@bugs.etersoft.ru`** (hostname `bugs`; lav@ тоже работает).
- Backend: systemd **`bugswatcher.service`**`/usr/bin/node /var/www/html/bugs-watcher/server/index.js`, слушает **127.0.0.1:17003**. Лог: `/home/bugs-watcher/bugs-watcher.log` (без timestamp'ов); рестарты — `journalctl -u bugswatcher`.
- nginx: `/etc/nginx/sites-enabled.d/bugs.etersoft.ru.conf`, `location /socket.io``proxy_pass http://127.0.0.1:17003` (http/1.1 + Upgrade). Статика фронта: `/var/www/html/bugs-watcher/dashboard/`.
- Worker-клиент встроен в Bugzilla: шаблон `template/en/default/etersoft/bugswatcher.html.tmpl` PROCESS'ится в `bug/edit.html.tmpl:115`; JS — `/var/www/bugs.etersoft.ru/js/etersoft/bugsWatcher.js` (подключает socket.io 2.3.0 с cdnjs).
## Архитектура
socket.io **v2.3.0** (Engine.IO v3) и на клиенте, и на сервере. События: клиент Bugzilla шлёт `addUser {bugID,email,workTime}` → сервер хранит состояние в памяти (`workers`/`bugs`) → дашборд читает `getActiveBugsList``showActiveBugsList`. Состояние **только в памяти** (при рестарте очищается; клиенты переотправляют только при перезагрузке страницы, не на реконнекте).
## Репозиторий
`git@gitlab.eterfund.ru:etersoft/bugs-watcher.git` (проект перенесён из `kantegory/bugs-watcher`; у lav push-доступ). **Локальный чекаут:** `/home/lav/Projects/git-eter/bugs-watcher` (с 2026-07-27; до этого был во временном `/home/lav/tmp/claude/bw-git`). Пуш — обычный `git push origin master` (gitlab, НЕ gpush). **2026-07-25 синхронирован:** была дивержденция (развёрнуто на старом `ff30da7`, origin/master `7b6996b` с workTime); local master сброшен на upstream + коммиты. deployed==committed==origin/master. Backup-tag локально: `backup/pre-sync-ff30da7`.
**master HEAD `e9afef1`** (на 2026-07-27). Фиксы:
- `e7446ec` server: ping cadence 25s→15s (fallback для close-detection).
- `40f5b4d` fix: dashboard live-update (Fix 2 — см. ниже).
- `2966698` graph: summary + workers на узлах, направленные рёбра.
- `b64b7c6` dashboard: auto light/dark theme (`prefers-color-scheme`).
- **Дашборд 2026-07-26 (issue-фиксы):** `1c270a2` authedFetch (token+cookie fallback, первая версия — ловил только пустой массив); `7ce87f2` убран дублирующий столбец #; `9790b86` cache-busting `?v=` на js/css; `d988113` скрыт 00:00:00 workTime для свежих задач.
- `97f823c` (затем снят) — попытка cookie-fallback: ловил и error `{code:32000}`, и пустой массив → ретраил same-origin без токена. **Основан на неверной посылке** (REST не читает cookies) — бесполезен.
- **`08a881a` (рабочий фикс summary/рёбер):** cookie-fallback убран; при REST-ошибке `32000` (протухший токен) дашборд чистит токен из localStorage и делает `location.reload()` → после релоада `isLogged()=false``showLogin()` → юзер перезаходит → `/rest/login` → свежий токен → `successLogin``initDashboard()` наполняет summary/рёбра. Паттерн как у kanban (`signOut()` на 32000). Пустой массив (restricted-баг без прав) НЕ считается auth-failure.
- **`232530f` (стабильность графа + колонка Summary):** (1) граф раньше на КАЖДОМ store-update делал `destroy()`+`grid`-relayout → узлы/стрелки скакали. Теперь `cyInstance` строится один раз (grid), дальше `renderGraph()` синхронизирует на месте (`startBatch`: add/remove узлов — новые на спирали Фибоначчи; обновление label; rebuild рёбер) **без relayout** → позиции стабильны. Серия enrichment-апдейтов коалесится через `requestAnimationFrame` (`scheduleRender` на `bugsFullfilled`). Защита от отсоединённого `#cy` после logout/login. (2) Таблица Active bugs: добавлена колонка **Summary**; `.bug`-ссылка держит `#id`, summary пишется в отдельную ячейку `.summary` (bug-объект `tag:'.summary'`, не `'a.bug'`). Cache-bust `?v=20260726d`.
- **`b35e7d0` (ghost-зависимости + фикс мигания/стрелок/зума):** (1) граф показывает **зависимые/блокируемые баги, над которыми сейчас НЕ работают** — «ghost»-узлы, приглушённым цветом (серый фон `#3a3a40`/`#f4f4f4` + пунктирная рамка), селектор `node[ghost=1]`. Видно связь, но сразу понятно «не в работе». (2) Метаданные багов (`window.bwMeta`: `{summary, depends_on, blocks}`) — глобальный кэш, **переживает wipe store на каждом showActiveBugsList emit**, шарится между bugsWatcher.js (наполняет из REST) и graph.js (читает узлы/рёбра). Ghost-метаданные тянутся **один хоп** (нет транзитивного раскрытия — `enqueueGhostFetches` только для активных багов, по `.bug[data-id]`). (3) Стрелки больше не «пляшут»: ребро одно на пару — depends_on даёт `prereq→bug`, blocks только к ghost (`bug→dependent`); если dependent активен, reverse depends_on уже нарисовал стрелку → blocks пропускаем; дедуп по unordered-паре (`collectEdges`). (4) Summary больше не мигает пустым — ячейка `.summary` сидируется из кэша при перестроении; `userNameCache` (email→real_name) чтобы не рефетчить имена. (5) `wheelSensitivity:0.3` — мельче шаг зума колесом. Cache-bust `?v=20260726e`. Развёрнуто на 91.232.225.24.
- **`63e8301` (плотность графа + статусы + шрифт + живое время):** (1) **Расстояние:** новые узлы ставятся рядом с уже размещённым соседом по зависимости (`neighborIds` + anchor), а не на глобальной раскручивающейся спирали; ghost-баги одним уровнем **НИЖЕ** активного родителя (`down=130`, `spread=70`) — иерархия «в работе сверху, простаивает снизу»; существующие узлы не двигаются → граф не прыгает. Initial grid ужат (`condense:true`). (2) **Цвет узла = статус Bugzilla** (`statusColor`: NEW/REOPENED красный `#c0392b`, ASSIGNED синий `#2980b9`, RESOLVED зелёный `#27ae60`, VERIFIED бирюза `#16a085`, CLOSED серый `#7f8c8d`); статус тянется в `bwMeta.status` (`include_fields=summary,status,depends_on,blocks`); ghost'ы тот же статус-цвет, но `opacity:0.5` + пунктир (не серый фон). (3) **Шрифт подписей 10px**. (4) **Work-time ЖИВОЙ**: сервер шлёт снапшот один раз → дашборд тикает на клиенте каждую секунду (`liveTimers[bug|email]={src,base,since}`, `startLiveTicker` `setInterval(1000)` обновляет все `.livetime`-спаны; `noteWorkTime` НЕ реcидит при том же снапшоте → таймер не сбрасывается на перестроениях; чистится по `activeKeys` при rebuild). **Без правок сервера.** Оговорка: тикает «по умолчанию активно» (нет реального сигнала активности — отошёл, но баг открыт = идёт). Cache-bust `?v=20260726f`.
- **`fcd5478` (легенда + 2 хопа + grace 10м + no-overlap + шрифт по таблице):** только фронт, без сервера. (1) **Легенда** цвет-ключ под графом (`#graphLegend` в index.html, стили `.legend/.lg/.sw` в dashboard.css) — статусы + «idle dependency» (пунктир) + «recently left <10m» (серый). `renderLegend()` перерисовывается в `createGraph` и `applyGraphTheme`. (2) **2 хопа зависимостей:** `collectGhostIds` (graph.js) два прохода `addDepsOf`; `fetchBugMeta(id,hop)` + `MAX_DEP_HOP=2` (bugsWatcher.js) активные hop 0, их deps hop 1, deps-of-deps hop 2, дальше стоп (нет безконечного транзитивного раскрытия). Рёбра по depends_on только (blocks = обратное) `collectEdges`, дедуп unordered-пары. (3) **Grace 10 мин при закрытии вкладки:** баг не пропадает сразу сереет на 10 мин (снапшот воркеров + замороженный work-time), потом уходит. Считается на клиенте diff'ом активного списка между апдейтами: `prevActiveBugs`/`lastWorkers`/`graceLeaving` (bug→{workers:[{email,secs}],removedAt}), `GRACE_MS=10*60*1000`; `renderGracedBug` `<tr class="leaving">` с `.bug[data-leaving=1]` → граф красит узел серым (`node[leaving=1]`, стоит после status-селекторов → перекрывает). `ensureGraceTimer` реэмитит `getActiveBugsList` раз в 60с (чистит истёкшее, обновляет «left Nm ago»), сам-стоп когда `graceLeaving` пуст. `deleteUser` теперь просто `socket.emit('getActiveBugsList')` (diff сам кладёт баг в grace). (4) **No-overlap:** cose-layout запускается ТОЛЬКО при изменении набора узлов/рёбер (`structureKey`, `lastStructureKey`), не на заливке лейблов — узлы не перекрывают друг друга, но граф не скачет на каждом апдейте. (5) **Шрифт = таблица:** `tableFontSize()` читает computed font-size `#workers` (≈14px), не хардкод 10px. (6) `escapeHtml` для summary/real_name в innerHTML (XSS-гигиена). Cache-bust `?v=20260727a` (js) / `?v=20260727` (css). Развёрнуто на 91.232.225.24.
- **`bc3681b` (ОТКАТ 2-хопа → 1 хоп + шрифт 11px):** фидбэк пользователя. (1) **Зависимости — только 1 хоп:** показывать только активные баги и их ПРЯМЫЕ blockers/зависимости (`depends_on`+`blocks` активного), НЕ рекурсивно раскрывая deps самих idle-ghost'ов (2-й хоп тянул off-chain баги). `collectGhostIds` — убран hop-2 проход; `MAX_DEP_HOP` 2→1. Рёбра по depends_on только — без изменений. (2) **Шрифт 14→11px:** «как в списке» (=14px) оказалось слишком крупным на компактных узлах → `GRAPH_FONT_SIZE=11` (хардкод), `tableFontSize()` убран. Cache-bust `?v=20260727b`. **Важно:** 2-й хоп был сделан по прямому запросу «ещё на шаг глубже», но пользователь откатил — держать 1 хоп.
- **`db4556a` (направленный layered-граф + список сверху + авторазмер canvas):** фидбэк «prereqs сверху, dependents снизу» + «сначала список, потом граф, без ограничения поля» + «используй библиотечный алгоритм, не hand-roll». (1) **Раскладка:** убрал cose (force-directed, не чувствителен к направлению) и весь hand-rolled positioning (`layeredPositions`/`neighborIds`/`nextNodePosition`/`layoutOpts`) → **cytoscape built-in `breadthfirst` с `directed:true`**. Рёбра и так указывают prereq→dependent (`collectEdges`: для X depends_on Y ребро Y→X), значит направленный top-down layout САМ кладёт prereqs сверху, dependents снизу — без ручных координат, без перекрытий. Roots = узлы без входящих рёбер (чистые prereqs), `opts.roots='#id1, #id2'`; fallback на дефолт если цикл. `fit:false` + `spacingFactor:1.1`. (2) **Порядок:** в index.html «Active bugs» таблица ТЕПЕРЬ ВЫШЕ секции графа. (3) **Без ограничения поля:** `.graph` больше не 50vh — `sizeGraphToContent()` после layout меряет `nodes.boundingBox({includeLabels:true})`, ставит `.graph.style.height` = контент+80 (min 420), `cy.resize()`, потом `cy.fit(undefined,30)`. CSS дефолт `height:60vh; min-height:320px` (только первичная отрисовка до JS). Структура-меняется → re-layout+resize+fit (gate по `structureKey`, label-fill не триггерит). **Библиотека:** cytoscape.js (локально `scripts/graph/cytoscape.min.js`); built-in layouts: cose/breadthfirst/grid/circle/concentric/preset; canonical DAG-алгоритм = dagre/Sugiyama (extension `cytoscape-dagre`, НЕ подключён — добавляется при необходимости). Cache-bust `?v=20260727c`.
- **`e9afef1` (форматирование index.html, ОТДЕЛЬНЫЙ коммит):** tabs→2-space, whitespace-only (`git diff --ignore-all-space` пуст). Пользователь просил форматирование отдельным коммитом, не смешивать с фичей. Больше файлов с tab-смесью нет (graph.js/bugsWatcher.js/dashboard.css уже на space).
## Worker-клиент (Bugzilla) — ВНЕ репозитория
`/var/www/bugs.etersoft.ru/js/etersoft/bugsWatcher.js` (Bugzilla-кастомизация, НЕ часть `etersoft/bugs-watcher`). Шлёт `addUser {bugID,email,workTime}` на `socket.io ${window.location.origin}`; слушает `showAllUsers` (`for...in` по `data[bugID]` — object-shape, работает) и `deleteUser`. **2026-07-26: добавлен `pagehide`/`beforeunload` → `socket.disconnect()`** — раньше закрытие вкладки не давало чистого disconnect, сервер ловил уход только по ping-timeout (~30s) → «при закрытии не обновляется». Backup: `*.bak-20260725`. Права `0640 root:apache2`.
## Фиксы архитектуры (2026-07-26)
1. **Close-delay (Fix 1):** worker-клиент теперь шлёт чистый disconnect на `pagehide`/`beforeunload`; сервер `pingInterval` 25s→15s как fallback. Закрытие бага теперь сразу убирает воркера (а не через ~30s).
2. **Dashboard live-update (Fix 2):** `showActiveBugsList` раньше эмитился в **двух несовместимых форматах** — `getActiveBugsList` слал `{bug:[{socketId,workerEmail,workTime}]}` (array), а `addUser` слал сырой `{bug:{socketId:email}}` (object, ещё и с мусорными пустыми `{}` от disconnect). Обработчик дашборда `for (let worker of activeBugsList[bug])` падал на object → дашборд НЕ live-обновлялся (только initial snapshot). Фикс: единые `buildActiveBugsList()`/`emitActiveBugsList()` (array, все воркеры, stale-пропуск) в `addUser`+`getActiveBugsList`+`disconnect`; `disconnect` удаляет пустые `bugs[bugID]`; дашборд `showActiveBugsList` делает clear (`workers.replaceChildren()` + `store.object.storeData.length=0`) + rebuild + `initGraph()`.
3. **Graph:** узел = `#id` + summary (truncate 42) + воркеры (из DOM `#workers{id} a.worker`); рёбра depends_on/blocks направленные; `cyInstance.destroy()` перед redraw (не плодить canvas'ы).
4. **Theme:** `prefers-color-scheme` CSS-переменные (`--bw-bg/fg/surface/border/muted`) в `dashboard.css` (body/table/modal/form/borders); graph.js `graphColors()` через `matchMedia` + re-render на смену темы.
## Доп.
- Дашборд **требует логин** (auth-модалка, `auth.js`) — без него граф/таблица не строятся. `/bugs-watcher/` статика через nginx alias.
- **REST auth (root cause отсутствия summary/рёбер, подтверждено 2026-07-26):** REST **НЕ читает session-cookies** — `WebService/Server/REST.pm:203` ставит `auth_no_automatic_login=1` → `Auth::Login::Cookie.pm:38` пропускает cookies. Поэтому «залогинен на сайте» для `/rest/` бесполезно. Работает только валидный `token` (logincookie, 30 дней, `MAX_LOGINCOOKIE_AGE`) или `api_key`. Дашбордный токен в localStorage протух → `/rest/bug?token=STALE` → `{"code":32000,...}` → enrichment падает. Без auth restricted-баг → `{"bugs":[]}` (проверено повторно 2026-07-26). **Решение (`08a881a`, deployed):** re-login на 32000 (kanban-паттерн) — токен чистится, страница релоадится, юзер перезаходит, свежий токен возвращает summary + `depends_on`/`blocks`. Готового серверного api_key-proxy пока НЕТ (вариант A отложен; `server/index.js`-proxy был написан локально, НЕ deployed). Полная картина инстанса — [.claude/docs/bugzilla.md](../../docs/bugzilla.md).
- Большинство багов с пустыми depends_on/blocks → рёбра на графе бывают редко.
## Краш-баги (починены 2026-07-25, запушено в master)
Сервис падал и рестартился **>104 раз** (`systemctl show -p NRestarts`), симптомы: intermittent **502 Bad Gateway** на `/socket.io/?...&sid=...` (handshake без sid при этом 200). Node умирал → sid инвалидировался → 502. Два bug'а в `server/index.js`:
1. `addUser`: `bugID.split('#')` / `email.length` на undefined (Bugzilla-клиент шлёт addUser на странице без `?id=`, напр. `process_bug.cgi`). Фикс: guard `if (!data || data.bugID==null || data.email==null) return;` + `String()` coercion + bail если пусто.
2. `getActiveBugsList`: `workers[socketId].workTime` где `socketId=undefined` для пустой записи `{}` (накапливаются в `bugs` после дисконнекта — disconnect ставит `bugs[bugID]={}` вместо delete). Любой запуск дашборда ронял сервер, если есть хоть одна пустая запись. Фикс: `if (!socketId || !workers[socketId]) continue;` в цикле.
Коммиты в `etersoft/bugs-watcher` master: `5e27d2b` (краш-фикс), `4c81c08` (deploy: `0.0.0.0``127.0.0.1` + URL дашборда `${window.location.origin}:17003``${window.location.origin}` — upstream-версии были сломаны для деплоя за nginx). Проверено end-to-end через публичный nginx-эндпоинт, NRestarts=0.
## Тикет разработки
**Баг #12105** «Хранение статуса задачи "в работе" на лету в сервере на nodejs» (создан lav 2017-10-29, на kantegory, ASSIGNED, component bugs.etersoft.ru) — на него ссылаются upstream-коммиты `Fix #12105#cNN`. Связанные: #4529 «Поддержка bugs.etersoft.ru» (на lav), #14524 «граф активных задач» (на kantegory).
--- ---
name: reference_eterventcontrol name: reference_eterventcontrol
description: "eterventcontrol управление вентиляцией офиса (Modbus + FastAPI), где код, где запущен, порт" description: "eterventcontrol управление вентиляцией офиса (Modbus + FastAPI): где код, где запущен (контейнер vent), порт, Kerberos"
metadata: metadata:
node_type: memory node_type: memory
type: reference type: reference
originSessionId: 127a3012-0746-43d6-89a5-719f3ca01d7d originSessionId: 127a3012-0746-43d6-89a5-719f3ca01d7d
--- ---
**eterventcontrol** — сервис управления вентиляцией офиса (Modbus TCP → приточки, FastAPI/uvicorn API). **eterventcontrol** — сервис управления вентиляцией офиса (Modbus TCP → приточка Zentec 192.168.8.217:502, PID по CO2/температуре, FastAPI/uvicorn API).
## Где код ## Где код
- Репозиторий: `/srv/lav/Projects/git-eter/eterventcontrol` (Python, отдельный git-проект, НЕ в etersoft-admin-essential) - Репозиторий: `/srv/lav/Projects/git-eter/eterventcontrol` (Python, отдельный git-проект, НЕ в etersoft-admin-essential)
- Запуск: `python3 -m eterventcontrol.main loop` (он же `/usr/bin/eterventcontrol loop`) - Запуск: `python3 -m eterventcontrol.main loop`
- Есть `services/eterventcontrol.service` (ExecStart=/usr/bin/eterventcontrol loop), но на server он НЕ установлен как сервис - Свежее дерево лежит и в dev-репо, и в контейнере `/opt/eterventcontrol` (rsync)
## Где запущен ## Где запущен (production)
- Машина **server** = `192.168.0.1` - **Контейнер `vent`** = CT **762** на PVE-хосте **border**, `vent.office.etersoft.ru` = **192.168.0.67**, ALT p11
- **API: http://192.168.0.1:8000** (uvicorn/FastAPI; в коде дефолт `127.0.0.1:8000`, host переопределён на LAN-адрес 192.168.0.1) - **Сетевой мост `vmbr0`** (office LAN). Контейнер — единственное место, где LXC имеет L2-доступ к office LAN и досягаемость до Modbus-устройства.
- Крутится как обычный процесс, НЕ через systemd: `serv eterventcontrol status``not-found`, автозапуск выключен (запущен вручную/из другого места — родитель не уточнён) - **Сервис: system unit `/etc/systemd/system/eterventcontrol.service`** (root), enabled, `Restart=always`, `ExecStart=/usr/bin/python3 -m eterventcontrol.main loop`, `WorkingDirectory=/opt/eterventcontrol`. onboot=1.
- Управление: `ssh root@192.168.0.67 "systemctl {status,restart} eterventcontrol"`; логи `journalctl -u eterventcontrol`.
## Порты (дефолты в коде) - **API: http://vent.office.etersoft.ru:8000** — UI `/`, API под `/api/v1/` (`/api/v1/health`, `/api/v1/rooms`, `/api/v1/status`…). Auth: api_key (`X-API-Key`) ИЛИ Kerberos SPNEGO.
- API: **8000** (`main.py` api_cfg) - DNS: A+PTR `vent.office.etersoft.ru`→192.168.0.67 на dhcp (зона office.etersoft.ru внутренняя, на ns1 нет).
- Modbus TCP к устройству вентиляции: **502** (`vent_device.py`, `modbus_client.py`)
- InfluxDB (метрики): **8086** (`influx_client.py`) ## Сеть в контейнере (грабли шаблона 720)
- Шаблон 720 — etcnet, настроен на `breth0` (10.20.30.246), а PVE создаёт `eth0` и пишет мёртвый `/etc/systemd/network/eth0.network` (networkd **не установлен**).
- Фикс: настроить `/etc/net/ifaces/eth0/` (ipv4address 192.168.0.67/24, ipv4route `default via 192.168.0.1`, options BOOTPROTO=static TYPE=eth ONBOOT=yes), убрать `breth0` и мёртвые networkd .network, **`systemctl enable network`** (в шаблоне active-но-disabled → без enable eth0 не поднимется после ребута). См. [[lesson_lxc_etcnet_to_systemd_networkd]].
- Modbus 192.168.8.217:502 достижим через server (192.168.0.1) как gw.
## Пакеты (p11)
- epm: `python3-module-{toml,requests,fastapi,uvicorn,pydantic,gssapi,influxdb}`, `libmodbus`, `python3-module-cffi`.
- `python3-module-pylibmodbus` **нет в регулярном p11/branch** (только girar-task 402985). Ставят локальным rpm: на server кеширован `python3-module-pylibmodbus_0.6.2-alt1_noarch.rpm` (p11+402985). noarch, зависит только от `python3(cffi)`.
## Kerberos SSO
- AD-аккаунт **`http-vent`** (CN=Users, UAC 512, как `http-builder`), SPN `HTTP/vent.office.etersoft.ru`.
- Keytab `/etc/eterventcontrol/http.keytab` (RC4/enctype 23, KVNO 2). Создание: `samba-tool user create http-vent --random-password` + `spn add HTTP/vent.office.etersoft.ru http-vent` + `domain exportkeytab --principal=HTTP/vent.office.etersoft.ru` (на dc.etersoft.ru, lav+sudo).
- `[auth] fqdn = "vent.office.etersoft.ru"`, `admin_users = ["lav@ETERSOFT.RU","owl2@ETERSOFT.RU"]`.
- Тест acceptor: `gssapi.Credentials(name=HTTP@vent.office.etersoft.ru, usage=accept, store={keytab:...})` — грузится OK. `kinit -k HTTP/...` падает «not found» (SPN-as-client не работает в этом Samba) — это нормально, акцептор-роль работает; реальный тест — `curl --negotiate` с клиентского TGT.
## Порты / внешние сервисы (из config.toml)
- Modbus TCP: 192.168.8.217:502
- InfluxDB: `http://influxdb.k8s.eterfund.ru:80`, БД `co2_levels`
- API: 8000
- Сенсоры (читаются из InfluxDB): `fenix.office.etersoft.ru`, `lav.office.etersoft.ru`
## Важно
- **Два инстанса одновременно запускать нельзя** — оба пишут Modbus control-регистры (конфликт управления, износ EEPROM). Перед миграцией — cutover-свап (stop старого → start нового).
- `cleanup()` восстанавливает initial fan/supply (пишет, только если значение успело измениться).
## История
- До 2026-07-21: крутился как `systemctl --user` сервис у `lav` на `server` (192.168.0.1, шлюз, не PVE). После ребута server 02:49 был down ~12ч (unit с `ConditionHost=server.office.etersoft.ru`). Перенесён в контейнер vent (CT 762 на border), server-side user-service disabled.
---
name: reference_ikev2_fr_tunnel
description: "Обход claude.ai/Anthropic через Францию вся система fr/ikev2.fr (route-update + .140 + rpi). Data path, доступ, диагностика \"egress=.140\", фикс, persistence"
metadata:
node_type: memory
type: reference
originSessionId: e7921933-7fc7-4871-889c-9d5efe71c046
---
Обход `claude.ai` и Anthropic-доменов через французский egress. Вся система = 3 части: группа `fr` в route-update (на igw/egw) → IPsec-туннель `.140` ↔ rpi(Франция). Зафиксировано после инцидента «egress = .140 вместо Франции» (2026-07-05).
## Data path (как идёт трафик)
```
клиент/сервис
│ (claude.ai резолвится в 160.79.104.10 / 2607:6bc0::10)
igw / egw ── ip rule: lookup claude.ai (pref 900) ──▶ table claude.ai (per-list, table 223)
│ gateway 91.232.225.140 (v4) / unreachable (v6)
▼ v6 = ICMP unreachable (fast reject ~2мс) — т.к. туннель IPv4-only
91.232.225.140 «ikev2» (IPsec RESPONDER, strongSwan)
│ ip route get claude.ai → via 10.10.10.6 dev ipsec0
│ XFRM if_id=42 → шифрует (ESP) → NAT-T :4500
rpi (Франция, public 78.193.2.190, Orange/AS3215) — контейнер ikev2-fr (INITIATOR)
│ ipsec0 = 10.10.10.6/30, MASQUERADE → eth0
claude.ai (видит src 78.193.2.190 — Франция, проходит геоблок)
```
## Часть 1. Группа `fr` в route-update (igw + egw)
`routes.d/fr/` (v4) и `routes6.d/fr/` (v6) на обоих шлюзах:
- `gateway` (v4) = `91.232.225.140 metric 50`
- `gateway` (v6) = `unreachable` (route-type keyword, см. [[reference_route_update_sh]])
- `options` = `pref 900` (явный override, **перебивает** `egw/ai` на pref 1210)
- `claude.ai.list` → симлинк на `/root/egw-route/claude.ai.list` (Anthropic-домены)
- таблица `claude.ai` (per-list, №223 на igw)
Симметрично на igw и egw. Подробно механика route-update — [[reference_route_update_sh]]. Сами списки живут в git `eterfund/egw-route.git` (`/root/egw-route/` на хостах).
## Часть 2. .140 «ikev2» (responder)
- Хост `ikev2` = `91.232.225.140`. Доступ: `ssh root@91.232.225.140`.
- strongSwan + swanctl. Конфиг: `/etc/strongswan/swanctl/conf.d/site-to-site.conf`.
- Соединение `site-to-site`: `local_addrs=91.232.225.140:4500`, `remote_addrs=%any` (**responder**, ждёт инициатора). local id `ikev2.fr.egw.etersoft.ru`, remote id `fr.egw.etersoft.ru`. PSK в `secrets`.
- `ipsec0` XFRM if_id=42, `10.10.10.5/30`.
- Перезагрузка конфига: `swanctl --load-all` (или `systemctl restart strongswan`).
## Часть 3. rpi (initiator, Франция)
- **Raspberry Pi** во Франции, public `78.193.2.190` (Orange).
- Доступ через reverse-SSH: `ssh -p 10338 root@anyssh.eterhost.ru` (hostname `rpi`). Reverse-SSH поднят контейнером `tunnel-fr` (autossh `-R 10338:localhost:22`, проект `/root/tunnel-fr/`, пользователь `a338`, ключ `/root/tunnel-fr/etersoft.key` → COPY в образ как `/root/.ssh/etersoft.key`).
- **Альтернативный доступ** (когда reverse-SSH упал): с .140 через IPsec → Docker bridge. `ikev2-fr` = host networking, rpi-хост = `172.17.0.1` (Docker bridge): `ssh root@91.232.225.140 'ssh root@172.17.0.1 "hostname"'`. Примечание: `10.10.10.6` — это контейнер `ikev2-fr`, НЕ rpi-хост.
- IPsec-инициатор = docker-контейнер **`ikev2-fr`** (image `ikev2-fr-client`), проект `/opt/ikev2-fr-docker/` (Dockerfile, swanctl.conf, entrypoint.sh, healthcheck.sh, docker-compose.yml, README.md).
- `swanctl.conf` в образе: `remote_addrs=91.232.225.140:4500`, local id `fr.egw.etersoft.ru`. PSK = тот же, что на .140. `start_action=start`, `close_action=start`, `dpd_action=restart`.
- `entrypoint.sh`: создаёт `ipsec0` (10.10.10.6/30, if_id 42), MASQUERADE на eth0, `exec charon-systemd` (PID 1).
- `ipsec0` = `10.10.10.6/30`.
| | .140 (responder) | rpi / ikev2-fr (initiator) |
|---|---|---|
| public | 91.232.225.140 | **78.193.2.190** |
| local id | ikev2.fr.egw.etersoft.ru | fr.egw.etersoft.ru |
| ipsec0 | 10.10.10.5/30 | 10.10.10.6/30 |
| conf | /etc/strongswan/swanctl/conf.d/site-to-site.conf | /opt/ikev2-fr-docker/swanctl.conf (+в образе) |
## Ключевая диагностика: «egress = .140 вместо Франции»
= **IPsec SA не установлен**. Тогда .140 маршрутизирует claude.ai через свой default (Etersoft uplink 91.232.225.1) и egress'ит как `91.232.225.140` (Anthropic отдаёт 403 на curl).
Проверки:
```bash
# на .140:
ssh root@91.232.225.140 'swanctl --list-sas | grep -i ESTABLISHED' # пусто → SA down
ssh root@91.232.225.140 'ip route get 160.79.104.10' # via ipsec0=OK, via eth0=down
# на rpi:
docker exec ikev2-fr swanctl --list-sas # SA со стороны инициатора
docker ps | grep ikev2-fr # Up (unhealthy)? → SA down
```
AUTH_FAILED в логе .140 от IP типа 164.90.141.172 / 205.210.31.215 — **сканеры**, не наш пир (они до IKE_AUTH не доходят или фейлятся раньше). Реальный пир = rpi (78.193.2.190).
## Фикс
Переинициировать SA:
```bash
ssh -p 10338 root@anyssh.eterhost.ru 'docker exec ikev2-fr swanctl --initiate --child s2s'
```
После этого `swanctl --list-sas` на .140 покажет `ESTABLISHED ... remote 'fr.egw.etersoft.ru' @ 78.193.2.190`, `ip route get 160.79.104.10``via 10.10.10.6 dev ipsec0`, egress claude.ai = 78.193.2.190.
## Persistence (self-healing)
`ikev2-fr` имеет label `autoheal=true``docker-compose.yml`). Контейнер `autoheal` (`willfarrell/autoheal`, проект `/root/tunnel-fr/`) рестартит unhealthy-контейнеры с этой меткой:
```
SA down → healthcheck (swanctl --list-sas | grep ESTABLISHED) fail → unhealthy
→ autoheal рестартит ikev2-fr → entrypoint → charon → start_action=start → SA ↑
```
~60-90с восстановление. На свежем старте SA поднимается сам. **Именно отсутствие autoheal-label** было причиной 7-дневного дауна (контейнер висел unhealthy, никто не рестартил).
Все настройки ikev2-fr в образе `ikev2-fr-client` (== `/opt/ikev2-fr-docker/*`, `docker diff` чист) — пересоздание через `docker compose up -d` ничего не теряет.
## Мониторинг (route-health)
`route-health.sh` (timer на igw/egw) + Telegraf → InfluxDB (`gateways`): по шлюзам собирает ping/iperf3/vpn/proxy, пишет `health.json` (кормит route-web UI).
**ikev2.fr и ikev2.gr — ping-only** (`is_pingonly_gw` в route-health.sh): у rpi-пира нет iperf3-сервера и VPN-status-feed, поэтому судят **только по ping** (loss≥50 или no-data = dead). Раньше они проверялись по iperf3 → success=0 → перманентный false-`down`, что маскировало реальные обрывы (7-дневный даун не заметили). Теперь: туннель up → ping через него идёт → healthy; упал → ping до 10.10.10.6 не идёт → dead (реальная детекция).
Health виден в route-web UI (`health.json`: `groups[].gateways[]` для fr → ikev2.fr). **Push-алертинга пока нет** — состояние видно в UI + rpi восстанавливается через autoheal, но уведомления (Telegram/почта) не настроены.
## Грабли / нюансы
- `claude.ai` на plain-`curl` = **403** (anti-bot по User-Agent). С браузерным UA = **302** (норма). 403 ≠ геоблок.
- Туннель **IPv4-only** → v6 claude.ai = unreachable (fast reject), не пытаться чинить v6 через rpi.
- `docker logs ikev2-fr` пустой (charon-systemd пишет в journald, которого нет в контейнере) — диагностика через `swanctl --list-sas` / `--initiate`.
- PSK хранится в swanctl.conf с обеих сторон (в secrets); при смене — одинаково в `/opt/ikev2-fr-docker/swanctl.conf` (rpi) и `/etc/strongswan/swanctl/conf.d/site-to-site.conf` (.140), потом rebuild/reload.
---
name: reference_ikev2_gr_tunnel
description: "Greek egress для gvpn через gr.egw (CT 706 .139 responder) parentglobal (Греция, initiator). Data path, доступ, фикс 'gvpn offline = CT 706 stopped'. Параллельно [[reference_ikev2_fr_tunnel]]"
metadata:
type: reference
---
Greek egress для **gvpn** (CT 752 на enceladus). Полный аналог французского туннеля ([[reference_ikev2_fr_tunnel]]), только страна/хосты другие. Зафиксировано 2026-07-15 после инцидента «gvpn offline».
## Data path
```
gvpn (CT 752, enceladus) default via 91.232.225.139
gr.egw / CT 706 (.139, PVE-нода border) — IPsec RESPONDER (strongSwan/swanctl)
│ ip_forward=1, route: default via 10.10.10.6 dev ipsec0 metric 50
│ ipsec0 = 10.10.10.5/30, XFRM if_id=42
parentglobal (Греция, Cosmote, dyn 94.69.12.186, за NAT) — INITIATOR
│ IKE id gr.egw.etersoft.ru, ipsec0 = 10.10.10.6/30
инет (эгресс = 94.69.12.186, Греция)
```
gvpn выходит в инет как `94.69.12.186` (проверка: `lxc-attach -n 752 -- curl -s https://icanhazip.com`).
## Узлы
| | gr.egw (responder) | parentglobal (initiator) |
|---|---|---|
| что это | CT 706 `ikev2.gr.egw.etersoft.ru` на **border** | ALT Workstation K 11 дома в Греции (Cosmote) |
| IP | 91.232.225.139 (public) | dyn **94.69.12.186**, за NAT (192.168.1.99 lan) |
| local id | ikev2.gr.egw.etersoft.ru | gr.egw.etersoft.ru |
| ipsec0 | 10.10.10.5/30 | 10.10.10.6/30 |
| conf | `/etc/strongswan/swanctl/conf.d/site-to-site.conf` | `/etc/strongswan/swanctl/conf.d/site-to-site.conf` |
| start_action | none (responder) | **start**, dpd_action=restart, close_action=start (self-heal) |
| PSK | 7f02acba… (id-server ikev2.gr.egw… / id-client gr.egw…) | тот же |
PSK и конфиг — близнецы fr.egw/CT704, только `.139`/`gr` вместо `.140`/`fr`.
## Доступ к parentglobal (за NAT)
- **Reverse-SSH:** `ssh -p 10337 etersoft@anyssh.ru` (anyssh.ru=91.232.225.8), потом sudo. См. [[reference_remote_ssh_access]].
- **Через IPsec (когда SA up):** `ssh -J root@91.232.225.139 root@10.10.10.6`.
## Ключевая диагностика: «gvpn offline»
gvpn (CT 752) имеет `default via 91.232.225.139 dev breth0`. Если **CT 706 остановлен** → у gvpn нет шлюза → offline. Симптом: `ping 91.232.225.139` из gvpn = 100% loss, `lxc-attach -n 752 -- curl` виснет.
```bash
ssh root@border 'pct status 706' # stopped → причина
ssh root@border 'pct start 706' # поднять responder
ssh root@border 'lxc-attach -n 706 -- swanctl --list-sas' # пусто → parentglobal не подключился
```
CT 706 = responder (`remote_addrs=%any`, `start_action=none`) — сам SA не инициирует. Если parentglobal не переподключился сам (редко), инициировать с его стороны:
```bash
ssh -p 10337 etersoft@anyssh.ru 'sudo swanctl --initiate --child s2s'
```
## Фикс 2026-07-15 (этот инцидент)
CT 706 был остановлен (onboot:1, но не бежал; все 5 собратьев ikev2.*.egw бежали) → gvpn offline.
1. `ssh root@border 'pct start 706'` — responder поднялся (ipsec0=10.10.10.5/30).
2. gvpn снова пингует .139, но SA пустой.
3. `ssh -p 10337 etersoft@anyssh.ru 'sudo swanctl --initiate --child s2s'` → SA ESTABLISHED, gvpn эгрессит `94.69.12.186`.
После ручного initiate дальше self-heal (`close_action=start`).
## Различие с fr
- fr.egw (CT 704, .140) обслуживает **обход claude.ai/Anthropic** через igw/egw (группа `fr` в route-update, таблица claude.ai). См. [[reference_ikev2_fr_tunnel]].
- gr.egw (CT 706, .139) обслуживает **только gvpn** (default route). Группы `gr` в route-update НЕТ, route-health ikev2.gr не мониторит (только ikev2.fr/ikev2.gr формально ping-only, но routes.d/gr пуст).
## Смежный туннель на parentglobal: `divserver`
У parentglobal в swanctl **две** conn: `site-to-site` (→.139, этот документ) и `divserver` (→divserver, telemt/Telegram MTProxy, ipsec1 10.43.0.0/30). На 2026-07-15 `divserver` SA **down** — это отдельная deferred-проблема ([[project_dchat_telemt_greek_tunnel_down]]), к gvpn-эгрессу отношения не имеет.
## Грабли
- [[lesson_anyssh_ru_in_91232225]] — НЕ добавлять на parentglobal маршрут `91.232.225.0/24 via ipsec0` до установки SA: перехватит трафик к anyssh.ru (.8), умрёт reverse-SSH, parentglobal недоступен извне.
- Туннель **IPv4-only** (как и fr). gvpn без IPv6 специально ([[lesson_clone_container_iptables]] контекст, fvpn/gvpn v6 убран 2026-07-15).
- `pct start` пишет «failed to connect to monitor socket: Connection refused» — безвредно (QEMU monitor ещё не готов), старт идёт.
---
name: reference-mysql-servers
description: "Инвентарь MySQL/MariaDB-серверов инфры: 4 прод-хоста (auth 5.5 / office 8.0 / fond 5.7 / host03 8.0) + тест-стенды; RT 4.4.x работает с 5.5/5.7 и MariaDB 10/11, но НЕ MySQL 8"
metadata:
type: reference
---
# MySQL/MariaDB серверы в инфре Етерсофт
Версии сняты **pre-auth handshake-баннером** (без credов) — Python `socket`→ HandshakeV10, версия идёт до авторизации. См. `~/tmp/claude/mysql_banner.py`.
## Прод-серверы (4 выделенных, port 3306 открыт в сети)
| Хост | IP | Версия | Назначение |
|---|---|---|---|
| **mysql.auth**.etersoft.ru | 10.20.30.202 | MySQL **5.5.43**-alt2 | shared: `eter_rt` (RT), `mail`, `eter_mnogosearch`, `eter_sales`, `phpmyadmin`. OS **ALT p8** EOL. [[reference-rt-etersoft-ru]], [[lesson-mail-mysql-credentials]] |
| **mysql.office**.etersoft.ru | 10.20.30.191 | MySQL **8.0.16**-alt1 | почти наверняка shared-БД офиса (zabbix CT132, taiga CT152, wiki CT235, collectd CT213, noc CT225). ⚠️ у имени **НЕТ A-записи** (forward не резолвится), только stale PTR. SSH-ключа нет. |
| **mysql.fond**.eterhost.ru | 10.20.30.81 | MySQL **5.7.28**-alt1 | выделенная БД «фонда» (fond). Чужой хост. |
| **mysql.host03**.eterhost.ru | 10.20.30.83 | MySQL **8.0.30**-alt1.1 | выделенная БД хостинга host03. |
| **mariadb.dmz**.etersoft.ru | 10.20.30.195 | MariaDB **11.8.8** | ✅ **NEW (CT 369, border, 2026-07-16)** — выделенный RT-хост. Full-clone шаблона 720 → etcnet убран, **systemd-networkd**. utf8mb4/utf8mb4_unicode_ci, `innodb_buffer_pool_size=4G`, `max_allowed_packet=64M`, bind **10.20.30.195:3306** (skip-networking OFF). Зарегистрирован в phpMyAdmin (mysql.eterhost.ru, CT 240; в массиве `$hosts` в `/var/www/webapps/phpMyAdmin/config.inc.php`). **Юзеры созданы** (пароли = как на mysql.auth): `root`@`10.20.30.%`+`91.232.225.23` (=`6f1XQPLsfd`, pma ходит с **10.20.30.99**), `eter_rt`@`%` (=`Veegi9sh`, боевой — RT переподключится без смены пароля при смене DatabaseHost), `phpmyadmin` controluser (=`CHwJTSr58wwBTYBS`, фикс. в config.inc.php) + 19 `pma__` таблиц. Готов к заливке дампа `eter_rt`. [[plans-rt-update]] |
Все 4 старых — на DHCP (без static ip= в PVE-конфиге). **mariadb.dmz — статический** (10.20.30.195). SSH-ключа к office/fond/host03 нет — к auth (CT 219) и mariadb.dmz (CT 369) есть.
## Тест-стенды
- **rt-test** (CT 183, gefest, 192.168.0.191) — локальная **MariaDB 11.8.8** (дамп боевой `eter_rt`), RT 4.4.7 с ней работает. [[plans/rt-update]]
- **test-mail** (CT 943, border, 10.20.30.246) — MariaDB **bound на 127.0.0.1:3306** (по сети недоступен), копия таблиц `mail`. [[plans/mail-etersoft-ru]]
## Совместимость с RT (для миграции [plans/rt-update])
RT 4.4.x работает с **MySQL 5.5 / 5.7** и **MariaDB 10 / 11**, но **НЕ с MySQL 8** (RT не использует quoted identifiers → `SQL syntax error` на `Groups`/`Principals`, баг 7693#c51-53). Поэтому:
- ✅ RT-совместимые прод-хосты: только **auth (5.5)** и **fond (5.7)**
- ❌ office (8.0) и host03 (8.0) — MySQL 8, RT не заведётся
- Решение по БД для RT: **новый выделенный MariaDB 11 на p11** — ✅ ВЫПОЛНЕНО, это **mariadb.dmz.etersoft.ru (CT 369, 10.20.30.195, MariaDB 11.8.8)**. (office/host03 отлетают по версии, auth — EOL-p8 shared с которого уходим, fond — чужой). Открытый вопрос про БД-хост в [[plans/rt-update]] закрыт.
## ⚠️ Документация
`plans/mail-etersoft-ru.md` (стр. 17) ошибочно пишет `mysql.auth.dmz.etersoft.ru (10.20.30.191)` — mysql.auth реально на **.202**, а .191 = mysql.office (MySQL 8). При ротации/миграции не перепутать.
## 🔜 Будущее: mysql.auth сам требует обновления (отдельная задача, «потом»)
`mysql.auth` (CT 219) = **ALT p8 + MySQL 5.5.43** — глубоко EOL, и это **shared-хост** (`eter_rt` + `mail` + `eter_mnogosearch` + `eter_sales`). Его собственная миграция (p8→p11, MySQL 5.5→MariaDB 10/11) — крупная и рискованная: `mail` критична и тесно связана (Cyrus/Postfix-мапы, cyradm, sec — см. [[lesson-mail-mysql-credentials]]). **Вывод для RT:** не привязывать RT к mysql.auth — иначе его БД-коннекшен переедет ещё раз при будущем апгрейде auth, плюс RT окажется завязан на чужую рискованную миграцию. Дать RT свой выделенный MariaDB-хост сейчас → RT независим.
---
name: reference_pam_limits_desktop_builder64
description: "pam-limits-desktop на builder64: nproc/nofile подняты из-за Chromium + ИИ-агентов (bug 19109)"
metadata:
node_type: memory
type: reference
---
## 2026-07-10 — подняты лимиты на builder64
Причина: Chromium крашился с `pthread_create: Resource temporarily unavailable` (bug https://bugs.etersoft.ru/show_bug.cgi?id=19109). ИИ-агенты (codex+claude+mimo) занимают ~770 потоков, Chromium с 50 вкладками — ещё ~1500-2000. Лимит4096 не хватало.
**Изменён** `/etc/security/limits.d/90-desktop.conf` (pam-limits-desktop-0.1-alt2):
```
nproc: soft 4096 → 16384, hard 5120 → 32768
nofile: soft 8192 → 65536, hard 10240 → 131072
```
**Добавлено** в `/etc/systemd/system.conf`:
```
DefaultLimitNOFILE=65536:131072
DefaultLimitNPROC=16384:32768
```
Требуется полный logout/login графической сессии (systemd user наследует лимиты от PAM при входе).
Серверы не затронуты — pam-limits-desktop не установлен на серверах (chat.eterfund.ru и др.).
---
name: reference_pantum_scanner
description: "Pantum M6700DW сетевой сканер настройка через sane-airscan (eSCL)"
metadata:
node_type: memory
type: reference
---
# Pantum M6700DW — сетевой сканер (192.168.0.134)
## Инфраструктура
- Модель: Pantum M6700DW series, IP 192.168.0.134
- eSCL: http://192.168.0.134:631/eSCL/
- Native (pantum6500): порт 9200 — НЕ работает (Invalid argument)
- Баг: https://bugs.etersoft.ru/show_bug.cgi?id=19196
## Рабочее решение: sane-airscan (eSCL)
- sane-airscan ≥0.99.36 (из Sisyphus)
- Устройство: `airscan:e0:Pantum M6700DW` (или `airscan:e0:Pantum M6700DW Series 6E14A7` при auto-discovery)
## Настройка
Для виртуальных машин (mDNS не работает) — ручная запись в `/etc/sane.d/airscan.conf`:
```
[devices]
"Pantum M6700DW" = http://192.168.0.134:631/eSCL/, eSCL
```
Для физических машин — auto-discovery работает (не нужна ручная запись).
## Конфликт с pantum-бэкендами
Pantum-бэкенды (pantum6500, pantum_bm4200 и т.д.) мешают `scanimage -L`. Решение:
```bash
for f in /etc/sane.d/dll.d/pantum*; do mv "$f" "$f.disabled"; done
```
## Матрица (2026-07-11)
| Машина | ALT | sane-airscan | scanimage -L | сканирование |
|---|---|---|---|---|
| lav (VM) | Sisyphus | 0.99.36 | ✅ (ручная запись) | ✅ |
| statos (phys) | p11 | 0.99.37 (Sisyphus) | ✅ (auto-discovery) | ✅ |
| fenix (VM) | p10 | 0.99.29 | ❌ | ❌ (нужен Sisyphus) |
## Установка на новую машину
```bash
sudo epm install sisyphus/sane-airscan
# Для VM: добавить ручную запись в /etc/sane.d/airscan.conf
# Отключить pantum-бэкенды:
for f in /etc/sane.d/dll.d/pantum*; do sudo mv "$f" "$f.disabled"; done
```
---
type: reference
description: "BIRD2 routing и аплинки на priv конфигурация, preferences, диагностика 8.8.8.8"
---
# BIRD2 и аплинки на priv (91.232.225.1)
## Аплинки
| Интерфейс | IP | Провайдер | AS | Тип | BIRD pref |
|---|---|---|---|---|---|
| ether3 | 109.235.223.53 | Petrosvyaz | AS50538 | default route | 50 |
| ether1 | 85.235.192.190 | Prometey/Severen | — | down (нет ARP) | — |
| enp8s0f0.3210 | 178.18.227.15 | DATA-IX (Peering Ltd) | AS50952 | пиринг | 150 |
| enp8s0f0.3211 | 94.124.183.151 | GlobalNet | AS31500 | транзит | 50 |
## BIRD конфигурация
- Main config: `/etc/bird/bird.conf`
- Import filters: `/etc/bird/bird.d/globalnet.conf`
- Kernel table: 5 (ip rule `from all lookup 5 pref 100` в `/etc/net/ifaces/default/ipv4rule`)
- ECMP: `merge paths yes` в kernel protocol k4/k6
### Import filters
```
import_ix_v4: preference = 150 # IX peering beats transit
import_transit_v4: preference = 50
petrosvyaz4: preference = 50
```
### AS-PATH prepend
- GlobalNet (3211): 1x prepend (`bgp_path.prepend(198324)` в `export_gbl_v4/v6`)
- DATA-IX (3210): без prepend (`export_own_v4/v6`)
- Petrosvyaz (ether3): без prepend
## Проблема с 8.8.8.8 (UDP 53)
**Симптомы:**
- `dig @8.8.8.8` (UDP 53) — timeout с любых хостов Etersoft
- `dig @8.8.8.8 +tcp` (TCP 53) — работает
- `ping 8.8.8.8` — работает
- `dig @1.1.1.1`, `dig @9.9.9.9` — работает (UDP 53)
**Маршрут BIRD:** `8.8.8.8 via 178.18.225.111 dev enp8s0f0.3210 table 5` (DIX, pref 150)
**Traceroute UDP 53:** пакеты доходят до Google (хопы 3-4: 216.239.x, 172.253.x), Google не отвечает на UDP 53.
**iptables на priv:** нет правил блокирующих UDP 53. OUTPUT/FORWARD policy ACCEPT.
**Вывод:** блокировка UDP 53 к 8.8.8.8 на уровне провайдеров/DPI. Специфична для Google DNS, не для других резолверов.
## Диагностика
```bash
# Проверить BIRD маршрут
birdc show route for 8.8.8.8 all
# Проверить import filter preferences
grep -A5 "import_ix_v4\|import_transit_v4" /etc/bird/bird.d/globalnet.conf
# Traceroute UDP 53
traceroute -n -U -p 53 -s 109.235.223.53 -m 15 -w 2 8.8.8.8
# Проверить conntrack
conntrack -L -p udp --dport 53 | grep 8.8.8.8
```
## Миграция БД после обновления пакета
При обновлении пакета roundcube миграции БД НЕ выполняются автоматически.
**Проверить текущую версию:**
```
mysql -h mysql.dmz.etersoft.ru -u eter_roundcube -p4h7dGS2Sp3ruw5Vd eter_roundcube -e "SELECT * FROM \`system\`;"
```
**Запустить миграции:**
```
cd /usr/share/roundcube && php bin/updatedb.sh --dir SQL --package roundcube
```
**Если миграция падает** (таблица/колонка уже существует — бывает от плагинов):
1. Посмотреть что делает миграция: `cat /usr/share/roundcube/SQL/mysql/<VERSION>.sql`
2. Пропустить проблемную миграцию: `UPDATE \`system\` SET value='<VERSION>' WHERE name='roundcube-version';`
3. Запустить updatedb.sh снова
**Важно:** `--dir SQL` (не `--dir SQL/mysql`!) — скрипт сам добавляет `/mysql` к пути.
### Симптом: вложения не прикладываются к письмам
Если при прикреплении файлов к письму Roundcube молча не загружает вложение — проверить наличие таблицы `uploads` в БД. Если таблицы нет — миграции не выполнялись после обновления пакета. Пример: БД на версии 2016112200 (Roundcube ~1.2), пакет — 1.7.2. После `updatedb.sh` создаётся таблица `uploads` и вложения работают. [ses_077433517ffe]
---
name: reference_route_update_sh
description: "route-update.sh на igw архитектура, слои персистентности IP, pref, inotify; баг volatile_ips (fixed 2026-07-05)"
metadata:
node_type: memory
type: reference
originSessionId: e7921933-7fc7-4871-889c-9d5efe71c046
---
`router/route-update.sh` на igw.etersoft.ru — декларативная policy-маршрутизация. Каталоги `routes.d/` (v4) и `routes6.d/` (v6), site-specific (в `.gitignore`), сам скрипт — в git.
**Архитектура**
- **Таблица = per-list**, не per-group. Каждому `.list` — своя таблица 200-250 (автовыделение в `/etc/iproute2/rt_tables`, напр. `ai`=201, `claude.ai`=223). Несколько `.list` в каталоге группы = несколько таблиц, шарящих `gateway`.
- **`ip rule` pref = порядок обработки, НЕ имя/номер таблицы.** Группы обрабатываются **по алфавиту** (`for d in routes*.d/*/`, локаль `ru_RU.UTF-8`**цифры сортируются ПОСЛЕ букв**, так что `0-fr` встанет последним, не первым!), каждой — блок в 100 с 1000, внутри группы `.list` += 10. По умолчанию `egw` (ai=1210) перебивает `fr` (claude.ai=1300).
- **Override приоритета**: `options` файл в группе со строкой `pref N``process_routes` использует N как pref_base вместо алфавитного. `option_value()` в `functions`. Применено: `routes.d/fr/options` + `routes6.d/fr/options` = `pref 900` → claude.ai (fr) перебивает ai. Грабля (fixed): переезд правила на новый pref не происходил при skip-ветке «resolved unchanged» — `_fixup_rule_pref` теперь вызывается и перед этим skip.
- gateway: IP/hostname/`default`, метрики (`metric N`) = failover, multipath если несколько строк без метрики.
- **gateway route-type keywords** (added 2026-07-05): `blackhole`/`unreachable`/`prohibit`/`throw` — скрипт ставит маршруты этого типа (без `via`), `ip route replace <kw> <dst> table N`. `unreachable`/`prohibit` = ICMP назад → мгновенный отказ (curl падает за ~2мс). Применено на `routes6.d/fr` → claude.ai v6 reject (50 unreachable-маршрутов, вкл. covering-подсети). Keyword первым в `gateway` → вся группаrejected. **Почему v6=unreachable**: fr-узел `ikev2.fr` (.140, v4-only gateway группы fr) **не имеет IPv6** — v6-пути во Францию нет, поэтому reject fast вместо попытки шлюза.
**Слои персистентности IP** (для DNS round-robin tolerance)
1. `resolved` — текущий набор IP списка.
2. `resolve_history/{1..20}` — union из `HISTORY_SIZE=20` snapshot'ов, мерджится в `resolved` каждый запуск. Самоочищается за N запусков **только если IP перестаёт поступать**.
3. `volatile_ips/<домен>` (только IPv6) — для volatile-доменов (TTL≤120 / diff-resolvers); `expand_volatile_subnets` собирает IP с нескольких резолверов и добавляет covering-подсети. **Имена файлов = домены.**
**Баг (fixed 2026-07-05):** `expand_volatile_subnets` читал весь `volatile_ips/` как список доменов к ре-резолву, **не удаляя осиротевшие** → удалённый из `.list` домен пожизненно перевпрыскивал свои IP в таблицу. Фикс: prune `volatile_ips/<домен>` если домена нет в текущем `.list` (передан `$_f`). Побочно вычистило кучи стаи в web-bypass (~40 доменов), blocked и др.
**Грабли при очистке state:** после удаления IP из `resolved`+history перезагрузка может пройти мимо stale-removal — если `resolved` совпал сам с собой, load идёт по короткому пути «без изменений» и kernel stale-маршруты не трогает. Добивать вручную: `while ip -6 route del <dst> table <T>; do :; done`.
**Триггеры на igw:** inotify-наблюдатель (PID ~962337) — `route-update.sh` при изменении `/root/egw-route/`, `/root/antifilter/`, `/root/antifilter.network/`; + `route-update.timer` раз в 30 мин. Запуск: `./route-update.sh [--resolve|--force|-v]`, `--flush GROUP`, `--add|--del IP|DOMAIN GROUP`.
См. [[reference_igw_route_api]] (HTTP API поверх тех же списков), [[feedback_route_list_symlink_only]].
---
name: reference-rt-etersoft-ru
description: "RT (Request Tracker) rt.etersoft.ru где живёт, как зайти, архитектура, логирование, лимиты, проблемы; azbykar (CT 102 фронтенд): ротация nginx-логов, logrotate state в /var/lib/status"
metadata:
node_type: memory
type: reference
originSessionId: 3460c94c-e556-43c7-bec4-edb5ce23ef05
---
# RT (Request Tracker) — rt.etersoft.ru
## Где живёт и доступ
- **PVE: CT 233** (LXC), name `rt.etersoft.ru`, нода **enceladus**, pool Infrastructure.
- Публичный IP **91.232.225.23** (TLS терминируется на фронтенде .23 — `client: 10.20.30.23` в логах nginx).
- Внутренний IP **10.20.30.53**, hostname `rt`.
- **SSH:** `ssh root@10.20.30.53` (ключ добавлен 2026-07-06, до этого работал только `lav@` с sudo по паролю).
- Публичные алиасы (rt.etersoft.ru порты 32/5322/6722) **не работают** — ходить внутренним IP.
## 🆕 ПРОД с 2026-07-16: CT 412 + MariaDB 11 (миграция с CT 233)
Cutover выполнен 2026-07-16. Старый **CT 233 остановлен 2026-07-23** (`onboot=no`, до этого висел idle-призраком).
**Split-brain'а не было** (проверено 2026-07-23): на CT 233 нет ни fetchmail в `/etc/cron.d`/`crontab`/`anacrontab`/timers,
`fetchmail_rt.log` пуст с 19 июл, а старая БД `eter_rt` на CT 219 (mysql.auth.dmz) **удалена при миграции**
(осталась только `mail`) — CT 233 после cutover был DB-less и физически не мог создать тикет. nginx переключён.
- **Контейнер CT 412** на ноде **border** (PVE), hostname `rt`, внутренний IP **10.20.30.196**,
DNS `rt.dmz.etersoft.ru`. `ssh root@10.20.30.196`.
- **ОС ALT p11 x86_64**, **RT 6.0.3-alt1** (cutover 4.4.9→6.0.3 **2026-07-23**, girar-задача **426033** [test-only];
ранее 4.4.9/4.4.7). Схема БД апгрейднута **4.4.9→6.0.3** штатно (`rt-setup-database --action upgrade`, см. ниже),
0 ошибок; utf8mb4-конверсия больших таблиц шла ~15 мин (Attachments/Transactions — `Repair by sorting`).
DB `eter_rt` на **mariadb.dmz = CT 369**
(`ssh root@10.20.30.195`, root@localhost socket-auth, без пароля; root@10.20.30.% mysql_native_password **с паролем** — для апгрейда НЕ нужен).
MariaDB **11** (НЕ MySQL 8 — баг 7693). `innodb_buffer_pool_size=4G`, utf8mb4, max_allowed_packet=64M.
- **⚠️ runtime-deps 6.0 на p11 (ставить вручную — spec rt их не требует жёстко):**
`perl-Time-ParseDate ≥2026` (girar **426102**), `perl-CSS-Inliner`+`perl-GraphViz2` (#426095),
`perl-DateTime-Set`+`perl-DateTime-Event-Recurrence`, и **`perl-DBIx-SearchBuilder ≥1.85` (girar #426059)**
без него `Can't locate object method SelectAllColumns` на SavedSearch/QueueList (RT/Record.pm:2829).
См. [[lesson_rt6_runtime_deps_missed]], [[reference_rt_package_requires]].
- **`lesson_rt_condition_perl538` (RT/Condition.pm:201 без `;`) в 6.0.3 починен** (стр.245 `= undef;`, `perl -c` OK) — патч не нужен.
- **Scrips мигрировали 1:1** в `ObjectScrips` (5.0): Scrips=30, ObjectScrips=30, Templates=42, Queues=21.
- **Frontend azbykar (CT 102, 10.20.30.23)** `proxy_pass` переведён `10.20.30.53 → 10.20.30.196`
(бэкап `.bak-20260716-cutover`). RT-контейнер слушает :80.
- **realip починен 2026-07-16:** в `/etc/nginx/sites-enabled.d/rt.etersoft.ru.conf` на CT 412
`set_real_ip_from 10.20.30.23; real_ip_header X-Forwarded-For; real_ip_recursive on;`
(бэкап `.bak-20260716-realip`). Теперь в логах RT реальные клиентские IP (был везде 10.20.30.23).
- **UI-фикс 2026-07-23 (RT 6.0 за TLS-прокси воспринимал себя как :80) — три стадии, одна
первопричина.** Цепочка: azbykar CT 102 (TLS :443) → CT 412 nginx (:80) → rt-fcgi. Бэкендный
`:80` утекал во все места, где RT выводит свой URL из запроса:
1) **Scheme.** `RT-fcgi` не передавал схему → RT плодил `http://` self-URLs →
`SecurityError: pushState` (http vs https origin), страница тикета не перерисовывалась после
delete/update. Фикс: `fastcgi_param HTTPS on;` в `/etc/nginx/RT-fcgi` (бэкап `.bak-20260723-https`).
2) **Port в генерируемых URL.** После п.1 RT клеил `:80` из `SERVER_PORT $server_port`=80 →
`https://rt.etersoft.ru:80` ≠ origin `:443` → та же `SecurityError` по порту. Фикс:
`Set($WebBaseURL,'https://rt.etersoft.ru')` в `/etc/rt/RT_SiteConfig.pm` (бэкап
`.bak-20260723-webbaseurl`) — канонический scheme+host+port для генерируемых URL. Reload
воркеров: `systemctl restart rt-fcgi@{1..4}.service` (RT_SiteConfig читается на старте воркера).
3) **Port в CSRF/whitelist-проверке.** Баннер «inline edit does not work under the current
domain». `IsRefererCSRFWhitelisted(GetWebURLFromRequest())` берёт URL запроса **напрямую из
`SERVER_PORT`** (не из `$WebBaseURL`) → видел `rt.etersoft.ru:80`, не попадал в
`@ReferrerWhitelist` (`rt.etersoft.ru:443`). Фикс: `fastcgi_param SERVER_PORT 443;` в `RT-fcgi`
(бэкап `.bak-20260723-serverport`). Применяется `nginx -t && serv reload nginx` (fastcgi_param
шлётся per-request — воркеры рестартить НЕ надо).
⚠️ Смена SERVER_PORT 80→443 переименовывает сессионную куку `RT_SID_rt.etersoft.ru.80``.443`
всех разлогинит ОДИН раз (перелогин). Итог: `:80`/`http://` в странице = 0, кука `secure`,
баннера нет. Урок: scheme/port бэкенда утекают во ВСЕ места вывода self-URL — чинить каждое
(scheme→HTTPS-env, порт→SERVER_PORT, каноника→$WebBaseURL). См. [docs/nginx.md](../docs/nginx.md).
- **ExternalStorage:** вложения `/var/local/rt-data/attachments` (apache:apache, 775), rsync'нуты с CT 233.
- **Медленные тикеты (id=70334) починены:** причина = cold buffer pool + устаревшая статистика после bulk-import.
Лекарство: `mariadb-check --analyze eter_rt` + прогрев buffer pool 4G.
- **Тот же root cause подтверждён на rt-test (RT 6.0.3, MariaDB 12.3.2, 2026-07-19):** вис htmx-виджета
истории `/Helpers/TicketHistoryPage?id=...` (EXPLAIN показал BNL full-scan Attachments 194225 строк).
`mariadb-check --analyze eter_rt` → helper 15с+ → 0.024с. См. [[lesson_rt_stale_stats_analyze]].
**Включить `mariadb-check --analyze eter_rt` в cutover 4.4→6.0 обязательно**, до отдачи трафика.
- **rt-setup-database 4.4.4→4.4.7** (`--dba-password`, `--upgrade-from/to`, `y` на Proceed).
Грабля: MariaDB11 ломает создание индекса `` `objectcustomfields`1 `` → создать вручную
`CREATE INDEX objectcustomfields1 ON ObjectCustomFields(ObjectId)`.
- `rt.log` был root:root (создан rt-setup-database) → 502 "Cannot write" → `chown apache:apache /var/log/rt/rt.log`.
- ⚠️ **Таблица `Scrips` в RT 4.4 НЕ имеет колонки `Queue`** (убрана; per-queue scrip'ы — в `ObjectScrips`,
ObjectId=queue). Не искать `Scrips.Queue`. RT-API `$scrips->Limit(FIELD=>'Queue')` падает.
### Autoreply по очередям (scrip On Create → Autoreply To Requestors)
Привязан (через ObjectScrips) к очередям: **SUPPORT(1), SELTA(8), SCHOOL(16), SERVICE(18), WEBMASTER(19)**.
Каждой — свой шаблон `Автоответ.<QUEUE>` (Type=Perl, generic: использует `{$Ticket->QueueObj->CorrespondAddress()}`).
**BOSS(9) autoreply добавлен 2026-07-16:** template `Автоответ.BOSS` (id=40) + scrip id=42.
Добавлять через `RT::Scrip->Create(Queue=>N, ScripCondition=>'On Create', ScripAction=>'Autoreply To Requestors', Template=>'Автоответ.X')`
(имена/описания удобно выводить из существующей записи заменой SUPPORT→NEW, без кириллицы в скрипте).
### Отправка почты (postfix на CT 412, = прод-конфиг с CT 233)
- RT: `MailCommand=sendmailpipe``/usr/sbin/sendmail` (postfix). `relayhost=[mail.etersoft.ru]:587`.
- canonical: `apache→noreply@etersoft.ru`, `rt→noreply@etersoft.ru`. transport: `etersoft.ru smtp:[87.249.47.42]`
(фактически не срабатывает — transport.db битый из-за man-page в файле — всё идёт через relayhost, и это работает).
- **Логи postfix → journald** (`journalctl | grep postfix`), НЕ /var/log/mail/all.
- Забор: fetchmail каждые 3 мин (`/etc/cron.d/rt-fetchmail`), 9 POP3-ящиков @office.etersoft.ru,
mda `rt-mailgate --url http://localhost --queue <Q> --action correspond`. Пароли в `/etc/rt/fetchmailrc`.
- ⚠️ **Грабля cutover (~9.5 ч простоя забора 2026-07-23):** rt-fetchmail создан в `/etc/cron.d`, но
давно работающий crond в LXC **не перечитал каталог** (inotify-провал на overlay) → забор встал.
Лекарство: `serv restart crond`. **После настройки системы перезапускать контейнер и перепроверять
автозапуск** — см. [[lesson_restart_container_verify_autostart]]. `/etc/cron.d` режим `0700` — пакетный
(vixie-cron), НЕ менять.
- Тест забора/autoreply — см. [[lesson-rt-mail-pickup-test]]: слать на `name@etersoft.ru`,
автопроверка в логах mail.etersoft.ru по `from=<noreply@etersoft.ru>`.
### Тестовые тикеты 2026-07-16 — удалены
#70337 (BOSS), #70338 (SUPPORT), #70339 (BOSS) — проверки забора/autoreply после миграции; переведены в status=deleted (SetStatus('deleted') под страховкой subject=~^TEST).
---
## ⏳ НИЖЕ — исторически по CT 233 (снят с прода 2026-07-16)
## Архитектура
- **ОС: ALT p9 (VERSION_ID=9, EOL), 32-bit i686.** RT 4.4.4 (`rt-4.4.4-alt1_2`). В ALT p11/Sisyphus RT только **4.4.5** (апстрим уже 6.0.x, 4.4.x — EOL, CVE непатчены: ALT bug 49420). План обновления: [plans/rt-update.md](plans/rt-update.md).
- Веб: **nginx****rt-fcgi@1..4.service** (systemd, socket-activated, unix-сокеты `/run/rt[1-4].socket`, upstream `backend`). Контейнер слушает **только :80** (HTTPS нет — фронтенд терминирует). `StandardInput=socket`.
- **Фронтенд (TLS): azbykar.etersoft.ru = CT 102 (10.20.30.23, hostname `eterhost`).** `ssh -p32 root@10.20.30.23` (хост-ключ менялся — миграция). Конфиг `/etc/nginx/sites-enabled.d/rt.etersoft.ru.conf`: `listen 443 ssl http2` + `proxy_pass http://10.20.30.53:80`, `include include/trans-proxy.conf` (proxy_buffering on, proxy_buffers 50 8k). Отдельные логи `/var/log/nginx/rt_access.log`+`rt_error.log` (формат `logdetail`). **Важно: в логах rt контейнер видит клиентом `10.20.30.23` (azbykar), а не реального юзера.**
- Конфиг RT: `/etc/rt/RT_SiteConfig.pm` (mode 640 root:apache), `/etc/rt/RT_SiteConfig.d/` (пусто). DB: `eter_rt` на **mysql.auth.dmz.etersoft.ru** (User `eter_rt`).
- nginx: `/etc/nginx/sites-enabled.d/rt.etersoft.ru.conf`. `upstream backend` = 4 сокета.
## Тестовый контур rt.pr (p11, 2026-07-13) — Этап 1 обновления
- **CT `rt-test`** на ноде **gefest** (НЕ enceladus), IP **192.168.0.191** (vmbr0 office LAN — ⚠️ тест-серверы должны быть на 10.20.30.x, [feedback_test_servers_on_dmz](feedback_test_servers_on_dmz.md); оставлено как есть). ALT **p11 x86_64**, RT **4.4.5-alt1_4**, MariaDB **11.8.8** локально (дамп боевой `eter_rt`).
- Доступ: **http://rt.pr.etersoft.ru/** (внутренняя зона; root, temp-pass `Rt-test-2026` — тестовая копия БД). nginx webroot `/usr/share/rt/html`, override `/etc/rt/RT_SiteConfig.d/local.pm`.
- 🔴 **Блокер для p11:** [lesson_rt_condition_perl538](lesson_rt_condition_perl538.md)`RT/Condition.pm:201` без `;` = fatal на Perl 5.38, падают все scrips. На тесте запатчено. Для прода — пересборка ALT-пакета.
- После миграции БД обязательно: `mariadb-check --analyze eter_rt` + `innodb_buffer_pool_size=2G` (иначе поиск/просмотр висят). `rt` не тянет `perl-Plack-FCGI`/`perl-FCGI`. Детали: [plans/rt-update.md](plans/rt-update.md).
## ⚠️ Известная причина 500 при ответах (2026-07-06): диск azbykar полон
- **Корневой диск azbykar (CT 102) всего 8 ГБ**. При ответе с цитированием тело POST > in-memory буфера → nginx пишет temp в `/var/spool/nginx/tmp/client` (на корне). **Диск 100% полон → temp не создаётся → 500 Internal Server Error (страница `nginx/1.28.1`).** POST не доходит до rt (в access-логе rt его нет); GET'ы (форма ответа) маленькие — работают.
- Забивали диск: `/var/log/nginx/gitlab.eterhost.ru-access.log` (~5.8 ГБ активный) + ротированные `.1`/`.xz`, `/var/log/fail2ban.log`. Логи azbykar `rt_access/rt_error` при этом «не пишутся с Jul 5» (nginx держит fd после ротации — слепая зона).
- **Починено 2026-07-06:** удалены ротированные логи gitlab/fail2ban + `journalctl --vacuum-size=100M`. Затем **диск расширен 8→16 ГБ** (CT 102 rootfs `pool0/subvol-102-disk-0`, ZFS на enceladus). И **настроена ротация gitlab-access.log по размеру** (`maxsize 200M`, см. ниже) — пик на диске ~1 ГБ вместо 5.8 ГБ. Текущее состояние: ~3 ГБ занято / ~14 ГБ свободно.
## Ротация логов на azbykar (настроено 2026-07-06)
- `/etc/logrotate.d/nginx`: станза `/var/log/nginx/*log` с **`maxsize 200M`** + `weekly rotate 7 compress delaycompress`, postrotate `nginx -s reopen`. `gitlab.eterhost.ru-access.log` растёт **~1 ГБ/день** → ротируется фактически ежедневно (проверка в cron.daily 04:11), пик ~1 ГБ. Мелкие логи как и прежде по неделям.
- ⚠️ **Бэкап станзы НЕ класть в `/etc/logrotate.d/`** — logrotate читает там ВСЕ файлы → дубль-станза → `duplicate log entry` → реальный cron падает с `EXIT=1` и не пишет state. Бэкап лежит в `/root/nginx.logrotate.bak.20260706`.
- ⚠️ **State-файл logrotate на azbykar = `/var/lib/status`** (дефолт ALT-сборки `logrotate-3.20.1-alt2`), **НЕ `/var/lib/logrotate/status`** (последний — мёртвый остаток 2023, игнорируется). Смотреть реальный state: `grep nginx /var/lib/status`.
- Шум в gitlab-access (можно резать на уровне nginx, если надо меньше лога): `/assets/icons-*.svg` ~11% запросов, опрос `gitlab-runner` `/api/v4/jobs/request`, краулер ClaudeBot.
- Предсуществующее (не чинил — чужая подсистема): станза `clamav` падает каждый прогон (`/var/log/clamav/{clamd,freshclam}.log` отсутствуют, нет `missingok`) → logrotate ежедневно `EXIT=1`. **nginx-ротацию не блокирует**, но маскирует ошибки. Фикс — добавить `missingok` в `/etc/logrotate.d/clamav`.
## Логирование (починено 2026-07-06)
- RT пишет в **`/var/log/rt/rt.log`** (`$LogToFile='notice'`, `$LogDir=/var/log/rt`, `$LogToFileNamed=rt.log`). **Важно:** файл пишется ТОЛЬКО при событиях уровня notice+ (warning/error). Если лог пустой/молчит — это нормально для спокойного RT, НЕ значит что лог сломан. Проверить: `su -s /bin/sh apache -c "cd /usr/share/rt && perl -MRT -e 'RT::LoadConfig();RT::Init();\$RT::Logger->warning(q{test});'"`.
- **Раньше stderr уходил в `/dev/null`** (systemd `StandardOutput=null`, `StandardError` наследовал). Добавлен drop-in `/etc/systemd/system/rt-fcgi@.service.d/logging.conf``StandardError=journal`. Теперь Perl `die`/`warn` видны: `journalctl -u 'rt-fcgi@*'`. (Менять `StandardOutput` НЕ надо — FCGI говорит по сокету fd 0, но безопаснее оставить null.)
- **logrotate** `/etc/logrotate.d/rt` теперь крутит и rt.log, и fetchmail_rt.log (`copytruncate`, без postreload). Раньше rt.log вообще не ротировался → дорос до 1.5 ГБ (sparse).
- fetchmail пишет отдельно в `/var/log/rt/fetchmail_rt.log` (очень многословный).
## Лимиты
- RT: `MaxAttachmentSize=20000000` (20 МБ). nginx: `client_max_body_size 20m` (совпадает).
- **В server-блоке nginx для rt НЕ задан `fastcgi_read_timeout`** → дефолт **60s**. Тяжёлые ответы/входящая почта могут превышать 60s → nginx «upstream timed out» → 502. Кандидат на увеличение (напр. 300s) + `fastcgi_buffers`.
- systemd rt-fcgi: все лимиты infinity (MemoryMax/Limit/tasks). `Restart=always`.
## Ресурсы
- RAM: **3 ГБ** (поднят 2026-07-06 с 2 ГБ; контейнер LXC применяет memory при stop/start, `pct reboot` тоже сработал). 1 vCPU. Диск: rootfs `pool0/subvol-233-disk-0` (ZFS на enceladus), 240+ ГБ свободно.
- **loadavg в контейнере показывает нагрузку ХОСТА enceladus** (loadavg не изолирован в этом LXC) — `top` даёт реальную картину (обычно idle). Не пугаться «load 7».
## Известная проблема: периодические 5xx при работе с RT
- В nginx `rt-error.log`: `upstream prematurely closed connection` / `Connection reset by peer` / `upstream timed out` — rt-server.fcgi воркер **умирает посреди запроса**, `Restart=always` его поднимает (NRestarts копится). Бывает на разных тикетах/эндпоинтах, не только одном.
- До 2026-07-06 причина НЕ логировалась (stderr → /dev/null, а воркер умирал без Perl-level error → в rt.log ничего). Теперь ошибки ловятся в journal — при репорте 500 воспроизвести и смотреть `journalctl -u 'rt-fcgi@*'` + `tail /var/log/rt/rt.log` + nginx access/error.
## Быстрая диагностика при «internal server error» в RT
1. `journalctl -u 'rt-fcgi@*' --since '5 min ago'` — Perl-ошибка (с 2026-07-06).
2. `tail -50 /var/log/rt/rt.log` — RT-ошибки уровня error/warning.
3. `grep " 5" /var/log/nginx/rt-access.log*` и `tail /var/log/nginx/rt-error.log*` — конкретный запрос и сторона nginx.
4. К proxy `10.20.30.23` обращений нет — он фронтенд.
См. также [[reference-create-lxc-container-pve]], [[lesson-cyrus-sasl-restart]] (почта).
---
name: reference_rt_package_requires
description: "Пакет rt (ALT): состояние Requires runtime-условных Perl-модулей в spec 6.0.3-alt1. DateTime::Set, DateTime::Event::Recurrence, CSS::Inliner добавлены как жёсткие Requires. GraphViz2 ПОКА опционален (%filter_from_requires стр.8-9) но фильтр устарел (perl-GraphViz2 уже в Сизифе via #426095) и RT требует его жёстко (RT/Graph/Tickets.pm:61). Решение о Requires: perl(GraphViz2.pm) pending."
metadata:
type: reference
---
# Пакет `rt` (ALT): Requires runtime-условных модулей — состояние spec
RT 6.0 подключает часть Perl-модулей **runtime-условно** (`require`/`use` только в
кодовом пути фичи) → `make testdeps` и авто-detect `perl.req` их видят не всегда.
Spec: `/srv/lav/Projects/git-alt/00-perl/rt/rt.spec` (gear-чекаут на builder64).
Детали/симптомы: [[lesson_rt6_runtime_deps_missed]], план: [[plans/rt-6.0]].
## Что уже в spec 6.0.3-alt1 (girar #426033, test-only, собирается 2026-07-22)
Жёсткие `Requires:` добавлены (совпадает с нашими находками на rt-test):
```spec
Requires: perl(DateTime/Set.pm)
Requires: perl(DateTime/Event/Recurrence.pm)
Requires: perl(CSS/Inliner.pm) >= 4027
```
Модули в Сизифе: DateTime/Set + Event/Recurrence — давно; CSS/Inliner + GraphViz2 —
опубликованы girar **#426095** (Kanban git-alt #297). ✅ Деплой `epm install rt`
подтянет эти три автоматически.
## ⚠️ GraphViz2 — опционален в spec, но RT требует его жёстко (open decision)
Source RT 6.0.3 (локально `rt/lib/`):
```
rt/lib/RT/Graph/Tickets.pm:61 require GraphViz2; # ГОЛЫЙ require, без eval → без модуля падает рендер графа
rt/lib/RT/Graph/Tickets.pm:62 GraphViz2->import;
rt/lib/RT/Graph/Tickets.pm:316 $args{'Graph'} = GraphViz2->new(...
rt/lib/RT/Config.pm:874 return if RT::StaticUtil::RequireModule("GraphViz2"); # мягкая проверка (eval)
```
→ для RT в целом модуль опционален, но **фича графов связей тикетов без него ломается**
(ровно как видели на rt-test).
Spec **оставил GraphViz2 опциональным** через фильтр:
```spec
7: # GraphViz/GraphViz2 is an optional dependency (ticket graphs), not a hard requirement
8: %filter_from_requires /^perl(GraphViz.pm)/d # v1 — оставить (нужна только testsuite, см. стр.441-442)
9: %filter_from_requires /^perl(GraphViz2)/d # ← УСТАРЕЛ
```
Changelog (стр.784-785): фильтр добавлен т.к. *«4.4.7 switched graph rendering to
GraphViz2, **not packaged in Sisyphus**»*. Это обоснование **исчезло** — perl-GraphViz2
теперь в Сизифе (#426095). Без правки в #426033 GraphViz2 останется необязательным →
на чистой `epm install rt` графы тикетов не рендерятся.
### Правка (pending решение мейнтейнера)
- убрать **строку 9** (`%filter_from_requires /^perl(GraphViz2)/d`);
- строку **8** (v1) оставить — v1 нужна только testsuite (стр.441 «testsuite unconditionally
depends upon perl(GraphViz)»), runtime-Requires у неё быть не должно;
- обновить комментарий стр. 7;
- добавить `Requires: perl(GraphViz2.pm)`.
- **graphviz/dot доб. НЕ надо**: `perl-GraphViz2` сам `Depends: graphviz` (проверено:
`apt-cache depends perl-GraphViz2` → graphviz).
## Что НЕ трогать (уже в rpm --requires rt)
`perl(DateTime.pm)`, `perl(DateTime/TimeZone.pm)`,
`perl(DateTime/Format/Natural.pm) >= 0.670`, `perl(DateTime/Locale.pm) >= 0.40`,
`perl(Calendar/Simple.pm)`, `perl(Data/ICal.pm)`, `perl(Data/ICal/Entry/Event.pm)`,
`perl(CSS/Squish.pm)`, `perl(CSS/Minifier/XS.pm)`, `perl(HTML/Gumbo.pm)`.
BuildRequires `perl(GraphViz.pm)` (v1, для testsuite) — отдельно от runtime.
Связанные: [[lesson_rt6_runtime_deps_missed]], [[plans/rt-6.0]],
[[reference_rt_etersoft_ru]], [[lesson_rt6_secure_cookies_http]].
...@@ -7,7 +7,7 @@ metadata: ...@@ -7,7 +7,7 @@ metadata:
originSessionId: 14186ced-b55d-4e55-aa31-d3b9dee9d8f9 originSessionId: 14186ced-b55d-4e55-aa31-d3b9dee9d8f9
--- ---
Бэкенды chat.eterfund.ru (telemt MTProxy): **e5/.105** (91.232.225.105, ALT p11, хост `telemt`, осн. chat.eterfund.ru), **d.chat**=divserver (46.148.53.122, ssh root@div.eterhost.ru), **s.chat**=schat (139.100.207.140, selectel, Ubuntu 24.04), **b.chat**=beget (217.12.37.55, Ubuntu 20.04). Связано: [[lesson_chat_eterfund_telemt_oom]], [[lesson_telemt_middle_proxy_egress_match]], [[lesson_divserver_telemt_mtproxy]]. Бэкенды chat.eterfund.ru (telemt MTProxy): **e5/.105** (91.232.225.105, ALT Sisyphus, хост `telemt`, осн. chat.eterfund.ru; ssh `root@91.232.225.105` — host key был не в known_hosts, accept-new), **d.chat**=divserver (46.148.53.122, ssh root@div.eterhost.ru), **s.chat**=schat (139.100.207.140, selectel, Ubuntu 24.04), **b.chat**=beget (217.12.37.55, Ubuntu 20.04). Связано: [[lesson_chat_eterfund_telemt_oom]], [[lesson_telemt_middle_proxy_egress_match]], [[lesson_divserver_telemt_mtproxy]].
## Обновление telemt (3.x.x → 3.4.18), всё через epm (НЕ обходить — [[feedback_never_bypass_epm_play]]) ## Обновление telemt (3.x.x → 3.4.18), всё через epm (НЕ обходить — [[feedback_never_bypass_epm_play]])
- Ставится через `epm play` (deb `…-epm1.repacked`), `/usr/bin/telemt` = wrapper → реальный бинарь `/opt/telemt/telemt`. - Ставится через `epm play` (deb `…-epm1.repacked`), `/usr/bin/telemt` = wrapper → реальный бинарь `/opt/telemt/telemt`.
...@@ -26,8 +26,20 @@ metadata: ...@@ -26,8 +26,20 @@ metadata:
На каждом бэкенде: `/etc/telegraf/scrape-telemt-ja4.py` (python: curl localhost:9091 → агрегат по JA4 → influx line protocol) + `/etc/telegraf/telegraf.d/telemt-ja4.conf` (`[[inputs.exec]]` interval 60s). telegraf уже шлёт в **telegraf.office.etersoft.ru:8086, db `gateways`**, host-тег = имя бэкенда. На каждом бэкенде: `/etc/telegraf/scrape-telemt-ja4.py` (python: curl localhost:9091 → агрегат по JA4 → influx line protocol) + `/etc/telegraf/telegraf.d/telemt-ja4.conf` (`[[inputs.exec]]` interval 60s). telegraf уже шлёт в **telegraf.office.etersoft.ru:8086, db `gateways`**, host-тег = имя бэкенда.
- Измерения: **`telemt_beobachten`** (host-сводка: total_auth, total_probe, real_client_ja4, distinct_ja4), **`telemt_ja4`** (теги host,ja4; поля total, auth_success, bad_or_probe). - Измерения: **`telemt_beobachten`** (host-сводка: total_auth, total_probe, real_client_ja4, distinct_ja4), **`telemt_ja4`** (теги host,ja4; поля total, auth_success, bad_or_probe).
- Развёрнуто на ВСЕХ 4 бэкендах (2026-06-22): beget, schat, divserver, **e5/.105** (host-тег в InfluxDB: beget/schat/divserver/**telemt**). - Развёрнуто на ВСЕХ 4 бэкендах (2026-06-22): beget, schat, divserver, **e5/.105** (host-тег в InfluxDB: beget/schat/divserver/**telemt**).
- **e5 — ALT Sisyphus** (не p11!), telemt ALT-нативный пакет (НЕ epm-play): обновлять из girar-задания — `apt-repo add <task>``apt-get update``epm install telemt``apt-repo rm <task>`. Версия 3.4.18-alt1 уже в Sisyphus, задание #422377 — тест для p11. Юнит ALT-пакета: User=telemt → нужен CAP_NET_ADMIN drop-in. - **e5 — ALT Sisyphus** (не p11!), telemt ALT-нативный пакет (НЕ epm-play): обновлять из girar-задания ОДНОЙ командой **`epm install <task_id>`** (epm через `is_taskarg`/`epm_install_alt_tasks` сам добавляет task-репо во **ВРЕМЕННЫЙ** apt-config, делает update, ставит, чистит — реальный `sources.list` вообще не трогается; `--full` = ставить даже -devel/-debuginfo, по умолчанию они excluded). **НЕ лепить `apt-repo add task` руками** — см. [[feedback_epm_install_girar_task]]. Вывод apt внутри epm может врать «Последняя версия уже установлена / 0 обновлено» — верить `rpm -q telemt`. Текущая версия: **3.4.22-alt1** (обновлено 2026-07-05 через task #424260 [test-only]; до этого было 3.4.18-alt1 из p11 task #422377). Юнит ALT-пакета: DynamicUser + `User=telemt` → нужен CAP_NET_ADMIN drop-in `/etc/systemd/system/telemt.service.d/caps.conf` (`AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN` + `CapabilityBoundingSet=...`) — на e5 уже есть. Проверка после апгрейда: `rpm -q telemt``serv restart telemt``curl -sk --resolve chat.eterfund.ru:443:91.232.225.105 https://chat.eterfund.ru/` (HTTP 200), `curl 127.0.0.1:9091/v1/runtime/tls-fingerprints?limit=1` (HTTP 200). WARN «mismatched certificate metadata file=…json» при старте = stale TLS-cache от прошлой версии (benign, сервится cert всё равно). Бэкап перед апгрейдом: `cp /usr/bin/telemt /etc/telemt/config.toml ~/tmp/claude/telemt-backup-<ver>/`.
- Картина по эндпоинтам (2026-06-22): **beget/divserver обслуживают живых клиентов** (auth сотни/десятки, разные JA4); **e5 (основной chat) — частично** (auth ~47, но probe ~988, тяжёлое зондирование); **schat — сплошь probe** (auth=0, 1000 probe) → блокировка РАЗНАЯ по эндпоинтам, beobachten это показывает в реальном времени. - **s.chat (Ubuntu 24.04) → 3.4.24** (2026-07-18, girar task #425671 [test-only] sisyphus telemt.git=3.4.24-alt1): на Ubuntu sisyphus-task НЕ применим (rpm/apt-rpm), ставим через **`epm play telemt=3.4.24`** — epm play умеет явную версию `NAME=VER`; `epm play --latest`/app-versions отстаёт (там 3.4.22, а 3.4.24 уже в girar). Процедура: бэкап `/usr/bin/telemt` (41B wrapper) + **`/opt/telemt/telemt`** (реальный бинарь) + `/etc/telemt/config.toml` в `~/tmp/claude/telemt-backup-pre-3.4.24/``epm ei` (3.64.63→3.64.66, от граблей repack [[reference_telemt_fleet_ja4_monitoring]]) → `epm play --remove telemt``epm play telemt=3.4.24``serv restart telemt`. Проверка: beobachten `curl 127.0.0.1:9091`/`:9090` → HTTP 200, слушает `0.0.0.0:443`. **Новый WARN в 3.4.24:** `Unknown config key timeouts.tg_connect, suggestion=tg_connect` — схема конфига изменилась, ключ надо мигрировать `timeouts.tg_connect``tg_connect` (пока игнор, таймаут = дефолт; конфиг НЕ правлю без подтверждения). WARN `mismatched certificate metadata file=chatgpt.com.json` — benign (stale TLS-cache). Был 3.4.18 → 3.4.24.
- **s.chat: synfix + диагноз блокировки (2026-07-18):** жалоба «клиент не подключается к s.chat». Применил **synfix с e5 1:1** (nftables table `mtpr_synfix`: iOS по TCP-fingerprint accept / прочие SYN 54/мин / reject host-unreachable + sysctl `99-telemt.conf` fastopen=3 somaxconn=65535 syn_backlog=65535 keepalive 45/15/3 + `listen_backlog` 4096→65535 + сервис `mtpr-nft-synfix.service`, файлы в `~/tmp/claude/telemt-synfix/`). **Клиенту НЕ помогло:** beobachten auth=0 хронически (с 2026-06), только DPI-зонды (30 шт, 1 JA4 `t13d1516h2_…`). **Root cause: IP `139.100.207.140` жёстко заблокирован ТСПУ** — DPI режет глубже SYN (ClientHello/payload), synfix отбивает лишь зонды (698 reject). telemt/конфиг ПОЛНОСТЬЮ исправны: `[censorship]` побайтово идентичен рабочему d.chat, **gre3→egw2→.27 работает** (`curl --interface 10.42.0.2 ipify` = 178.105.209.27 = registered для ad_tag; egress-mismatch ОПРОВЕРГНУТ — [[lesson_telemt_middle_proxy_egress_match]] не про этот случай), direct upstream 506/506 ok, ME pool пуст (`no live writers for DC groups [-3,-2,1,2,3]`) но direct-fallback работает. `chat.eterfund.ru` → только e5 (.105), s.chat раздаётся отдельно по `s.chat.eterfund.ru`→.140. **d.chat** (46.148.53.122 / egress 93.120.163.2 через ipsec1, ad_tag `a248912d…`, `middle_proxy_nat_ip` НЕ задан) и **b.chat** работают (другие IP, не в блоке). Варианты по s.chat: сменить IP / VLESS+Reality / вывести из клиентского доступа. **b.chat: nftables установлен (`epm install nftables`, v0.9.3), synfix отложен** — хост работает, применить схему можно по тем же файлам. **tg_connect в 3.4.24** перенесён `[timeouts]``[general]` (WARN `Unknown config key timeouts.tg_connect, suggestion=tg_connect`).
- Картина по эндпоинтам (2026-06-22, обновлено 2026-07-18): **beget/divserver обслуживают живых клиентов** (auth сотни/десятки, разные JA4); **e5 (основной chat) — частично** (auth ~47, но probe ~988, тяжёлое зондирование); **schat — сплошь probe** (auth=0 хронически с 2026-06 = IP .140 заблокирован ТСПУ, не лечится synfix) → блокировка РАЗНАЯ по эндпоинтам/IP.
## SYN FIX (nftables) на e5/.105 (2026-07-09)
Применён фикс из MTPROTO_FIX_By_MEKO (https://github.com/Mekotofeuka/MTPROTO_FIX_By_MEKO, v1.67) — решает проблему с 4 июня 2025, когда ТСПУ режет TCP SYN к MTProto прокси. Суть: двухуровневая фильтрация SYN на порту 443.
- **Скрипт**: `/opt/mtpr-synfix-nft.sh` (nftables, т.к. xt_u32 модуль отсутствует в ядре ALT 6.12.74)
- **Сервис**: `mtpr-nft-synfix.service` (enabled, oneshot RemainAfterExit)
- **Логика**: 1) iOS по TCP fingerprint (@th matching) → ACCEPT без лимита; 2) остальные → hashlimit 54/minute burst 1; 3) превысившие → reject icmp host-unreachable (не DROP!)
- **Статистика** (~2 мин): 173 iOS accept, 1604 rate-limited, 3621 rejected
- **sysctl** (`/etc/sysctl.d/99-telemt.conf`): tcp_fastopen=3, somaxconn=65535, syn_backlog=65535, keepalive 45/15/3
- **listen_backlog** в config.toml увеличен до 65535
- **OpenSSL** 3.5.4 установлен (для SelfSteal SNI проверки)
## Блокировка ТСПУ (2026, из разведки + beobachten) ## Блокировка ТСПУ (2026, из разведки + beobachten)
DPI палит по триаде **JA4 Telegram + один SNI + много ClientHello на один ip:port**. JA4 формирует КЛИЕНТ (мобильный TG с непатченным fingerprint — фиксы только Desktop). Лечится: рассредоточение ip:port, «одобренный» SNI (напр. rusk.ru=91.232.225.80, наш реальный сайт), смена протокола (VLESS+Reality). Конфиг маски: один SNI на инстанс telemt (`tls_domain`); фронтить реальный домен через `mask_host`. DPI палит по триаде **JA4 Telegram + один SNI + много ClientHello на один ip:port**. JA4 формирует КЛИЕНТ (мобильный TG с непатченным fingerprint — фиксы только Desktop). Лечится: рассредоточение ip:port, «одобренный» SNI (напр. rusk.ru=91.232.225.80, наш реальный сайт), смена протокола (VLESS+Reality). Конфиг маски: один SNI на инстанс telemt (`tls_domain`); фронтить реальный домен через `mask_host`.
---
name: reference_zclaude_glm_config
description: "Как устроен Claude Code + z.ai GLM на машине lav wrapper zclaude, env-z.conf, tiered routing моделей"
metadata:
node_type: memory
type: reference
originSessionId: 4c8c587c-964b-4433-9a95-0a7682fce282
---
Конфиг Claude Code под z.ai GLM на машине lav:
- **`/usr/local/bin/zclaude`** — shell-wrapper (362 б): source'ит `/etc/opt/claude.ai/env-z.conf` и `~/.claude/env-z.conf` (через `set -a` allexport), затем `exec /opt/claude.ai/claude "$@"`. Штатный `/usr/bin/claude` — отдельный wrapper, source'ит `env.conf` (без z), для др. провайдера.
- **`/etc/opt/claude.ai/env-z.conf`** (root:eterworkers, 640) — тут живут `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic`, `ANTHROPIC_DEFAULT_{HAIKU,SONNET,OPUS}_MODEL`, `CLAUDE_CODE_AUTO_COMPACT_WINDOW`, `API_TIMEOUT_MS`. Лежат 2 токена z.ai (один закомментирован). Пользовательского `~/.claude/env-z.conf` нет — всё из системного.
- Список моделей z.ai: GET `https://api.z.ai/api/anthropic/v1/models` (Bearer или x-api-key). 8 шт: glm-4.5/4.5-air/4.6/4.7/5/5-turbo/5.1/5.2. `[1m]` суффикс = 1M context window.
- Цены/доки: https://docs.z.ai/guides/overview/pricing ; https://docs.z.ai/devpack/faq (5h rolling-квота: Lite ~80 prompts/5h, Pro ~400, Max ~1600).
**Почему квота GLM тает «во много раз быстрее» Anthropic Opus** (подтверждено r/ZaiGLM + z.ai devpack):
1. В Coding Plan **cached tokens считаются как full usage** (у Anthropic cache hits не считаются против rate limit).
2. **glm-5.x имеет множитель 2–3×** (3× пик, 2× внепик) против glm-4.7.
**Tiered routing с 2026-07-06** (бэкап: `env-z.conf.bak.2026-07-06`): HAIKU=`glm-4.7`, SONNET=`glm-4.7`, OPUS=`glm-5.2` (без `[1m]`), `AUTO_COMPACT_WINDOW=200000`. Было: SONNET=OPUS=`glm-5.2[1m]`, compact=1M → квота горела. Менять только через sudo (tee сохраняет права); правки вступают только при новом запуске `zclaude`.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment