package constant // Ключи таблицы `config` — то, чем действительно владеет админка. // // Сетевых настроек самой панели (порт, web-контекст, пути к сертификатам) // здесь больше нет. Они достались от H UI, где панель конфигурировала себя // сама, и в архитектуре HY2XS были вторым источником истины: порт приходит из // HY2XS_UI_PORT через systemd, bind — из HY2XS_UI_BIND_HOST, TLS // терминируется на внешнем слое. Панель записывала в SQLite тот же порт, // который ей передали аргументом, и тут же читала его обратно. const ( JwtSecret = "JWT_SECRET" PeerSecretKey = "PEER_SECRET_KEY" PeerSecretEncryptionKey = "PEER_SECRET_ENCRYPTION_KEY" Hysteria2TrafficStatsSecret = "HYSTERIA2_TRAFFIC_STATS_SECRET" Hysteria2Enable = "HYSTERIA2_ENABLE" Hysteria2Config = "HYSTERIA2_CONFIG" Hysteria2TrafficTime = "HYSTERIA2_TRAFFIC_TIME" Hysteria2ConfigRemark = "HYSTERIA2_CONFIG_REMARK" ResetTrafficCron = "RESET_TRAFFIC_CRON" ) // Что операторский API имеет право прочитать и записать в таблице `config`. // // Список — allowlist, и это принципиально. Раньше здесь работал denylist из // трёх orchestrator-ключей, а `getConfig`/`listConfig` принимали произвольную // строку. В той же таблице лежат JWT_SECRET, PEER_SECRET_KEY, // PEER_SECRET_ENCRYPTION_KEY и HYSTERIA2_TRAFFIC_STATS_SECRET — то есть // авторизованный запрос `?key=PEER_SECRET_ENCRYPTION_KEY` отдавал master-key // шифрования секретов пиров, а updateConfigs позволял его подменить. После // подмены перестают расшифровываться секреты уже существующих пиров. // // Именно ради этого удаляли generic export/import таблицы `config`, но точечный // API остался прежним, и дыра просто сменила размер отверстия. // // Denylist здесь неверен структурно: он требует, чтобы автор КАЖДОГО нового // ключа вспомнил про этот файл. Забытый ключ при denylist сразу публичен, при // allowlist — сразу закрыт. Отказ по умолчанию — единственное поведение, // которое не зависит от внимательности. var ( // publicReadableConfigKeys — настройки, которые панель показывает оператору. publicReadableConfigKeys = map[string]struct{}{ Hysteria2TrafficTime: {}, ResetTrafficCron: {}, Hysteria2ConfigRemark: {}, } // publicWritableConfigKeys — подмножество читаемых, которые оператор // действительно редактирует в интерфейсе. // // HYSTERIA2_CONFIG_REMARK сюда не входит: панель его только отображает. // Право записи, которым никто не пользуется, — это attack surface без // пользователя. publicWritableConfigKeys = map[string]struct{}{ Hysteria2TrafficTime: {}, ResetTrafficCron: {}, } ) // IsPublicReadableConfigKey сообщает, можно ли отдать значение ключа наружу. func IsPublicReadableConfigKey(key string) bool { _, ok := publicReadableConfigKeys[key] return ok } // IsPublicWritableConfigKey сообщает, можно ли принять значение ключа снаружи. func IsPublicWritableConfigKey(key string) bool { _, ok := publicWritableConfigKeys[key] return ok } // PublicReadableConfigKeys возвращает копию списка читаемых ключей. // Используется тестами и текстами ошибок. func PublicReadableConfigKeys() []string { return keysOf(publicReadableConfigKeys) } // PublicWritableConfigKeys возвращает копию списка записываемых ключей. func PublicWritableConfigKeys() []string { return keysOf(publicWritableConfigKeys) } // InternalConfigKeys — ключи, которые не покидают процесс ни при каком // сценарии. Объявлены явно, чтобы тест мог проверить их поимённо, а не // полагаться на то, что их «случайно нет» в allowlist. func InternalConfigKeys() []string { return []string{ JwtSecret, PeerSecretKey, PeerSecretEncryptionKey, Hysteria2TrafficStatsSecret, Hysteria2Enable, Hysteria2Config, } } func keysOf(set map[string]struct{}) []string { out := make([]string, 0, len(set)) for key := range set { out = append(out, key) } return out }