mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-18 14:47:38 +01:00
Merge pull request #10542 from wy65701436/middleware-regtoken
add regtoken middleware in new v2 handler
This commit is contained in:
commit
36661af85e
66
src/server/middleware/regtoken/regtoken.go
Normal file
66
src/server/middleware/regtoken/regtoken.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package regtoken
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/docker/distribution/registry/auth"
|
||||||
|
"github.com/goharbor/harbor/src/common/rbac"
|
||||||
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
|
pkg_token "github.com/goharbor/harbor/src/pkg/token"
|
||||||
|
"github.com/goharbor/harbor/src/pkg/token/claims/registry"
|
||||||
|
"github.com/goharbor/harbor/src/server/middleware"
|
||||||
|
reg_err "github.com/goharbor/harbor/src/server/registry/error"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Middleware parses the docker pull bearer token and check whether it's a scanner pull.
|
||||||
|
func Middleware() func(http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
err := parseToken(req)
|
||||||
|
if err != nil {
|
||||||
|
reg_err.Handle(rw, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next.ServeHTTP(rw, req)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseToken(req *http.Request) error {
|
||||||
|
mf, ok := middleware.ManifestInfoFromContext(req.Context())
|
||||||
|
if !ok {
|
||||||
|
return errors.New("cannot get the manifest information from request context")
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(req.Header.Get("Authorization"), " ")
|
||||||
|
if len(parts) != 2 || strings.ToLower(parts[0]) != "bearer" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rawToken := parts[1]
|
||||||
|
opt := pkg_token.DefaultTokenOptions()
|
||||||
|
regTK, err := pkg_token.Parse(opt, rawToken, ®istry.Claim{})
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to decode reg token: %v, the error is skipped and round the request to native registry.", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
accessItems := []auth.Access{}
|
||||||
|
accessItems = append(accessItems, auth.Access{
|
||||||
|
Resource: auth.Resource{
|
||||||
|
Type: rbac.ResourceRepository.String(),
|
||||||
|
Name: mf.Repository,
|
||||||
|
},
|
||||||
|
Action: rbac.ActionScannerPull.String(),
|
||||||
|
})
|
||||||
|
|
||||||
|
accessSet := regTK.Claims.(*registry.Claim).GetAccess()
|
||||||
|
for _, access := range accessItems {
|
||||||
|
if accessSet.Contains(access) {
|
||||||
|
*req = *(req.WithContext(middleware.NewScannerPullContext(req.Context(), true)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
64
src/server/middleware/regtoken/regtoken_test.go
Normal file
64
src/server/middleware/regtoken/regtoken_test.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package regtoken
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/goharbor/harbor/src/core/middlewares/util"
|
||||||
|
"github.com/goharbor/harbor/src/server/middleware"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HandlerSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func doPullManifestRequest(projectName, name, tag string, next ...http.HandlerFunc) int {
|
||||||
|
repository := fmt.Sprintf("%s/%s", projectName, name)
|
||||||
|
|
||||||
|
url := fmt.Sprintf("/v2/%s/manifests/%s", repository, tag)
|
||||||
|
req, _ := http.NewRequest("GET", url, nil)
|
||||||
|
|
||||||
|
token := "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IkNWUTc6REM3NTpHVEROOkxTTUs6VUFJTjpIUUVWOlZVSDQ6Q0lRRDpRV01COlM0Qzc6U0c0STpGRUhYIn0.eyJpc3MiOiJoYXJib3ItdG9rZW4taXNzdWVyIiwic3ViIjoicm9ib3QkZGVtbzExIiwiYXVkIjoiaGFyYm9yLXJlZ2lzdHJ5IiwiZXhwIjoxNTcxNzYzOTI2LCJuYmYiOjE1NzE3NjM4NjYsImlhdCI6MTU3MTc2Mzg2NiwianRpIjoiTnRaZWx4Z01KTUU1MXlEMCIsImFjY2VzcyI6W3sidHlwZSI6InJlcG9zaXRvcnkiLCJuYW1lIjoibGlicmFyeS9oZWxsby13b3JsZCIsImFjdGlvbnMiOlsicHVzaCIsIioiLCJwdWxsIiwic2Nhbm5lcnB1bGwiXX1dfQ.GlWuvtoxmChnpvbWaG5901Z9-g63DrzyNUREWlDbR5gnNeuOKjLNyE4QpogAQKx2yYtcGxbqNL3VfJkExJ_gMS0Qw8e10utGOawwqD4oqf_J06eKq4HzpZJengZfcjMA4g2RoeOlqdVdwimB_PdX9vkBO1od0wX0Cc2v0p2w5TkibcThKRoeLeVs2oRewkKLuVHNSM8wwRIlAvpWJuNnvRCFlHRkLcZM_KpGXqT7H-PZETTisWCi1pMxeYEwIsDFLlTKdV8LaiDeDmH-RaLOsuyAySYEW9Ynk5K3P_dUl2c_SYQXloPyi0MvXxSn6EWE4eHF2oQDM_SvIzR9sOVB8TtjMjKKMQ4yr_mqgMcfEpnInJATExBR56wmxNdLESncHl8rUYCe2jCjQFuR9NGQA1tGdjI4NoBN-OVD0dBs9rm_mkb2tgD-3gEhyzAw6hg0uzDsF7bj5Aq8scoi42UurhX2bZM89s4-TWBp4DWuBG0HDiwpOiBvB3RMm6MpQxsqrl0hQm_WH18L6QCknAW2e3d_6DJWJ0eBzISrhDr7LkqJKl1J8pv4zqoh_EUVeLyzTmjEULm-VbnpVF4wW5yTLF3S6F7Ox4vwWtVfi1XQNVOcJDB3VPUsRgiTTuCW-ZGcBLw-OdIcwaJ3T_QZkEjUw1f6i1JcGa0Mpgl83aLiSdQ 0xc0003c77c0 map[alg:RS256 kid:CVQ7:DC75:GTDN:LSMK:UAIN:HQEV:VUH4:CIQD:QWMB:S4C7:SG4I:FEHX typ:JWT] 0xc000496000 GlWuvtoxmChnpvbWaG5901Z9-g63DrzyNUREWlDbR5gnNeuOKjLNyE4QpogAQKx2yYtcGxbqNL3VfJkExJ_gMS0Qw8e10utGOawwqD4oqf_J06eKq4HzpZJengZfcjMA4g2RoeOlqdVdwimB_PdX9vkBO1od0wX0Cc2v0p2w5TkibcThKRoeLeVs2oRewkKLuVHNSM8wwRIlAvpWJuNnvRCFlHRkLcZM_KpGXqT7H-PZETTisWCi1pMxeYEwIsDFLlTKdV8LaiDeDmH-RaLOsuyAySYEW9Ynk5K3P_dUl2c_SYQXloPyi0MvXxSn6EWE4eHF2oQDM_SvIzR9sOVB8TtjMjKKMQ4yr_mqgMcfEpnInJATExBR56wmxNdLESncHl8rUYCe2jCjQFuR9NGQA1tGdjI4NoBN-OVD0dBs9rm_mkb2tgD-3gEhyzAw6hg0uzDsF7bj5Aq8scoi42UurhX2bZM89s4-TWBp4DWuBG0HDiwpOiBvB3RMm6MpQxsqrl0hQm_WH18L6QCknAW2e3d_6DJWJ0eBzISrhDr7LkqJKl1J8pv4zqoh_EUVeLyzTmjEULm-VbnpVF4wW5yTLF3S6F7Ox4vwWtVfi1XQNVOcJDB3VPUsRgiTTuCW-ZGcBLw-OdIcwaJ3T_QZkEjUw1f6i1JcGa0Mpgl83aLiSdQ"
|
||||||
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
mfInfo := &middleware.ManifestInfo{
|
||||||
|
ProjectID: 1,
|
||||||
|
Repository: name,
|
||||||
|
Tag: tag,
|
||||||
|
Digest: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
var n http.HandlerFunc
|
||||||
|
if len(next) > 0 {
|
||||||
|
n = next[0]
|
||||||
|
} else {
|
||||||
|
n = func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*req = *(req.WithContext(middleware.NewManifestInfoContext(req.Context(), mfInfo)))
|
||||||
|
|
||||||
|
h := Middleware()(http.HandlerFunc(n))
|
||||||
|
h.ServeHTTP(util.NewCustomResponseWriter(rr), req)
|
||||||
|
|
||||||
|
return rr.Code
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *HandlerSuite) TestPullManifest() {
|
||||||
|
code1 := doPullManifestRequest("library", "photon", "release-1.10")
|
||||||
|
suite.Equal(http.StatusNotFound, code1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
if result := m.Run(); result != 0 {
|
||||||
|
os.Exit(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunHandlerSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(HandlerSuite))
|
||||||
|
}
|
@ -9,6 +9,8 @@ type contextKey string
|
|||||||
const (
|
const (
|
||||||
// manifestInfoKey the context key for manifest info
|
// manifestInfoKey the context key for manifest info
|
||||||
manifestInfoKey = contextKey("ManifestInfo")
|
manifestInfoKey = contextKey("ManifestInfo")
|
||||||
|
// ScannerPullCtxKey the context key for robot account to bypass the pull policy check.
|
||||||
|
ScannerPullCtxKey = contextKey("ScannerPullCheck")
|
||||||
)
|
)
|
||||||
|
|
||||||
// ManifestInfo ...
|
// ManifestInfo ...
|
||||||
@ -29,3 +31,8 @@ func ManifestInfoFromContext(ctx context.Context) (*ManifestInfo, bool) {
|
|||||||
info, ok := ctx.Value(manifestInfoKey).(*ManifestInfo)
|
info, ok := ctx.Value(manifestInfoKey).(*ManifestInfo)
|
||||||
return info, ok
|
return info, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewScannerPullContext returns context with policy check info
|
||||||
|
func NewScannerPullContext(ctx context.Context, scannerPull bool) context.Context {
|
||||||
|
return context.WithValue(ctx, ScannerPullCtxKey, scannerPull)
|
||||||
|
}
|
||||||
|
@ -19,6 +19,8 @@ import (
|
|||||||
pkg_repo "github.com/goharbor/harbor/src/pkg/repository"
|
pkg_repo "github.com/goharbor/harbor/src/pkg/repository"
|
||||||
pkg_tag "github.com/goharbor/harbor/src/pkg/tag"
|
pkg_tag "github.com/goharbor/harbor/src/pkg/tag"
|
||||||
"github.com/goharbor/harbor/src/server/middleware"
|
"github.com/goharbor/harbor/src/server/middleware"
|
||||||
|
"github.com/goharbor/harbor/src/server/middleware/manifestinfo"
|
||||||
|
"github.com/goharbor/harbor/src/server/middleware/regtoken"
|
||||||
"github.com/goharbor/harbor/src/server/registry/blob"
|
"github.com/goharbor/harbor/src/server/registry/blob"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
@ -49,7 +51,7 @@ func New(url *url.URL) http.Handler {
|
|||||||
// handle manifest
|
// handle manifest
|
||||||
// TODO maybe we should split it into several sub routers based on the method
|
// TODO maybe we should split it into several sub routers based on the method
|
||||||
manifestRouter := rootRouter.Path("/v2/{name:.*}/manifests/{reference}").Subrouter()
|
manifestRouter := rootRouter.Path("/v2/{name:.*}/manifests/{reference}").Subrouter()
|
||||||
manifestRouter.NewRoute().Methods(http.MethodGet).Handler(manifest.NewHandler(project.Mgr, proxy))
|
manifestRouter.NewRoute().Methods(http.MethodGet).Handler(middleware.WithMiddlewares(manifest.NewHandler(project.Mgr, proxy), manifestinfo.Middleware(), regtoken.Middleware()))
|
||||||
manifestRouter.NewRoute().Methods(http.MethodHead).Handler(manifest.NewHandler(project.Mgr, proxy))
|
manifestRouter.NewRoute().Methods(http.MethodHead).Handler(manifest.NewHandler(project.Mgr, proxy))
|
||||||
manifestRouter.NewRoute().Methods(http.MethodPut).Handler(middleware.WithMiddlewares(manifest.NewHandler(project.Mgr, proxy), middleware.ReadOnly()))
|
manifestRouter.NewRoute().Methods(http.MethodPut).Handler(middleware.WithMiddlewares(manifest.NewHandler(project.Mgr, proxy), middleware.ReadOnly()))
|
||||||
manifestRouter.NewRoute().Methods(http.MethodDelete).Handler(middleware.WithMiddlewares(manifest.NewHandler(project.Mgr, proxy), middleware.ReadOnly()))
|
manifestRouter.NewRoute().Methods(http.MethodDelete).Handler(middleware.WithMiddlewares(manifest.NewHandler(project.Mgr, proxy), middleware.ReadOnly()))
|
||||||
|
Loading…
Reference in New Issue
Block a user