feat(mediaserver): authenticate internal clients
This commit is contained in:
parent
3866d9dd07
commit
bcaf9f1cae
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"cmp"
|
"cmp"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -50,6 +51,7 @@ type Actor struct {
|
|||||||
rtmpPort int
|
rtmpPort int
|
||||||
streamKey StreamKey
|
streamKey StreamKey
|
||||||
fetchIngressStateInterval time.Duration
|
fetchIngressStateInterval time.Duration
|
||||||
|
pass string // password for the media server
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
|
|
||||||
@ -83,6 +85,7 @@ func StartActor(ctx context.Context, params StartActorParams) *Actor {
|
|||||||
rtmpPort: cmp.Or(params.RTMPPort, defaultRTMPPort),
|
rtmpPort: cmp.Or(params.RTMPPort, defaultRTMPPort),
|
||||||
streamKey: cmp.Or(params.StreamKey, defaultStreamKey),
|
streamKey: cmp.Or(params.StreamKey, defaultStreamKey),
|
||||||
fetchIngressStateInterval: cmp.Or(params.FetchIngressStateInterval, defaultFetchIngressStateInterval),
|
fetchIngressStateInterval: cmp.Or(params.FetchIngressStateInterval, defaultFetchIngressStateInterval),
|
||||||
|
pass: generatePassword(),
|
||||||
actorC: make(chan action, chanSize),
|
actorC: make(chan action, chanSize),
|
||||||
state: new(domain.Source),
|
state: new(domain.Source),
|
||||||
stateC: make(chan domain.Source, chanSize),
|
stateC: make(chan domain.Source, chanSize),
|
||||||
@ -101,18 +104,26 @@ func StartActor(ctx context.Context, params StartActorParams) *Actor {
|
|||||||
LogDestinations: []string{"stdout"},
|
LogDestinations: []string{"stdout"},
|
||||||
AuthMethod: "internal",
|
AuthMethod: "internal",
|
||||||
AuthInternalUsers: []User{
|
AuthInternalUsers: []User{
|
||||||
// TODO: tighten permissions
|
// TODO: TLS
|
||||||
{
|
{
|
||||||
User: "any",
|
User: "any",
|
||||||
IPs: []string{}, // any IP
|
IPs: []string{}, // any IP
|
||||||
Permissions: []UserPermission{
|
Permissions: []UserPermission{
|
||||||
{Action: "publish"},
|
{Action: "publish"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
User: "api",
|
||||||
|
Pass: actor.pass,
|
||||||
|
IPs: []string{}, // any IP
|
||||||
|
Permissions: []UserPermission{
|
||||||
{Action: "read"},
|
{Action: "read"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
User: "any",
|
User: "api",
|
||||||
IPs: []string{"127.0.0.1", "::1", "172.17.0.0/16"},
|
Pass: actor.pass,
|
||||||
|
IPs: []string{}, // any IP
|
||||||
Permissions: []UserPermission{{Action: "api"}},
|
Permissions: []UserPermission{{Action: "api"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -144,7 +155,7 @@ func StartActor(ctx context.Context, params StartActorParams) *Actor {
|
|||||||
container.LabelComponent: componentName,
|
container.LabelComponent: componentName,
|
||||||
},
|
},
|
||||||
Healthcheck: &typescontainer.HealthConfig{
|
Healthcheck: &typescontainer.HealthConfig{
|
||||||
Test: []string{"CMD", "curl", "-f", "http://localhost:9997/v3/paths/list"},
|
Test: []string{"CMD", "curl", "-f", actor.pathsURL()},
|
||||||
Interval: time.Second * 10,
|
Interval: time.Second * 10,
|
||||||
StartPeriod: time.Second * 2,
|
StartPeriod: time.Second * 2,
|
||||||
StartInterval: time.Second * 2,
|
StartInterval: time.Second * 2,
|
||||||
@ -317,18 +328,18 @@ func (s *Actor) rtmpURL() string {
|
|||||||
// the app network.
|
// the app network.
|
||||||
func (s *Actor) rtmpInternalURL() string {
|
func (s *Actor) rtmpInternalURL() string {
|
||||||
// Container port, not host port:
|
// Container port, not host port:
|
||||||
return fmt.Sprintf("rtmp://mediaserver:1935/%s", s.streamKey)
|
return fmt.Sprintf("rtmp://mediaserver:1935/%s?user=api&pass=%s", s.streamKey, s.pass)
|
||||||
}
|
}
|
||||||
|
|
||||||
// rtmpConnsURL returns the URL for fetching RTMP connections, accessible from
|
// rtmpConnsURL returns the URL for fetching RTMP connections, accessible from
|
||||||
// the host.
|
// the host.
|
||||||
func (s *Actor) rtmpConnsURL() string {
|
func (s *Actor) rtmpConnsURL() string {
|
||||||
return fmt.Sprintf("http://localhost:%d/v3/rtmpconns/list", s.apiPort)
|
return fmt.Sprintf("http://api:%s@localhost:%d/v3/rtmpconns/list", s.pass, s.apiPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pathsURL returns the URL for fetching paths, accessible from the host.
|
// pathsURL returns the URL for fetching paths, accessible from the host.
|
||||||
func (s *Actor) pathsURL() string {
|
func (s *Actor) pathsURL() string {
|
||||||
return fmt.Sprintf("http://localhost:%d/v3/paths/list", s.apiPort)
|
return fmt.Sprintf("http://api:%s@localhost:%d/v3/paths/list", s.pass, s.apiPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
// shortID returns the first 12 characters of the given container ID.
|
// shortID returns the first 12 characters of the given container ID.
|
||||||
@ -338,3 +349,12 @@ func shortID(id string) string {
|
|||||||
}
|
}
|
||||||
return id[:12]
|
return id[:12]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generatePassword securely generates a random password suitable for
|
||||||
|
// authenticating media server endpoints.
|
||||||
|
func generatePassword() string {
|
||||||
|
const lenBytes = 32
|
||||||
|
p := make([]byte, lenBytes)
|
||||||
|
_, _ = rand.Read(p)
|
||||||
|
return fmt.Sprintf("%x", []byte(p))
|
||||||
|
}
|
||||||
|
12
internal/mediaserver/actor_test.go
Normal file
12
internal/mediaserver/actor_test.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package mediaserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGeneratePassword(t *testing.T) {
|
||||||
|
assert.Len(t, generatePassword(), 64)
|
||||||
|
assert.NotEqual(t, generatePassword(), generatePassword())
|
||||||
|
}
|
@ -36,6 +36,7 @@ type UserPermission struct {
|
|||||||
// User represents a user configuration in MediaMTX.
|
// User represents a user configuration in MediaMTX.
|
||||||
type User struct {
|
type User struct {
|
||||||
User string `yaml:"user,omitempty"`
|
User string `yaml:"user,omitempty"`
|
||||||
|
Pass string `yaml:"pass,omitempty"`
|
||||||
IPs []string `yaml:"ips,omitempty"`
|
IPs []string `yaml:"ips,omitempty"`
|
||||||
Permissions []UserPermission `yaml:"permissions,omitempty"`
|
Permissions []UserPermission `yaml:"permissions,omitempty"`
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user