[AUTHZ] Move Blocking of Session Tokens for GIT to Middleware (#646)

This commit is contained in:
Johannes Batzill 2023-10-03 22:15:21 +00:00 committed by Harness
parent d4e221f539
commit 9bede77cb9
6 changed files with 83 additions and 32 deletions

View File

@ -28,14 +28,14 @@ import (
// Attempt returns an http.HandlerFunc middleware that authenticates
// the http.Request if authentication payload is available.
func Attempt(authenticator authn.Authenticator, sourceRouter authn.SourceRouter) func(http.Handler) http.Handler {
return performAuthentication(authenticator, false, sourceRouter)
func Attempt(authenticator authn.Authenticator) func(http.Handler) http.Handler {
return performAuthentication(authenticator, false)
}
// Required returns an http.HandlerFunc middleware that authenticates
// the http.Request and fails the request if no auth data was available.
func Required(authenticator authn.Authenticator, sourceRouter authn.SourceRouter) func(http.Handler) http.Handler {
return performAuthentication(authenticator, true, sourceRouter)
func Required(authenticator authn.Authenticator) func(http.Handler) http.Handler {
return performAuthentication(authenticator, true)
}
// performAuthentication returns an http.HandlerFunc middleware that authenticates
@ -43,14 +43,14 @@ func Required(authenticator authn.Authenticator, sourceRouter authn.SourceRouter
// Depending on whether it is required or not, the request will be failed.
func performAuthentication(
authenticator authn.Authenticator,
required bool, sourceRouter authn.SourceRouter,
required bool,
) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
log := hlog.FromRequest(r)
session, err := authenticator.Authenticate(r, sourceRouter)
session, err := authenticator.Authenticate(r)
if err != nil {
if !errors.Is(err, authn.ErrNoAuthData) {
// log error to help with investigating any auth related errors

View File

@ -0,0 +1,48 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package authz
import (
"net/http"
"github.com/harness/gitness/app/api/render"
"github.com/harness/gitness/app/api/request"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types/enum"
"github.com/rs/zerolog/log"
)
// BlockSessionToken blocks any request that uses a session token for authentication.
// NOTE: Major use case as of now is blocking usage of session tokens with git.
func BlockSessionToken(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// only block if auth data was available and it's based on a session token.
if session, ok := request.AuthSessionFrom(ctx); ok {
if tokenMetadata, ok := session.Metadata.(*auth.TokenMetadata); ok &&
tokenMetadata.TokenType == enum.TokenTypeSession {
log.Ctx(ctx).Warn().Msg("blocking git operation - session tokens are not allowed for usage with git")
// NOTE: Git doesn't print the error message, so just return default 401 Unauthorized.
render.Unauthorized(w)
return
}
}
next.ServeHTTP(w, r)
})
}

View File

@ -29,13 +29,6 @@ var (
ErrNotAcceptedAuthMethod = errors.New("the request contains auth method that is not accepted by the Authorizer")
)
type SourceRouter string
const (
SourceRouterAPI SourceRouter = "api"
SourceRouterGIT SourceRouter = "git"
)
// Authenticator is an abstraction of an entity that's responsible for authenticating principals
// that are making calls via HTTP.
type Authenticator interface {
@ -46,5 +39,5 @@ type Authenticator interface {
* (nil, ErrNoAuthData) - request doesn't contain any auth data
* (nil, err) - request contains auth data but verification failed
*/
Authenticate(r *http.Request, sourceRouter SourceRouter) (*auth.Session, error)
Authenticate(r *http.Request) (*auth.Session, error)
}

View File

@ -51,7 +51,7 @@ func NewTokenAuthenticator(
}
}
func (a *JWTAuthenticator) Authenticate(r *http.Request, sourceRouter SourceRouter) (*auth.Session, error) {
func (a *JWTAuthenticator) Authenticate(r *http.Request) (*auth.Session, error) {
ctx := r.Context()
str := extractToken(r, a.cookieName)

View File

@ -126,7 +126,7 @@ func NewAPIHandler(
r.Use(corsHandler(config))
// for now always attempt auth - enforced per operation.
r.Use(middlewareauthn.Attempt(authenticator, authn.SourceRouterAPI))
r.Use(middlewareauthn.Attempt(authenticator))
r.Route("/v1", func(r chi.Router) {
setupRoutesV1(r, config, repoCtrl, executionCtrl, triggerCtrl, logCtrl, pipelineCtrl,

View File

@ -21,6 +21,7 @@ import (
"github.com/harness/gitness/app/api/controller/repo"
handlerrepo "github.com/harness/gitness/app/api/handler/repo"
middlewareauthn "github.com/harness/gitness/app/api/middleware/authn"
middlewareauthz "github.com/harness/gitness/app/api/middleware/authz"
"github.com/harness/gitness/app/api/middleware/encode"
"github.com/harness/gitness/app/api/middleware/logging"
"github.com/harness/gitness/app/api/request"
@ -64,26 +65,35 @@ func NewGitHandler(
r.Use(logging.HLogRequestIDHandler())
r.Use(logging.HLogAccessLogHandler())
// for now always attempt auth - enforced per operation.
r.Use(middlewareauthn.Attempt(authenticator))
r.Route(fmt.Sprintf("/{%s}", request.PathParamRepoRef), func(r chi.Router) {
r.Use(middlewareauthn.Attempt(authenticator, authn.SourceRouterGIT))
// routes that aren't coming from git
r.Group(func(r chi.Router) {
// redirect to repo (meant for UI, in case user navigates to clone url in browser)
r.Get("/", handlerrepo.HandleGitRedirect(repoCtrl, urlProvider))
})
// redirect to repo (meant for UI, in case user navigates to clone url in browser)
r.Get("/", handlerrepo.HandleGitRedirect(repoCtrl, urlProvider))
// routes that are coming from git (where we block the usage of session tokens)
r.Group(func(r chi.Router) {
r.Use(middlewareauthz.BlockSessionToken)
// smart protocol
r.Handle("/git-upload-pack", handlerrepo.GetUploadPack(client, urlProvider, repoStore, authorizer))
r.Post("/git-receive-pack", handlerrepo.PostReceivePack(client, urlProvider, repoStore, authorizer))
r.Get("/info/refs", handlerrepo.GetInfoRefs(client, repoStore, authorizer))
// smart protocol
r.Handle("/git-upload-pack", handlerrepo.GetUploadPack(client, urlProvider, repoStore, authorizer))
r.Post("/git-receive-pack", handlerrepo.PostReceivePack(client, urlProvider, repoStore, authorizer))
r.Get("/info/refs", handlerrepo.GetInfoRefs(client, repoStore, authorizer))
// dumb protocol
r.Get("/HEAD", stubGitHandler(repoStore))
r.Get("/objects/info/alternates", stubGitHandler(repoStore))
r.Get("/objects/info/http-alternates", stubGitHandler(repoStore))
r.Get("/objects/info/packs", stubGitHandler(repoStore))
r.Get("/objects/info/{file:[^/]*}", stubGitHandler(repoStore))
r.Get("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", stubGitHandler(repoStore))
r.Get("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", stubGitHandler(repoStore))
r.Get("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", stubGitHandler(repoStore))
// dumb protocol
r.Get("/HEAD", stubGitHandler(repoStore))
r.Get("/objects/info/alternates", stubGitHandler(repoStore))
r.Get("/objects/info/http-alternates", stubGitHandler(repoStore))
r.Get("/objects/info/packs", stubGitHandler(repoStore))
r.Get("/objects/info/{file:[^/]*}", stubGitHandler(repoStore))
r.Get("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", stubGitHandler(repoStore))
r.Get("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", stubGitHandler(repoStore))
r.Get("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", stubGitHandler(repoStore))
})
})
// wrap router in git path encoder.