session: use anonymous registry auth when sessions are unset

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax committed 2026-05-29 15:53:36 +02:00
1 parent f49e91af17
commit eb866c3d23
3 files changed
+81 -3

No files matched your search

+5 -1
View File
@@ -7,6 +7,10 @@ import (
"github.com/pkg/errors"
)
// ErrNoActiveSessions is returned when a session group does not contain any
// active session IDs.
var ErrNoActiveSessions = errors.New("no active sessions")
type Group interface {
SessionIterator() Iterator
}
@@ -69,7 +73,7 @@ func (sm *Manager) Any(ctx context.Context, g Group, f func(context.Context, str
if lastErr != nil {
return lastErr
}
return errors.Errorf("no active sessions")
return errors.WithStack(ErrNoActiveSessions)
}
timeoutCtx, cancel := context.WithCancelCause(ctx)
+12 -2
View File
@@ -48,6 +48,7 @@ func newAuthHandlerNS(sm *session.Manager) *authHandlerNS {
}
func (a *authHandlerNS) get(ctx context.Context, host string, sm *session.Manager, g session.Group) *authFetcher {
hasSession := false
if g != nil {
if iter := g.SessionIterator(); iter != nil {
for {
@@ -55,6 +56,7 @@ func (a *authHandlerNS) get(ctx context.Context, host string, sm *session.Manage
if id == "" {
break
}
hasSession = true
h, ok := a.fetchers[host+"/"+id]
if ok {
h.lastUsed = time.Now()
@@ -64,6 +66,14 @@ func (a *authHandlerNS) get(ctx context.Context, host string, sm *session.Manage
}
}
if !hasSession {
h, ok := a.fetchers[host+"/"]
if ok {
h.lastUsed = time.Now()
return h
}
}
// link existing fetcher
for k, h := range a.fetchers {
parts := strings.SplitN(k, "/", 2)
@@ -186,12 +196,12 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
var username, secret string
sessionID, pubKey, err := sessionauth.GetTokenAuthority(ctx, host, a.sm, a.session)
if err != nil {
if err != nil && !errors.Is(err, session.ErrNoActiveSessions) {
return err
}
if pubKey == nil {
sessionID, username, secret, err = a.getCredentials(ctx, host)
if err != nil {
if err != nil && !errors.Is(err, session.ErrNoActiveSessions) {
return err
}
}
+64
View File
@@ -1,8 +1,16 @@
package resolver
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"reflect"
"testing"
"time"
"github.com/moby/buildkit/session"
"github.com/stretchr/testify/require"
)
func TestParseScopes(t *testing.T) {
@@ -54,3 +62,59 @@ func TestParseScopes(t *testing.T) {
})
}
}
func TestBearerAuthFallsBackToAnonymousTokenWithoutSession(t *testing.T) {
type tokenRequest struct {
authorization string
service string
scope string
}
tokenRequests := make(chan tokenRequest, 1)
tokenServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tokenRequests <- tokenRequest{
authorization: r.Header.Get("Authorization"),
service: r.URL.Query().Get("service"),
scope: r.URL.Query().Get("scope"),
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(map[string]any{
"token": "anonymous-token",
"expires_in": 60,
}); err != nil {
t.Errorf("failed to write token response: %v", err)
}
}))
defer tokenServer.Close()
sm, err := session.NewManager()
require.NoError(t, err)
auth := newDockerAuthorizer(tokenServer.Client(), newAuthHandlerNS(sm), sm, session.NewGroup(""))
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "https://registry.example/v2/library/alpine/manifests/latest", nil)
res := &http.Response{
StatusCode: http.StatusUnauthorized,
Header: http.Header{},
Request: req,
}
res.Header.Set("WWW-Authenticate", fmt.Sprintf(
`Bearer realm=%q,service="registry.example",scope="repository:library/alpine:pull"`,
tokenServer.URL+"/token",
))
require.NoError(t, auth.AddResponses(t.Context(), []*http.Response{res}))
retryReq := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "https://registry.example/v2/library/alpine/manifests/latest", nil)
require.NoError(t, auth.Authorize(t.Context(), retryReq))
require.Equal(t, "Bearer anonymous-token", retryReq.Header.Get("Authorization"))
select {
case req := <-tokenRequests:
require.Empty(t, req.authorization)
require.Equal(t, "registry.example", req.service)
require.Equal(t, "repository:library/alpine:pull", req.scope)
case <-time.After(time.Second):
t.Fatal("expected anonymous token request")
}
}