Files
obsidian-vault/personal/tech/tirith-trust.md
T

7.0 KiB
Raw Blame History

Tirith — снятие блокировок (trust)

Дата: 2026-09-15 Статус: актуально Что это: tirith — сканер безопасности, который стоит между агентом и shell. Он блокирует команды по своим правилам (rule_id) и требует подтверждения.

Где что лежит

Что Путь
Бинарник ~/.hermes/bin/tirith (также ~/.hermes/hermes-whale/bin/tirith)
Allowlist (trust) ~/.config/tirith/trust.json
Threat DB ~/.local/share/tirith/tirith-threatdb.dat
Лог срабатываний ~/.local/share/tirith/log.jsonl
Последний триггер ~/.local/share/tirith/last_trigger.json

В конфиге Whale (hermes-whale/config.yaml, секция security):

security:
  tirith_enabled: true
  tirith_path: tirith          # бинарник резолвится из ~/.hermes/bin
  tirith_timeout: 5
  tirith_fail_open: true
  tirith_allowlist:            # ⚠️ МЁРТВЫЙ КЛЮЧ — не читается никем
  - mixed_script_in_label
  - homoglyph_in_path

Критично: security.tirith_allowlist в config.yaml не работает. Проверено grep по tools/tirith_security.py и tools/approval.py — ключ не читается. Реальное управление — только через tirith trust~/.config/tirith/trust.json. Ключ оставлен в конфиге как документация.

Как снять правило

TIRITH=~/.hermes/bin/tirith

# 1. Посмотреть, что сработало последним
$TIRITH trust last            # или: jq . ~/.local/share/tirith/last_trigger.json

# 2. Узнать, что делает правило
$TIRITH explain --rule <rule_id>

# 3. Добавить в trust (pattern + scope правила)
$TIRITH trust add <pattern> --rule <rule_id> --ttl 3650d

# 4. Проверить
$TIRITH trust list
$TIRITH check "<команда>"     # пустой вывод = проходит

Замечания по синтаксису:

  • tirith explain <rule>не работает, нужен флаг: tirith explain --rule <rule>
  • tirith trust add требует --rule, иначе правило не скоупится
  • --ttl 3650d = 10 лет (практически «навсегда»); в списке отображается как дата

Правило raw_ip_url — «IP вместо hostname»

Это ровно тот случай, о котором спрашивал Alex: URL с сырым IP вместо домена.

raw_ip_url — Raw IP address in URL  [hostname]
Severity: Medium — raw IP URLs bypass domain-based security controls
Description: URL uses a raw IPv4 or IPv6 address instead of a hostname.
             Loopback (127.x, ::1) exempt.
Example flagged: curl http://203.0.113.50/payload.sh
Example safe:    curl https://example-cli.dev/install.sh
False positives: Development/testing commonly use raw IPs.
                 Add trusted IPs to your policy allowlist.
Remediation: Replace IP with hostname, or verify IP belongs to trusted server.

Масштаб проблемы: в log.jsonl правило raw_ip_url сработало 2991 раз.

Частота по адресам (топ)

Срабатываний IP Чей
348 192.168.1.86 LAN
149 192.168.2.176 LAN (NAS)
127 192.168.1.14 LAN
56 90.189.160.148 внешний
36 192.168.1.75 LAN
14 91.207.28.205 внешний
10 192.168.2.1 LAN (роутер)

Что разрешено (2026-09-15)

Все локальные адреса из логов — доверенные, добавлены с --ttl 3650d:

192.168.2.176  192.168.1.86   192.168.1.14   192.168.1.75
192.168.1.8    192.168.1.15   192.168.2.1    192.168.2.197
192.168.2.17   192.168.2.20   172.17.0.1     172.16.3.5
172.16.3.4     10.99.1.2      127.0.0.1      127.0.0.0

Плюс ранее добавленные (не про IP):

confusable_text  non_ascii_path  homoglyph_in_path
mixed_script_in_label  non_ascii_hostname  confusable_domain

Проверка: tirith check "ssh admin@192.168.1.86" → пусто (проходит).

Внешние IP — НЕ разрешены (моё решение, не подтверждено Alex'ом)

В логах есть внешние адреса, они оставлены под блокировкой: 90.189.160.148 (56), 91.207.28.205 (14), 93.171.215.109 (8), 94.130.164.126, 91.207.28.2.

Решение принято мной (агент не стал разрешать чужие адреса без спроса). Alex'у задан вопрос — ответа не было. Т.е. это не подтверждённое решение, а консервативный дефолт. При следующем обращении — переспросить.

Если понадобится — добавлять каждой командой отдельно, осознанно.

Альтернатива: правило raw_ip_url можно снять целиком, добавив raw_ip_url как pattern (так сделано с confusable_text и остальными шестью). Тогда блокировка исчезнет для всех адресов, включая внешние. Не сделано сознательно — внешние адреса остаются под защитой.

Урок: где искать правило (моя ошибка в диагностике)

Изначально я сказал Alex'у «такого правила нет» — это было неверно. Искал в tools/approval.py (список DANGEROUS_PATTERNS, ~100 паттернов) и tools/tirith_security.py — в обоих пусто по IP.

Правило живёт в самом бинарнике tirith (Rust), а Python-модуль только вызывает его и получает вывод. То есть tirith_security.pyпотребитель, не источник. Искать надо в:

  1. ~/.local/share/tirith/last_trigger.json — что именно сработало (rule_ids)
  2. ~/.local/share/tirith/log.jsonl — вся история срабатываний
  3. tirith explain --rule <id> — описание правила
  4. tirith trust list — что уже доверено

Тот же принцип, что и с security.tirith_allowlist: в конфиге ключ есть, но система его не читает. Наличие ключа в конфиге ≠ рабочая настройка.

Связанное