From d101a76e9ee73e8cbe76d046285aedadb48634be Mon Sep 17 00:00:00 2001 From: Ingo Oppermann Date: Wed, 8 Mar 2023 15:21:55 +0100 Subject: [PATCH] Change anon user to localhost user only if DisableLocalhost is set --- app/api/api.go | 13 +++--- http/handler/api/about.go | 3 +- http/middleware/iam/iam.go | 80 +++++++++++++++++++++------------ http/middleware/iam/iam_test.go | 2 +- http/server.go | 9 ++-- 5 files changed, 63 insertions(+), 44 deletions(-) diff --git a/app/api/api.go b/app/api/api.go index f4c7ca6f..f0b5d371 100644 --- a/app/api/api.go +++ b/app/api/api.go @@ -441,8 +441,7 @@ func (a *api) start() error { return fmt.Errorf("iam: %w", err) } - // Create default policies for anonymous users in order to mimic - // the behaviour before IAM + // Create default policies for anonymous users in order to mimic the behaviour before IAM iam.RemovePolicy("$anon", "$none", "", nil) iam.RemovePolicy("$localhost", "$none", "", nil) @@ -451,16 +450,14 @@ func (a *api) start() error { iam.AddPolicy("$anon", "$none", "api:/api", []string{"GET", "HEAD", "OPTIONS"}) iam.AddPolicy("$anon", "$none", "api:/api/v3/widget/process/**", []string{"GET", "HEAD", "OPTIONS"}) - iam.AddPolicy("$localhost", "$none", "api:/api", []string{"GET", "HEAD", "OPTIONS"}) - iam.AddPolicy("$localhost", "$none", "api:/api/v3/widget/process/**", []string{"GET", "HEAD", "OPTIONS"}) - if !cfg.API.Auth.Enable { iam.AddPolicy("$anon", "$none", "api:/api/**", []string{"ANY"}) iam.AddPolicy("$anon", "$none", "process:*", []string{"ANY"}) - iam.AddPolicy("$localhost", "$none", "api:/api/**", []string{"ANY"}) - iam.AddPolicy("$localhost", "$none", "process:*", []string{"ANY"}) } else { if cfg.API.Auth.DisableLocalhost { + iam.AddPolicy("$localhost", "$none", "api:/api", []string{"GET", "HEAD", "OPTIONS"}) + iam.AddPolicy("$localhost", "$none", "api:/api/v3/widget/process/**", []string{"GET", "HEAD", "OPTIONS"}) + iam.AddPolicy("$localhost", "$none", "api:/api/**", []string{"ANY"}) iam.AddPolicy("$localhost", "$none", "process:*", []string{"ANY"}) } @@ -1125,7 +1122,7 @@ func (a *api) start() error { Router: router, ReadOnly: cfg.API.ReadOnly, IAM: a.iam, - IAMDisableLocalhost: cfg.API.Auth.DisableLocalhost, + IAMDisableLocalhost: cfg.API.Auth.Enable && cfg.API.Auth.DisableLocalhost, } mainserverhandler, err := http.NewServer(serverConfig) diff --git a/http/handler/api/about.go b/http/handler/api/about.go index b80fa8cd..a9add8ca 100644 --- a/http/handler/api/about.go +++ b/http/handler/api/about.go @@ -36,9 +36,8 @@ func NewAbout(restream restream.Restreamer, auths func() []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" || (user == "$localhost" && !disablelocalhost) { + if user == "$anon" { return c.JSON(http.StatusOK, api.MinimalAbout{ App: app.Name, Auths: p.auths(), diff --git a/http/middleware/iam/iam.go b/http/middleware/iam/iam.go index 8620f6cf..4cfe7df8 100644 --- a/http/middleware/iam/iam.go +++ b/http/middleware/iam/iam.go @@ -17,8 +17,9 @@ // only allow JWT as authentication method. // // If the identity can't be detected, the identity of "$anon" is given, representing -// an anonmyous user. If the request originates from localhost, the identity will -// be $localhost, representing an anonymous user from localhost. +// an anonmyous user. If the request originates from localhost and DisableLocalhost +// is configured, the identity will be $localhost, representing an anonymous user from +// localhost. // // The domain is provided as query parameter "group" for all API requests or the // first path element after a mountpoint for filesystem requests. @@ -53,21 +54,25 @@ import ( type Config struct { // Skipper defines a function to skip middleware. - Skipper middleware.Skipper - Mounts []string - IAM iam.IAM - DisableLocalhost bool - Logger log.Logger + Skipper middleware.Skipper + Mounts []string + IAM iam.IAM + DisableLocalhost bool + WaitAfterFailedLogin bool + Logger log.Logger } var DefaultConfig = Config{ - Skipper: middleware.DefaultSkipper, - Mounts: []string{}, - IAM: nil, - DisableLocalhost: false, - Logger: nil, + Skipper: middleware.DefaultSkipper, + Mounts: []string{}, + IAM: nil, + DisableLocalhost: false, + WaitAfterFailedLogin: false, + Logger: nil, } +var realm = "datarhei-core" + type iammiddleware struct { iam iam.IAM mounts []string @@ -106,9 +111,6 @@ 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) @@ -129,14 +131,18 @@ func NewWithConfig(config Config) echo.MiddlewareFunc { if resource == "/api/login" { identity, err = mw.findIdentityFromUserpass(c) if err != nil { - time.Sleep(5 * time.Second) + if config.WaitAfterFailedLogin { + time.Sleep(5 * time.Second) + } return api.Err(http.StatusForbidden, "Forbidden", "%s", err) } if identity == nil { identity, err = mw.findIdentityFromAuth0(c) if err != nil { - time.Sleep(5 * time.Second) + if config.WaitAfterFailedLogin { + time.Sleep(5 * time.Second) + } return api.Err(http.StatusForbidden, "Forbidden", "%s", err) } } @@ -150,22 +156,28 @@ func NewWithConfig(config Config) echo.MiddlewareFunc { if resource == "/api/login/refresh" { usefor, _ := c.Get("usefor").(string) if usefor != "refresh" { - time.Sleep(5 * time.Second) + if config.WaitAfterFailedLogin { + time.Sleep(5 * time.Second) + } return api.Err(http.StatusForbidden, "Forbidden", "invalid token") } } else { usefor, _ := c.Get("usefor").(string) if usefor != "access" { - time.Sleep(5 * time.Second) + if config.WaitAfterFailedLogin { + time.Sleep(5 * time.Second) + } return api.Err(http.StatusForbidden, "Forbidden", "invalid token") } } } } - ip := c.RealIP() - if ip == "127.0.0.1" || ip == "::1" { - username = "$localhost" + if config.DisableLocalhost { + ip := c.RealIP() + if ip == "127.0.0.1" || ip == "::1" { + username = "$localhost" + } } domain = c.QueryParam("group") @@ -173,14 +185,23 @@ 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") + if err == ErrAuthRequired { + c.Response().Header().Set(echo.HeaderWWWAuthenticate, "Basic realm="+realm) return api.Err(http.StatusUnauthorized, "Unauthorized", "%s", err) - } else if err == ErrBadRequest { - return api.Err(http.StatusBadRequest, "Bad request", "%s", err) - } + } else { + if config.WaitAfterFailedLogin { + time.Sleep(5 * time.Second) + } - return api.Err(http.StatusForbidden, "Forbidden", "%s", err) + if err == ErrBadRequest { + return api.Err(http.StatusBadRequest, "Bad request", "%s", err) + } else if err == ErrUnauthorized { + c.Response().Header().Set(echo.HeaderWWWAuthenticate, "Basic realm="+realm) + return api.Err(http.StatusUnauthorized, "Unauthorized", "%s", err) + } else { + return api.Err(http.StatusForbidden, "Forbidden", "%s", err) + } + } } domain = mw.findDomainFromFilesystem(resource) @@ -208,6 +229,7 @@ func NewWithConfig(config Config) echo.MiddlewareFunc { } } +var ErrAuthRequired = errors.New("unauthorized") var ErrUnauthorized = errors.New("unauthorized") var ErrBadRequest = errors.New("bad request") @@ -224,7 +246,7 @@ func (m *iammiddleware) findIdentityFromBasicAuth(c echo.Context) (iam.IdentityV } if !m.iam.Enforce("$anon", domain, "fs:"+path, c.Request().Method) { - return nil, ErrUnauthorized + return nil, ErrAuthRequired } return nil, nil diff --git a/http/middleware/iam/iam_test.go b/http/middleware/iam/iam_test.go index ae5afd26..7d77246c 100644 --- a/http/middleware/iam/iam_test.go +++ b/http/middleware/iam/iam_test.go @@ -147,7 +147,7 @@ func TestFindDomainFromFilesystem(t *testing.T) { mw := &iammiddleware{ iam: iam, - mounts: []string{"/memfs", "/"}, + mounts: []string{"/", "/memfs"}, } domain := mw.findDomainFromFilesystem("/") diff --git a/http/server.go b/http/server.go index 00fcb406..b71a2c00 100644 --- a/http/server.go +++ b/http/server.go @@ -223,10 +223,11 @@ func NewServer(config Config) (Server, error) { } s.middleware.iam = mwiam.NewWithConfig(mwiam.Config{ - IAM: config.IAM, - Mounts: mounts, - DisableLocalhost: config.IAMDisableLocalhost, - Logger: s.logger.WithComponent("IAM"), + IAM: config.IAM, + Mounts: mounts, + DisableLocalhost: config.IAMDisableLocalhost, + WaitAfterFailedLogin: true, + Logger: s.logger.WithComponent("IAM"), }) s.handler.about = api.NewAbout(