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

Переход с mihomo

Формат конфига у Citrus тот же, что у mihomo, и большинство конфигов запускаются без правок. Ниже — то, что всё-таки может потребовать изменений, и поведение, которое стало строже. Проверить конфиг можно до перезапуска:

citrus -t -f config.yaml

Для тех, кто встраивает ядро как библиотеку: путь Go-модуля остался github.com/metacubex/mihomo, подключайте Citrus через replace. Сам Citrus тоже подменяет через replace модули github.com/metacubex/utls, github.com/metacubex/tls и github.com/metacubex/http на свои форки; эти строки нужно перенести в go.mod встраивающего проекта. Модулю нужен Go 1.26 или новее.

VMess (вместе с mKCP, mekya и TLSMirror), ShadowsocksR, Hysteria v1, TUIC v4, потоковые шифры Shadowsocks и v2ray-plugin удалены. Замены — в таблице совместимости.

  • Основной конфиг: такой прокси или листенер — ошибка с понятным текстом, например «VMess was removed from Citrus, use VLESS» или «TUIC v4 is no longer supported, use uuid/password (v5)». Конфиг не загрузится.
  • Провайдеры прокси: узел с удалённым протоколом пропускается с предупреждением, остальная подписка загружается.
  • Ссылки: vmess://, ssr://, hysteria:// и ссылки TUIC v4 конвертер пропускает.
  • Опции: network: mkcp, kcp, mekya и tlsmirror-opts теперь ошибка; раньше VLESS и Trojan молча откатывались на TCP. Правило IN-TYPE,VMESS больше не разбирается, верхнеуровневого vmess-config нет.
  • Группа relay удалена ещё в mihomo; её ошибка теперь предлагает группу type: chain, см. Цепочки.

