Files
Alessandro RosandGitHub 58c6099e50 ask for credentials only in case of protocols that support it (#5966)
When clients connect with some protocols (SRT, RTMP), they are unable
to provide credentials even if they are asked to. In this case, it's
useless to wait for credentials, and it's better to immediately log
authentication errors and apply the anti-brute force algorithm.
2026-07-19 16:52:14 +02:00

137 lines
3.4 KiB
Go

// Package pprof contains a pprof exporter.
package pprof //nolint:revive
import (
"fmt"
"net"
"net/http"
"time"
"github.com/gin-contrib/pprof"
"github.com/gin-gonic/gin"
"github.com/bluenviron/mediamtx/internal/auth"
"github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/defs"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/protocols/httpp"
)
type pprofAuthManager interface {
Authenticate(req *auth.Request) (string, *auth.Error)
}
type pprofParent interface {
logger.Writer
}
// PPROF is a pprof exporter.
type PPROF struct {
Address string
DumpPackets bool
Encryption bool
ServerKey string
ServerCert string
AllowOrigins []string
TrustedProxies conf.IPNetworks
ReadTimeout conf.Duration
WriteTimeout conf.Duration
AuthManager pprofAuthManager
Parent pprofParent
httpServer *httpp.Server
}
// Initialize initializes PPROF.
func (pp *PPROF) Initialize() error {
router := gin.New()
router.SetTrustedProxies(pp.TrustedProxies.ToTrustedProxies()) //nolint:errcheck
router.Use(pp.middlewarePreflightRequests)
router.Use(pp.middlewareAuth)
pprof.Register(router)
pp.httpServer = &httpp.Server{
Address: pp.Address,
DumpPackets: pp.DumpPackets,
AllowOrigins: pp.AllowOrigins,
DumpPacketsPrefix: "pprof_server_conn",
ReadTimeout: time.Duration(pp.ReadTimeout),
WriteTimeout: time.Duration(pp.WriteTimeout),
Encryption: pp.Encryption,
ServerCert: pp.ServerCert,
ServerKey: pp.ServerKey,
Handler: router,
Parent: pp,
}
err := pp.httpServer.Initialize()
if err != nil {
return err
}
str := "started with listener on " + pp.Address
if !pp.Encryption {
str += " (TCP/HTTP)"
} else {
str += " (TCP/HTTPS)"
}
pp.Log(logger.Info, str)
return nil
}
// Close closes PPROF.
func (pp *PPROF) Close() {
pp.Log(logger.Info, "closing")
pp.httpServer.Close()
}
// Log implements logger.Writer.
func (pp *PPROF) Log(level logger.Level, format string, args ...any) {
pp.Parent.Log(level, "[pprof] "+format, args...)
}
func (pp *PPROF) middlewarePreflightRequests(ctx *gin.Context) {
if ctx.Request.Method == http.MethodOptions &&
ctx.Request.Header.Get("Access-Control-Request-Method") != "" {
ctx.Header("Access-Control-Allow-Methods", "OPTIONS, GET")
ctx.Header("Access-Control-Allow-Headers", "Authorization")
ctx.AbortWithStatus(http.StatusNoContent)
return
}
}
func (pp *PPROF) writeErrorNoLog(ctx *gin.Context, status int, err error) {
ctx.AbortWithStatusJSON(status, &defs.APIError{
Status: defs.APIErrorStatusError,
Error: err.Error(),
})
}
func (pp *PPROF) middlewareAuth(ctx *gin.Context) {
req := &auth.Request{
Action: conf.AuthActionPprof,
Query: ctx.Request.URL.RawQuery,
Credentials: httpp.Credentials(ctx.Request),
IP: net.ParseIP(ctx.ClientIP()),
EnableAskCredentials: true,
}
_, err := pp.AuthManager.Authenticate(req)
if err != nil {
if err.AskCredentials {
ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`)
pp.writeErrorNoLog(ctx, http.StatusUnauthorized, fmt.Errorf("authentication error"))
return
}
auth.LogAndDelayError(&logger.InlineWriter{
Parent: pp,
Prefix: fmt.Sprintf("[conn %v]", httpp.RemoteAddr(ctx)),
}, err)
pp.writeErrorNoLog(ctx, http.StatusUnauthorized, fmt.Errorf("authentication error"))
return
}
}