fix: apply idle timeout to domain fronting relay connections

Domain fronting relay (for non-Telegram traffic) had no idle timeout,
causing worker pool exhaustion under traffic spikes.

The ProxyOpts.IdleTimeout field existed but was never wired into the
proxy. Now domain fronting connections are wrapped with per-read/write
deadlines reset to the configured idle timeout (default 1m), so stale
or slowloris-style connections are reaped promptly.

Fixes #378
This commit is contained in:
Alexey Dolotov
2026-03-28 22:47:39 +03:00
parent cc4b6ce2f4
commit 836090ebdf
4 changed files with 32 additions and 2 deletions
+1
View File
@@ -262,6 +262,7 @@ func runProxy(conf *config.Config, version string) error { //nolint: funlen
AllowFallbackOnUnknownDC: conf.AllowFallbackOnUnknownDC.Get(false), AllowFallbackOnUnknownDC: conf.AllowFallbackOnUnknownDC.Get(false),
TolerateTimeSkewness: conf.TolerateTimeSkewness.Value, TolerateTimeSkewness: conf.TolerateTimeSkewness.Value,
IdleTimeout: conf.Network.Timeout.Idle.Get(mtglib.DefaultIdleTimeout),
DoppelGangerURLs: doppelGangerURLs, DoppelGangerURLs: doppelGangerURLs,
DoppelGangerPerRaid: conf.Defense.Doppelganger.Repeats.Get(mtglib.DoppelGangerPerRaid), DoppelGangerPerRaid: conf.Defense.Doppelganger.Repeats.Get(mtglib.DoppelGangerPerRaid),
+19
View File
@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"time"
"github.com/9seconds/mtg/v2/essentials" "github.com/9seconds/mtg/v2/essentials"
"github.com/pires/go-proxyproto" "github.com/pires/go-proxyproto"
@@ -95,3 +96,21 @@ func newConnProxyProtocol(source, target essentials.Conn) *connProxyProtocol {
sourceAddr: source.RemoteAddr(), sourceAddr: source.RemoteAddr(),
} }
} }
type connIdleTimeout struct {
essentials.Conn
timeout time.Duration
}
func (c connIdleTimeout) Read(b []byte) (int, error) {
c.Conn.SetReadDeadline(time.Now().Add(c.timeout)) //nolint: errcheck
return c.Conn.Read(b) //nolint: wrapcheck
}
func (c connIdleTimeout) Write(b []byte) (int, error) {
c.Conn.SetWriteDeadline(time.Now().Add(c.timeout)) //nolint: errcheck
return c.Conn.Write(b) //nolint: wrapcheck
}
+4 -2
View File
@@ -27,6 +27,7 @@ type Proxy struct {
allowFallbackOnUnknownDC bool allowFallbackOnUnknownDC bool
tolerateTimeSkewness time.Duration tolerateTimeSkewness time.Duration
idleTimeout time.Duration
domainFrontingPort int domainFrontingPort int
domainFrontingIP string domainFrontingIP string
domainFrontingProxyProtocol bool domainFrontingProxyProtocol bool
@@ -306,8 +307,8 @@ func (p *Proxy) doDomainFronting(ctx *streamContext, conn *connRewind) {
relay.Relay( relay.Relay(
ctx, ctx,
ctx.logger.Named("domain-fronting"), ctx.logger.Named("domain-fronting"),
frontConn, connIdleTimeout{Conn: frontConn, timeout: p.idleTimeout},
conn, connIdleTimeout{Conn: conn, timeout: p.idleTimeout},
) )
} }
@@ -339,6 +340,7 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) {
domainFrontingPort: opts.getDomainFrontingPort(), domainFrontingPort: opts.getDomainFrontingPort(),
domainFrontingIP: opts.DomainFrontingIP, domainFrontingIP: opts.DomainFrontingIP,
tolerateTimeSkewness: opts.getTolerateTimeSkewness(), tolerateTimeSkewness: opts.getTolerateTimeSkewness(),
idleTimeout: opts.getIdleTimeout(),
allowFallbackOnUnknownDC: opts.AllowFallbackOnUnknownDC, allowFallbackOnUnknownDC: opts.AllowFallbackOnUnknownDC,
telegram: tg, telegram: tg,
doppelGanger: doppel.NewGanger( doppelGanger: doppel.NewGanger(
+8
View File
@@ -216,6 +216,14 @@ func (p ProxyOpts) getPreferIP() string {
return p.PreferIP return p.PreferIP
} }
func (p ProxyOpts) getIdleTimeout() time.Duration {
if p.IdleTimeout == 0 {
return DefaultIdleTimeout
}
return p.IdleTimeout
}
func (p ProxyOpts) getLogger(name string) Logger { func (p ProxyOpts) getLogger(name string) Logger {
return p.Logger.Named(name) return p.Logger.Named(name)
} }