fix(admin): различать достижимость Traffic Stats API и соответствие профилю
Признак на странице конфигурации отвечал только на вопрос «достучится ли админка», поэтому 0.0.0.0 показывался как норма — хотя внутренний control plane при нём опубликован на всех интерфейсах, а оркестратор такой конфигурации не создаёт. Состояний теперь четыре: канон профиля, wildcard, не-канонический loopback и недостижимый адрес. Backend не тронут: он по-прежнему отвечает только на вопрос достижимости — превращать лишнюю публикацию в отказ обслуживания значило бы отключить всех пиров. Исправлено ложное утверждение в его комментарии: пустой хост `:36712` в Go означает все интерфейсы, а не loopback. Удалены мёртвые фразы common.wait/enableSuccess/disableSuccess — остатки операций запуска, остановки и смены версии Hysteria, которых у панели нет.
This commit is contained in:
+13
-4
@@ -135,23 +135,32 @@ func GetHysteria2ApiPort() (int64, error) {
|
||||
// это и есть тот самый второй контракт: значение, которое админка приняла и
|
||||
// проигнорировала.
|
||||
//
|
||||
// Вопрос здесь ровно один: ДОСТУЧИТСЯ ли админка по этому адресу. Соответствие
|
||||
// production-профилю — вопрос другой и решается там, где конфиг создаётся
|
||||
// (validateRuntimeConfig) и где он показывается оператору (страница
|
||||
// конфигурации отдельно называет wildcard и не-канонический loopback).
|
||||
//
|
||||
// Что принимается:
|
||||
//
|
||||
// ":36712" -> хост не задан, Hysteria слушает loopback;
|
||||
// "127.0.0.1:36712" -> канонический production-профиль;
|
||||
// "127.0.0.5:36712" -> любой loopback достижим по loopback;
|
||||
// "0.0.0.0:36712" -> wildcard включает loopback, обмен состоится.
|
||||
// "0.0.0.0:36712" -> wildcard включает loopback, обмен состоится;
|
||||
// ":36712" -> тот же wildcard: пустой хост в Go означает ВСЕ
|
||||
// интерфейсы, а не loopback.
|
||||
//
|
||||
// Wildcard принимается именно потому, что связь между компонентами при нём
|
||||
// РАБОТАЕТ: отказывать в обслуживании из-за лишней публикации внутреннего API
|
||||
// значило бы наказывать за неё отключением всех пиров. Запрет на wildcard —
|
||||
// решение оркестратора, который конфиг создаёт (см. validateRuntimeConfig).
|
||||
// значило бы наказывать за неё отключением всех пиров. Сама лишняя публикация
|
||||
// при этом не остаётся незамеченной — о ней сообщает панель.
|
||||
func parseTrafficStatsPort(listen string) (int64, error) {
|
||||
trimmed := strings.TrimSpace(listen)
|
||||
if trimmed == "" {
|
||||
return 0, errors.New("empty listen")
|
||||
}
|
||||
|
||||
// Пустой хост подставляется только затем, чтобы SplitHostPort принял
|
||||
// строку: сам по себе `:36712` означает все интерфейсы, и loopback среди
|
||||
// них, поэтому подстановка не меняет ответа на вопрос о достижимости.
|
||||
hostPort := trimmed
|
||||
if strings.HasPrefix(trimmed, ":") {
|
||||
hostPort = "127.0.0.1" + trimmed
|
||||
|
||||
@@ -24,14 +24,16 @@ func TestParseTrafficStatsPortAcceptsReachableAddresses(t *testing.T) {
|
||||
want int64
|
||||
}{
|
||||
{"канонический профиль", "127.0.0.1:36712", 36712},
|
||||
{"хост не задан", ":36712", 36712},
|
||||
{"пробелы по краям", " 127.0.0.1:36712 ", 36712},
|
||||
{"другой адрес loopback", "127.0.0.5:36712", 36712},
|
||||
// Wildcard включает loopback: обмен состоится, и отказывать в
|
||||
// обслуживании из-за лишней публикации внутреннего API нельзя —
|
||||
// это отключило бы всех пиров. Запрет на wildcard принадлежит
|
||||
// оркестратору, который конфиг и создаёт.
|
||||
// оркестратору, который конфиг и создаёт, а сообщает о нём панель.
|
||||
{"wildcard", "0.0.0.0:36712", 36712},
|
||||
// Пустой хост — тот же wildcard, а не loopback: в Go `:36712`
|
||||
// означает все интерфейсы.
|
||||
{"хост не задан", ":36712", 36712},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
|
||||
Reference in New Issue
Block a user