Перейти к содержимому
НовостиGitHubСкачать

Безопасность контроллера

API контроллера (external-controller, external-controller-tls, external-controller-unix, external-controller-pipe) может менять конфиг, правила, пользователей входящих, перезапускать и обновлять ядро. Его единственный ключ — secret: все пути, кроме проверок здоровья, файлов /ui и пути DoH, требуют Authorization: Bearer <secret> (WebSocket из браузера использует одноразовый билет; основной секрет в ?token= требует явной опции совместимости).

Любому TCP-контроллеру нужен ключ, включая loopback. Обычный HTTP external-controller по умолчанию разрешён только на loopback: на удалённом адресе bearer-secret шёл бы открытым текстом. Отказ пишется в лог, а PUT /configs отвечает 400 и ничего не применяет.

Без secret работают:

  • external-controller-unix и external-controller-pipe — их защищают права файла и ACL канала;
  • external-controller-tls с tls.client-auth-type: require-and-verify (или с заданным tls.client-auth-cert): ключом служит сертификат клиента.

external-controller-allow-insecure: true (или -ext-ctl-allow-insecure, CLASH_OVERRIDE_EXTERNAL_CONTROLLER_ALLOW_INSECURE=true) — явный legacy opt-in для TCP без secret или удалённого plaintext HTTP с bearer. Для удалённого доступа используйте external-controller-tls.

TCP-контроллер без secret существует только в явном insecure-режиме либо с проверенным клиентским TLS-сертификатом. Для второго случая browser guard остаётся дополнительной защитой:

  • отвечает только на запросы, где Host — IP-адрес или localhost;
  • отклоняет запросы с заголовком Origin (все кросс-доменные запросы и вебсокеты из браузера), если источник не сам контроллер (встроенный /ui) и не указан явно в external-controller-cors.allow-origins; "*" не считается.

Программы (скрипты, панели, curl) не шлют Origin. Дашборду на внешнем сайте нужен secret или проверенный клиентский сертификат, а при необходимости — явный источник в allow-origins. Insecure-режим отключает эту защиту.

PUT /configs, SIGHUP (systemctl reload citrus) и перезапуск применяют настройки контроллера из нового конфига: secret, адреса, tls, external-controller-cors, external-ui. Если что-то из них изменилось, листенеры контроллера перезапускаются (ответ на PUT /configs уходит раньше), а открытые вебсокет-потоки (/logs, /traffic, /memory, /connections, /events) закрываются — ничто, разрешённое старым secret, не остаётся открытым. Переопределения действуют при каждой перезагрузке. Используйте -secret-file: литеральные -secret, -age-secret-key и -config отклоняются, если только -allow-secret-arguments явно не возвращает риск утечки через список процессов. Перезагрузка, не меняющая контроллер, его соединения не трогает.

GET /version отвечает так же, как mihomo, и с одним полем сверху:

{"meta": true, "version": "v1.0.0-beta.3", "core": "citrus"}

Клиенты mihomo лишнее поле пропускают. Приложение, которое работает с обоими ядрами, может назвать ядро по core, а если поля нет — считать его mihomo.

Каждый листенер контроллера обрывает клиента, который за 10 секунд не прислал заголовки запроса (или не закончил TLS-рукопожатие), закрывает простаивающие keep-alive соединения через 2 минуты и принимает не больше 256 КиБ заголовков. Потоки по времени не ограничены.

GET /connections как вебсокет принимает interval в миллисекундах от 1 до 3600000; на другое значение ответ 400.