mirror of
https://github.com/project-zot/zot.git
synced 2026-06-17 21:17:58 +08:00
708adf63d4
* fix: CVE-2025-30204 - golang-jwt DoS vulnerability via excessive memory allocation Signed-off-by: Asgeir Nilsen <asgeir@twingine.no> * fix: linting Signed-off-by: Asgeir Nilsen <asgeir@twingine.no> * chore: update project-zot/mockoidc to remove golang-jwt v3 Signed-off-by: Asgeir Nilsen <asgeir@twingine.no> * test: Add more tests for bearer tokens Signed-off-by: Asgeir Nilsen <asgeir@twingine.no> * fix: Rewrite tests to remove MakeAuthTestServerLegacy Signed-off-by: Asgeir Nilsen <asgeir@twingine.no> --------- Signed-off-by: Asgeir Nilsen <asgeir@twingine.no>
128 lines
2.6 KiB
Go
128 lines
2.6 KiB
Go
package auth
|
|
|
|
import (
|
|
"crypto"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"github.com/mitchellh/mapstructure"
|
|
|
|
"zotregistry.dev/zot/v2/pkg/api"
|
|
)
|
|
|
|
type (
|
|
AccessTokenResponse struct {
|
|
AccessToken string `json:"access_token"` //nolint:tagliatelle // token format
|
|
}
|
|
|
|
AuthHeader struct {
|
|
Realm string
|
|
Service string
|
|
Scope string
|
|
}
|
|
)
|
|
|
|
func MakeAuthTestServer(serverKey, signAlg string, unauthorizedNamespace string) *httptest.Server {
|
|
signingKey := loadPrivateKeyFromFile(serverKey)
|
|
signingMethod := jwt.GetSigningMethod(signAlg)
|
|
|
|
authTestServer := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
|
if request.Method != http.MethodGet {
|
|
response.WriteHeader(http.StatusMethodNotAllowed)
|
|
|
|
return
|
|
}
|
|
|
|
var access []api.ResourceAccess
|
|
|
|
scope := request.URL.Query().Get("scope")
|
|
if scope != "" {
|
|
parts := strings.Split(scope, ":")
|
|
name := parts[1]
|
|
actions := strings.Split(parts[2], ",")
|
|
|
|
if name == unauthorizedNamespace {
|
|
actions = []string{}
|
|
}
|
|
|
|
access = []api.ResourceAccess{
|
|
{
|
|
Name: name,
|
|
Type: "repository",
|
|
Actions: actions,
|
|
},
|
|
}
|
|
}
|
|
|
|
now := time.Now()
|
|
claims := api.ClaimsWithAccess{
|
|
Access: access,
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(now.Add(time.Minute * 1)),
|
|
IssuedAt: jwt.NewNumericDate(now),
|
|
Issuer: "Zot",
|
|
Audience: []string{"Zot Registry"},
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(signingMethod, claims)
|
|
|
|
signedString, err := token.SignedString(signingKey)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
response.Header().Set("Content-Type", "application/json")
|
|
fmt.Fprintf(response, `{"access_token": "%s"}`, signedString)
|
|
}))
|
|
|
|
return authTestServer
|
|
}
|
|
|
|
func ParseBearerAuthHeader(authHeaderRaw string) *AuthHeader {
|
|
re := regexp.MustCompile(`([a-zA-z]+)="(.+?)"`)
|
|
matches := re.FindAllStringSubmatch(authHeaderRaw, -1)
|
|
matchmap := make(map[string]string)
|
|
|
|
for i := range matches {
|
|
matchmap[matches[i][1]] = matches[i][2]
|
|
}
|
|
|
|
var h AuthHeader
|
|
if err := mapstructure.Decode(matchmap, &h); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return &h
|
|
}
|
|
|
|
func loadPrivateKeyFromFile(path string) crypto.PrivateKey {
|
|
privateKeyBytes, err := os.ReadFile(path)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
rsaKey, err := jwt.ParseRSAPrivateKeyFromPEM(privateKeyBytes)
|
|
if err == nil {
|
|
return rsaKey
|
|
}
|
|
|
|
ecKey, err := jwt.ParseECPrivateKeyFromPEM(privateKeyBytes)
|
|
if err == nil {
|
|
return ecKey
|
|
}
|
|
|
|
edKey, err := jwt.ParseEdPrivateKeyFromPEM(privateKeyBytes)
|
|
if err == nil {
|
|
return edKey
|
|
}
|
|
|
|
panic("no valid private key found in file " + path)
|
|
}
|