Главное: CLOSE-WAIT — это не проблема тайм-аутов ядра
Состояние CLOSE-WAIT означает, что удалённая сторона уже прислала FIN, ядро передало приложению EOF, но локальный процесс ещё не вызвал close() для своего сокета. Поэтому увеличение tcp_keepalive_* само по себе накопление CLOSE-WAIT не исправляет: keepalive предназначен прежде всего для обнаружения пропавших соединений в состоянии ESTABLISHED.
Сначала нужно определить, какие именно соединения не закрываются и действительно ли их удерживает Xray.
Минимальная диагностика
pid=$(pidof xray)
sudo ss -tanp state close-wait
sudo lsof -nP -a -p "$pid" -iTCP
sudo ls -l "/proc/$pid/fd" | wc -l
readlink -f "/proc/$pid/exe"
"/proc/$pid/exe" version
systemctl cat xray
Сравните локальные и удалённые адреса в выводе ss/lsof. Это покажет, зависают ли клиентские соединения к российскому серверу либо соединения российского сервера с NL-сервером или Reality target.
Почему важно проверить реальный бинарник
В Xray действительно была подтверждённая утечка сокетов VLESS+REALITY: исправление существовало в REALITY, но некоторое время не было подтянуто в основную зависимость Xray. В релизе 26.6.1 заявлено исправление ситуации, когда соединение между сервером и target могло закрываться несвоевременно.
Если Xray установлен панелью или работает в контейнере, строка версии в интерфейсе ещё не гарантирует, что запущен именно обновлённый официальный бинарник. Поэтому стоит проверить путь через /proc/PID/exe, версию самого файла и перезапуск процесса после обновления.
Обсуждение исправления:
https://github.com/XTLS/Xray-core/issues/5828
Релиз 26.6.1:
https://github.com/XTLS/Xray-core/releases/tag/v26.6.1
Что делать дальше
Если фактически запущен официальный 26.6.1, я бы:
- воспроизвёл проблему на минимальной конфигурации без панели и лишних маршрутов;
- временно включил debug-логирование;
- собрал вывод ss с PID и адресами до и после роста CLOSE-WAIT;
- собрал pprof-профиль goroutine/heap Xray;
- сравнил поведение с соседним релизом core.
О TCP KeepAlive
Для действительно пропавших клиентов можно дополнительно настроить sockopt на нужном inbound:
"sockopt": {
"tcpKeepAliveIdle": 90,
"tcpKeepAliveInterval": 30,
"tcpUserTimeout": 60000
}
Но это вспомогательная мера для зависших ESTABLISHED-соединений, а не лечение бесконечного CLOSE-WAIT. Параметр mark: 255 к закрытию сокетов не относится — это метка для policy routing. Добавлять её без соответствующих правил маршрутизации не нужно.
Документация sockopt:
https://xtls.github.io/en/config/transports/sockop...