WIP: don't require IndexInfo

Lots to do here; too many wrappers everywhere, which may become easier
when content trust is removed (which adds another level of abstraction)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2025-03-10 21:48:03 +01:00
parent ba21666654
commit 274b2dcba7
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
3 changed files with 51 additions and 7 deletions

View File

@ -18,7 +18,6 @@ import (
"github.com/docker/cli/internal/jsonstream"
"github.com/docker/cli/internal/registry"
"github.com/docker/cli/internal/tui"
"github.com/moby/moby/api/pkg/authconfig"
"github.com/moby/moby/api/types/auxprogress"
"github.com/moby/moby/client"
"github.com/morikuni/aec"
@ -106,12 +105,8 @@ To push the complete multi-platform image, remove the --platform flag.
}
}
// Resolve the Repository name from fqn to RepositoryInfo
indexInfo := registry.NewIndexInfo(ref)
// Resolve the Auth config relevant for this server
authConfig := command.ResolveAuthConfig(dockerCli.ConfigFile(), indexInfo)
encodedAuth, err := authconfig.Encode(authConfig)
encodedAuth, err := command.RetrieveAuthTokenFromImage(dockerCli.ConfigFile(), ref.String())
if err != nil {
return err
}
@ -134,6 +129,9 @@ To push the complete multi-platform image, remove the --platform flag.
}()
if !opts.untrusted {
// Resolve the Repository name from fqn to RepositoryInfo
indexInfo := registry.NewIndexInfo(ref)
authConfig := command.ResolveAuthConfig(dockerCli.ConfigFile(), indexInfo)
return pushTrustedReference(ctx, dockerCli, indexInfo, ref, authConfig, responseBody)
}

View File

@ -150,6 +150,7 @@ func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth)
// imagePullPrivileged pulls the image and displays it to the output
func imagePullPrivileged(ctx context.Context, cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth, opts pullOptions) error {
// TODO(thaJeztah): get rid of this trust.ImageRefAndAuth monstrosity; we're wrapping wrappers around wrappers; all we need here is the image ref (or even less: the registry name)
encodedAuth, err := authconfig.Encode(*imgRefAndAuth.AuthConfig())
if err != nil {
return err

View File

@ -35,6 +35,44 @@ const (
// [registry.IndexServer]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/registry#IndexServer
const authConfigKey = "https://index.docker.io/v1/"
// NewAuthRequester returns a RequestPrivilegeFunc for the specified registry
// and the given cmdName (used as informational message to the user).
//
// The returned function is a [registrytypes.RequestAuthConfig] to prompt the user
// for credentials if needed. It is called as fallback if the credentials (if any)
// used for the initial operation did not work.
//
// TODO(thaJeztah): cli Cli could be a Streams if it was not for cli.SetIn to be needed?
// TODO(thaJeztah): ideally, this would accept reposName / imageRef as a regular string (we can parse it if needed!), or .. maybe generics and accept either?
func NewAuthRequester(cli Cli, indexServer string, promptMsg string) registrytypes.RequestAuthConfig {
configKey := getAuthConfigKey(indexServer)
return newPrivilegeFunc(cli, configKey, promptMsg)
}
func newPrivilegeFunc(cli Cli, indexServer string, promptMsg string) registrytypes.RequestAuthConfig {
return func(ctx context.Context) (string, error) {
// TODO(thaJeztah): can we make the prompt an argument? ("prompt func()" or "prompt func()?
_, _ = fmt.Fprint(cli.Out(), "\n"+promptMsg+"\n")
isDefaultRegistry := indexServer == authConfigKey
authConfig, err := GetDefaultAuthConfig(cli.ConfigFile(), true, indexServer, isDefaultRegistry)
if err != nil {
_, _ = fmt.Fprintf(cli.Err(), "Unable to retrieve stored credentials for %s, error: %s.\n", indexServer, err)
}
select {
case <-ctx.Done():
return "", ctx.Err()
default:
}
authConfig, err = PromptUserForCredentials(ctx, cli, "", "", authConfig.Username, indexServer)
if err != nil {
return "", err
}
return registrytypes.EncodeAuthConfig(authConfig)
}
}
// ResolveAuthConfig returns auth-config for the given registry from the
// credential-store. It returns an empty AuthConfig if no credentials were
// found.
@ -44,7 +82,8 @@ const authConfigKey = "https://index.docker.io/v1/"
//
// [registry.ResolveAuthConfig]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/registry#ResolveAuthConfig
func ResolveAuthConfig(cfg *configfile.ConfigFile, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
configKey := index.Name
indexServer := index.Name
configKey := getAuthConfigKey(indexServer)
if index.Official {
configKey = authConfigKey
}
@ -57,6 +96,7 @@ func ResolveAuthConfig(cfg *configfile.ConfigFile, index *registrytypes.IndexInf
// If credentials for given serverAddress exists in the credential store, the configuration will be populated with values in it
func GetDefaultAuthConfig(cfg *configfile.ConfigFile, checkCredStore bool, serverAddress string, isDefaultRegistry bool) (registrytypes.AuthConfig, error) {
if !isDefaultRegistry {
// FIXME(thaJeztah): should the same logic be used for getAuthConfigKey ?? Looks like we're normalizing things here, but not elsewhere? why?
serverAddress = credentials.ConvertToHostname(serverAddress)
}
authCfg := configtypes.AuthConfig{}
@ -83,6 +123,8 @@ func GetDefaultAuthConfig(cfg *configfile.ConfigFile, checkCredStore bool, serve
// If defaultUsername is not empty, the username prompt includes that username
// and the user can hit enter without inputting a username to use that default
// username.
//
// TODO(thaJeztah): cli Cli could be a Streams if it was not for cli.SetIn to be needed?
func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword, defaultUsername, serverAddress string) (registrytypes.AuthConfig, error) {
// On Windows, force the use of the regular OS stdin stream.
//
@ -174,6 +216,9 @@ func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword
// base64url encoded ([RFC 4648, Section 5]) JSON string for sending through
// the "X-Registry-Auth" header.
//
// FIXME(thaJeztah): do we need a variant like this, but with "indexServer" (domainName) as input?
// TODO(thaJeztah): should this accept an image ref-type, and use instead of ResolveAuthConfig
//
// [RFC 4648, Section 5]: https://tools.ietf.org/html/rfc4648#section-5
func RetrieveAuthTokenFromImage(cfg *configfile.ConfigFile, image string) (string, error) {
registryRef, err := reference.ParseNormalizedNamed(image)