fix: parse public key as fallback for certificate for bearer authentication (#3180)

* fix: parse public key as fallback for bearer auth

Signed-off-by: evanebb <git@evanus.nl>

* fix: use correct error message

Signed-off-by: evanebb <git@evanus.nl>

---------

Signed-off-by: evanebb <git@evanus.nl>
This commit is contained in:
Evan
2025-06-04 07:53:44 +02:00
committed by GitHub
parent 167f7e34cd
commit 0c51cb72c3
4 changed files with 76 additions and 16 deletions
+1 -1
View File
@@ -179,7 +179,7 @@ var (
ErrNoBearerToken = errors.New("no bearer token given") ErrNoBearerToken = errors.New("no bearer token given")
ErrInvalidBearerToken = errors.New("invalid bearer token given") ErrInvalidBearerToken = errors.New("invalid bearer token given")
ErrInsufficientScope = errors.New("bearer token does not have sufficient scope") ErrInsufficientScope = errors.New("bearer token does not have sufficient scope")
ErrCouldNotLoadCertificate = errors.New("failed to load certificate") ErrCouldNotLoadPublicKey = errors.New("failed to load public key")
ErrEventTypeEmpty = errors.New("event type empty") ErrEventTypeEmpty = errors.New("event type empty")
ErrEventSinkIsNil = errors.New("event sink is nil") ErrEventSinkIsNil = errors.New("event sink is nil")
ErrUnsupportedEventSink = errors.New("event sink is not supported") ErrUnsupportedEventSink = errors.New("event sink is not supported")
+24 -12
View File
@@ -2,6 +2,7 @@ package api
import ( import (
"context" "context"
"crypto"
"crypto/sha256" "crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/base64" "encoding/base64"
@@ -402,15 +403,17 @@ func (amw *AuthnMiddleware) tryAuthnHandlers(ctlr *Controller) mux.MiddlewareFun
} }
func bearerAuthHandler(ctlr *Controller) mux.MiddlewareFunc { func bearerAuthHandler(ctlr *Controller) mux.MiddlewareFunc {
certificate, err := loadCertificateFromFile(ctlr.Config.HTTP.Auth.Bearer.Cert) // although the configuration option is called 'cert', this function will also parse a public key directly
// see https://github.com/project-zot/zot/issues/3173 for info
publicKey, err := loadPublicKeyFromFile(ctlr.Config.HTTP.Auth.Bearer.Cert)
if err != nil { if err != nil {
ctlr.Log.Panic().Err(err).Msg("failed to load certificate for bearer authentication") ctlr.Log.Panic().Err(err).Msg("failed to load public key for bearer authentication")
} }
authorizer := NewBearerAuthorizer( authorizer := NewBearerAuthorizer(
ctlr.Config.HTTP.Auth.Bearer.Realm, ctlr.Config.HTTP.Auth.Bearer.Realm,
ctlr.Config.HTTP.Auth.Bearer.Service, ctlr.Config.HTTP.Auth.Bearer.Service,
certificate.PublicKey, publicKey,
) )
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
@@ -902,21 +905,30 @@ func GenerateAPIKey(uuidGenerator guuid.Generator, log log.Logger,
return apiKey, apiKeyID.String(), err return apiKey, apiKeyID.String(), err
} }
func loadCertificateFromFile(path string) (*x509.Certificate, error) { func loadPublicKeyFromFile(path string) (crypto.PublicKey, error) {
rawCert, err := os.ReadFile(path) raw, err := os.ReadFile(path)
if err != nil { if err != nil {
return nil, fmt.Errorf("%w: %w, path %s", zerr.ErrCouldNotLoadCertificate, err, path) return nil, fmt.Errorf("%w: %w, path %s", zerr.ErrCouldNotLoadPublicKey, err, path)
} }
block, _ := pem.Decode(rawCert) block, _ := pem.Decode(raw)
if block == nil { if block == nil {
return nil, fmt.Errorf("%w: no valid PEM data found", zerr.ErrCouldNotLoadCertificate) return nil, fmt.Errorf("%w: no valid PEM data found", zerr.ErrCouldNotLoadPublicKey)
} }
cert, err := x509.ParseCertificate(block.Bytes) pemBytes := block.Bytes
if err != nil {
return nil, fmt.Errorf("%w: %w", zerr.ErrCouldNotLoadCertificate, err) if cert, err := x509.ParseCertificate(pemBytes); err == nil {
return cert.PublicKey, nil
} }
return cert, nil if key, err := x509.ParsePKIXPublicKey(pemBytes); err == nil {
return key, nil
}
if key, err := x509.ParsePKCS1PublicKey(pemBytes); err == nil {
return key, nil
}
return nil, fmt.Errorf("%w: no valid x509 certificate or public key found", zerr.ErrCouldNotLoadPublicKey)
} }
+31 -3
View File
@@ -77,11 +77,15 @@ import (
const ( const (
ServerCert = "../../test/data/server.cert" ServerCert = "../../test/data/server.cert"
ServerKey = "../../test/data/server.key" ServerKey = "../../test/data/server.key"
ServerPublicKey = "../../test/data/server-public.key"
ServerPublicKeyPKCS1 = "../../test/data/server-public-pkcs1.key"
CACert = "../../test/data/ca.crt" CACert = "../../test/data/ca.crt"
ServerCertECDSA = "../../test/data/server-ecdsa.cert" ServerCertECDSA = "../../test/data/server-ecdsa.cert"
ServerKeyECDSA = "../../test/data/server-ecdsa.key" ServerKeyECDSA = "../../test/data/server-ecdsa.key"
ServerPublicKeyECDSA = "../../test/data/server-public-ecdsa.key"
ServerCertED25519 = "../../test/data/server-ed25519.cert" ServerCertED25519 = "../../test/data/server-ed25519.cert"
ServerKeyED25519 = "../../test/data/server-ed25519.key" ServerKeyED25519 = "../../test/data/server-ed25519.key"
ServerPublicKeyED25519 = "../../test/data/server-public-ed25519.key"
UnauthorizedNamespace = "fortknox/notallowed" UnauthorizedNamespace = "fortknox/notallowed"
AuthorizationNamespace = "authz/image" AuthorizationNamespace = "authz/image"
LDAPAddress = "127.0.0.1" LDAPAddress = "127.0.0.1"
@@ -3921,23 +3925,47 @@ func TestBearerAuthMultipleAlgorithms(t *testing.T) {
alg string alg string
}{ }{
{ {
"RSA signing key", "RSA signing key using certificate",
ServerKey, ServerKey,
ServerCert, ServerCert,
"RS256", "RS256",
}, },
{ {
"ECDSA signing key", "RSA signing key using public key",
ServerKey,
ServerPublicKey,
"RS256",
},
{
"RSA signing key using public key in PKCS1 format",
ServerKey,
ServerPublicKeyPKCS1,
"RS256",
},
{
"ECDSA signing key using certificate",
ServerKeyECDSA, ServerKeyECDSA,
ServerCertECDSA, ServerCertECDSA,
"ES256", "ES256",
}, },
{ {
"ED25519 signing key", "ECDSA signing key using public key",
ServerKeyECDSA,
ServerPublicKeyECDSA,
"ES256",
},
{
"ED25519 signing key using certificate",
ServerKeyED25519, ServerKeyED25519,
ServerCertED25519, ServerCertED25519,
"EdDSA", "EdDSA",
}, },
{
"ED25519 signing key using public key",
ServerKeyED25519,
ServerPublicKeyED25519,
"EdDSA",
},
} }
for _, testCase := range testCases { for _, testCase := range testCases {
+20
View File
@@ -19,6 +19,16 @@ openssl req \
-out server.csr \ -out server.csr \
-subj "/OU=TestServer/CN=*" -subj "/OU=TestServer/CN=*"
openssl rsa \
-in server.key \
-pubout \
-out server-public.key
openssl rsa \
-in server.key \
-RSAPublicKey_out \
-out server-public-pkcs1.key
openssl x509 \ openssl x509 \
-req \ -req \
-days 3650 \ -days 3650 \
@@ -76,6 +86,11 @@ openssl req \
-out server-ecdsa.csr \ -out server-ecdsa.csr \
-subj "/OU=TestServer/CN=*" -subj "/OU=TestServer/CN=*"
openssl ec \
-in server-ecdsa.key \
-pubout \
-out server-public-ecdsa.key
openssl x509 \ openssl x509 \
-req \ -req \
-days 3650 \ -days 3650 \
@@ -112,6 +127,11 @@ openssl req \
-out server-ed25519.csr \ -out server-ed25519.csr \
-subj "/OU=TestServer/CN=*" -subj "/OU=TestServer/CN=*"
openssl pkey \
-in server-ed25519.key \
-pubout \
-out server-public-ed25519.key
openssl x509 \ openssl x509 \
-req \ -req \
-days 3650 \ -days 3650 \