support HTTPS, Allow-Origin and trusted proxies in API, playback server, metrics server and pprof server (#2658) (#2491) (#3235) (#3280)

This commit is contained in:
Alessandro Ros 2024-04-21 17:10:35 +02:00 committed by GitHub
parent 9e636308d9
commit 85dd81698b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 495 additions and 278 deletions

View File

@ -43,14 +43,6 @@ components:
type: integer
externalAuthenticationURL:
type: string
metrics:
type: boolean
metricsAddress:
type: string
pprof:
type: boolean
pprofAddress:
type: string
runOnConnect:
type: string
runOnConnectRestart:
@ -58,17 +50,77 @@ components:
runOnDisconnect:
type: string
# API
# Control API
api:
type: boolean
apiAddress:
type: string
apiEncryption:
type: boolean
apiServerKey:
type: string
apiServerCert:
type: string
apiAllowOrigin:
type: string
apiTrustedProxies:
type: array
items:
type: string
# Metrics
metrics:
type: boolean
metricsAddress:
type: string
metricsEncryption:
type: boolean
metricsServerKey:
type: string
metricsServerCert:
type: string
metricsAllowOrigin:
type: string
metricsTrustedProxies:
type: array
items:
type: string
# PPROF
pprof:
type: boolean
pprofAddress:
type: string
pprofEncryption:
type: boolean
pprofServerKey:
type: string
pprofServerCert:
type: string
pprofAllowOrigin:
type: string
pprofTrustedProxies:
type: array
items:
type: string
# Playback server
playback:
type: boolean
playbackAddress:
type: string
playbackEncryption:
type: boolean
playbackServerKey:
type: string
playbackServerCert:
type: string
playbackAllowOrigin:
type: string
playbackTrustedProxies:
type: array
items:
type: string
# RTSP server
rtsp:
@ -127,6 +179,12 @@ components:
type: string
hlsServerCert:
type: string
hlsAllowOrigin:
type: string
hlsTrustedProxies:
type: array
items:
type: string
hlsAlwaysRemux:
type: boolean
hlsVariant:
@ -139,12 +197,6 @@ components:
type: string
hlsSegmentMaxSize:
type: string
hlsAllowOrigin:
type: string
hlsTrustedProxies:
type: array
items:
type: string
hlsDirectory:
type: string

View File

@ -162,19 +162,24 @@ type apiParent interface {
// API is an API server.
type API struct {
Address string
ReadTimeout conf.StringDuration
Conf *conf.Conf
AuthManager apiAuthManager
PathManager PathManager
RTSPServer RTSPServer
RTSPSServer RTSPServer
RTMPServer RTMPServer
RTMPSServer RTMPServer
HLSServer HLSServer
WebRTCServer WebRTCServer
SRTServer SRTServer
Parent apiParent
Address string
Encryption bool
ServerKey string
ServerCert string
AllowOrigin string
TrustedProxies conf.IPNetworks
ReadTimeout conf.StringDuration
Conf *conf.Conf
AuthManager apiAuthManager
PathManager PathManager
RTSPServer RTSPServer
RTSPSServer RTSPServer
RTMPServer RTMPServer
RTMPSServer RTMPServer
HLSServer HLSServer
WebRTCServer WebRTCServer
SRTServer SRTServer
Parent apiParent
httpServer *httpp.WrappedServer
mutex sync.RWMutex
@ -183,9 +188,9 @@ type API struct {
// Initialize initializes API.
func (a *API) Initialize() error {
router := gin.New()
router.SetTrustedProxies(nil) //nolint:errcheck
router.SetTrustedProxies(a.TrustedProxies.ToTrustedProxies()) //nolint:errcheck
group := router.Group("/", a.mwAuth)
group := router.Group("/", a.middlewareOrigin, a.middlewareAuth)
group.GET("/v3/config/global/get", a.onConfigGlobalGet)
group.PATCH("/v3/config/global/patch", a.onConfigGlobalPatch)
@ -254,16 +259,17 @@ func (a *API) Initialize() error {
network, address := restrictnetwork.Restrict("tcp", a.Address)
var err error
a.httpServer, err = httpp.NewWrappedServer(
network,
address,
time.Duration(a.ReadTimeout),
"",
"",
router,
a,
)
a.httpServer = &httpp.WrappedServer{
Network: network,
Address: address,
ReadTimeout: time.Duration(a.ReadTimeout),
Encryption: a.Encryption,
ServerCert: a.ServerCert,
ServerKey: a.ServerKey,
Handler: router,
Parent: a,
}
err := a.httpServer.Initialize()
if err != nil {
return err
}
@ -294,7 +300,12 @@ func (a *API) writeError(ctx *gin.Context, status int, err error) {
})
}
func (a *API) mwAuth(ctx *gin.Context) {
func (a *API) middlewareOrigin(ctx *gin.Context) {
ctx.Writer.Header().Set("Access-Control-Allow-Origin", a.AllowOrigin)
ctx.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
}
func (a *API) middlewareAuth(ctx *gin.Context) {
user, pass, hasCredentials := ctx.Request.BasicAuth()
err := a.AuthManager.Authenticate(&auth.Request{

View File

@ -130,10 +130,6 @@ type Conf struct {
ReadBufferCount *int `json:"readBufferCount,omitempty"` // deprecated
WriteQueueSize int `json:"writeQueueSize"`
UDPMaxPayloadSize int `json:"udpMaxPayloadSize"`
Metrics bool `json:"metrics"`
MetricsAddress string `json:"metricsAddress"`
PPROF bool `json:"pprof"`
PPROFAddress string `json:"pprofAddress"`
RunOnConnect string `json:"runOnConnect"`
RunOnConnectRestart bool `json:"runOnConnectRestart"`
RunOnDisconnect string `json:"runOnDisconnect"`
@ -146,13 +142,41 @@ type Conf struct {
AuthHTTPExclude []AuthInternalUserPermission `json:"authHTTPExclude"`
AuthJWTJWKS string `json:"authJWTJWKS"`
// API
API bool `json:"api"`
APIAddress string `json:"apiAddress"`
// Control API
API bool `json:"api"`
APIAddress string `json:"apiAddress"`
APIEncryption bool `json:"apiEncryption"`
APIServerKey string `json:"apiServerKey"`
APIServerCert string `json:"apiServerCert"`
APIAllowOrigin string `json:"apiAllowOrigin"`
APITrustedProxies IPNetworks `json:"apiTrustedProxies"`
// Metrics
Metrics bool `json:"metrics"`
MetricsAddress string `json:"metricsAddress"`
MetricsEncryption bool `json:"metricsEncryption"`
MetricsServerKey string `json:"metricsServerKey"`
MetricsServerCert string `json:"metricsServerCert"`
MetricsAllowOrigin string `json:"metricsAllowOrigin"`
MetricsTrustedProxies IPNetworks `json:"metricsTrustedProxies"`
// PPROF
PPROF bool `json:"pprof"`
PPROFAddress string `json:"pprofAddress"`
PPROFEncryption bool `json:"pprofEncryption"`
PPROFServerKey string `json:"pprofServerKey"`
PPROFServerCert string `json:"pprofServerCert"`
PPROFAllowOrigin string `json:"pprofAllowOrigin"`
PPROFTrustedProxies IPNetworks `json:"pprofTrustedProxies"`
// Playback
Playback bool `json:"playback"`
PlaybackAddress string `json:"playbackAddress"`
Playback bool `json:"playback"`
PlaybackAddress string `json:"playbackAddress"`
PlaybackEncryption bool `json:"playbackEncryption"`
PlaybackServerKey string `json:"playbackServerKey"`
PlaybackServerCert string `json:"playbackServerCert"`
PlaybackAllowOrigin string `json:"playbackAllowOrigin"`
PlaybackTrustedProxies IPNetworks `json:"playbackTrustedProxies"`
// RTSP server
RTSP bool `json:"rtsp"`
@ -187,14 +211,14 @@ type Conf struct {
HLSEncryption bool `json:"hlsEncryption"`
HLSServerKey string `json:"hlsServerKey"`
HLSServerCert string `json:"hlsServerCert"`
HLSAllowOrigin string `json:"hlsAllowOrigin"`
HLSTrustedProxies IPNetworks `json:"hlsTrustedProxies"`
HLSAlwaysRemux bool `json:"hlsAlwaysRemux"`
HLSVariant HLSVariant `json:"hlsVariant"`
HLSSegmentCount int `json:"hlsSegmentCount"`
HLSSegmentDuration StringDuration `json:"hlsSegmentDuration"`
HLSPartDuration StringDuration `json:"hlsPartDuration"`
HLSSegmentMaxSize StringSize `json:"hlsSegmentMaxSize"`
HLSAllowOrigin string `json:"hlsAllowOrigin"`
HLSTrustedProxies IPNetworks `json:"hlsTrustedProxies"`
HLSDirectory string `json:"hlsDirectory"`
// WebRTC server
@ -246,8 +270,6 @@ func (conf *Conf) setDefaults() {
conf.WriteTimeout = 10 * StringDuration(time.Second)
conf.WriteQueueSize = 512
conf.UDPMaxPayloadSize = 1472
conf.MetricsAddress = ":9998"
conf.PPROFAddress = ":9999"
// Authentication
conf.AuthInternalUsers = []AuthInternalUser{
@ -295,11 +317,29 @@ func (conf *Conf) setDefaults() {
},
}
// API
// Control API
conf.APIAddress = ":9997"
conf.APIServerKey = "server.key"
conf.APIServerCert = "server.crt"
conf.APIAllowOrigin = "*"
// Metrics
conf.MetricsAddress = ":9998"
conf.MetricsServerKey = "server.key"
conf.MetricsServerCert = "server.crt"
conf.MetricsAllowOrigin = "*"
// PPROF
conf.PPROFAddress = ":9999"
conf.PPROFServerKey = "server.key"
conf.PPROFServerCert = "server.crt"
conf.PPROFAllowOrigin = "*"
// Playback server
conf.PlaybackAddress = ":9996"
conf.PlaybackServerKey = "server.key"
conf.PlaybackServerCert = "server.crt"
conf.PlaybackAllowOrigin = "*"
// RTSP server
conf.RTSP = true
@ -331,12 +371,12 @@ func (conf *Conf) setDefaults() {
conf.HLSAddress = ":8888"
conf.HLSServerKey = "server.key"
conf.HLSServerCert = "server.crt"
conf.HLSAllowOrigin = "*"
conf.HLSVariant = HLSVariant(gohlslib.MuxerVariantLowLatency)
conf.HLSSegmentCount = 7
conf.HLSSegmentDuration = 1 * StringDuration(time.Second)
conf.HLSPartDuration = 200 * StringDuration(time.Millisecond)
conf.HLSSegmentMaxSize = 50 * 1024 * 1024
conf.HLSAllowOrigin = "*"
// WebRTC server
conf.WebRTC = true

View File

@ -295,10 +295,15 @@ func (p *Core) createResources(initial bool) error {
if p.conf.Metrics &&
p.metrics == nil {
i := &metrics.Metrics{
Address: p.conf.MetricsAddress,
ReadTimeout: p.conf.ReadTimeout,
AuthManager: p.authManager,
Parent: p,
Address: p.conf.MetricsAddress,
Encryption: p.conf.MetricsEncryption,
ServerKey: p.conf.MetricsServerKey,
ServerCert: p.conf.MetricsServerCert,
AllowOrigin: p.conf.MetricsAllowOrigin,
TrustedProxies: p.conf.MetricsTrustedProxies,
ReadTimeout: p.conf.ReadTimeout,
AuthManager: p.authManager,
Parent: p,
}
err := i.Initialize()
if err != nil {
@ -310,10 +315,15 @@ func (p *Core) createResources(initial bool) error {
if p.conf.PPROF &&
p.pprof == nil {
i := &pprof.PPROF{
Address: p.conf.PPROFAddress,
ReadTimeout: p.conf.ReadTimeout,
AuthManager: p.authManager,
Parent: p,
Address: p.conf.PPROFAddress,
Encryption: p.conf.PPROFEncryption,
ServerKey: p.conf.PPROFServerKey,
ServerCert: p.conf.PPROFServerCert,
AllowOrigin: p.conf.PPROFAllowOrigin,
TrustedProxies: p.conf.PPROFTrustedProxies,
ReadTimeout: p.conf.ReadTimeout,
AuthManager: p.authManager,
Parent: p,
}
err := i.Initialize()
if err != nil {
@ -335,11 +345,16 @@ func (p *Core) createResources(initial bool) error {
if p.conf.Playback &&
p.playbackServer == nil {
i := &playback.Server{
Address: p.conf.PlaybackAddress,
ReadTimeout: p.conf.ReadTimeout,
PathConfs: p.conf.Paths,
AuthManager: p.authManager,
Parent: p,
Address: p.conf.PlaybackAddress,
Encryption: p.conf.PlaybackEncryption,
ServerKey: p.conf.PlaybackServerKey,
ServerCert: p.conf.PlaybackServerCert,
AllowOrigin: p.conf.PlaybackAllowOrigin,
TrustedProxies: p.conf.PlaybackTrustedProxies,
ReadTimeout: p.conf.ReadTimeout,
PathConfs: p.conf.Paths,
AuthManager: p.authManager,
Parent: p,
}
err := i.Initialize()
if err != nil {
@ -520,14 +535,14 @@ func (p *Core) createResources(initial bool) error {
Encryption: p.conf.HLSEncryption,
ServerKey: p.conf.HLSServerKey,
ServerCert: p.conf.HLSServerCert,
AllowOrigin: p.conf.HLSAllowOrigin,
TrustedProxies: p.conf.HLSTrustedProxies,
AlwaysRemux: p.conf.HLSAlwaysRemux,
Variant: p.conf.HLSVariant,
SegmentCount: p.conf.HLSSegmentCount,
SegmentDuration: p.conf.HLSSegmentDuration,
PartDuration: p.conf.HLSPartDuration,
SegmentMaxSize: p.conf.HLSSegmentMaxSize,
AllowOrigin: p.conf.HLSAllowOrigin,
TrustedProxies: p.conf.HLSTrustedProxies,
Directory: p.conf.HLSDirectory,
ReadTimeout: p.conf.ReadTimeout,
WriteQueueSize: p.conf.WriteQueueSize,
@ -609,19 +624,24 @@ func (p *Core) createResources(initial bool) error {
if p.conf.API &&
p.api == nil {
i := &api.API{
Address: p.conf.APIAddress,
ReadTimeout: p.conf.ReadTimeout,
Conf: p.conf,
AuthManager: p.authManager,
PathManager: p.pathManager,
RTSPServer: p.rtspServer,
RTSPSServer: p.rtspsServer,
RTMPServer: p.rtmpServer,
RTMPSServer: p.rtmpsServer,
HLSServer: p.hlsServer,
WebRTCServer: p.webRTCServer,
SRTServer: p.srtServer,
Parent: p,
Address: p.conf.APIAddress,
Encryption: p.conf.APIEncryption,
ServerKey: p.conf.APIServerKey,
ServerCert: p.conf.APIServerCert,
AllowOrigin: p.conf.APIAllowOrigin,
TrustedProxies: p.conf.APITrustedProxies,
ReadTimeout: p.conf.ReadTimeout,
Conf: p.conf,
AuthManager: p.authManager,
PathManager: p.pathManager,
RTSPServer: p.rtspServer,
RTSPSServer: p.rtspsServer,
RTMPServer: p.rtmpServer,
RTMPSServer: p.rtmpsServer,
HLSServer: p.hlsServer,
WebRTCServer: p.webRTCServer,
SRTServer: p.srtServer,
Parent: p,
}
err := i.Initialize()
if err != nil {
@ -660,6 +680,11 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
closeMetrics := newConf == nil ||
newConf.Metrics != p.conf.Metrics ||
newConf.MetricsAddress != p.conf.MetricsAddress ||
newConf.MetricsEncryption != p.conf.MetricsEncryption ||
newConf.MetricsServerKey != p.conf.MetricsServerKey ||
newConf.MetricsServerCert != p.conf.MetricsServerCert ||
newConf.MetricsAllowOrigin != p.conf.MetricsAllowOrigin ||
!reflect.DeepEqual(newConf.MetricsTrustedProxies, p.conf.MetricsTrustedProxies) ||
newConf.ReadTimeout != p.conf.ReadTimeout ||
closeAuthManager ||
closeLogger
@ -667,6 +692,11 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
closePPROF := newConf == nil ||
newConf.PPROF != p.conf.PPROF ||
newConf.PPROFAddress != p.conf.PPROFAddress ||
newConf.PPROFEncryption != p.conf.PPROFEncryption ||
newConf.PPROFServerKey != p.conf.PPROFServerKey ||
newConf.PPROFServerCert != p.conf.PPROFServerCert ||
newConf.PPROFAllowOrigin != p.conf.PPROFAllowOrigin ||
!reflect.DeepEqual(newConf.PPROFTrustedProxies, p.conf.PPROFTrustedProxies) ||
newConf.ReadTimeout != p.conf.ReadTimeout ||
closeAuthManager ||
closeLogger
@ -678,6 +708,11 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
closePlaybackServer := newConf == nil ||
newConf.Playback != p.conf.Playback ||
newConf.PlaybackAddress != p.conf.PlaybackAddress ||
newConf.PlaybackEncryption != p.conf.PlaybackEncryption ||
newConf.PlaybackServerKey != p.conf.PlaybackServerKey ||
newConf.PlaybackServerCert != p.conf.PlaybackServerCert ||
newConf.PlaybackAllowOrigin != p.conf.PlaybackAllowOrigin ||
!reflect.DeepEqual(newConf.PlaybackTrustedProxies, p.conf.PlaybackTrustedProxies) ||
newConf.ReadTimeout != p.conf.ReadTimeout ||
closeAuthManager ||
closeLogger
@ -780,14 +815,14 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
newConf.HLSEncryption != p.conf.HLSEncryption ||
newConf.HLSServerKey != p.conf.HLSServerKey ||
newConf.HLSServerCert != p.conf.HLSServerCert ||
newConf.HLSAllowOrigin != p.conf.HLSAllowOrigin ||
!reflect.DeepEqual(newConf.HLSTrustedProxies, p.conf.HLSTrustedProxies) ||
newConf.HLSAlwaysRemux != p.conf.HLSAlwaysRemux ||
newConf.HLSVariant != p.conf.HLSVariant ||
newConf.HLSSegmentCount != p.conf.HLSSegmentCount ||
newConf.HLSSegmentDuration != p.conf.HLSSegmentDuration ||
newConf.HLSPartDuration != p.conf.HLSPartDuration ||
newConf.HLSSegmentMaxSize != p.conf.HLSSegmentMaxSize ||
newConf.HLSAllowOrigin != p.conf.HLSAllowOrigin ||
!reflect.DeepEqual(newConf.HLSTrustedProxies, p.conf.HLSTrustedProxies) ||
newConf.HLSDirectory != p.conf.HLSDirectory ||
newConf.ReadTimeout != p.conf.ReadTimeout ||
newConf.WriteQueueSize != p.conf.WriteQueueSize ||
@ -832,6 +867,11 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) {
closeAPI := newConf == nil ||
newConf.API != p.conf.API ||
newConf.APIAddress != p.conf.APIAddress ||
newConf.APIEncryption != p.conf.APIEncryption ||
newConf.APIServerKey != p.conf.APIServerKey ||
newConf.APIServerCert != p.conf.APIServerCert ||
newConf.APIAllowOrigin != p.conf.APIAllowOrigin ||
!reflect.DeepEqual(newConf.APITrustedProxies, p.conf.APITrustedProxies) ||
newConf.ReadTimeout != p.conf.ReadTimeout ||
closeAuthManager ||
closePathManager ||

View File

@ -42,10 +42,15 @@ type metricsParent interface {
// Metrics is a metrics provider.
type Metrics struct {
Address string
ReadTimeout conf.StringDuration
AuthManager metricsAuthManager
Parent metricsParent
Address string
Encryption bool
ServerKey string
ServerCert string
AllowOrigin string
TrustedProxies conf.IPNetworks
ReadTimeout conf.StringDuration
AuthManager metricsAuthManager
Parent metricsParent
httpServer *httpp.WrappedServer
mutex sync.Mutex
@ -62,22 +67,22 @@ type Metrics struct {
// Initialize initializes metrics.
func (m *Metrics) Initialize() error {
router := gin.New()
router.SetTrustedProxies(nil) //nolint:errcheck
router.GET("/metrics", m.mwAuth, m.onMetrics)
router.SetTrustedProxies(m.TrustedProxies.ToTrustedProxies()) //nolint:errcheck
router.NoRoute(m.onRequest)
network, address := restrictnetwork.Restrict("tcp", m.Address)
var err error
m.httpServer, err = httpp.NewWrappedServer(
network,
address,
time.Duration(m.ReadTimeout),
"",
"",
router,
m,
)
m.httpServer = &httpp.WrappedServer{
Network: network,
Address: address,
ReadTimeout: time.Duration(m.ReadTimeout),
Encryption: m.Encryption,
ServerCert: m.ServerCert,
ServerKey: m.ServerKey,
Handler: router,
Parent: m,
}
err := m.httpServer.Initialize()
if err != nil {
return err
}
@ -98,7 +103,14 @@ func (m *Metrics) Log(level logger.Level, format string, args ...interface{}) {
m.Parent.Log(level, "[metrics] "+format, args...)
}
func (m *Metrics) mwAuth(ctx *gin.Context) {
func (m *Metrics) onRequest(ctx *gin.Context) {
ctx.Writer.Header().Set("Access-Control-Allow-Origin", m.AllowOrigin)
ctx.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
if ctx.Request.URL.Path != "/metrics" || ctx.Request.Method != http.MethodGet {
return
}
user, pass, hasCredentials := ctx.Request.BasicAuth()
err := m.AuthManager.Authenticate(&auth.Request{
@ -121,9 +133,7 @@ func (m *Metrics) mwAuth(ctx *gin.Context) {
ctx.Writer.WriteHeader(http.StatusUnauthorized)
return
}
}
func (m *Metrics) onMetrics(ctx *gin.Context) {
out := ""
data, err := m.pathManager.APIPathsList()

View File

@ -24,85 +24,94 @@ type serverAuthManager interface {
// Server is the playback server.
type Server struct {
Address string
ReadTimeout conf.StringDuration
PathConfs map[string]*conf.Path
AuthManager serverAuthManager
Parent logger.Writer
Address string
Encryption bool
ServerKey string
ServerCert string
AllowOrigin string
TrustedProxies conf.IPNetworks
ReadTimeout conf.StringDuration
PathConfs map[string]*conf.Path
AuthManager serverAuthManager
Parent logger.Writer
httpServer *httpp.WrappedServer
mutex sync.RWMutex
}
// Initialize initializes Server.
func (p *Server) Initialize() error {
func (s *Server) Initialize() error {
router := gin.New()
router.SetTrustedProxies(nil) //nolint:errcheck
router.SetTrustedProxies(s.TrustedProxies.ToTrustedProxies()) //nolint:errcheck
group := router.Group("/", s.middlewareOrigin)
group.GET("/list", s.onList)
group.GET("/get", s.onGet)
group := router.Group("/")
network, address := restrictnetwork.Restrict("tcp", s.Address)
group.GET("/list", p.onList)
group.GET("/get", p.onGet)
network, address := restrictnetwork.Restrict("tcp", p.Address)
var err error
p.httpServer, err = httpp.NewWrappedServer(
network,
address,
time.Duration(p.ReadTimeout),
"",
"",
router,
p,
)
s.httpServer = &httpp.WrappedServer{
Network: network,
Address: address,
ReadTimeout: time.Duration(s.ReadTimeout),
Encryption: s.Encryption,
ServerCert: s.ServerCert,
ServerKey: s.ServerKey,
Handler: router,
Parent: s,
}
err := s.httpServer.Initialize()
if err != nil {
return err
}
p.Log(logger.Info, "listener opened on "+address)
s.Log(logger.Info, "listener opened on "+address)
return nil
}
// Close closes Server.
func (p *Server) Close() {
p.Log(logger.Info, "listener is closing")
p.httpServer.Close()
func (s *Server) Close() {
s.Log(logger.Info, "listener is closing")
s.httpServer.Close()
}
// Log implements logger.Writer.
func (p *Server) Log(level logger.Level, format string, args ...interface{}) {
p.Parent.Log(level, "[playback] "+format, args...)
func (s *Server) Log(level logger.Level, format string, args ...interface{}) {
s.Parent.Log(level, "[playback] "+format, args...)
}
// ReloadPathConfs is called by core.Core.
func (p *Server) ReloadPathConfs(pathConfs map[string]*conf.Path) {
p.mutex.Lock()
defer p.mutex.Unlock()
p.PathConfs = pathConfs
func (s *Server) ReloadPathConfs(pathConfs map[string]*conf.Path) {
s.mutex.Lock()
defer s.mutex.Unlock()
s.PathConfs = pathConfs
}
func (p *Server) writeError(ctx *gin.Context, status int, err error) {
func (s *Server) writeError(ctx *gin.Context, status int, err error) {
// show error in logs
p.Log(logger.Error, err.Error())
s.Log(logger.Error, err.Error())
// add error to response
ctx.String(status, err.Error())
}
func (p *Server) safeFindPathConf(name string) (*conf.Path, error) {
p.mutex.RLock()
defer p.mutex.RUnlock()
func (s *Server) safeFindPathConf(name string) (*conf.Path, error) {
s.mutex.RLock()
defer s.mutex.RUnlock()
_, pathConf, _, err := conf.FindPathConf(p.PathConfs, name)
_, pathConf, _, err := conf.FindPathConf(s.PathConfs, name)
return pathConf, err
}
func (p *Server) doAuth(ctx *gin.Context, pathName string) bool {
func (s *Server) middlewareOrigin(ctx *gin.Context) {
ctx.Writer.Header().Set("Access-Control-Allow-Origin", s.AllowOrigin)
ctx.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
}
func (s *Server) doAuth(ctx *gin.Context, pathName string) bool {
user, pass, hasCredentials := ctx.Request.BasicAuth()
err := p.AuthManager.Authenticate(&auth.Request{
err := s.AuthManager.Authenticate(&auth.Request{
User: user,
Pass: pass,
Query: ctx.Request.URL.RawQuery,
@ -120,7 +129,7 @@ func (p *Server) doAuth(ctx *gin.Context, pathName string) bool {
var terr auth.Error
errors.As(err, &terr)
p.Log(logger.Info, "connection %v failed to authenticate: %v", httpp.RemoteAddr(ctx), terr.Message)
s.Log(logger.Info, "connection %v failed to authenticate: %v", httpp.RemoteAddr(ctx), terr.Message)
// wait some seconds to mitigate brute force attacks
<-time.After(auth.PauseAfterError)

View File

@ -4,7 +4,6 @@ package pprof
import (
"net"
"net/http"
"strings"
"time"
// start pprof
@ -15,6 +14,7 @@ import (
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/protocols/httpp"
"github.com/bluenviron/mediamtx/internal/restrictnetwork"
"github.com/gin-gonic/gin"
)
type pprofAuthManager interface {
@ -27,28 +27,38 @@ type pprofParent interface {
// PPROF is a pprof exporter.
type PPROF struct {
Address string
ReadTimeout conf.StringDuration
AuthManager pprofAuthManager
Parent pprofParent
Address string
Encryption bool
ServerKey string
ServerCert string
AllowOrigin string
TrustedProxies conf.IPNetworks
ReadTimeout conf.StringDuration
AuthManager pprofAuthManager
Parent pprofParent
httpServer *httpp.WrappedServer
}
// Initialize initializes PPROF.
func (pp *PPROF) Initialize() error {
router := gin.New()
router.SetTrustedProxies(pp.TrustedProxies.ToTrustedProxies()) //nolint:errcheck
router.NoRoute(pp.onRequest)
network, address := restrictnetwork.Restrict("tcp", pp.Address)
var err error
pp.httpServer, err = httpp.NewWrappedServer(
network,
address,
time.Duration(pp.ReadTimeout),
"",
"",
pp,
pp,
)
pp.httpServer = &httpp.WrappedServer{
Network: network,
Address: address,
ReadTimeout: time.Duration(pp.ReadTimeout),
Encryption: pp.Encryption,
ServerCert: pp.ServerCert,
ServerKey: pp.ServerKey,
Handler: router,
Parent: pp,
}
err := pp.httpServer.Initialize()
if err != nil {
return err
}
@ -69,31 +79,32 @@ func (pp *PPROF) Log(level logger.Level, format string, args ...interface{}) {
pp.Parent.Log(level, "[pprof] "+format, args...)
}
func (pp *PPROF) ServeHTTP(w http.ResponseWriter, r *http.Request) {
user, pass, hasCredentials := r.BasicAuth()
func (pp *PPROF) onRequest(ctx *gin.Context) {
ctx.Writer.Header().Set("Access-Control-Allow-Origin", pp.AllowOrigin)
ctx.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
ip, _, _ := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr))
user, pass, hasCredentials := ctx.Request.BasicAuth()
err := pp.AuthManager.Authenticate(&auth.Request{
User: user,
Pass: pass,
Query: r.URL.RawQuery,
IP: net.ParseIP(ip),
Query: ctx.Request.URL.RawQuery,
IP: net.ParseIP(ctx.ClientIP()),
Action: conf.AuthActionMetrics,
})
if err != nil {
if !hasCredentials {
w.Header().Set("WWW-Authenticate", `Basic realm="mediamtx"`)
w.WriteHeader(http.StatusUnauthorized)
ctx.Writer.Header().Set("WWW-Authenticate", `Basic realm="mediamtx"`)
ctx.Writer.WriteHeader(http.StatusUnauthorized)
return
}
// wait some seconds to mitigate brute force attacks
<-time.After(auth.PauseAfterError)
w.WriteHeader(http.StatusUnauthorized)
ctx.Writer.WriteHeader(http.StatusUnauthorized)
return
}
http.DefaultServeMux.ServeHTTP(w, r)
http.DefaultServeMux.ServeHTTP(ctx.Writer, ctx.Request)
}

View File

@ -4,6 +4,7 @@ package httpp
import (
"context"
"crypto/tls"
"fmt"
"log"
"net"
"net/http"
@ -26,31 +27,29 @@ func (nilWriter) Write(p []byte) (int, error) {
// - server header
// - filtering of invalid requests
type WrappedServer struct {
Network string
Address string
ReadTimeout time.Duration
Encryption bool
ServerCert string
ServerKey string
Handler http.Handler
Parent logger.Writer
ln net.Listener
inner *http.Server
}
// NewWrappedServer allocates a WrappedServer.
func NewWrappedServer(
network string,
address string,
readTimeout time.Duration,
serverCert string,
serverKey string,
handler http.Handler,
parent logger.Writer,
) (*WrappedServer, error) {
ln, err := net.Listen(network, address)
if err != nil {
return nil, err
}
// Initialize initializes a WrappedServer.
func (s *WrappedServer) Initialize() error {
var tlsConfig *tls.Config
if serverCert != "" {
crt, err := tls.LoadX509KeyPair(serverCert, serverKey)
if s.Encryption {
if s.ServerCert == "" {
return fmt.Errorf("server cert is missing")
}
crt, err := tls.LoadX509KeyPair(s.ServerCert, s.ServerKey)
if err != nil {
ln.Close()
return nil, err
return err
}
tlsConfig = &tls.Config{
@ -58,21 +57,24 @@ func NewWrappedServer(
}
}
h := handler
var err error
s.ln, err = net.Listen(s.Network, s.Address)
if err != nil {
return err
}
h := s.Handler
h = &handlerFilterRequests{h}
h = &handlerFilterRequests{h}
h = &handlerServerHeader{h}
h = &handlerLogger{h, parent}
h = &handlerLogger{h, s.Parent}
h = &handlerExitOnPanic{h}
s := &WrappedServer{
ln: ln,
inner: &http.Server{
Handler: h,
TLSConfig: tlsConfig,
ReadHeaderTimeout: readTimeout,
ErrorLog: log.New(&nilWriter{}, "", 0),
},
s.inner = &http.Server{
Handler: h,
TLSConfig: tlsConfig,
ReadHeaderTimeout: s.ReadTimeout,
ErrorLog: log.New(&nilWriter{}, "", 0),
}
if tlsConfig != nil {
@ -81,7 +83,7 @@ func NewWrappedServer(
go s.inner.Serve(s.ln)
}
return s, nil
return nil
}
// Close closes all resources and waits for all routines to return.

View File

@ -8,23 +8,17 @@ import (
"github.com/stretchr/testify/require"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/test"
)
type testLogger struct{}
func (testLogger) Log(_ logger.Level, _ string, _ ...interface{}) {
}
func TestFilterEmptyPath(t *testing.T) {
s, err := NewWrappedServer(
"tcp",
"localhost:4555",
10*time.Second,
"",
"",
nil,
&testLogger{})
s := &WrappedServer{
Network: "tcp",
Address: "localhost:4555",
ReadTimeout: 10 * time.Second,
Parent: test.NilLogger,
}
err := s.Initialize()
require.NoError(t, err)
defer s.Close()

View File

@ -3,7 +3,6 @@ package hls
import (
_ "embed"
"errors"
"fmt"
"net"
"net/http"
gopath "path"
@ -52,32 +51,23 @@ type httpServer struct {
}
func (s *httpServer) initialize() error {
if s.encryption {
if s.serverCert == "" {
return fmt.Errorf("server cert is missing")
}
} else {
s.serverKey = ""
s.serverCert = ""
}
router := gin.New()
router.SetTrustedProxies(s.trustedProxies.ToTrustedProxies()) //nolint:errcheck
router.NoRoute(s.onRequest)
network, address := restrictnetwork.Restrict("tcp", s.address)
var err error
s.inner, err = httpp.NewWrappedServer(
network,
address,
time.Duration(s.readTimeout),
s.serverCert,
s.serverKey,
router,
s,
)
s.inner = &httpp.WrappedServer{
Network: network,
Address: address,
ReadTimeout: time.Duration(s.readTimeout),
Encryption: s.encryption,
ServerCert: s.serverCert,
ServerKey: s.serverKey,
Handler: router,
Parent: s,
}
err := s.inner.Initialize()
if err != nil {
return err
}

View File

@ -64,14 +64,14 @@ type Server struct {
Encryption bool
ServerKey string
ServerCert string
AllowOrigin string
TrustedProxies conf.IPNetworks
AlwaysRemux bool
Variant conf.HLSVariant
SegmentCount int
SegmentDuration conf.StringDuration
PartDuration conf.StringDuration
SegmentMaxSize conf.StringSize
AllowOrigin string
TrustedProxies conf.IPNetworks
Directory string
ReadTimeout conf.StringDuration
WriteQueueSize int

View File

@ -74,31 +74,23 @@ type httpServer struct {
}
func (s *httpServer) initialize() error {
if s.encryption {
if s.serverCert == "" {
return fmt.Errorf("server cert is missing")
}
} else {
s.serverKey = ""
s.serverCert = ""
}
router := gin.New()
router.SetTrustedProxies(s.trustedProxies.ToTrustedProxies()) //nolint:errcheck
router.NoRoute(s.onRequest)
network, address := restrictnetwork.Restrict("tcp", s.address)
var err error
s.inner, err = httpp.NewWrappedServer(
network,
address,
time.Duration(s.readTimeout),
s.serverCert,
s.serverKey,
router,
s,
)
s.inner = &httpp.WrappedServer{
Network: network,
Address: address,
ReadTimeout: time.Duration(s.readTimeout),
Encryption: s.encryption,
ServerCert: s.serverCert,
ServerKey: s.serverKey,
Handler: router,
Parent: s,
}
err := s.inner.Initialize()
if err != nil {
return err
}

View File

@ -24,16 +24,6 @@ writeQueueSize: 512
# This can be decreased to avoid fragmentation on networks with a low UDP MTU.
udpMaxPayloadSize: 1472
# Enable Prometheus-compatible metrics.
metrics: no
# Address of the metrics listener.
metricsAddress: :9998
# Enable pprof-compatible endpoint to monitor performances.
pprof: no
# Address of the pprof listener.
pprofAddress: :9999
# Command to run when a client connects to the server.
# This is terminated with SIGINT when a client disconnects from the server.
# The following environment variables are available:
@ -133,12 +123,73 @@ authHTTPExclude:
authJWTJWKS:
###############################################
# Global settings -> API
# Global settings -> Control API
# Enable controlling the server through the API.
# Enable controlling the server through the Control API.
api: no
# Address of the API listener.
# Address of the Control API listener.
apiAddress: :9997
# Enable TLS/HTTPS on the Control API server.
apiEncryption: no
# Path to the server key. This is needed only when encryption is yes.
# This can be generated with:
# openssl genrsa -out server.key 2048
# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
apiServerKey: server.key
# Path to the server certificate.
apiServerCert: server.crt
# Value of the Access-Control-Allow-Origin header provided in every HTTP response.
apiAllowOrigin: '*'
# List of IPs or CIDRs of proxies placed before the HTTP server.
# If the server receives a request from one of these entries, IP in logs
# will be taken from the X-Forwarded-For header.
apiTrustedProxies: []
###############################################
# Global settings -> Metrics
# Enable Prometheus-compatible metrics.
metrics: no
# Address of the metrics HTTP listener.
metricsAddress: :9998
# Enable TLS/HTTPS on the Metrics server.
metricsEncryption: no
# Path to the server key. This is needed only when encryption is yes.
# This can be generated with:
# openssl genrsa -out server.key 2048
# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
metricsServerKey: server.key
# Path to the server certificate.
metricsServerCert: server.crt
# Value of the Access-Control-Allow-Origin header provided in every HTTP response.
metricsAllowOrigin: '*'
# List of IPs or CIDRs of proxies placed before the HTTP server.
# If the server receives a request from one of these entries, IP in logs
# will be taken from the X-Forwarded-For header.
metricsTrustedProxies: []
###############################################
# Global settings -> PPROF
# Enable pprof-compatible endpoint to monitor performances.
pprof: no
# Address of the pprof listener.
pprofAddress: :9999
# Enable TLS/HTTPS on the pprof server.
pprofEncryption: no
# Path to the server key. This is needed only when encryption is yes.
# This can be generated with:
# openssl genrsa -out server.key 2048
# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
pprofServerKey: server.key
# Path to the server certificate.
pprofServerCert: server.crt
# Value of the Access-Control-Allow-Origin header provided in every HTTP response.
pprofAllowOrigin: '*'
# List of IPs or CIDRs of proxies placed before the HTTP server.
# If the server receives a request from one of these entries, IP in logs
# will be taken from the X-Forwarded-For header.
pprofTrustedProxies: []
###############################################
# Global settings -> Playback server
@ -147,6 +198,21 @@ apiAddress: :9997
playback: no
# Address of the playback server listener.
playbackAddress: :9996
# Enable TLS/HTTPS on the playback server.
playbackEncryption: no
# Path to the server key. This is needed only when encryption is yes.
# This can be generated with:
# openssl genrsa -out server.key 2048
# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
playbackServerKey: server.key
# Path to the server certificate.
playbackServerCert: server.crt
# Value of the Access-Control-Allow-Origin header provided in every HTTP response.
playbackAllowOrigin: '*'
# List of IPs or CIDRs of proxies placed before the HTTP server.
# If the server receives a request from one of these entries, IP in logs
# will be taken from the X-Forwarded-For header.
playbackTrustedProxies: []
###############################################
# Global settings -> RTSP server
@ -225,6 +291,13 @@ hlsEncryption: no
hlsServerKey: server.key
# Path to the server certificate.
hlsServerCert: server.crt
# Value of the Access-Control-Allow-Origin header provided in every HTTP response.
# This allows to play the HLS stream from an external website.
hlsAllowOrigin: '*'
# List of IPs or CIDRs of proxies placed before the HLS server.
# If the server receives a request from one of these entries, IP in logs
# will be taken from the X-Forwarded-For header.
hlsTrustedProxies: []
# By default, HLS is generated only when requested by a user.
# This option allows to generate it always, avoiding the delay between request and generation.
hlsAlwaysRemux: no
@ -252,13 +325,6 @@ hlsPartDuration: 200ms
# Maximum size of each segment.
# This prevents RAM exhaustion.
hlsSegmentMaxSize: 50M
# Value of the Access-Control-Allow-Origin header provided in every HTTP response.
# This allows to play the HLS stream from an external website.
hlsAllowOrigin: '*'
# List of IPs or CIDRs of proxies placed before the HLS server.
# If the server receives a request from one of these entries, IP in logs
# will be taken from the X-Forwarded-For header.
hlsTrustedProxies: []
# Directory in which to save segments, instead of keeping them in the RAM.
# This decreases performance, since reading from disk is less performant than
# reading from RAM, but allows to save RAM.