mirror of
https://github.com/caddyserver/caddy.git
synced 2026-05-13 09:06:41 +00:00
admin: rate-limit pprof/profile and pprof/trace endpoints
/debug/pprof/profile and /debug/pprof/trace each block for up to 30 seconds while collecting a CPU or execution trace. Without any concurrency guard, any process that can reach the admin socket can hammer these endpoints to cause sustained CPU overhead and degrade server performance. Add pprofRateLimited(), a semaphore-based wrapper (capacity 1) that allows at most one in-flight profiling request at a time and returns 429 Too Many Requests to any concurrent caller. The fast snapshot endpoints (index, cmdline, symbol) are left unwrapped
This commit is contained in:
parent
929d0e502a
commit
b38b107000
1 changed files with 20 additions and 2 deletions
22
admin.go
22
admin.go
|
|
@ -263,9 +263,9 @@ func (admin *AdminConfig) newAdminHandler(addr NetworkAddress, remote bool, _ Co
|
|||
// register debugging endpoints
|
||||
addRouteWithMetrics("/debug/pprof/", handlerLabel, http.HandlerFunc(pprof.Index))
|
||||
addRouteWithMetrics("/debug/pprof/cmdline", handlerLabel, http.HandlerFunc(pprof.Cmdline))
|
||||
addRouteWithMetrics("/debug/pprof/profile", handlerLabel, http.HandlerFunc(pprof.Profile))
|
||||
addRouteWithMetrics("/debug/pprof/profile", handlerLabel, pprofRateLimited(http.HandlerFunc(pprof.Profile)))
|
||||
addRouteWithMetrics("/debug/pprof/symbol", handlerLabel, http.HandlerFunc(pprof.Symbol))
|
||||
addRouteWithMetrics("/debug/pprof/trace", handlerLabel, http.HandlerFunc(pprof.Trace))
|
||||
addRouteWithMetrics("/debug/pprof/trace", handlerLabel, pprofRateLimited(http.HandlerFunc(pprof.Trace)))
|
||||
addRouteWithMetrics("/debug/vars", handlerLabel, expvar.Handler())
|
||||
|
||||
// register third-party module endpoints
|
||||
|
|
@ -1356,6 +1356,24 @@ func (e APIError) Error() string {
|
|||
return e.Message
|
||||
}
|
||||
|
||||
// pprofSem limits concurrent CPU-intensive pprof operations (profile, trace)
|
||||
// to prevent a DoS via repeated 30-second profiling sessions.
|
||||
var pprofSem = make(chan struct{}, 1)
|
||||
|
||||
// pprofRateLimited wraps an http.Handler so that at most one request is
|
||||
// served at a time. Additional concurrent callers receive 429.
|
||||
func pprofRateLimited(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
select {
|
||||
case pprofSem <- struct{}{}:
|
||||
defer func() { <-pprofSem }()
|
||||
h.ServeHTTP(w, r)
|
||||
default:
|
||||
http.Error(w, "too many profiling requests; try again later", http.StatusTooManyRequests)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// parseAdminListenAddr extracts a singular listen address from either addr
|
||||
// or defaultAddr, returning the network and the address of the listener.
|
||||
func parseAdminListenAddr(addr string, defaultAddr string) (NetworkAddress, error) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue