feat(v1): Gecko-обфускация, latest-stable Hysteria на сборке и forward-compatible admin
Сквозная миграция HY2XS на современную Hysteria (2.12.2) и переход на v1. Build: - версия Hysteria резолвится на этапе сборки из HyNetworks/hysteria и замораживается в metadata пакета (version + immutable url + sha256); - compatibility gate: реальный бинарник должен принять канонический конфиг HY2XS для gecko и salamander до создания пакета; - сборка прогоняет тесты оркестратора и админки. Конфигурационный контракт: - HY2XS_CONFIG_SCHEMA_VERSION=2, чужая схема отклоняется fail-fast; - obfs стал настоящим union gecko|salamander, gecko — default; - obfs-блок рендерится оркестратором целиком, два подтипа одновременно структурно невозможны; - современный baseline: congestion bbr/standard, disableLossCompensation=false, disableStatelessReset=false, полный quic-блок. Исправления: - share URI для gecko: генератор был завязан на Obfs.Salamander.Password и выдавал нерабочую ссылку при любой другой обфускации; - SNI брался только из ACME-блока и уходил пустым при HY2XS_TLS_MODE=file; - экспорт конфига выносил trafficStats.secret, access_token и obfs-пароль; - экспорт терял неизвестные upstream-поля при round-trip через типизированную модель; - renderRuntimeEnv печатал тип обфускации литералом, расходясь с конфигом; - namedotcom удалён из ACME-реестра (нет в Hysteria с 2.11.0). Тесты: - 95 тестов оркестратора: env, рендер, семантика профиля, резолвер, rollover; - тесты URI и экспорта в Go; - tools/test/e2e-hysteria.sh с реальным клиентом Hysteria. UX: - подсказки и примеры в форме создания пира. Прочее: CHANGELOG.md, .gitattributes (LF для target-side файлов), документация на русском.
This commit is contained in:
+114
-1
@@ -1,12 +1,55 @@
|
||||
package bo
|
||||
|
||||
// ObfsShareConfig — та часть обфускации, которую способна описать официальная
|
||||
// схема hysteria2:// URI: только type и password.
|
||||
//
|
||||
// Gecko minPacketSize/maxPacketSize в ссылку не помещаются. Именно поэтому
|
||||
// HY2XS фиксирует их на upstream defaults 512/1200: иначе сгенерированная
|
||||
// ссылка не описывала бы подключение полностью.
|
||||
type ObfsShareConfig struct {
|
||||
Type string
|
||||
Password string
|
||||
}
|
||||
|
||||
// ObfsShare приводит любой поддерживаемый тип обфускации к единому виду для
|
||||
// генерации клиентской ссылки. Знание о подтипах живёт рядом с моделью, чтобы
|
||||
// добавление нового типа не требовало правок в слое share URI.
|
||||
func (c *Hysteria2ServerConfig) ObfsShare() ObfsShareConfig {
|
||||
if c == nil || c.Obfs == nil || c.Obfs.Type == nil {
|
||||
return ObfsShareConfig{}
|
||||
}
|
||||
|
||||
switch *c.Obfs.Type {
|
||||
case "salamander":
|
||||
if c.Obfs.Salamander != nil && c.Obfs.Salamander.Password != nil {
|
||||
return ObfsShareConfig{Type: "salamander", Password: *c.Obfs.Salamander.Password}
|
||||
}
|
||||
case "gecko":
|
||||
if c.Obfs.Gecko != nil && c.Obfs.Gecko.Password != nil {
|
||||
return ObfsShareConfig{Type: "gecko", Password: *c.Obfs.Gecko.Password}
|
||||
}
|
||||
}
|
||||
|
||||
return ObfsShareConfig{}
|
||||
}
|
||||
|
||||
// AcmeDomain возвращает первый ACME-домен, если он есть.
|
||||
func (c *Hysteria2ServerConfig) AcmeDomain() string {
|
||||
if c == nil || c.ACME == nil || len(c.ACME.Domains) == 0 {
|
||||
return ""
|
||||
}
|
||||
return c.ACME.Domains[0]
|
||||
}
|
||||
|
||||
type Hysteria2ServerConfig struct {
|
||||
Listen *string `yaml:"listen,omitempty" json:"listen" validate:"required"`
|
||||
Obfs *serverConfigObfs `yaml:"obfs,omitempty" json:"obfs" validate:"omitempty"`
|
||||
TLS *serverConfigTLS `yaml:"tls,omitempty" json:"tls" validate:"omitempty"`
|
||||
ACME *serverConfigACME `yaml:"acme,omitempty" json:"acme" validate:"omitempty"`
|
||||
ECH *serverConfigECH `yaml:"ech,omitempty" json:"ech" validate:"omitempty"`
|
||||
QUIC *serverConfigQUIC `yaml:"quic,omitempty" json:"quic" validate:"omitempty"`
|
||||
Bandwidth *serverConfigBandwidth `yaml:"bandwidth,omitempty" json:"bandwidth" validate:"omitempty"`
|
||||
Congestion *ServerConfigCongestion `yaml:"congestion,omitempty" json:"congestion" validate:"omitempty"`
|
||||
IgnoreClientBandwidth *bool `yaml:"ignoreClientBandwidth,omitempty" json:"ignoreClientBandwidth" validate:"omitempty"`
|
||||
SpeedTest *bool `yaml:"speedTest,omitempty" json:"speedTest" validate:"omitempty"`
|
||||
DisableUDP *bool `yaml:"disableUDP,omitempty" json:"disableUDP" validate:"omitempty"`
|
||||
@@ -18,21 +61,43 @@ type Hysteria2ServerConfig struct {
|
||||
Outbounds []serverConfigOutboundEntry `yaml:"outbounds,omitempty" json:"outbounds" validate:"omitempty"`
|
||||
TrafficStats *ServerConfigTrafficStats `yaml:"trafficStats,omitempty" json:"trafficStats" validate:"required"`
|
||||
Masquerade *serverConfigMasquerade `yaml:"masquerade,omitempty" json:"masquerade" validate:"omitempty"`
|
||||
Mimic *serverConfigMimic `yaml:"mimic,omitempty" json:"mimic" validate:"omitempty"`
|
||||
Realm *serverConfigRealm `yaml:"realm,omitempty" json:"realm" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type serverConfigObfsSalamander struct {
|
||||
Password *string `yaml:"password,omitempty" json:"password" validate:"required"`
|
||||
}
|
||||
|
||||
// serverConfigObfsGecko описывает Gecko-обфускацию (Hysteria 2.9.2+).
|
||||
// Gecko достраивается поверх Salamander и дополнительно фрагментирует
|
||||
// QUIC handshake на пакеты случайного размера в диапазоне min..max.
|
||||
type serverConfigObfsGecko struct {
|
||||
Password *string `yaml:"password,omitempty" json:"password" validate:"required"`
|
||||
MinPacketSize *int `yaml:"minPacketSize,omitempty" json:"minPacketSize" validate:"omitempty"`
|
||||
MaxPacketSize *int `yaml:"maxPacketSize,omitempty" json:"maxPacketSize" validate:"omitempty"`
|
||||
}
|
||||
|
||||
// Обе ветки опциональны: в конфиге присутствует ровно одна из них,
|
||||
// соответствующая Type.
|
||||
type serverConfigObfs struct {
|
||||
Type *string `yaml:"type,omitempty" json:"type" validate:"required"`
|
||||
Salamander *serverConfigObfsSalamander `yaml:"salamander,omitempty" json:"salamander" validate:"required"`
|
||||
Salamander *serverConfigObfsSalamander `yaml:"salamander,omitempty" json:"salamander" validate:"omitempty"`
|
||||
Gecko *serverConfigObfsGecko `yaml:"gecko,omitempty" json:"gecko" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type serverConfigTLS struct {
|
||||
Cert *string `yaml:"cert,omitempty" json:"cert" validate:"required"`
|
||||
Key *string `yaml:"key,omitempty" json:"key" validate:"required"`
|
||||
SNIGuard *string `yaml:"sniGuard,omitempty" json:"sniGuard" validate:"omitempty"`
|
||||
ClientCA *string `yaml:"clientCA,omitempty" json:"clientCA" validate:"omitempty"`
|
||||
}
|
||||
|
||||
// serverConfigECH — Encrypted Client Hello (Hysteria 2.10.0+).
|
||||
// HY2XS не генерирует ECH keypair и не включает ECH в default-профиле,
|
||||
// но обязан корректно читать и сохранять этот блок.
|
||||
type serverConfigECH struct {
|
||||
KeyPath *string `yaml:"keyPath,omitempty" json:"keyPath" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type serverConfigACME struct {
|
||||
@@ -78,11 +143,25 @@ type serverConfigQUIC struct {
|
||||
MaxIdleTimeout *string `yaml:"maxIdleTimeout,omitempty" json:"maxIdleTimeout" validate:"omitempty"`
|
||||
MaxIncomingStreams *int64 `yaml:"maxIncomingStreams,omitempty" json:"maxIncomingStreams" validate:"omitempty"`
|
||||
DisablePathMTUDiscovery *bool `yaml:"disablePathMTUDiscovery,omitempty" json:"disablePathMTUDiscovery" validate:"omitempty"`
|
||||
// DisableStatelessReset появился в Hysteria 2.12.2. HY2XS оставляет
|
||||
// stateless reset включённым: клиент после перезапуска сервера или сна
|
||||
// устройства переподключается сразу, а не по таймауту.
|
||||
DisableStatelessReset *bool `yaml:"disableStatelessReset,omitempty" json:"disableStatelessReset" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type serverConfigBandwidth struct {
|
||||
Up *string `yaml:"up,omitempty" json:"up" validate:"required"`
|
||||
Down *string `yaml:"down,omitempty" json:"down" validate:"required"`
|
||||
// DisableLossCompensation появился в Hysteria 2.10.0.
|
||||
DisableLossCompensation *bool `yaml:"disableLossCompensation,omitempty" json:"disableLossCompensation" validate:"omitempty"`
|
||||
}
|
||||
|
||||
// ServerConfigCongestion — fallback congestion controller. Используется, когда
|
||||
// Brutal bandwidth не согласован сторонами. Это не то же самое, что host-level
|
||||
// BBR ядра Linux: у Hysteria собственный congestion-control контур.
|
||||
type ServerConfigCongestion struct {
|
||||
Type *string `yaml:"type,omitempty" json:"type" validate:"omitempty"`
|
||||
BBRProfile *string `yaml:"bbrProfile,omitempty" json:"bbrProfile" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type ServerConfigAuthHTTP struct {
|
||||
@@ -183,9 +262,11 @@ type serverConfigMasqueradeFile struct {
|
||||
}
|
||||
|
||||
type serverConfigMasqueradeProxy struct {
|
||||
// URL с Hysteria 2.12.2 может указывать и на unix socket.
|
||||
URL *string `yaml:"url,omitempty" json:"url" validate:"required"`
|
||||
RewriteHost *bool `yaml:"rewriteHost,omitempty" json:"rewriteHost" validate:"required"`
|
||||
Insecure *bool `yaml:"insecure,omitempty" json:"insecure" validate:"required"`
|
||||
XForwarded *bool `yaml:"xForwarded,omitempty" json:"xForwarded" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type serverConfigMasqueradeString struct {
|
||||
@@ -203,3 +284,35 @@ type serverConfigMasquerade struct {
|
||||
ListenHTTPS *string `yaml:"listenHTTPS,omitempty" json:"listenHTTPS" validate:"omitempty"`
|
||||
ForceHTTPS *bool `yaml:"forceHTTPS,omitempty" json:"forceHTTPS" validate:"omitempty"`
|
||||
}
|
||||
|
||||
// serverConfigMimic — Mimic / fake TCP (Hysteria 2.12.0+).
|
||||
// Требует отдельный сторонний бинарник, root и eBPF/XDP, поэтому в
|
||||
// production-профиле HY2XS выключен: текущий systemd-контракт запускает
|
||||
// Hysteria под непривилегированным пользователем.
|
||||
type serverConfigMimic struct {
|
||||
Enabled *bool `yaml:"enabled,omitempty" json:"enabled" validate:"omitempty"`
|
||||
Interface *string `yaml:"interface,omitempty" json:"interface" validate:"omitempty"`
|
||||
XDPMode *string `yaml:"xdpMode,omitempty" json:"xdpMode" validate:"omitempty"`
|
||||
Path *string `yaml:"path,omitempty" json:"path" validate:"omitempty"`
|
||||
ExtraArgs []string `yaml:"extraArgs,omitempty" json:"extraArgs" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type serverConfigRealmPortMapping struct {
|
||||
Enabled *bool `yaml:"enabled,omitempty" json:"enabled" validate:"omitempty"`
|
||||
Timeout *string `yaml:"timeout,omitempty" json:"timeout" validate:"omitempty"`
|
||||
Lifetime *string `yaml:"lifetime,omitempty" json:"lifetime" validate:"omitempty"`
|
||||
}
|
||||
|
||||
// serverConfigRealm — Hysteria Realms (2.9.0+), запуск сервера за NAT через
|
||||
// STUN/hole punching. Меняет фундаментальный сетевой контракт HY2XS
|
||||
// (выделенный сервер, публичный IPv4, UDP/443, own nftables), поэтому в
|
||||
// default-профиле не используется, но должен корректно читаться.
|
||||
type serverConfigRealm struct {
|
||||
StunServers []string `yaml:"stunServers,omitempty" json:"stunServers" validate:"omitempty"`
|
||||
StunTimeout *string `yaml:"stunTimeout,omitempty" json:"stunTimeout" validate:"omitempty"`
|
||||
PunchTimeout *string `yaml:"punchTimeout,omitempty" json:"punchTimeout" validate:"omitempty"`
|
||||
HeartbeatInterval *string `yaml:"heartbeatInterval,omitempty" json:"heartbeatInterval" validate:"omitempty"`
|
||||
Insecure *bool `yaml:"insecure,omitempty" json:"insecure" validate:"omitempty"`
|
||||
IPMode *string `yaml:"ipMode,omitempty" json:"ipMode" validate:"omitempty"`
|
||||
PortMapping *serverConfigRealmPortMapping `yaml:"portMapping,omitempty" json:"portMapping" validate:"omitempty"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user