mirror of
https://github.com/bluenviron/mediamtx
synced 2025-01-18 04:52:10 +00:00
replace publish key with specification-compliant username and password
This commit is contained in:
parent
a17cd5d51d
commit
2133737b2a
13
README.md
13
README.md
@ -13,7 +13,7 @@ Features:
|
||||
* Publish multiple streams at once, each in a separate path, that can be read by multiple users
|
||||
* Each stream can have multiple video and audio tracks
|
||||
* Supports the RTP/RTCP streaming protocol
|
||||
* Optional authentication mechanism for publishers
|
||||
* Authentication mechanism for publishers
|
||||
* Run a script when a client connects or disconnects
|
||||
* Compatible with Linux and Windows, does not require any dependency or interpreter, it's a single executable
|
||||
|
||||
@ -47,14 +47,14 @@ Precompiled binaries are available in the [release](https://github.com/aler9/rts
|
||||
|
||||
#### Publisher authentication
|
||||
|
||||
1. Start the server and set a publish key:
|
||||
1. Start the server and set a username and a password
|
||||
```
|
||||
./rtsp-simple-server --publish-key=IU23yyfaw6324
|
||||
./rtsp-simple-server --publish-user=admin --publish-pass=mypassword
|
||||
```
|
||||
|
||||
2. Only publishers which have the key will be able to publish:
|
||||
2. Only publishers which know both username and password will be able to publish:
|
||||
```
|
||||
ffmpeg -re -stream_loop -1 -i file.ts -c copy -f rtsp rtsp://localhost:8554/mystream?key=IU23yyfaw6324
|
||||
ffmpeg -re -stream_loop -1 -i file.ts -c copy -f rtsp rtsp://admin:mypassword@localhost:8554/mystream
|
||||
```
|
||||
|
||||
#### Full command-line usage
|
||||
@ -73,7 +73,8 @@ Flags:
|
||||
--rtsp-port=8554 port of the RTSP TCP listener
|
||||
--rtp-port=8000 port of the RTP UDP listener
|
||||
--rtcp-port=8001 port of the RTCP UDP listener
|
||||
--publish-key="" optional authentication key required to publish
|
||||
--publish-user="" optional username required to publish
|
||||
--publish-pass="" optional password required to publish
|
||||
--pre-script="" optional script to run on client connect
|
||||
--post-script="" optional script to run on client disconnect
|
||||
```
|
||||
|
53
client.go
53
client.go
@ -109,6 +109,7 @@ type client struct {
|
||||
state clientState
|
||||
ip net.IP
|
||||
path string
|
||||
as *gortsplib.AuthServer
|
||||
streamSdpText []byte // filled only if publisher
|
||||
streamSdpParsed *sdp.Message // filled only if publisher
|
||||
streamProtocol streamProtocol
|
||||
@ -328,6 +329,36 @@ func (c *client) handleRequest(req *gortsplib.Request) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
if c.p.publishUser != "" {
|
||||
initialRequest := false
|
||||
if c.as == nil {
|
||||
initialRequest = true
|
||||
c.as = gortsplib.NewAuthServer(c.p.publishUser, c.p.publishPass)
|
||||
}
|
||||
|
||||
err := c.as.ValidateHeader(req.Header["Authorization"], "ANNOUNCE", req.Url)
|
||||
if err != nil {
|
||||
if !initialRequest {
|
||||
c.log("ERR: Unauthorized: %s", err)
|
||||
}
|
||||
|
||||
c.writeResDeadline(&gortsplib.Response{
|
||||
StatusCode: 401,
|
||||
Status: "Unauthorized",
|
||||
Header: gortsplib.Header{
|
||||
"CSeq": []string{cseq[0]},
|
||||
"WWW-Authenticate": c.as.GenerateHeader(),
|
||||
},
|
||||
})
|
||||
|
||||
if !initialRequest {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
ct, ok := req.Header["Content-Type"]
|
||||
if !ok || len(ct) != 1 {
|
||||
c.writeResError(req, fmt.Errorf("Content-Type header missing"))
|
||||
@ -347,28 +378,6 @@ func (c *client) handleRequest(req *gortsplib.Request) bool {
|
||||
|
||||
sdpParsed, req.Content = sdpFilter(sdpParsed, req.Content)
|
||||
|
||||
if c.p.publishKey != "" {
|
||||
q, err := url.ParseQuery(ur.RawQuery)
|
||||
if err != nil {
|
||||
c.writeResError(req, fmt.Errorf("unable to parse query"))
|
||||
return false
|
||||
}
|
||||
|
||||
key, ok := q["key"]
|
||||
if !ok || len(key) != 1 || key[0] != c.p.publishKey {
|
||||
// reply with 401 and exit
|
||||
c.log("ERR: publish key wrong or missing")
|
||||
c.writeResDeadline(&gortsplib.Response{
|
||||
StatusCode: 401,
|
||||
Status: "Unauthorized",
|
||||
Header: gortsplib.Header{
|
||||
"CSeq": []string{cseq[0]},
|
||||
},
|
||||
})
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
err = func() error {
|
||||
c.p.mutex.Lock()
|
||||
defer c.p.mutex.Unlock()
|
||||
|
2
go.mod
2
go.mod
@ -5,7 +5,7 @@ go 1.13
|
||||
require (
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
|
||||
github.com/aler9/gortsplib v0.0.0-20200126152308-13da0e672306
|
||||
github.com/aler9/gortsplib v0.0.0-20200216144726-cfd906cc6eb0
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
||||
gortc.io/sdp v0.17.0
|
||||
)
|
||||
|
4
go.sum
4
go.sum
@ -2,8 +2,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafo
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/aler9/gortsplib v0.0.0-20200126152308-13da0e672306 h1:mSGMii9I9cEyw2cgyujnlaYwml9MwUkC2Ko8R0vKS6w=
|
||||
github.com/aler9/gortsplib v0.0.0-20200126152308-13da0e672306/go.mod h1:YiIgmmv0ELkWUy11Jj2h5AgfqLCpy8sIX/l9MmS8+uw=
|
||||
github.com/aler9/gortsplib v0.0.0-20200216144726-cfd906cc6eb0 h1:98xoEAoen2Up1KtSp5qbEMBKgM0qzIpKszFstcPp5+I=
|
||||
github.com/aler9/gortsplib v0.0.0-20200216144726-cfd906cc6eb0/go.mod h1:YiIgmmv0ELkWUy11Jj2h5AgfqLCpy8sIX/l9MmS8+uw=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
|
114
main.go
114
main.go
@ -48,22 +48,52 @@ func (s streamProtocol) String() string {
|
||||
}
|
||||
|
||||
type program struct {
|
||||
protocols map[streamProtocol]struct{}
|
||||
rtspPort int
|
||||
rtpPort int
|
||||
rtcpPort int
|
||||
publishKey string
|
||||
preScript string
|
||||
postScript string
|
||||
mutex sync.RWMutex
|
||||
rtspl *serverTcpListener
|
||||
rtpl *serverUdpListener
|
||||
rtcpl *serverUdpListener
|
||||
clients map[*client]struct{}
|
||||
publishers map[string]*client
|
||||
protocols map[streamProtocol]struct{}
|
||||
rtspPort int
|
||||
rtpPort int
|
||||
rtcpPort int
|
||||
publishUser string
|
||||
publishPass string
|
||||
preScript string
|
||||
postScript string
|
||||
mutex sync.RWMutex
|
||||
rtspl *serverTcpListener
|
||||
rtpl *serverUdpListener
|
||||
rtcpl *serverUdpListener
|
||||
clients map[*client]struct{}
|
||||
publishers map[string]*client
|
||||
}
|
||||
|
||||
func newProgram(protocolsStr string, rtspPort int, rtpPort int, rtcpPort int, publishKey string, preScript string, postScript string) (*program, error) {
|
||||
func newProgram() (*program, error) {
|
||||
kingpin.CommandLine.Help = "rtsp-simple-server " + Version + "\n\n" +
|
||||
"RTSP server."
|
||||
|
||||
argVersion := kingpin.Flag("version", "print rtsp-simple-server version").Bool()
|
||||
argProtocolsStr := kingpin.Flag("protocols", "supported protocols").Default("udp,tcp").String()
|
||||
argRtspPort := kingpin.Flag("rtsp-port", "port of the RTSP TCP listener").Default("8554").Int()
|
||||
argRtpPort := kingpin.Flag("rtp-port", "port of the RTP UDP listener").Default("8000").Int()
|
||||
argRtcpPort := kingpin.Flag("rtcp-port", "port of the RTCP UDP listener").Default("8001").Int()
|
||||
argPublishUser := kingpin.Flag("publish-user", "optional username required to publish").Default("").String()
|
||||
argPublishPass := kingpin.Flag("publish-pass", "optional password required to publish").Default("").String()
|
||||
argPreScript := kingpin.Flag("pre-script", "optional script to run on client connect").Default("").String()
|
||||
argPostScript := kingpin.Flag("post-script", "optional script to run on client disconnect").Default("").String()
|
||||
|
||||
kingpin.Parse()
|
||||
|
||||
version := *argVersion
|
||||
protocolsStr := *argProtocolsStr
|
||||
rtspPort := *argRtspPort
|
||||
rtpPort := *argRtpPort
|
||||
rtcpPort := *argRtcpPort
|
||||
publishUser := *argPublishUser
|
||||
publishPass := *argPublishPass
|
||||
preScript := *argPreScript
|
||||
postScript := *argPostScript
|
||||
|
||||
if version == true {
|
||||
fmt.Println("rtsp-simple-server " + Version)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if rtspPort == 0 {
|
||||
return nil, fmt.Errorf("rtsp port not provided")
|
||||
@ -102,24 +132,35 @@ func newProgram(protocolsStr string, rtspPort int, rtpPort int, rtcpPort int, pu
|
||||
return nil, fmt.Errorf("no protocols provided")
|
||||
}
|
||||
|
||||
if publishKey != "" {
|
||||
if !regexp.MustCompile("^[a-zA-Z0-9]+$").MatchString(publishKey) {
|
||||
return nil, fmt.Errorf("publish key must be alphanumeric")
|
||||
if publishUser != "" {
|
||||
if !regexp.MustCompile("^[a-zA-Z0-9]+$").MatchString(publishUser) {
|
||||
return nil, fmt.Errorf("publish username must be alphanumeric")
|
||||
}
|
||||
}
|
||||
|
||||
if publishPass != "" {
|
||||
if !regexp.MustCompile("^[a-zA-Z0-9]+$").MatchString(publishPass) {
|
||||
return nil, fmt.Errorf("publish password must be alphanumeric")
|
||||
}
|
||||
}
|
||||
|
||||
if publishUser != "" && publishPass == "" || publishUser == "" && publishPass != "" {
|
||||
return nil, fmt.Errorf("publish username and password must be both filled")
|
||||
}
|
||||
|
||||
log.Printf("rtsp-simple-server %s", Version)
|
||||
|
||||
p := &program{
|
||||
protocols: protocols,
|
||||
rtspPort: rtspPort,
|
||||
rtpPort: rtpPort,
|
||||
rtcpPort: rtcpPort,
|
||||
publishKey: publishKey,
|
||||
preScript: preScript,
|
||||
postScript: postScript,
|
||||
clients: make(map[*client]struct{}),
|
||||
publishers: make(map[string]*client),
|
||||
protocols: protocols,
|
||||
rtspPort: rtspPort,
|
||||
rtpPort: rtpPort,
|
||||
rtcpPort: rtcpPort,
|
||||
publishUser: publishUser,
|
||||
publishPass: publishPass,
|
||||
preScript: preScript,
|
||||
postScript: postScript,
|
||||
clients: make(map[*client]struct{}),
|
||||
publishers: make(map[string]*client),
|
||||
}
|
||||
|
||||
var err error
|
||||
@ -184,26 +225,7 @@ func (p *program) forwardTrack(path string, id int, flow trackFlow, frame []byte
|
||||
}
|
||||
|
||||
func main() {
|
||||
kingpin.CommandLine.Help = "rtsp-simple-server " + Version + "\n\n" +
|
||||
"RTSP server."
|
||||
|
||||
version := kingpin.Flag("version", "print rtsp-simple-server version").Bool()
|
||||
protocols := kingpin.Flag("protocols", "supported protocols").Default("udp,tcp").String()
|
||||
rtspPort := kingpin.Flag("rtsp-port", "port of the RTSP TCP listener").Default("8554").Int()
|
||||
rtpPort := kingpin.Flag("rtp-port", "port of the RTP UDP listener").Default("8000").Int()
|
||||
rtcpPort := kingpin.Flag("rtcp-port", "port of the RTCP UDP listener").Default("8001").Int()
|
||||
publishKey := kingpin.Flag("publish-key", "optional authentication key required to publish").Default("").String()
|
||||
preScript := kingpin.Flag("pre-script", "optional script to run on client connect").Default("").String()
|
||||
postScript := kingpin.Flag("post-script", "optional script to run on client disconnect").Default("").String()
|
||||
|
||||
kingpin.Parse()
|
||||
|
||||
if *version == true {
|
||||
fmt.Println("rtsp-simple-server " + Version)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
p, err := newProgram(*protocols, *rtspPort, *rtpPort, *rtcpPort, *publishKey, *preScript, *postScript)
|
||||
p, err := newProgram()
|
||||
if err != nil {
|
||||
log.Fatal("ERR: ", err)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user