mirror of
https://github.com/docker/cli.git
synced 2026-01-13 18:22:35 +03:00
Merge pull request #6603 from thaJeztah/remove_trust_integration
remove support for client-side docker content trust validation
This commit is contained in:
@@ -17,11 +17,9 @@ import (
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/cli/cli/command/image"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/config/types"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/docker/cli/internal/jsonstream"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/moby/moby/api/types/mount"
|
||||
@@ -41,7 +39,6 @@ const (
|
||||
type createOptions struct {
|
||||
name string
|
||||
platform string
|
||||
untrusted bool
|
||||
pull string // always, missing, never
|
||||
quiet bool
|
||||
useAPISocket bool
|
||||
@@ -88,7 +85,9 @@ func newCreateCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
_ = flags.SetAnnotation("platform", "version", []string{"1.32"})
|
||||
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms())
|
||||
|
||||
flags.BoolVar(&options.untrusted, "disable-content-trust", !trust.Enabled(), "Skip image verification")
|
||||
// TODO(thaJeztah): DEPRECATED: remove in v29.1 or v30
|
||||
flags.Bool("disable-content-trust", true, "Skip image verification (deprecated)")
|
||||
_ = flags.MarkDeprecated("disable-content-trust", "support for docker content trust was removed")
|
||||
copts = addFlags(flags)
|
||||
|
||||
addCompletions(cmd, dockerCLI)
|
||||
@@ -213,10 +212,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
|
||||
hostConfig := containerCfg.HostConfig
|
||||
networkingConfig := containerCfg.NetworkingConfig
|
||||
|
||||
var (
|
||||
trustedRef reference.Canonical
|
||||
namedRef reference.Named
|
||||
)
|
||||
var namedRef reference.Named
|
||||
|
||||
// TODO(thaJeztah): add a platform option-type / flag-type.
|
||||
if options.platform != "" {
|
||||
@@ -240,15 +236,6 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
|
||||
}
|
||||
if named, ok := ref.(reference.Named); ok {
|
||||
namedRef = reference.TagNameOnly(named)
|
||||
|
||||
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && !options.untrusted {
|
||||
var err error
|
||||
trustedRef, err = image.TrustedReference(ctx, dockerCli, taggedRef)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
config.Image = reference.FamiliarString(trustedRef)
|
||||
}
|
||||
}
|
||||
|
||||
const dockerConfigPathInContainer = "/run/secrets/docker/config.json"
|
||||
@@ -331,9 +318,6 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
|
||||
if err := pullImage(ctx, dockerCli, config.Image, options); err != nil {
|
||||
return err
|
||||
}
|
||||
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil {
|
||||
return trust.TagTrusted(ctx, dockerCli.Client(), dockerCli.Err(), trustedRef, taggedRef)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/internal/test"
|
||||
"github.com/docker/cli/internal/test/notary"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/system"
|
||||
@@ -136,10 +135,9 @@ func TestCreateContainerImagePullPolicy(t *testing.T) {
|
||||
}
|
||||
fakeCLI := test.NewFakeCli(apiClient)
|
||||
id, err := createContainer(context.Background(), fakeCLI, config, &createOptions{
|
||||
name: "name",
|
||||
platform: runtime.GOOS,
|
||||
untrusted: true,
|
||||
pull: tc.PullPolicy,
|
||||
name: "name",
|
||||
platform: runtime.GOOS,
|
||||
pull: tc.PullPolicy,
|
||||
})
|
||||
|
||||
if tc.ExpectedErrMsg != "" {
|
||||
@@ -215,51 +213,6 @@ func TestCreateContainerValidateFlags(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewCreateCommandWithContentTrustErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
expectedError string
|
||||
notaryFunc test.NotaryClientFuncType
|
||||
}{
|
||||
{
|
||||
name: "offline-notary-server",
|
||||
notaryFunc: notary.GetOfflineNotaryRepository,
|
||||
expectedError: "client is offline",
|
||||
args: []string{"image:tag"},
|
||||
},
|
||||
{
|
||||
name: "uninitialized-notary-server",
|
||||
notaryFunc: notary.GetUninitializedNotaryRepository,
|
||||
expectedError: "remote trust data does not exist",
|
||||
args: []string{"image:tag"},
|
||||
},
|
||||
{
|
||||
name: "empty-notary-server",
|
||||
notaryFunc: notary.GetEmptyTargetsNotaryRepository,
|
||||
expectedError: "No valid trust data for tag",
|
||||
args: []string{"image:tag"},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Setenv("DOCKER_CONTENT_TRUST", "true")
|
||||
fakeCLI := test.NewFakeCli(&fakeClient{
|
||||
createContainerFunc: func(options client.ContainerCreateOptions) (client.ContainerCreateResult, error) {
|
||||
return client.ContainerCreateResult{}, errors.New("shouldn't try to pull image")
|
||||
},
|
||||
})
|
||||
fakeCLI.SetNotaryClient(tc.notaryFunc)
|
||||
cmd := newCreateCommand(fakeCLI)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
err := cmd.Execute()
|
||||
assert.ErrorContains(t, err, tc.expectedError)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewCreateCommandWithWarnings(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
@@ -73,7 +72,10 @@ func newRunCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
// TODO(thaJeztah): consider adding platform as "image create option" on containerOptions
|
||||
flags.StringVar(&options.platform, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable")
|
||||
_ = flags.SetAnnotation("platform", "version", []string{"1.32"})
|
||||
flags.BoolVar(&options.untrusted, "disable-content-trust", !trust.Enabled(), "Skip image verification")
|
||||
|
||||
// TODO(thaJeztah): DEPRECATED: remove in v29.1 or v30
|
||||
flags.Bool("disable-content-trust", true, "Skip image verification (deprecated)")
|
||||
_ = flags.MarkDeprecated("disable-content-trust", "support for docker content trust was removed")
|
||||
copts = addFlags(flags)
|
||||
|
||||
_ = cmd.RegisterFlagCompletionFunc("detach-keys", completeDetachKeys)
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/cli/internal/test"
|
||||
"github.com/docker/cli/internal/test/notary"
|
||||
"github.com/moby/moby/api/types"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
@@ -295,54 +294,6 @@ func TestRunPullTermination(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCommandWithContentTrustErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
expectedError string
|
||||
notaryFunc test.NotaryClientFuncType
|
||||
}{
|
||||
{
|
||||
name: "offline-notary-server",
|
||||
notaryFunc: notary.GetOfflineNotaryRepository,
|
||||
expectedError: "client is offline",
|
||||
args: []string{"image:tag"},
|
||||
},
|
||||
{
|
||||
name: "uninitialized-notary-server",
|
||||
notaryFunc: notary.GetUninitializedNotaryRepository,
|
||||
expectedError: "remote trust data does not exist",
|
||||
args: []string{"image:tag"},
|
||||
},
|
||||
{
|
||||
name: "empty-notary-server",
|
||||
notaryFunc: notary.GetEmptyTargetsNotaryRepository,
|
||||
expectedError: "No valid trust data for tag",
|
||||
args: []string{"image:tag"},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Setenv("DOCKER_CONTENT_TRUST", "true")
|
||||
fakeCLI := test.NewFakeCli(&fakeClient{
|
||||
createContainerFunc: func(options client.ContainerCreateOptions) (client.ContainerCreateResult, error) {
|
||||
return client.ContainerCreateResult{}, errors.New("shouldn't try to pull image")
|
||||
},
|
||||
})
|
||||
fakeCLI.SetNotaryClient(tc.notaryFunc)
|
||||
cmd := newRunCommand(fakeCLI)
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
err := cmd.Execute()
|
||||
statusErr := cli.StatusError{}
|
||||
assert.Check(t, errors.As(err, &statusErr))
|
||||
assert.Check(t, is.Equal(statusErr.StatusCode, 125))
|
||||
assert.Check(t, is.ErrorContains(err, tc.expectedError))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunContainerImagePullPolicyInvalid(t *testing.T) {
|
||||
cases := []struct {
|
||||
PullPolicy string
|
||||
|
||||
@@ -149,8 +149,9 @@ func newBuildCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
flags.SetAnnotation("target", annotation.ExternalURL, []string{"https://docs.docker.com/reference/cli/docker/buildx/build/#target"})
|
||||
flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file")
|
||||
|
||||
// TODO(thaJeztah): DEPRECATED: remove in v29.1 or v30
|
||||
flags.Bool("disable-content-trust", true, "Skip image verification (deprecated)")
|
||||
_ = flags.MarkHidden("disable-content-trust")
|
||||
_ = flags.MarkDeprecated("disable-content-trust", "support for docker content trust was removed")
|
||||
|
||||
flags.StringVar(&options.platform, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable")
|
||||
flags.SetAnnotation("platform", "version", []string{"1.38"})
|
||||
|
||||
@@ -13,10 +13,7 @@ import (
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/docker/cli/internal/jsonstream"
|
||||
"github.com/moby/moby/api/pkg/authconfig"
|
||||
registrytypes "github.com/moby/moby/api/types/registry"
|
||||
"github.com/moby/moby/client"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -24,11 +21,10 @@ import (
|
||||
|
||||
// pullOptions defines what and how to pull.
|
||||
type pullOptions struct {
|
||||
remote string
|
||||
all bool
|
||||
platform string
|
||||
quiet bool
|
||||
untrusted bool
|
||||
remote string
|
||||
all bool
|
||||
platform string
|
||||
quiet bool
|
||||
}
|
||||
|
||||
// newPullCommand creates a new `docker pull` command
|
||||
@@ -57,7 +53,11 @@ func newPullCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
|
||||
flags.BoolVarP(&opts.all, "all-tags", "a", false, "Download all tagged images in the repository")
|
||||
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress verbose output")
|
||||
flags.BoolVar(&opts.untrusted, "disable-content-trust", !trust.Enabled(), "Skip image verification")
|
||||
|
||||
// TODO(thaJeztah): DEPRECATED: remove in v29.1 or v30
|
||||
flags.Bool("disable-content-trust", true, "Skip image verification (deprecated)")
|
||||
_ = flags.MarkDeprecated("disable-content-trust", "support for docker content trust was removed")
|
||||
|
||||
flags.StringVar(&opts.platform, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable")
|
||||
_ = flags.SetAnnotation("platform", "version", []string{"1.32"})
|
||||
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms())
|
||||
@@ -80,46 +80,22 @@ func runPull(ctx context.Context, dockerCLI command.Cli, opts pullOptions) error
|
||||
}
|
||||
}
|
||||
|
||||
if opts.platform != "" {
|
||||
// TODO(thaJeztah): add a platform option-type / flag-type.
|
||||
if _, err = platforms.Parse(opts.platform); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, authResolver(dockerCLI), distributionRef.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if reference has a digest
|
||||
_, isCanonical := distributionRef.(reference.Canonical)
|
||||
if !opts.untrusted && !isCanonical {
|
||||
if err := trustedPull(ctx, dockerCLI, imgRefAndAuth, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := imagePullPrivileged(ctx, dockerCLI, imgRefAndAuth.Reference(), imgRefAndAuth.AuthConfig(), opts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), imgRefAndAuth.Reference().String())
|
||||
return nil
|
||||
}
|
||||
|
||||
// imagePullPrivileged pulls the image and displays it to the output
|
||||
func imagePullPrivileged(ctx context.Context, dockerCLI command.Cli, ref reference.Named, authConfig *registrytypes.AuthConfig, opts pullOptions) error {
|
||||
encodedAuth, err := authconfig.Encode(*authConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var ociPlatforms []ocispec.Platform
|
||||
if opts.platform != "" {
|
||||
// Already validated.
|
||||
ociPlatforms = append(ociPlatforms, platforms.MustParse(opts.platform))
|
||||
// TODO(thaJeztah): add a platform option-type / flag-type.
|
||||
p, err := platforms.Parse(opts.platform)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ociPlatforms = append(ociPlatforms, p)
|
||||
}
|
||||
|
||||
responseBody, err := dockerCLI.Client().ImagePull(ctx, reference.FamiliarString(ref), client.ImagePullOptions{
|
||||
encodedAuth, err := command.RetrieveAuthTokenFromImage(dockerCLI.ConfigFile(), distributionRef.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
responseBody, err := dockerCLI.Client().ImagePull(ctx, reference.FamiliarString(distributionRef), client.ImagePullOptions{
|
||||
RegistryAuth: encodedAuth,
|
||||
PrivilegeFunc: nil,
|
||||
All: opts.all,
|
||||
@@ -134,5 +110,9 @@ func imagePullPrivileged(ctx context.Context, dockerCLI command.Cli, ref referen
|
||||
if opts.quiet {
|
||||
out = streams.NewOut(io.Discard)
|
||||
}
|
||||
return jsonstream.Display(ctx, responseBody, out)
|
||||
if err := jsonstream.Display(ctx, responseBody, out); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), distributionRef.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/internal/test"
|
||||
"github.com/docker/cli/internal/test/notary"
|
||||
"github.com/moby/moby/client"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
@@ -89,49 +88,3 @@ func TestNewPullCommandSuccess(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPullCommandWithContentTrustErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
expectedError string
|
||||
notaryFunc test.NotaryClientFuncType
|
||||
}{
|
||||
{
|
||||
name: "offline-notary-server",
|
||||
notaryFunc: notary.GetOfflineNotaryRepository,
|
||||
expectedError: "client is offline",
|
||||
args: []string{"image:tag"},
|
||||
},
|
||||
{
|
||||
name: "uninitialized-notary-server",
|
||||
notaryFunc: notary.GetUninitializedNotaryRepository,
|
||||
expectedError: "remote trust data does not exist",
|
||||
args: []string{"image:tag"},
|
||||
},
|
||||
{
|
||||
name: "empty-notary-server",
|
||||
notaryFunc: notary.GetEmptyTargetsNotaryRepository,
|
||||
expectedError: "No valid trust data for tag",
|
||||
args: []string{"image:tag"},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Setenv("DOCKER_CONTENT_TRUST", "true")
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
imagePullFunc: func(ref string, options client.ImagePullOptions) (client.ImagePullResponse, error) {
|
||||
// FIXME(thaJeztah): how to mock this?
|
||||
return fakeStreamResult{ReadCloser: http.NoBody}, nil
|
||||
},
|
||||
})
|
||||
cli.SetNotaryClient(tc.notaryFunc)
|
||||
cmd := newPullCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
err := cmd.Execute()
|
||||
assert.ErrorContains(t, err, tc.expectedError)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/docker/cli/internal/jsonstream"
|
||||
"github.com/docker/cli/internal/tui"
|
||||
"github.com/moby/moby/api/types/auxprogress"
|
||||
@@ -27,11 +26,10 @@ import (
|
||||
)
|
||||
|
||||
type pushOptions struct {
|
||||
all bool
|
||||
remote string
|
||||
untrusted bool
|
||||
quiet bool
|
||||
platform string
|
||||
all bool
|
||||
remote string
|
||||
quiet bool
|
||||
platform string
|
||||
}
|
||||
|
||||
// newPushCommand creates a new `docker push` command
|
||||
@@ -57,7 +55,10 @@ func newPushCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&opts.all, "all-tags", "a", false, "Push all tags of an image to the repository")
|
||||
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress verbose output")
|
||||
flags.BoolVar(&opts.untrusted, "disable-content-trust", !trust.Enabled(), "Skip image signing")
|
||||
|
||||
// TODO(thaJeztah): DEPRECATED: remove in v29.1 or v30
|
||||
flags.Bool("disable-content-trust", true, "Skip image verification (deprecated)")
|
||||
_ = flags.MarkDeprecated("disable-content-trust", "support for docker content trust was removed")
|
||||
|
||||
// Don't default to DOCKER_DEFAULT_PLATFORM env variable, always default to
|
||||
// pushing the image as-is. This also avoids forcing the platform selection
|
||||
@@ -129,10 +130,6 @@ To push the complete multi-platform image, remove the --platform flag.
|
||||
}
|
||||
}()
|
||||
|
||||
if !opts.untrusted {
|
||||
return pushTrustedReference(ctx, dockerCli, ref, responseBody)
|
||||
}
|
||||
|
||||
if opts.quiet {
|
||||
err = jsonstream.Display(ctx, responseBody, streams.NewOut(io.Discard), jsonstream.WithAuxCallback(handleAux()))
|
||||
if err == nil {
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/docker/cli/internal/registry"
|
||||
registrytypes "github.com/moby/moby/api/types/registry"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/sirupsen/logrus"
|
||||
notaryclient "github.com/theupdateframework/notary/client"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
)
|
||||
|
||||
type target struct {
|
||||
name string
|
||||
digest digest.Digest
|
||||
size int64
|
||||
}
|
||||
|
||||
// notaryClientProvider is used in tests to provide a dummy notary client.
|
||||
type notaryClientProvider interface {
|
||||
NotaryClient() (notaryclient.Repository, error)
|
||||
}
|
||||
|
||||
// newNotaryClient provides a Notary Repository to interact with signed metadata for an image.
|
||||
func newNotaryClient(cli command.Streams, repoInfo *trust.RepositoryInfo, authConfig *registrytypes.AuthConfig) (notaryclient.Repository, error) {
|
||||
if ncp, ok := cli.(notaryClientProvider); ok {
|
||||
// notaryClientProvider is used in tests to provide a dummy notary client.
|
||||
return ncp.NotaryClient()
|
||||
}
|
||||
return trust.GetNotaryRepository(cli.In(), cli.Out(), command.UserAgent(), repoInfo, authConfig, "pull")
|
||||
}
|
||||
|
||||
// pushTrustedReference pushes a canonical reference to the trust server.
|
||||
func pushTrustedReference(ctx context.Context, dockerCLI command.Cli, ref reference.Named, responseBody io.Reader) error {
|
||||
// Resolve the Repository name from fqn to RepositoryInfo, and create an
|
||||
// IndexInfo. Docker Content Trust uses the IndexInfo.Official field to
|
||||
// select the right domain for Docker Hub's Notary server;
|
||||
// https://github.com/docker/cli/blob/v28.4.0/cli/trust/trust.go#L65-L79
|
||||
indexInfo := registry.NewIndexInfo(ref)
|
||||
repoInfo := &trust.RepositoryInfo{
|
||||
Name: reference.TrimNamed(ref),
|
||||
Index: indexInfo,
|
||||
}
|
||||
authConfig := command.ResolveAuthConfig(dockerCLI.ConfigFile(), indexInfo)
|
||||
return trust.PushTrustedReference(ctx, dockerCLI, repoInfo, ref, authConfig, responseBody, command.UserAgent())
|
||||
}
|
||||
|
||||
// trustedPull handles content trust pulling of an image
|
||||
func trustedPull(ctx context.Context, cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth, opts pullOptions) error {
|
||||
refs, err := getTrustedPullTargets(cli, imgRefAndAuth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ref := imgRefAndAuth.Reference()
|
||||
for i, r := range refs {
|
||||
displayTag := r.name
|
||||
if displayTag != "" {
|
||||
displayTag = ":" + displayTag
|
||||
}
|
||||
_, _ = fmt.Fprintf(cli.Out(), "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), reference.FamiliarName(ref), displayTag, r.digest)
|
||||
|
||||
trustedRef, err := reference.WithDigest(reference.TrimNamed(ref), r.digest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updatedImgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, authResolver(cli), trustedRef.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := imagePullPrivileged(ctx, cli, updatedImgRefAndAuth.Reference(), updatedImgRefAndAuth.AuthConfig(), pullOptions{
|
||||
all: false,
|
||||
platform: opts.platform,
|
||||
quiet: opts.quiet,
|
||||
remote: opts.remote,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tagged, err := reference.WithTag(reference.TrimNamed(ref), r.name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use familiar references when interacting with client and output
|
||||
familiarRef := reference.FamiliarString(tagged)
|
||||
trustedFamiliarRef := reference.FamiliarString(trustedRef)
|
||||
_, _ = fmt.Fprintf(cli.Err(), "Tagging %s as %s\n", trustedFamiliarRef, familiarRef)
|
||||
_, err = cli.Client().ImageTag(ctx, client.ImageTagOptions{
|
||||
Source: trustedFamiliarRef,
|
||||
Target: familiarRef,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth) ([]target, error) {
|
||||
notaryRepo, err := newNotaryClient(cli, imgRefAndAuth.RepoInfo(), imgRefAndAuth.AuthConfig())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error establishing connection to trust repository: %w", err)
|
||||
}
|
||||
|
||||
ref := imgRefAndAuth.Reference()
|
||||
tagged, isTagged := ref.(reference.NamedTagged)
|
||||
if !isTagged {
|
||||
// List all targets
|
||||
targets, err := notaryRepo.ListTargets(trust.ReleasesRole, data.CanonicalTargetsRole)
|
||||
if err != nil {
|
||||
return nil, trust.NotaryError(ref.Name(), err)
|
||||
}
|
||||
var refs []target
|
||||
for _, tgt := range targets {
|
||||
t, err := convertTarget(tgt.Target)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(cli.Err(), "Skipping target for %q\n", reference.FamiliarName(ref))
|
||||
continue
|
||||
}
|
||||
// Only list tags in the top level targets role or the releases delegation role - ignore
|
||||
// all other delegation roles
|
||||
if tgt.Role != trust.ReleasesRole && tgt.Role != data.CanonicalTargetsRole {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, t)
|
||||
}
|
||||
if len(refs) == 0 {
|
||||
return nil, trust.NotaryError(ref.Name(), fmt.Errorf("no trusted tags for %s", ref.Name()))
|
||||
}
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
t, err := notaryRepo.GetTargetByName(tagged.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
|
||||
if err != nil {
|
||||
return nil, trust.NotaryError(ref.Name(), err)
|
||||
}
|
||||
// Only get the tag if it's in the top level targets role or the releases delegation role
|
||||
// ignore it if it's in any other delegation roles
|
||||
if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
|
||||
return nil, trust.NotaryError(ref.Name(), fmt.Errorf("no trust data for %s", tagged.Tag()))
|
||||
}
|
||||
|
||||
logrus.Debugf("retrieving target for %s role", t.Role)
|
||||
r, err := convertTarget(t.Target)
|
||||
return []target{r}, err
|
||||
}
|
||||
|
||||
// TrustedReference returns the canonical trusted reference for an image reference
|
||||
func TrustedReference(ctx context.Context, cli command.Cli, ref reference.NamedTagged) (reference.Canonical, error) {
|
||||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, authResolver(cli), ref.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
notaryRepo, err := newNotaryClient(cli, imgRefAndAuth.RepoInfo(), imgRefAndAuth.AuthConfig())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error establishing connection to trust repository: %w", err)
|
||||
}
|
||||
|
||||
t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
|
||||
if err != nil {
|
||||
return nil, trust.NotaryError(imgRefAndAuth.RepoInfo().Name.Name(), err)
|
||||
}
|
||||
// Only list tags in the top level targets role or the releases delegation role - ignore
|
||||
// all other delegation roles
|
||||
if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
|
||||
return nil, trust.NotaryError(imgRefAndAuth.RepoInfo().Name.Name(), notaryclient.ErrNoSuchTarget(ref.Tag()))
|
||||
}
|
||||
r, err := convertTarget(t.Target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return reference.WithDigest(reference.TrimNamed(ref), r.digest)
|
||||
}
|
||||
|
||||
func convertTarget(t notaryclient.Target) (target, error) {
|
||||
h, ok := t.Hashes["sha256"]
|
||||
if !ok {
|
||||
return target{}, errors.New("no valid hash, expecting sha256")
|
||||
}
|
||||
return target{
|
||||
name: t.Name,
|
||||
digest: digest.NewDigestFromHex("sha256", hex.EncodeToString(h)),
|
||||
size: t.Length,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// authResolver returns an auth resolver function from a [config.Provider].
|
||||
func authResolver(dockerCLI config.Provider) func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
|
||||
return func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
|
||||
return command.ResolveAuthConfig(dockerCLI.ConfigFile(), index)
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,27 @@ func newManifestStore(dockerCLI command.Cli) store.Store {
|
||||
return store.NewStore(filepath.Join(config.Dir(), "manifests"))
|
||||
}
|
||||
|
||||
// authConfigKey is the key used to store credentials for Docker Hub. It is
|
||||
// a copy of [registry.IndexServer].
|
||||
//
|
||||
// [registry.IndexServer]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/registry#IndexServer
|
||||
const authConfigKey = "https://index.docker.io/v1/"
|
||||
|
||||
// getAuthConfigKey special-cases using the full index address of the official
|
||||
// index as the AuthConfig key, and uses the (host)name[:port] for private indexes.
|
||||
//
|
||||
// It is similar to [registry.GetAuthConfigKey], but does not require on
|
||||
// [registrytypes.IndexInfo] as intermediate.
|
||||
//
|
||||
// [registry.GetAuthConfigKey]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/registry#GetAuthConfigKey
|
||||
// [registrytypes.IndexInfo]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/api/types/registry#IndexInfo
|
||||
func getAuthConfigKey(domainName string) string {
|
||||
if domainName == "docker.io" || domainName == "index.docker.io" {
|
||||
return authConfigKey
|
||||
}
|
||||
return domainName
|
||||
}
|
||||
|
||||
// newRegistryClient returns a client for communicating with a Docker distribution
|
||||
// registry
|
||||
func newRegistryClient(dockerCLI command.Cli, allowInsecure bool) registryclient.RegistryClient {
|
||||
@@ -51,8 +72,20 @@ func newRegistryClient(dockerCLI command.Cli, allowInsecure bool) registryclient
|
||||
// manifestStoreProvider is used in tests to provide a dummy store.
|
||||
return msp.RegistryClient(allowInsecure)
|
||||
}
|
||||
resolver := func(ctx context.Context, index *registry.IndexInfo) registry.AuthConfig {
|
||||
return command.ResolveAuthConfig(dockerCLI.ConfigFile(), index)
|
||||
cfg := dockerCLI.ConfigFile()
|
||||
resolver := func(ctx context.Context, domainName string) registry.AuthConfig {
|
||||
configKey := getAuthConfigKey(domainName)
|
||||
a, _ := cfg.GetAuthConfig(configKey)
|
||||
return registry.AuthConfig{
|
||||
Username: a.Username,
|
||||
Password: a.Password,
|
||||
ServerAddress: a.ServerAddress,
|
||||
|
||||
// TODO(thaJeztah): Are these expected to be included?
|
||||
Auth: a.Auth,
|
||||
IdentityToken: a.IdentityToken,
|
||||
RegistryToken: a.RegistryToken,
|
||||
}
|
||||
}
|
||||
// FIXME(thaJeztah): this should use the userAgent as configured on the dockerCLI.
|
||||
return registryclient.NewRegistryClient(resolver, command.UserAgent(), allowInsecure)
|
||||
|
||||
@@ -44,8 +44,10 @@ func newInstallCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
flags.BoolVar(&options.grantPerms, "grant-all-permissions", false, "Grant all permissions necessary to run the plugin")
|
||||
flags.BoolVar(&options.disable, "disable", false, "Do not enable the plugin on install")
|
||||
flags.StringVar(&options.localName, "alias", "", "Local name for plugin")
|
||||
|
||||
// TODO(thaJeztah): DEPRECATED: remove in v29.1 or v30
|
||||
flags.Bool("disable-content-trust", true, "Skip image verification (deprecated)")
|
||||
_ = flags.MarkHidden("disable-content-trust")
|
||||
_ = flags.MarkDeprecated("disable-content-trust", "support for docker content trust was removed")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,9 @@ func newPushCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
// TODO(thaJeztah): DEPRECATED: remove in v29.1 or v30
|
||||
flags.Bool("disable-content-trust", true, "Skip image verification (deprecated)")
|
||||
_ = flags.MarkHidden("disable-content-trust")
|
||||
_ = flags.MarkDeprecated("disable-content-trust", "support for docker content trust was removed")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,9 @@ func newUpgradeCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVar(&options.grantPerms, "grant-all-permissions", false, "Grant all permissions necessary to run the plugin")
|
||||
// TODO(thaJeztah): DEPRECATED: remove in v29.1 or v30
|
||||
flags.Bool("disable-content-trust", true, "Skip image verification (deprecated)")
|
||||
_ = flags.MarkHidden("disable-content-trust")
|
||||
_ = flags.MarkDeprecated("disable-content-trust", "support for docker content trust was removed")
|
||||
flags.BoolVar(&options.skipRemoteCheck, "skip-remote-check", false, "Do not check if specified remote plugin matches existing plugin image")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -39,10 +39,7 @@ const authConfigKey = "https://index.docker.io/v1/"
|
||||
// credential-store. It returns an empty AuthConfig if no credentials were
|
||||
// found.
|
||||
//
|
||||
// It is similar to [registry.ResolveAuthConfig], but uses the credentials-
|
||||
// store, instead of looking up credentials from a map.
|
||||
//
|
||||
// [registry.ResolveAuthConfig]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/registry#ResolveAuthConfig
|
||||
// Deprecated: this function is no longer used, and will be removed in the next release.
|
||||
func ResolveAuthConfig(cfg *configfile.ConfigFile, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
|
||||
configKey := index.Name
|
||||
if index.Official {
|
||||
|
||||
@@ -115,10 +115,6 @@ func runCreate(ctx context.Context, dockerCLI command.Cli, flags *pflag.FlagSet,
|
||||
return err
|
||||
}
|
||||
|
||||
if err := resolveServiceImageDigestContentTrust(dockerCLI, &service); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// only send auth if flag was set
|
||||
var encodedAuth string
|
||||
if opts.registryAuth {
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/docker/cli/internal/registry"
|
||||
"github.com/moby/moby/api/types/swarm"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
)
|
||||
|
||||
func resolveServiceImageDigestContentTrust(dockerCli command.Cli, service *swarm.ServiceSpec) error {
|
||||
if !trust.Enabled() {
|
||||
// When not using content trust, digest resolution happens later when
|
||||
// contacting the registry to retrieve image information.
|
||||
return nil
|
||||
}
|
||||
|
||||
ref, err := reference.ParseAnyReference(service.TaskTemplate.ContainerSpec.Image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid reference %s: %w", service.TaskTemplate.ContainerSpec.Image, err)
|
||||
}
|
||||
|
||||
// If reference does not have digest (is not canonical nor image id)
|
||||
if _, ok := ref.(reference.Digested); !ok {
|
||||
namedRef, ok := ref.(reference.Named)
|
||||
if !ok {
|
||||
return errors.New("failed to resolve image digest using content trust: reference is not named")
|
||||
}
|
||||
namedRef = reference.TagNameOnly(namedRef)
|
||||
taggedRef, ok := namedRef.(reference.NamedTagged)
|
||||
if !ok {
|
||||
return errors.New("failed to resolve image digest using content trust: reference is not tagged")
|
||||
}
|
||||
|
||||
resolvedImage, err := trustedResolveDigest(dockerCli, taggedRef)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to resolve image digest using content trust: %w", err)
|
||||
}
|
||||
resolvedFamiliar := reference.FamiliarString(resolvedImage)
|
||||
logrus.Debugf("resolved image tag to %s using content trust", resolvedFamiliar)
|
||||
service.TaskTemplate.ContainerSpec.Image = resolvedFamiliar
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func trustedResolveDigest(cli command.Cli, ref reference.NamedTagged) (reference.Canonical, error) {
|
||||
indexInfo := registry.NewIndexInfo(ref)
|
||||
authConfig := command.ResolveAuthConfig(cli.ConfigFile(), indexInfo)
|
||||
repoInfo := &trust.RepositoryInfo{
|
||||
Name: reference.TrimNamed(ref),
|
||||
Index: indexInfo,
|
||||
}
|
||||
notaryRepo, err := trust.GetNotaryRepository(cli.In(), cli.Out(), command.UserAgent(), repoInfo, &authConfig, "pull")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error establishing connection to trust repository: %w", err)
|
||||
}
|
||||
|
||||
t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
|
||||
if err != nil {
|
||||
return nil, trust.NotaryError(repoInfo.Name.Name(), err)
|
||||
}
|
||||
// Only get the tag if it's in the top level targets role or the releases delegation role
|
||||
// ignore it if it's in any other delegation roles
|
||||
if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole {
|
||||
return nil, trust.NotaryError(repoInfo.Name.Name(), fmt.Errorf("no trust data for %s", reference.FamiliarString(ref)))
|
||||
}
|
||||
|
||||
logrus.Debugf("retrieving target for %s role", t.Role)
|
||||
h, ok := t.Hashes["sha256"]
|
||||
if !ok {
|
||||
return nil, errors.New("no valid hash, expecting sha256")
|
||||
}
|
||||
|
||||
dgst := digest.NewDigestFromHex("sha256", hex.EncodeToString(h))
|
||||
|
||||
// Allow returning canonical reference with tag
|
||||
return reference.WithDigest(ref, dgst)
|
||||
}
|
||||
@@ -193,9 +193,6 @@ func runUpdate(ctx context.Context, dockerCLI command.Cli, flags *pflag.FlagSet,
|
||||
}
|
||||
|
||||
if flags.Changed("image") {
|
||||
if err := resolveServiceImageDigestContentTrust(dockerCLI, spec); err != nil {
|
||||
return err
|
||||
}
|
||||
updateOpts.QueryRegistry = !options.noResolveImage
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/fvbommel/sortorder"
|
||||
registrytypes "github.com/moby/moby/api/types/registry"
|
||||
@@ -172,6 +173,39 @@ func matchReleasedSignatures(allTargets []client.TargetSignedStruct) []trustTagR
|
||||
// authResolver returns an auth resolver function from a [config.Provider].
|
||||
func authResolver(dockerCLI config.Provider) func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
|
||||
return func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
|
||||
return command.ResolveAuthConfig(dockerCLI.ConfigFile(), index)
|
||||
return resolveAuthConfig(dockerCLI.ConfigFile(), index)
|
||||
}
|
||||
}
|
||||
|
||||
// authConfigKey is the key used to store credentials for Docker Hub. It is
|
||||
// a copy of [registry.IndexServer].
|
||||
//
|
||||
// [registry.IndexServer]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/registry#IndexServer
|
||||
const authConfigKey = "https://index.docker.io/v1/"
|
||||
|
||||
// resolveAuthConfig returns auth-config for the given registry from the
|
||||
// credential-store. It returns an empty AuthConfig if no credentials were
|
||||
// found.
|
||||
//
|
||||
// It is similar to [registry.ResolveAuthConfig], but uses the credentials-
|
||||
// store, instead of looking up credentials from a map.
|
||||
//
|
||||
// [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
|
||||
if index.Official {
|
||||
configKey = authConfigKey
|
||||
}
|
||||
|
||||
a, _ := cfg.GetAuthConfig(configKey)
|
||||
return registrytypes.AuthConfig{
|
||||
Username: a.Username,
|
||||
Password: a.Password,
|
||||
ServerAddress: a.ServerAddress,
|
||||
|
||||
// TODO(thaJeztah): Are these expected to be included?
|
||||
Auth: a.Auth,
|
||||
IdentityToken: a.IdentityToken,
|
||||
RegistryToken: a.RegistryToken,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ func runSignImage(ctx context.Context, dockerCLI command.Cli, options signOption
|
||||
}
|
||||
_, _ = fmt.Fprintf(dockerCLI.Err(), "Signing and pushing trust data for local image %s, may overwrite remote trust data\n", imageName)
|
||||
|
||||
authConfig := command.ResolveAuthConfig(dockerCLI.ConfigFile(), imgRefAndAuth.RepoInfo().Index)
|
||||
authConfig := resolveAuthConfig(dockerCLI.ConfigFile(), imgRefAndAuth.RepoInfo().Index)
|
||||
encodedAuth, err := authconfig.Encode(authConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -37,7 +37,6 @@ Create a new container
|
||||
| `--device-read-iops` | `list` | | Limit read rate (IO per second) from a device |
|
||||
| `--device-write-bps` | `list` | | Limit write rate (bytes per second) to a device |
|
||||
| `--device-write-iops` | `list` | | Limit write rate (IO per second) to a device |
|
||||
| `--disable-content-trust` | `bool` | `true` | Skip image verification |
|
||||
| `--dns` | `list` | | Set custom DNS servers |
|
||||
| `--dns-option` | `list` | | Set DNS options |
|
||||
| `--dns-search` | `list` | | Set custom DNS search domains |
|
||||
|
||||
@@ -39,7 +39,6 @@ Create and run a new container from an image
|
||||
| `--device-read-iops` | `list` | | Limit read rate (IO per second) from a device |
|
||||
| `--device-write-bps` | `list` | | Limit write rate (bytes per second) to a device |
|
||||
| `--device-write-iops` | `list` | | Limit write rate (IO per second) to a device |
|
||||
| `--disable-content-trust` | `bool` | `true` | Skip image verification |
|
||||
| `--dns` | `list` | | Set custom DNS servers |
|
||||
| `--dns-option` | `list` | | Set DNS options |
|
||||
| `--dns-search` | `list` | | Set custom DNS search domains |
|
||||
|
||||
@@ -37,7 +37,6 @@ Create a new container
|
||||
| `--device-read-iops` | `list` | | Limit read rate (IO per second) from a device |
|
||||
| `--device-write-bps` | `list` | | Limit write rate (bytes per second) to a device |
|
||||
| `--device-write-iops` | `list` | | Limit write rate (IO per second) to a device |
|
||||
| `--disable-content-trust` | `bool` | `true` | Skip image verification |
|
||||
| `--dns` | `list` | | Set custom DNS servers |
|
||||
| `--dns-option` | `list` | | Set DNS options |
|
||||
| `--dns-search` | `list` | | Set custom DNS search domains |
|
||||
|
||||
@@ -123,8 +123,6 @@ line:
|
||||
| `DOCKER_API_VERSION` | Override the negotiated API version to use for debugging (e.g. `1.19`) |
|
||||
| `DOCKER_CERT_PATH` | Location of your authentication keys. This variable is used both by the `docker` CLI and the [`dockerd` daemon](https://docs.docker.com/reference/cli/dockerd/) |
|
||||
| `DOCKER_CONFIG` | The location of your client configuration files. |
|
||||
| `DOCKER_CONTENT_TRUST_SERVER` | The URL of the Notary server to use. Defaults to the same URL as the registry. |
|
||||
| `DOCKER_CONTENT_TRUST` | When set Docker uses notary to sign and verify images. Equates to `--disable-content-trust=false` for build, create, pull, push, run. |
|
||||
| `DOCKER_CONTEXT` | Name of the `docker context` to use (overrides `DOCKER_HOST` env var and default context set with `docker context use`) |
|
||||
| `DOCKER_CUSTOM_HEADERS` | (Experimental) Configure [custom HTTP headers](#custom-http-headers) to be sent by the client. Headers must be provided as a comma-separated list of `name=value` pairs. This is the equivalent to the `HttpHeaders` field in the configuration file. |
|
||||
| `DOCKER_DEFAULT_PLATFORM` | Default platform for commands that take the `--platform` flag. |
|
||||
|
||||
@@ -12,7 +12,6 @@ Download an image from a registry
|
||||
| Name | Type | Default | Description |
|
||||
|:---------------------------------------------|:---------|:--------|:-------------------------------------------------|
|
||||
| [`-a`](#all-tags), [`--all-tags`](#all-tags) | `bool` | | Download all tagged images in the repository |
|
||||
| `--disable-content-trust` | `bool` | `true` | Skip image verification |
|
||||
| `--platform` | `string` | | Set platform if server is multi-platform capable |
|
||||
| `-q`, `--quiet` | `bool` | | Suppress verbose output |
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ Upload an image to a registry
|
||||
| Name | Type | Default | Description |
|
||||
|:---------------------------------------------|:---------|:--------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [`-a`](#all-tags), [`--all-tags`](#all-tags) | `bool` | | Push all tags of an image to the repository |
|
||||
| `--disable-content-trust` | `bool` | `true` | Skip image signing |
|
||||
| `--platform` | `string` | | Push a platform-specific manifest as a single-platform image to the registry.<br>Image index won't be pushed, meaning that other manifests, including attestations won't be preserved.<br>'os[/arch[/variant]]': Explicit platform (eg. linux/amd64) |
|
||||
| `-q`, `--quiet` | `bool` | | Suppress verbose output |
|
||||
|
||||
|
||||
@@ -9,12 +9,11 @@ Download an image from a registry
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------------------|:---------|:--------|:-------------------------------------------------|
|
||||
| `-a`, `--all-tags` | `bool` | | Download all tagged images in the repository |
|
||||
| `--disable-content-trust` | `bool` | `true` | Skip image verification |
|
||||
| `--platform` | `string` | | Set platform if server is multi-platform capable |
|
||||
| `-q`, `--quiet` | `bool` | | Suppress verbose output |
|
||||
| Name | Type | Default | Description |
|
||||
|:-------------------|:---------|:--------|:-------------------------------------------------|
|
||||
| `-a`, `--all-tags` | `bool` | | Download all tagged images in the repository |
|
||||
| `--platform` | `string` | | Set platform if server is multi-platform capable |
|
||||
| `-q`, `--quiet` | `bool` | | Suppress verbose output |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -9,12 +9,11 @@ Upload an image to a registry
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------------------|:---------|:--------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `-a`, `--all-tags` | `bool` | | Push all tags of an image to the repository |
|
||||
| `--disable-content-trust` | `bool` | `true` | Skip image signing |
|
||||
| `--platform` | `string` | | Push a platform-specific manifest as a single-platform image to the registry.<br>Image index won't be pushed, meaning that other manifests, including attestations won't be preserved.<br>'os[/arch[/variant]]': Explicit platform (eg. linux/amd64) |
|
||||
| `-q`, `--quiet` | `bool` | | Suppress verbose output |
|
||||
| Name | Type | Default | Description |
|
||||
|:-------------------|:---------|:--------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `-a`, `--all-tags` | `bool` | | Push all tags of an image to the repository |
|
||||
| `--platform` | `string` | | Push a platform-specific manifest as a single-platform image to the registry.<br>Image index won't be pushed, meaning that other manifests, including attestations won't be preserved.<br>'os[/arch[/variant]]': Explicit platform (eg. linux/amd64) |
|
||||
| `-q`, `--quiet` | `bool` | | Suppress verbose output |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -39,7 +39,6 @@ Create and run a new container from an image
|
||||
| `--device-read-iops` | `list` | | Limit read rate (IO per second) from a device |
|
||||
| `--device-write-bps` | `list` | | Limit write rate (bytes per second) to a device |
|
||||
| `--device-write-iops` | `list` | | Limit write rate (IO per second) to a device |
|
||||
| `--disable-content-trust` | `bool` | `true` | Skip image verification |
|
||||
| `--dns` | `list` | | Set custom DNS servers |
|
||||
| `--dns-option` | `list` | | Set DNS options |
|
||||
| `--dns-search` | `list` | | Set custom DNS search domains |
|
||||
|
||||
@@ -34,7 +34,7 @@ func NewRegistryClient(resolver AuthConfigResolver, userAgent string, insecure b
|
||||
}
|
||||
|
||||
// AuthConfigResolver returns Auth Configuration for an index
|
||||
type AuthConfigResolver func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig
|
||||
type AuthConfigResolver func(ctx context.Context, hostName string) registrytypes.AuthConfig
|
||||
|
||||
type client struct {
|
||||
authConfigResolver AuthConfigResolver
|
||||
@@ -146,7 +146,7 @@ func (c *client) getRepositoryForReference(ctx context.Context, ref reference.Na
|
||||
|
||||
func (c *client) getHTTPTransportForRepoEndpoint(ctx context.Context, repoEndpoint repositoryEndpoint) (http.RoundTripper, error) {
|
||||
httpTransport, err := getHTTPTransport(
|
||||
c.authConfigResolver(ctx, repoEndpoint.indexInfo),
|
||||
c.authConfigResolver(ctx, repoEndpoint.indexInfo.Name),
|
||||
repoEndpoint.endpoint,
|
||||
repoEndpoint.repoName,
|
||||
c.userAgent,
|
||||
|
||||
Reference in New Issue
Block a user