Add dynamic cert noise calibration for FakeTLS handshake

The hardcoded noise range (2500-4700 bytes) in the FakeTLS ServerHello
does not match the real certificate chain sizes of many popular fronting
domains (e.g., dl.google.com ≈ 6480 bytes, microsoft.com ≈ 13004 bytes).
This makes the proxy detectable by DPI systems that compare the
ApplicationData size with the real cert chain size for the SNI domain.

On startup, probe the fronting domain's actual TLS handshake size and
use the measured value ± jitter instead of the static range. Falls back
to the legacy 2500-4700 range if the probe fails.

Also adds optional caching of probe results between restarts
(noise-cache-path, noise-cache-ttl) and a configurable probe count
(noise-probe-count) under [defense.doppelganger].

Closes #408
This commit is contained in:
Alexey Dolotov
2026-03-26 23:38:58 +03:00
parent d32e8e8b97
commit 80213ad35d
8 changed files with 405 additions and 29 deletions
+4
View File
@@ -267,6 +267,10 @@ func runProxy(conf *config.Config, version string) error { //nolint: funlen
DoppelGangerPerRaid: conf.Defense.Doppelganger.Repeats.Get(mtglib.DoppelGangerPerRaid),
DoppelGangerEach: conf.Defense.Doppelganger.UpdateEach.Get(mtglib.DoppelGangerEach),
DoppelGangerDRS: conf.Defense.Doppelganger.DRS.Get(false),
NoiseProbeCount: conf.Defense.Doppelganger.NoiseProbeCount.Get(0),
NoiseCacheTTL: conf.Defense.Doppelganger.NoiseCacheTTL.Get(0),
NoiseCachePath: conf.Defense.Doppelganger.NoiseCachePath,
}
proxy, err := mtglib.NewProxy(opts)
+7 -4
View File
@@ -50,10 +50,13 @@ type Config struct {
Blocklist ListConfig `json:"blocklist"`
Allowlist ListConfig `json:"allowlist"`
Doppelganger struct {
URLs []TypeHttpsURL `json:"urls"`
Repeats TypeConcurrency `json:"repeats_per_raid"`
UpdateEach TypeDuration `json:"raid_each"`
DRS TypeBool `json:"drs"`
URLs []TypeHttpsURL `json:"urls"`
Repeats TypeConcurrency `json:"repeats_per_raid"`
UpdateEach TypeDuration `json:"raid_each"`
DRS TypeBool `json:"drs"`
NoiseProbeCount TypeConcurrency `json:"noise_probe_count"`
NoiseCacheTTL TypeDuration `json:"noise_cache_ttl"`
NoiseCachePath string `json:"noise_cache_path"`
} `json:"doppelganger"`
} `json:"defense"`
Network struct {
+7 -4
View File
@@ -45,10 +45,13 @@ type tomlConfig struct {
UpdateEach string `toml:"update-each" json:"updateEach,omitempty"`
} `toml:"allowlist" json:"allowlist,omitempty"`
Doppelganger struct {
URLs []string `toml:"urls" json:"urls,omitempty"`
Repeats uint `toml:"repeats-per-raid" json:"repeats_per_raid,omitempty"`
UpdateEach string `toml:"raid-each" json:"raid_each,omitempty"`
DRS bool `toml:"drs" json:"drs,omitempty"`
URLs []string `toml:"urls" json:"urls,omitempty"`
Repeats uint `toml:"repeats-per-raid" json:"repeats_per_raid,omitempty"`
UpdateEach string `toml:"raid-each" json:"raid_each,omitempty"`
DRS bool `toml:"drs" json:"drs,omitempty"`
NoiseProbeCount uint `toml:"noise-probe-count" json:"noise_probe_count,omitempty"`
NoiseCacheTTL string `toml:"noise-cache-ttl" json:"noise_cache_ttl,omitempty"`
NoiseCachePath string `toml:"noise-cache-path" json:"noise_cache_path,omitempty"`
} `toml:"doppelganger" json:"doppelganger,omitempty"`
} `toml:"defense" json:"defense,omitempty"`
Network struct {