Fix basic auth, disable localhost, replace template func

This commit is contained in:
Ingo Oppermann
2023-02-17 17:27:39 +01:00
parent 2df83c8032
commit 8215c20ae6
6 changed files with 118 additions and 52 deletions

View File

@@ -459,11 +459,11 @@ func (a *api) start() error {
iam.AddPolicy("$anon", "$none", "process:*", "ANY")
iam.AddPolicy("$localhost", "$none", "api:/api/**", "ANY")
iam.AddPolicy("$localhost", "$none", "process:*", "ANY")
}
if cfg.API.Auth.DisableLocalhost {
iam.AddPolicy("$localhost", "$none", "api:/api/**", "ANY")
iam.AddPolicy("$localhost", "$none", "process:*", "ANY")
} else {
if cfg.API.Auth.DisableLocalhost {
iam.AddPolicy("$localhost", "$none", "api:/api/**", "ANY")
iam.AddPolicy("$localhost", "$none", "process:*", "ANY")
}
}
if !cfg.Storage.Memory.Auth.Enable {
@@ -471,11 +471,11 @@ func (a *api) start() error {
}
if cfg.RTMP.Enable && len(cfg.RTMP.Token) == 0 {
iam.AddPolicy("$anon", "$none", "rtmp:/**", "PUBLISH|PLAY")
iam.AddPolicy("$anon", "$none", "rtmp:/**", "ANY")
}
if cfg.SRT.Enable && len(cfg.SRT.Token) == 0 {
iam.AddPolicy("$anon", "$none", "srt:**", "PUBLISH|PLAY")
iam.AddPolicy("$anon", "$none", "srt:**", "ANY")
}
a.iam = iam
@@ -669,7 +669,15 @@ func (a *api) start() error {
}
template += "/{name}"
if identity, _ := a.iam.GetIdentity(config.Owner); identity != nil {
var identity iam.IdentityVerifier = nil
if len(config.Owner) == 0 {
identity, _ = a.iam.GetDefaultIdentity()
} else {
identity, _ = a.iam.GetIdentity(config.Owner)
}
if identity != nil {
template += "/" + identity.GetServiceToken()
}
@@ -687,7 +695,15 @@ func (a *api) start() error {
template += ",mode:publish"
}
if identity, _ := a.iam.GetIdentity(config.Owner); identity != nil {
var identity iam.IdentityVerifier = nil
if len(config.Owner) == 0 {
identity, _ = a.iam.GetDefaultIdentity()
} else {
identity, _ = a.iam.GetIdentity(config.Owner)
}
if identity != nil {
template += ",token:" + identity.GetServiceToken()
}
@@ -1102,13 +1118,14 @@ func (a *api) start() error {
Cors: http.CorsConfig{
Origins: cfg.Storage.CORS.Origins,
},
RTMP: a.rtmpserver,
SRT: a.srtserver,
Config: a.config.store,
Sessions: a.sessions,
Router: router,
ReadOnly: cfg.API.ReadOnly,
IAM: a.iam,
RTMP: a.rtmpserver,
SRT: a.srtserver,
Config: a.config.store,
Sessions: a.sessions,
Router: router,
ReadOnly: cfg.API.ReadOnly,
IAM: a.iam,
IAMDisableLocalhost: cfg.API.Auth.DisableLocalhost,
}
mainserverhandler, err := http.NewServer(serverConfig)

View File

@@ -15,11 +15,11 @@ import (
// about the API version and build infos.
type AboutHandler struct {
restream restream.Restreamer
auths []string
auths func() []string
}
// NewAbout returns a new About type
func NewAbout(restream restream.Restreamer, auths []string) *AboutHandler {
func NewAbout(restream restream.Restreamer, auths func() []string) *AboutHandler {
return &AboutHandler{
restream: restream,
auths: auths,
@@ -36,11 +36,12 @@ func NewAbout(restream restream.Restreamer, auths []string) *AboutHandler {
// @Router /api [get]
func (p *AboutHandler) About(c echo.Context) error {
user, _ := c.Get("user").(string)
disablelocalhost, _ := c.Get("disablelocalhost").(bool)
if user == "$anon" {
if user == "$anon" || (user == "$localhost" && !disablelocalhost) {
return c.JSON(http.StatusOK, api.MinimalAbout{
App: app.Name,
Auths: p.auths,
Auths: p.auths(),
Version: api.VersionMinimal{
Number: app.Version.MajorString(),
},
@@ -52,7 +53,7 @@ func (p *AboutHandler) About(c echo.Context) error {
about := api.About{
App: app.Name,
Name: p.restream.Name(),
Auths: p.auths,
Auths: p.auths(),
ID: p.restream.ID(),
CreatedAt: createdAt.Format(time.RFC3339),
Uptime: uint64(time.Since(createdAt).Seconds()),

View File

@@ -19,7 +19,7 @@ func getDummyAboutRouter() (*echo.Echo, error) {
return nil, err
}
handler := NewAbout(rs, []string{})
handler := NewAbout(rs, func() []string { return []string{} })
router.Add("GET", "/", handler.About)

View File

@@ -33,6 +33,7 @@ package iam
import (
"encoding/base64"
"errors"
"fmt"
"net/http"
"path/filepath"
@@ -52,17 +53,19 @@ import (
type Config struct {
// Skipper defines a function to skip middleware.
Skipper middleware.Skipper
Mounts []string
IAM iam.IAM
Logger log.Logger
Skipper middleware.Skipper
Mounts []string
IAM iam.IAM
DisableLocalhost bool
Logger log.Logger
}
var DefaultConfig = Config{
Skipper: middleware.DefaultSkipper,
Mounts: []string{},
IAM: nil,
Logger: nil,
Skipper: middleware.DefaultSkipper,
Mounts: []string{},
IAM: nil,
DisableLocalhost: false,
Logger: nil,
}
type iammiddleware struct {
@@ -95,6 +98,9 @@ func NewWithConfig(config Config) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Set("disablelocalhost", config.DisableLocalhost)
if config.Skipper(c) {
c.Set("user", "$anon")
return next(c)
@@ -159,6 +165,11 @@ func NewWithConfig(config Config) echo.MiddlewareFunc {
} else {
identity, err = mw.findIdentityFromBasicAuth(c)
if err != nil {
if err == ErrUnauthorized {
c.Response().Header().Set(echo.HeaderWWWAuthenticate, "Basic realm=datarhei-core")
return api.Err(http.StatusUnauthorized, "Unauthorized", "%s", err)
}
return api.Err(http.StatusForbidden, "Forbidden", "%s", err)
}
@@ -187,12 +198,30 @@ func NewWithConfig(config Config) echo.MiddlewareFunc {
}
}
var ErrUnauthorized = errors.New("unauthorized")
func (m *iammiddleware) findIdentityFromBasicAuth(c echo.Context) (iam.IdentityVerifier, error) {
basic := "basic"
auth := c.Request().Header.Get(echo.HeaderAuthorization)
l := len(basic)
if len(auth) == 0 {
method := c.Request().Method
if method == http.MethodGet || method == http.MethodHead || method == http.MethodOptions {
return nil, nil
}
path := c.Request().URL.Path
for _, m := range m.mounts {
if m == "/" {
continue
}
if strings.HasPrefix(path, m+"/") {
return nil, ErrUnauthorized
}
}
return nil, nil
}

View File

@@ -75,24 +75,25 @@ import (
var ListenAndServe = http.ListenAndServe
type Config struct {
Logger log.Logger
LogBuffer log.BufferWriter
Restream restream.Restreamer
Metrics monitor.HistoryReader
Prometheus prometheus.Reader
MimeTypesFile string
Filesystems []fs.FS
IPLimiter net.IPLimiter
Profiling bool
Cors CorsConfig
RTMP rtmp.Server
SRT srt.Server
Config cfgstore.Store
Cache cache.Cacher
Sessions session.RegistryReader
Router router.Router
ReadOnly bool
IAM iam.IAM
Logger log.Logger
LogBuffer log.BufferWriter
Restream restream.Restreamer
Metrics monitor.HistoryReader
Prometheus prometheus.Reader
MimeTypesFile string
Filesystems []fs.FS
IPLimiter net.IPLimiter
Profiling bool
Cors CorsConfig
RTMP rtmp.Server
SRT srt.Server
Config cfgstore.Store
Cache cache.Cacher
Sessions session.RegistryReader
Router router.Router
ReadOnly bool
IAM iam.IAM
IAMDisableLocalhost bool
}
type CorsConfig struct {
@@ -221,14 +222,15 @@ func NewServer(config Config) (Server, error) {
}
s.middleware.iam = mwiam.NewWithConfig(mwiam.Config{
IAM: config.IAM,
Mounts: mounts,
Logger: s.logger.WithComponent("IAM"),
IAM: config.IAM,
Mounts: mounts,
DisableLocalhost: config.IAMDisableLocalhost,
Logger: s.logger.WithComponent("IAM"),
})
s.handler.about = api.NewAbout(
config.Restream,
config.IAM.Validators(),
func() []string { return config.IAM.Validators() },
)
s.handler.jwt = api.NewJWT(config.IAM)

View File

@@ -71,6 +71,23 @@ func TestIdentity(t *testing.T) {
require.False(t, identity.IsSuperuser())
identity.user.Superuser = true
require.True(t, identity.IsSuperuser())
dummyfs, err := fs.NewMemFilesystem(fs.MemConfig{})
require.NoError(t, err)
im, err := NewIdentityManager(IdentityConfig{
FS: dummyfs,
Superuser: User{Name: "foobar"},
JWTRealm: "test-realm",
JWTSecret: "abc123",
Logger: nil,
})
require.NoError(t, err)
require.NotNil(t, im)
id, err := im.GetVerifier("unknown")
require.Error(t, err)
require.Nil(t, id)
}
func TestDefaultIdentity(t *testing.T) {