cmd/derper,derp: add --rate-config file with SIGHUP reload (#19314)
Add a --rate-config flag pointing to a JSON file for per-client receive rate limits (bytes/sec and burst bytes). The config is reloaded on SIGHUP, updating all existing client connections live. The --per-client-rate-limit and --per-client-rate-burst flags are removed in favor of the config file. In derpserver, rate limiting uses an atomic.Pointer[xrate.Limiter] per client: nil when unlimited or mesh (zero overhead), non-nil when rate-limited. Document that clientSet.activeClient Store operations require Server.mu. Updates tailscale/corp#38509 Signed-off-by: Mike O'Driscoll <mikeo@tailscale.com>
This commit is contained in:
+26
-7
@@ -87,8 +87,7 @@ var (
|
||||
acceptConnLimit = flag.Float64("accept-connection-limit", math.Inf(+1), "rate limit for accepting new connection")
|
||||
acceptConnBurst = flag.Int("accept-connection-burst", math.MaxInt, "burst limit for accepting new connection")
|
||||
|
||||
perClientRateLimit = flag.Uint("per-client-rate-limit", 0, "per-client receive rate limit in bytes/sec; 0 means unlimited. Mesh peers are exempt.")
|
||||
perClientRateBurst = flag.Uint("per-client-rate-burst", 0, "per-client receive rate burst in bytes; 0 defaults to 2x the rate limit (only relevant when using nonzero --per-client-rate-limit)")
|
||||
rateConfigPath = flag.String("rate-config", "", "path to JSON rate limit config file; reloaded on SIGHUP")
|
||||
|
||||
// tcpKeepAlive is intentionally long, to reduce battery cost. There is an L7 keepalive on a higher frequency schedule.
|
||||
tcpKeepAlive = flag.Duration("tcp-keepalive-time", 10*time.Minute, "TCP keepalive time")
|
||||
@@ -195,12 +194,11 @@ func main() {
|
||||
s.SetVerifyClientURL(*verifyClientURL)
|
||||
s.SetVerifyClientURLFailOpen(*verifyFailOpen)
|
||||
s.SetTCPWriteTimeout(*tcpWriteTimeout)
|
||||
if *perClientRateLimit > 0 {
|
||||
burst := *perClientRateBurst
|
||||
if burst < 1 {
|
||||
burst = *perClientRateLimit * 2
|
||||
if *rateConfigPath != "" {
|
||||
if err := s.LoadAndApplyRateConfig(*rateConfigPath); err != nil {
|
||||
log.Fatalf("derper: loading rate config: %v", err)
|
||||
}
|
||||
s.SetPerClientRateLimit(*perClientRateLimit, burst)
|
||||
go watchRateConfig(ctx, s, *rateConfigPath)
|
||||
}
|
||||
|
||||
var meshKey string
|
||||
@@ -436,6 +434,27 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// watchRateConfig listens for SIGHUP signals and reloads the rate config
|
||||
// file on each signal, applying it to the server. It returns when ctx is done.
|
||||
func watchRateConfig(ctx context.Context, s *derpserver.Server, path string) {
|
||||
sighup := make(chan os.Signal, 1)
|
||||
signal.Notify(sighup, syscall.SIGHUP)
|
||||
defer signal.Stop(sighup)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-sighup:
|
||||
log.Printf("derper: received SIGHUP, reloading rate config from %s", path)
|
||||
if err := s.LoadAndApplyRateConfig(path); err != nil {
|
||||
log.Printf("derper: rate config reload failed: %v", err)
|
||||
continue
|
||||
}
|
||||
log.Printf("derper: rate config reloaded successfully")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var validProdHostname = regexp.MustCompile(`^derp([^.]*)\.tailscale\.com\.?$`)
|
||||
|
||||
func prodAutocertHostPolicy(_ context.Context, host string) error {
|
||||
|
||||
Reference in New Issue
Block a user