mirror of
https://github.com/containers/skopeo.git
synced 2026-05-04 10:22:34 +00:00
[release-1.22] Bump c/common to v0.67.1, c/image v5.39.2
Signed-off-by: Tom Sweeney <tsweeney@redhat.com>
This commit is contained in:
4
go.mod
4
go.mod
@@ -17,8 +17,8 @@ require (
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/stretchr/testify v1.11.1
|
||||
go.podman.io/common v0.67.0
|
||||
go.podman.io/image/v5 v5.39.1
|
||||
go.podman.io/common v0.67.1
|
||||
go.podman.io/image/v5 v5.39.2
|
||||
go.podman.io/storage v1.62.0
|
||||
golang.org/x/term v0.36.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
|
||||
8
go.sum
8
go.sum
@@ -276,10 +276,10 @@ go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKr
|
||||
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.podman.io/common v0.67.0 h1:6Ci5oU1ek08OAxBLkHEqSyWmjNh5zf03PRqZ04cPdwU=
|
||||
go.podman.io/common v0.67.0/go.mod h1:sB9L8LMtmf5Hpek2qkEyRrcSzpb+gYpG3vq5Khima3U=
|
||||
go.podman.io/image/v5 v5.39.1 h1:loIw4qHzZzBlUguYZau40u8HbR5MrTPQhwT4Hy6sCm0=
|
||||
go.podman.io/image/v5 v5.39.1/go.mod h1:SlaR6Pra1ATIx4BcuZ16oafb3QcCHISaKcJbtlN/G/0=
|
||||
go.podman.io/common v0.67.1 h1:HddYLJfkfFUmFJ0V3PVoewguFM9eHkqk0g+fOc2B9R4=
|
||||
go.podman.io/common v0.67.1/go.mod h1:XVmSLtnhJwGb+ImYn6BXiozxVE3mYZJgiiBuDOiOquk=
|
||||
go.podman.io/image/v5 v5.39.2 h1:EJua/pRtvgLV/a5y8/RvA+ekKukZh0UuKMvLdTmEWFk=
|
||||
go.podman.io/image/v5 v5.39.2/go.mod h1:SlaR6Pra1ATIx4BcuZ16oafb3QcCHISaKcJbtlN/G/0=
|
||||
go.podman.io/storage v1.62.0 h1:0QjX1XlzVmbiaulb+aR/CG6p9+pzaqwIeZPe3tEjHbY=
|
||||
go.podman.io/storage v1.62.0/go.mod h1:A3UBK0XypjNZ6pghRhuxg62+2NIm5lcUGv/7XyMhMUI=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
|
||||
190
vendor/go.podman.io/image/v5/docker/docker_client.go
generated
vendored
190
vendor/go.podman.io/image/v5/docker/docker_client.go
generated
vendored
@@ -35,6 +35,7 @@ import (
|
||||
"go.podman.io/image/v5/types"
|
||||
"go.podman.io/storage/pkg/fileutils"
|
||||
"go.podman.io/storage/pkg/homedir"
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -86,8 +87,19 @@ type extensionSignatureList struct {
|
||||
Signatures []extensionSignature `json:"signatures"`
|
||||
}
|
||||
|
||||
// bearerToken records a cached token we can use to authenticate.
|
||||
// bearerToken records a cached token we can use to authenticate, or a pending process to obtain one.
|
||||
//
|
||||
// The goroutine obtaining the token holds lock to block concurrent token requests, and fills the structure (err and possibly the other fields)
|
||||
// before releasing the lock.
|
||||
// Other goroutines obtain lock to block on the token request, if any; and then inspect err to see if the token is usable.
|
||||
// If it is not, they try to get a new one.
|
||||
type bearerToken struct {
|
||||
// lock is held while obtaining the token. Potentially nested inside dockerClient.tokenCacheLock.
|
||||
// This is a counting semaphore only because we need a cancellable lock operation.
|
||||
lock *semaphore.Weighted
|
||||
|
||||
// The following fields can only be accessed with lock held.
|
||||
err error // nil if the token was successfully obtained (but may be expired); an error if the next lock holder _must_ obtain a new token.
|
||||
token string
|
||||
expirationTime time.Time
|
||||
}
|
||||
@@ -116,8 +128,9 @@ type dockerClient struct {
|
||||
challenges []challenge
|
||||
supportsSignatures bool
|
||||
|
||||
// Private state for setupRequestAuth (key: string, value: bearerToken)
|
||||
tokenCache sync.Map
|
||||
// Private state for setupRequestAuth
|
||||
tokenCacheLock sync.Mutex // Protects tokenCache.
|
||||
tokenCache map[string]*bearerToken
|
||||
// Private state for detectProperties:
|
||||
detectPropertiesOnce sync.Once // detectPropertiesOnce is used to execute detectProperties() at most once.
|
||||
detectPropertiesError error // detectPropertiesError caches the initial error.
|
||||
@@ -274,6 +287,7 @@ func newDockerClient(sys *types.SystemContext, registry, reference string) (*doc
|
||||
registry: registry,
|
||||
userAgent: userAgent,
|
||||
tlsClientConfig: tlsClientConfig,
|
||||
tokenCache: map[string]*bearerToken{},
|
||||
reportedWarnings: set.New[string](),
|
||||
}, nil
|
||||
}
|
||||
@@ -716,50 +730,11 @@ func (c *dockerClient) setupRequestAuth(req *http.Request, extraScope *authScope
|
||||
req.SetBasicAuth(c.auth.Username, c.auth.Password)
|
||||
return nil
|
||||
case "bearer":
|
||||
registryToken := c.registryToken
|
||||
if registryToken == "" {
|
||||
cacheKey := ""
|
||||
scopes := []authScope{c.scope}
|
||||
if extraScope != nil {
|
||||
// Using ':' as a separator here is unambiguous because getBearerToken below
|
||||
// uses the same separator when formatting a remote request (and because
|
||||
// repository names that we create can't contain colons, and extraScope values
|
||||
// coming from a server come from `parseAuthScope`, which also splits on colons).
|
||||
cacheKey = fmt.Sprintf("%s:%s:%s", extraScope.resourceType, extraScope.remoteName, extraScope.actions)
|
||||
if colonCount := strings.Count(cacheKey, ":"); colonCount != 2 {
|
||||
return fmt.Errorf(
|
||||
"Internal error: there must be exactly 2 colons in the cacheKey ('%s') but got %d",
|
||||
cacheKey,
|
||||
colonCount,
|
||||
)
|
||||
}
|
||||
scopes = append(scopes, *extraScope)
|
||||
}
|
||||
var token bearerToken
|
||||
t, inCache := c.tokenCache.Load(cacheKey)
|
||||
if inCache {
|
||||
token = t.(bearerToken)
|
||||
}
|
||||
if !inCache || time.Now().After(token.expirationTime) {
|
||||
var (
|
||||
t *bearerToken
|
||||
err error
|
||||
)
|
||||
if c.auth.IdentityToken != "" {
|
||||
t, err = c.getBearerTokenOAuth2(req.Context(), challenge, scopes)
|
||||
} else {
|
||||
t, err = c.getBearerToken(req.Context(), challenge, scopes)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
token = *t
|
||||
c.tokenCache.Store(cacheKey, token)
|
||||
}
|
||||
registryToken = token.token
|
||||
token, err := c.obtainBearerToken(req.Context(), challenge, extraScope)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", registryToken))
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
return nil
|
||||
default:
|
||||
logrus.Debugf("no handler for %s authentication", challenge.Scheme)
|
||||
@@ -769,16 +744,94 @@ func (c *dockerClient) setupRequestAuth(req *http.Request, extraScope *authScope
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *dockerClient) getBearerTokenOAuth2(ctx context.Context, challenge challenge,
|
||||
scopes []authScope) (*bearerToken, error) {
|
||||
// obtainBearerToken gets an "Authorization: Bearer" token if one is available, or obtains a fresh one.
|
||||
func (c *dockerClient) obtainBearerToken(ctx context.Context, challenge challenge, extraScope *authScope) (string, error) {
|
||||
if c.registryToken != "" {
|
||||
return c.registryToken, nil
|
||||
}
|
||||
|
||||
cacheKey := ""
|
||||
scopes := []authScope{c.scope}
|
||||
if extraScope != nil {
|
||||
// Using ':' as a separator here is unambiguous because getBearerToken below
|
||||
// uses the same separator when formatting a remote request (and because
|
||||
// repository names that we create can't contain colons, and extraScope values
|
||||
// coming from a server come from `parseAuthScope`, which also splits on colons).
|
||||
cacheKey = fmt.Sprintf("%s:%s:%s", extraScope.resourceType, extraScope.remoteName, extraScope.actions)
|
||||
if colonCount := strings.Count(cacheKey, ":"); colonCount != 2 {
|
||||
return "", fmt.Errorf(
|
||||
"Internal error: there must be exactly 2 colons in the cacheKey ('%s') but got %d",
|
||||
cacheKey,
|
||||
colonCount,
|
||||
)
|
||||
}
|
||||
scopes = append(scopes, *extraScope)
|
||||
}
|
||||
|
||||
token, newEntry, err := func() (*bearerToken, bool, error) { // A scope for defer
|
||||
c.tokenCacheLock.Lock()
|
||||
defer c.tokenCacheLock.Unlock()
|
||||
token, ok := c.tokenCache[cacheKey]
|
||||
if ok {
|
||||
return token, false, nil
|
||||
} else {
|
||||
token = &bearerToken{
|
||||
lock: semaphore.NewWeighted(1),
|
||||
}
|
||||
// If this is a new *bearerToken, lock the entry before adding it to the cache, so that any other goroutine that finds
|
||||
// this entry blocks until we obtain the token for the first time, and does not see an empty object
|
||||
// (and does not try to obtain the token itself when we are going to do so).
|
||||
if err := token.lock.Acquire(ctx, 1); err != nil {
|
||||
// We do not block on this Acquire, so we don’t really expect to fail here — but if ctx is canceled,
|
||||
// there is no point in trying to continue anyway.
|
||||
return nil, false, err
|
||||
}
|
||||
c.tokenCache[cacheKey] = token
|
||||
return token, true, nil
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !newEntry {
|
||||
// If this is an existing *bearerToken, obtain the lock only after releasing c.tokenCacheLock,
|
||||
// so that users of other cacheKey values are not blocked for the whole duration of our HTTP roundtrip.
|
||||
if err := token.lock.Acquire(ctx, 1); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
defer token.lock.Release(1)
|
||||
|
||||
if !newEntry && token.err == nil && !time.Now().After(token.expirationTime) {
|
||||
return token.token, nil // We have a usable token already.
|
||||
}
|
||||
|
||||
if c.auth.IdentityToken != "" {
|
||||
err = c.getBearerTokenOAuth2(ctx, token, challenge, scopes)
|
||||
} else {
|
||||
err = c.getBearerToken(ctx, token, challenge, scopes)
|
||||
}
|
||||
token.err = err
|
||||
if token.err != nil {
|
||||
return "", token.err
|
||||
}
|
||||
return token.token, nil
|
||||
}
|
||||
|
||||
// getBearerTokenOAuth2 obtains an "Authorization: Bearer" token using a pre-existing identity token per
|
||||
// https://github.com/distribution/distribution/blob/main/docs/spec/auth/oauth.md for challenge and scopes,
|
||||
// and writes it into dest.
|
||||
func (c *dockerClient) getBearerTokenOAuth2(ctx context.Context, dest *bearerToken, challenge challenge,
|
||||
scopes []authScope) error {
|
||||
realm, ok := challenge.Parameters["realm"]
|
||||
if !ok {
|
||||
return nil, errors.New("missing realm in bearer auth challenge")
|
||||
return errors.New("missing realm in bearer auth challenge")
|
||||
}
|
||||
|
||||
authReq, err := http.NewRequestWithContext(ctx, http.MethodPost, realm, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
// Make the form data required against the oauth2 authentication
|
||||
@@ -803,26 +856,29 @@ func (c *dockerClient) getBearerTokenOAuth2(ctx context.Context, challenge chall
|
||||
logrus.Debugf("%s %s", authReq.Method, authReq.URL.Redacted())
|
||||
res, err := c.client.Do(authReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if err := httpResponseToError(res, "Trying to obtain access token"); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
return newBearerTokenFromHTTPResponseBody(res)
|
||||
return dest.readFromHTTPResponseBody(res)
|
||||
}
|
||||
|
||||
func (c *dockerClient) getBearerToken(ctx context.Context, challenge challenge,
|
||||
scopes []authScope) (*bearerToken, error) {
|
||||
// getBearerToken obtains an "Authorization: Bearer" token using a GET request, per
|
||||
// https://github.com/distribution/distribution/blob/main/docs/spec/auth/token.md for challenge and scopes,
|
||||
// and writes it into dest.
|
||||
func (c *dockerClient) getBearerToken(ctx context.Context, dest *bearerToken, challenge challenge,
|
||||
scopes []authScope) error {
|
||||
realm, ok := challenge.Parameters["realm"]
|
||||
if !ok {
|
||||
return nil, errors.New("missing realm in bearer auth challenge")
|
||||
return errors.New("missing realm in bearer auth challenge")
|
||||
}
|
||||
|
||||
authReq, err := http.NewRequestWithContext(ctx, http.MethodGet, realm, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
params := authReq.URL.Query()
|
||||
@@ -850,22 +906,22 @@ func (c *dockerClient) getBearerToken(ctx context.Context, challenge challenge,
|
||||
logrus.Debugf("%s %s", authReq.Method, authReq.URL.Redacted())
|
||||
res, err := c.client.Do(authReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if err := httpResponseToError(res, "Requesting bearer token"); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
return newBearerTokenFromHTTPResponseBody(res)
|
||||
return dest.readFromHTTPResponseBody(res)
|
||||
}
|
||||
|
||||
// newBearerTokenFromHTTPResponseBody parses a http.Response to obtain a bearerToken.
|
||||
// readFromHTTPResponseBody sets token data by parsing a http.Response.
|
||||
// The caller is still responsible for ensuring res.Body is closed.
|
||||
func newBearerTokenFromHTTPResponseBody(res *http.Response) (*bearerToken, error) {
|
||||
func (bt *bearerToken) readFromHTTPResponseBody(res *http.Response) error {
|
||||
blob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxAuthTokenBodySize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
var token struct {
|
||||
@@ -881,12 +937,10 @@ func newBearerTokenFromHTTPResponseBody(res *http.Response) (*bearerToken, error
|
||||
if len(bodySample) > bodySampleLength {
|
||||
bodySample = bodySample[:bodySampleLength]
|
||||
}
|
||||
return nil, fmt.Errorf("decoding bearer token (last URL %q, body start %q): %w", res.Request.URL.Redacted(), string(bodySample), err)
|
||||
return fmt.Errorf("decoding bearer token (last URL %q, body start %q): %w", res.Request.URL.Redacted(), string(bodySample), err)
|
||||
}
|
||||
|
||||
bt := &bearerToken{
|
||||
token: token.Token,
|
||||
}
|
||||
bt.token = token.Token
|
||||
if bt.token == "" {
|
||||
bt.token = token.AccessToken
|
||||
}
|
||||
@@ -899,7 +953,7 @@ func newBearerTokenFromHTTPResponseBody(res *http.Response) (*bearerToken, error
|
||||
token.IssuedAt = time.Now().UTC()
|
||||
}
|
||||
bt.expirationTime = token.IssuedAt.Add(time.Duration(token.ExpiresIn) * time.Second)
|
||||
return bt, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// detectPropertiesHelper performs the work of detectProperties which executes
|
||||
|
||||
2
vendor/go.podman.io/image/v5/version/version.go
generated
vendored
2
vendor/go.podman.io/image/v5/version/version.go
generated
vendored
@@ -8,7 +8,7 @@ const (
|
||||
// VersionMinor is for functionality in a backwards-compatible manner
|
||||
VersionMinor = 39
|
||||
// VersionPatch is for backwards-compatible bug fixes
|
||||
VersionPatch = 1
|
||||
VersionPatch = 2
|
||||
|
||||
// VersionDev indicates development branch. Releases will be empty string.
|
||||
VersionDev = ""
|
||||
|
||||
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@@ -351,7 +351,7 @@ go.opentelemetry.io/otel/trace
|
||||
go.opentelemetry.io/otel/trace/embedded
|
||||
go.opentelemetry.io/otel/trace/internal/telemetry
|
||||
go.opentelemetry.io/otel/trace/noop
|
||||
# go.podman.io/common v0.67.0
|
||||
# go.podman.io/common v0.67.1
|
||||
## explicit; go 1.24.2
|
||||
go.podman.io/common/pkg/auth
|
||||
go.podman.io/common/pkg/capabilities
|
||||
@@ -361,7 +361,7 @@ go.podman.io/common/pkg/password
|
||||
go.podman.io/common/pkg/report
|
||||
go.podman.io/common/pkg/report/camelcase
|
||||
go.podman.io/common/pkg/retry
|
||||
# go.podman.io/image/v5 v5.39.1
|
||||
# go.podman.io/image/v5 v5.39.2
|
||||
## explicit; go 1.24.0
|
||||
go.podman.io/image/v5/copy
|
||||
go.podman.io/image/v5/directory
|
||||
|
||||
Reference in New Issue
Block a user