Подробности — на странице Безопасность контроллера.

  • Сетевой адрес требует secret. external-controller и external-controller-tls на не-loopback адресе (0.0.0.0:9090, :9090, адрес в локальной сети) без secret не запускаются: в логе ошибка, остальное ядро работает. Без secret можно на loopback, на external-controller-unix и external-controller-pipe, и на TLS с проверкой клиентского сертификата. Отключить проверку: external-controller-allow-insecure: true (или -ext-ctl-allow-insecure, CLASH_OVERRIDE_EXTERNAL_CONTROLLER_ALLOW_INSECURE=true). Это касается Docker-образов, которые публикуют порт контроллера.
  • Защита от браузера без secret. tcp/tls-контроллер без secret отвечает только на Host в виде IP или localhost и отклоняет запросы с чужим Origin. Дашборду на внешнем сайте нужен secret или его источник в external-controller-cors.allow-origins ("*" не считается). Скрипты и панели, которые не шлют Origin, это не затрагивает.
  • PUT /configs применяет настройки контроллера. Новый secret, адреса, TLS, CORS и external-ui вступают в силу сразу: старый токен получает 401, открытые вебсокеты закрываются. Конфиг, чей контроллер был бы отклонён, получает 400, и ничего не применяется.
  • Unix-сокет и именованный канал доступны только владельцу (сокет 0600, канал — пользователь ядра, SYSTEM и администраторы). Если GUI работает от другого пользователя, задайте CITRUS_UNIX_SOCKET_MODE=0660 с общей группой или свой LISTEN_NAMEDPIPE_SDDL. Если secret задан, он нужен и на сокете, и на канале.
  • Лимиты соединений контроллера: 10 секунд на заголовки (и TLS), 2 минуты простоя keep-alive, 256 КиБ заголовков.
  • GET /configs скрывает секреты TUIC и Shadowsocks (******). Читайте их из файла конфига.
  • /debug (pprof) требует secret, как и остальной API.
  • Без force=true политика доступа тоже применяется. PUT /configs и systemctl reload citrus применяют allow-lan, bind-address, skip-auth-prefixes, lan-allowed-ips, lan-disallowed-ips, встроенные ss-config и tuic-server при любой перезагрузке. force теперь решает только, переоткрываются ли встроенные порты (port, socks-port, mixed-port и другие) на значениях из файла.
  • Смена учётных данных закрывает сессии. Если перезагрузка убрала пользователя из authentication или сменила ему пароль, его открытые сессии на HTTP, SOCKS и mixed закрываются. Включение authentication закрывает сессии, пущенные без него.
  • Пустые имена пользователей запрещены: authentication: [":pass"] и users с пустым username у socks, http и mixed — ошибка конфига.
  • external-ui не может указывать на корень конфигурации. Домашний каталог, каталог файла конфига, корень из SAFE_PATHS или их родитель не принимаются, даже с SKIP_SAFE_PATH_CHECK: обновление UI очищает этот каталог. Используйте подкаталог, например ./ui.
  • Символические ссылки. Путь, который через симлинк или junction уходит за пределы домашнего каталога и SAFE_PATHS, отклоняется. Например, ui -> /usr/share/metacubexd: добавьте цель ссылки в SAFE_PATHS или задайте SKIP_SAFE_PATH_CHECK=true. Файл провайдера, который был симлинком, при обновлении заменяется обычным файлом.
  • Права новых файлов: каталог конфигурации 0700, config.yaml 0600, файлы провайдеров 0600, cache.db 0600. Существующие файлы сохраняют свои права.
  • Редиректы в частные сети отклоняются. Публичный URL провайдера, rule-set, geodata или дашборда не может перенаправить на loopback, частный, link-local, CGNAT и подобные адреса. Если в конфиге сразу указан частный адрес, ограничения нет.
  • size-limit: 0 означает 64 МиБ, а не «без ограничения»; -1 снимает ограничение. Превышение лимита — ошибка обновления, предыдущая копия остаётся (раньше файл обрезался).
  • Пределы размера: провайдер — не больше 50 000 записей proxies и 10 000 построенных прокси; override-expr — не больше 65 536 результатов; логические правила — не глубже 32 уровней скобок. Обычные подписки в эти пределы укладываются.
  • TLS 1.2 минимум на всех TLS-входящих и на TLS контроллера.

  • HTTP-прокси авторизует каждый запрос. На keep-alive соединении запрос без учётных данных получает 407, с неверными — 403 и закрытие соединения. Без авторизации Proxy-Authorization больше не задаёт пользователя для IN-USER. Заголовок запроса — не больше 64 КиБ (431).

  • SOCKS5 UDP привязан к пользователю и порту. При включённой авторизации датаграммы принимаются только от клиента с открытым UDP ASSOCIATE и только с одного UDP-порта этой ассоциации. Если порт клиента сменился (перепривязка NAT), нужна новая ассоциация. UDP-сессии SOCKS5 теперь учитываются в лимитах и статистике пользователя.

  • Client ID в Snell больше не задаёт пользователя. Его выбирает любой клиент, знающий общий PSK, поэтому он игнорируется: правила IN-USER, лимиты и статистика по пользователям видят пользователя Snell, только если его проверил ShadowTLS v3 или JLS. У обычного Snell-входа пользователя нет.

  • Срок рукопожатия 15 секунд до авторизации: VLESS, Trojan, AnyTLS, Shadowsocks, Snell, HTTP, SOCKS, mixed и HTTP-транспорты. Клиент, который за это время не прислал заголовок, отключается; после авторизации срока нет.

  • Hysteria2 Realm за обратным прокси верит trusted-proxy-header только от адресов из trusted-proxies. Пустой список значит «не верить никогда»: перечислите прокси, например trusted-proxies: [loopback]. У VLESS и Trojan так было с самого появления trusted-proxy-header.

  • Маскировка Hysteria2 по HTTPS проверяет сертификат upstream. Для самоподписанного локального сайта задайте masquerade-skip-cert-verify: true, иначе ответ будет 502.

  • REALITY: max-time-difference в миллисекундах, как maxTimeDiff в Xray (раньше были микросекунды).

  • Одинаковый адрес на разных входящих или под разными пользователями получает отдельные UDP-сессии со своими правилами и лимитами.

  • Пользователи серверных входящих не попадают через DIRECT в сеть сервера. Это касается VLESS, Trojan, Shadowsocks, Snell, TUIC, Hysteria2, AnyTLS, Mieru, Sudoku, TrustTunnel и ShadowQUIC. Им недоступны loopback, частные, link-local, CGNAT и ULA адреса, metadata облака и адреса этого хоста, какие бы ни были правила. Если им нужен доступ к чему-то там, добавьте это в egress-guard.allow-ips или задайте egress-guard: false на listener. Клиентские входящие не затрагиваются. См. защиту исходящего трафика.

  • TUIC и TrustTunnel (HTTP/3): серверный zero-rtt по умолчанию выключен. zero-rtt: true явно включает ранние данные QUIC, допускающие повторную доставку. ShadowQUIC уже использует такой же выключенный по умолчанию параметр.

  • KCPTun: без key сохранён известный общий ключ для старых конфигов, но теперь выводится предупреждение. Задайте уникальный случайный ключ; max-sessions и max-streams ограничивают сессии сервера и потоки одной сессии (по умолчанию 256/256).

  • Sudoku fallback: максимум 256 relay на listener и 64 на источник (IPv4-адрес или IPv6 /64), простой в любом направлении ограничен 60 секундами; активная передача продлевает соединение. Формат handshake и существующие ключи Sudoku сохранены.

  • WebSocket контроллера: основной secret в ?token= по умолчанию больше не принимается. Панель должна получить одноразовый билет через POST /auth/ws-ticket с Bearer-заголовком, затем передать ?ticket= при подключении. Для старой панели временно включите external-controller-allow-legacy-websocket-token: true; это возвращает риск утечки основного секрета через URL. Описание API.

  • REALITY по умолчанию шлёт X25519MLKEM768. Для старых серверов задайте reality-opts.support-x25519mlkem768: false.
  • XHTTP: без reuse-settings соединения разделяются, как в Xray (кроме HTTP/1.1); h-max-request-times: "1" отключает это. При данных в заголовках или cookie без uplink-data-key клиент шлёт X-Data-N и x_data_N, как Xray; если смешиваете версии, задайте uplink-data-key явно с обеих сторон.
  • SSH без host-key по-прежнему подключается, но пишет в лог предупреждение и ключ сервера в формате authorized_keys, чтобы его можно было закрепить. skip-host-key-check: true явно разрешает любой ключ и убирает предупреждение.
  • OpenVPN без проверки имени сервера пишет предупреждение. Новые verify-x509-name, verify-x509-type и verify-server-name включают проверку.
  • Ссылки http(s):// и socks5:// больше не включают skip-cert-verify сами; только если в ссылке есть skip-cert-verify, allowInsecure или insecure со значением 1 или true.
  • tcp-concurrent набирает не больше 8 адресов на соединение и 4 одновременно, проигравшие попытки отменяются.
  • DIRECT подключается к тому адресу, который проверили IP-правила, а не резолвит имя заново.

/upgrade сверяет пакет с checksums.txt того же релиза. Релиз без этого файла или без строки для пакета не установится; сборки Citrus публикуют его всегда. version.txt проверяется, имена файлов внутри архива игнорируются, zip должен содержать ровно один файл.

Для сборки Citrus нужен Go 1.26 или новее. mihomo публикует ещё и сборки на старых версиях Go (файлы с окончанием -go120, -go122, -go123, -go124, -go125); у Citrus их нет.

  • Windows 7/8 и старые macOS: релизы для Windows и macOS собираются пропатченным тулчейном Go от MetaCubeX, который сохраняет работу на этих системах. Отдельно в Citrus они не тестируются.
  • Ядра Linux старше 3.2 не поддерживаются: Go 1.24 и новее требуют ядро 3.2.