fix(security): структурная редакция секретов и строгая проверка конфига
Diagnostics-бандл уносил machine token наружу. Построчное правило `.replace(/(auth:\s*).*/gi, ...)` подставляло маркер в заголовок mapping'а и оставляло нетронутым вложенный auth.http.url: http://127.0.0.1:8080/hui/hysteria2/auth?access_token=<секрет> Это тот же trafficStats secret, который открывает и traffic API, и auth-endpoint. Бандл собирается автоматически при любом падении install/reconfigure и предназначен для передачи наружу. Редакция YAML переписана структурно: документ разбирается и обходится как дерево. Значение секрета может лежать где угодно, поэтому обходить нужно дерево, а не строки. Для неразбираемого документа остаётся консервативный построчный fallback. В env-артефактах секрет теперь вырезается и из URL-значений: HY2_AUTH_URL в post-install.env не подходит ни под один маркер имени ключа, но несёт access_token в значении. Семантическая проверка сгенерированного конфига: - добавлен quic.maxIdleTimeout - он был в production-профиле, но не проверялся, и конфиг с уехавшим idle timeout проходил проверку; - auth.http.url сверяется целиком (host/port/path/token), а не по наличию подстроки access_token=. Это единственный канал допуска пиров, уехавший порт или путь остались бы незамеченными; - сообщение об ошибке auth.http.url не печатает сам токен: текст уходит в логи и в diagnostics-бандл; - добавлены auth.http.insecure, поля ACME и запрет посторонних секций верхнего уровня. Маркеры секретных имён в Go-санитайзере расширены и синхронизированы с оркестратором. Формулировка гарантии сужена до честной: известные секреты и неизвестные поля с секретоподобным именем.
This commit is contained in:
@@ -42,7 +42,8 @@ func GetRawHysteria2Config() (string, error) {
|
||||
// Гарантии:
|
||||
// 1. неизвестные upstream-поля сохраняются — будущие версии Hysteria не
|
||||
// обрезаются только потому, что HY2XS ещё не научился их показывать;
|
||||
// 2. секреты не покидают сервер в открытом виде.
|
||||
// 2. известные секреты и неизвестные поля с секретоподобным именем не
|
||||
// покидают сервер в открытом виде.
|
||||
func ExportHysteria2ConfigYaml() ([]byte, error) {
|
||||
raw, err := GetRawHysteria2Config()
|
||||
if err != nil {
|
||||
@@ -72,12 +73,39 @@ func SanitizeHysteria2ConfigYaml(raw string) ([]byte, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// isSecretKey — обобщённое правило. Оно важно именно потому, что экспорт
|
||||
// сохраняет неизвестные поля: новое upstream-поле с секретом будет вырезано
|
||||
// ещё до того, как HY2XS про него узнает.
|
||||
// secretKeyMarkers покрывает известные секреты HY2XS/Hysteria и типовые имена,
|
||||
// под которыми секрет может появиться в будущем upstream-поле.
|
||||
//
|
||||
// Гарантия формулируется именно так: вырезаются известные секреты и
|
||||
// неизвестные поля с секретоподобным именем. Обещать, что под правило попадёт
|
||||
// ЛЮБОЙ будущий секрет, нельзя — обобщённый sanitizer этого не умеет.
|
||||
//
|
||||
// Список синхронизирован с SECRET_KEY_MARKERS в
|
||||
// orchestrator/src/lib/redaction.ts.
|
||||
var secretKeyMarkers = []string{
|
||||
"password",
|
||||
"passwd",
|
||||
"passphrase",
|
||||
"secret",
|
||||
"token",
|
||||
"credential",
|
||||
"apikey",
|
||||
"api_key",
|
||||
"privatekey",
|
||||
"private_key",
|
||||
"accesskey",
|
||||
"access_key",
|
||||
"secretkey",
|
||||
"secret_key",
|
||||
"authorization",
|
||||
"cookie",
|
||||
"bearer",
|
||||
"signature",
|
||||
}
|
||||
|
||||
func isSecretKey(key string) bool {
|
||||
lowered := strings.ToLower(key)
|
||||
for _, marker := range []string{"password", "passwd", "secret", "token", "credential"} {
|
||||
for _, marker := range secretKeyMarkers {
|
||||
if strings.Contains(lowered, marker) {
|
||||
return true
|
||||
}
|
||||
@@ -139,7 +167,7 @@ func redactNode(node *yaml.Node, path []string) {
|
||||
|
||||
func looksLikeURLKey(key string) bool {
|
||||
lowered := strings.ToLower(key)
|
||||
return lowered == "url" || lowered == "addr"
|
||||
return lowered == "url" || lowered == "addr" || strings.HasSuffix(lowered, "url")
|
||||
}
|
||||
|
||||
func redactSubtree(node *yaml.Node) {
|
||||
|
||||
Reference in New Issue
Block a user