diff --git a/Dockerfile b/Dockerfile index 792ebc6fa4e5..d28d46046ee4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -105,10 +105,6 @@ FROM docker/buildx-bin:${BUILDX_VERSION} AS buildx FROM docker/compose-bin:${COMPOSE_VERSION} AS compose FROM e2e-base-${BASE_VARIANT} AS e2e -ARG NOTARY_VERSION=v0.6.1 -ADD --chmod=0755 https://github.com/theupdateframework/notary/releases/download/${NOTARY_VERSION}/notary-Linux-amd64 /usr/local/bin/notary -COPY --link e2e/testdata/notary/root-ca.cert /usr/share/ca-certificates/notary.cert -RUN echo 'notary.cert' >> /etc/ca-certificates.conf && update-ca-certificates COPY --link --from=gotestsum /out/gotestsum /usr/bin/gotestsum COPY --link --from=build /out ./build/ COPY --link --from=build-plugins /out ./build/ diff --git a/cli/command/cli.go b/cli/command/cli.go index 1e042ec0e23b..440f9f72512d 100644 --- a/cli/command/cli.go +++ b/cli/command/cli.go @@ -50,7 +50,6 @@ type Cli interface { ServerInfo() ServerInfo DefaultVersion() string CurrentVersion() string - ContentTrustEnabled() bool BuildKitEnabled() (bool, error) ContextStore() store.Store CurrentContext() string @@ -70,7 +69,6 @@ type DockerCli struct { err *streams.Out client client.APIClient serverInfo ServerInfo - contentTrust bool contextStore store.Store currentContext string init sync.Once @@ -157,12 +155,6 @@ func (cli *DockerCli) ServerInfo() ServerInfo { return cli.serverInfo } -// ContentTrustEnabled returns whether content trust has been enabled by an -// environment variable. -func (cli *DockerCli) ContentTrustEnabled() bool { - return cli.contentTrust -} - // BuildKitEnabled returns buildkit is enabled or not. func (cli *DockerCli) BuildKitEnabled() (bool, error) { // use DOCKER_BUILDKIT env var value if set and not empty @@ -523,11 +515,9 @@ type ServerInfo struct { } // NewDockerCli returns a DockerCli instance with all operators applied on it. -// It applies by default the standard streams, and the content trust from -// environment. +// It applies by default the standard streams. func NewDockerCli(ops ...CLIOption) (*DockerCli, error) { defaultOps := []CLIOption{ - WithContentTrustFromEnv(), WithDefaultContextStoreConfig(), WithStandardStreams(), } diff --git a/cli/command/cli_options.go b/cli/command/cli_options.go index dd3c9473369d..10fdcb51c0fe 100644 --- a/cli/command/cli_options.go +++ b/cli/command/cli_options.go @@ -6,7 +6,6 @@ import ( "io" "net/http" "os" - "strconv" "strings" "github.com/docker/cli/cli/streams" @@ -75,28 +74,6 @@ func WithErrorStream(err io.Writer) CLIOption { } } -// WithContentTrustFromEnv enables content trust on a cli from environment variable DOCKER_CONTENT_TRUST value. -func WithContentTrustFromEnv() CLIOption { - return func(cli *DockerCli) error { - cli.contentTrust = false - if e := os.Getenv("DOCKER_CONTENT_TRUST"); e != "" { - if t, err := strconv.ParseBool(e); t || err != nil { - // treat any other value as true - cli.contentTrust = true - } - } - return nil - } -} - -// WithContentTrust enables content trust on a cli. -func WithContentTrust(enabled bool) CLIOption { - return func(cli *DockerCli) error { - cli.contentTrust = enabled - return nil - } -} - // WithDefaultContextStoreConfig configures the cli to use the default context store configuration. func WithDefaultContextStoreConfig() CLIOption { return func(cli *DockerCli) error { diff --git a/cli/command/cli_options_test.go b/cli/command/cli_options_test.go deleted file mode 100644 index 45ac1d8a5773..000000000000 --- a/cli/command/cli_options_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package command - -import ( - "os" - "testing" - - "gotest.tools/v3/assert" -) - -func contentTrustEnabled(t *testing.T) bool { - t.Helper() - var cli DockerCli - assert.NilError(t, WithContentTrustFromEnv()(&cli)) - return cli.contentTrust -} - -// NB: Do not t.Parallel() this test -- it messes with the process environment. -func TestWithContentTrustFromEnv(t *testing.T) { - const envvar = "DOCKER_CONTENT_TRUST" - t.Setenv(envvar, "true") - assert.Check(t, contentTrustEnabled(t)) - t.Setenv(envvar, "false") - assert.Check(t, !contentTrustEnabled(t)) - t.Setenv(envvar, "invalid") - assert.Check(t, contentTrustEnabled(t)) - os.Unsetenv(envvar) - assert.Check(t, !contentTrustEnabled(t)) -} diff --git a/cli/command/commands/commands.go b/cli/command/commands/commands.go index d3929293999d..e96a0f1a1ed4 100644 --- a/cli/command/commands/commands.go +++ b/cli/command/commands/commands.go @@ -20,7 +20,6 @@ import ( "github.com/docker/cli/cli/command/stack" "github.com/docker/cli/cli/command/swarm" "github.com/docker/cli/cli/command/system" - "github.com/docker/cli/cli/command/trust" "github.com/docker/cli/cli/command/volume" "github.com/spf13/cobra" ) @@ -53,7 +52,6 @@ func AddCommands(cmd *cobra.Command, dockerCli command.Cli) { network.NewNetworkCommand(dockerCli), plugin.NewPluginCommand(dockerCli), system.NewSystemCommand(dockerCli), - trust.NewTrustCommand(dockerCli), volume.NewVolumeCommand(dockerCli), // orchestration (swarm) commands diff --git a/cli/command/container/create.go b/cli/command/container/create.go index beb11995de68..109708d631ca 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -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/docker/docker/api/types/container" @@ -45,7 +43,6 @@ const ( type createOptions struct { name string platform string - untrusted bool pull string // always, missing, never quiet bool useAPISocket bool @@ -87,7 +84,7 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command { flags.Bool("help", false, "Print usage") command.AddPlatformFlag(flags, &options.platform) - command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled()) + // TODO add a (hidden) --disable-content-trust flag that throws a deprecation/removal warning and does nothing copts = addFlags(flags) addCompletions(cmd, dockerCli) @@ -212,11 +209,6 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c hostConfig := containerCfg.HostConfig networkingConfig := containerCfg.NetworkingConfig - var ( - trustedRef reference.Canonical - namedRef reference.Named - ) - containerIDFile, err := newCIDFile(hostConfig.ContainerIDFile) if err != nil { return "", err @@ -227,17 +219,9 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c if err != nil { return "", err } + var namedRef reference.Named 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" @@ -321,18 +305,8 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c platform = &p } - pullAndTagImage := func() error { - 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 - } - if options.pull == PullImageAlways { - if err := pullAndTagImage(); err != nil { + if err := pullImage(ctx, dockerCli, config.Image, options); err != nil { return "", err } } @@ -348,7 +322,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c _, _ = fmt.Fprintf(dockerCli.Err(), "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef)) } - if err := pullAndTagImage(); err != nil { + if err := pullImage(ctx, dockerCli, config.Image, options); err != nil { return "", err } diff --git a/cli/command/container/create_test.go b/cli/command/container/create_test.go index fd94b624c822..90484a058538 100644 --- a/cli/command/container/create_test.go +++ b/cli/command/container/create_test.go @@ -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/docker/docker/api/types/container" "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/network" @@ -142,10 +141,9 @@ func TestCreateContainerImagePullPolicy(t *testing.T) { } fakeCLI := test.NewFakeCli(client) 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 != "" { @@ -221,55 +219,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) { - fakeCLI := test.NewFakeCli(&fakeClient{ - createContainerFunc: func(config *container.Config, - hostConfig *container.HostConfig, - networkingConfig *network.NetworkingConfig, - platform *ocispec.Platform, - containerName string, - ) (container.CreateResponse, error) { - return container.CreateResponse{}, errors.New("shouldn't try to pull image") - }, - }, test.EnableContentTrust) - 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 diff --git a/cli/command/container/run.go b/cli/command/container/run.go index b86ad9d5b275..93a352aed207 100644 --- a/cli/command/container/run.go +++ b/cli/command/container/run.go @@ -67,7 +67,7 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command { flags.Bool("help", false, "Print usage") command.AddPlatformFlag(flags, &options.platform) - command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled()) + // TODO add a (hidden) --disable-content-trust flag that throws a deprecation/removal warning and does nothing copts = addFlags(flags) _ = cmd.RegisterFlagCompletionFunc("detach-keys", completeDetachKeys) diff --git a/cli/command/container/run_test.go b/cli/command/container/run_test.go index f60ebde360a1..1092123b24bf 100644 --- a/cli/command/container/run_test.go +++ b/cli/command/container/run_test.go @@ -15,7 +15,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/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/image" @@ -300,58 +299,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) { - fakeCLI := test.NewFakeCli(&fakeClient{ - createContainerFunc: func(config *container.Config, - hostConfig *container.HostConfig, - networkingConfig *network.NetworkingConfig, - platform *ocispec.Platform, - containerName string, - ) (container.CreateResponse, error) { - return container.CreateResponse{}, errors.New("shouldn't try to pull image") - }, - }, test.EnableContentTrust) - 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 diff --git a/cli/command/image/build.go b/cli/command/image/build.go index 66beeee2bba2..8bb362a3160a 100644 --- a/cli/command/image/build.go +++ b/cli/command/image/build.go @@ -1,8 +1,6 @@ package image import ( - "archive/tar" - "bufio" "bytes" "context" "encoding/json" @@ -20,11 +18,8 @@ import ( "github.com/docker/cli/cli/command/completion" "github.com/docker/cli/cli/command/image/build" "github.com/docker/cli/cli/streams" - "github.com/docker/cli/cli/trust" "github.com/docker/cli/internal/jsonstream" - "github.com/docker/cli/internal/lazyregexp" "github.com/docker/cli/opts" - "github.com/docker/docker/api" buildtypes "github.com/docker/docker/api/types/build" "github.com/docker/docker/api/types/container" registrytypes "github.com/docker/docker/api/types/registry" @@ -67,7 +62,6 @@ type buildOptions struct { target string imageIDFile string platform string - untrusted bool } // dockerfileFromStdin returns true when the user specified that the Dockerfile @@ -152,7 +146,7 @@ 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") - command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled()) + // TODO add a (hidden) --disable-content-trust flag that throws a deprecation/removal warning and does nothing 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"}) @@ -286,26 +280,6 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) ctx, cancel := context.WithCancel(ctx) defer cancel() - var resolvedTags []*resolvedTag - if !options.untrusted { - translator := func(ctx context.Context, ref reference.NamedTagged) (reference.Canonical, error) { - return TrustedReference(ctx, dockerCli, ref) - } - // if there is a tar wrapper, the dockerfile needs to be replaced inside it - if buildCtx != nil { - // Wrap the tar archive to replace the Dockerfile entry with the rewritten - // Dockerfile which uses trusted pulls. - buildCtx = replaceDockerfileForContentTrust(ctx, buildCtx, relDockerfile, translator, &resolvedTags) - } else if dockerfileCtx != nil { - // if there was not archive context still do the possible replacements in Dockerfile - newDockerfile, _, err := rewriteDockerfileFromForContentTrust(ctx, dockerfileCtx, translator) - if err != nil { - return err - } - dockerfileCtx = io.NopCloser(bytes.NewBuffer(newDockerfile)) - } - } - if options.compress { buildCtx, err = build.Compress(buildCtx) if err != nil { @@ -402,15 +376,6 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) return err } } - if !options.untrusted { - // Since the build was successful, now we must tag any of the resolved - // images from the above Dockerfile rewrite. - for _, resolved := range resolvedTags { - if err := trust.TagTrusted(ctx, dockerCli.Client(), dockerCli.Err(), resolved.digestRef, resolved.tagRef); err != nil { - return err - } - } - } return nil } @@ -420,8 +385,6 @@ func isLocalDir(c string) bool { return err == nil } -type translatorFunc func(context.Context, reference.NamedTagged) (reference.Canonical, error) - // validateTag checks if the given image name can be resolved. func validateTag(rawRepo string) (string, error) { _, err := reference.ParseNormalizedNamed(rawRepo) @@ -432,114 +395,6 @@ func validateTag(rawRepo string) (string, error) { return rawRepo, nil } -var dockerfileFromLinePattern = lazyregexp.New(`(?i)^[\s]*FROM[ \f\r\t\v]+(?P[^ \f\r\t\v\n#]+)`) - -// resolvedTag records the repository, tag, and resolved digest reference -// from a Dockerfile rewrite. -type resolvedTag struct { - digestRef reference.Canonical - tagRef reference.NamedTagged -} - -// rewriteDockerfileFromForContentTrust rewrites the given Dockerfile by resolving images in -// "FROM " instructions to a digest reference. `translator` is a -// function that takes a repository name and tag reference and returns a -// trusted digest reference. -// This should be called *only* when content trust is enabled -func rewriteDockerfileFromForContentTrust(ctx context.Context, dockerfile io.Reader, translator translatorFunc) (newDockerfile []byte, resolvedTags []*resolvedTag, err error) { - scanner := bufio.NewScanner(dockerfile) - buf := bytes.NewBuffer(nil) - - // Scan the lines of the Dockerfile, looking for a "FROM" line. - for scanner.Scan() { - line := scanner.Text() - - matches := dockerfileFromLinePattern.FindStringSubmatch(line) - if matches != nil && matches[1] != api.NoBaseImageSpecifier { - // Replace the line with a resolved "FROM repo@digest" - var ref reference.Named - ref, err = reference.ParseNormalizedNamed(matches[1]) - if err != nil { - return nil, nil, err - } - ref = reference.TagNameOnly(ref) - if ref, ok := ref.(reference.NamedTagged); ok { - trustedRef, err := translator(ctx, ref) - if err != nil { - return nil, nil, err - } - - line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, "FROM "+reference.FamiliarString(trustedRef)) - resolvedTags = append(resolvedTags, &resolvedTag{ - digestRef: trustedRef, - tagRef: ref, - }) - } - } - - _, err := fmt.Fprintln(buf, line) - if err != nil { - return nil, nil, err - } - } - - return buf.Bytes(), resolvedTags, scanner.Err() -} - -// replaceDockerfileForContentTrust wraps the given input tar archive stream and -// uses the translator to replace the Dockerfile which uses a trusted reference. -// Returns a new tar archive stream with the replaced Dockerfile. -func replaceDockerfileForContentTrust(ctx context.Context, inputTarStream io.ReadCloser, dockerfileName string, translator translatorFunc, resolvedTags *[]*resolvedTag) io.ReadCloser { - pipeReader, pipeWriter := io.Pipe() - go func() { - tarReader := tar.NewReader(inputTarStream) - tarWriter := tar.NewWriter(pipeWriter) - - defer inputTarStream.Close() - - for { - hdr, err := tarReader.Next() - if err == io.EOF { - // Signals end of archive. - _ = tarWriter.Close() - _ = pipeWriter.Close() - return - } - if err != nil { - _ = pipeWriter.CloseWithError(err) - return - } - - content := io.Reader(tarReader) - if hdr.Name == dockerfileName { - // This entry is the Dockerfile. Since the tar archive was - // generated from a directory on the local filesystem, the - // Dockerfile will only appear once in the archive. - var newDockerfile []byte - newDockerfile, *resolvedTags, err = rewriteDockerfileFromForContentTrust(ctx, content, translator) - if err != nil { - _ = pipeWriter.CloseWithError(err) - return - } - hdr.Size = int64(len(newDockerfile)) - content = bytes.NewBuffer(newDockerfile) - } - - if err := tarWriter.WriteHeader(hdr); err != nil { - _ = pipeWriter.CloseWithError(err) - return - } - - if _, err := io.Copy(tarWriter, content); err != nil { - _ = pipeWriter.CloseWithError(err) - return - } - } - }() - - return pipeReader -} - func imageBuildOptions(dockerCli command.Cli, options buildOptions) buildtypes.ImageBuildOptions { configFile := dockerCli.ConfigFile() return buildtypes.ImageBuildOptions{ diff --git a/cli/command/image/build_test.go b/cli/command/image/build_test.go index 22105e47c024..9e12ec03ee96 100644 --- a/cli/command/image/build_test.go +++ b/cli/command/image/build_test.go @@ -47,7 +47,6 @@ func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) { options.compress = true options.dockerfileName = "-" options.context = dir.Path() - options.untrusted = true assert.NilError(t, runBuild(context.TODO(), cli, options)) expected := []string{fakeBuild.options.Dockerfile, ".dockerignore", "foo"} @@ -74,7 +73,6 @@ func TestRunBuildResetsUidAndGidInContext(t *testing.T) { options := newBuildOptions() options.context = dir.Path() - options.untrusted = true assert.NilError(t, runBuild(context.TODO(), cli, options)) headers := fakeBuild.headers(t) @@ -109,7 +107,6 @@ COPY data /data options := newBuildOptions() options.context = dir.Path() options.dockerfileName = df.Path() - options.untrusted = true assert.NilError(t, runBuild(context.TODO(), cli, options)) expected := []string{fakeBuild.options.Dockerfile, ".dockerignore", "data"} @@ -170,7 +167,6 @@ RUN echo hello world cli := test.NewFakeCli(&fakeClient{imageBuildFunc: fakeBuild.build}) options := newBuildOptions() options.context = tmpDir.Join("context-link") - options.untrusted = true assert.NilError(t, runBuild(context.TODO(), cli, options)) assert.DeepEqual(t, fakeBuild.filenames(t), []string{"Dockerfile"}) diff --git a/cli/command/image/pull.go b/cli/command/image/pull.go index 235e3a7a1754..5e1fe80564b9 100644 --- a/cli/command/image/pull.go +++ b/cli/command/image/pull.go @@ -3,13 +3,18 @@ package image import ( "context" "fmt" + "io" "strings" "github.com/distribution/reference" "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/cli/streams" + "github.com/docker/cli/internal/jsonstream" + "github.com/docker/docker/api/types/image" + registrytypes "github.com/docker/docker/api/types/registry" + "github.com/docker/docker/registry" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -19,11 +24,10 @@ type PullOptions = pullOptions // 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 @@ -51,7 +55,8 @@ func NewPullCommand(dockerCli command.Cli) *cobra.Command { flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress verbose output") command.AddPlatformFlag(flags, &opts.platform) - command.AddTrustVerificationFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled()) + + // TODO add a (hidden) --disable-content-trust flag that throws a deprecation/removal warning and does nothing _ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms) @@ -78,24 +83,41 @@ func runPull(ctx context.Context, dockerCLI command.Cli, opts pullOptions) error } } - imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, AuthResolver(dockerCLI), distributionRef.String()) + // Resolve the Repository name from fqn to RepositoryInfo + repoInfo, err := registry.ParseRepositoryInfo(distributionRef) if err != nil { return err } - // Check if reference has a digest - _, isCanonical := distributionRef.(reference.Canonical) - if !opts.untrusted && !isCanonical { - err = trustedPull(ctx, dockerCLI, imgRefAndAuth, opts) - } else { - err = imagePullPrivileged(ctx, dockerCLI, imgRefAndAuth, opts) + authConfig := command.ResolveAuthConfig(dockerCLI.ConfigFile(), repoInfo.Index) + + encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig) + if err != nil { + return err } + requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCLI, repoInfo.Index, "pull") + responseBody, err := dockerCLI.Client().ImagePull(ctx, reference.FamiliarString(distributionRef), image.PullOptions{ + RegistryAuth: encodedAuth, + PrivilegeFunc: requestPrivilege, + All: opts.all, + Platform: opts.platform, + }) if err != nil { if strings.Contains(err.Error(), "when fetching 'plugin'") { return errors.New(err.Error() + " - Use `docker plugin install`") } return err } - _, _ = fmt.Fprintln(dockerCLI.Out(), imgRefAndAuth.Reference().String()) + defer responseBody.Close() + + out := dockerCLI.Out() + if opts.quiet { + out = streams.NewOut(io.Discard) + } + if err := jsonstream.Display(ctx, responseBody, out); err != nil { + return err + } + + _, _ = fmt.Fprintln(dockerCLI.Out(), distributionRef.String()) return nil } diff --git a/cli/command/image/pull_test.go b/cli/command/image/pull_test.go index a853949f32ca..775bd05f35ef 100644 --- a/cli/command/image/pull_test.go +++ b/cli/command/image/pull_test.go @@ -1,14 +1,12 @@ package image import ( - "errors" "fmt" "io" "strings" "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/notary" "github.com/docker/docker/api/types/image" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" @@ -89,47 +87,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) { - cli := test.NewFakeCli(&fakeClient{ - imagePullFunc: func(ref string, options image.PullOptions) (io.ReadCloser, error) { - return io.NopCloser(strings.NewReader("")), errors.New("shouldn't try to pull image") - }, - }, test.EnableContentTrust) - 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) - }) - } -} diff --git a/cli/command/image/push.go b/cli/command/image/push.go index e91189d92d33..2d2cd78f1fda 100644 --- a/cli/command/image/push.go +++ b/cli/command/image/push.go @@ -28,11 +28,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 +56,8 @@ 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") - command.AddTrustSigningFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled()) + + // TODO add a (hidden) --disable-content-trust flag that throws a deprecation/removal warning and does nothing // Don't default to DOCKER_DEFAULT_PLATFORM env variable, always default to // pushing the image as-is. This also avoids forcing the platform selection @@ -133,10 +133,6 @@ To push the complete multi-platform image, remove the --platform flag. }() defer responseBody.Close() - if !opts.untrusted { - // TODO pushTrustedReference currently doesn't respect `--quiet` - return pushTrustedReference(ctx, dockerCli, repoInfo, ref, authConfig, responseBody) - } if opts.quiet { err = jsonstream.Display(ctx, responseBody, streams.NewOut(io.Discard), jsonstream.WithAuxCallback(handleAux())) diff --git a/cli/command/image/trust.go b/cli/command/image/trust.go deleted file mode 100644 index e224077a56f9..000000000000 --- a/cli/command/image/trust.go +++ /dev/null @@ -1,216 +0,0 @@ -package image - -import ( - "context" - "encoding/hex" - "fmt" - "io" - - "github.com/distribution/reference" - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/streams" - "github.com/docker/cli/cli/trust" - "github.com/docker/cli/internal/jsonstream" - "github.com/docker/docker/api/types/image" - registrytypes "github.com/docker/docker/api/types/registry" - "github.com/docker/docker/registry" - "github.com/opencontainers/go-digest" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "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(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (client.Repository, error) -} - -// newNotaryClient provides a Notary Repository to interact with signed metadata for an image. -func newNotaryClient(cli command.Streams, imgRefAndAuth trust.ImageRefAndAuth) (client.Repository, error) { - if ncp, ok := cli.(notaryClientProvider); ok { - // notaryClientProvider is used in tests to provide a dummy notary client. - return ncp.NotaryClient(imgRefAndAuth, []string{"pull"}) - } - return trust.GetNotaryRepository(cli.In(), cli.Out(), command.UserAgent(), imgRefAndAuth.RepoInfo(), imgRefAndAuth.AuthConfig(), "pull") -} - -// pushTrustedReference pushes a canonical reference to the trust server. -func pushTrustedReference(ctx context.Context, ioStreams command.Streams, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig registrytypes.AuthConfig, in io.Reader) error { - return trust.PushTrustedReference(ctx, ioStreams, repoInfo, ref, authConfig, in, 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, 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) - if err := cli.Client().ImageTag(ctx, trustedFamiliarRef, familiarRef); err != nil { - return err - } - } - return nil -} - -func getTrustedPullTargets(cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth) ([]target, error) { - notaryRepo, err := newNotaryClient(cli, imgRefAndAuth) - if err != nil { - return nil, errors.Wrap(err, "error establishing connection to trust repository") - } - - 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(), errors.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(), errors.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 -} - -// imagePullPrivileged pulls the image and displays it to the output -func imagePullPrivileged(ctx context.Context, cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth, opts pullOptions) error { - encodedAuth, err := registrytypes.EncodeAuthConfig(*imgRefAndAuth.AuthConfig()) - if err != nil { - return err - } - requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(cli, imgRefAndAuth.RepoInfo().Index, "pull") - responseBody, err := cli.Client().ImagePull(ctx, reference.FamiliarString(imgRefAndAuth.Reference()), image.PullOptions{ - RegistryAuth: encodedAuth, - PrivilegeFunc: requestPrivilege, - All: opts.all, - Platform: opts.platform, - }) - if err != nil { - return err - } - defer responseBody.Close() - - out := cli.Out() - if opts.quiet { - out = streams.NewOut(io.Discard) - } - return jsonstream.Display(ctx, responseBody, out) -} - -// 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) - if err != nil { - return nil, errors.Wrap(err, "error establishing connection to trust repository") - } - - 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(), client.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 client.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 command.Cli -func AuthResolver(cli command.Cli) func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig { - return func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig { - return command.ResolveAuthConfig(cli.ConfigFile(), index) - } -} diff --git a/cli/command/plugin/install.go b/cli/command/plugin/install.go index f2004413b091..0ec1781402c7 100644 --- a/cli/command/plugin/install.go +++ b/cli/command/plugin/install.go @@ -8,7 +8,6 @@ import ( "github.com/distribution/reference" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/command/image" "github.com/docker/cli/internal/jsonstream" "github.com/docker/cli/internal/prompt" "github.com/docker/docker/api/types" @@ -26,12 +25,11 @@ type pluginOptions struct { disable bool args []string skipRemoteCheck bool - untrusted bool } -func loadPullFlags(dockerCli command.Cli, opts *pluginOptions, flags *pflag.FlagSet) { +func loadPullFlags(opts *pluginOptions, flags *pflag.FlagSet) { flags.BoolVar(&opts.grantPerms, "grant-all-permissions", false, "Grant all permissions necessary to run the plugin") - command.AddTrustVerificationFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled()) + // TODO add a (hidden) --disable-content-trust flag that throws a deprecation/removal warning and does nothing } func newInstallCommand(dockerCli command.Cli) *cobra.Command { @@ -50,13 +48,13 @@ func newInstallCommand(dockerCli command.Cli) *cobra.Command { } flags := cmd.Flags() - loadPullFlags(dockerCli, &options, flags) + loadPullFlags(&options, flags) flags.BoolVar(&options.disable, "disable", false, "Do not enable the plugin on install") flags.StringVar(&options.localName, "alias", "", "Local name for plugin") return cmd } -func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOptions, cmdName string) (types.PluginInstallOptions, error) { +func buildPullConfig(dockerCli command.Cli, opts pluginOptions, cmdName string) (types.PluginInstallOptions, error) { // Names with both tag and digest will be treated by the daemon // as a pull by digest with a local name for the tag // (if no local name is provided). @@ -69,21 +67,6 @@ func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOpti remote := ref.String() - _, isCanonical := ref.(reference.Canonical) - if !opts.untrusted && !isCanonical { - ref = reference.TagNameOnly(ref) - nt, ok := ref.(reference.NamedTagged) - if !ok { - return types.PluginInstallOptions{}, errors.Errorf("invalid name: %s", ref.String()) - } - - trusted, err := image.TrustedReference(ctx, dockerCli, nt) - if err != nil { - return types.PluginInstallOptions{}, err - } - remote = reference.FamiliarString(trusted) - } - authConfig := command.ResolveAuthConfig(dockerCli.ConfigFile(), repoInfo.Index) encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig) if err != nil { @@ -115,7 +98,7 @@ func runInstall(ctx context.Context, dockerCLI command.Cli, opts pluginOptions) localName = reference.FamiliarString(reference.TagNameOnly(aref)) } - options, err := buildPullConfig(ctx, dockerCLI, opts, "plugin install") + options, err := buildPullConfig(dockerCLI, opts, "plugin install") if err != nil { return err } diff --git a/cli/command/plugin/install_test.go b/cli/command/plugin/install_test.go index b59ed98fcbee..3c2f92ec4c99 100644 --- a/cli/command/plugin/install_test.go +++ b/cli/command/plugin/install_test.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/notary" "github.com/docker/docker/api/types" "gotest.tools/v3/assert" @@ -65,50 +64,6 @@ func TestInstallErrors(t *testing.T) { } } -func TestInstallContentTrustErrors(t *testing.T) { - testCases := []struct { - description string - args []string - expectedError string - notaryFunc test.NotaryClientFuncType - }{ - { - description: "install plugin, offline notary server", - args: []string{"plugin:tag"}, - expectedError: "client is offline", - notaryFunc: notary.GetOfflineNotaryRepository, - }, - { - description: "install plugin, uninitialized notary server", - args: []string{"plugin:tag"}, - expectedError: "remote trust data does not exist", - notaryFunc: notary.GetUninitializedNotaryRepository, - }, - { - description: "install plugin, empty notary server", - args: []string{"plugin:tag"}, - expectedError: "No valid trust data for tag", - notaryFunc: notary.GetEmptyTargetsNotaryRepository, - }, - } - - for _, tc := range testCases { - t.Run(tc.description, func(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{ - pluginInstallFunc: func(name string, options types.PluginInstallOptions) (io.ReadCloser, error) { - return nil, errors.New("should not try to install plugin") - }, - }, test.EnableContentTrust) - cli.SetNotaryClient(tc.notaryFunc) - cmd := newInstallCommand(cli) - cmd.SetArgs(tc.args) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.ErrorContains(t, cmd.Execute(), tc.expectedError) - }) - } -} - func TestInstall(t *testing.T) { testCases := []struct { description string diff --git a/cli/command/plugin/push.go b/cli/command/plugin/push.go index 402068034797..625223a93180 100644 --- a/cli/command/plugin/push.go +++ b/cli/command/plugin/push.go @@ -6,7 +6,6 @@ import ( "github.com/distribution/reference" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/trust" "github.com/docker/cli/internal/jsonstream" registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/registry" @@ -15,8 +14,7 @@ import ( ) type pushOptions struct { - name string - untrusted bool + name string } func newPushCommand(dockerCli command.Cli) *cobra.Command { @@ -33,7 +31,7 @@ func newPushCommand(dockerCli command.Cli) *cobra.Command { flags := cmd.Flags() - command.AddTrustSigningFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled()) + _ = flags // TODO add a (hidden) --disable-content-trust flag that throws a deprecation/removal warning and does nothing return cmd } @@ -62,9 +60,5 @@ func runPush(ctx context.Context, dockerCli command.Cli, opts pushOptions) error } defer responseBody.Close() - if !opts.untrusted { - return trust.PushTrustedReference(ctx, dockerCli, repoInfo, named, authConfig, responseBody, command.UserAgent()) - } - return jsonstream.Display(ctx, responseBody, dockerCli.Out()) } diff --git a/cli/command/plugin/upgrade.go b/cli/command/plugin/upgrade.go index da9ea4076d84..a8c3ab30a253 100644 --- a/cli/command/plugin/upgrade.go +++ b/cli/command/plugin/upgrade.go @@ -31,7 +31,7 @@ func newUpgradeCommand(dockerCli command.Cli) *cobra.Command { } flags := cmd.Flags() - loadPullFlags(dockerCli, &options, flags) + loadPullFlags(&options, flags) flags.BoolVar(&options.skipRemoteCheck, "skip-remote-check", false, "Do not check if specified remote plugin matches existing plugin image") return cmd } @@ -73,7 +73,7 @@ func runUpgrade(ctx context.Context, dockerCLI command.Cli, opts pluginOptions) } } - options, err := buildPullConfig(ctx, dockerCLI, opts, "plugin upgrade") + options, err := buildPullConfig(dockerCLI, opts, "plugin upgrade") if err != nil { return err } diff --git a/cli/command/service/create.go b/cli/command/service/create.go index f63f65aefe24..a9c97a277377 100644 --- a/cli/command/service/create.go +++ b/cli/command/service/create.go @@ -122,10 +122,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 if opts.registryAuth { // Retrieve encoded auth token from the image reference diff --git a/cli/command/service/trust.go b/cli/command/service/trust.go deleted file mode 100644 index 6fb4d129a5ff..000000000000 --- a/cli/command/service/trust.go +++ /dev/null @@ -1,82 +0,0 @@ -package service - -import ( - "encoding/hex" - - "github.com/distribution/reference" - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/trust" - "github.com/docker/docker/api/types/swarm" - "github.com/docker/docker/registry" - "github.com/opencontainers/go-digest" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "github.com/theupdateframework/notary/tuf/data" -) - -func resolveServiceImageDigestContentTrust(dockerCli command.Cli, service *swarm.ServiceSpec) error { - if !dockerCli.ContentTrustEnabled() { - // 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 errors.Wrapf(err, "invalid reference %s", service.TaskTemplate.ContainerSpec.Image) - } - - // 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 errors.Wrap(err, "failed to resolve image digest using content trust") - } - 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) { - repoInfo, _ := registry.ParseRepositoryInfo(ref) - authConfig := command.ResolveAuthConfig(cli.ConfigFile(), repoInfo.Index) - - notaryRepo, err := trust.GetNotaryRepository(cli.In(), cli.Out(), command.UserAgent(), repoInfo, &authConfig, "pull") - if err != nil { - return nil, errors.Wrap(err, "error establishing connection to trust repository") - } - - 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(), errors.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) -} diff --git a/cli/command/service/update.go b/cli/command/service/update.go index 2b75b6b59b18..074c35fd7a9e 100644 --- a/cli/command/service/update.go +++ b/cli/command/service/update.go @@ -211,9 +211,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 - } if !options.noResolveImage && versions.GreaterThanOrEqualTo(apiClient.ClientVersion(), "1.30") { updateOpts.QueryRegistry = true } diff --git a/cli/command/trust.go b/cli/command/trust.go deleted file mode 100644 index 65f2408585d8..000000000000 --- a/cli/command/trust.go +++ /dev/null @@ -1,15 +0,0 @@ -package command - -import ( - "github.com/spf13/pflag" -) - -// AddTrustVerificationFlags adds content trust flags to the provided flagset -func AddTrustVerificationFlags(fs *pflag.FlagSet, v *bool, trusted bool) { - fs.BoolVar(v, "disable-content-trust", !trusted, "Skip image verification") -} - -// AddTrustSigningFlags adds "signing" flags to the provided flagset -func AddTrustSigningFlags(fs *pflag.FlagSet, v *bool, trusted bool) { - fs.BoolVar(v, "disable-content-trust", !trusted, "Skip image signing") -} diff --git a/cli/command/trust/cmd.go b/cli/command/trust/cmd.go deleted file mode 100644 index bb6ceace042b..000000000000 --- a/cli/command/trust/cmd.go +++ /dev/null @@ -1,25 +0,0 @@ -package trust - -import ( - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" - "github.com/spf13/cobra" -) - -// NewTrustCommand returns a cobra command for `trust` subcommands -func NewTrustCommand(dockerCli command.Cli) *cobra.Command { - cmd := &cobra.Command{ - Use: "trust", - Short: "Manage trust on Docker images", - Args: cli.NoArgs, - RunE: command.ShowHelp(dockerCli.Err()), - } - cmd.AddCommand( - newRevokeCommand(dockerCli), - newSignCommand(dockerCli), - newTrustKeyCommand(dockerCli), - newTrustSignerCommand(dockerCli), - newInspectCommand(dockerCli), - ) - return cmd -} diff --git a/cli/command/trust/common.go b/cli/command/trust/common.go deleted file mode 100644 index 7d1d50e29688..000000000000 --- a/cli/command/trust/common.go +++ /dev/null @@ -1,169 +0,0 @@ -package trust - -import ( - "context" - "encoding/hex" - "fmt" - "sort" - "strings" - - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/command/image" - "github.com/docker/cli/cli/trust" - "github.com/fvbommel/sortorder" - "github.com/sirupsen/logrus" - "github.com/theupdateframework/notary" - "github.com/theupdateframework/notary/client" - "github.com/theupdateframework/notary/tuf/data" -) - -// trustTagKey represents a unique signed tag and hex-encoded hash pair -type trustTagKey struct { - SignedTag string - Digest string -} - -// trustTagRow encodes all human-consumable information for a signed tag, including signers -type trustTagRow struct { - trustTagKey - Signers []string -} - -// trustRepo represents consumable information about a trusted repository -type trustRepo struct { - Name string - SignedTags []trustTagRow - Signers []trustSigner - AdministrativeKeys []trustSigner -} - -// trustSigner represents a trusted signer in a trusted repository -// a signer is defined by a name and list of trustKeys -type trustSigner struct { - Name string `json:",omitempty"` - Keys []trustKey `json:",omitempty"` -} - -// trustKey contains information about trusted keys -type trustKey struct { - ID string `json:",omitempty"` -} - -// notaryClientProvider is used in tests to provide a dummy notary client. -type notaryClientProvider interface { - NotaryClient(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (client.Repository, error) -} - -// newNotaryClient provides a Notary Repository to interact with signed metadata for an image. -func newNotaryClient(cli command.Streams, imgRefAndAuth trust.ImageRefAndAuth, actions []string) (client.Repository, error) { - if ncp, ok := cli.(notaryClientProvider); ok { - // notaryClientProvider is used in tests to provide a dummy notary client. - return ncp.NotaryClient(imgRefAndAuth, actions) - } - return trust.GetNotaryRepository(cli.In(), cli.Out(), command.UserAgent(), imgRefAndAuth.RepoInfo(), imgRefAndAuth.AuthConfig(), actions...) -} - -// lookupTrustInfo returns processed signature and role information about a notary repository. -// This information is to be pretty printed or serialized into a machine-readable format. -func lookupTrustInfo(ctx context.Context, cli command.Cli, remote string) ([]trustTagRow, []client.RoleWithSignatures, []data.Role, error) { - imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, image.AuthResolver(cli), remote) - if err != nil { - return []trustTagRow{}, []client.RoleWithSignatures{}, []data.Role{}, err - } - tag := imgRefAndAuth.Tag() - notaryRepo, err := newNotaryClient(cli, imgRefAndAuth, trust.ActionsPullOnly) - if err != nil { - return []trustTagRow{}, []client.RoleWithSignatures{}, []data.Role{}, trust.NotaryError(imgRefAndAuth.Reference().Name(), err) - } - - if err = clearChangeList(notaryRepo); err != nil { - return []trustTagRow{}, []client.RoleWithSignatures{}, []data.Role{}, err - } - defer clearChangeList(notaryRepo) - - // Retrieve all released signatures, match them, and pretty print them - allSignedTargets, err := notaryRepo.GetAllTargetMetadataByName(tag) - if err != nil { - logrus.Debug(trust.NotaryError(remote, err)) - // print an empty table if we don't have signed targets, but have an initialized notary repo - if _, ok := err.(client.ErrNoSuchTarget); !ok { - return []trustTagRow{}, []client.RoleWithSignatures{}, []data.Role{}, fmt.Errorf("no signatures or cannot access %s", remote) - } - } - signatureRows := matchReleasedSignatures(allSignedTargets) - - // get the administrative roles - adminRolesWithSigs, err := notaryRepo.ListRoles() - if err != nil { - return []trustTagRow{}, []client.RoleWithSignatures{}, []data.Role{}, fmt.Errorf("no signers for %s", remote) - } - - // get delegation roles with the canonical key IDs - delegationRoles, err := notaryRepo.GetDelegationRoles() - if err != nil { - logrus.Debugf("no delegation roles found, or error fetching them for %s: %v", remote, err) - } - - return signatureRows, adminRolesWithSigs, delegationRoles, nil -} - -func formatAdminRole(roleWithSigs client.RoleWithSignatures) string { - adminKeyList := roleWithSigs.KeyIDs - sort.Strings(adminKeyList) - - var role string - switch roleWithSigs.Name { - case data.CanonicalTargetsRole: - role = "Repository Key" - case data.CanonicalRootRole: - role = "Root Key" - default: - return "" - } - return fmt.Sprintf("%s:\t%s\n", role, strings.Join(adminKeyList, ", ")) -} - -func getDelegationRoleToKeyMap(rawDelegationRoles []data.Role) map[string][]string { - signerRoleToKeyIDs := make(map[string][]string) - for _, delRole := range rawDelegationRoles { - switch delRole.Name { - case trust.ReleasesRole, data.CanonicalRootRole, data.CanonicalSnapshotRole, data.CanonicalTargetsRole, data.CanonicalTimestampRole: - continue - default: - signerRoleToKeyIDs[notaryRoleToSigner(delRole.Name)] = delRole.KeyIDs - } - } - return signerRoleToKeyIDs -} - -// aggregate all signers for a "released" hash+tagname pair. To be "released," the tag must have been -// signed into the "targets" or "targets/releases" role. Output is sorted by tag name -func matchReleasedSignatures(allTargets []client.TargetSignedStruct) []trustTagRow { - signatureRows := []trustTagRow{} - // do a first pass to get filter on tags signed into "targets" or "targets/releases" - releasedTargetRows := map[trustTagKey][]string{} - for _, tgt := range allTargets { - if isReleasedTarget(tgt.Role.Name) { - releasedKey := trustTagKey{tgt.Target.Name, hex.EncodeToString(tgt.Target.Hashes[notary.SHA256])} - releasedTargetRows[releasedKey] = []string{} - } - } - - // now fill out all signers on released keys - for _, tgt := range allTargets { - targetKey := trustTagKey{tgt.Target.Name, hex.EncodeToString(tgt.Target.Hashes[notary.SHA256])} - // only considered released targets - if _, ok := releasedTargetRows[targetKey]; ok && !isReleasedTarget(tgt.Role.Name) { - releasedTargetRows[targetKey] = append(releasedTargetRows[targetKey], notaryRoleToSigner(tgt.Role.Name)) - } - } - - // compile the final output as a sorted slice - for targetKey, signers := range releasedTargetRows { - signatureRows = append(signatureRows, trustTagRow{targetKey, signers}) - } - sort.Slice(signatureRows, func(i, j int) bool { - return sortorder.NaturalLess(signatureRows[i].SignedTag, signatureRows[j].SignedTag) - }) - return signatureRows -} diff --git a/cli/command/trust/common_test.go b/cli/command/trust/common_test.go deleted file mode 100644 index 13f24f50222b..000000000000 --- a/cli/command/trust/common_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package trust - -import ( - "testing" - - "github.com/docker/cli/cli/trust" - "github.com/theupdateframework/notary/client" - "github.com/theupdateframework/notary/tuf/data" - "gotest.tools/v3/assert" - is "gotest.tools/v3/assert/cmp" -) - -func TestMatchReleasedSignaturesSortOrder(t *testing.T) { - releasesRole := data.DelegationRole{BaseRole: data.BaseRole{Name: trust.ReleasesRole}} - targets := []client.TargetSignedStruct{ - {Target: client.Target{Name: "target10-foo"}, Role: releasesRole}, - {Target: client.Target{Name: "target1-foo"}, Role: releasesRole}, - {Target: client.Target{Name: "target2-foo"}, Role: releasesRole}, - } - - rows := matchReleasedSignatures(targets) - - targetNames := make([]string, 0, len(rows)) - for _, r := range rows { - targetNames = append(targetNames, r.SignedTag) - } - expected := []string{ - "target1-foo", - "target2-foo", - "target10-foo", - } - assert.Check(t, is.DeepEqual(expected, targetNames)) -} diff --git a/cli/command/trust/formatter.go b/cli/command/trust/formatter.go deleted file mode 100644 index 5cf9e9d3357f..000000000000 --- a/cli/command/trust/formatter.go +++ /dev/null @@ -1,132 +0,0 @@ -package trust - -import ( - "sort" - "strings" - - "github.com/docker/cli/cli/command/formatter" - "github.com/docker/docker/pkg/stringid" -) - -const ( - defaultTrustTagTableFormat = "table {{.SignedTag}}\t{{.Digest}}\t{{.Signers}}" - signedTagNameHeader = "SIGNED TAG" - trustedDigestHeader = "DIGEST" - signersHeader = "SIGNERS" - defaultSignerInfoTableFormat = "table {{.Signer}}\t{{.Keys}}" - signerNameHeader = "SIGNER" - keysHeader = "KEYS" -) - -// SignedTagInfo represents all formatted information needed to describe a signed tag: -// Name: name of the signed tag -// Digest: hex encoded digest of the contents -// Signers: list of entities who signed the tag -type SignedTagInfo struct { - Name string - Digest string - Signers []string -} - -// SignerInfo represents all formatted information needed to describe a signer: -// Name: name of the signer role -// Keys: the keys associated with the signer -type SignerInfo struct { - Name string - Keys []string -} - -// NewTrustTagFormat returns a Format for rendering using a trusted tag Context -func NewTrustTagFormat() formatter.Format { - return defaultTrustTagTableFormat -} - -// NewSignerInfoFormat returns a Format for rendering a signer role info Context -func NewSignerInfoFormat() formatter.Format { - return defaultSignerInfoTableFormat -} - -// TagWrite writes the context -func TagWrite(ctx formatter.Context, signedTagInfoList []SignedTagInfo) error { - render := func(format func(subContext formatter.SubContext) error) error { - for _, signedTag := range signedTagInfoList { - if err := format(&trustTagContext{s: signedTag}); err != nil { - return err - } - } - return nil - } - trustTagCtx := trustTagContext{} - trustTagCtx.Header = formatter.SubHeaderContext{ - "SignedTag": signedTagNameHeader, - "Digest": trustedDigestHeader, - "Signers": signersHeader, - } - return ctx.Write(&trustTagCtx, render) -} - -type trustTagContext struct { - formatter.HeaderContext - s SignedTagInfo -} - -// SignedTag returns the name of the signed tag -func (c *trustTagContext) SignedTag() string { - return c.s.Name -} - -// Digest returns the hex encoded digest associated with this signed tag -func (c *trustTagContext) Digest() string { - return c.s.Digest -} - -// Signers returns the sorted list of entities who signed this tag -func (c *trustTagContext) Signers() string { - sort.Strings(c.s.Signers) - return strings.Join(c.s.Signers, ", ") -} - -// SignerInfoWrite writes the context -func SignerInfoWrite(ctx formatter.Context, signerInfoList []SignerInfo) error { - render := func(format func(subContext formatter.SubContext) error) error { - for _, signerInfo := range signerInfoList { - if err := format(&signerInfoContext{ - trunc: ctx.Trunc, - s: signerInfo, - }); err != nil { - return err - } - } - return nil - } - signerInfoCtx := signerInfoContext{} - signerInfoCtx.Header = formatter.SubHeaderContext{ - "Signer": signerNameHeader, - "Keys": keysHeader, - } - return ctx.Write(&signerInfoCtx, render) -} - -type signerInfoContext struct { - formatter.HeaderContext - trunc bool - s SignerInfo -} - -// Keys returns the sorted list of keys associated with the signer -func (c *signerInfoContext) Keys() string { - sort.Strings(c.s.Keys) - truncatedKeys := []string{} - if c.trunc { - for _, keyID := range c.s.Keys { - truncatedKeys = append(truncatedKeys, stringid.TruncateID(keyID)) - } - return strings.Join(truncatedKeys, ", ") - } - return strings.Join(c.s.Keys, ", ") -} - -// Signer returns the name of the signer -func (c *signerInfoContext) Signer() string { - return c.s.Name -} diff --git a/cli/command/trust/formatter_test.go b/cli/command/trust/formatter_test.go deleted file mode 100644 index db1534526f0f..000000000000 --- a/cli/command/trust/formatter_test.go +++ /dev/null @@ -1,244 +0,0 @@ -package trust - -import ( - "bytes" - "testing" - - "github.com/docker/cli/cli/command/formatter" - "github.com/docker/docker/pkg/stringid" - "gotest.tools/v3/assert" - is "gotest.tools/v3/assert/cmp" -) - -func TestTrustTag(t *testing.T) { - digest := stringid.GenerateRandomID() - trustedTag := "tag" - - var ctx trustTagContext - - cases := []struct { - trustTagCtx trustTagContext - expValue string - call func() string - }{ - { - trustTagContext{ - s: SignedTagInfo{ - Name: trustedTag, - Digest: digest, - Signers: nil, - }, - }, - digest, - ctx.Digest, - }, - { - trustTagContext{ - s: SignedTagInfo{ - Name: trustedTag, - Digest: digest, - Signers: nil, - }, - }, - trustedTag, - ctx.SignedTag, - }, - // Empty signers makes a row with empty string - { - trustTagContext{ - s: SignedTagInfo{ - Name: trustedTag, - Digest: digest, - Signers: nil, - }, - }, - "", - ctx.Signers, - }, - { - trustTagContext{ - s: SignedTagInfo{ - Name: trustedTag, - Digest: digest, - Signers: []string{"alice", "bob", "claire"}, - }, - }, - "alice, bob, claire", - ctx.Signers, - }, - // alphabetic signing on Signers - { - trustTagContext{ - s: SignedTagInfo{ - Name: trustedTag, - Digest: digest, - Signers: []string{"claire", "bob", "alice"}, - }, - }, - "alice, bob, claire", - ctx.Signers, - }, - } - - for _, c := range cases { - ctx = c.trustTagCtx - v := c.call() - if v != c.expValue { - t.Fatalf("Expected %s, was %s\n", c.expValue, v) - } - } -} - -func TestTrustTagContextWrite(t *testing.T) { - cases := []struct { - context formatter.Context - expected string - }{ - // Errors - { - formatter.Context{ - Format: "{{InvalidFunction}}", - }, - `template parsing error: template: :1: function "InvalidFunction" not defined`, - }, - { - formatter.Context{ - Format: "{{nil}}", - }, - `template parsing error: template: :1:2: executing "" at : nil is not a command`, - }, - // Table Format - { - formatter.Context{ - Format: NewTrustTagFormat(), - }, - `SIGNED TAG DIGEST SIGNERS -tag1 deadbeef alice -tag2 aaaaaaaa alice, bob -tag3 bbbbbbbb -`, - }, - } - - signedTags := []SignedTagInfo{ - {Name: "tag1", Digest: "deadbeef", Signers: []string{"alice"}}, - {Name: "tag2", Digest: "aaaaaaaa", Signers: []string{"alice", "bob"}}, - {Name: "tag3", Digest: "bbbbbbbb", Signers: []string{}}, - } - - for _, tc := range cases { - t.Run(string(tc.context.Format), func(t *testing.T) { - var out bytes.Buffer - tc.context.Output = &out - - if err := TagWrite(tc.context, signedTags); err != nil { - assert.Error(t, err, tc.expected) - } else { - assert.Equal(t, out.String(), tc.expected) - } - }) - } -} - -// With no trust data, the TagWrite will print an empty table: -// it's up to the caller to decide whether or not to print this versus an error -func TestTrustTagContextEmptyWrite(t *testing.T) { - emptyCase := struct { - context formatter.Context - expected string - }{ - formatter.Context{ - Format: NewTrustTagFormat(), - }, - `SIGNED TAG DIGEST SIGNERS -`, - } - - emptySignedTags := []SignedTagInfo{} - out := bytes.NewBufferString("") - emptyCase.context.Output = out - err := TagWrite(emptyCase.context, emptySignedTags) - assert.NilError(t, err) - assert.Check(t, is.Equal(emptyCase.expected, out.String())) -} - -func TestSignerInfoContextEmptyWrite(t *testing.T) { - emptyCase := struct { - context formatter.Context - expected string - }{ - formatter.Context{ - Format: NewSignerInfoFormat(), - }, - `SIGNER KEYS -`, - } - emptySignerInfo := []SignerInfo{} - out := bytes.NewBufferString("") - emptyCase.context.Output = out - err := SignerInfoWrite(emptyCase.context, emptySignerInfo) - assert.NilError(t, err) - assert.Check(t, is.Equal(emptyCase.expected, out.String())) -} - -func TestSignerInfoContextWrite(t *testing.T) { - cases := []struct { - context formatter.Context - expected string - }{ - // Errors - { - formatter.Context{ - Format: "{{InvalidFunction}}", - }, - `template parsing error: template: :1: function "InvalidFunction" not defined`, - }, - { - formatter.Context{ - Format: "{{nil}}", - }, - `template parsing error: template: :1:2: executing "" at : nil is not a command`, - }, - // Table Format - { - formatter.Context{ - Format: NewSignerInfoFormat(), - Trunc: true, - }, - `SIGNER KEYS -alice key11, key12 -bob key21 -eve foobarbazqux, key31, key32 -`, - }, - // No truncation - { - formatter.Context{ - Format: NewSignerInfoFormat(), - }, - `SIGNER KEYS -alice key11, key12 -bob key21 -eve foobarbazquxquux, key31, key32 -`, - }, - } - - signerInfo := []SignerInfo{ - {Name: "alice", Keys: []string{"key11", "key12"}}, - {Name: "bob", Keys: []string{"key21"}}, - {Name: "eve", Keys: []string{"key31", "key32", "foobarbazquxquux"}}, - } - for _, tc := range cases { - t.Run(string(tc.context.Format), func(t *testing.T) { - var out bytes.Buffer - tc.context.Output = &out - - if err := SignerInfoWrite(tc.context, signerInfo); err != nil { - assert.Error(t, err, tc.expected) - } else { - assert.Equal(t, out.String(), tc.expected) - } - }) - } -} diff --git a/cli/command/trust/helpers.go b/cli/command/trust/helpers.go deleted file mode 100644 index 9f4e58b8b791..000000000000 --- a/cli/command/trust/helpers.go +++ /dev/null @@ -1,55 +0,0 @@ -package trust - -import ( - "strings" - - "github.com/docker/cli/cli/trust" - "github.com/theupdateframework/notary/client" - "github.com/theupdateframework/notary/tuf/data" -) - -const ( - releasedRoleName = "Repo Admin" - releasesRoleTUFName = "targets/releases" -) - -// isReleasedTarget checks if a role name is "released": -// either targets/releases or targets TUF roles -func isReleasedTarget(role data.RoleName) bool { - return role == data.CanonicalTargetsRole || role == trust.ReleasesRole -} - -// notaryRoleToSigner converts TUF role name to a human-understandable signer name -func notaryRoleToSigner(tufRole data.RoleName) string { - // don't show a signer for "targets" or "targets/releases" - if isReleasedTarget(data.RoleName(tufRole.String())) { - return releasedRoleName - } - return strings.TrimPrefix(tufRole.String(), "targets/") -} - -// clearChangeList clears the notary staging changelist. -func clearChangeList(notaryRepo client.Repository) error { - cl, err := notaryRepo.GetChangelist() - if err != nil { - return err - } - return cl.Clear("") -} - -// getOrGenerateRootKeyAndInitRepo initializes the notary repository -// with a remotely managed snapshot key. The initialization will use -// an existing root key if one is found, else a new one will be generated. -func getOrGenerateRootKeyAndInitRepo(notaryRepo client.Repository) error { - rootKey, err := getOrGenerateNotaryKey(notaryRepo, data.CanonicalRootRole) - if err != nil { - return err - } - return notaryRepo.Initialize([]string{rootKey.ID()}, data.CanonicalSnapshotRole) -} - -const testPass = "password" - -func testPassRetriever(string, string, bool, int) (string, bool, error) { - return testPass, false, nil -} diff --git a/cli/command/trust/inspect.go b/cli/command/trust/inspect.go deleted file mode 100644 index 18705f519c12..000000000000 --- a/cli/command/trust/inspect.go +++ /dev/null @@ -1,119 +0,0 @@ -// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: -//go:build go1.23 - -package trust - -import ( - "context" - "encoding/json" - "fmt" - "sort" - - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/command/inspect" - "github.com/spf13/cobra" - "github.com/theupdateframework/notary/tuf/data" -) - -type inspectOptions struct { - remotes []string - // FIXME(n4ss): this is consistent with `docker service inspect` but we should provide - // a `--format` flag too. (format and pretty-print should be exclusive) - prettyPrint bool -} - -func newInspectCommand(dockerCli command.Cli) *cobra.Command { - options := inspectOptions{} - cmd := &cobra.Command{ - Use: "inspect IMAGE[:TAG] [IMAGE[:TAG]...]", - Short: "Return low-level information about keys and signatures", - Args: cli.RequiresMinArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - options.remotes = args - - return runInspect(cmd.Context(), dockerCli, options) - }, - } - - flags := cmd.Flags() - flags.BoolVar(&options.prettyPrint, "pretty", false, "Print the information in a human friendly format") - - return cmd -} - -func runInspect(ctx context.Context, dockerCLI command.Cli, opts inspectOptions) error { - if opts.prettyPrint { - var err error - - for index, remote := range opts.remotes { - if err = prettyPrintTrustInfo(ctx, dockerCLI, remote); err != nil { - return err - } - - // Additional separator between the inspection output of each image - if index < len(opts.remotes)-1 { - _, _ = fmt.Fprint(dockerCLI.Out(), "\n\n") - } - } - - return err - } - - getRefFunc := func(ref string) (any, []byte, error) { - i, err := getRepoTrustInfo(ctx, dockerCLI, ref) - return nil, i, err - } - return inspect.Inspect(dockerCLI.Out(), opts.remotes, "", getRefFunc) -} - -func getRepoTrustInfo(ctx context.Context, dockerCLI command.Cli, remote string) ([]byte, error) { - signatureRows, adminRolesWithSigs, delegationRoles, err := lookupTrustInfo(ctx, dockerCLI, remote) - if err != nil { - return []byte{}, err - } - // process the signatures to include repo admin if signed by the base targets role - for idx, sig := range signatureRows { - if len(sig.Signers) == 0 { - signatureRows[idx].Signers = []string{releasedRoleName} - } - } - - signerList, adminList := []trustSigner{}, []trustSigner{} - - signerRoleToKeyIDs := getDelegationRoleToKeyMap(delegationRoles) - - for signerName, signerKeys := range signerRoleToKeyIDs { - signerKeyList := []trustKey{} - for _, keyID := range signerKeys { - signerKeyList = append(signerKeyList, trustKey{ID: keyID}) - } - signerList = append(signerList, trustSigner{signerName, signerKeyList}) - } - sort.Slice(signerList, func(i, j int) bool { return signerList[i].Name > signerList[j].Name }) - - for _, adminRole := range adminRolesWithSigs { - switch adminRole.Name { - case data.CanonicalRootRole: - rootKeys := []trustKey{} - for _, keyID := range adminRole.KeyIDs { - rootKeys = append(rootKeys, trustKey{ID: keyID}) - } - adminList = append(adminList, trustSigner{"Root", rootKeys}) - case data.CanonicalTargetsRole: - targetKeys := []trustKey{} - for _, keyID := range adminRole.KeyIDs { - targetKeys = append(targetKeys, trustKey{ID: keyID}) - } - adminList = append(adminList, trustSigner{"Repository", targetKeys}) - } - } - sort.Slice(adminList, func(i, j int) bool { return adminList[i].Name > adminList[j].Name }) - - return json.Marshal(trustRepo{ - Name: remote, - SignedTags: signatureRows, - Signers: signerList, - AdministrativeKeys: adminList, - }) -} diff --git a/cli/command/trust/inspect_pretty.go b/cli/command/trust/inspect_pretty.go deleted file mode 100644 index b0b5e8c51c84..000000000000 --- a/cli/command/trust/inspect_pretty.go +++ /dev/null @@ -1,94 +0,0 @@ -package trust - -import ( - "context" - "fmt" - "io" - "sort" - - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/command/formatter" - "github.com/fvbommel/sortorder" - "github.com/theupdateframework/notary/client" -) - -func prettyPrintTrustInfo(ctx context.Context, dockerCLI command.Cli, remote string) error { - signatureRows, adminRolesWithSigs, delegationRoles, err := lookupTrustInfo(ctx, dockerCLI, remote) - if err != nil { - return err - } - - if len(signatureRows) > 0 { - _, _ = fmt.Fprintf(dockerCLI.Out(), "\nSignatures for %s\n\n", remote) - - if err := printSignatures(dockerCLI.Out(), signatureRows); err != nil { - return err - } - } else { - _, _ = fmt.Fprintf(dockerCLI.Out(), "\nNo signatures for %s\n\n", remote) - } - signerRoleToKeyIDs := getDelegationRoleToKeyMap(delegationRoles) - - // If we do not have additional signers, do not display - if len(signerRoleToKeyIDs) > 0 { - _, _ = fmt.Fprintf(dockerCLI.Out(), "\nList of signers and their keys for %s\n\n", remote) - if err := printSignerInfo(dockerCLI.Out(), signerRoleToKeyIDs); err != nil { - return err - } - } - - // This will always have the root and targets information - _, _ = fmt.Fprintf(dockerCLI.Out(), "\nAdministrative keys for %s\n\n", remote) - printSortedAdminKeys(dockerCLI.Out(), adminRolesWithSigs) - return nil -} - -func printSortedAdminKeys(out io.Writer, adminRoles []client.RoleWithSignatures) { - sort.Slice(adminRoles, func(i, j int) bool { return adminRoles[i].Name > adminRoles[j].Name }) - for _, adminRole := range adminRoles { - if formattedAdminRole := formatAdminRole(adminRole); formattedAdminRole != "" { - _, _ = fmt.Fprintf(out, " %s", formattedAdminRole) - } - } -} - -// pretty print with ordered rows -func printSignatures(out io.Writer, signatureRows []trustTagRow) error { - trustTagCtx := formatter.Context{ - Output: out, - Format: NewTrustTagFormat(), - } - // convert the formatted type before printing - formattedTags := []SignedTagInfo{} - for _, sigRow := range signatureRows { - formattedSigners := sigRow.Signers - if len(formattedSigners) == 0 { - formattedSigners = append(formattedSigners, fmt.Sprintf("(%s)", releasedRoleName)) - } - formattedTags = append(formattedTags, SignedTagInfo{ - Name: sigRow.SignedTag, - Digest: sigRow.Digest, - Signers: formattedSigners, - }) - } - return TagWrite(trustTagCtx, formattedTags) -} - -func printSignerInfo(out io.Writer, roleToKeyIDs map[string][]string) error { - signerInfoCtx := formatter.Context{ - Output: out, - Format: NewSignerInfoFormat(), - Trunc: true, - } - formattedSignerInfo := []SignerInfo{} - for name, keyIDs := range roleToKeyIDs { - formattedSignerInfo = append(formattedSignerInfo, SignerInfo{ - Name: name, - Keys: keyIDs, - }) - } - sort.Slice(formattedSignerInfo, func(i, j int) bool { - return sortorder.NaturalLess(formattedSignerInfo[i].Name, formattedSignerInfo[j].Name) - }) - return SignerInfoWrite(signerInfoCtx, formattedSignerInfo) -} diff --git a/cli/command/trust/inspect_pretty_test.go b/cli/command/trust/inspect_pretty_test.go deleted file mode 100644 index 07e4edba41de..000000000000 --- a/cli/command/trust/inspect_pretty_test.go +++ /dev/null @@ -1,478 +0,0 @@ -package trust - -import ( - "bytes" - "context" - "encoding/hex" - "io" - "testing" - - "github.com/docker/cli/cli/trust" - "github.com/docker/cli/internal/test" - notaryfake "github.com/docker/cli/internal/test/notary" - "github.com/docker/docker/api/types/image" - "github.com/docker/docker/api/types/system" - "github.com/docker/docker/client" - "github.com/theupdateframework/notary" - notaryclient "github.com/theupdateframework/notary/client" - "github.com/theupdateframework/notary/tuf/data" - "github.com/theupdateframework/notary/tuf/utils" - "gotest.tools/v3/assert" - is "gotest.tools/v3/assert/cmp" - "gotest.tools/v3/golden" -) - -// TODO(n4ss): remove common tests with the regular inspect command - -type fakeClient struct { - client.Client -} - -func (*fakeClient) Info(context.Context) (system.Info, error) { - return system.Info{}, nil -} - -func (*fakeClient) ImageInspect(context.Context, string, ...client.ImageInspectOption) (image.InspectResponse, error) { - return image.InspectResponse{}, nil -} - -func (*fakeClient) ImagePush(context.Context, string, image.PushOptions) (io.ReadCloser, error) { - return &utils.NoopCloser{Reader: bytes.NewBuffer([]byte{})}, nil -} - -func TestTrustInspectPrettyCommandErrors(t *testing.T) { - testCases := []struct { - name string - args []string - expectedError string - }{ - { - name: "not-enough-args", - expectedError: "requires at least 1 argument", - }, - { - name: "sha-reference", - args: []string{"870d292919d01a0af7e7f056271dc78792c05f55f49b9b9012b6d89725bd9abd"}, - expectedError: "invalid repository name", - }, - { - name: "invalid-img-reference", - args: []string{"ALPINE"}, - expectedError: "invalid reference format", - }, - } - for _, tc := range testCases { - cmd := newInspectCommand( - test.NewFakeCli(&fakeClient{})) - cmd.SetArgs(tc.args) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - cmd.Flags().Set("pretty", "true") - assert.ErrorContains(t, cmd.Execute(), tc.expectedError) - } -} - -func TestTrustInspectPrettyCommandOfflineErrors(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetOfflineNotaryRepository) - cmd := newInspectCommand(cli) - cmd.Flags().Set("pretty", "true") - cmd.SetArgs([]string{"nonexistent-reg-name.io/image"}) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.ErrorContains(t, cmd.Execute(), "no signatures or cannot access nonexistent-reg-name.io/image") - - cli = test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetOfflineNotaryRepository) - cmd = newInspectCommand(cli) - cmd.Flags().Set("pretty", "true") - cmd.SetArgs([]string{"nonexistent-reg-name.io/image:tag"}) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.ErrorContains(t, cmd.Execute(), "no signatures or cannot access nonexistent-reg-name.io/image") -} - -func TestTrustInspectPrettyCommandUninitializedErrors(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetUninitializedNotaryRepository) - cmd := newInspectCommand(cli) - cmd.Flags().Set("pretty", "true") - cmd.SetArgs([]string{"reg/unsigned-img"}) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.ErrorContains(t, cmd.Execute(), "no signatures or cannot access reg/unsigned-img") - - cli = test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetUninitializedNotaryRepository) - cmd = newInspectCommand(cli) - cmd.Flags().Set("pretty", "true") - cmd.SetArgs([]string{"reg/unsigned-img:tag"}) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.ErrorContains(t, cmd.Execute(), "no signatures or cannot access reg/unsigned-img:tag") -} - -func TestTrustInspectPrettyCommandEmptyNotaryRepoErrors(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetEmptyTargetsNotaryRepository) - cmd := newInspectCommand(cli) - cmd.Flags().Set("pretty", "true") - cmd.SetArgs([]string{"reg/img:unsigned-tag"}) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.NilError(t, cmd.Execute()) - assert.Check(t, is.Contains(cli.OutBuffer().String(), "No signatures for reg/img:unsigned-tag")) - assert.Check(t, is.Contains(cli.OutBuffer().String(), "Administrative keys for reg/img")) - - cli = test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetEmptyTargetsNotaryRepository) - cmd = newInspectCommand(cli) - cmd.Flags().Set("pretty", "true") - cmd.SetArgs([]string{"reg/img"}) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.NilError(t, cmd.Execute()) - assert.Check(t, is.Contains(cli.OutBuffer().String(), "No signatures for reg/img")) - assert.Check(t, is.Contains(cli.OutBuffer().String(), "Administrative keys for reg/img")) -} - -func TestTrustInspectPrettyCommandFullRepoWithoutSigners(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetLoadedWithNoSignersNotaryRepository) - cmd := newInspectCommand(cli) - cmd.Flags().Set("pretty", "true") - cmd.SetArgs([]string{"signed-repo"}) - assert.NilError(t, cmd.Execute()) - - golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-pretty-full-repo-no-signers.golden") -} - -func TestTrustInspectPrettyCommandOneTagWithoutSigners(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetLoadedWithNoSignersNotaryRepository) - cmd := newInspectCommand(cli) - cmd.Flags().Set("pretty", "true") - cmd.SetArgs([]string{"signed-repo:green"}) - assert.NilError(t, cmd.Execute()) - - golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-pretty-one-tag-no-signers.golden") -} - -func TestTrustInspectPrettyCommandFullRepoWithSigners(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetLoadedNotaryRepository) - cmd := newInspectCommand(cli) - cmd.Flags().Set("pretty", "true") - cmd.SetArgs([]string{"signed-repo"}) - assert.NilError(t, cmd.Execute()) - - golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-pretty-full-repo-with-signers.golden") -} - -func TestTrustInspectPrettyCommandUnsignedTagInSignedRepo(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetLoadedNotaryRepository) - cmd := newInspectCommand(cli) - cmd.Flags().Set("pretty", "true") - cmd.SetArgs([]string{"signed-repo:unsigned"}) - assert.NilError(t, cmd.Execute()) - - golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-pretty-unsigned-tag-with-signers.golden") -} - -func TestNotaryRoleToSigner(t *testing.T) { - assert.Check(t, is.Equal(releasedRoleName, notaryRoleToSigner(data.CanonicalTargetsRole))) - assert.Check(t, is.Equal(releasedRoleName, notaryRoleToSigner(trust.ReleasesRole))) - assert.Check(t, is.Equal("signer", notaryRoleToSigner("targets/signer"))) - assert.Check(t, is.Equal("docker/signer", notaryRoleToSigner("targets/docker/signer"))) - - // It's nonsense for other base roles to have signed off on a target, but this function leaves role names intact - for _, role := range data.BaseRoles { - if role == data.CanonicalTargetsRole { - continue - } - assert.Check(t, is.Equal(role.String(), notaryRoleToSigner(role))) - } - assert.Check(t, is.Equal("notarole", notaryRoleToSigner("notarole"))) -} - -// check if a role name is "released": either targets/releases or targets TUF roles -func TestIsReleasedTarget(t *testing.T) { - assert.Check(t, isReleasedTarget(trust.ReleasesRole)) - for _, role := range data.BaseRoles { - assert.Check(t, is.Equal(role == data.CanonicalTargetsRole, isReleasedTarget(role))) - } - assert.Check(t, !isReleasedTarget("targets/not-releases")) - assert.Check(t, !isReleasedTarget("random")) - assert.Check(t, !isReleasedTarget("targets/releases/subrole")) -} - -// creates a mock delegation with a given name and no keys -func mockDelegationRoleWithName(name string) data.DelegationRole { - baseRole := data.NewBaseRole( - data.RoleName(name), - notary.MinThreshold, - ) - return data.DelegationRole{BaseRole: baseRole, Paths: []string{}} -} - -func TestMatchEmptySignatures(t *testing.T) { - // first try empty targets - emptyTgts := []notaryclient.TargetSignedStruct{} - - matchedSigRows := matchReleasedSignatures(emptyTgts) - assert.Check(t, is.Len(matchedSigRows, 0)) -} - -func TestMatchUnreleasedSignatures(t *testing.T) { - // try an "unreleased" target with 3 signatures, 0 rows will appear - unreleasedTgts := []notaryclient.TargetSignedStruct{} - - tgt := notaryclient.Target{Name: "unreleased", Hashes: data.Hashes{notary.SHA256: []byte("hash")}} - for _, unreleasedRole := range []string{"targets/a", "targets/b", "targets/c"} { - unreleasedTgts = append(unreleasedTgts, notaryclient.TargetSignedStruct{Role: mockDelegationRoleWithName(unreleasedRole), Target: tgt}) - } - - matchedSigRows := matchReleasedSignatures(unreleasedTgts) - assert.Check(t, is.Len(matchedSigRows, 0)) -} - -func TestMatchOneReleasedSingleSignature(t *testing.T) { - // now try only 1 "released" target with no additional sigs, 1 row will appear with 0 signers - oneReleasedTgt := []notaryclient.TargetSignedStruct{} - - // make and append the "released" target to our mock input - releasedTgt := notaryclient.Target{Name: "released", Hashes: data.Hashes{notary.SHA256: []byte("released-hash")}} - oneReleasedTgt = append(oneReleasedTgt, notaryclient.TargetSignedStruct{Role: mockDelegationRoleWithName("targets/releases"), Target: releasedTgt}) - - // make and append 3 non-released signatures on the "unreleased" target - unreleasedTgt := notaryclient.Target{Name: "unreleased", Hashes: data.Hashes{notary.SHA256: []byte("hash")}} - for _, unreleasedRole := range []string{"targets/a", "targets/b", "targets/c"} { - oneReleasedTgt = append(oneReleasedTgt, notaryclient.TargetSignedStruct{Role: mockDelegationRoleWithName(unreleasedRole), Target: unreleasedTgt}) - } - - matchedSigRows := matchReleasedSignatures(oneReleasedTgt) - assert.Check(t, is.Len(matchedSigRows, 1)) - - outputRow := matchedSigRows[0] - // Empty signers because "targets/releases" doesn't show up - assert.Check(t, is.Len(outputRow.Signers, 0)) - assert.Check(t, is.Equal(releasedTgt.Name, outputRow.SignedTag)) - assert.Check(t, is.Equal(hex.EncodeToString(releasedTgt.Hashes[notary.SHA256]), outputRow.Digest)) -} - -func TestMatchOneReleasedMultiSignature(t *testing.T) { - // now try only 1 "released" target with 3 additional sigs, 1 row will appear with 3 signers - oneReleasedTgt := []notaryclient.TargetSignedStruct{} - - // make and append the "released" target to our mock input - releasedTgt := notaryclient.Target{Name: "released", Hashes: data.Hashes{notary.SHA256: []byte("released-hash")}} - oneReleasedTgt = append(oneReleasedTgt, notaryclient.TargetSignedStruct{Role: mockDelegationRoleWithName("targets/releases"), Target: releasedTgt}) - - // make and append 3 non-released signatures on both the "released" and "unreleased" targets - unreleasedTgt := notaryclient.Target{Name: "unreleased", Hashes: data.Hashes{notary.SHA256: []byte("hash")}} - for _, unreleasedRole := range []string{"targets/a", "targets/b", "targets/c"} { - oneReleasedTgt = append(oneReleasedTgt, notaryclient.TargetSignedStruct{Role: mockDelegationRoleWithName(unreleasedRole), Target: unreleasedTgt}) - oneReleasedTgt = append(oneReleasedTgt, notaryclient.TargetSignedStruct{Role: mockDelegationRoleWithName(unreleasedRole), Target: releasedTgt}) - } - - matchedSigRows := matchReleasedSignatures(oneReleasedTgt) - assert.Check(t, is.Len(matchedSigRows, 1)) - - outputRow := matchedSigRows[0] - // We should have three signers - assert.Check(t, is.DeepEqual(outputRow.Signers, []string{"a", "b", "c"})) - assert.Check(t, is.Equal(releasedTgt.Name, outputRow.SignedTag)) - assert.Check(t, is.Equal(hex.EncodeToString(releasedTgt.Hashes[notary.SHA256]), outputRow.Digest)) -} - -func TestMatchMultiReleasedMultiSignature(t *testing.T) { - // now try 3 "released" targets with additional sigs to show 3 rows as follows: - // target-a is signed by targets/releases and targets/a - a will be the signer - // target-b is signed by targets/releases, targets/a, targets/b - a and b will be the signers - // target-c is signed by targets/releases, targets/a, targets/b, targets/c - a, b, and c will be the signers - multiReleasedTgts := []notaryclient.TargetSignedStruct{} - // make target-a, target-b, and target-c - targetA := notaryclient.Target{Name: "target-a", Hashes: data.Hashes{notary.SHA256: []byte("target-a-hash")}} - targetB := notaryclient.Target{Name: "target-b", Hashes: data.Hashes{notary.SHA256: []byte("target-b-hash")}} - targetC := notaryclient.Target{Name: "target-c", Hashes: data.Hashes{notary.SHA256: []byte("target-c-hash")}} - - // have targets/releases "sign" on all of these targets so they are released - multiReleasedTgts = append(multiReleasedTgts, notaryclient.TargetSignedStruct{Role: mockDelegationRoleWithName("targets/releases"), Target: targetA}) - multiReleasedTgts = append(multiReleasedTgts, notaryclient.TargetSignedStruct{Role: mockDelegationRoleWithName("targets/releases"), Target: targetB}) - multiReleasedTgts = append(multiReleasedTgts, notaryclient.TargetSignedStruct{Role: mockDelegationRoleWithName("targets/releases"), Target: targetC}) - - // targets/a signs off on all three targets (target-a, target-b, target-c): - for _, tgt := range []notaryclient.Target{targetA, targetB, targetC} { - multiReleasedTgts = append(multiReleasedTgts, notaryclient.TargetSignedStruct{Role: mockDelegationRoleWithName("targets/a"), Target: tgt}) - } - - // targets/b signs off on the final two targets (target-b, target-c): - for _, tgt := range []notaryclient.Target{targetB, targetC} { - multiReleasedTgts = append(multiReleasedTgts, notaryclient.TargetSignedStruct{Role: mockDelegationRoleWithName("targets/b"), Target: tgt}) - } - - // targets/c only signs off on the last target (target-c): - multiReleasedTgts = append(multiReleasedTgts, notaryclient.TargetSignedStruct{Role: mockDelegationRoleWithName("targets/c"), Target: targetC}) - - matchedSigRows := matchReleasedSignatures(multiReleasedTgts) - assert.Check(t, is.Len(matchedSigRows, 3)) - - // note that the output is sorted by tag name, so we can reliably index to validate data: - outputTargetA := matchedSigRows[0] - assert.Check(t, is.DeepEqual(outputTargetA.Signers, []string{"a"})) - assert.Check(t, is.Equal(targetA.Name, outputTargetA.SignedTag)) - assert.Check(t, is.Equal(hex.EncodeToString(targetA.Hashes[notary.SHA256]), outputTargetA.Digest)) - - outputTargetB := matchedSigRows[1] - assert.Check(t, is.DeepEqual(outputTargetB.Signers, []string{"a", "b"})) - assert.Check(t, is.Equal(targetB.Name, outputTargetB.SignedTag)) - assert.Check(t, is.Equal(hex.EncodeToString(targetB.Hashes[notary.SHA256]), outputTargetB.Digest)) - - outputTargetC := matchedSigRows[2] - assert.Check(t, is.DeepEqual(outputTargetC.Signers, []string{"a", "b", "c"})) - assert.Check(t, is.Equal(targetC.Name, outputTargetC.SignedTag)) - assert.Check(t, is.Equal(hex.EncodeToString(targetC.Hashes[notary.SHA256]), outputTargetC.Digest)) -} - -func TestMatchReleasedSignatureFromTargets(t *testing.T) { - // now try only 1 "released" target with no additional sigs, one rows will appear - oneReleasedTgt := []notaryclient.TargetSignedStruct{} - // make and append the "released" target to our mock input - releasedTgt := notaryclient.Target{Name: "released", Hashes: data.Hashes{notary.SHA256: []byte("released-hash")}} - oneReleasedTgt = append(oneReleasedTgt, notaryclient.TargetSignedStruct{Role: mockDelegationRoleWithName(data.CanonicalTargetsRole.String()), Target: releasedTgt}) - matchedSigRows := matchReleasedSignatures(oneReleasedTgt) - assert.Check(t, is.Len(matchedSigRows, 1)) - outputRow := matchedSigRows[0] - // Empty signers because "targets" doesn't show up - assert.Check(t, is.Len(outputRow.Signers, 0)) - assert.Check(t, is.Equal(releasedTgt.Name, outputRow.SignedTag)) - assert.Check(t, is.Equal(hex.EncodeToString(releasedTgt.Hashes[notary.SHA256]), outputRow.Digest)) -} - -func TestGetSignerRolesWithKeyIDs(t *testing.T) { - roles := []data.Role{ - { - RootRole: data.RootRole{ - KeyIDs: []string{"key11"}, - }, - Name: "targets/alice", - }, - { - RootRole: data.RootRole{ - KeyIDs: []string{"key21", "key22"}, - }, - Name: "targets/releases", - }, - { - RootRole: data.RootRole{ - KeyIDs: []string{"key31"}, - }, - Name: data.CanonicalTargetsRole, - }, - { - RootRole: data.RootRole{ - KeyIDs: []string{"key41", "key01"}, - }, - Name: data.CanonicalRootRole, - }, - { - RootRole: data.RootRole{ - KeyIDs: []string{"key51"}, - }, - Name: data.CanonicalSnapshotRole, - }, - { - RootRole: data.RootRole{ - KeyIDs: []string{"key61"}, - }, - Name: data.CanonicalTimestampRole, - }, - { - RootRole: data.RootRole{ - KeyIDs: []string{"key71", "key72"}, - }, - Name: "targets/bob", - }, - } - expectedSignerRoleToKeyIDs := map[string][]string{ - "alice": {"key11"}, - "bob": {"key71", "key72"}, - } - - signerRoleToKeyIDs := getDelegationRoleToKeyMap(roles) - assert.Check(t, is.DeepEqual(expectedSignerRoleToKeyIDs, signerRoleToKeyIDs)) -} - -func TestFormatAdminRole(t *testing.T) { - aliceRole := data.Role{ - RootRole: data.RootRole{ - KeyIDs: []string{"key11"}, - }, - Name: "targets/alice", - } - aliceRoleWithSigs := notaryclient.RoleWithSignatures{Role: aliceRole, Signatures: nil} - assert.Check(t, is.Equal("", formatAdminRole(aliceRoleWithSigs))) - - releasesRole := data.Role{ - RootRole: data.RootRole{ - KeyIDs: []string{"key11"}, - }, - Name: "targets/releases", - } - releasesRoleWithSigs := notaryclient.RoleWithSignatures{Role: releasesRole, Signatures: nil} - assert.Check(t, is.Equal("", formatAdminRole(releasesRoleWithSigs))) - - timestampRole := data.Role{ - RootRole: data.RootRole{ - KeyIDs: []string{"key11"}, - }, - Name: data.CanonicalTimestampRole, - } - timestampRoleWithSigs := notaryclient.RoleWithSignatures{Role: timestampRole, Signatures: nil} - assert.Check(t, is.Equal("", formatAdminRole(timestampRoleWithSigs))) - - snapshotRole := data.Role{ - RootRole: data.RootRole{ - KeyIDs: []string{"key11"}, - }, - Name: data.CanonicalSnapshotRole, - } - snapshotRoleWithSigs := notaryclient.RoleWithSignatures{Role: snapshotRole, Signatures: nil} - assert.Check(t, is.Equal("", formatAdminRole(snapshotRoleWithSigs))) - - rootRole := data.Role{ - RootRole: data.RootRole{ - KeyIDs: []string{"key11"}, - }, - Name: data.CanonicalRootRole, - } - rootRoleWithSigs := notaryclient.RoleWithSignatures{Role: rootRole, Signatures: nil} - assert.Check(t, is.Equal("Root Key:\tkey11\n", formatAdminRole(rootRoleWithSigs))) - - targetsRole := data.Role{ - RootRole: data.RootRole{ - KeyIDs: []string{"key99", "abc", "key11"}, - }, - Name: data.CanonicalTargetsRole, - } - targetsRoleWithSigs := notaryclient.RoleWithSignatures{Role: targetsRole, Signatures: nil} - assert.Check(t, is.Equal("Repository Key:\tabc, key11, key99\n", formatAdminRole(targetsRoleWithSigs))) -} - -func TestPrintSignerInfoSortOrder(t *testing.T) { - roleToKeyIDs := map[string][]string{ - "signer2-foo": {"B"}, - "signer10-foo": {"C"}, - "signer1-foo": {"A"}, - } - - expected := `SIGNER KEYS -signer1-foo A -signer2-foo B -signer10-foo C -` - buf := new(bytes.Buffer) - assert.NilError(t, printSignerInfo(buf, roleToKeyIDs)) - assert.Check(t, is.Equal(expected, buf.String())) -} diff --git a/cli/command/trust/inspect_test.go b/cli/command/trust/inspect_test.go deleted file mode 100644 index 5823f329093f..000000000000 --- a/cli/command/trust/inspect_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package trust - -import ( - "io" - "testing" - - "github.com/docker/cli/cli/trust" - "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/notary" - "github.com/theupdateframework/notary/client" - "gotest.tools/v3/assert" - "gotest.tools/v3/golden" -) - -func TestTrustInspectCommandErrors(t *testing.T) { - testCases := []struct { - name string - args []string - expectedError string - }{ - { - name: "not-enough-args", - expectedError: "requires at least 1 argument", - }, - { - name: "sha-reference", - args: []string{"870d292919d01a0af7e7f056271dc78792c05f55f49b9b9012b6d89725bd9abd"}, - expectedError: "invalid repository name", - }, - { - name: "invalid-img-reference", - args: []string{"ALPINE"}, - expectedError: "invalid reference format", - }, - } - for _, tc := range testCases { - cmd := newInspectCommand( - test.NewFakeCli(&fakeClient{})) - cmd.Flags().Set("pretty", "true") - cmd.SetArgs(tc.args) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.ErrorContains(t, cmd.Execute(), tc.expectedError) - } -} - -func TestTrustInspectCommandRepositoryErrors(t *testing.T) { - testCases := []struct { - doc string - args []string - notaryRepository func(trust.ImageRefAndAuth, []string) (client.Repository, error) - err string - golden string - }{ - { - doc: "OfflineErrors", - args: []string{"nonexistent-reg-name.io/image"}, - notaryRepository: notary.GetOfflineNotaryRepository, - err: "no signatures or cannot access nonexistent-reg-name.io/image", - }, - { - doc: "OfflineErrorsWithImageTag", - args: []string{"nonexistent-reg-name.io/image:tag"}, - notaryRepository: notary.GetOfflineNotaryRepository, - err: "no signatures or cannot access nonexistent-reg-name.io/image:tag", - }, - { - doc: "UninitializedErrors", - args: []string{"reg/unsigned-img"}, - notaryRepository: notary.GetUninitializedNotaryRepository, - err: "no signatures or cannot access reg/unsigned-img", - golden: "trust-inspect-uninitialized.golden", - }, - { - doc: "UninitializedErrorsWithImageTag", - args: []string{"reg/unsigned-img:tag"}, - notaryRepository: notary.GetUninitializedNotaryRepository, - err: "no signatures or cannot access reg/unsigned-img:tag", - golden: "trust-inspect-uninitialized.golden", - }, - } - - for _, tc := range testCases { - t.Run(tc.doc, func(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(tc.notaryRepository) - cmd := newInspectCommand(cli) - cmd.SetArgs(tc.args) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.ErrorContains(t, cmd.Execute(), tc.err) - if tc.golden != "" { - golden.Assert(t, cli.OutBuffer().String(), tc.golden) - } - }) - } -} - -func TestTrustInspectCommand(t *testing.T) { - testCases := []struct { - doc string - args []string - notaryRepository func(trust.ImageRefAndAuth, []string) (client.Repository, error) - golden string - }{ - { - doc: "EmptyNotaryRepo", - args: []string{"reg/img:unsigned-tag"}, - notaryRepository: notary.GetEmptyTargetsNotaryRepository, - golden: "trust-inspect-empty-repo.golden", - }, - { - doc: "FullRepoWithoutSigners", - args: []string{"signed-repo"}, - notaryRepository: notary.GetLoadedWithNoSignersNotaryRepository, - golden: "trust-inspect-full-repo-no-signers.golden", - }, - { - doc: "OneTagWithoutSigners", - args: []string{"signed-repo:green"}, - notaryRepository: notary.GetLoadedWithNoSignersNotaryRepository, - golden: "trust-inspect-one-tag-no-signers.golden", - }, - { - doc: "FullRepoWithSigners", - args: []string{"signed-repo"}, - notaryRepository: notary.GetLoadedNotaryRepository, - golden: "trust-inspect-full-repo-with-signers.golden", - }, - { - doc: "MultipleFullReposWithSigners", - args: []string{"signed-repo", "signed-repo"}, - notaryRepository: notary.GetLoadedNotaryRepository, - golden: "trust-inspect-multiple-repos-with-signers.golden", - }, - { - doc: "UnsignedTagInSignedRepo", - args: []string{"signed-repo:unsigned"}, - notaryRepository: notary.GetLoadedNotaryRepository, - golden: "trust-inspect-unsigned-tag-with-signers.golden", - }, - } - - for _, tc := range testCases { - t.Run(tc.doc, func(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(tc.notaryRepository) - cmd := newInspectCommand(cli) - cmd.SetArgs(tc.args) - assert.NilError(t, cmd.Execute()) - golden.Assert(t, cli.OutBuffer().String(), tc.golden) - }) - } -} diff --git a/cli/command/trust/key.go b/cli/command/trust/key.go deleted file mode 100644 index f57b44c771bb..000000000000 --- a/cli/command/trust/key.go +++ /dev/null @@ -1,22 +0,0 @@ -package trust - -import ( - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" - "github.com/spf13/cobra" -) - -// newTrustKeyCommand returns a cobra command for `trust key` subcommands -func newTrustKeyCommand(dockerCli command.Streams) *cobra.Command { - cmd := &cobra.Command{ - Use: "key", - Short: "Manage keys for signing Docker images", - Args: cli.NoArgs, - RunE: command.ShowHelp(dockerCli.Err()), - } - cmd.AddCommand( - newKeyGenerateCommand(dockerCli), - newKeyLoadCommand(dockerCli), - ) - return cmd -} diff --git a/cli/command/trust/key_generate.go b/cli/command/trust/key_generate.go deleted file mode 100644 index 9943c4772032..000000000000 --- a/cli/command/trust/key_generate.go +++ /dev/null @@ -1,132 +0,0 @@ -package trust - -import ( - "encoding/pem" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/trust" - "github.com/docker/cli/internal/lazyregexp" - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/theupdateframework/notary" - "github.com/theupdateframework/notary/trustmanager" - "github.com/theupdateframework/notary/tuf/data" - tufutils "github.com/theupdateframework/notary/tuf/utils" -) - -type keyGenerateOptions struct { - name string - directory string -} - -func newKeyGenerateCommand(dockerCli command.Streams) *cobra.Command { - options := keyGenerateOptions{} - cmd := &cobra.Command{ - Use: "generate NAME", - Short: "Generate and load a signing key-pair", - Args: cli.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - options.name = args[0] - return setupPassphraseAndGenerateKeys(dockerCli, options) - }, - } - flags := cmd.Flags() - flags.StringVar(&options.directory, "dir", "", "Directory to generate key in, defaults to current directory") - return cmd -} - -// key names can use lowercase alphanumeric + _ + - characters -var validKeyName = lazyregexp.New(`^[a-z0-9][a-z0-9\_\-]*$`).MatchString - -// validate that all of the key names are unique and are alphanumeric + _ + - -// and that we do not already have public key files in the target dir on disk -func validateKeyArgs(keyName string, targetDir string) error { - if !validKeyName(keyName) { - return fmt.Errorf("key name \"%s\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character", keyName) - } - - pubKeyFileName := keyName + ".pub" - if _, err := os.Stat(targetDir); err != nil { - return fmt.Errorf("public key path does not exist: \"%s\"", targetDir) - } - targetPath := filepath.Join(targetDir, pubKeyFileName) - if _, err := os.Stat(targetPath); err == nil { - return fmt.Errorf("public key file already exists: \"%s\"", targetPath) - } - return nil -} - -func setupPassphraseAndGenerateKeys(streams command.Streams, opts keyGenerateOptions) error { - targetDir := opts.directory - if targetDir == "" { - cwd, err := os.Getwd() - if err != nil { - return err - } - targetDir = cwd - } - return validateAndGenerateKey(streams, opts.name, targetDir) -} - -func validateAndGenerateKey(streams command.Streams, keyName string, workingDir string) error { - freshPassRetGetter := func() notary.PassRetriever { return trust.GetPassphraseRetriever(streams.In(), streams.Out()) } - if err := validateKeyArgs(keyName, workingDir); err != nil { - return err - } - _, _ = fmt.Fprintf(streams.Out(), "Generating key for %s...\n", keyName) - // Automatically load the private key to local storage for use - privKeyFileStore, err := trustmanager.NewKeyFileStore(trust.GetTrustDirectory(), freshPassRetGetter()) - if err != nil { - return err - } - - pubPEM, err := generateKeyAndOutputPubPEM(keyName, privKeyFileStore) - if err != nil { - _, _ = fmt.Fprint(streams.Out(), err) - return errors.Wrapf(err, "failed to generate key for %s", keyName) - } - - // Output the public key to a file in the CWD or specified dir - writtenPubFile, err := writePubKeyPEMToDir(pubPEM, keyName, workingDir) - if err != nil { - return err - } - _, _ = fmt.Fprintln(streams.Out(), "Successfully generated and loaded private key. Corresponding public key available:", writtenPubFile) - - return nil -} - -func generateKeyAndOutputPubPEM(keyName string, privKeyStore trustmanager.KeyStore) (pem.Block, error) { - privKey, err := tufutils.GenerateKey(data.ECDSAKey) - if err != nil { - return pem.Block{}, err - } - - if err := privKeyStore.AddKey(trustmanager.KeyInfo{Role: data.RoleName(keyName)}, privKey); err != nil { - return pem.Block{}, err - } - - pubKey := data.PublicKeyFromPrivate(privKey) - return pem.Block{ - Type: "PUBLIC KEY", - Headers: map[string]string{ - "role": keyName, - }, - Bytes: pubKey.Public(), - }, nil -} - -func writePubKeyPEMToDir(pubPEM pem.Block, keyName, workingDir string) (string, error) { - // Output the public key to a file in the CWD or specified dir - pubFileName := strings.Join([]string{keyName, "pub"}, ".") - pubFilePath := filepath.Join(workingDir, pubFileName) - if err := os.WriteFile(pubFilePath, pem.EncodeToMemory(&pubPEM), notary.PrivNoExecPerms); err != nil { - return "", errors.Wrapf(err, "failed to write public key to %s", pubFilePath) - } - return pubFilePath, nil -} diff --git a/cli/command/trust/key_generate_test.go b/cli/command/trust/key_generate_test.go deleted file mode 100644 index 1efd1d31214c..000000000000 --- a/cli/command/trust/key_generate_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package trust - -import ( - "encoding/pem" - "fmt" - "io" - "os" - "path/filepath" - "testing" - - "github.com/docker/cli/cli/config" - "github.com/docker/cli/internal/test" - "github.com/theupdateframework/notary" - "github.com/theupdateframework/notary/trustmanager" - tufutils "github.com/theupdateframework/notary/tuf/utils" - "gotest.tools/v3/assert" - is "gotest.tools/v3/assert/cmp" -) - -func TestTrustKeyGenerateErrors(t *testing.T) { - testCases := []struct { - name string - args []string - expectedError string - }{ - { - name: "not-enough-args", - expectedError: "requires 1 argument", - }, - { - name: "too-many-args", - args: []string{"key-1", "key-2"}, - expectedError: "requires 1 argument", - }, - } - - config.SetDir(t.TempDir()) - - for _, tc := range testCases { - cli := test.NewFakeCli(&fakeClient{}) - cmd := newKeyGenerateCommand(cli) - cmd.SetArgs(tc.args) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.ErrorContains(t, cmd.Execute(), tc.expectedError) - } -} - -func TestGenerateKeySuccess(t *testing.T) { - pubKeyCWD := t.TempDir() - privKeyStorageDir := t.TempDir() - - // generate a single key - keyName := "alice" - privKeyFileStore, err := trustmanager.NewKeyFileStore(privKeyStorageDir, testPassRetriever) - assert.NilError(t, err) - - pubKeyPEM, err := generateKeyAndOutputPubPEM(keyName, privKeyFileStore) - assert.NilError(t, err) - - assert.Check(t, is.Equal(keyName, pubKeyPEM.Headers["role"])) - // the default GUN is empty - assert.Check(t, is.Equal("", pubKeyPEM.Headers["gun"])) - // assert public key header - assert.Check(t, is.Equal("PUBLIC KEY", pubKeyPEM.Type)) - - // check that an appropriate ~//private/.key file exists - expectedPrivKeyDir := filepath.Join(privKeyStorageDir, notary.PrivDir) - _, err = os.Stat(expectedPrivKeyDir) - assert.NilError(t, err) - - keyFiles, err := os.ReadDir(expectedPrivKeyDir) - assert.NilError(t, err) - assert.Check(t, is.Len(keyFiles, 1)) - privKeyFilePath := filepath.Join(expectedPrivKeyDir, keyFiles[0].Name()) - - // verify the key content - privFrom, _ := os.OpenFile(privKeyFilePath, os.O_RDONLY, notary.PrivExecPerms) - defer privFrom.Close() - fromBytes, _ := io.ReadAll(privFrom) - privKeyPEM, _ := pem.Decode(fromBytes) - assert.Check(t, is.Equal(keyName, privKeyPEM.Headers["role"])) - // the default GUN is empty - assert.Check(t, is.Equal("", privKeyPEM.Headers["gun"])) - // assert encrypted header - assert.Check(t, is.Equal("ENCRYPTED PRIVATE KEY", privKeyPEM.Type)) - // check that the passphrase matches - _, err = tufutils.ParsePKCS8ToTufKey(privKeyPEM.Bytes, []byte(testPass)) - assert.NilError(t, err) - - // check that the public key exists at the correct path if we use the helper: - returnedPath, err := writePubKeyPEMToDir(pubKeyPEM, keyName, pubKeyCWD) - assert.NilError(t, err) - expectedPubKeyPath := filepath.Join(pubKeyCWD, keyName+".pub") - assert.Check(t, is.Equal(returnedPath, expectedPubKeyPath)) - _, err = os.Stat(expectedPubKeyPath) - assert.NilError(t, err) - // check that the public key is the only file output in CWD - cwdKeyFiles, err := os.ReadDir(pubKeyCWD) - assert.NilError(t, err) - assert.Check(t, is.Len(cwdKeyFiles, 1)) -} - -func TestValidateKeyArgs(t *testing.T) { - pubKeyCWD := t.TempDir() - - err := validateKeyArgs("a", pubKeyCWD) - assert.NilError(t, err) - - err = validateKeyArgs("a/b", pubKeyCWD) - assert.Error(t, err, "key name \"a/b\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character") - - err = validateKeyArgs("-", pubKeyCWD) - assert.Error(t, err, "key name \"-\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character") - - assert.NilError(t, os.WriteFile(filepath.Join(pubKeyCWD, "a.pub"), []byte("abc"), notary.PrivExecPerms)) - err = validateKeyArgs("a", pubKeyCWD) - assert.Error(t, err, fmt.Sprintf("public key file already exists: \"%s\"", filepath.Join(pubKeyCWD, "a.pub"))) - - err = validateKeyArgs("a", "/random/dir/") - assert.Error(t, err, "public key path does not exist: \"/random/dir/\"") -} diff --git a/cli/command/trust/key_load.go b/cli/command/trust/key_load.go deleted file mode 100644 index b4ce1d4d51f6..000000000000 --- a/cli/command/trust/key_load.go +++ /dev/null @@ -1,118 +0,0 @@ -package trust - -import ( - "bytes" - "encoding/pem" - "fmt" - "io" - "os" - "runtime" - - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/trust" - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/theupdateframework/notary" - "github.com/theupdateframework/notary/storage" - "github.com/theupdateframework/notary/trustmanager" - tufutils "github.com/theupdateframework/notary/tuf/utils" -) - -const ( - nonOwnerReadWriteMask = 0o077 -) - -type keyLoadOptions struct { - keyName string -} - -func newKeyLoadCommand(dockerCli command.Streams) *cobra.Command { - var options keyLoadOptions - cmd := &cobra.Command{ - Use: "load [OPTIONS] KEYFILE", - Short: "Load a private key file for signing", - Args: cli.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - return loadPrivKey(dockerCli, args[0], options) - }, - } - flags := cmd.Flags() - flags.StringVar(&options.keyName, "name", "signer", "Name for the loaded key") - return cmd -} - -func loadPrivKey(streams command.Streams, keyPath string, options keyLoadOptions) error { - // validate the key name if provided - if options.keyName != "" && !validKeyName(options.keyName) { - return fmt.Errorf("key name \"%s\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character", options.keyName) - } - trustDir := trust.GetTrustDirectory() - keyFileStore, err := storage.NewPrivateKeyFileStorage(trustDir, notary.KeyExtension) - if err != nil { - return err - } - privKeyImporters := []trustmanager.Importer{keyFileStore} - - _, _ = fmt.Fprintf(streams.Out(), "Loading key from \"%s\"...\n", keyPath) - - // Always use a fresh passphrase retriever for each import - passRet := trust.GetPassphraseRetriever(streams.In(), streams.Out()) - keyBytes, err := getPrivKeyBytesFromPath(keyPath) - if err != nil { - return errors.Wrapf(err, "refusing to load key from %s", keyPath) - } - if err := loadPrivKeyBytesToStore(keyBytes, privKeyImporters, keyPath, options.keyName, passRet); err != nil { - return errors.Wrapf(err, "error importing key from %s", keyPath) - } - _, _ = fmt.Fprintln(streams.Out(), "Successfully imported key from", keyPath) - return nil -} - -func getPrivKeyBytesFromPath(keyPath string) ([]byte, error) { - if runtime.GOOS != "windows" { - fileInfo, err := os.Stat(keyPath) - if err != nil { - return nil, err - } - if fileInfo.Mode()&nonOwnerReadWriteMask != 0 { - return nil, fmt.Errorf("private key file %s must not be readable or writable by others", keyPath) - } - } - - from, err := os.OpenFile(keyPath, os.O_RDONLY, notary.PrivExecPerms) - if err != nil { - return nil, err - } - defer from.Close() - - return io.ReadAll(from) -} - -func loadPrivKeyBytesToStore(privKeyBytes []byte, privKeyImporters []trustmanager.Importer, keyPath, keyName string, passRet notary.PassRetriever) error { - var err error - if _, _, err = tufutils.ExtractPrivateKeyAttributes(privKeyBytes); err != nil { - return fmt.Errorf("provided file %s is not a supported private key - to add a signer's public key use docker trust signer add", keyPath) - } - if privKeyBytes, err = decodePrivKeyIfNecessary(privKeyBytes, passRet); err != nil { - return errors.Wrapf(err, "cannot load key from provided file %s", keyPath) - } - // Make a reader, rewind the file pointer - return trustmanager.ImportKeys(bytes.NewReader(privKeyBytes), privKeyImporters, keyName, "", passRet) -} - -func decodePrivKeyIfNecessary(privPemBytes []byte, passRet notary.PassRetriever) ([]byte, error) { - pemBlock, _ := pem.Decode(privPemBytes) - _, containsDEKInfo := pemBlock.Headers["DEK-Info"] - if containsDEKInfo || pemBlock.Type == "ENCRYPTED PRIVATE KEY" { - // if we do not have enough information to properly import, try to decrypt the key - if _, ok := pemBlock.Headers["path"]; !ok { - privKey, _, err := trustmanager.GetPasswdDecryptBytes(passRet, privPemBytes, "", "encrypted") - if err != nil { - return []byte{}, errors.New("could not decrypt key") - } - privPemBytes = privKey.Private() - } - } - return privPemBytes, nil -} diff --git a/cli/command/trust/key_load_test.go b/cli/command/trust/key_load_test.go deleted file mode 100644 index 2c004d38849d..000000000000 --- a/cli/command/trust/key_load_test.go +++ /dev/null @@ -1,224 +0,0 @@ -package trust - -import ( - "encoding/pem" - "fmt" - "io" - "os" - "path/filepath" - "runtime" - "testing" - - "github.com/docker/cli/cli/config" - "github.com/docker/cli/internal/test" - "github.com/theupdateframework/notary" - "github.com/theupdateframework/notary/storage" - "github.com/theupdateframework/notary/trustmanager" - tufutils "github.com/theupdateframework/notary/tuf/utils" - "gotest.tools/v3/assert" - is "gotest.tools/v3/assert/cmp" - "gotest.tools/v3/skip" -) - -func TestTrustKeyLoadErrors(t *testing.T) { - noSuchFile := "stat iamnotakey: no such file or directory" - if runtime.GOOS == "windows" { - noSuchFile = "CreateFile iamnotakey: The system cannot find the file specified." - } - testCases := []struct { - name string - args []string - expectedError string - expectedOutput string - }{ - { - name: "not-enough-args", - expectedError: "1 argument", - args: []string{}, - expectedOutput: "", - }, - { - name: "too-many-args", - args: []string{"iamnotakey", "alsonotakey"}, - expectedError: "1 argument", - expectedOutput: "", - }, - { - name: "not-a-key", - args: []string{"iamnotakey"}, - expectedError: "refusing to load key from iamnotakey: " + noSuchFile, - expectedOutput: "Loading key from \"iamnotakey\"...\n", - }, - { - name: "bad-key-name", - args: []string{"iamnotakey", "--name", "KEYNAME"}, - expectedError: "key name \"KEYNAME\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character", - expectedOutput: "", - }, - } - config.SetDir(t.TempDir()) - - for _, tc := range testCases { - cli := test.NewFakeCli(&fakeClient{}) - cmd := newKeyLoadCommand(cli) - cmd.SetArgs(tc.args) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.ErrorContains(t, cmd.Execute(), tc.expectedError) - assert.Check(t, is.Contains(cli.OutBuffer().String(), tc.expectedOutput)) - } -} - -var rsaPrivKeyFixture = []byte(`-----BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAs7yVMzCw8CBZPoN+QLdx3ZzbVaHnouHIKu+ynX60IZ3stpbb -6rowu78OWON252JcYJqe++2GmdIgbBhg+mZDwhX0ZibMVztJaZFsYL+Ch/2J9KqD -A5NtE1s/XdhYoX5hsv7W4ok9jLFXRYIMj+T4exJRlR4f4GP9p0fcqPWd9/enPnlJ -JFTmu0DXJTZUMVS1UrXUy5t/DPXdrwyl8pM7VCqO3bqK7jqE6mWawdTkEeiku1fJ -ydP0285uiYTbj1Q38VVhPwXzMuLbkaUgRJhCI4BcjfQIjtJLbWpS+VdhUEvtgMVx -XJMKxCVGG69qjXyj9TjI7pxanb/bWglhovJN9wIDAQABAoIBAQCSnMsLxbUfOxPx -RWuwOLN+NZxIvtfnastQEtSdWiRvo5Xa3zYmw5hLHa8DXRC57+cwug/jqr54LQpb -gotg1hiBck05In7ezTK2FXTVeoJskal91bUnLpP0DSOkVnz9xszFKNF6Wr7FTEfH -IC1FF16Fbcz0mW0hKg9X6+uYOzqPcKpQRwli5LAwhT18Alf9h4/3NCeKotiJyr2J -xvcEH1eY2m2c/jQZurBkys7qBC3+i8LJEOW8MBQt7mxajwfbU91wtP2YoqMcoYiS -zsPbYp7Ui2t4G9Yn+OJw+uj4RGP1Bo4nSyRxWDtg+8Zug/JYU6/s+8kVRpiGffd3 -T1GvoxUhAoGBAOnPDWG/g1xlJf65Rh71CxMs638zhYbIloU2K4Rqr05DHe7GryTS -9hLVrwhHddK+KwfVbR8HFMPo1DC/NVbuKt8StTAadAu3HsC088gWd28nOiGAWuvH -Bo3x/DYQGYwGFfoo4rzCOgMj6DJjXmcWEXNv3NDMoXoYpkxa0g6zZDyHAoGBAMTL -t7EUneJT+Mm7wyL1I5bmaT/HFwqoUQB2ccBPVD8p1el62NgLdfhOa8iNlBVhMrlh -2aTjrMlSPcjr9sCgKrLcenSWw+2qFsf4+SmV01ntB9kWes2phXpnB0ynXIcbeG05 -+BLxbqDTVV0Iqh4r/dGeplyV2WyL3mTpkT3hRq8RAoGAZ93degEUICWnHWO9LN97 -Dge0joua0+ekRoVsC6VBP6k9UOfewqMdQfy/hxQH2Zk1kINVuKTyqp1yNj2bOoUP -co3jA/2cc9/jv4QjkE26vRxWDK/ytC90T/aiLno0fyns9XbYUzaNgvuemVPfijgZ -hIi7Nd7SFWWB6wWlr3YuH10CgYEAwh7JVa2mh8iZEjVaKTNyJbmmfDjgq6yYKkKr -ti0KRzv3O9Xn7ERx27tPaobtWaGFLYQt8g57NCMhuv23aw8Sz1fYmwTUw60Rx7P5 -42FdF8lOAn/AJvpfJfxXIO+9v7ADPIr//3+TxqRwAdM4K4btWkaKh61wyTe26gfT -MxzyYmECgYAnlU5zsGyiZqwoXVktkhtZrE7Qu0SoztzFb8KpvFNmMTPF1kAAYmJY -GIhbizeGJ3h4cUdozKmt8ZWIt6uFDEYCqEA7XF4RH75dW25x86mpIPO7iRl9eisY -IsLeMYqTIwXAwGx6Ka9v5LOL1kzcHQ2iVj6+QX+yoptSft1dYa9jOA== ------END RSA PRIVATE KEY-----`) - -const rsaPrivKeyID = "ee69e8e07a14756ad5ff0aca2336b37f86b0ac1710d1f3e94440081e080aecd7" - -var ecPrivKeyFixture = []byte(`-----BEGIN EC PRIVATE KEY----- -MHcCAQEEINfxKtDH3ug7ZIQPDyeAzujCdhw36D+bf9ToPE1A7YEyoAoGCCqGSM49 -AwEHoUQDQgAEUIH9AYtrcDFzZrFJBdJZkn21d+4cH3nzy2O6Q/ct4BjOBKa+WCdR -tPo78bA+C/7t81ADQO8Jqaj59W50rwoqDQ== ------END EC PRIVATE KEY-----`) - -const ecPrivKeyID = "46157cb0becf9c72c3219e11d4692424fef9bf4460812ccc8a71a3dfcafc7e60" - -var testKeys = map[string][]byte{ - ecPrivKeyID: ecPrivKeyFixture, - rsaPrivKeyID: rsaPrivKeyFixture, -} - -func TestLoadKeyFromPath(t *testing.T) { - skip.If(t, runtime.GOOS == "windows") - for keyID, keyBytes := range testKeys { - t.Run(fmt.Sprintf("load-key-id-%s-from-path", keyID), func(t *testing.T) { - privKeyFilepath := filepath.Join(t.TempDir(), "privkey.pem") - assert.NilError(t, os.WriteFile(privKeyFilepath, keyBytes, notary.PrivNoExecPerms)) - - keyStorageDir := t.TempDir() - - keyFileStore, err := storage.NewPrivateKeyFileStorage(keyStorageDir, notary.KeyExtension) - assert.NilError(t, err) - privKeyImporters := []trustmanager.Importer{keyFileStore} - - // get the privKeyBytes - privKeyBytes, err := getPrivKeyBytesFromPath(privKeyFilepath) - assert.NilError(t, err) - - // import the key to our keyStorageDir - assert.Check(t, loadPrivKeyBytesToStore(privKeyBytes, privKeyImporters, privKeyFilepath, "signer-name", testPassRetriever)) - - // check that the appropriate ~//private/.key file exists - expectedImportKeyPath := filepath.Join(keyStorageDir, notary.PrivDir, keyID+"."+notary.KeyExtension) - _, err = os.Stat(expectedImportKeyPath) - assert.NilError(t, err) - - // verify the key content - from, _ := os.OpenFile(expectedImportKeyPath, os.O_RDONLY, notary.PrivExecPerms) - defer from.Close() - fromBytes, _ := io.ReadAll(from) - keyPEM, _ := pem.Decode(fromBytes) - assert.Check(t, is.Equal("signer-name", keyPEM.Headers["role"])) - // the default GUN is empty - assert.Check(t, is.Equal("", keyPEM.Headers["gun"])) - // assert encrypted header - assert.Check(t, is.Equal("ENCRYPTED PRIVATE KEY", keyPEM.Type)) - - decryptedKey, err := tufutils.ParsePKCS8ToTufKey(keyPEM.Bytes, []byte(testPass)) - assert.NilError(t, err) - fixturePEM, _ := pem.Decode(keyBytes) - assert.Check(t, is.DeepEqual(fixturePEM.Bytes, decryptedKey.Private())) - }) - } -} - -func TestLoadKeyTooPermissive(t *testing.T) { - skip.If(t, runtime.GOOS == "windows") - for keyID, keyBytes := range testKeys { - t.Run(fmt.Sprintf("load-key-id-%s-too-permissive", keyID), func(t *testing.T) { - privKeyDir := t.TempDir() - privKeyFilepath := filepath.Join(privKeyDir, "privkey477.pem") - assert.NilError(t, os.WriteFile(privKeyFilepath, keyBytes, 0o477)) - - // import the key to our keyStorageDir - _, err := getPrivKeyBytesFromPath(privKeyFilepath) - expected := fmt.Sprintf("private key file %s must not be readable or writable by others", privKeyFilepath) - assert.Error(t, err, expected) - - privKeyFilepath = filepath.Join(privKeyDir, "privkey667.pem") - assert.NilError(t, os.WriteFile(privKeyFilepath, keyBytes, 0o677)) - - _, err = getPrivKeyBytesFromPath(privKeyFilepath) - expected = fmt.Sprintf("private key file %s must not be readable or writable by others", privKeyFilepath) - assert.Error(t, err, expected) - - privKeyFilepath = filepath.Join(privKeyDir, "privkey777.pem") - assert.NilError(t, os.WriteFile(privKeyFilepath, keyBytes, 0o777)) - - _, err = getPrivKeyBytesFromPath(privKeyFilepath) - expected = fmt.Sprintf("private key file %s must not be readable or writable by others", privKeyFilepath) - assert.Error(t, err, expected) - - privKeyFilepath = filepath.Join(privKeyDir, "privkey400.pem") - assert.NilError(t, os.WriteFile(privKeyFilepath, keyBytes, 0o400)) - - _, err = getPrivKeyBytesFromPath(privKeyFilepath) - assert.NilError(t, err) - - privKeyFilepath = filepath.Join(privKeyDir, "privkey600.pem") - assert.NilError(t, os.WriteFile(privKeyFilepath, keyBytes, 0o600)) - - _, err = getPrivKeyBytesFromPath(privKeyFilepath) - assert.NilError(t, err) - }) - } -} - -var pubKeyFixture = []byte(`-----BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUIH9AYtrcDFzZrFJBdJZkn21d+4c -H3nzy2O6Q/ct4BjOBKa+WCdRtPo78bA+C/7t81ADQO8Jqaj59W50rwoqDQ== ------END PUBLIC KEY-----`) - -func TestLoadPubKeyFailure(t *testing.T) { - skip.If(t, runtime.GOOS == "windows") - pubKeyDir := t.TempDir() - pubKeyFilepath := filepath.Join(pubKeyDir, "pubkey.pem") - assert.NilError(t, os.WriteFile(pubKeyFilepath, pubKeyFixture, notary.PrivNoExecPerms)) - keyStorageDir := t.TempDir() - - keyFileStore, err := storage.NewPrivateKeyFileStorage(keyStorageDir, notary.KeyExtension) - assert.NilError(t, err) - privKeyImporters := []trustmanager.Importer{keyFileStore} - - pubKeyBytes, err := getPrivKeyBytesFromPath(pubKeyFilepath) - assert.NilError(t, err) - - // import the key to our keyStorageDir - it should fail - err = loadPrivKeyBytesToStore(pubKeyBytes, privKeyImporters, pubKeyFilepath, "signer-name", testPassRetriever) - expected := fmt.Sprintf("provided file %s is not a supported private key - to add a signer's public key use docker trust signer add", pubKeyFilepath) - assert.Error(t, err, expected) -} diff --git a/cli/command/trust/revoke.go b/cli/command/trust/revoke.go deleted file mode 100644 index 303c7b8c2d68..000000000000 --- a/cli/command/trust/revoke.go +++ /dev/null @@ -1,130 +0,0 @@ -package trust - -import ( - "context" - "fmt" - - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/command/image" - "github.com/docker/cli/cli/trust" - "github.com/docker/cli/internal/prompt" - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/theupdateframework/notary/client" - "github.com/theupdateframework/notary/tuf/data" -) - -type revokeOptions struct { - forceYes bool -} - -func newRevokeCommand(dockerCLI command.Cli) *cobra.Command { - options := revokeOptions{} - cmd := &cobra.Command{ - Use: "revoke [OPTIONS] IMAGE[:TAG]", - Short: "Remove trust for an image", - Args: cli.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - return revokeTrust(cmd.Context(), dockerCLI, args[0], options) - }, - } - flags := cmd.Flags() - flags.BoolVarP(&options.forceYes, "yes", "y", false, "Do not prompt for confirmation") - return cmd -} - -func revokeTrust(ctx context.Context, dockerCLI command.Cli, remote string, options revokeOptions) error { - imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, image.AuthResolver(dockerCLI), remote) - if err != nil { - return err - } - tag := imgRefAndAuth.Tag() - if imgRefAndAuth.Tag() == "" && imgRefAndAuth.Digest() != "" { - return errors.New("cannot use a digest reference for IMAGE:TAG") - } - if imgRefAndAuth.Tag() == "" && !options.forceYes { - deleteRemote, err := prompt.Confirm(ctx, dockerCLI.In(), dockerCLI.Out(), fmt.Sprintf("Confirm you would like to delete all signature data for %s?", remote)) - if err != nil { - return err - } - if !deleteRemote { - return cancelledErr{errors.New("trust revoke has been cancelled")} - } - } - - notaryRepo, err := newNotaryClient(dockerCLI, imgRefAndAuth, trust.ActionsPushAndPull) - if err != nil { - return err - } - - if err = clearChangeList(notaryRepo); err != nil { - return err - } - defer clearChangeList(notaryRepo) - if err := revokeSignature(notaryRepo, tag); err != nil { - return errors.Wrapf(err, "could not remove signature for %s", remote) - } - _, _ = fmt.Fprintf(dockerCLI.Out(), "Successfully deleted signature for %s\n", remote) - return nil -} - -type cancelledErr struct{ error } - -func (cancelledErr) Cancelled() {} - -func revokeSignature(notaryRepo client.Repository, tag string) error { - if tag != "" { - // Revoke signature for the specified tag - if err := revokeSingleSig(notaryRepo, tag); err != nil { - return err - } - } else { - // revoke all signatures for the image, as no tag was given - if err := revokeAllSigs(notaryRepo); err != nil { - return err - } - } - - // Publish change - return notaryRepo.Publish() -} - -func revokeSingleSig(notaryRepo client.Repository, tag string) error { - releasedTargetWithRole, err := notaryRepo.GetTargetByName(tag, trust.ReleasesRole, data.CanonicalTargetsRole) - if err != nil { - return err - } - releasedTarget := releasedTargetWithRole.Target - return getSignableRolesForTargetAndRemove(releasedTarget, notaryRepo) -} - -func revokeAllSigs(notaryRepo client.Repository) error { - releasedTargetWithRoleList, err := notaryRepo.ListTargets(trust.ReleasesRole, data.CanonicalTargetsRole) - if err != nil { - return err - } - - if len(releasedTargetWithRoleList) == 0 { - return errors.New("no signed tags to remove") - } - - // we need all the roles that signed each released target so we can remove from all roles. - for _, releasedTargetWithRole := range releasedTargetWithRoleList { - // remove from all roles - if err := getSignableRolesForTargetAndRemove(releasedTargetWithRole.Target, notaryRepo); err != nil { - return err - } - } - return nil -} - -// get all the roles that signed the target and removes it from all roles. -func getSignableRolesForTargetAndRemove(releasedTarget client.Target, notaryRepo client.Repository) error { - signableRoles, err := trust.GetSignableRoles(notaryRepo, &releasedTarget) - if err != nil { - return err - } - // remove from all roles - return notaryRepo.RemoveTarget(releasedTarget.Name, signableRoles...) -} diff --git a/cli/command/trust/revoke_test.go b/cli/command/trust/revoke_test.go deleted file mode 100644 index da1e48ecf876..000000000000 --- a/cli/command/trust/revoke_test.go +++ /dev/null @@ -1,163 +0,0 @@ -package trust - -import ( - "context" - "io" - "testing" - - "github.com/docker/cli/cli/trust" - "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/notary" - "github.com/theupdateframework/notary/client" - "gotest.tools/v3/assert" - is "gotest.tools/v3/assert/cmp" - "gotest.tools/v3/golden" -) - -func TestTrustRevokeCommandErrors(t *testing.T) { - testCases := []struct { - name string - args []string - expectedError string - }{ - { - name: "not-enough-args", - expectedError: "requires 1 argument", - }, - { - name: "too-many-args", - args: []string{"remote1", "remote2"}, - expectedError: "requires 1 argument", - }, - { - name: "sha-reference", - args: []string{"870d292919d01a0af7e7f056271dc78792c05f55f49b9b9012b6d89725bd9abd"}, - expectedError: "invalid repository name", - }, - { - name: "invalid-img-reference", - args: []string{"ALPINE"}, - expectedError: "invalid reference format", - }, - { - name: "digest-reference", - args: []string{"ubuntu@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2"}, - expectedError: "cannot use a digest reference for IMAGE:TAG", - }, - } - for _, tc := range testCases { - cmd := newRevokeCommand( - test.NewFakeCli(&fakeClient{})) - cmd.SetArgs(tc.args) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.ErrorContains(t, cmd.Execute(), tc.expectedError) - } -} - -func TestTrustRevokeCommand(t *testing.T) { - revokeCancelledError := "trust revoke has been cancelled" - - testCases := []struct { - doc string - notaryRepository func(trust.ImageRefAndAuth, []string) (client.Repository, error) - args []string - expectedErr string - expectedMessage string - }{ - { - doc: "OfflineErrors_Confirm", - notaryRepository: notary.GetOfflineNotaryRepository, - args: []string{"reg-name.io/image"}, - expectedMessage: "Confirm you would like to delete all signature data for reg-name.io/image? [y/N] ", - expectedErr: revokeCancelledError, - }, - { - doc: "OfflineErrors_Offline", - notaryRepository: notary.GetOfflineNotaryRepository, - args: []string{"reg-name.io/image", "-y"}, - expectedErr: "could not remove signature for reg-name.io/image: client is offline", - }, - { - doc: "OfflineErrors_WithTag_Offline", - notaryRepository: notary.GetOfflineNotaryRepository, - args: []string{"reg-name.io/image:tag"}, - expectedErr: "could not remove signature for reg-name.io/image:tag: client is offline", - }, - { - doc: "UninitializedErrors_Confirm", - notaryRepository: notary.GetUninitializedNotaryRepository, - args: []string{"reg-name.io/image"}, - expectedMessage: "Confirm you would like to delete all signature data for reg-name.io/image? [y/N] ", - expectedErr: revokeCancelledError, - }, - { - doc: "UninitializedErrors_NoTrustData", - notaryRepository: notary.GetUninitializedNotaryRepository, - args: []string{"reg-name.io/image", "-y"}, - expectedErr: "could not remove signature for reg-name.io/image: does not have trust data for", - }, - { - doc: "UninitializedErrors_WithTag_NoTrustData", - notaryRepository: notary.GetUninitializedNotaryRepository, - args: []string{"reg-name.io/image:tag"}, - expectedErr: "could not remove signature for reg-name.io/image:tag: does not have trust data for", - }, - { - doc: "EmptyNotaryRepo_Confirm", - notaryRepository: notary.GetEmptyTargetsNotaryRepository, - args: []string{"reg-name.io/image"}, - expectedMessage: "Confirm you would like to delete all signature data for reg-name.io/image? [y/N] ", - expectedErr: revokeCancelledError, - }, - { - doc: "EmptyNotaryRepo_NoSignedTags", - notaryRepository: notary.GetEmptyTargetsNotaryRepository, - args: []string{"reg-name.io/image", "-y"}, - expectedErr: "could not remove signature for reg-name.io/image: no signed tags to remove", - }, - { - doc: "EmptyNotaryRepo_NoValidTrustData", - notaryRepository: notary.GetEmptyTargetsNotaryRepository, - args: []string{"reg-name.io/image:tag"}, - expectedErr: "could not remove signature for reg-name.io/image:tag: No valid trust data for tag", - }, - { - doc: "AllSigConfirmation", - notaryRepository: notary.GetEmptyTargetsNotaryRepository, - args: []string{"alpine"}, - expectedMessage: "Confirm you would like to delete all signature data for alpine? [y/N] ", - expectedErr: revokeCancelledError, - }, - } - - for _, tc := range testCases { - t.Run(tc.doc, func(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(tc.notaryRepository) - cmd := newRevokeCommand(cli) - cmd.SetArgs(tc.args) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - if tc.expectedErr != "" { - assert.ErrorContains(t, cmd.Execute(), tc.expectedErr) - } else { - assert.NilError(t, cmd.Execute()) - } - assert.Check(t, is.Contains(cli.OutBuffer().String(), tc.expectedMessage)) - }) - } -} - -func TestRevokeTrustPromptTermination(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - cli := test.NewFakeCli(&fakeClient{}) - cmd := newRevokeCommand(cli) - cmd.SetArgs([]string{"example/trust-demo"}) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - test.TerminatePrompt(ctx, t, cmd, cli) - golden.Assert(t, cli.OutBuffer().String(), "trust-revoke-prompt-termination.golden") -} diff --git a/cli/command/trust/sign.go b/cli/command/trust/sign.go deleted file mode 100644 index df97f04d3250..000000000000 --- a/cli/command/trust/sign.go +++ /dev/null @@ -1,264 +0,0 @@ -package trust - -import ( - "context" - "fmt" - "io" - "path" - "sort" - "strings" - - "github.com/distribution/reference" - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/command/image" - "github.com/docker/cli/cli/trust" - imagetypes "github.com/docker/docker/api/types/image" - registrytypes "github.com/docker/docker/api/types/registry" - "github.com/docker/docker/client" - "github.com/pkg/errors" - "github.com/spf13/cobra" - notaryclient "github.com/theupdateframework/notary/client" - "github.com/theupdateframework/notary/tuf/data" -) - -type signOptions struct { - local bool - imageName string -} - -func newSignCommand(dockerCLI command.Cli) *cobra.Command { - options := signOptions{} - cmd := &cobra.Command{ - Use: "sign IMAGE:TAG", - Short: "Sign an image", - Args: cli.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - options.imageName = args[0] - return runSignImage(cmd.Context(), dockerCLI, options) - }, - } - flags := cmd.Flags() - flags.BoolVar(&options.local, "local", false, "Sign a locally tagged image") - return cmd -} - -func runSignImage(ctx context.Context, dockerCLI command.Cli, options signOptions) error { - imageName := options.imageName - imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, image.AuthResolver(dockerCLI), imageName) - if err != nil { - return err - } - if err := validateTag(imgRefAndAuth); err != nil { - return err - } - - notaryRepo, err := newNotaryClient(dockerCLI, imgRefAndAuth, trust.ActionsPushAndPull) - if err != nil { - return trust.NotaryError(imgRefAndAuth.Reference().Name(), err) - } - if err = clearChangeList(notaryRepo); err != nil { - return err - } - defer clearChangeList(notaryRepo) - - // get the latest repository metadata so we can figure out which roles to sign - if _, err = notaryRepo.ListTargets(); err != nil { - switch err.(type) { - case notaryclient.ErrRepoNotInitialized, notaryclient.ErrRepositoryNotExist: - // before initializing a new repo, check that the image exists locally: - if err := checkLocalImageExistence(ctx, dockerCLI.Client(), imageName); err != nil { - return err - } - - userRole := data.RoleName(path.Join(data.CanonicalTargetsRole.String(), imgRefAndAuth.AuthConfig().Username)) - if err := initNotaryRepoWithSigners(notaryRepo, userRole); err != nil { - return trust.NotaryError(imgRefAndAuth.Reference().Name(), err) - } - - _, _ = fmt.Fprintln(dockerCLI.Out(), "Created signer:", imgRefAndAuth.AuthConfig().Username) - _, _ = fmt.Fprintln(dockerCLI.Out(), "Finished initializing signed repository for", imageName) - default: - return trust.NotaryError(imgRefAndAuth.RepoInfo().Name.Name(), err) - } - } - requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCLI, imgRefAndAuth.RepoInfo().Index, "push") - target, err := createTarget(notaryRepo, imgRefAndAuth.Tag()) - if err != nil || options.local { - switch err := err.(type) { - // If the error is nil then the local flag is set - case notaryclient.ErrNoSuchTarget, notaryclient.ErrRepositoryNotExist, nil: - // Fail fast if the image doesn't exist locally - if err := checkLocalImageExistence(ctx, dockerCLI.Client(), imageName); err != nil { - return err - } - _, _ = 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) - encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig) - if err != nil { - return err - } - responseBody, err := dockerCLI.Client().ImagePush(ctx, reference.FamiliarString(imgRefAndAuth.Reference()), imagetypes.PushOptions{ - RegistryAuth: encodedAuth, - PrivilegeFunc: requestPrivilege, - }) - if err != nil { - return err - } - defer responseBody.Close() - return trust.PushTrustedReference(ctx, dockerCLI, imgRefAndAuth.RepoInfo(), imgRefAndAuth.Reference(), authConfig, responseBody, command.UserAgent()) - default: - return err - } - } - return signAndPublishToTarget(dockerCLI.Out(), imgRefAndAuth, notaryRepo, target) -} - -func signAndPublishToTarget(out io.Writer, imgRefAndAuth trust.ImageRefAndAuth, notaryRepo notaryclient.Repository, target notaryclient.Target) error { - tag := imgRefAndAuth.Tag() - _, _ = fmt.Fprintln(out, "Signing and pushing trust metadata for", imgRefAndAuth.Name()) - existingSigInfo, err := getExistingSignatureInfoForReleasedTag(notaryRepo, tag) - if err != nil { - return err - } - err = trust.AddToAllSignableRoles(notaryRepo, &target) - if err == nil { - prettyPrintExistingSignatureInfo(out, existingSigInfo) - err = notaryRepo.Publish() - } - if err != nil { - return errors.Wrapf(err, "failed to sign %s:%s", imgRefAndAuth.RepoInfo().Name.Name(), tag) - } - _, _ = fmt.Fprintf(out, "Successfully signed %s:%s\n", imgRefAndAuth.RepoInfo().Name.Name(), tag) - return nil -} - -func validateTag(imgRefAndAuth trust.ImageRefAndAuth) error { - tag := imgRefAndAuth.Tag() - if tag == "" { - if imgRefAndAuth.Digest() != "" { - return errors.New("cannot use a digest reference for IMAGE:TAG") - } - return fmt.Errorf("no tag specified for %s", imgRefAndAuth.Name()) - } - return nil -} - -func checkLocalImageExistence(ctx context.Context, apiClient client.APIClient, imageName string) error { - _, err := apiClient.ImageInspect(ctx, imageName) - return err -} - -func createTarget(notaryRepo notaryclient.Repository, tag string) (notaryclient.Target, error) { - target := ¬aryclient.Target{} - var err error - if tag == "" { - return *target, errors.New("no tag specified") - } - target.Name = tag - target.Hashes, target.Length, err = getSignedManifestHashAndSize(notaryRepo, tag) - return *target, err -} - -func getSignedManifestHashAndSize(notaryRepo notaryclient.Repository, tag string) (data.Hashes, int64, error) { - targets, err := notaryRepo.GetAllTargetMetadataByName(tag) - if err != nil { - return nil, 0, err - } - return getReleasedTargetHashAndSize(targets, tag) -} - -func getReleasedTargetHashAndSize(targets []notaryclient.TargetSignedStruct, tag string) (data.Hashes, int64, error) { - for _, tgt := range targets { - if isReleasedTarget(tgt.Role.Name) { - return tgt.Target.Hashes, tgt.Target.Length, nil - } - } - return nil, 0, notaryclient.ErrNoSuchTarget(tag) -} - -func getExistingSignatureInfoForReleasedTag(notaryRepo notaryclient.Repository, tag string) (trustTagRow, error) { - targets, err := notaryRepo.GetAllTargetMetadataByName(tag) - if err != nil { - return trustTagRow{}, err - } - releasedTargetInfoList := matchReleasedSignatures(targets) - if len(releasedTargetInfoList) == 0 { - return trustTagRow{}, nil - } - return releasedTargetInfoList[0], nil -} - -func prettyPrintExistingSignatureInfo(out io.Writer, existingSigInfo trustTagRow) { - sort.Strings(existingSigInfo.Signers) - joinedSigners := strings.Join(existingSigInfo.Signers, ", ") - _, _ = fmt.Fprintf(out, "Existing signatures for tag %s digest %s from:\n%s\n", existingSigInfo.SignedTag, existingSigInfo.Digest, joinedSigners) -} - -func initNotaryRepoWithSigners(notaryRepo notaryclient.Repository, newSigner data.RoleName) error { - rootKey, err := getOrGenerateNotaryKey(notaryRepo, data.CanonicalRootRole) - if err != nil { - return err - } - rootKeyID := rootKey.ID() - - // Initialize the notary repository with a remotely managed snapshot key - if err := notaryRepo.Initialize([]string{rootKeyID}, data.CanonicalSnapshotRole); err != nil { - return err - } - - signerKey, err := getOrGenerateNotaryKey(notaryRepo, newSigner) - if err != nil { - return err - } - if err := addStagedSigner(notaryRepo, newSigner, []data.PublicKey{signerKey}); err != nil { - return errors.Wrapf(err, "could not add signer to repo: %s", strings.TrimPrefix(newSigner.String(), "targets/")) - } - - return notaryRepo.Publish() -} - -// generates an ECDSA key without a GUN for the specified role -func getOrGenerateNotaryKey(notaryRepo notaryclient.Repository, role data.RoleName) (data.PublicKey, error) { - // use the signer name in the PEM headers if this is a delegation key - if data.IsDelegation(role) { - role = data.RoleName(notaryRoleToSigner(role)) - } - keys := notaryRepo.GetCryptoService().ListKeys(role) - var err error - var key data.PublicKey - // always select the first key by ID - if len(keys) > 0 { - sort.Strings(keys) - keyID := keys[0] - privKey, _, err := notaryRepo.GetCryptoService().GetPrivateKey(keyID) - if err != nil { - return nil, err - } - key = data.PublicKeyFromPrivate(privKey) - } else { - key, err = notaryRepo.GetCryptoService().Create(role, "", data.ECDSAKey) - if err != nil { - return nil, err - } - } - return key, nil -} - -// stages changes to add a signer with the specified name and key(s). Adds to targets/ and targets/releases -func addStagedSigner(notaryRepo notaryclient.Repository, newSigner data.RoleName, signerKeys []data.PublicKey) error { - // create targets/ - if err := notaryRepo.AddDelegationRoleAndKeys(newSigner, signerKeys); err != nil { - return err - } - if err := notaryRepo.AddDelegationPaths(newSigner, []string{""}); err != nil { - return err - } - - // create targets/releases - if err := notaryRepo.AddDelegationRoleAndKeys(trust.ReleasesRole, signerKeys); err != nil { - return err - } - return notaryRepo.AddDelegationPaths(trust.ReleasesRole, []string{""}) -} diff --git a/cli/command/trust/sign_test.go b/cli/command/trust/sign_test.go deleted file mode 100644 index e24ffbba73cb..000000000000 --- a/cli/command/trust/sign_test.go +++ /dev/null @@ -1,282 +0,0 @@ -package trust - -import ( - "bytes" - "encoding/json" - "io" - "runtime" - "testing" - - "github.com/docker/cli/cli/config" - "github.com/docker/cli/cli/trust" - "github.com/docker/cli/internal/test" - notaryfake "github.com/docker/cli/internal/test/notary" - "github.com/theupdateframework/notary" - "github.com/theupdateframework/notary/client" - "github.com/theupdateframework/notary/client/changelist" - "github.com/theupdateframework/notary/trustpinning" - "github.com/theupdateframework/notary/tuf/data" - "gotest.tools/v3/assert" - is "gotest.tools/v3/assert/cmp" - "gotest.tools/v3/skip" -) - -func TestTrustSignCommandErrors(t *testing.T) { - testCases := []struct { - name string - args []string - expectedError string - }{ - { - name: "not-enough-args", - expectedError: "requires 1 argument", - }, - { - name: "too-many-args", - args: []string{"image", "tag"}, - expectedError: "requires 1 argument", - }, - { - name: "sha-reference", - args: []string{"870d292919d01a0af7e7f056271dc78792c05f55f49b9b9012b6d89725bd9abd"}, - expectedError: "invalid repository name", - }, - { - name: "invalid-img-reference", - args: []string{"ALPINE:latest"}, - expectedError: "invalid reference format", - }, - { - name: "no-tag", - args: []string{"reg/img"}, - expectedError: "no tag specified for reg/img", - }, - { - name: "digest-reference", - args: []string{"ubuntu@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2"}, - expectedError: "cannot use a digest reference for IMAGE:TAG", - }, - } - // change to a tmpdir - config.SetDir(t.TempDir()) - for _, tc := range testCases { - cmd := newSignCommand( - test.NewFakeCli(&fakeClient{})) - cmd.SetArgs(tc.args) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.ErrorContains(t, cmd.Execute(), tc.expectedError) - } -} - -func TestTrustSignCommandOfflineErrors(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetOfflineNotaryRepository) - cmd := newSignCommand(cli) - cmd.SetArgs([]string{"reg-name.io/image:tag"}) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.ErrorContains(t, cmd.Execute(), "client is offline") -} - -func TestGetOrGenerateNotaryKey(t *testing.T) { - notaryRepo, err := client.NewFileCachedRepository(t.TempDir(), "gun", "https://localhost", nil, testPassRetriever, trustpinning.TrustPinConfig{}) - assert.NilError(t, err) - - // repo is empty, try making a root key - rootKeyA, err := getOrGenerateNotaryKey(notaryRepo, data.CanonicalRootRole) - assert.NilError(t, err) - assert.Check(t, rootKeyA != nil) - - // we should only have one newly generated key - allKeys := notaryRepo.GetCryptoService().ListAllKeys() - assert.Check(t, is.Len(allKeys, 1)) - assert.Check(t, notaryRepo.GetCryptoService().GetKey(rootKeyA.ID()) != nil) - - // this time we should get back the same key if we ask for another root key - rootKeyB, err := getOrGenerateNotaryKey(notaryRepo, data.CanonicalRootRole) - assert.NilError(t, err) - assert.Check(t, rootKeyB != nil) - - // we should only have one newly generated key - allKeys = notaryRepo.GetCryptoService().ListAllKeys() - assert.Check(t, is.Len(allKeys, 1)) - assert.Check(t, notaryRepo.GetCryptoService().GetKey(rootKeyB.ID()) != nil) - - // The key we retrieved should be identical to the one we generated - assert.Check(t, is.DeepEqual(rootKeyA.Public(), rootKeyB.Public())) - - // Now also try with a delegation key - releasesKey, err := getOrGenerateNotaryKey(notaryRepo, trust.ReleasesRole) - assert.NilError(t, err) - assert.Check(t, releasesKey != nil) - - // we should now have two keys - allKeys = notaryRepo.GetCryptoService().ListAllKeys() - assert.Check(t, is.Len(allKeys, 2)) - assert.Check(t, notaryRepo.GetCryptoService().GetKey(releasesKey.ID()) != nil) - // The key we retrieved should be identical to the one we generated - assert.Check(t, releasesKey != rootKeyA) - assert.Check(t, releasesKey != rootKeyB) -} - -func TestAddStageSigners(t *testing.T) { - skip.If(t, runtime.GOOS == "windows", "FIXME: not supported currently") - - notaryRepo, err := client.NewFileCachedRepository(t.TempDir(), "gun", "https://localhost", nil, testPassRetriever, trustpinning.TrustPinConfig{}) - assert.NilError(t, err) - - // stage targets/user - userRole := data.RoleName("targets/user") - userKey := data.NewPublicKey("algoA", []byte("a")) - err = addStagedSigner(notaryRepo, userRole, []data.PublicKey{userKey}) - assert.NilError(t, err) - // check the changelist for four total changes: two on targets/releases and two on targets/user - cl, err := notaryRepo.GetChangelist() - assert.NilError(t, err) - changeList := cl.List() - assert.Check(t, is.Len(changeList, 4)) - // ordering is deterministic: - - // first change is for targets/user key creation - newSignerKeyChange := changeList[0] - expectedJSON, err := json.Marshal(&changelist.TUFDelegation{ - NewThreshold: notary.MinThreshold, - AddKeys: data.KeyList([]data.PublicKey{userKey}), - }) - assert.NilError(t, err) - expectedChange := changelist.NewTUFChange( - changelist.ActionCreate, - userRole, - changelist.TypeTargetsDelegation, - "", // no path for delegations - expectedJSON, - ) - assert.Check(t, is.DeepEqual(expectedChange, newSignerKeyChange)) - - // second change is for targets/user getting all paths - newSignerPathsChange := changeList[1] - expectedJSON, err = json.Marshal(&changelist.TUFDelegation{ - AddPaths: []string{""}, - }) - assert.NilError(t, err) - expectedChange = changelist.NewTUFChange( - changelist.ActionCreate, - userRole, - changelist.TypeTargetsDelegation, - "", // no path for delegations - expectedJSON, - ) - assert.Check(t, is.DeepEqual(expectedChange, newSignerPathsChange)) - - releasesRole := data.RoleName("targets/releases") - - // third change is for targets/releases key creation - releasesKeyChange := changeList[2] - expectedJSON, err = json.Marshal(&changelist.TUFDelegation{ - NewThreshold: notary.MinThreshold, - AddKeys: data.KeyList([]data.PublicKey{userKey}), - }) - assert.NilError(t, err) - expectedChange = changelist.NewTUFChange( - changelist.ActionCreate, - releasesRole, - changelist.TypeTargetsDelegation, - "", // no path for delegations - expectedJSON, - ) - assert.Check(t, is.DeepEqual(expectedChange, releasesKeyChange)) - - // fourth change is for targets/releases getting all paths - releasesPathsChange := changeList[3] - expectedJSON, err = json.Marshal(&changelist.TUFDelegation{ - AddPaths: []string{""}, - }) - assert.NilError(t, err) - expectedChange = changelist.NewTUFChange( - changelist.ActionCreate, - releasesRole, - changelist.TypeTargetsDelegation, - "", // no path for delegations - expectedJSON, - ) - assert.Check(t, is.DeepEqual(expectedChange, releasesPathsChange)) -} - -func TestGetSignedManifestHashAndSize(t *testing.T) { - notaryRepo, err := client.NewFileCachedRepository(t.TempDir(), "gun", "https://localhost", nil, testPassRetriever, trustpinning.TrustPinConfig{}) - assert.NilError(t, err) - _, _, err = getSignedManifestHashAndSize(notaryRepo, "test") - assert.Error(t, err, "client is offline") -} - -func TestGetReleasedTargetHashAndSize(t *testing.T) { - oneReleasedTgt := []client.TargetSignedStruct{} - // make and append 3 non-released signatures on the "unreleased" target - unreleasedTgt := client.Target{Name: "unreleased", Hashes: data.Hashes{notary.SHA256: []byte("hash")}} - for _, unreleasedRole := range []string{"targets/a", "targets/b", "targets/c"} { - oneReleasedTgt = append(oneReleasedTgt, client.TargetSignedStruct{Role: mockDelegationRoleWithName(unreleasedRole), Target: unreleasedTgt}) - } - _, _, err := getReleasedTargetHashAndSize(oneReleasedTgt, "unreleased") - assert.Error(t, err, "No valid trust data for unreleased") - releasedTgt := client.Target{Name: "released", Hashes: data.Hashes{notary.SHA256: []byte("released-hash")}} - oneReleasedTgt = append(oneReleasedTgt, client.TargetSignedStruct{Role: mockDelegationRoleWithName("targets/releases"), Target: releasedTgt}) - hash, _, _ := getReleasedTargetHashAndSize(oneReleasedTgt, "unreleased") - assert.Check(t, is.DeepEqual(data.Hashes{notary.SHA256: []byte("released-hash")}, hash)) -} - -func TestCreateTarget(t *testing.T) { - notaryRepo, err := client.NewFileCachedRepository(t.TempDir(), "gun", "https://localhost", nil, testPassRetriever, trustpinning.TrustPinConfig{}) - assert.NilError(t, err) - _, err = createTarget(notaryRepo, "") - assert.Error(t, err, "no tag specified") - _, err = createTarget(notaryRepo, "1") - assert.Error(t, err, "client is offline") -} - -func TestGetExistingSignatureInfoForReleasedTag(t *testing.T) { - notaryRepo, err := client.NewFileCachedRepository(t.TempDir(), "gun", "https://localhost", nil, testPassRetriever, trustpinning.TrustPinConfig{}) - assert.NilError(t, err) - _, err = getExistingSignatureInfoForReleasedTag(notaryRepo, "test") - assert.Error(t, err, "client is offline") -} - -func TestPrettyPrintExistingSignatureInfo(t *testing.T) { - buf := bytes.NewBuffer(nil) - signers := []string{"Bob", "Alice", "Carol"} - existingSig := trustTagRow{trustTagKey{"tagName", "abc123"}, signers} - prettyPrintExistingSignatureInfo(buf, existingSig) - - assert.Check(t, is.Contains(buf.String(), "Existing signatures for tag tagName digest abc123 from:\nAlice, Bob, Carol")) -} - -func TestSignCommandChangeListIsCleanedOnError(t *testing.T) { - tmpDir := t.TempDir() - - config.SetDir(tmpDir) - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetLoadedNotaryRepository) - cmd := newSignCommand(cli) - cmd.SetArgs([]string{"ubuntu:latest"}) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - - err := cmd.Execute() - assert.Assert(t, err != nil) - - notaryRepo, err := client.NewFileCachedRepository(tmpDir, "docker.io/library/ubuntu", "https://localhost", nil, testPassRetriever, trustpinning.TrustPinConfig{}) - assert.NilError(t, err) - cl, err := notaryRepo.GetChangelist() - assert.NilError(t, err) - assert.Check(t, is.Equal(len(cl.List()), 0)) -} - -func TestSignCommandLocalFlag(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetEmptyTargetsNotaryRepository) - cmd := newSignCommand(cli) - cmd.SetArgs([]string{"--local", "reg-name.io/image:red"}) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.ErrorContains(t, cmd.Execute(), "error contacting notary server: dial tcp: lookup reg-name.io") -} diff --git a/cli/command/trust/signer.go b/cli/command/trust/signer.go deleted file mode 100644 index 807ad6c955dd..000000000000 --- a/cli/command/trust/signer.go +++ /dev/null @@ -1,22 +0,0 @@ -package trust - -import ( - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" - "github.com/spf13/cobra" -) - -// newTrustSignerCommand returns a cobra command for `trust signer` subcommands -func newTrustSignerCommand(dockerCli command.Cli) *cobra.Command { - cmd := &cobra.Command{ - Use: "signer", - Short: "Manage entities who can sign Docker images", - Args: cli.NoArgs, - RunE: command.ShowHelp(dockerCli.Err()), - } - cmd.AddCommand( - newSignerAddCommand(dockerCli), - newSignerRemoveCommand(dockerCli), - ) - return cmd -} diff --git a/cli/command/trust/signer_add.go b/cli/command/trust/signer_add.go deleted file mode 100644 index 155347074190..000000000000 --- a/cli/command/trust/signer_add.go +++ /dev/null @@ -1,139 +0,0 @@ -package trust - -import ( - "context" - "fmt" - "io" - "os" - "path" - "strings" - - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/command/image" - "github.com/docker/cli/cli/trust" - "github.com/docker/cli/internal/lazyregexp" - "github.com/docker/cli/opts" - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/theupdateframework/notary/client" - "github.com/theupdateframework/notary/tuf/data" - tufutils "github.com/theupdateframework/notary/tuf/utils" -) - -type signerAddOptions struct { - keys opts.ListOpts - signer string - repos []string -} - -func newSignerAddCommand(dockerCLI command.Cli) *cobra.Command { - var options signerAddOptions - cmd := &cobra.Command{ - Use: "add OPTIONS NAME REPOSITORY [REPOSITORY...] ", - Short: "Add a signer", - Args: cli.RequiresMinArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - options.signer = args[0] - options.repos = args[1:] - return addSigner(cmd.Context(), dockerCLI, options) - }, - } - flags := cmd.Flags() - options.keys = opts.NewListOpts(nil) - flags.Var(&options.keys, "key", "Path to the signer's public key file") - return cmd -} - -var validSignerName = lazyregexp.New(`^[a-z0-9][a-z0-9\_\-]*$`).MatchString - -func addSigner(ctx context.Context, dockerCLI command.Cli, options signerAddOptions) error { - signerName := options.signer - if !validSignerName(signerName) { - return fmt.Errorf("signer name \"%s\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character", signerName) - } - if signerName == "releases" { - return errors.New("releases is a reserved keyword, use a different signer name") - } - - if options.keys.Len() == 0 { - return errors.New("path to a public key must be provided using the `--key` flag") - } - signerPubKeys, err := ingestPublicKeys(options.keys.GetSlice()) - if err != nil { - return err - } - var errRepos []string - for _, repoName := range options.repos { - _, _ = fmt.Fprintf(dockerCLI.Out(), "Adding signer \"%s\" to %s...\n", signerName, repoName) - if err := addSignerToRepo(ctx, dockerCLI, signerName, repoName, signerPubKeys); err != nil { - _, _ = fmt.Fprintln(dockerCLI.Err(), err.Error()+"\n") - errRepos = append(errRepos, repoName) - } else { - _, _ = fmt.Fprintf(dockerCLI.Out(), "Successfully added signer: %s to %s\n\n", signerName, repoName) - } - } - if len(errRepos) > 0 { - return fmt.Errorf("failed to add signer to: %s", strings.Join(errRepos, ", ")) - } - return nil -} - -func addSignerToRepo(ctx context.Context, dockerCLI command.Cli, signerName string, repoName string, signerPubKeys []data.PublicKey) error { - imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, image.AuthResolver(dockerCLI), repoName) - if err != nil { - return err - } - - notaryRepo, err := newNotaryClient(dockerCLI, imgRefAndAuth, trust.ActionsPushAndPull) - if err != nil { - return trust.NotaryError(imgRefAndAuth.Reference().Name(), err) - } - - if _, err = notaryRepo.ListTargets(); err != nil { - switch err.(type) { - case client.ErrRepoNotInitialized, client.ErrRepositoryNotExist: - _, _ = fmt.Fprintf(dockerCLI.Out(), "Initializing signed repository for %s...\n", repoName) - if err := getOrGenerateRootKeyAndInitRepo(notaryRepo); err != nil { - return trust.NotaryError(repoName, err) - } - _, _ = fmt.Fprintf(dockerCLI.Out(), "Successfully initialized %q\n", repoName) - default: - return trust.NotaryError(repoName, err) - } - } - - newSignerRoleName := data.RoleName(path.Join(data.CanonicalTargetsRole.String(), signerName)) - - if err := addStagedSigner(notaryRepo, newSignerRoleName, signerPubKeys); err != nil { - return errors.Wrapf(err, "could not add signer to repo: %s", strings.TrimPrefix(newSignerRoleName.String(), "targets/")) - } - - return notaryRepo.Publish() -} - -func ingestPublicKeys(pubKeyPaths []string) ([]data.PublicKey, error) { - pubKeys := []data.PublicKey{} - for _, pubKeyPath := range pubKeyPaths { - // Read public key bytes from PEM file, limit to 1 KiB - pubKeyFile, err := os.OpenFile(pubKeyPath, os.O_RDONLY, 0o666) - if err != nil { - return nil, errors.Wrap(err, "unable to read public key from file") - } - defer pubKeyFile.Close() - // limit to - l := io.LimitReader(pubKeyFile, 1<<20) - pubKeyBytes, err := io.ReadAll(l) - if err != nil { - return nil, errors.Wrap(err, "unable to read public key from file") - } - - // Parse PEM bytes into type PublicKey - pubKey, err := tufutils.ParsePEMPublicKey(pubKeyBytes) - if err != nil { - return nil, errors.Wrapf(err, "could not parse public key from file: %s", pubKeyPath) - } - pubKeys = append(pubKeys, pubKey) - } - return pubKeys, nil -} diff --git a/cli/command/trust/signer_add_test.go b/cli/command/trust/signer_add_test.go deleted file mode 100644 index 31fd98361176..000000000000 --- a/cli/command/trust/signer_add_test.go +++ /dev/null @@ -1,139 +0,0 @@ -package trust - -import ( - "fmt" - "io" - "os" - "path/filepath" - "runtime" - "testing" - - "github.com/docker/cli/cli/config" - "github.com/docker/cli/internal/test" - notaryfake "github.com/docker/cli/internal/test/notary" - "github.com/theupdateframework/notary" - "gotest.tools/v3/assert" - is "gotest.tools/v3/assert/cmp" -) - -func TestTrustSignerAddErrors(t *testing.T) { - testCases := []struct { - name string - args []string - expectedError string - }{ - { - name: "not-enough-args", - expectedError: "requires at least 2 argument", - }, - { - name: "no-key", - args: []string{"foo", "bar"}, - expectedError: "path to a public key must be provided using the `--key` flag", - }, - { - name: "reserved-releases-signer-add", - args: []string{"releases", "my-image", "--key", "/path/to/key"}, - expectedError: "releases is a reserved keyword, use a different signer name", - }, - { - name: "disallowed-chars", - args: []string{"ali/ce", "my-image", "--key", "/path/to/key"}, - expectedError: "signer name \"ali/ce\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character", - }, - { - name: "no-upper-case", - args: []string{"Alice", "my-image", "--key", "/path/to/key"}, - expectedError: "signer name \"Alice\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character", - }, - { - name: "start-with-letter", - args: []string{"_alice", "my-image", "--key", "/path/to/key"}, - expectedError: "signer name \"_alice\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character", - }, - } - config.SetDir(t.TempDir()) - - for _, tc := range testCases { - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetOfflineNotaryRepository) - cmd := newSignerAddCommand(cli) - cmd.SetArgs(tc.args) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.ErrorContains(t, cmd.Execute(), tc.expectedError) - } -} - -func TestSignerAddCommandNoTargetsKey(t *testing.T) { - config.SetDir(t.TempDir()) - - tmpDir := t.TempDir() - tmpFile, err := os.CreateTemp(tmpDir, "pemfile") - assert.NilError(t, err) - assert.Check(t, tmpFile.Close()) - - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetEmptyTargetsNotaryRepository) - cmd := newSignerAddCommand(cli) - cmd.SetArgs([]string{"--key", tmpFile.Name(), "alice", "alpine", "linuxkit/alpine"}) - - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.Error(t, cmd.Execute(), fmt.Sprintf("could not parse public key from file: %s: no valid public key found", tmpFile.Name())) -} - -func TestSignerAddCommandBadKeyPath(t *testing.T) { - config.SetDir(t.TempDir()) - - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetEmptyTargetsNotaryRepository) - cmd := newSignerAddCommand(cli) - cmd.SetArgs([]string{"--key", "/path/to/key.pem", "alice", "alpine"}) - - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - expectedError := "unable to read public key from file: open /path/to/key.pem: no such file or directory" - if runtime.GOOS == "windows" { - expectedError = "unable to read public key from file: open /path/to/key.pem: The system cannot find the path specified." - } - assert.Error(t, cmd.Execute(), expectedError) -} - -func TestSignerAddCommandInvalidRepoName(t *testing.T) { - config.SetDir(t.TempDir()) - - pubKeyDir := t.TempDir() - pubKeyFilepath := filepath.Join(pubKeyDir, "pubkey.pem") - assert.NilError(t, os.WriteFile(pubKeyFilepath, pubKeyFixture, notary.PrivNoExecPerms)) - - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetUninitializedNotaryRepository) - cmd := newSignerAddCommand(cli) - imageName := "870d292919d01a0af7e7f056271dc78792c05f55f49b9b9012b6d89725bd9abd" - cmd.SetArgs([]string{"--key", pubKeyFilepath, "alice", imageName}) - - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.Error(t, cmd.Execute(), "failed to add signer to: 870d292919d01a0af7e7f056271dc78792c05f55f49b9b9012b6d89725bd9abd") - expectedErr := fmt.Sprintf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings\n\n", imageName) - - assert.Check(t, is.Equal(expectedErr, cli.ErrBuffer().String())) -} - -func TestIngestPublicKeys(t *testing.T) { - // Call with a bad path - _, err := ingestPublicKeys([]string{"foo", "bar"}) - expectedError := "unable to read public key from file: open foo: no such file or directory" - if runtime.GOOS == "windows" { - expectedError = "unable to read public key from file: open foo: The system cannot find the file specified." - } - assert.Error(t, err, expectedError) - // Call with real file path - tmpDir := t.TempDir() - tmpFile, err := os.CreateTemp(tmpDir, "pemfile") - assert.NilError(t, err) - assert.Check(t, tmpFile.Close()) - _, err = ingestPublicKeys([]string{tmpFile.Name()}) - assert.Error(t, err, fmt.Sprintf("could not parse public key from file: %s: no valid public key found", tmpFile.Name())) -} diff --git a/cli/command/trust/signer_remove.go b/cli/command/trust/signer_remove.go deleted file mode 100644 index 10d2c2933bad..000000000000 --- a/cli/command/trust/signer_remove.go +++ /dev/null @@ -1,153 +0,0 @@ -package trust - -import ( - "context" - "fmt" - "strings" - - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/command/image" - "github.com/docker/cli/cli/trust" - "github.com/docker/cli/internal/prompt" - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/theupdateframework/notary/client" - "github.com/theupdateframework/notary/tuf/data" -) - -type signerRemoveOptions struct { - signer string - repos []string - forceYes bool -} - -func newSignerRemoveCommand(dockerCli command.Cli) *cobra.Command { - options := signerRemoveOptions{} - cmd := &cobra.Command{ - Use: "remove [OPTIONS] NAME REPOSITORY [REPOSITORY...]", - Short: "Remove a signer", - Args: cli.RequiresMinArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - options.signer = args[0] - options.repos = args[1:] - return removeSigner(cmd.Context(), dockerCli, options) - }, - } - flags := cmd.Flags() - flags.BoolVarP(&options.forceYes, "force", "f", false, "Do not prompt for confirmation before removing the most recent signer") - return cmd -} - -func removeSigner(ctx context.Context, dockerCLI command.Cli, options signerRemoveOptions) error { - var errRepos []string - for _, repo := range options.repos { - _, _ = fmt.Fprintf(dockerCLI.Out(), "Removing signer \"%s\" from %s...\n", options.signer, repo) - if _, err := removeSingleSigner(ctx, dockerCLI, repo, options.signer, options.forceYes); err != nil { - _, _ = fmt.Fprintln(dockerCLI.Err(), err.Error()+"\n") - errRepos = append(errRepos, repo) - } - } - if len(errRepos) > 0 { - return errors.Errorf("error removing signer from: %s", strings.Join(errRepos, ", ")) - } - return nil -} - -func isLastSignerForReleases(roleWithSig data.Role, allRoles []client.RoleWithSignatures) (bool, error) { - var releasesRoleWithSigs client.RoleWithSignatures - for _, role := range allRoles { - if role.Name == releasesRoleTUFName { - releasesRoleWithSigs = role - break - } - } - counter := len(releasesRoleWithSigs.Signatures) - if counter == 0 { - return false, errors.New("all signed tags are currently revoked, use docker trust sign to fix") - } - for _, signature := range releasesRoleWithSigs.Signatures { - for _, key := range roleWithSig.KeyIDs { - if signature.KeyID == key { - counter-- - } - } - } - return counter < releasesRoleWithSigs.Threshold, nil -} - -func maybePromptForSignerRemoval(ctx context.Context, dockerCLI command.Cli, repoName, signerName string, isLastSigner, forceYes bool) (bool, error) { - if isLastSigner && !forceYes { - message := fmt.Sprintf("The signer \"%s\" signed the last released version of %s. "+ - "Removing this signer will make %s unpullable. "+ - "Are you sure you want to continue?", - signerName, repoName, repoName, - ) - return prompt.Confirm(ctx, dockerCLI.In(), dockerCLI.Out(), message) - } - return false, nil -} - -// removeSingleSigner attempts to remove a single signer and returns whether signer removal happened. -// The signer not being removed doesn't necessarily raise an error e.g. user choosing "No" when prompted for confirmation. -func removeSingleSigner(ctx context.Context, dockerCLI command.Cli, repoName, signerName string, forceYes bool) (bool, error) { - imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, image.AuthResolver(dockerCLI), repoName) - if err != nil { - return false, err - } - - signerDelegation := data.RoleName("targets/" + signerName) - if signerDelegation == releasesRoleTUFName { - return false, errors.Errorf("releases is a reserved keyword and cannot be removed") - } - notaryRepo, err := newNotaryClient(dockerCLI, imgRefAndAuth, trust.ActionsPushAndPull) - if err != nil { - return false, trust.NotaryError(imgRefAndAuth.Reference().Name(), err) - } - delegationRoles, err := notaryRepo.GetDelegationRoles() - if err != nil { - return false, errors.Wrapf(err, "error retrieving signers for %s", repoName) - } - var role data.Role - for _, delRole := range delegationRoles { - if delRole.Name == signerDelegation { - role = delRole - break - } - } - if role.Name == "" { - return false, errors.Errorf("no signer %s for repository %s", signerName, repoName) - } - allRoles, err := notaryRepo.ListRoles() - if err != nil { - return false, err - } - - isLastSigner, err := isLastSignerForReleases(role, allRoles) - if err != nil { - return false, err - } - - ok, err := maybePromptForSignerRemoval(ctx, dockerCLI, repoName, signerName, isLastSigner, forceYes) - if err != nil { - return false, err - } - if !ok { - return false, nil - } - - if err := notaryRepo.RemoveDelegationKeys(releasesRoleTUFName, role.KeyIDs); err != nil { - return false, err - } - if err := notaryRepo.RemoveDelegationRole(signerDelegation); err != nil { - return false, err - } - - if err := notaryRepo.Publish(); err != nil { - return false, err - } - - _, _ = fmt.Fprintf(dockerCLI.Out(), "Successfully removed %s from %s\n\n", signerName, repoName) - - return true, nil -} diff --git a/cli/command/trust/signer_remove_test.go b/cli/command/trust/signer_remove_test.go deleted file mode 100644 index 402ddda991ca..000000000000 --- a/cli/command/trust/signer_remove_test.go +++ /dev/null @@ -1,146 +0,0 @@ -package trust - -import ( - "context" - "io" - "testing" - - "github.com/docker/cli/internal/test" - notaryfake "github.com/docker/cli/internal/test/notary" - "github.com/theupdateframework/notary/client" - "github.com/theupdateframework/notary/tuf/data" - "gotest.tools/v3/assert" - is "gotest.tools/v3/assert/cmp" -) - -func TestTrustSignerRemoveErrors(t *testing.T) { - testCases := []struct { - name string - args []string - expectedError string - }{ - { - name: "not-enough-args-0", - expectedError: "requires at least 2 arguments", - }, - { - name: "not-enough-args-1", - args: []string{"user"}, - expectedError: "requires at least 2 arguments", - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - cmd := newSignerRemoveCommand( - test.NewFakeCli(&fakeClient{})) - cmd.SetArgs(tc.args) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - assert.ErrorContains(t, cmd.Execute(), tc.expectedError) - }) - } - testCasesWithOutput := []struct { - name string - args []string - expectedError string - expectedErrOut string - }{ - { - name: "not-an-image", - args: []string{"user", "notanimage"}, - expectedError: "error removing signer from: notanimage", - expectedErrOut: "error retrieving signers for notanimage", - }, - { - name: "sha-reference", - args: []string{"user", "870d292919d01a0af7e7f056271dc78792c05f55f49b9b9012b6d89725bd9abd"}, - expectedError: "error removing signer from: 870d292919d01a0af7e7f056271dc78792c05f55f49b9b9012b6d89725bd9abd", - expectedErrOut: "invalid repository name", - }, - { - name: "invalid-img-reference", - args: []string{"user", "ALPINE"}, - expectedError: "error removing signer from: ALPINE", - expectedErrOut: "invalid reference format", - }, - } - for _, tc := range testCasesWithOutput { - t.Run(tc.name, func(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetOfflineNotaryRepository) - cmd := newSignerRemoveCommand(cli) - cmd.SetArgs(tc.args) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - err := cmd.Execute() - assert.Check(t, is.Error(err, tc.expectedError)) - assert.Check(t, is.Contains(cli.ErrBuffer().String(), tc.expectedErrOut)) - }) - } -} - -func TestRemoveSingleSigner(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetLoadedNotaryRepository) - ctx := context.Background() - removed, err := removeSingleSigner(ctx, cli, "signed-repo", "test", true) - assert.Error(t, err, "no signer test for repository signed-repo") - assert.Equal(t, removed, false, "No signer should be removed") - - removed, err = removeSingleSigner(ctx, cli, "signed-repo", "releases", true) - assert.Error(t, err, "releases is a reserved keyword and cannot be removed") - assert.Equal(t, removed, false, "No signer should be removed") -} - -func TestRemoveMultipleSigners(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(notaryfake.GetLoadedNotaryRepository) - ctx := context.Background() - err := removeSigner(ctx, cli, signerRemoveOptions{signer: "test", repos: []string{"signed-repo", "signed-repo"}, forceYes: true}) - assert.Error(t, err, "error removing signer from: signed-repo, signed-repo") - assert.Check(t, is.Contains(cli.ErrBuffer().String(), - "no signer test for repository signed-repo")) - assert.Check(t, is.Contains(cli.OutBuffer().String(), "Removing signer \"test\" from signed-repo...\n")) -} - -func TestRemoveLastSignerWarning(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - ctx := context.Background() - cli.SetNotaryClient(notaryfake.GetLoadedNotaryRepository) - - err := removeSigner(ctx, cli, signerRemoveOptions{signer: "alice", repos: []string{"signed-repo"}, forceYes: false}) - assert.NilError(t, err) - assert.Check(t, is.Contains(cli.OutBuffer().String(), - "The signer \"alice\" signed the last released version of signed-repo. "+ - "Removing this signer will make signed-repo unpullable. "+ - "Are you sure you want to continue? [y/N]")) -} - -func TestIsLastSignerForReleases(t *testing.T) { - role := data.Role{} - releaserole := client.RoleWithSignatures{} - releaserole.Name = releasesRoleTUFName - releaserole.Threshold = 1 - allrole := []client.RoleWithSignatures{releaserole} - lastsigner, err := isLastSignerForReleases(role, allrole) - assert.Error(t, err, "all signed tags are currently revoked, use docker trust sign to fix") - assert.Check(t, is.Equal(false, lastsigner)) - - role.KeyIDs = []string{"deadbeef"} - sig := data.Signature{} - sig.KeyID = "deadbeef" - releaserole.Signatures = []data.Signature{sig} - releaserole.Threshold = 1 - allrole = []client.RoleWithSignatures{releaserole} - lastsigner, err = isLastSignerForReleases(role, allrole) - assert.NilError(t, err) - assert.Check(t, is.Equal(true, lastsigner)) - - sig.KeyID = "8badf00d" - releaserole.Signatures = []data.Signature{sig} - releaserole.Threshold = 1 - allrole = []client.RoleWithSignatures{releaserole} - lastsigner, err = isLastSignerForReleases(role, allrole) - assert.NilError(t, err) - assert.Check(t, is.Equal(false, lastsigner)) -} diff --git a/cli/command/trust/testdata/trust-inspect-empty-repo.golden b/cli/command/trust/testdata/trust-inspect-empty-repo.golden deleted file mode 100644 index 3aba7f3b2dbe..000000000000 --- a/cli/command/trust/testdata/trust-inspect-empty-repo.golden +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "Name": "reg/img:unsigned-tag", - "SignedTags": [], - "Signers": [], - "AdministrativeKeys": [ - { - "Name": "Root", - "Keys": [ - { - "ID": "rootID" - } - ] - }, - { - "Name": "Repository", - "Keys": [ - { - "ID": "targetsID" - } - ] - } - ] - } -] diff --git a/cli/command/trust/testdata/trust-inspect-full-repo-no-signers.golden b/cli/command/trust/testdata/trust-inspect-full-repo-no-signers.golden deleted file mode 100644 index 23158f75bc39..000000000000 --- a/cli/command/trust/testdata/trust-inspect-full-repo-no-signers.golden +++ /dev/null @@ -1,33 +0,0 @@ -[ - { - "Name": "signed-repo", - "SignedTags": [ - { - "SignedTag": "green", - "Digest": "677265656e2d646967657374", - "Signers": [ - "Repo Admin" - ] - } - ], - "Signers": [], - "AdministrativeKeys": [ - { - "Name": "Root", - "Keys": [ - { - "ID": "rootID" - } - ] - }, - { - "Name": "Repository", - "Keys": [ - { - "ID": "targetsID" - } - ] - } - ] - } -] diff --git a/cli/command/trust/testdata/trust-inspect-full-repo-with-signers.golden b/cli/command/trust/testdata/trust-inspect-full-repo-with-signers.golden deleted file mode 100644 index 4901a7cca546..000000000000 --- a/cli/command/trust/testdata/trust-inspect-full-repo-with-signers.golden +++ /dev/null @@ -1,65 +0,0 @@ -[ - { - "Name": "signed-repo", - "SignedTags": [ - { - "SignedTag": "blue", - "Digest": "626c75652d646967657374", - "Signers": [ - "alice" - ] - }, - { - "SignedTag": "green", - "Digest": "677265656e2d646967657374", - "Signers": [ - "Repo Admin" - ] - }, - { - "SignedTag": "red", - "Digest": "7265642d646967657374", - "Signers": [ - "alice", - "bob" - ] - } - ], - "Signers": [ - { - "Name": "bob", - "Keys": [ - { - "ID": "B" - } - ] - }, - { - "Name": "alice", - "Keys": [ - { - "ID": "A" - } - ] - } - ], - "AdministrativeKeys": [ - { - "Name": "Root", - "Keys": [ - { - "ID": "rootID" - } - ] - }, - { - "Name": "Repository", - "Keys": [ - { - "ID": "targetsID" - } - ] - } - ] - } -] diff --git a/cli/command/trust/testdata/trust-inspect-multiple-repos-with-signers.golden b/cli/command/trust/testdata/trust-inspect-multiple-repos-with-signers.golden deleted file mode 100644 index 1958bb7eb466..000000000000 --- a/cli/command/trust/testdata/trust-inspect-multiple-repos-with-signers.golden +++ /dev/null @@ -1,128 +0,0 @@ -[ - { - "Name": "signed-repo", - "SignedTags": [ - { - "SignedTag": "blue", - "Digest": "626c75652d646967657374", - "Signers": [ - "alice" - ] - }, - { - "SignedTag": "green", - "Digest": "677265656e2d646967657374", - "Signers": [ - "Repo Admin" - ] - }, - { - "SignedTag": "red", - "Digest": "7265642d646967657374", - "Signers": [ - "alice", - "bob" - ] - } - ], - "Signers": [ - { - "Name": "bob", - "Keys": [ - { - "ID": "B" - } - ] - }, - { - "Name": "alice", - "Keys": [ - { - "ID": "A" - } - ] - } - ], - "AdministrativeKeys": [ - { - "Name": "Root", - "Keys": [ - { - "ID": "rootID" - } - ] - }, - { - "Name": "Repository", - "Keys": [ - { - "ID": "targetsID" - } - ] - } - ] - }, - { - "Name": "signed-repo", - "SignedTags": [ - { - "SignedTag": "blue", - "Digest": "626c75652d646967657374", - "Signers": [ - "alice" - ] - }, - { - "SignedTag": "green", - "Digest": "677265656e2d646967657374", - "Signers": [ - "Repo Admin" - ] - }, - { - "SignedTag": "red", - "Digest": "7265642d646967657374", - "Signers": [ - "alice", - "bob" - ] - } - ], - "Signers": [ - { - "Name": "bob", - "Keys": [ - { - "ID": "B" - } - ] - }, - { - "Name": "alice", - "Keys": [ - { - "ID": "A" - } - ] - } - ], - "AdministrativeKeys": [ - { - "Name": "Root", - "Keys": [ - { - "ID": "rootID" - } - ] - }, - { - "Name": "Repository", - "Keys": [ - { - "ID": "targetsID" - } - ] - } - ] - } -] diff --git a/cli/command/trust/testdata/trust-inspect-one-tag-no-signers.golden b/cli/command/trust/testdata/trust-inspect-one-tag-no-signers.golden deleted file mode 100644 index 0fcefebbc9f7..000000000000 --- a/cli/command/trust/testdata/trust-inspect-one-tag-no-signers.golden +++ /dev/null @@ -1,33 +0,0 @@ -[ - { - "Name": "signed-repo:green", - "SignedTags": [ - { - "SignedTag": "green", - "Digest": "677265656e2d646967657374", - "Signers": [ - "Repo Admin" - ] - } - ], - "Signers": [], - "AdministrativeKeys": [ - { - "Name": "Root", - "Keys": [ - { - "ID": "rootID" - } - ] - }, - { - "Name": "Repository", - "Keys": [ - { - "ID": "targetsID" - } - ] - } - ] - } -] diff --git a/cli/command/trust/testdata/trust-inspect-pretty-full-repo-no-signers.golden b/cli/command/trust/testdata/trust-inspect-pretty-full-repo-no-signers.golden deleted file mode 100644 index 920ba9240bea..000000000000 --- a/cli/command/trust/testdata/trust-inspect-pretty-full-repo-no-signers.golden +++ /dev/null @@ -1,10 +0,0 @@ - -Signatures for signed-repo - -SIGNED TAG DIGEST SIGNERS -green 677265656e2d646967657374 (Repo Admin) - -Administrative keys for signed-repo - - Repository Key: targetsID - Root Key: rootID diff --git a/cli/command/trust/testdata/trust-inspect-pretty-full-repo-with-signers.golden b/cli/command/trust/testdata/trust-inspect-pretty-full-repo-with-signers.golden deleted file mode 100644 index f6e5b9794ef4..000000000000 --- a/cli/command/trust/testdata/trust-inspect-pretty-full-repo-with-signers.golden +++ /dev/null @@ -1,18 +0,0 @@ - -Signatures for signed-repo - -SIGNED TAG DIGEST SIGNERS -blue 626c75652d646967657374 alice -green 677265656e2d646967657374 (Repo Admin) -red 7265642d646967657374 alice, bob - -List of signers and their keys for signed-repo - -SIGNER KEYS -alice A -bob B - -Administrative keys for signed-repo - - Repository Key: targetsID - Root Key: rootID diff --git a/cli/command/trust/testdata/trust-inspect-pretty-one-tag-no-signers.golden b/cli/command/trust/testdata/trust-inspect-pretty-one-tag-no-signers.golden deleted file mode 100644 index b4fd4d37e641..000000000000 --- a/cli/command/trust/testdata/trust-inspect-pretty-one-tag-no-signers.golden +++ /dev/null @@ -1,10 +0,0 @@ - -Signatures for signed-repo:green - -SIGNED TAG DIGEST SIGNERS -green 677265656e2d646967657374 (Repo Admin) - -Administrative keys for signed-repo:green - - Repository Key: targetsID - Root Key: rootID diff --git a/cli/command/trust/testdata/trust-inspect-pretty-unsigned-tag-with-signers.golden b/cli/command/trust/testdata/trust-inspect-pretty-unsigned-tag-with-signers.golden deleted file mode 100644 index ba8f8c67af93..000000000000 --- a/cli/command/trust/testdata/trust-inspect-pretty-unsigned-tag-with-signers.golden +++ /dev/null @@ -1,14 +0,0 @@ - -No signatures for signed-repo:unsigned - - -List of signers and their keys for signed-repo:unsigned - -SIGNER KEYS -alice A -bob B - -Administrative keys for signed-repo:unsigned - - Repository Key: targetsID - Root Key: rootID diff --git a/cli/command/trust/testdata/trust-inspect-uninitialized.golden b/cli/command/trust/testdata/trust-inspect-uninitialized.golden deleted file mode 100644 index fe51488c7066..000000000000 --- a/cli/command/trust/testdata/trust-inspect-uninitialized.golden +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/cli/command/trust/testdata/trust-inspect-unsigned-tag-with-signers.golden b/cli/command/trust/testdata/trust-inspect-unsigned-tag-with-signers.golden deleted file mode 100644 index 82a3282fe608..000000000000 --- a/cli/command/trust/testdata/trust-inspect-unsigned-tag-with-signers.golden +++ /dev/null @@ -1,42 +0,0 @@ -[ - { - "Name": "signed-repo:unsigned", - "SignedTags": [], - "Signers": [ - { - "Name": "bob", - "Keys": [ - { - "ID": "B" - } - ] - }, - { - "Name": "alice", - "Keys": [ - { - "ID": "A" - } - ] - } - ], - "AdministrativeKeys": [ - { - "Name": "Root", - "Keys": [ - { - "ID": "rootID" - } - ] - }, - { - "Name": "Repository", - "Keys": [ - { - "ID": "targetsID" - } - ] - } - ] - } -] diff --git a/cli/command/trust/testdata/trust-revoke-prompt-termination.golden b/cli/command/trust/testdata/trust-revoke-prompt-termination.golden deleted file mode 100644 index a7e2aecd4db7..000000000000 --- a/cli/command/trust/testdata/trust-revoke-prompt-termination.golden +++ /dev/null @@ -1 +0,0 @@ -Confirm you would like to delete all signature data for example/trust-demo? [y/N] diff --git a/cli/trust/trust.go b/cli/trust/trust.go deleted file mode 100644 index 27453ae22ee4..000000000000 --- a/cli/trust/trust.go +++ /dev/null @@ -1,386 +0,0 @@ -package trust - -import ( - "context" - "encoding/json" - "io" - "net" - "net/http" - "net/url" - "os" - "path" - "path/filepath" - "time" - - "github.com/distribution/reference" - "github.com/docker/cli/cli/config" - "github.com/docker/distribution/registry/client/auth" - "github.com/docker/distribution/registry/client/auth/challenge" - "github.com/docker/distribution/registry/client/transport" - registrytypes "github.com/docker/docker/api/types/registry" - "github.com/docker/docker/registry" - "github.com/docker/go-connections/tlsconfig" - "github.com/opencontainers/go-digest" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "github.com/theupdateframework/notary" - "github.com/theupdateframework/notary/client" - "github.com/theupdateframework/notary/passphrase" - "github.com/theupdateframework/notary/storage" - "github.com/theupdateframework/notary/trustmanager" - "github.com/theupdateframework/notary/trustpinning" - "github.com/theupdateframework/notary/tuf/data" - "github.com/theupdateframework/notary/tuf/signed" -) - -var ( - // ReleasesRole is the role named "releases" - ReleasesRole = data.RoleName(path.Join(data.CanonicalTargetsRole.String(), "releases")) - // ActionsPullOnly defines the actions for read-only interactions with a Notary Repository - ActionsPullOnly = []string{"pull"} - // ActionsPushAndPull defines the actions for read-write interactions with a Notary Repository - ActionsPushAndPull = []string{"pull", "push"} -) - -// NotaryServer is the endpoint serving the Notary trust server -const NotaryServer = "https://notary.docker.io" - -// GetTrustDirectory returns the base trust directory name -func GetTrustDirectory() string { - return filepath.Join(config.Dir(), "trust") -} - -// certificateDirectory returns the directory containing -// TLS certificates for the given server. An error is -// returned if there was an error parsing the server string. -func certificateDirectory(server string) (string, error) { - u, err := url.Parse(server) - if err != nil { - return "", err - } - - return filepath.Join(config.Dir(), "tls", u.Host), nil -} - -// Server returns the base URL for the trust server. -func Server(index *registrytypes.IndexInfo) (string, error) { - if s := os.Getenv("DOCKER_CONTENT_TRUST_SERVER"); s != "" { - urlObj, err := url.Parse(s) - if err != nil || urlObj.Scheme != "https" { - return "", errors.Errorf("valid https URL required for trust server, got %s", s) - } - - return s, nil - } - if index.Official { - return NotaryServer, nil - } - return "https://" + index.Name, nil -} - -type simpleCredentialStore struct { - auth registrytypes.AuthConfig -} - -func (scs simpleCredentialStore) Basic(*url.URL) (string, string) { - return scs.auth.Username, scs.auth.Password -} - -func (scs simpleCredentialStore) RefreshToken(*url.URL, string) string { - return scs.auth.IdentityToken -} - -func (simpleCredentialStore) SetRefreshToken(*url.URL, string, string) {} - -// GetNotaryRepository returns a NotaryRepository which stores all the -// information needed to operate on a notary repository. -// It creates an HTTP transport providing authentication support. -func GetNotaryRepository(in io.Reader, out io.Writer, userAgent string, repoInfo *registry.RepositoryInfo, authConfig *registrytypes.AuthConfig, actions ...string) (client.Repository, error) { - server, err := Server(repoInfo.Index) - if err != nil { - return nil, err - } - - cfg := tlsconfig.ClientDefault() - cfg.InsecureSkipVerify = !repoInfo.Index.Secure - - // Get certificate base directory - certDir, err := certificateDirectory(server) - if err != nil { - return nil, err - } - logrus.Debugf("reading certificate directory: %s", certDir) - - if err := registry.ReadCertsDirectory(cfg, certDir); err != nil { - return nil, err - } - - base := &http.Transport{ - Proxy: http.ProxyFromEnvironment, - Dial: (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - }).Dial, - TLSHandshakeTimeout: 10 * time.Second, - TLSClientConfig: cfg, - DisableKeepAlives: true, - } - - // Skip configuration headers since request is not going to Docker daemon - modifiers := registry.Headers(userAgent, http.Header{}) - authTransport := transport.NewTransport(base, modifiers...) - pingClient := &http.Client{ - Transport: authTransport, - Timeout: 5 * time.Second, - } - endpointStr := server + "/v2/" - req, err := http.NewRequest(http.MethodGet, endpointStr, nil) - if err != nil { - return nil, err - } - - challengeManager := challenge.NewSimpleManager() - - resp, err := pingClient.Do(req) - if err != nil { - // Ignore error on ping to operate in offline mode - logrus.Debugf("Error pinging notary server %q: %s", endpointStr, err) - } else { - defer resp.Body.Close() - - // Add response to the challenge manager to parse out - // authentication header and register authentication method - if err := challengeManager.AddResponse(resp); err != nil { - return nil, err - } - } - - scope := auth.RepositoryScope{ - Repository: repoInfo.Name.Name(), - Actions: actions, - } - creds := simpleCredentialStore{auth: *authConfig} - tokenHandler := auth.NewTokenHandlerWithOptions(auth.TokenHandlerOptions{ - Transport: authTransport, - Credentials: creds, - Scopes: []auth.Scope{scope}, - ClientID: registry.AuthClientID, - }) - basicHandler := auth.NewBasicHandler(creds) - modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)) - tr := transport.NewTransport(base, modifiers...) - - return client.NewFileCachedRepository( - GetTrustDirectory(), - data.GUN(repoInfo.Name.Name()), - server, - tr, - GetPassphraseRetriever(in, out), - trustpinning.TrustPinConfig{}) -} - -// GetPassphraseRetriever returns a passphrase retriever that utilizes Content Trust env vars -func GetPassphraseRetriever(in io.Reader, out io.Writer) notary.PassRetriever { - aliasMap := map[string]string{ - "root": "root", - "snapshot": "repository", - "targets": "repository", - "default": "repository", - } - baseRetriever := passphrase.PromptRetrieverWithInOut(in, out, aliasMap) - env := map[string]string{ - "root": os.Getenv("DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE"), - "snapshot": os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"), - "targets": os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"), - "default": os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"), - } - - return func(keyName string, alias string, createNew bool, numAttempts int) (string, bool, error) { - if v := env[alias]; v != "" { - return v, numAttempts > 1, nil - } - // For non-root roles, we can also try the "default" alias if it is specified - if v := env["default"]; v != "" && alias != data.CanonicalRootRole.String() { - return v, numAttempts > 1, nil - } - return baseRetriever(keyName, alias, createNew, numAttempts) - } -} - -// NotaryError formats an error message received from the notary service -func NotaryError(repoName string, err error) error { - switch err.(type) { - case *json.SyntaxError: - logrus.Debugf("Notary syntax error: %s", err) - return errors.Errorf("Error: no trust data available for remote repository %s. Try running notary server and setting DOCKER_CONTENT_TRUST_SERVER to its HTTPS address?", repoName) - case signed.ErrExpired: - return errors.Errorf("Error: remote repository %s out-of-date: %v", repoName, err) - case trustmanager.ErrKeyNotFound: - return errors.Errorf("Error: signing keys for remote repository %s not found: %v", repoName, err) - case storage.NetworkError: - return errors.Errorf("Error: error contacting notary server: %v", err) - case storage.ErrMetaNotFound: - return errors.Errorf("Error: trust data missing for remote repository %s or remote repository not found: %v", repoName, err) - case trustpinning.ErrRootRotationFail, trustpinning.ErrValidationFail, signed.ErrInvalidKeyType: - return errors.Errorf("Warning: potential malicious behavior - trust data mismatch for remote repository %s: %v", repoName, err) - case signed.ErrNoKeys: - return errors.Errorf("Error: could not find signing keys for remote repository %s, or could not decrypt signing key: %v", repoName, err) - case signed.ErrLowVersion: - return errors.Errorf("Warning: potential malicious behavior - trust data version is lower than expected for remote repository %s: %v", repoName, err) - case signed.ErrRoleThreshold: - return errors.Errorf("Warning: potential malicious behavior - trust data has insufficient signatures for remote repository %s: %v", repoName, err) - case client.ErrRepositoryNotExist: - return errors.Errorf("Error: remote trust data does not exist for %s: %v", repoName, err) - case signed.ErrInsufficientSignatures: - return errors.Errorf("Error: could not produce valid signature for %s. If Yubikey was used, was touch input provided?: %v", repoName, err) - } - - return err -} - -// AddToAllSignableRoles attempts to add the image target to all the top level -// delegation roles we can (based on whether we have the signing key and whether -// the role's path allows us to). -// -// If there are no delegation roles, we add to the targets role. -func AddToAllSignableRoles(repo client.Repository, target *client.Target) error { - signableRoles, err := GetSignableRoles(repo, target) - if err != nil { - return err - } - - return repo.AddTarget(target, signableRoles...) -} - -// GetSignableRoles returns a list of roles for which we have valid signing -// keys, given a notary repository and a target -func GetSignableRoles(repo client.Repository, target *client.Target) ([]data.RoleName, error) { - var signableRoles []data.RoleName - - // translate the full key names, which includes the GUN, into just the key IDs - allCanonicalKeyIDs := make(map[string]struct{}) - for fullKeyID := range repo.GetCryptoService().ListAllKeys() { - allCanonicalKeyIDs[path.Base(fullKeyID)] = struct{}{} - } - - allDelegationRoles, err := repo.GetDelegationRoles() - if err != nil { - return signableRoles, err - } - - // if there are no delegation roles, then just try to sign it into the targets role - if len(allDelegationRoles) == 0 { - signableRoles = append(signableRoles, data.CanonicalTargetsRole) - return signableRoles, nil - } - - // there are delegation roles, find every delegation role we have a key for, - // and attempt to sign in to all those roles. - for _, delegationRole := range allDelegationRoles { - // We do not support signing any delegation role that isn't a direct child of the targets role. - // Also don't bother checking the keys if we can't add the target - // to this role due to path restrictions - if path.Dir(delegationRole.Name.String()) != data.CanonicalTargetsRole.String() || !delegationRole.CheckPaths(target.Name) { - continue - } - - for _, canonicalKeyID := range delegationRole.KeyIDs { - if _, ok := allCanonicalKeyIDs[canonicalKeyID]; ok { - signableRoles = append(signableRoles, delegationRole.Name) - break - } - } - } - - if len(signableRoles) == 0 { - return signableRoles, errors.Errorf("no valid signing keys for delegation roles") - } - - return signableRoles, nil -} - -// ImageRefAndAuth contains all reference information and the auth config for an image request -type ImageRefAndAuth struct { - original string - authConfig *registrytypes.AuthConfig - reference reference.Named - repoInfo *registry.RepositoryInfo - tag string - digest digest.Digest -} - -// GetImageReferencesAndAuth retrieves the necessary reference and auth information for an image name -// as an ImageRefAndAuth struct -func GetImageReferencesAndAuth(ctx context.Context, - authResolver func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig, - imgName string, -) (ImageRefAndAuth, error) { - ref, err := reference.ParseNormalizedNamed(imgName) - if err != nil { - return ImageRefAndAuth{}, err - } - - // Resolve the Repository name from fqn to RepositoryInfo - repoInfo, _ := registry.ParseRepositoryInfo(ref) - authConfig := authResolver(ctx, repoInfo.Index) - return ImageRefAndAuth{ - original: imgName, - authConfig: &authConfig, - reference: ref, - repoInfo: repoInfo, - tag: getTag(ref), - digest: getDigest(ref), - }, nil -} - -func getTag(ref reference.Named) string { - switch x := ref.(type) { - case reference.Canonical, reference.Digested: - return "" - case reference.NamedTagged: - return x.Tag() - default: - return "" - } -} - -func getDigest(ref reference.Named) digest.Digest { - switch x := ref.(type) { - case reference.Canonical: - return x.Digest() - case reference.Digested: - return x.Digest() - default: - return digest.Digest("") - } -} - -// AuthConfig returns the auth information (username, etc) for a given ImageRefAndAuth -func (imgRefAuth *ImageRefAndAuth) AuthConfig() *registrytypes.AuthConfig { - return imgRefAuth.authConfig -} - -// Reference returns the Image reference for a given ImageRefAndAuth -func (imgRefAuth *ImageRefAndAuth) Reference() reference.Named { - return imgRefAuth.reference -} - -// RepoInfo returns the repository information for a given ImageRefAndAuth -func (imgRefAuth *ImageRefAndAuth) RepoInfo() *registry.RepositoryInfo { - return imgRefAuth.repoInfo -} - -// Tag returns the Image tag for a given ImageRefAndAuth -func (imgRefAuth *ImageRefAndAuth) Tag() string { - return imgRefAuth.tag -} - -// Digest returns the Image digest for a given ImageRefAndAuth -func (imgRefAuth *ImageRefAndAuth) Digest() digest.Digest { - return imgRefAuth.digest -} - -// Name returns the image name used to initialize the ImageRefAndAuth -func (imgRefAuth *ImageRefAndAuth) Name() string { - return imgRefAuth.original -} diff --git a/cli/trust/trust_push.go b/cli/trust/trust_push.go deleted file mode 100644 index 1a8c5e4b7281..000000000000 --- a/cli/trust/trust_push.go +++ /dev/null @@ -1,143 +0,0 @@ -package trust - -import ( - "context" - "encoding/hex" - "encoding/json" - "fmt" - "io" - "sort" - - "github.com/distribution/reference" - "github.com/docker/cli/cli/streams" - "github.com/docker/cli/internal/jsonstream" - "github.com/docker/docker/api/types" - registrytypes "github.com/docker/docker/api/types/registry" - "github.com/docker/docker/registry" - "github.com/opencontainers/go-digest" - "github.com/pkg/errors" - "github.com/theupdateframework/notary/client" - "github.com/theupdateframework/notary/tuf/data" -) - -// Streams is an interface which exposes the standard input and output streams. -// -// Same interface as [github.com/docker/cli/cli/command.Streams] but defined here to prevent a circular import. -type Streams interface { - In() *streams.In - Out() *streams.Out - Err() *streams.Out -} - -// PushTrustedReference pushes a canonical reference to the trust server. -// -//nolint:gocyclo -func PushTrustedReference(ctx context.Context, ioStreams Streams, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig registrytypes.AuthConfig, in io.Reader, userAgent string) error { - // If it is a trusted push we would like to find the target entry which match the - // tag provided in the function and then do an AddTarget later. - notaryTarget := &client.Target{} - // Count the times of calling for handleTarget, - // if it is called more that once, that should be considered an error in a trusted push. - cnt := 0 - handleTarget := func(msg jsonstream.JSONMessage) { - cnt++ - if cnt > 1 { - // handleTarget should only be called once. This will be treated as an error. - return - } - - var pushResult types.PushResult - err := json.Unmarshal(*msg.Aux, &pushResult) - if err == nil && pushResult.Tag != "" { - if dgst, err := digest.Parse(pushResult.Digest); err == nil { - h, err := hex.DecodeString(dgst.Hex()) - if err != nil { - notaryTarget = nil - return - } - notaryTarget.Name = pushResult.Tag - notaryTarget.Hashes = data.Hashes{string(dgst.Algorithm()): h} - notaryTarget.Length = int64(pushResult.Size) - } - } - } - - var tag string - switch x := ref.(type) { - case reference.Canonical: - return errors.New("cannot push a digest reference") - case reference.NamedTagged: - tag = x.Tag() - default: - // We want trust signatures to always take an explicit tag, - // otherwise it will act as an untrusted push. - if err := jsonstream.Display(ctx, in, ioStreams.Out()); err != nil { - return err - } - _, _ = fmt.Fprintln(ioStreams.Err(), "No tag specified, skipping trust metadata push") - return nil - } - - if err := jsonstream.Display(ctx, in, ioStreams.Out(), jsonstream.WithAuxCallback(handleTarget)); err != nil { - return err - } - - if cnt > 1 { - return errors.Errorf("internal error: only one call to handleTarget expected") - } - - if notaryTarget == nil { - return errors.Errorf("no targets found, provide a specific tag in order to sign it") - } - - _, _ = fmt.Fprintln(ioStreams.Out(), "Signing and pushing trust metadata") - - repo, err := GetNotaryRepository(ioStreams.In(), ioStreams.Out(), userAgent, repoInfo, &authConfig, "push", "pull") - if err != nil { - return errors.Wrap(err, "error establishing connection to trust repository") - } - - // get the latest repository metadata so we can figure out which roles to sign - _, err = repo.ListTargets() - - switch err.(type) { - case client.ErrRepoNotInitialized, client.ErrRepositoryNotExist: - keys := repo.GetCryptoService().ListKeys(data.CanonicalRootRole) - var rootKeyID string - // always select the first root key - if len(keys) > 0 { - sort.Strings(keys) - rootKeyID = keys[0] - } else { - rootPublicKey, err := repo.GetCryptoService().Create(data.CanonicalRootRole, "", data.ECDSAKey) - if err != nil { - return err - } - rootKeyID = rootPublicKey.ID() - } - - // Initialize the notary repository with a remotely managed snapshot key - if err := repo.Initialize([]string{rootKeyID}, data.CanonicalSnapshotRole); err != nil { - return NotaryError(repoInfo.Name.Name(), err) - } - _, _ = fmt.Fprintf(ioStreams.Out(), "Finished initializing %q\n", repoInfo.Name.Name()) - err = repo.AddTarget(notaryTarget, data.CanonicalTargetsRole) - case nil: - // already initialized and we have successfully downloaded the latest metadata - err = AddToAllSignableRoles(repo, notaryTarget) - default: - return NotaryError(repoInfo.Name.Name(), err) - } - - if err == nil { - err = repo.Publish() - } - - if err != nil { - err = errors.Wrapf(err, "failed to sign %s:%s", repoInfo.Name.Name(), tag) - return NotaryError(repoInfo.Name.Name(), err) - } - - _, _ = fmt.Fprintf(ioStreams.Out(), "Successfully signed %s:%s\n", repoInfo.Name.Name(), tag) - return nil -} diff --git a/cli/trust/trust_tag.go b/cli/trust/trust_tag.go deleted file mode 100644 index 053f9317d164..000000000000 --- a/cli/trust/trust_tag.go +++ /dev/null @@ -1,22 +0,0 @@ -package trust - -import ( - "context" - "fmt" - "io" - - "github.com/distribution/reference" - "github.com/docker/docker/client" -) - -// TagTrusted tags a trusted ref. It is a shallow wrapper around [client.Client.ImageTag] -// that updates the given image references to their familiar format for tagging -// and printing. -func TagTrusted(ctx context.Context, apiClient client.ImageAPIClient, out io.Writer, trustedRef reference.Canonical, ref reference.NamedTagged) error { - // Use familiar references when interacting with client and output - familiarRef := reference.FamiliarString(ref) - trustedFamiliarRef := reference.FamiliarString(trustedRef) - - _, _ = fmt.Fprintf(out, "Tagging %s as %s\n", trustedFamiliarRef, familiarRef) - return apiClient.ImageTag(ctx, trustedFamiliarRef, familiarRef) -} diff --git a/cli/trust/trust_test.go b/cli/trust/trust_test.go deleted file mode 100644 index 18531bf4c941..000000000000 --- a/cli/trust/trust_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package trust - -import ( - "testing" - - "github.com/distribution/reference" - registrytypes "github.com/docker/docker/api/types/registry" - "github.com/opencontainers/go-digest" - "github.com/theupdateframework/notary/client" - "github.com/theupdateframework/notary/trustpinning" - "gotest.tools/v3/assert" - is "gotest.tools/v3/assert/cmp" -) - -func TestGetTag(t *testing.T) { - ref, err := reference.ParseNormalizedNamed("ubuntu@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2") - assert.NilError(t, err) - tag := getTag(ref) - assert.Check(t, is.Equal("", tag)) - - ref, err = reference.ParseNormalizedNamed("alpine:latest") - assert.NilError(t, err) - tag = getTag(ref) - assert.Check(t, is.Equal(tag, "latest")) - - ref, err = reference.ParseNormalizedNamed("alpine") - assert.NilError(t, err) - tag = getTag(ref) - assert.Check(t, is.Equal(tag, "")) -} - -func TestGetDigest(t *testing.T) { - ref, err := reference.ParseNormalizedNamed("ubuntu@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2") - assert.NilError(t, err) - d := getDigest(ref) - assert.Check(t, is.Equal(digest.Digest("sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2"), d)) - - ref, err = reference.ParseNormalizedNamed("alpine:latest") - assert.NilError(t, err) - d = getDigest(ref) - assert.Check(t, is.Equal(digest.Digest(""), d)) - - ref, err = reference.ParseNormalizedNamed("alpine") - assert.NilError(t, err) - d = getDigest(ref) - assert.Check(t, is.Equal(digest.Digest(""), d)) -} - -func TestGetSignableRolesError(t *testing.T) { - notaryRepo, err := client.NewFileCachedRepository(t.TempDir(), "gun", "https://localhost", nil, nil, trustpinning.TrustPinConfig{}) - assert.NilError(t, err) - _, err = GetSignableRoles(notaryRepo, &client.Target{}) - const expected = "client is offline" - assert.Error(t, err, expected) -} - -func TestENVTrustServer(t *testing.T) { - t.Setenv("DOCKER_CONTENT_TRUST_SERVER", "https://notary-test.example.com:5000") - indexInfo := ®istrytypes.IndexInfo{Name: "testserver"} - output, err := Server(indexInfo) - const expected = "https://notary-test.example.com:5000" - assert.NilError(t, err) - assert.Equal(t, output, expected) -} - -func TestHTTPENVTrustServer(t *testing.T) { - t.Setenv("DOCKER_CONTENT_TRUST_SERVER", "http://notary-test.example.com:5000") - indexInfo := ®istrytypes.IndexInfo{Name: "testserver"} - _, err := Server(indexInfo) - const expected = "valid https URL required for trust server" - assert.ErrorContains(t, err, expected, "Expected error with invalid scheme") -} - -func TestOfficialTrustServer(t *testing.T) { - indexInfo := ®istrytypes.IndexInfo{Name: "testserver", Official: true} - output, err := Server(indexInfo) - const expected = NotaryServer - assert.NilError(t, err) - assert.Equal(t, output, expected) -} - -func TestNonOfficialTrustServer(t *testing.T) { - indexInfo := ®istrytypes.IndexInfo{Name: "testserver", Official: false} - output, err := Server(indexInfo) - const expected = "https://testserver" - assert.NilError(t, err) - assert.Equal(t, output, expected) -} diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index 595dec6ce132..a29a94b5415e 100644 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -1978,7 +1978,6 @@ _docker_container_run_and_create() { " local boolean_options=" - --disable-content-trust=false --help --init --interactive -i @@ -2803,7 +2802,6 @@ _docker_image_build() { " local boolean_options=" - --disable-content-trust=false --force-rm --help --no-cache @@ -3059,7 +3057,7 @@ _docker_image_pull() { case "$cur" in -*) - local options="--all-tags -a --disable-content-trust=false --help --platform --quiet -q" + local options="--all-tags -a --help --platform --quiet -q" COMPREPLY=( $( compgen -W "$options" -- "$cur" ) ) ;; *) @@ -3082,7 +3080,7 @@ _docker_image_pull() { _docker_image_push() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--all-tags -a --disable-content-trust=false --help --quiet -q" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--all-tags -a --help --quiet -q" -- "$cur" ) ) ;; *) local counter=$(__docker_pos_first_nonflag) @@ -4541,7 +4539,7 @@ _docker_plugin_install() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--alias --disable --disable-content-trust=false --grant-all-permissions --help" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--alias --disable --grant-all-permissions --help" -- "$cur" ) ) ;; esac } @@ -4627,7 +4625,7 @@ _docker_plugin_set() { _docker_plugin_upgrade() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--disable-content-trust --grant-all-permissions --help --skip-remote-check" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--grant-all-permissions --help --skip-remote-check" -- "$cur" ) ) ;; *) local counter=$(__docker_pos_first_nonflag) @@ -5208,67 +5206,6 @@ _docker_tag() { } -_docker_trust() { - local subcommands=" - inspect - revoke - sign - " - __docker_subcommands "$subcommands" && return - - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) - ;; - *) - COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) ) - ;; - esac -} - -_docker_trust_inspect() { - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "--help --pretty" -- "$cur" ) ) - ;; - *) - local counter=$(__docker_pos_first_nonflag) - if [ "$cword" -eq "$counter" ]; then - __docker_complete_images --repo --tag - fi - ;; - esac -} - -_docker_trust_revoke() { - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "--help --yes -y" -- "$cur" ) ) - ;; - *) - local counter=$(__docker_pos_first_nonflag) - if [ "$cword" -eq "$counter" ]; then - __docker_complete_images --repo --tag - fi - ;; - esac -} - -_docker_trust_sign() { - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "--help --local" -- "$cur" ) ) - ;; - *) - local counter=$(__docker_pos_first_nonflag) - if [ "$cword" -eq "$counter" ]; then - __docker_complete_images --force-tag --id - fi - ;; - esac -} - - _docker_unpause() { _docker_container_unpause } @@ -5447,7 +5384,6 @@ _docker() { stack swarm system - trust volume ) diff --git a/contrib/completion/fish/docker.fish b/contrib/completion/fish/docker.fish index 10d72f28b5f3..ea02667d54e2 100644 --- a/contrib/completion/fish/docker.fish +++ b/contrib/completion/fish/docker.fish @@ -16,7 +16,7 @@ function __fish_docker_no_subcommand --description 'Test if docker has yet to be given the subcommand' for i in (commandline -opc) - if contains -- $i attach build commit cp create diff events exec export history images import info inspect kill load login logout logs network pause port ps pull push rename restart rm rmi run save search start stop tag top trust unpause version wait stats + if contains -- $i attach build commit cp create diff events exec export history images import info inspect kill load login logout logs network pause port ps pull push rename restart rm rmi run save search start stop tag top unpause version wait stats return 1 end end @@ -39,18 +39,6 @@ function __fish_print_docker_networks --description 'Print a list of docker netw end -function __fish_docker_no_subcommand_trust --description 'Test if docker has yet to be given the trust subcommand' - if __fish_seen_subcommand_from trust - for i in (commandline -opc) - if contains -- $i inspect key revoke sign signer - return 1 - end - end - return 0 - end - return 1 -end - function __fish_docker_subcommand_path --description 'Test if command has all arguments in any order' set -l cmd (commandline -poc) set -e cmd[1] @@ -139,7 +127,6 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l cpu-quota -d complete -c docker -A -f -n '__fish_seen_subcommand_from build' -s c -l cpu-shares -d 'CPU shares (relative weight)' complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l cpuset-cpus -d 'CPUs in which to allow execution (0-3, 0,1)' complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l cpuset-mems -d 'MEMs in which to allow execution (0-3, 0,1)' -complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l disable-content-trust -d 'Skip image verification' complete -c docker -A -f -n '__fish_seen_subcommand_from build' -s f -l file -d "Name of the Dockerfile (Default is ‘PATH/Dockerfile’)" complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l force-rm -d 'Always remove intermediate containers' complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l help -d 'Print usage' @@ -202,7 +189,6 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l device-read- complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l device-read-iops -d 'Limit read rate (IO per second) from a device' complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l device-write-bps -d 'Limit write rate (bytes per second) to a device' complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l device-write-iops -d 'Limit write rate (IO per second) to a device' -complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l disable-content-trust -d 'Skip image verification' complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l dns -d 'Set custom DNS servers' complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l dns-opt -d 'Set DNS options' complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l dns-option -d 'Set DNS options' @@ -544,34 +530,6 @@ complete -c docker -f -n '__fish_docker_no_subcommand' -a top -d 'Lookup the run complete -c docker -A -f -n '__fish_seen_subcommand_from top' -l help -d 'Print usage' complete -c docker -A -f -n '__fish_seen_subcommand_from top' -a '(__fish_print_docker_containers running)' -d "Container" -#trust -complete -c docker -f -n '__fish_docker_no_subcommand' -a trust -d 'Manage trust on Docker images' -complete -c docker -A -f -n '__fish_seen_subcommand_from trust' -l help -d 'Print usage' - -#trust inspect -complete -c docker -A -f -n '__fish_docker_no_subcommand_trust' -a inspect -d 'Return low-level information about keys and signatures' -complete -c docker -A -f -n '__fish_docker_subcommand_path trust inspect' -l pretty -d 'Print the information in a human friendly format' - -#trust key -complete -c docker -A -f -n '__fish_docker_no_subcommand_trust' -a key -d 'Manage keys for signing Docker images' -complete -c docker -A -f -n '__fish_docker_subcommand_path trust key; and __fish_docker_subcommand_path_without generate load' -a generate -d 'Generate and load a signing key-pair' -complete -c docker -A -f -n '__fish_docker_subcommand_path trust key load' -l dir -d 'Directory to generate key in, defaults to current directory' -complete -c docker -A -f -n '__fish_docker_subcommand_path trust key; and __fish_docker_subcommand_path_without generate load' -a load -d 'Load a private key file for signing' -complete -c docker -A -f -n '__fish_docker_subcommand_path trust key load' -l name -d 'Name for the loaded key (default "signer")' - -#trust revoke -complete -c docker -A -f -n '__fish_docker_no_subcommand_trust' -a revoke -d 'Remove trust for an image' -complete -c docker -A -f -n '__fish_docker_subcommand_path trust revoke' -s y -l yes -d 'Do not prompt for confirmation' - -#trust sign -complete -c docker -A -f -n '__fish_docker_no_subcommand_trust' -a sign -d 'Sign an image' -complete -c docker -A -f -n '__fish_docker_subcommand_path trust sign' -l local -d 'Sign a locally tagged image' - -#trust signer -complete -c docker -A -f -n '__fish_docker_no_subcommand_trust' -a signer -d 'Manage entities who can sign Docker images' -complete -c docker -A -f -n '__fish_docker_subcommand_path trust signer; and __fish_docker_subcommand_path_without add remove' -a add -d 'Add a signer' -complete -c docker -A -f -n '__fish_docker_subcommand_path trust signer; and __fish_docker_subcommand_path_without add remove' -a remove -d 'remove a signer' - # unpause complete -c docker -f -n '__fish_docker_no_subcommand' -a unpause -d 'Unpause a paused container' complete -c docker -A -f -n '__fish_seen_subcommand_from unpause' -a '(__fish_print_docker_containers running)' -d "Container" diff --git a/contrib/completion/zsh/_docker b/contrib/completion/zsh/_docker index 88350aabfb8e..55f814f329c6 100644 --- a/contrib/completion/zsh/_docker +++ b/contrib/completion/zsh/_docker @@ -614,7 +614,6 @@ __docker_container_subcommand() { "($help)*--device-read-iops=[Limit the read rate (IO per second) from a device]:device:IO rate: " "($help)*--device-write-bps=[Limit the write rate (bytes per second) to a device]:device:IO rate: " "($help)*--device-write-iops=[Limit the write rate (IO per second) to a device]:device:IO rate: " - "($help)--disable-content-trust[Skip image verification]" "($help)*--dns=[Custom DNS servers]:DNS server: " "($help)*--dns-option=[Custom DNS options]:DNS option: " "($help)*--dns-search=[Custom DNS search domains]:DNS domains: " @@ -1005,7 +1004,6 @@ __docker_image_subcommand() { "($help)--cpu-rt-runtime=[Limit the CPU real-time runtime]:CPU real-time runtime in microseconds: " \ "($help)--cpuset-cpus=[CPUs in which to allow execution]:CPUs: " \ "($help)--cpuset-mems=[MEMs in which to allow execution]:MEMs: " \ - "($help)--disable-content-trust[Skip image verification]" \ "($help -f --file)"{-f=,--file=}"[Name of the Dockerfile]:Dockerfile:_files" \ "($help)--force-rm[Always remove intermediate containers]" \ "($help)--isolation=[Container isolation technology]:isolation:(default hyperv process)" \ @@ -1076,14 +1074,12 @@ __docker_image_subcommand() { _arguments $(__docker_arguments) \ $opts_help \ "($help -a --all-tags)"{-a,--all-tags}"[Download all tagged images]" \ - "($help)--disable-content-trust[Skip image verification]" \ "($help -):name:__docker_search" && ret=0 ;; (push) _arguments $(__docker_arguments) \ $opts_help \ "($help -a --all-tags)"{-a,--all-tags}"[Push all tags of an image to the repository]" \ - "($help)--disable-content-trust[Skip image signing]" \ "($help -): :__docker_complete_images" && ret=0 ;; (rm) @@ -1652,7 +1648,6 @@ __docker_plugin_subcommand() { $opts_help \ "($help)--alias=[Local name for plugin]:alias: " \ "($help)--disable[Do not enable the plugin on install]" \ - "($help)--disable-content-trust[Skip image verification (default true)]" \ "($help)--grant-all-permissions[Grant all permissions necessary to run the plugin]" \ "($help -)1:plugin:__docker_complete_plugins" \ "($help -)*:key=value: " && ret=0 @@ -1668,7 +1663,6 @@ __docker_plugin_subcommand() { (push) _arguments $(__docker_arguments) \ $opts_help \ - "($help)--disable-content-trust[Skip image verification (default true)]" \ "($help -)1:plugin:__docker_complete_plugins" && ret=0 ;; (rm|remove) @@ -1686,7 +1680,6 @@ __docker_plugin_subcommand() { (upgrade) _arguments $(__docker_arguments) \ $opts_help \ - "($help)--disable-content-trust[Skip image verification (default true)]" \ "($help)--grant-all-permissions[Grant all permissions necessary to run the plugin]" \ "($help)--skip-remote-check[Do not check if specified remote plugin matches existing plugin image]" \ "($help -)1:plugin:__docker_complete_plugins" \ diff --git a/docker-bake.hcl b/docker-bake.hcl index 9c2347653d06..ed20d34a4e9d 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -163,12 +163,6 @@ target "e2e-image" { } } -target "e2e-gencerts" { - inherits = ["_common"] - dockerfile = "./e2e/testdata/Dockerfile.gencerts" - output = ["./e2e/testdata"] -} - target "docker-metadata-action" { tags = ["cli-bin:local"] } diff --git a/docs/reference/commandline/build.md b/docs/reference/commandline/build.md index dca5ee76ab84..491b0477dba5 100644 --- a/docs/reference/commandline/build.md +++ b/docs/reference/commandline/build.md @@ -21,7 +21,6 @@ Build an image from a Dockerfile | `-c`, `--cpu-shares` | `int64` | `0` | CPU shares (relative weight) | | `--cpuset-cpus` | `string` | | CPUs in which to allow execution (0-3, 0,1) | | `--cpuset-mems` | `string` | | MEMs in which to allow execution (0-3, 0,1) | -| `--disable-content-trust` | `bool` | `true` | Skip image verification | | [`-f`](https://docs.docker.com/reference/cli/docker/buildx/build/#file), [`--file`](https://docs.docker.com/reference/cli/docker/buildx/build/#file) | `string` | | Name of the Dockerfile (Default is `PATH/Dockerfile`) | | `--force-rm` | `bool` | | Always remove intermediate containers | | `--iidfile` | `string` | | Write the image ID to the file | diff --git a/docs/reference/commandline/builder_build.md b/docs/reference/commandline/builder_build.md index ad9c09532159..71589da94b93 100644 --- a/docs/reference/commandline/builder_build.md +++ b/docs/reference/commandline/builder_build.md @@ -21,7 +21,6 @@ Build an image from a Dockerfile | `-c`, `--cpu-shares` | `int64` | `0` | CPU shares (relative weight) | | `--cpuset-cpus` | `string` | | CPUs in which to allow execution (0-3, 0,1) | | `--cpuset-mems` | `string` | | MEMs in which to allow execution (0-3, 0,1) | -| `--disable-content-trust` | `bool` | `true` | Skip image verification | | [`-f`](https://docs.docker.com/reference/cli/docker/buildx/build/#file), [`--file`](https://docs.docker.com/reference/cli/docker/buildx/build/#file) | `string` | | Name of the Dockerfile (Default is `PATH/Dockerfile`) | | `--force-rm` | `bool` | | Always remove intermediate containers | | `--iidfile` | `string` | | Write the image ID to the file | diff --git a/docs/reference/commandline/container_create.md b/docs/reference/commandline/container_create.md index 3b812a77747f..8291de7f5171 100644 --- a/docs/reference/commandline/container_create.md +++ b/docs/reference/commandline/container_create.md @@ -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 | diff --git a/docs/reference/commandline/container_run.md b/docs/reference/commandline/container_run.md index 14186446098a..2701ac899144 100644 --- a/docs/reference/commandline/container_run.md +++ b/docs/reference/commandline/container_run.md @@ -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 | diff --git a/docs/reference/commandline/create.md b/docs/reference/commandline/create.md index a3068217095e..91b8da029e8b 100644 --- a/docs/reference/commandline/create.md +++ b/docs/reference/commandline/create.md @@ -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 | diff --git a/docs/reference/commandline/docker.md b/docs/reference/commandline/docker.md index 69b1c91303be..18118c6e1ee0 100644 --- a/docs/reference/commandline/docker.md +++ b/docs/reference/commandline/docker.md @@ -59,7 +59,6 @@ The base command for the Docker CLI. | [`system`](system.md) | Manage Docker | | [`tag`](tag.md) | Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE | | [`top`](top.md) | Display the running processes of a container | -| [`trust`](trust.md) | Manage trust on Docker images | | [`unpause`](unpause.md) | Unpause all processes within one or more containers | | [`update`](update.md) | Update configuration of one or more containers | | [`version`](version.md) | Show the Docker version information | @@ -123,8 +122,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. | @@ -415,15 +412,6 @@ between releases without warning or can be removed from a future release. Starting with Docker 20.10, experimental CLI features are enabled by default, and require no configuration to enable them. -#### Notary - -If using your own notary server and a self-signed certificate or an internal -Certificate Authority, you need to place the certificate at -`tls//ca.crt` in your Docker config directory. - -Alternatively you can trust the certificate globally by adding it to your system's -list of root Certificate Authorities. - ## Examples ### Specify daemon host (-H, --host) diff --git a/docs/reference/commandline/image_build.md b/docs/reference/commandline/image_build.md index bd9ae8ee3dff..62575c4e080f 100644 --- a/docs/reference/commandline/image_build.md +++ b/docs/reference/commandline/image_build.md @@ -21,7 +21,6 @@ Build an image from a Dockerfile | `-c`, `--cpu-shares` | `int64` | `0` | CPU shares (relative weight) | | `--cpuset-cpus` | `string` | | CPUs in which to allow execution (0-3, 0,1) | | `--cpuset-mems` | `string` | | MEMs in which to allow execution (0-3, 0,1) | -| `--disable-content-trust` | `bool` | `true` | Skip image verification | | [`-f`](https://docs.docker.com/reference/cli/docker/buildx/build/#file), [`--file`](https://docs.docker.com/reference/cli/docker/buildx/build/#file) | `string` | | Name of the Dockerfile (Default is `PATH/Dockerfile`) | | `--force-rm` | `bool` | | Always remove intermediate containers | | `--iidfile` | `string` | | Write the image ID to the file | diff --git a/docs/reference/commandline/image_pull.md b/docs/reference/commandline/image_pull.md index 3d390a294e49..aad73fe29ade 100644 --- a/docs/reference/commandline/image_pull.md +++ b/docs/reference/commandline/image_pull.md @@ -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 | diff --git a/docs/reference/commandline/image_push.md b/docs/reference/commandline/image_push.md index 91f6a41be1f0..543563398d7a 100644 --- a/docs/reference/commandline/image_push.md +++ b/docs/reference/commandline/image_push.md @@ -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.
Image index won't be pushed, meaning that other manifests, including attestations won't be preserved.
'os[/arch[/variant]]': Explicit platform (eg. linux/amd64) | | `-q`, `--quiet` | `bool` | | Suppress verbose output | diff --git a/docs/reference/commandline/plugin_install.md b/docs/reference/commandline/plugin_install.md index 25ceb35c217e..38b619b51b23 100644 --- a/docs/reference/commandline/plugin_install.md +++ b/docs/reference/commandline/plugin_install.md @@ -9,7 +9,6 @@ Install a plugin |:--------------------------|:---------|:--------|:--------------------------------------------------| | `--alias` | `string` | | Local name for plugin | | `--disable` | `bool` | | Do not enable the plugin on install | -| `--disable-content-trust` | `bool` | `true` | Skip image verification | | `--grant-all-permissions` | `bool` | | Grant all permissions necessary to run the plugin | diff --git a/docs/reference/commandline/plugin_push.md b/docs/reference/commandline/plugin_push.md index 3fcfe47f6f2c..155d2d44d23c 100644 --- a/docs/reference/commandline/plugin_push.md +++ b/docs/reference/commandline/plugin_push.md @@ -3,12 +3,6 @@ Push a plugin to a registry -### Options - -| Name | Type | Default | Description | -|:--------------------------|:-------|:--------|:-------------------| -| `--disable-content-trust` | `bool` | `true` | Skip image signing | - diff --git a/docs/reference/commandline/plugin_upgrade.md b/docs/reference/commandline/plugin_upgrade.md index 39730104d4b7..df4094875b05 100644 --- a/docs/reference/commandline/plugin_upgrade.md +++ b/docs/reference/commandline/plugin_upgrade.md @@ -7,7 +7,6 @@ Upgrade an existing plugin | Name | Type | Default | Description | |:--------------------------|:-------|:--------|:----------------------------------------------------------------------| -| `--disable-content-trust` | `bool` | `true` | Skip image verification | | `--grant-all-permissions` | `bool` | | Grant all permissions necessary to run the plugin | | `--skip-remote-check` | `bool` | | Do not check if specified remote plugin matches existing plugin image | diff --git a/docs/reference/commandline/pull.md b/docs/reference/commandline/pull.md index 66acb611da1b..3cc2b0f94f93 100644 --- a/docs/reference/commandline/pull.md +++ b/docs/reference/commandline/pull.md @@ -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 | diff --git a/docs/reference/commandline/push.md b/docs/reference/commandline/push.md index 9558d38e5ebc..61f48b22abfd 100644 --- a/docs/reference/commandline/push.md +++ b/docs/reference/commandline/push.md @@ -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.
Image index won't be pushed, meaning that other manifests, including attestations won't be preserved.
'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.
Image index won't be pushed, meaning that other manifests, including attestations won't be preserved.
'os[/arch[/variant]]': Explicit platform (eg. linux/amd64) | +| `-q`, `--quiet` | `bool` | | Suppress verbose output | diff --git a/docs/reference/commandline/run.md b/docs/reference/commandline/run.md index 0e9a90ef3efd..f51944793462 100644 --- a/docs/reference/commandline/run.md +++ b/docs/reference/commandline/run.md @@ -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 | diff --git a/docs/reference/commandline/trust.md b/docs/reference/commandline/trust.md deleted file mode 100644 index bc1fa7206cb5..000000000000 --- a/docs/reference/commandline/trust.md +++ /dev/null @@ -1,19 +0,0 @@ -# trust - - -Manage trust on Docker images - -### Subcommands - -| Name | Description | -|:------------------------------|:-------------------------------------------------------| -| [`inspect`](trust_inspect.md) | Return low-level information about keys and signatures | -| [`key`](trust_key.md) | Manage keys for signing Docker images | -| [`revoke`](trust_revoke.md) | Remove trust for an image | -| [`sign`](trust_sign.md) | Sign an image | -| [`signer`](trust_signer.md) | Manage entities who can sign Docker images | - - - - - diff --git a/docs/reference/commandline/trust_inspect.md b/docs/reference/commandline/trust_inspect.md deleted file mode 100644 index 11c8ecf6d233..000000000000 --- a/docs/reference/commandline/trust_inspect.md +++ /dev/null @@ -1,486 +0,0 @@ -# trust inspect - - -Return low-level information about keys and signatures - -### Options - -| Name | Type | Default | Description | -|:-----------|:-------|:--------|:-------------------------------------------------| -| `--pretty` | `bool` | | Print the information in a human friendly format | - - - - -## Description - -`docker trust inspect` provides low-level JSON information on signed repositories. -This includes all image tags that are signed, who signed them, and who can sign -new tags. - -## Examples - -### Get low-level details about signatures for a single image tag - -Use the `docker trust inspect` to get trust information about an image. The -following example prints trust information for the `alpine:latest` image: - -```console -$ docker trust inspect alpine:latest -``` - -The output is in JSON format, for example: - -```json -[ - { - "Name": "alpine:latest", - "SignedTags": [ - { - "SignedTag": "latest", - "Digest": "d6bfc3baf615dc9618209a8d607ba2a8103d9c8a405b3bd8741d88b4bef36478", - "Signers": [ - "Repo Admin" - ] - } - ], - "Signers": [], - "AdministrativeKeys": [ - { - "Name": "Repository", - "Keys": [ - { - "ID": "5a46c9aaa82ff150bb7305a2d17d0c521c2d784246807b2dc611f436a69041fd" - } - ] - }, - { - "Name": "Root", - "Keys": [ - { - "ID": "a2489bcac7a79aa67b19b96c4a3bf0c675ffdf00c6d2fabe1a5df1115e80adce" - } - ] - } - ] - } -] -``` - -The `SignedTags` key will list the `SignedTag` name, its `Digest`, -and the `Signers` responsible for the signature. - -`AdministrativeKeys` will list the `Repository` and `Root` keys. - -If signers are set up for the repository via other `docker trust` -commands, `docker trust inspect` includes a `Signers` key: - -```console -$ docker trust inspect my-image:purple -``` - -The output is in JSON format, for example: - -```json -[ - { - "Name": "my-image:purple", - "SignedTags": [ - { - "SignedTag": "purple", - "Digest": "941d3dba358621ce3c41ef67b47cf80f701ff80cdf46b5cc86587eaebfe45557", - "Signers": [ - "alice", - "bob", - "carol" - ] - } - ], - "Signers": [ - { - "Name": "alice", - "Keys": [ - { - "ID": "04dd031411ed671ae1e12f47ddc8646d98f135090b01e54c3561e843084484a3" - }, - { - "ID": "6a11e4898a4014d400332ab0e096308c844584ff70943cdd1d6628d577f45fd8" - } - ] - }, - { - "Name": "bob", - "Keys": [ - { - "ID": "433e245c656ae9733cdcc504bfa560f90950104442c4528c9616daa45824ccba" - } - ] - }, - { - "Name": "carol", - "Keys": [ - { - "ID": "d32fa8b5ca08273a2880f455fcb318da3dc80aeae1a30610815140deef8f30d9" - }, - { - "ID": "9a8bbec6ba2af88a5fad6047d428d17e6d05dbdd03d15b4fc8a9a0e8049cd606" - } - ] - } - ], - "AdministrativeKeys": [ - { - "Name": "Repository", - "Keys": [ - { - "ID": "27df2c8187e7543345c2e0bf3a1262e0bc63a72754e9a7395eac3f747ec23a44" - } - ] - }, - { - "Name": "Root", - "Keys": [ - { - "ID": "40b66ccc8b176be8c7d365a17f3e046d1c3494e053dd57cfeacfe2e19c4f8e8f" - } - ] - } - ] - } -] -``` - -If the image tag is unsigned or unavailable, `docker trust inspect` does not -display any signed tags. - -```console -$ docker trust inspect unsigned-img - -no signatures or cannot access unsigned-img -``` - -However, if other tags are signed in the same image repository, -`docker trust inspect` reports relevant key information: - -```console -$ docker trust inspect alpine:unsigned -``` - -The output is in JSON format, for example: - -```json -[ - { - "Name": "alpine:unsigned", - "Signers": [], - "AdministrativeKeys": [ - { - "Name": "Repository", - "Keys": [ - { - "ID": "5a46c9aaa82ff150bb7305a2d17d0c521c2d784246807b2dc611f436a69041fd" - } - ] - }, - { - "Name": "Root", - "Keys": [ - { - "ID": "a2489bcac7a79aa67b19b96c4a3bf0c675ffdf00c6d2fabe1a5df1115e80adce" - } - ] - } - ] - } -] -``` - -### Get details about signatures for all image tags in a repository - -If no tag is specified, `docker trust inspect` will report details for all -signed tags in the repository: - -```console -$ docker trust inspect alpine -``` - -The output is in JSON format, for example: - -```json -[ - { - "Name": "alpine", - "SignedTags": [ - { - "SignedTag": "3.5", - "Digest": "b007a354427e1880de9cdba533e8e57382b7f2853a68a478a17d447b302c219c", - "Signers": [ - "Repo Admin" - ] - }, - { - "SignedTag": "3.6", - "Digest": "d6bfc3baf615dc9618209a8d607ba2a8103d9c8a405b3bd8741d88b4bef36478", - "Signers": [ - "Repo Admin" - ] - }, - { - "SignedTag": "edge", - "Digest": "23e7d843e63a3eee29b6b8cfcd10e23dd1ef28f47251a985606a31040bf8e096", - "Signers": [ - "Repo Admin" - ] - }, - { - "SignedTag": "latest", - "Digest": "d6bfc3baf615dc9618209a8d607ba2a8103d9c8a405b3bd8741d88b4bef36478", - "Signers": [ - "Repo Admin" - ] - } - ], - "Signers": [], - "AdministrativeKeys": [ - { - "Name": "Repository", - "Keys": [ - { - "ID": "5a46c9aaa82ff150bb7305a2d17d0c521c2d784246807b2dc611f436a69041fd" - } - ] - }, - { - "Name": "Root", - "Keys": [ - { - "ID": "a2489bcac7a79aa67b19b96c4a3bf0c675ffdf00c6d2fabe1a5df1115e80adce" - } - ] - } - ] - } -] -``` - - -### Get details about signatures for multiple images - -`docker trust inspect` can take multiple repositories and images as arguments, -and reports the results in an ordered list: - -```console -$ docker trust inspect alpine notary -``` - -The output is in JSON format, for example: - -```json -[ - { - "Name": "alpine", - "SignedTags": [ - { - "SignedTag": "3.5", - "Digest": "b007a354427e1880de9cdba533e8e57382b7f2853a68a478a17d447b302c219c", - "Signers": [ - "Repo Admin" - ] - }, - { - "SignedTag": "3.6", - "Digest": "d6bfc3baf615dc9618209a8d607ba2a8103d9c8a405b3bd8741d88b4bef36478", - "Signers": [ - "Repo Admin" - ] - }, - { - "SignedTag": "edge", - "Digest": "23e7d843e63a3eee29b6b8cfcd10e23dd1ef28f47251a985606a31040bf8e096", - "Signers": [ - "Repo Admin" - ] - }, - { - "SignedTag": "integ-test-base", - "Digest": "3952dc48dcc4136ccdde37fbef7e250346538a55a0366e3fccc683336377e372", - "Signers": [ - "Repo Admin" - ] - }, - { - "SignedTag": "latest", - "Digest": "d6bfc3baf615dc9618209a8d607ba2a8103d9c8a405b3bd8741d88b4bef36478", - "Signers": [ - "Repo Admin" - ] - } - ], - "Signers": [], - "AdministrativeKeys": [ - { - "Name": "Repository", - "Keys": [ - { - "ID": "5a46c9aaa82ff150bb7305a2d17d0c521c2d784246807b2dc611f436a69041fd" - } - ] - }, - { - "Name": "Root", - "Keys": [ - { - "ID": "a2489bcac7a79aa67b19b96c4a3bf0c675ffdf00c6d2fabe1a5df1115e80adce" - } - ] - } - ] - }, - { - "Name": "notary", - "SignedTags": [ - { - "SignedTag": "server", - "Digest": "71f64ab718a3331dee103bc5afc6bc492914738ce37c2d2f127a8133714ecf5c", - "Signers": [ - "Repo Admin" - ] - }, - { - "SignedTag": "signer", - "Digest": "a6122d79b1e74f70b5dd933b18a6d1f99329a4728011079f06b245205f158fe8", - "Signers": [ - "Repo Admin" - ] - } - ], - "Signers": [], - "AdministrativeKeys": [ - { - "Name": "Root", - "Keys": [ - { - "ID": "8cdcdef5bd039f4ab5a029126951b5985eebf57cabdcdc4d21f5b3be8bb4ce92" - } - ] - }, - { - "Name": "Repository", - "Keys": [ - { - "ID": "85bfd031017722f950d480a721f845a2944db26a3dc084040a70f1b0d9bbb3df" - } - ] - } - ] - } -] -``` - -### Formatting - -You can print the inspect output in a human-readable format instead of the default -JSON output, by using the `--pretty` option: - -### Get details about signatures for a single image tag - -```console -$ docker trust inspect --pretty alpine:latest - -SIGNED TAG DIGEST SIGNERS -latest 1072e499f3f655a032e88542330cf75b02e7bdf673278f701d7ba61629ee3ebe (Repo Admin) - -Administrative keys for alpine:latest: -Repository Key: 5a46c9aaa82ff150bb7305a2d17d0c521c2d784246807b2dc611f436a69041fd -Root Key: a2489bcac7a79aa67b19b96c4a3bf0c675ffdf00c6d2fabe1a5df1115e80adce -``` - -The `SIGNED TAG` is the signed image tag with a unique content-addressable -`DIGEST`. `SIGNERS` lists all entities who have signed. - -The administrative keys listed specify the root key of trust, as well as -the administrative repository key. These keys are responsible for modifying -signers, and rotating keys for the signed repository. - -If signers are set up for the repository via other `docker trust` commands, -`docker trust inspect --pretty` displays them appropriately as a `SIGNER` -and specify their `KEYS`: - -```console -$ docker trust inspect --pretty my-image:purple - -SIGNED TAG DIGEST SIGNERS -purple 941d3dba358621ce3c41ef67b47cf80f701ff80cdf46b5cc86587eaebfe45557 alice, bob, carol - -List of signers and their keys: - -SIGNER KEYS -alice 47caae5b3e61, a85aab9d20a4 -bob 034370bcbd77, 82a66673242c -carol b6f9f8e1aab0 - -Administrative keys for my-image: -Repository Key: 27df2c8187e7543345c2e0bf3a1262e0bc63a72754e9a7395eac3f747ec23a44 -Root Key: 40b66ccc8b176be8c7d365a17f3e046d1c3494e053dd57cfeacfe2e19c4f8e8f -``` - -However, if other tags are signed in the same image repository, -`docker trust inspect` reports relevant key information. - -```console -$ docker trust inspect --pretty alpine:unsigned - -No signatures for alpine:unsigned - - -Administrative keys for alpine:unsigned: -Repository Key: 5a46c9aaa82ff150bb7305a2d17d0c521c2d784246807b2dc611f436a69041fd -Root Key: a2489bcac7a79aa67b19b96c4a3bf0c675ffdf00c6d2fabe1a5df1115e80adce -``` - -### Get details about signatures for all image tags in a repository - -```console -$ docker trust inspect --pretty alpine - -SIGNED TAG DIGEST SIGNERS -2.6 9ace551613070689a12857d62c30ef0daa9a376107ec0fff0e34786cedb3399b (Repo Admin) -2.7 9f08005dff552038f0ad2f46b8e65ff3d25641747d3912e3ea8da6785046561a (Repo Admin) -3.1 d9477888b78e8c6392e0be8b2e73f8c67e2894ff9d4b8e467d1488fcceec21c8 (Repo Admin) -3.2 19826d59171c2eb7e90ce52bfd822993bef6a6fe3ae6bb4a49f8c1d0a01e99c7 (Repo Admin) -3.3 8fd4b76819e1e5baac82bd0a3d03abfe3906e034cc5ee32100d12aaaf3956dc7 (Repo Admin) -3.4 833ad81ace8277324f3ca8c91c02bdcf1d13988d8ecf8a3f97ecdd69d0390ce9 (Repo Admin) -3.5 af2a5bd2f8de8fc1ecabf1c76611cdc6a5f1ada1a2bdd7d3816e121b70300308 (Repo Admin) -3.6 1072e499f3f655a032e88542330cf75b02e7bdf673278f701d7ba61629ee3ebe (Repo Admin) -edge 79d50d15bd7ea48ea00cf3dd343b0e740c1afaa8e899bee475236ef338e1b53b (Repo Admin) -latest 1072e499f3f655a032e88542330cf75b02e7bdf673278f701d7ba61629ee3ebe (Repo Admin) - -Administrative keys for alpine: -Repository Key: 5a46c9aaa82ff150bb7305a2d17d0c521c2d784246807b2dc611f436a69041fd -Root Key: a2489bcac7a79aa67b19b96c4a3bf0c675ffdf00c6d2fabe1a5df1115e80adce -``` - -Here's an example with signers that are set up by `docker trust` commands: - -```console -$ docker trust inspect --pretty my-image - -SIGNED TAG DIGEST SIGNERS -red 852cc04935f930a857b630edc4ed6131e91b22073bcc216698842e44f64d2943 alice -blue f1c38dbaeeb473c36716f6494d803fbfbe9d8a76916f7c0093f227821e378197 alice, bob -green cae8fedc840f90c8057e1c24637d11865743ab1e61a972c1c9da06ec2de9a139 alice, bob -yellow 9cc65fc3126790e683d1b92f307a71f48f75fa7dd47a7b03145a123eaf0b45ba carol -purple 941d3dba358621ce3c41ef67b47cf80f701ff80cdf46b5cc86587eaebfe45557 alice, bob, carol -orange d6c271baa6d271bcc24ef1cbd65abf39123c17d2e83455bdab545a1a9093fc1c alice - -List of signers and their keys for my-image: - -SIGNER KEYS -alice 47caae5b3e61, a85aab9d20a4 -bob 034370bcbd77, 82a66673242c -carol b6f9f8e1aab0 - -Administrative keys for my-image: -Repository Key: 27df2c8187e7543345c2e0bf3a1262e0bc63a72754e9a7395eac3f747ec23a44 -Root Key: 40b66ccc8b176be8c7d365a17f3e046d1c3494e053dd57cfeacfe2e19c4f8e8f -``` diff --git a/docs/reference/commandline/trust_key.md b/docs/reference/commandline/trust_key.md deleted file mode 100644 index eed149a3e891..000000000000 --- a/docs/reference/commandline/trust_key.md +++ /dev/null @@ -1,16 +0,0 @@ -# trust key - - -Manage keys for signing Docker images - -### Subcommands - -| Name | Description | -|:------------------------------------|:-------------------------------------| -| [`generate`](trust_key_generate.md) | Generate and load a signing key-pair | -| [`load`](trust_key_load.md) | Load a private key file for signing | - - - - - diff --git a/docs/reference/commandline/trust_key_generate.md b/docs/reference/commandline/trust_key_generate.md deleted file mode 100644 index 8e36f1b35729..000000000000 --- a/docs/reference/commandline/trust_key_generate.md +++ /dev/null @@ -1,52 +0,0 @@ -# trust key generate - - -Generate and load a signing key-pair - -### Options - -| Name | Type | Default | Description | -|:--------|:---------|:--------|:------------------------------------------------------------| -| `--dir` | `string` | | Directory to generate key in, defaults to current directory | - - - - -## Description - -`docker trust key generate` generates a key-pair to be used with signing, - and loads the private key into the local Docker trust keystore. - -## Examples - -### Generate a key-pair - -```console -$ docker trust key generate alice - -Generating key for alice... -Enter passphrase for new alice key with ID 17acf3c: -Repeat passphrase for new alice key with ID 17acf3c: -Successfully generated and loaded private key. Corresponding public key available: alice.pub -$ ls -alice.pub -``` - -The private signing key is encrypted by the passphrase and loaded into the Docker trust keystore. -All passphrase requests to sign with the key will be referred to by the provided `NAME`. - -The public key component `alice.pub` will be available in the current working directory, and can -be used directly by `docker trust signer add`. - -Provide the `--dir` argument to specify a directory to generate the key in: - -```console -$ docker trust key generate alice --dir /foo - -Generating key for alice... -Enter passphrase for new alice key with ID 17acf3c: -Repeat passphrase for new alice key with ID 17acf3c: -Successfully generated and loaded private key. Corresponding public key available: alice.pub -$ ls /foo -alice.pub -``` diff --git a/docs/reference/commandline/trust_key_load.md b/docs/reference/commandline/trust_key_load.md deleted file mode 100644 index f2e862736edf..000000000000 --- a/docs/reference/commandline/trust_key_load.md +++ /dev/null @@ -1,45 +0,0 @@ -# trust key load - - -Load a private key file for signing - -### Options - -| Name | Type | Default | Description | -|:---------|:---------|:---------|:------------------------| -| `--name` | `string` | `signer` | Name for the loaded key | - - - - -## Description - -`docker trust key load` adds private keys to the local Docker trust keystore. - -To add a signer to a repository use `docker trust signer add`. - -## Examples - -### Load a single private key - -For a private key `alice.pem` with permissions `-rw-------` - -```console -$ docker trust key load alice.pem - -Loading key from "alice.pem"... -Enter passphrase for new signer key with ID f8097df: -Repeat passphrase for new signer key with ID f8097df: -Successfully imported key from alice.pem -``` - -To specify a name use the `--name` flag: - -```console -$ docker trust key load --name alice-key alice.pem - -Loading key from "alice.pem"... -Enter passphrase for new alice-key key with ID f8097df: -Repeat passphrase for new alice-key key with ID f8097df: -Successfully imported key from alice.pem -``` diff --git a/docs/reference/commandline/trust_revoke.md b/docs/reference/commandline/trust_revoke.md deleted file mode 100644 index ed9efa52ab3d..000000000000 --- a/docs/reference/commandline/trust_revoke.md +++ /dev/null @@ -1,117 +0,0 @@ -# trust revoke - - -Remove trust for an image - -### Options - -| Name | Type | Default | Description | -|:--------------|:-------|:--------|:-------------------------------| -| `-y`, `--yes` | `bool` | | Do not prompt for confirmation | - - - - -## Description - -`docker trust revoke` removes signatures from tags in signed repositories. - -## Examples - -### Revoke signatures from a signed tag - -Here's an example of a repository with two signed tags: - - -```console -$ docker trust inspect --pretty example/trust-demo -SIGNED TAG DIGEST SIGNERS -red 852cc04935f930a857b630edc4ed6131e91b22073bcc216698842e44f64d2943 alice -blue f1c38dbaeeb473c36716f6494d803fbfbe9d8a76916f7c0093f227821e378197 alice, bob - -List of signers and their keys for example/trust-demo: - -SIGNER KEYS -alice 05e87edcaecb -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo: -Repository Key: ecc457614c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -When `alice`, one of the signers, runs `docker trust revoke`: - -```console -$ docker trust revoke example/trust-demo:red -Enter passphrase for delegation key with ID 27d42a8: -Successfully deleted signature for example/trust-demo:red -``` - -After revocation, the tag is removed from the list of released tags: - -```console -$ docker trust inspect --pretty example/trust-demo -SIGNED TAG DIGEST SIGNERS -blue f1c38dbaeeb473c36716f6494d803fbfbe9d8a76916f7c0093f227821e378197 alice, bob - -List of signers and their keys for example/trust-demo: - -SIGNER KEYS -alice 05e87edcaecb -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo: -Repository Key: ecc457614c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -### Revoke signatures on all tags in a repository - -When no tag is specified, `docker trust` revokes all signatures that you have a signing key for. - -```console -$ docker trust inspect --pretty example/trust-demo -SIGNED TAG DIGEST SIGNERS -red 852cc04935f930a857b630edc4ed6131e91b22073bcc216698842e44f64d2943 alice -blue f1c38dbaeeb473c36716f6494d803fbfbe9d8a76916f7c0093f227821e378197 alice, bob - -List of signers and their keys for example/trust-demo: - -SIGNER KEYS -alice 05e87edcaecb -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo: -Repository Key: ecc457614c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -When `alice`, one of the signers, runs `docker trust revoke`: - -```console -$ docker trust revoke example/trust-demo -Confirm you would like to delete all signature data for example/trust-demo? [y/N] y -Enter passphrase for delegation key with ID 27d42a8: -Successfully deleted signature for example/trust-demo -``` - -All tags that have `alice`'s signature on them are removed from the list of released tags: - -```console -$ docker trust inspect --pretty example/trust-demo - -No signatures for example/trust-demo - - -List of signers and their keys for example/trust-demo: - -SIGNER KEYS -alice 05e87edcaecb -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo: -Repository Key: ecc457614c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - diff --git a/docs/reference/commandline/trust_sign.md b/docs/reference/commandline/trust_sign.md deleted file mode 100644 index c8596d571931..000000000000 --- a/docs/reference/commandline/trust_sign.md +++ /dev/null @@ -1,177 +0,0 @@ -# trust sign - - -Sign an image - -### Options - -| Name | Type | Default | Description | -|:----------|:-------|:--------|:----------------------------| -| `--local` | `bool` | | Sign a locally tagged image | - - - - -## Description - -`docker trust sign` adds signatures to tags to create signed repositories. - -## Examples - -### Sign a tag as a repository admin - -Given an image: - -```console -$ docker trust inspect --pretty example/trust-demo - -SIGNED TAG DIGEST SIGNERS -v1 c24134c079c35e698060beabe110bb83ab285d0d978de7d92fed2c8c83570a41 (Repo Admin) - -Administrative keys for example/trust-demo: -Repository Key: 36d4c3601102fa7c5712a343c03b94469e5835fb27c191b529c06fd19c14a942 -Root Key: 246d360f7c53a9021ee7d4259e3c5692f3f1f7ad4737b1ea8c7b8da741ad980b -``` - -Sign a new tag with `docker trust sign`: - -```console -$ docker trust sign example/trust-demo:v2 - -Signing and pushing trust metadata for example/trust-demo:v2 -The push refers to a repository [docker.io/example/trust-demo] -eed4e566104a: Layer already exists -77edfb6d1e3c: Layer already exists -c69f806905c2: Layer already exists -582f327616f1: Layer already exists -a3fbb648f0bd: Layer already exists -5eac2de68a97: Layer already exists -8d4d1ab5ff74: Layer already exists -v2: digest: sha256:8f6f460abf0436922df7eb06d28b3cdf733d2cac1a185456c26debbff0839c56 size: 1787 -Signing and pushing trust metadata -Enter passphrase for repository key with ID 36d4c36: -Successfully signed docker.io/example/trust-demo:v2 -``` - -Use `docker trust inspect --pretty` to list the new signature: - -```console -$ docker trust inspect --pretty example/trust-demo - -SIGNED TAG DIGEST SIGNERS -v1 c24134c079c35e698060beabe110bb83ab285d0d978de7d92fed2c8c83570a41 (Repo Admin) -v2 8f6f460abf0436922df7eb06d28b3cdf733d2cac1a185456c26debbff0839c56 (Repo Admin) - -Administrative keys for example/trust-demo: -Repository Key: 36d4c3601102fa7c5712a343c03b94469e5835fb27c191b529c06fd19c14a942 -Root Key: 246d360f7c53a9021ee7d4259e3c5692f3f1f7ad4737b1ea8c7b8da741ad980b -``` - -### Sign a tag as a signer - -Given an image: - -```console -$ docker trust inspect --pretty example/trust-demo - -No signatures for example/trust-demo - - -List of signers and their keys for example/trust-demo: - -SIGNER KEYS -alice 05e87edcaecb -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo: -Repository Key: ecc457614c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -Sign a new tag with `docker trust sign`: - -```console -$ docker trust sign example/trust-demo:v1 - -Signing and pushing trust metadata for example/trust-demo:v1 -The push refers to a repository [docker.io/example/trust-demo] -26b126eb8632: Layer already exists -220d34b5f6c9: Layer already exists -8a5132998025: Layer already exists -aca233ed29c3: Layer already exists -e5d2f035d7a4: Layer already exists -v1: digest: sha256:74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 size: 1357 -Signing and pushing trust metadata -Enter passphrase for delegation key with ID 27d42a8: -Successfully signed docker.io/example/trust-demo:v1 -``` - -`docker trust inspect --pretty` lists the new signature: - -```console -$ docker trust inspect --pretty example/trust-demo - -SIGNED TAG DIGEST SIGNERS -v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 alice - -List of signers and their keys for example/trust-demo: - -SIGNER KEYS -alice 05e87edcaecb -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo: -Repository Key: ecc457614c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -## Initialize a new repository and sign a tag - -When signing an image on a repository for the first time, `docker trust sign` sets up new keys before signing the image. - -```console -$ docker trust inspect --pretty example/trust-demo - -no signatures or cannot access example/trust-demo -``` - -```console -$ docker trust sign example/trust-demo:v1 - -Signing and pushing trust metadata for example/trust-demo:v1 -Enter passphrase for root key with ID 36cac18: -Enter passphrase for new repository key with ID 731396b: -Repeat passphrase for new repository key with ID 731396b: -Enter passphrase for new alice key with ID 6d52b29: -Repeat passphrase for new alice key with ID 6d52b29: -Created signer: alice -Finished initializing "docker.io/example/trust-demo" -The push refers to a repository [docker.io/example/trust-demo] -eed4e566104a: Layer already exists -77edfb6d1e3c: Layer already exists -c69f806905c2: Layer already exists -582f327616f1: Layer already exists -a3fbb648f0bd: Layer already exists -5eac2de68a97: Layer already exists -8d4d1ab5ff74: Layer already exists -v1: digest: sha256:8f6f460abf0436922df7eb06d28b3cdf733d2cac1a185456c26debbff0839c56 size: 1787 -Signing and pushing trust metadata -Enter passphrase for alice key with ID 6d52b29: -Successfully signed docker.io/example/trust-demo:v1 -``` - -```console -$ docker trust inspect --pretty example/trust-demo - -SIGNED TAG DIGEST SIGNERS -v1 8f6f460abf0436922df7eb06d28b3cdf733d2cac1a185456c26debbff0839c56 alice - -List of signers and their keys for example/trust-demo: - -SIGNER KEYS -alice 6d52b29d940f - -Administrative keys for example/trust-demo: -Repository Key: 731396b65eac3ef5ec01406801bdfb70feb40c17808d2222427c18046eb63beb -Root Key: 70d174714bd1461f6c58cb3ef39087c8fdc7633bb11a98af844fd9a04e208103 -``` diff --git a/docs/reference/commandline/trust_signer.md b/docs/reference/commandline/trust_signer.md deleted file mode 100644 index 52aeda273faf..000000000000 --- a/docs/reference/commandline/trust_signer.md +++ /dev/null @@ -1,16 +0,0 @@ -# trust signer - - -Manage entities who can sign Docker images - -### Subcommands - -| Name | Description | -|:-----------------------------------|:----------------| -| [`add`](trust_signer_add.md) | Add a signer | -| [`remove`](trust_signer_remove.md) | Remove a signer | - - - - - diff --git a/docs/reference/commandline/trust_signer_add.md b/docs/reference/commandline/trust_signer_add.md deleted file mode 100644 index bd116b3db116..000000000000 --- a/docs/reference/commandline/trust_signer_add.md +++ /dev/null @@ -1,211 +0,0 @@ -# trust signer add - - -Add a signer - -### Options - -| Name | Type | Default | Description | -|:--------|:-------|:--------|:-------------------------------------| -| `--key` | `list` | | Path to the signer's public key file | - - - - -## Description - -`docker trust signer add` adds signers to signed repositories. - -## Examples - -### Add a signer to a repository - -To add a new signer, `alice`, to this repository: - -```console -$ docker trust inspect --pretty example/trust-demo - -No signatures for example/trust-demo - - -List of signers and their keys: - -SIGNER KEYS -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo: -Repository Key: 642692c14c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -Add `alice` with `docker trust signer add`: - -```console -$ docker trust signer add alice example/trust-demo --key alice.crt - Adding signer "alice" to example/trust-demo... - Enter passphrase for repository key with ID 642692c: -Successfully added signer: alice to example/trust-demo -``` - -`docker trust inspect --pretty` now lists `alice` as a valid signer: - -```console -$ docker trust inspect --pretty example/trust-demo - -No signatures for example/trust-demo - - -List of signers and their keys: - -SIGNER KEYS -alice 05e87edcaecb -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo: -Repository Key: 642692c14c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -## Initialize a new repository and add a signer - -When adding a signer on a repository for the first time, `docker trust signer add` sets up a new repository if it doesn't exist. - -```console -$ docker trust inspect --pretty example/trust-demo - -no signatures or cannot access example/trust-demo -``` - -```console -$ docker trust signer add alice example/trust-demo --key alice.crt - -Initializing signed repository for example/trust-demo... -Enter passphrase for root key with ID 748121c: -Enter passphrase for new repository key with ID 95b9e55: -Repeat passphrase for new repository key with ID 95b9e55: -Successfully initialized "example/trust-demo" - -Adding signer "alice" to example/trust-demo... -Successfully added signer: alice to example/trust-demo -``` - -```console -$ docker trust inspect --pretty example/trust-demo - -No signatures for example/trust-demo - - -SIGNED TAG DIGEST SIGNERS - -List of signers and their keys: - -SIGNER KEYS -alice 6d52b29d940f - -Administrative keys for example/trust-demo: -Repository Key: 95b9e5565eac3ef5ec01406801bdfb70feb40c17808d2222427c18046eb63beb -Root Key: 748121c14bd1461f6c58cb3ef39087c8fdc7633bb11a98af844fd9a04e208103 -``` - -## Add a signer to multiple repositories - -To add a signer, `alice`, to multiple repositories: - -```console -$ docker trust inspect --pretty example/trust-demo - -SIGNED TAG DIGEST SIGNERS -v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 bob - -List of signers and their keys: - -SIGNER KEYS -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo: -Repository Key: ecc457614c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -```console -$ docker trust inspect --pretty example/trust-demo2 - -SIGNED TAG DIGEST SIGNERS -v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 bob - -List of signers and their keys: - -SIGNER KEYS -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo2: -Repository Key: ece554f14c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4553d2ab20a8d9268 -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -Add `alice` to both repositories with a single `docker trust signer add` command: - -```console -$ docker trust signer add alice example/trust-demo example/trust-demo2 --key alice.crt - -Adding signer "alice" to example/trust-demo... -Enter passphrase for repository key with ID 95b9e55: -Successfully added signer: alice to example/trust-demo - -Adding signer "alice" to example/trust-demo2... -Enter passphrase for repository key with ID ece554f: -Successfully added signer: alice to example/trust-demo2 -``` - -`docker trust inspect --pretty` now lists `alice` as a valid signer of both `example/trust-demo` and `example/trust-demo2`: - - -```console -$ docker trust inspect --pretty example/trust-demo - -SIGNED TAG DIGEST SIGNERS -v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 bob - -List of signers and their keys: - -SIGNER KEYS -alice 05e87edcaecb -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo: -Repository Key: 95b9e5514c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -```console -$ docker trust inspect --pretty example/trust-demo2 - -SIGNED TAG DIGEST SIGNERS -v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 bob - -List of signers and their keys: - -SIGNER KEYS -alice 05e87edcaecb -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo2: -Repository Key: ece554f14c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4553d2ab20a8d9268 -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -`docker trust signer add` adds signers to repositories on a best effort basis. -It continues to add the signer to subsequent repositories if one attempt fails: - -```console -$ docker trust signer add alice example/unauthorized example/authorized --key alice.crt - -Adding signer "alice" to example/unauthorized... -you are not authorized to perform this operation: server returned 401. - -Adding signer "alice" to example/authorized... -Enter passphrase for repository key with ID c6772a0: -Successfully added signer: alice to example/authorized - -failed to add signer to: example/unauthorized -``` diff --git a/docs/reference/commandline/trust_signer_remove.md b/docs/reference/commandline/trust_signer_remove.md deleted file mode 100644 index 2b8d52772934..000000000000 --- a/docs/reference/commandline/trust_signer_remove.md +++ /dev/null @@ -1,171 +0,0 @@ -# trust signer remove - - -Remove a signer - -### Options - -| Name | Type | Default | Description | -|:----------------|:-------|:--------|:----------------------------------------------------------------------| -| `-f`, `--force` | `bool` | | Do not prompt for confirmation before removing the most recent signer | - - - - -## Description - -`docker trust signer remove` removes signers from signed repositories. - -## Examples - -### Remove a signer from a repository - -To remove an existing signer, `alice`, from this repository: - -```console -$ docker trust inspect --pretty example/trust-demo - -No signatures for example/trust-demo - - -List of signers and their keys: - -SIGNER KEYS -alice 05e87edcaecb -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo: -Repository Key: ecc457614c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -Remove `alice` with `docker trust signer remove`: - -```console -$ docker trust signer remove alice example/trust-demo - -Removing signer "alice" from image example/trust-demo... -Enter passphrase for repository key with ID 642692c: -Successfully removed alice from example/trust-demo -``` - -`docker trust inspect --pretty` now doesn't list `alice` as a valid signer: - -```console -$ docker trust inspect --pretty example/trust-demo - -No signatures for example/trust-demo - - -List of signers and their keys: - -SIGNER KEYS -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo: -Repository Key: ecc457614c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -### Remove a signer from multiple repositories - -To remove an existing signer, `alice`, from multiple repositories: - -```console -$ docker trust inspect --pretty example/trust-demo - -SIGNED TAG DIGEST SIGNERS -v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 alice, bob - -List of signers and their keys: - -SIGNER KEYS -alice 05e87edcaecb -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo: -Repository Key: 95b9e5514c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -```console -$ docker trust inspect --pretty example/trust-demo2 - -SIGNED TAG DIGEST SIGNERS -v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 alice, bob - -List of signers and their keys: - -SIGNER KEYS -alice 05e87edcaecb -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo2: -Repository Key: ece554f14c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4553d2ab20a8d9268 -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -Remove `alice` from both images with a single `docker trust signer remove` command: - -```console -$ docker trust signer remove alice example/trust-demo example/trust-demo2 - -Removing signer "alice" from image example/trust-demo... -Enter passphrase for repository key with ID 95b9e55: -Successfully removed alice from example/trust-demo - -Removing signer "alice" from image example/trust-demo2... -Enter passphrase for repository key with ID ece554f: -Successfully removed alice from example/trust-demo2 -``` - -Run `docker trust inspect --pretty` to confirm that `alice` is no longer listed as a valid -signer of either `example/trust-demo` or `example/trust-demo2`: - -```console -$ docker trust inspect --pretty example/trust-demo - -SIGNED TAG DIGEST SIGNERS -v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 bob - -List of signers and their keys: - -SIGNER KEYS -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo: -Repository Key: ecc457614c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -```console -$ docker trust inspect --pretty example/trust-demo2 - -SIGNED TAG DIGEST SIGNERS -v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 bob - -List of signers and their keys: - -SIGNER KEYS -bob 5600f5ab76a2 - -Administrative keys for example/trust-demo2: -Repository Key: ece554f14c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4553d2ab20a8d9268 -Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 -``` - -`docker trust signer remove` removes signers to repositories on a best effort basis. -It continues to remove the signer from subsequent repositories if one attempt fails: - -```console -$ docker trust signer remove alice example/unauthorized example/authorized - -Removing signer "alice" from image example/unauthorized... -No signer alice for image example/unauthorized - -Removing signer "alice" from image example/authorized... -Enter passphrase for repository key with ID c6772a0: -Successfully removed alice from example/authorized - -Error removing signer from: example/unauthorized -``` diff --git a/e2e/compose-env.yaml b/e2e/compose-env.yaml index df810df6cc9e..3c21ea2c62de 100644 --- a/e2e/compose-env.yaml +++ b/e2e/compose-env.yaml @@ -9,19 +9,3 @@ services: command: ['--insecure-registry=registry:5000', '--experimental'] environment: - DOCKER_TLS_CERTDIR= - - notary-server: - build: - context: ./testdata - dockerfile: Dockerfile.notary-server - ports: - - 4443:4443 - command: ['notary-server', '-config=/fixtures/notary-config.json'] - - evil-notary-server: - build: - context: ./testdata - dockerfile: Dockerfile.evil-notary-server - ports: - - 4444:4443 - command: ['notary-server', '-config=/fixtures/notary-config.json'] diff --git a/e2e/container/create_test.go b/e2e/container/create_test.go index daf8e092f601..7a4bf67ae771 100644 --- a/e2e/container/create_test.go +++ b/e2e/container/create_test.go @@ -1,107 +1,12 @@ package container import ( - "fmt" "testing" "github.com/docker/cli/e2e/internal/fixtures" - "github.com/docker/cli/internal/test/environment" "gotest.tools/v3/icmd" - "gotest.tools/v3/skip" ) -func TestCreateWithContentTrust(t *testing.T) { - skip.If(t, environment.RemoteDaemon()) - - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - image := fixtures.CreateMaskedTrustedRemoteImage(t, registryPrefix, "trust-create", "latest") - - defer func() { - icmd.RunCommand("docker", "image", "rm", image).Assert(t, icmd.Success) - }() - - result := icmd.RunCmd( - icmd.Command("docker", "create", image), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - ) - result.Assert(t, icmd.Expected{ - Err: fmt.Sprintf("Tagging %s@sha", image[:len(image)-7]), - }) -} - -func TestTrustedCreateFromUnreachableTrustServer(t *testing.T) { - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - image := fixtures.CreateMaskedTrustedRemoteImage(t, registryPrefix, "trust-create", "latest") - - result := icmd.RunCmd( - icmd.Command("docker", "create", image), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotaryServer("https://notary.invalid"), - ) - result.Assert(t, icmd.Expected{ - ExitCode: 1, - Err: "error contacting notary server", - }) -} - -func TestTrustedCreateFromBadTrustServer(t *testing.T) { - evilImageName := "registry:5000/evil-alpine:latest" - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - - // tag the image and upload it to the private registry - icmd.RunCmd(icmd.Command("docker", "tag", fixtures.AlpineImage, evilImageName), - fixtures.WithConfig(dir.Path()), - ).Assert(t, icmd.Success) - icmd.RunCmd(icmd.Command("docker", "image", "push", evilImageName), - fixtures.WithConfig(dir.Path()), - fixtures.WithPassphrase("root_password", "repo_password"), - fixtures.WithTrust, - fixtures.WithNotary, - ).Assert(t, icmd.Success) - icmd.RunCmd(icmd.Command("docker", "image", "rm", evilImageName)).Assert(t, icmd.Success) - - // try create - icmd.RunCmd(icmd.Command("docker", "create", evilImageName), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - ).Assert(t, icmd.Success) - icmd.RunCmd(icmd.Command("docker", "image", "rm", evilImageName)).Assert(t, icmd.Success) - - // init a client with the evil-server and a new trust dir - evilNotaryDir := fixtures.SetupConfigWithNotaryURL(t, "evil-test", fixtures.EvilNotaryURL) - defer evilNotaryDir.Remove() - - // tag the same image and upload it to the private registry but signed with evil notary server - icmd.RunCmd(icmd.Command("docker", "tag", fixtures.AlpineImage, evilImageName), - fixtures.WithConfig(evilNotaryDir.Path()), - ).Assert(t, icmd.Success) - icmd.RunCmd(icmd.Command("docker", "image", "push", evilImageName), - fixtures.WithConfig(evilNotaryDir.Path()), - fixtures.WithPassphrase("root_password", "repo_password"), - fixtures.WithTrust, - fixtures.WithNotaryServer(fixtures.EvilNotaryURL), - ).Assert(t, icmd.Success) - icmd.RunCmd(icmd.Command("docker", "image", "rm", evilImageName)).Assert(t, icmd.Success) - - // try creating with the original client from the evil notary server. This should failed - // because the new root is invalid - icmd.RunCmd(icmd.Command("docker", "create", evilImageName), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotaryServer(fixtures.EvilNotaryURL), - ).Assert(t, icmd.Expected{ - ExitCode: 1, - Err: "could not rotate trust to a new trusted root", - }) -} - func TestCreateWithEmptySourceVolume(t *testing.T) { icmd.RunCmd(icmd.Command("docker", "create", "-v", ":/volume", fixtures.AlpineImage)). Assert(t, icmd.Expected{ diff --git a/e2e/container/run_test.go b/e2e/container/run_test.go index d279f2521bf2..6a720757d4f8 100644 --- a/e2e/container/run_test.go +++ b/e2e/container/run_test.go @@ -2,7 +2,6 @@ package container import ( "bytes" - "fmt" "io" "math/rand" "os/exec" @@ -90,104 +89,6 @@ func TestRunInvalidEntrypointWithAutoremove(t *testing.T) { } } -func TestRunWithContentTrust(t *testing.T) { - skip.If(t, environment.RemoteDaemon()) - - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - image := fixtures.CreateMaskedTrustedRemoteImage(t, registryPrefix, "trust-run", "latest") - - defer func() { - icmd.RunCommand("docker", "image", "rm", image).Assert(t, icmd.Success) - }() - - result := icmd.RunCmd( - icmd.Command("docker", "run", image), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - ) - result.Assert(t, icmd.Expected{ - Err: fmt.Sprintf("Tagging %s@sha", image[:len(image)-7]), - }) -} - -func TestUntrustedRun(t *testing.T) { - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - image := registryPrefix + "/alpine:untrusted" - // tag the image and upload it to the private registry - icmd.RunCommand("docker", "tag", fixtures.AlpineImage, image).Assert(t, icmd.Success) - defer func() { - icmd.RunCommand("docker", "image", "rm", image).Assert(t, icmd.Success) - }() - - // try trusted run on untrusted tag - result := icmd.RunCmd( - icmd.Command("docker", "run", image), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - ) - result.Assert(t, icmd.Expected{ - ExitCode: 125, - Err: "does not have trust data for", - }) -} - -func TestTrustedRunFromBadTrustServer(t *testing.T) { - evilImageName := registryPrefix + "/evil-alpine:latest" - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - - // tag the image and upload it to the private registry - icmd.RunCmd(icmd.Command("docker", "tag", fixtures.AlpineImage, evilImageName), - fixtures.WithConfig(dir.Path()), - ).Assert(t, icmd.Success) - icmd.RunCmd(icmd.Command("docker", "image", "push", evilImageName), - fixtures.WithConfig(dir.Path()), - fixtures.WithPassphrase("root_password", "repo_password"), - fixtures.WithTrust, - fixtures.WithNotary, - ).Assert(t, icmd.Success) - icmd.RunCmd(icmd.Command("docker", "image", "rm", evilImageName)).Assert(t, icmd.Success) - - // try run - icmd.RunCmd(icmd.Command("docker", "run", evilImageName), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - ).Assert(t, icmd.Success) - icmd.RunCmd(icmd.Command("docker", "image", "rm", evilImageName)).Assert(t, icmd.Success) - - // init a client with the evil-server and a new trust dir - evilNotaryDir := fixtures.SetupConfigWithNotaryURL(t, "evil-test", fixtures.EvilNotaryURL) - defer evilNotaryDir.Remove() - - // tag the same image and upload it to the private registry but signed with evil notary server - icmd.RunCmd(icmd.Command("docker", "tag", fixtures.AlpineImage, evilImageName), - fixtures.WithConfig(evilNotaryDir.Path()), - ).Assert(t, icmd.Success) - icmd.RunCmd(icmd.Command("docker", "image", "push", evilImageName), - fixtures.WithConfig(evilNotaryDir.Path()), - fixtures.WithPassphrase("root_password", "repo_password"), - fixtures.WithTrust, - fixtures.WithNotaryServer(fixtures.EvilNotaryURL), - ).Assert(t, icmd.Success) - icmd.RunCmd(icmd.Command("docker", "image", "rm", evilImageName)).Assert(t, icmd.Success) - - // try running with the original client from the evil notary server. This should failed - // because the new root is invalid - icmd.RunCmd(icmd.Command("docker", "run", evilImageName), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotaryServer(fixtures.EvilNotaryURL), - ).Assert(t, icmd.Expected{ - ExitCode: 125, - Err: "could not rotate trust to a new trusted root", - }) -} - // TODO: create this with registry API instead of engine API func createRemoteImage(t *testing.T) string { t.Helper() diff --git a/e2e/global/cli_test.go b/e2e/global/cli_test.go index f79acd1aa76b..cf32f05b022f 100644 --- a/e2e/global/cli_test.go +++ b/e2e/global/cli_test.go @@ -90,7 +90,6 @@ func TestPromptExitCode(t *testing.T) { defaultCmdOpts := []icmd.CmdOp{ fixtures.WithConfig(dir.Path()), - fixtures.WithNotary, } testCases := []struct { @@ -132,13 +131,6 @@ func TestPromptExitCode(t *testing.T) { return icmd.Command("docker", "system", "prune") }, }, - { - name: "revoke trust", - run: func(t *testing.T) icmd.Cmd { - t.Helper() - return icmd.Command("docker", "trust", "revoke", "example/trust-demo") - }, - }, { name: "plugin install", run: func(t *testing.T) icmd.Cmd { diff --git a/e2e/image/build_test.go b/e2e/image/build_test.go index fadd13922406..08f4a5a7e9ec 100644 --- a/e2e/image/build_test.go +++ b/e2e/image/build_test.go @@ -14,7 +14,6 @@ import ( is "gotest.tools/v3/assert/cmp" "gotest.tools/v3/fs" "gotest.tools/v3/icmd" - "gotest.tools/v3/skip" ) func TestBuildFromContextDirectoryWithTag(t *testing.T) { @@ -62,68 +61,6 @@ func TestBuildFromContextDirectoryWithTag(t *testing.T) { }) } -func TestTrustedBuild(t *testing.T) { - skip.If(t, environment.RemoteDaemon()) - t.Setenv("DOCKER_BUILDKIT", "0") - - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - image1 := fixtures.CreateMaskedTrustedRemoteImage(t, registryPrefix, "trust-build1", "latest") - image2 := fixtures.CreateMaskedTrustedRemoteImage(t, registryPrefix, "trust-build2", "latest") - - buildDir := fs.NewDir(t, "test-trusted-build-context-dir", - fs.WithFile("Dockerfile", fmt.Sprintf(` - FROM %s as build-base - RUN echo ok > /foo - FROM %s - COPY --from=build-base foo bar - `, image1, image2))) - defer buildDir.Remove() - - result := icmd.RunCmd( - icmd.Command("docker", "build", "-t", "myimage", "."), - withWorkingDir(buildDir), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - ) - - result.Assert(t, icmd.Expected{ - Out: fmt.Sprintf("FROM %s@sha", image1[:len(image1)-7]), - Err: fmt.Sprintf("Tagging %s@sha", image1[:len(image1)-7]), - }) - result.Assert(t, icmd.Expected{ - Out: fmt.Sprintf("FROM %s@sha", image2[:len(image2)-7]), - }) -} - -func TestTrustedBuildUntrustedImage(t *testing.T) { - skip.If(t, environment.RemoteDaemon()) - t.Setenv("DOCKER_BUILDKIT", "0") - - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - buildDir := fs.NewDir(t, "test-trusted-build-context-dir", - fs.WithFile("Dockerfile", fmt.Sprintf(` - FROM %s - RUN [] - `, fixtures.AlpineImage))) - defer buildDir.Remove() - - result := icmd.RunCmd( - icmd.Command("docker", "build", "-t", "myimage", "."), - withWorkingDir(buildDir), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - ) - - result.Assert(t, icmd.Expected{ - ExitCode: 1, - Err: "does not have trust data for", - }) -} - func TestBuildIidFileSquash(t *testing.T) { environment.SkipIfNotExperimentalDaemon(t) t.Setenv("DOCKER_BUILDKIT", "0") diff --git a/e2e/image/pull_test.go b/e2e/image/pull_test.go index c739e7e5e410..d21cd4c0956a 100644 --- a/e2e/image/pull_test.go +++ b/e2e/image/pull_test.go @@ -4,81 +4,16 @@ import ( "testing" "github.com/docker/cli/e2e/internal/fixtures" - "github.com/docker/cli/internal/test/environment" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" - "gotest.tools/v3/golden" "gotest.tools/v3/icmd" - "gotest.tools/v3/skip" ) const registryPrefix = "registry:5000" -func TestPullWithContentTrust(t *testing.T) { - skip.If(t, environment.RemoteDaemon()) - - // Digests in golden files are linux/amd64 specific. - // TODO: Fix this test and make it work on all platforms. - environment.SkipIfNotPlatform(t, "linux/amd64") - - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - image := fixtures.CreateMaskedTrustedRemoteImage(t, registryPrefix, "trust-pull", "latest") - defer func() { - icmd.RunCommand("docker", "image", "rm", image).Assert(t, icmd.Success) - }() - - result := icmd.RunCmd(icmd.Command("docker", "pull", image), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - ) - result.Assert(t, icmd.Success) - golden.Assert(t, result.Stderr(), "pull-with-content-trust-err.golden") - golden.Assert(t, result.Stdout(), "pull-with-content-trust.golden") -} - func TestPullQuiet(t *testing.T) { result := icmd.RunCommand("docker", "pull", "--quiet", fixtures.AlpineImage) result.Assert(t, icmd.Success) assert.Check(t, is.Equal(result.Stdout(), "registry:5000/alpine:frozen\n")) assert.Check(t, is.Equal(result.Stderr(), "")) } - -func TestPullWithContentTrustUsesCacheWhenNotaryUnavailable(t *testing.T) { - skip.If(t, environment.RemoteDaemon()) - - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - image := fixtures.CreateMaskedTrustedRemoteImage(t, registryPrefix, "trust-pull-unreachable", "latest") - defer func() { - icmd.RunCommand("docker", "image", "rm", image).Assert(t, icmd.Success) - }() - result := icmd.RunCmd(icmd.Command("docker", "pull", image), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotaryServer("https://invalidnotaryserver"), - ) - result.Assert(t, icmd.Expected{ - ExitCode: 1, - Err: "error contacting notary server", - }) - - // Do valid trusted pull to warm cache - result = icmd.RunCmd(icmd.Command("docker", "pull", image), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - ) - result.Assert(t, icmd.Success) - result = icmd.RunCommand("docker", "rmi", image) - result.Assert(t, icmd.Success) - - // Try pull again with invalid notary server, should use cache - result = icmd.RunCmd(icmd.Command("docker", "pull", image), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotaryServer("https://invalidnotaryserver"), - ) - result.Assert(t, icmd.Success) -} diff --git a/e2e/image/push_test.go b/e2e/image/push_test.go index 3ee86c535189..5ba246749d5a 100644 --- a/e2e/image/push_test.go +++ b/e2e/image/push_test.go @@ -2,33 +2,15 @@ package image import ( "fmt" - "os" - "strings" "testing" "github.com/docker/cli/e2e/internal/fixtures" "github.com/docker/cli/internal/test/environment" "github.com/docker/cli/internal/test/output" - "gotest.tools/v3/assert" - "gotest.tools/v3/fs" - "gotest.tools/v3/golden" "gotest.tools/v3/icmd" "gotest.tools/v3/skip" ) -const ( - notary = "/usr/local/bin/notary" - - pubkey1 = "./testdata/notary/delgkey1.crt" - privkey1 = "./testdata/notary/delgkey1.key" - pubkey2 = "./testdata/notary/delgkey2.crt" - privkey2 = "./testdata/notary/delgkey2.key" - pubkey3 = "./testdata/notary/delgkey3.crt" - privkey3 = "./testdata/notary/delgkey3.key" - pubkey4 = "./testdata/notary/delgkey4.crt" - privkey4 = "./testdata/notary/delgkey4.key" -) - func TestPushAllTags(t *testing.T) { skip.If(t, environment.RemoteDaemon()) @@ -40,7 +22,6 @@ func TestPushAllTags(t *testing.T) { result := icmd.RunCmd(icmd.Command("docker", "push", "--all-tags", registryPrefix+"/push-all-tags")) result.Assert(t, icmd.Success) - golden.Assert(t, result.Stderr(), "push-with-content-trust-err.golden") output.Assert(t, result.Stdout(), map[int]func(string) error{ 0: output.Equals("The push refers to repository [registry:5000/push-all-tags]"), 1: output.Equals("7cd52847ad77: Preparing"), @@ -51,35 +32,6 @@ func TestPushAllTags(t *testing.T) { }) } -func TestPushWithContentTrust(t *testing.T) { - skip.If(t, environment.RemoteDaemon()) - - // Compared digests are linux/amd64 specific. - // TODO: Fix this test and make it work on all platforms. - environment.SkipIfNotPlatform(t, "linux/amd64") - - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - image := createImage(t, "trust-push", "latest") - - result := icmd.RunCmd(icmd.Command("docker", "push", image), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - fixtures.WithPassphrase("foo", "bar"), - ) - result.Assert(t, icmd.Success) - golden.Assert(t, result.Stderr(), "push-with-content-trust-err.golden") - output.Assert(t, result.Stdout(), map[int]func(string) error{ - 0: output.Equals("The push refers to repository [registry:5000/trust-push]"), - 1: output.Equals("7cd52847ad77: Preparing"), - 3: output.Equals("latest: digest: sha256:e2e16842c9b54d985bf1ef9242a313f36b856181f188de21313820e177002501 size: 528"), - 4: output.Equals("Signing and pushing trust metadata"), - 5: output.Equals(`Finished initializing "registry:5000/trust-push"`), - 6: output.Equals("Successfully signed registry:5000/trust-push:latest"), - }) -} - func TestPushQuietErrors(t *testing.T) { result := icmd.RunCmd(icmd.Command("docker", "push", "--quiet", "nosuchimage")) result.Assert(t, icmd.Expected{ @@ -88,240 +40,6 @@ func TestPushQuietErrors(t *testing.T) { }) } -func TestPushWithContentTrustUnreachableServer(t *testing.T) { - skip.If(t, environment.RemoteDaemon()) - - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - image := createImage(t, "trust-push-unreachable", "latest") - - result := icmd.RunCmd(icmd.Command("docker", "push", image), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotaryServer("https://invalidnotaryserver"), - ) - result.Assert(t, icmd.Expected{ - ExitCode: 1, - Err: "error contacting notary server", - }) -} - -func TestPushWithContentTrustExistingTag(t *testing.T) { - skip.If(t, environment.RemoteDaemon()) - - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - image := createImage(t, "trust-push-existing", "latest") - - result := icmd.RunCmd(icmd.Command("docker", "push", image)) - result.Assert(t, icmd.Success) - - result = icmd.RunCmd(icmd.Command("docker", "push", image), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - fixtures.WithPassphrase("foo", "bar"), - ) - result.Assert(t, icmd.Expected{ - Out: "Signing and pushing trust metadata", - }) - - // Re-push - result = icmd.RunCmd(icmd.Command("docker", "push", image), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - fixtures.WithPassphrase("foo", "bar"), - ) - result.Assert(t, icmd.Expected{ - Out: "Signing and pushing trust metadata", - }) -} - -func TestPushWithContentTrustReleasesDelegationOnly(t *testing.T) { - skip.If(t, environment.RemoteDaemon()) - - role := "targets/releases" - - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - copyPrivateKey(t, dir.Join("trust", "private"), privkey1) - notaryDir := setupNotaryConfig(t, dir) - defer notaryDir.Remove() - homeDir := fs.NewDir(t, "push_test_home") - defer notaryDir.Remove() - - baseRef := fmt.Sprintf("%s/%s", registryPrefix, "trust-push-releases-delegation") - targetRef := fmt.Sprintf("%s:%s", baseRef, "latest") - - // Init repository - notaryInit(t, notaryDir, homeDir, baseRef) - // Add delegation key (public key) - notaryAddDelegation(t, notaryDir, homeDir, baseRef, role, pubkey1) - // Publish it - notaryPublish(t, notaryDir, homeDir, baseRef) - // Import private key - notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, role, privkey1) - - // Tag & push with content trust - icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success) - icmd.RunCommand("docker", "tag", fixtures.AlpineImage, targetRef).Assert(t, icmd.Success) - result := icmd.RunCmd(icmd.Command("docker", "push", targetRef), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - fixtures.WithPassphrase("foo", "foo"), - ) - result.Assert(t, icmd.Expected{ - Out: "Signing and pushing trust metadata", - }) - - targetsInRole := notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, role) - assert.Assert(t, targetsInRole["latest"] == role, "%v", targetsInRole) - targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets") - assert.Assert(t, targetsInRole["latest"] != "targets", "%v", targetsInRole) - - result = icmd.RunCmd(icmd.Command("docker", "pull", targetRef), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - ) - result.Assert(t, icmd.Success) -} - -func TestPushWithContentTrustSignsAllFirstLevelRolesWeHaveKeysFor(t *testing.T) { - skip.If(t, environment.RemoteDaemon()) - - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - copyPrivateKey(t, dir.Join("trust", "private"), privkey1) - copyPrivateKey(t, dir.Join("trust", "private"), privkey2) - copyPrivateKey(t, dir.Join("trust", "private"), privkey3) - notaryDir := setupNotaryConfig(t, dir) - defer notaryDir.Remove() - homeDir := fs.NewDir(t, "push_test_home") - defer notaryDir.Remove() - - baseRef := fmt.Sprintf("%s/%s", registryPrefix, "trust-push-releases-first-roles") - targetRef := fmt.Sprintf("%s:%s", baseRef, "latest") - - // Init repository - notaryInit(t, notaryDir, homeDir, baseRef) - // Add delegation key (public key) - notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role1", pubkey1) - notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role2", pubkey2) - notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role3", pubkey3) - notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role1/subrole", pubkey3) - // Import private key - notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role1", privkey1) - notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role2", privkey2) - notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role1/subrole", privkey3) - // Publish it - notaryPublish(t, notaryDir, homeDir, baseRef) - - // Tag & push with content trust - icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success) - icmd.RunCommand("docker", "tag", fixtures.AlpineImage, targetRef).Assert(t, icmd.Success) - result := icmd.RunCmd(icmd.Command("docker", "push", targetRef), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - fixtures.WithPassphrase("foo", "foo"), - ) - result.Assert(t, icmd.Expected{ - Out: "Signing and pushing trust metadata", - }) - - // check to make sure that the target has been added to targets/role1 and targets/role2, and - // not targets (because there are delegations) or targets/role3 (due to missing key) or - // targets/role1/subrole (due to it being a second level delegation) - targetsInRole := notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets/role1") - assert.Assert(t, targetsInRole["latest"] == "targets/role1", "%v", targetsInRole) - targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets/role2") - assert.Assert(t, targetsInRole["latest"] == "targets/role2", "%v", targetsInRole) - targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets") - assert.Assert(t, targetsInRole["latest"] != "targets", "%v", targetsInRole) - - assert.NilError(t, os.RemoveAll(dir.Join("trust"))) - // Try to pull, should fail because non of these are the release role - // FIXME(vdemeester) should be unit test - result = icmd.RunCmd(icmd.Command("docker", "pull", targetRef), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - ) - result.Assert(t, icmd.Expected{ - ExitCode: 1, - }) -} - -func TestPushWithContentTrustSignsForRolesWithKeysAndValidPaths(t *testing.T) { - skip.If(t, environment.RemoteDaemon()) - - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - copyPrivateKey(t, dir.Join("trust", "private"), privkey1) - copyPrivateKey(t, dir.Join("trust", "private"), privkey2) - copyPrivateKey(t, dir.Join("trust", "private"), privkey3) - copyPrivateKey(t, dir.Join("trust", "private"), privkey4) - notaryDir := setupNotaryConfig(t, dir) - defer notaryDir.Remove() - homeDir := fs.NewDir(t, "push_test_home") - defer notaryDir.Remove() - - baseRef := fmt.Sprintf("%s/%s", registryPrefix, "trust-push-releases-keys-valid-paths") - targetRef := fmt.Sprintf("%s:%s", baseRef, "latest") - - // Init repository - notaryInit(t, notaryDir, homeDir, baseRef) - // Add delegation key (public key) - notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role1", pubkey1, "l", "z") - notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role2", pubkey2, "x", "y") - notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role3", pubkey3, "latest") - notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role4", pubkey4, "latest") - // Import private keys (except 3rd key) - notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role1", privkey1) - notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role2", privkey2) - notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role4", privkey4) - // Publish it - notaryPublish(t, notaryDir, homeDir, baseRef) - - // Tag & push with content trust - icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success) - icmd.RunCommand("docker", "tag", fixtures.AlpineImage, targetRef).Assert(t, icmd.Success) - result := icmd.RunCmd(icmd.Command("docker", "push", targetRef), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - fixtures.WithPassphrase("foo", "foo"), - ) - result.Assert(t, icmd.Expected{ - Out: "Signing and pushing trust metadata", - }) - - // check to make sure that the target has been added to targets/role1 and targets/role4, and - // not targets (because there are delegations) or targets/role2 (due to path restrictions) or - // targets/role3 (due to missing key) - targetsInRole := notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets/role1") - assert.Assert(t, targetsInRole["latest"] == "targets/role1", "%v", targetsInRole) - targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets/role4") - assert.Assert(t, targetsInRole["latest"] == "targets/role4", "%v", targetsInRole) - targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets") - assert.Assert(t, targetsInRole["latest"] != "targets", "%v", targetsInRole) - - assert.NilError(t, os.RemoveAll(dir.Join("trust"))) - // Try to pull, should fail because non of these are the release role - // FIXME(vdemeester) should be unit test - result = icmd.RunCmd(icmd.Command("docker", "pull", targetRef), - fixtures.WithConfig(dir.Path()), - fixtures.WithTrust, - fixtures.WithNotary, - ) - result.Assert(t, icmd.Expected{ - ExitCode: 1, - }) -} - func createImage(t *testing.T, repo string, tags ...string) string { t.Helper() icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success) @@ -332,106 +50,3 @@ func createImage(t *testing.T, repo string, tags ...string) string { } return fmt.Sprintf("%s/%s:%s", registryPrefix, repo, tags[0]) } - -//nolint:unparam -func withNotaryPassphrase(pwd string) func(*icmd.Cmd) { - return func(c *icmd.Cmd) { - c.Env = append(c.Env, []string{ - "NOTARY_ROOT_PASSPHRASE=" + pwd, - "NOTARY_TARGETS_PASSPHRASE=" + pwd, - "NOTARY_SNAPSHOT_PASSPHRASE=" + pwd, - "NOTARY_DELEGATION_PASSPHRASE=" + pwd, - }...) - } -} - -func notaryImportPrivateKey(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef, role, privkey string) { - t.Helper() - icmd.RunCmd( - icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "key", "import", privkey, "-g", baseRef, "-r", role), - withNotaryPassphrase("foo"), - fixtures.WithHome(homeDir.Path()), - ).Assert(t, icmd.Success) -} - -func notaryPublish(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef string) { - t.Helper() - icmd.RunCmd( - icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "publish", baseRef), - withNotaryPassphrase("foo"), - fixtures.WithHome(homeDir.Path()), - ).Assert(t, icmd.Success) -} - -func notaryAddDelegation(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef, role, pubkey string, paths ...string) { - t.Helper() - pathsArg := "--all-paths" - if len(paths) > 0 { - pathsArg = "--paths=" + strings.Join(paths, ",") - } - icmd.RunCmd( - icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "delegation", "add", baseRef, role, pubkey, pathsArg), - withNotaryPassphrase("foo"), - fixtures.WithHome(homeDir.Path()), - ).Assert(t, icmd.Success) -} - -func notaryInit(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef string) { - t.Helper() - icmd.RunCmd( - icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "init", baseRef), - withNotaryPassphrase("foo"), - fixtures.WithHome(homeDir.Path()), - ).Assert(t, icmd.Success) -} - -func notaryListTargetsInRole(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef, role string) map[string]string { - t.Helper() - result := icmd.RunCmd( - icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "list", baseRef, "-r", role), - fixtures.WithHome(homeDir.Path()), - ) - out := result.Combined() - - // should look something like: - // NAME DIGEST SIZE (BYTES) ROLE - // ------------------------------------------------------------------------------------------------------ - // latest 24a36bbc059b1345b7e8be0df20f1b23caa3602e85d42fff7ecd9d0bd255de56 1377 targets - - targets := make(map[string]string) - - // no target - lines := strings.Split(strings.TrimSpace(out), "\n") - if len(lines) == 1 && strings.Contains(out, "No targets present in this repository.") { - return targets - } - - // otherwise, there is at least one target - assert.Assert(t, len(lines) >= 3, "output is %s", out) - - for _, line := range lines[2:] { - tokens := strings.Fields(line) - assert.Assert(t, len(tokens) == 4) - targets[tokens[0]] = tokens[3] - } - - return targets -} - -func setupNotaryConfig(t *testing.T, dockerConfigDir fs.Dir) *fs.Dir { - t.Helper() - return fs.NewDir(t, "notary_test", fs.WithMode(0o700), - fs.WithFile("client-config.json", fmt.Sprintf(` -{ - "trust_dir": "%s", - "remote_server": { - "url": "%s" - } -}`, dockerConfigDir.Join("trust"), fixtures.NotaryURL)), - ) -} - -func copyPrivateKey(t *testing.T, dir, source string) { - t.Helper() - icmd.RunCommand("/bin/cp", source, dir).Assert(t, icmd.Success) -} diff --git a/e2e/image/testdata/notary/delgkey1.crt b/e2e/image/testdata/notary/delgkey1.crt deleted file mode 100644 index 2218f23c89b1..000000000000 --- a/e2e/image/testdata/notary/delgkey1.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDhTCCAm2gAwIBAgIJAP2EcMN2UXPcMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV -BAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMQ8wDQYD -VQQKEwZEb2NrZXIxEzARBgNVBAMTCmRlbGVnYXRpb24wHhcNMTYwOTI4MTc0ODQ4 -WhcNMjYwNjI4MTc0ODQ4WjBXMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFTAT -BgNVBAcTDFNhbkZyYW5jaXNjbzEPMA0GA1UEChMGRG9ja2VyMRMwEQYDVQQDEwpk -ZWxlZ2F0aW9uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvgewhaYs -Ke5s2AM7xxKrT4A6n7hW17qSnBjonCcPcwTFmYqIOdxWjYITgJuHrTwB4ZhBqWS7 -tTsUUu6hWLMeB7Uo5/GEQAAZspKkT9G/rNKF9lbWK9PPhGGkeR01c/Q932m92Hsn -fCQ0Pp/OzD3nVTh0v9HKk+PObNMOCcqG87eYs4ylPRxs0RrE/rP+bEGssKQSbeCZ -wazDnO+kiatVgKQZ2CK23iFdRE1z2rzqVDeaFWdvBqrRdWnkOZClhlLgEQ5nK2yV -B6tSqOiI3MmHyHzIkGOQJp2/s7Pe0ckEkzsjTsJW8oKHlBBl6pRxHIKzNN4VFbeB -vvYvrogrDrC/owIDAQABo1QwUjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIF -oDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUFoHfukRa6qGk1ncON64Z -ASKlZdkwDQYJKoZIhvcNAQELBQADggEBAEq9Adpd03CPmpbRtTAJGAkjjLFr60sV -2r+/l/m9R31ZCN9ymM9nxToQ8zfMdeAh/nnPcErziil2gDVqXueCNDkRj09tmDIE -Q1Oc92uyNZNgcECow77cKZCTZSTku+qsJrYaykH5vSnia8ltcKj8inJedIcpBR+p -608HEQvF0Eg5eaLPJwH48BCb0Gqdri1dJgrNnqptz7MDr8M+u7tHVulbAd3YxLlq -JH1W2bkVUx6esbn/MUE5HL5iTuOYREEINvBSmLdmmFkampmCnCB/bDEyJeL9bAkt -ZPIi0UNSnqFKLSP1Vf8AGLXt6iO7+1OGvtsDXEEYdXVOMsSXZtUuT7A= ------END CERTIFICATE----- diff --git a/e2e/image/testdata/notary/delgkey1.key b/e2e/image/testdata/notary/delgkey1.key deleted file mode 100644 index cb37efc94a44..000000000000 --- a/e2e/image/testdata/notary/delgkey1.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAvgewhaYsKe5s2AM7xxKrT4A6n7hW17qSnBjonCcPcwTFmYqI -OdxWjYITgJuHrTwB4ZhBqWS7tTsUUu6hWLMeB7Uo5/GEQAAZspKkT9G/rNKF9lbW -K9PPhGGkeR01c/Q932m92HsnfCQ0Pp/OzD3nVTh0v9HKk+PObNMOCcqG87eYs4yl -PRxs0RrE/rP+bEGssKQSbeCZwazDnO+kiatVgKQZ2CK23iFdRE1z2rzqVDeaFWdv -BqrRdWnkOZClhlLgEQ5nK2yVB6tSqOiI3MmHyHzIkGOQJp2/s7Pe0ckEkzsjTsJW -8oKHlBBl6pRxHIKzNN4VFbeBvvYvrogrDrC/owIDAQABAoIBAB/o8KZwsgfUhqh7 -WoViSCwQb0e0z7hoFwhpUl4uXPTGf1v6HEgDDPG0PwwgkdbwNaypQZVtWevj4NTQ -R326jjdjH1xbfQa2PZpz722L3jDqJR6plEtFxRoIv3KrCffPsrgabIu2mnnJJpDB -ixtW5cq0sT4ov2i4H0i85CWWwbSY/G/MHsvCuK9PhoCj9uToVqrf1KrAESE5q4fh -mPSYUL99KVnj7SZkUz+79rc8sLLPVks3szZACMlm1n05ZTj/d6Nd2ZZUO45DllIj -1XJghfWmnChrB/P/KYXgQ3Y9BofIAw1ra2y3wOZeqRFNsbmojcGldfdtN/iQzhEj -uk4ThokCgYEA9FTmv36N8qSPWuqX/KzkixDQ8WrDGohcB54kK98Wx4ijXx3i38SY -tFjO8YUS9GVo1+UgmRjZbzVX7xeum6+TdBBwOjNOxEQ4tzwiQBWDdGpli8BccdJ2 -OOIVxSslWhiUWfpYloXVetrR88iHbT882g795pbonDaJdXSLnij4UW8CgYEAxxrr -QFpsmOEZvI/yPSOGdG7A1RIsCeH+cEOf4cKghs7+aCtAHlIweztNOrqirl3oKI1r -I0zQl46WsaW8S/y99v9lmmnZbWwqLa4vIu0NWs0zaZdzKZw3xljMhgp4Ge69hHa2 -utCtAxcX+7q/yLlHoTiYwKdxX54iLkheCB8csw0CgYEAleEG820kkjXUIodJ2JwO -Tihwo8dEC6CeI6YktizRgnEVFqH0rCOjMO5Rc+KX8AfNOrK5PnD54LguSuKSH7qi -j04OKgWTSd43lF90+y63RtCFnibQDpp2HwrBJAQFk7EEP/XMJfnPLN/SbuMSADgM -kg8kPTFRW5Iw3DYz9z9WpE0CgYAkn6/8Q2XMbUOFqti9JEa8Lg8sYk5VdwuNbPMA -3QMYKQUk9ieyLB4c3Nik3+XCuyVUKEc31A5egmz3umu7cn8i6vGuiJ/k/8t2YZ7s -Bry5Ihu95Yzab5DW3Eiqs0xKQN79ebS9AluAwQO5Wy2h52rknfuDHIm/M+BHsSoS -xl5KFQKBgQCokCsYuX1z2GojHw369/R2aX3ovCGuHqy4k7fWxUrpHTHvth2+qNPr -84qLJ9rLWoZE5sUiZ5YdwCgW877EdfkT+v4aaBX79ixso5VdqgJ/PdnoNntah/Vq -njQiW1skn6/P5V/eyimN2n0VsyBr/zMDEtYTRP/Tb1zi/njFLQkZEA== ------END RSA PRIVATE KEY----- diff --git a/e2e/image/testdata/notary/delgkey2.crt b/e2e/image/testdata/notary/delgkey2.crt deleted file mode 100644 index bec084790a59..000000000000 --- a/e2e/image/testdata/notary/delgkey2.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDhTCCAm2gAwIBAgIJAIq8naKlYAQfMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV -BAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMQ8wDQYD -VQQKEwZEb2NrZXIxEzARBgNVBAMTCmRlbGVnYXRpb24wHhcNMTYwOTI4MTc0ODQ4 -WhcNMjYwNjI4MTc0ODQ4WjBXMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFTAT -BgNVBAcTDFNhbkZyYW5jaXNjbzEPMA0GA1UEChMGRG9ja2VyMRMwEQYDVQQDEwpk -ZWxlZ2F0aW9uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyY2EWYTW -5VHipw08t675upmD6a+akiuZ1z+XpuOxZCgjZ0aHfoOe8wGKg3Ohz7UCBdD5Mob/ -L/qvRlsCaqPHGZKIyyX1HDO4mpuQQFBhYxt+ZAO3AaawEUOw2rwwMDEjLnDDTSZM -z8jxCMvsJjBDqgb8g3z+AmjducQ/OH6llldgHIBY8ioRbROCL2PGgqywWq2fThav -c70YMxtKviBGDNCouYeQ8JMK/PuLwPNDXNQAagFHVARXiUv/ILHk7ImYnSGJUcuk -JTUGN2MBnpY0eakg7i+4za8sjjqOdn+2I6aVzlGJDSiRP72nkg/cE4BqMl9FrMwK -9iS8xa9yMDLUvwIDAQABo1QwUjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIF -oDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUvQzzFmh3Sv3HcdExY3wx -/1u6JLAwDQYJKoZIhvcNAQELBQADggEBAJcmDme2Xj/HPUPwaN/EyCmjhY73EiHO -x6Pm16tscg5JGn5A+u3CZ1DmxUYl8Hp6MaW/sWzdtL0oKJg76pynadCWh5EacFR8 -u+2GV/IcN9mSX6JQzvrqbjSqo5/FehqBD+W5h3euwwApWA3STAadYeyEfmdOA3SQ -W1vzrA1y7i8qgTqeJ7UX1sEAXlIhBK2zPYaMB+en+ZOiPyNxJYj6IDdGdD2paC9L -6H9wKC+GAUTSdCWp89HP7ETSXEGr94AXkrwU+qNsiN+OyK8ke0EMngEPh5IQoplw -/7zEZCth3oKxvR1/4S5LmTVaHI2ZlbU4q9bnY72G4tw8YQr2gcBGo4w= ------END CERTIFICATE----- diff --git a/e2e/image/testdata/notary/delgkey2.key b/e2e/image/testdata/notary/delgkey2.key deleted file mode 100644 index 5ccabe908fb1..000000000000 --- a/e2e/image/testdata/notary/delgkey2.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEAyY2EWYTW5VHipw08t675upmD6a+akiuZ1z+XpuOxZCgjZ0aH -foOe8wGKg3Ohz7UCBdD5Mob/L/qvRlsCaqPHGZKIyyX1HDO4mpuQQFBhYxt+ZAO3 -AaawEUOw2rwwMDEjLnDDTSZMz8jxCMvsJjBDqgb8g3z+AmjducQ/OH6llldgHIBY -8ioRbROCL2PGgqywWq2fThavc70YMxtKviBGDNCouYeQ8JMK/PuLwPNDXNQAagFH -VARXiUv/ILHk7ImYnSGJUcukJTUGN2MBnpY0eakg7i+4za8sjjqOdn+2I6aVzlGJ -DSiRP72nkg/cE4BqMl9FrMwK9iS8xa9yMDLUvwIDAQABAoIBAHmffvzx7ydESWwa -zcfdu26BkptiTvjjfJrqEd4wSewxWGPKqJqMXE8xX99A2KTZClZuKuH1mmnecQQY -iRXGrK9ewFMuHYGeKEiLlPlqR8ohXhyGLVm+t0JDwaXMp5t9G0i73O5iLTm5fNGd -FGxa9YnVW20Q8MqNczbVGH1D1zInhxzzOyFzBd4bBBJ8PdrUdyLpd7+RxY2ghnbT -p9ZANR2vk5zmDLJgZx72n/u+miJWuhY6p0v3Vq4z/HHgdhf+K6vpDdzTcYlA0rO4 -c/c+RKED3ZadGUD5QoLsmEN0e3FVSMPN1kt4ZRTqWfH8f2X4mLz33aBryTjktP6+ -1rX6ThECgYEA74wc1Tq23B5R0/GaMm1AK3Ko2zzTD8wK7NSCElh2dls02B+GzrEB -aE3A2GMQSuzb+EA0zkipwANBaqs3ZemH5G1pu4hstQsXCMd4jAJn0TmTXlplXBCf -PSc8ZUU6XcJENRr9Q7O9/TGlgahX+z0ndxYx/CMCsSu7XsMg4IZsbAcCgYEA12Vb -wKOVG15GGp7pMshr+2rQfVimARUP4gf3JnQmenktI4PfdnMW3a4L3DEHfLhIerwT -6lRp/NpxSADmuT4h1UO1l2lc+gmTVPw0Vbl6VwHpgS5Kfu4ZyM6n3S66f/dE4nu7 -hQF9yZz7vn5Agghak4p6a1wC1gdMzR1tvxFzk4kCgYByBMTskWfcWeok8Yitm+bB -R3Ar+kWT7VD97SCETusD5uG+RTNLSmEbHnc+B9kHcLo67YS0800pAeOvPBPARGnU -RmffRU5I1iB+o0MzkSmNItSMQoagTaEd4IEUyuC/I+qHRHNsOC+kRm86ycAm67LP -MhdUpe1wGxqyPjp15EXTHQKBgDKzFu+3EWfJvvKRKQ7dAh3BvKVkcl6a2Iw5l8Ej -YdM+JpPPfI/i8yTmzL/dgoem0Nii4IUtrWzo9fUe0TAVId2S/HFRSaNJEbbVTnRH -HjbQqmfPv5U08jjD+9siHp/0UfCFc1QRT8xe+RqTmReCY9+KntoaZEiAm2FEZgqt -TukRAoGAf7QqbTP5/UH1KSkX89F5qy/6GS3pw6TLj9Ufm/l/NO8Um8gag6YhEKWR -7HpkpCqjfWj8Av8ESR9cqddPGrbdqXFm9z7dCjlAd5T3Q3h/h+v+JzLQWbsI6WOb -SsOSWNyE006ZZdIiFwO6GfxpLI24sVtYKgyob6Q71oxSqfnrnT0= ------END RSA PRIVATE KEY----- diff --git a/e2e/image/testdata/notary/delgkey3.crt b/e2e/image/testdata/notary/delgkey3.crt deleted file mode 100644 index f434b45fc8e9..000000000000 --- a/e2e/image/testdata/notary/delgkey3.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDhTCCAm2gAwIBAgIJAKHt/jxiWqMtMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV -BAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMQ8wDQYD -VQQKEwZEb2NrZXIxEzARBgNVBAMTCmRlbGVnYXRpb24wHhcNMTYwOTI4MTc0ODQ5 -WhcNMjYwNjI4MTc0ODQ5WjBXMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFTAT -BgNVBAcTDFNhbkZyYW5jaXNjbzEPMA0GA1UEChMGRG9ja2VyMRMwEQYDVQQDEwpk -ZWxlZ2F0aW9uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqfbJk2Dk -C9FJVjV2+Q2CQrJphG3vFc1Qlu9jgVA5RhGmF9jJzetsclsV/95nBhinIGcSmPQA -l318G7Bz/cG/6O2n5+hj+S1+YOvQweReZj3d4kCeS86SOyLNTpMD9gsF0S8nR1RN -h0jD4t1vxAVeGD1o61U8/k0O5eDoeOfOSWZagKk5PhyrMZgNip4IrG46umCkFlrw -zMMcgQdwTQXywPqkr/LmYpqT1WpMlzHYTQEY8rKorIJQbPtHVYdr4UxYnNmk6fbU -biEP1DQlwjBWcFTsDLqXKP/K+e3O0/e/hMB0y7Tj9fZ7Viw0t5IKXZPsxMhwknUT -9vmPzIJO6NiniwIDAQABo1QwUjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIF -oDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUdTXRP1EzxQ+UDZSoheVo -Mobud1cwDQYJKoZIhvcNAQELBQADggEBADV9asTWWdbmpkeRuKyi0xGho39ONK88 -xxkFlco766BVgemo/rGQj3oPuw6M6SzHFoJ6JUPjmLiAQDIGEU/2/b6LcOuLjP+4 -YejCcDTY3lSW/HMNoAmzr2foo/LngNGfe/qhVFUqV7GjFT9+XzFFBfIZ1cQiL2ed -kc8rgQxFPwWXFCSwaENWeFnMDugkd+7xanoAHq8GsJpg5fTruDTmJkUqC2RNiMLn -WM7QaqW7+lmUnMnc1IBoz0hFhgoiadWM/1RQxx51zTVw6Au1koIm4ZXu5a+/WyC8 -K1+HyUbc0AVaDaRBpRSOR9aHRwLGh6WQ4aUZQNyJroc999qfYrDEEV8= ------END CERTIFICATE----- diff --git a/e2e/image/testdata/notary/delgkey3.key b/e2e/image/testdata/notary/delgkey3.key deleted file mode 100644 index a61d18cc3d48..000000000000 --- a/e2e/image/testdata/notary/delgkey3.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpQIBAAKCAQEAqfbJk2DkC9FJVjV2+Q2CQrJphG3vFc1Qlu9jgVA5RhGmF9jJ -zetsclsV/95nBhinIGcSmPQAl318G7Bz/cG/6O2n5+hj+S1+YOvQweReZj3d4kCe -S86SOyLNTpMD9gsF0S8nR1RNh0jD4t1vxAVeGD1o61U8/k0O5eDoeOfOSWZagKk5 -PhyrMZgNip4IrG46umCkFlrwzMMcgQdwTQXywPqkr/LmYpqT1WpMlzHYTQEY8rKo -rIJQbPtHVYdr4UxYnNmk6fbUbiEP1DQlwjBWcFTsDLqXKP/K+e3O0/e/hMB0y7Tj -9fZ7Viw0t5IKXZPsxMhwknUT9vmPzIJO6NiniwIDAQABAoIBAQCAr/ed3A2umO7T -FDYZik3nXBiiiW4t7r+nGGgZ3/kNgY1lnuHlROxehXLZwbX1mrLnyML/BjhwezV9 -7ZNVPd6laVPpNj6DyxtWHRZ5yARlm1Al39E7CpQTrF0QsiWcpGnqIa62xjDRTpnq -askV/Q5qggyvqmE9FnFCQpEiAjlhvp7F0kVHVJm9s3MK3zSyR0UTZ3cpYus2Jr2z -OotHgAMHq5Hgb3dvxOeE2xRMeYAVDujbkNzXm2SddAtiRdLhWDh7JIr3zXhp0HyN -4rLOyhlgz00oIGeDt/C0q3fRmghr3iZOG+7m2sUx0FD1Ru1dI9v2A+jYmIVNW6+x -YJk5PzxJAoGBANDj7AGdcHSci/LDBPoTTUiz3uucAd27/IJma/iy8mdbVfOAb0Fy -PRSPvoozlpZyOxg2J4eH/o4QxQR4lVKtnLKZLNHK2tg3LarwyBX1LiI3vVlB+DT1 -AmV8i5bJAckDhqFeEH5qdWZFi03oZsSXWEqX5iMYCrdK5lTZggcrFZeHAoGBANBL -fkk3knAdcVfTYpmHx18GBi2AsCWTd20KD49YBdbVy0Y2Jaa1EJAmGWpTUKdYx40R -H5CuGgcAviXQz3bugdTU1I3tAclBtpJNU7JkhuE+Epz0CM/6WERJrE0YxcGQA5ui -6fOguFyiXD1/85jrDBOKy74aoS7lYz9r/a6eqmjdAoGBAJpm/nmrIAZx+Ff2ouUe -A1Ar9Ch/Zjm5zEmu3zwzOU4AiyWz14iuoktifNq2iyalRNz+mnVpplToPFizsNwu -C9dPtXtU0DJlhtIFrD/evLz6KnGhe4/ZUm4lgyBvb2xfuNHqL5Lhqelwmil6EQxb -Oh3Y7XkfOjyFln89TwlxZUJdAoGAJRMa4kta7EvBTeGZLjyltvsqhFTghX+vBSCC -ToBbYbbiHJgssXSPAylU4sD7nR3HPwuqM6VZip+OOMrm8oNXZpuPTce+xqTEq1vK -JvmPrG3RAFDLdMFZjqYSXhKnuGE60yv3Ol8EEbDwfB3XLQPBPYU56Jdy0xcPSE2f -dMJXEJ0CgYEAisZw0nXw6lFeYecu642EGuU0wv1O9i21p7eho9QwOcsoTl4Q9l+M -M8iBv+qTHO+D19l4JbkGvy2H2diKoYduUFACcuiFYs8fjrT+4Z6DyOQAQGAf6Ylw -BFbU15k6KbA9v4mZDfd1tY9x62L/XO55ZxYG+J+q0e26tEThgD8cEog= ------END RSA PRIVATE KEY----- diff --git a/e2e/image/testdata/notary/delgkey4.crt b/e2e/image/testdata/notary/delgkey4.crt deleted file mode 100644 index c8cbe46bdfa6..000000000000 --- a/e2e/image/testdata/notary/delgkey4.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDhTCCAm2gAwIBAgIJANae++ZkUEWMMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV -BAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMQ8wDQYD -VQQKEwZEb2NrZXIxEzARBgNVBAMTCmRlbGVnYXRpb24wHhcNMTYwOTI4MTc0ODQ5 -WhcNMjYwNjI4MTc0ODQ5WjBXMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFTAT -BgNVBAcTDFNhbkZyYW5jaXNjbzEPMA0GA1UEChMGRG9ja2VyMRMwEQYDVQQDEwpk -ZWxlZ2F0aW9uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqULAjgba -Y2I10WfqdmYnPfEqEe6iMDbzcgECb2xKafXcI4ltkQj1iO4zBTs0Ft9EzXFc5ZBh -pTjZrL6vrIa0y/CH2BiIHBJ0wRHx/40HXp4DSj3HZpVOlEMI3npRfBGNIBllUaRN -PWG7zL7DcKMIepBfPXyjBsxzH3yNiISq0W5hSiy+ImhSo3aipJUHHcp9Z9NgvpNC -3QvnxsGKRnECmDRDlxkq+FQu9Iqs/HWFYWgyfcsw+YTrWZq3qVnnqUouHO//c9PG -Ry3sZSDU97MwvkjvWys1e01Xvd3AbHx08YAsxih58i/OBKe81eD9NuZDP2KrjTxI -5xkXKhj6DV2NnQIDAQABo1QwUjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIF -oDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUDt95hiqbQvi0KcvZGAUu -VisnztQwDQYJKoZIhvcNAQELBQADggEBAGi7qHai7MWbfeu6SlXhzIP3AIMa8TMi -lp/+mvPUFPswIVqYJ71MAN8uA7CTH3z50a2vYupGeOEtZqVJeRf+xgOEpwycncxp -Qz6wc6TWPVIoT5q1Hqxw1RD2MyKL+Y+QBDYwFxFkthpDMlX48I9frcqoJUWFxBF2 -lnRr/cE7BbPE3sMbXV3wGPlH7+eUf+CgzXJo2HB6THzagyEgNrDiz/0rCQa1ipFd -mNU3D/U6BFGmJNxhvSOtXX9escg8yjr05YwwzokHS2K4jE0ZuJPBd50C/Rvo3Mf4 -0h7/2Q95e7d42zPe9WYPu2F8KTWsf4r+6ddhKrKhYzXIcTAfHIOiO+U= ------END CERTIFICATE----- diff --git a/e2e/image/testdata/notary/delgkey4.key b/e2e/image/testdata/notary/delgkey4.key deleted file mode 100644 index f473cc495a72..000000000000 --- a/e2e/image/testdata/notary/delgkey4.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAqULAjgbaY2I10WfqdmYnPfEqEe6iMDbzcgECb2xKafXcI4lt -kQj1iO4zBTs0Ft9EzXFc5ZBhpTjZrL6vrIa0y/CH2BiIHBJ0wRHx/40HXp4DSj3H -ZpVOlEMI3npRfBGNIBllUaRNPWG7zL7DcKMIepBfPXyjBsxzH3yNiISq0W5hSiy+ -ImhSo3aipJUHHcp9Z9NgvpNC3QvnxsGKRnECmDRDlxkq+FQu9Iqs/HWFYWgyfcsw -+YTrWZq3qVnnqUouHO//c9PGRy3sZSDU97MwvkjvWys1e01Xvd3AbHx08YAsxih5 -8i/OBKe81eD9NuZDP2KrjTxI5xkXKhj6DV2NnQIDAQABAoIBAGK0ZKnuYSiXux60 -5MvK4pOCsa/nY3mOcgVHhW4IzpRgJdIrcFOlz9ncXrBsSAIWjX7o3u2Ydvjs4DOW -t8d6frB3QiDInYcRVDjLCD6otWV97Bk9Ua0G4N4hAWkMF7ysV4oihS1JDSoAdo39 -qOdki6s9yeyHZGKwk2oHLlowU5TxQMBA8DHmxqBII1HTm+8xRz45bcEqRXydYSUn -P1JuSU9jFqdylxU+Nrq6ehslMQ3y7qNWQyiLGxu6EmR+vgrzSU0s3iAOqCHthaOS -VBBXPL3DNEYUS+0QGnGrACuJhanOMBfdiO6Orelx6ZzWZm38PNGv0yBt0WCM+8/A -TtQNGkECgYEA1LqR6AH9XikUQ0+rM4526BgVuYqtjw21h4Lj9alaA+YTQntBBJOv -iAcUpnJiV4T8jzAMLeqpK8R/rbxRnK5S9jOV2gr+puk4L6tH46cgahBUESDigDp8 -6vK8ur6ubBcXNPh3AT6rsPj+Ph2EU3raqiYdouvCdga/OCYZb+jr6UkCgYEAy7Cr -l8WssI/8/ORcQ4MFJFNyfz/Y2beNXyLd1PX0H+wRSiGcKzeUuTHNtzFFpMbrK/nx -ZOPCT2ROdHsBHzp1L+WquCb0fyMVSiYiXBU+VCFDbUU5tBr3ycTc7VwuFPENOiha -IdlWgew/aW110FQHIaqe9g+htRe+mXe++faZtbUCgYB/MSJmNzJX53XvHSZ/CBJ+ -iVAMBSfq3caJRLCqRNzGcf1YBbwFUYxlZ95n+wJj0+byckcF+UW3HqE8rtmZNf3y -qTtTCLnj8JQgpGeybU4LPMIXD7N9+fqQvBwuCC7gABpnGJyHCQK9KNNTLnDdPRqb -G3ki3ZYC3dvdZaJV8E2FyQKBgQCMa5Mf4kqWvezueo+QizZ0QILibqWUEhIH0AWV -1qkhiKCytlDvCjYhJdBnxjP40Jk3i+t6XfmKud/MNTAk0ywOhQoYQeKz8v+uSnPN -f2ekn/nXzq1lGGJSWsDjcXTjQvqXaVIZm7cjgjaE+80IfaUc9H75qvUT3vaq3f5u -XC7DMQKBgQDMAzCCpWlEPbZoFMl6F49+7jG0/TiqM/WRUSQnNtufPMbrR9Je4QM1 -L1UCANCPaHFOncKYer15NfIV1ctt5MZKImevDsUaQO8CUlO+dzd5H8KvHw9E29gA -B22v8k3jIjsYeRL+UJ/sBnWHgxdAe/NEM+TdlP2oP9D1gTifutPqAg== ------END RSA PRIVATE KEY----- diff --git a/e2e/image/testdata/notary/gen.sh b/e2e/image/testdata/notary/gen.sh deleted file mode 100755 index 8d6381cec4cf..000000000000 --- a/e2e/image/testdata/notary/gen.sh +++ /dev/null @@ -1,18 +0,0 @@ -for selfsigned in delgkey1 delgkey2 delgkey3 delgkey4; do - subj='/C=US/ST=CA/L=SanFrancisco/O=Docker/CN=delegation' - - openssl genrsa -out "${selfsigned}.key" 2048 - openssl req -new -key "${selfsigned}.key" -out "${selfsigned}.csr" -sha256 -subj "${subj}" - cat > "${selfsigned}.cnf" <> notary-server.cert - mv ca.pem root-ca.cert - cp notary-server.cert notary-server.key root-ca.cert ../notary-evil - cp -r /tmp/gencerts/notary* /out/ -EOT - -FROM scratch -COPY --from=generated /out / diff --git a/e2e/testdata/Dockerfile.notary-server b/e2e/testdata/Dockerfile.notary-server deleted file mode 100644 index 846253e2fba6..000000000000 --- a/e2e/testdata/Dockerfile.notary-server +++ /dev/null @@ -1,7 +0,0 @@ -# syntax=docker/dockerfile:1 - -ARG NOTARY_VERSION=0.6.1 - -FROM notary:server-${NOTARY_VERSION} - -COPY ./notary/ /fixtures/ diff --git a/e2e/testdata/notary-evil/notary-config.json b/e2e/testdata/notary-evil/notary-config.json deleted file mode 100644 index f3345c080a31..000000000000 --- a/e2e/testdata/notary-evil/notary-config.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "server": { - "http_addr": "evil-notary-server:4444", - "tls_key_file": "./notary-server.key", - "tls_cert_file": "./notary-server.cert" - }, - "trust_service": { - "type": "local", - "hostname": "", - "port": "", - "key_algorithm": "ed25519" - }, - "logging": { - "level": "debug" - }, - "storage": { - "backend": "memory" - } -} diff --git a/e2e/testdata/notary-evil/notary-server.cert b/e2e/testdata/notary-evil/notary-server.cert deleted file mode 100644 index eee2b3463d0d..000000000000 --- a/e2e/testdata/notary-evil/notary-server.cert +++ /dev/null @@ -1,40 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIQTujwx+1xxXeI5AbzAQ379TANBgkqhkiG9w0BAQsFADAi -MQ8wDQYDVQQKEwZEb2NrZXIxDzANBgNVBAMTBkRvY2tlcjAeFw0yMzAzMjcxMTA5 -NTBaFw0zMzAzMjQxMTA5NTBaMCkxDzANBgNVBAoTBkRvY2tlcjEWMBQGA1UEAxMN -bm90YXJ5LXNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPhZ -pU7DRK/2nwbTu+kVYhU/XARDleVSiLrQ5RMR1Cz2xC4LWkOEVSj4aCBo85O66JAx -p+WRVwoVEU2rdkK3k4983Xr34+7q5Hv4hmwlg6I7QLRRJapEgK5G5RB/9aQntolx -h5E0KaoF4PJP25y4FHCUr4td4QyitaICsCpuOAN6XgmE9sM9TBf+AEjTSxwwvgEz -DqHvyovl7pA+pQP2oTKBrf6KN8hHDOXmm9gd8ST9yKLrsYWhqExLLPnAD4lQEcKZ -29g+iTd4eNoJUXctpuY+3IpqBcQSLq35mNKBP/FQco6g3q26/cB4zWGxTr3jGJqs -ms8qdFLGZ2KiBCt+oDMCAwEAAaOBoTCBnjAOBgNVHQ8BAf8EBAMCBaAwDAYDVR0T -AQH/BAIwADAfBgNVHSMEGDAWgBTxYMNqgy2wkgmPZL/+bTCTQo6ulTBdBgNVHREE -VjBUgg1ub3Rhcnktc2VydmVyggxub3RhcnlzZXJ2ZXKCEmV2aWwtbm90YXJ5LXNl -cnZlcoIQZXZpbG5vdGFyeXNlcnZlcoIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3 -DQEBCwUAA4IBAQCDMjuZnNINFfqstFwhEEvAgWbjYW26ZQxhQffaqDlbMIQkWoXj -8inld9bma9Mf5i/GAkUwFqCnEHD4LQ6vDgfAgL+pSOv9VI5SBEuk/gLqvIBUeIRu -uHo1jWtll2Fr7eDLVdD4mPRPFC7V6mv6sFa9EN4tBN8eheQxHJvzwnnU7X28prfI -/hWnwPWScVvttqBSsq1h2CUpVu2zGVToeCJ9xl4r/NyDtM5TyMgz7RLrer0p8NSu -4Qp4ZXtxHDLduWcyMUHLGTprW05yjj9UVq89xfaCOqFSpx5i4oxotYm1PoOacHmN -RMp9vaYMAmopoxIEYX6fDg5T3sQ5cidZJEvU ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDEDCCAfigAwIBAgIQdxGVILXsVcogexr+Ia2MZDANBgkqhkiG9w0BAQsFADAi -MQ8wDQYDVQQKEwZEb2NrZXIxDzANBgNVBAMTBkRvY2tlcjAeFw0yMzAzMjcxMTA5 -NTBaFw0zMzAzMjQxMTA5NTBaMCIxDzANBgNVBAoTBkRvY2tlcjEPMA0GA1UEAxMG -RG9ja2VyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq3sA/g7Srrkz -uEf1Qa2jAw93EfEJvxU1ZmZ30aB7KLgHN2TznxAGYtNekAu88CV4H3PKS44BZOar -wOo3KL4wQffLt7lmsRJG1KOfyiAmjmvidP5JSeRdTiBtj4CCVoi3EE6BZXPpZjst -9OSOlld2bWWXHb2ZdoY3ZAhZ9rn3tVwyfoLKpuESp1WZSFHPIdcuoMmZPtqD0bSi -5hc4gVFNLlZOAILvUkXxcHKUgLHZg0YEDQWsYjqh8EYp5LUK2tt4Mpz0HwAt9siE -VxHGIsiEqG1ajmxZiS28nlRWc4JRlOdmy5x1TPzJTDy+49gxB4njp1nRUtUgzmaG -QHhml35xHQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAqQwDwYDVR0TAQH/BAUwAwEB -/zAdBgNVHQ4EFgQU8WDDaoMtsJIJj2S//m0wk0KOrpUwDQYJKoZIhvcNAQELBQAD -ggEBAKZJfQjjfqn0K/UlzmrGcRkhrLbJNUfCD6TvxD75MoGtEe+VUEjljm1JHSbj -DrevDyTnak1W4o5/dcy0h6kI6lhHgObbcoAV5CxQ4+HHmeowA/fzedbnIdnHwtNg -SUJEslqoJSiYiiFQLV/yWWfBCWpbIgpDrADU7x9Ccxt6INuxrxOQwf1LZnmVbYs0 -1Mb/O1UFnvW7MeVSR4Nb/4lw6lol+mrR1iF8tTQ+rk4sBdCxw2aU48x3Pjqm+XpV -PIm9uRUr4tRDyQfmBZuxWTNJ9NSx5zVpLEPhDmyOW5wlSw+aKGscu9+RjBx/gXPk -sK8jZi441ojEJ7OaggGPheO3mCU= ------END CERTIFICATE----- diff --git a/e2e/testdata/notary-evil/notary-server.key b/e2e/testdata/notary-evil/notary-server.key deleted file mode 100644 index 7f7562a9defd..000000000000 --- a/e2e/testdata/notary-evil/notary-server.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEA+FmlTsNEr/afBtO76RViFT9cBEOV5VKIutDlExHULPbELgta -Q4RVKPhoIGjzk7rokDGn5ZFXChURTat2QreTj3zdevfj7urke/iGbCWDojtAtFEl -qkSArkblEH/1pCe2iXGHkTQpqgXg8k/bnLgUcJSvi13hDKK1ogKwKm44A3peCYT2 -wz1MF/4ASNNLHDC+ATMOoe/Ki+XukD6lA/ahMoGt/oo3yEcM5eab2B3xJP3Iouux -haGoTEss+cAPiVARwpnb2D6JN3h42glRdy2m5j7cimoFxBIurfmY0oE/8VByjqDe -rbr9wHjNYbFOveMYmqyazyp0UsZnYqIEK36gMwIDAQABAoIBAQDy7W2f763+mbTQ -zshepQX+Vq3BlgLIAMWyR6fr0WLEYNVhXMV8ibNrkiD4ovCwLwJSGeBr1JFZUWZN -nUze0gdLMg7LvDN/ftDk2yNSIhfy1xbhywaW2M8uqjZiv2genKIXK7A6PtYKdBmn -rKnbUMzdmvNj1f7Ph1E4Gn0L5ChybJDJrq6wQjuTdZ6RmkGkbid0L+47Uv+6xBm9 -hgBPVXd8auQAYGmyXZwvfga5ZjfRMI4wvWkvjOAQcJtxxgOnLT1KDjYV+L70PWul -bYoKX0sNkFEP9tOq2pD9XVBuTVQxcYeztv0Vz+kG66Ju1KKCAnUYFhRt055zZLfm -WDYlWm0BAoGBAPvGW9LvzwCDE9QUcR46nG1ZihheJyGKwWVK+ZjYkUU9nLbrIpOD -/jmihoHHhKBC6YOfHHY73LtZ22fgXEu6ivDzZtTxBErXbdRpEKktJebRK7gPkfsB -PNQ8CRd/DxRC/JuVFR76OPsbZWhXCaeC7PRdyAtvU9toT1jIQf+a4OhBAoGBAPyE -kxEoNO1KhWtgByUlsPzvq9PaTjwW/LpmEoo0FBUhYRPxYzVuYrE0BBflDR6JcMRR -oE9CXYGjtVPB44gT7pHVP09f3Ugrxk7X+t8wy3PWUTaTprmmEGqF0TzfdH4oQz0Y -v1khwuIu6rRlddGEiCKldXxn+gJy9E70yO4bm4tzAoGAL/XFIBVWVT6i1E9gjOWV -Tq8zwxiMU7Ney7DQgvEeGxZ1d9Kqr3cBQnFXNfmPpgeY+92fSlZ04atoRA1VB4ft -V6DGAeI3cxo+bavl5JQZGDLYJSOyJyJBOByHjtZBRRbNj8WCVHhNymeZlZqe2C30 -fUgwBx2Z172y/7KF/+680QECgYEA1GhUKQ9wDdYsiliZSgb9bJXSLH8qZeNULRrl -J3mNFwUf2p2mvPAgdjxx4QOb2H716z1aIrGJZB4nzc9/LBzQBb2h5ouV4DpqMjH8 -5bbuvH6fi9ABY5Irpt7vVUwFeoU1ofPqKPh8LLQYWywpQddAiBwzyjTQGTVHCg9f -4OI6Ib8CgYAptl24MGOc6BminKgsux+vNS9X1WwIADiHDyWBPHeQgLX8bYegswq9 -/6uGXJQgdFBhfLuoTBBN0ia/0QQhDezzrqnERddciuL2zxFxEETdpIuxm4lhieX7 -9LqnFcjxM4sLCg4SDSRX+nburiCnLDQiaBzhARooMJO48luTZUiWYQ== ------END RSA PRIVATE KEY----- diff --git a/e2e/testdata/notary-evil/root-ca.cert b/e2e/testdata/notary-evil/root-ca.cert deleted file mode 100644 index e7411c14bef8..000000000000 --- a/e2e/testdata/notary-evil/root-ca.cert +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDEDCCAfigAwIBAgIQdxGVILXsVcogexr+Ia2MZDANBgkqhkiG9w0BAQsFADAi -MQ8wDQYDVQQKEwZEb2NrZXIxDzANBgNVBAMTBkRvY2tlcjAeFw0yMzAzMjcxMTA5 -NTBaFw0zMzAzMjQxMTA5NTBaMCIxDzANBgNVBAoTBkRvY2tlcjEPMA0GA1UEAxMG -RG9ja2VyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq3sA/g7Srrkz -uEf1Qa2jAw93EfEJvxU1ZmZ30aB7KLgHN2TznxAGYtNekAu88CV4H3PKS44BZOar -wOo3KL4wQffLt7lmsRJG1KOfyiAmjmvidP5JSeRdTiBtj4CCVoi3EE6BZXPpZjst -9OSOlld2bWWXHb2ZdoY3ZAhZ9rn3tVwyfoLKpuESp1WZSFHPIdcuoMmZPtqD0bSi -5hc4gVFNLlZOAILvUkXxcHKUgLHZg0YEDQWsYjqh8EYp5LUK2tt4Mpz0HwAt9siE -VxHGIsiEqG1ajmxZiS28nlRWc4JRlOdmy5x1TPzJTDy+49gxB4njp1nRUtUgzmaG -QHhml35xHQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAqQwDwYDVR0TAQH/BAUwAwEB -/zAdBgNVHQ4EFgQU8WDDaoMtsJIJj2S//m0wk0KOrpUwDQYJKoZIhvcNAQELBQAD -ggEBAKZJfQjjfqn0K/UlzmrGcRkhrLbJNUfCD6TvxD75MoGtEe+VUEjljm1JHSbj -DrevDyTnak1W4o5/dcy0h6kI6lhHgObbcoAV5CxQ4+HHmeowA/fzedbnIdnHwtNg -SUJEslqoJSiYiiFQLV/yWWfBCWpbIgpDrADU7x9Ccxt6INuxrxOQwf1LZnmVbYs0 -1Mb/O1UFnvW7MeVSR4Nb/4lw6lol+mrR1iF8tTQ+rk4sBdCxw2aU48x3Pjqm+XpV -PIm9uRUr4tRDyQfmBZuxWTNJ9NSx5zVpLEPhDmyOW5wlSw+aKGscu9+RjBx/gXPk -sK8jZi441ojEJ7OaggGPheO3mCU= ------END CERTIFICATE----- diff --git a/e2e/testdata/notary/notary-config.json b/e2e/testdata/notary/notary-config.json deleted file mode 100644 index a4aed592a6b5..000000000000 --- a/e2e/testdata/notary/notary-config.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "server": { - "http_addr": "notary-server:4443", - "tls_key_file": "./notary-server.key", - "tls_cert_file": "./notary-server.cert" - }, - "trust_service": { - "type": "local", - "hostname": "", - "port": "", - "key_algorithm": "ed25519" - }, - "logging": { - "level": "debug" - }, - "storage": { - "backend": "memory" - } -} diff --git a/e2e/testdata/notary/notary-server.cert b/e2e/testdata/notary/notary-server.cert deleted file mode 100644 index eee2b3463d0d..000000000000 --- a/e2e/testdata/notary/notary-server.cert +++ /dev/null @@ -1,40 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIQTujwx+1xxXeI5AbzAQ379TANBgkqhkiG9w0BAQsFADAi -MQ8wDQYDVQQKEwZEb2NrZXIxDzANBgNVBAMTBkRvY2tlcjAeFw0yMzAzMjcxMTA5 -NTBaFw0zMzAzMjQxMTA5NTBaMCkxDzANBgNVBAoTBkRvY2tlcjEWMBQGA1UEAxMN -bm90YXJ5LXNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPhZ -pU7DRK/2nwbTu+kVYhU/XARDleVSiLrQ5RMR1Cz2xC4LWkOEVSj4aCBo85O66JAx -p+WRVwoVEU2rdkK3k4983Xr34+7q5Hv4hmwlg6I7QLRRJapEgK5G5RB/9aQntolx -h5E0KaoF4PJP25y4FHCUr4td4QyitaICsCpuOAN6XgmE9sM9TBf+AEjTSxwwvgEz -DqHvyovl7pA+pQP2oTKBrf6KN8hHDOXmm9gd8ST9yKLrsYWhqExLLPnAD4lQEcKZ -29g+iTd4eNoJUXctpuY+3IpqBcQSLq35mNKBP/FQco6g3q26/cB4zWGxTr3jGJqs -ms8qdFLGZ2KiBCt+oDMCAwEAAaOBoTCBnjAOBgNVHQ8BAf8EBAMCBaAwDAYDVR0T -AQH/BAIwADAfBgNVHSMEGDAWgBTxYMNqgy2wkgmPZL/+bTCTQo6ulTBdBgNVHREE -VjBUgg1ub3Rhcnktc2VydmVyggxub3RhcnlzZXJ2ZXKCEmV2aWwtbm90YXJ5LXNl -cnZlcoIQZXZpbG5vdGFyeXNlcnZlcoIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3 -DQEBCwUAA4IBAQCDMjuZnNINFfqstFwhEEvAgWbjYW26ZQxhQffaqDlbMIQkWoXj -8inld9bma9Mf5i/GAkUwFqCnEHD4LQ6vDgfAgL+pSOv9VI5SBEuk/gLqvIBUeIRu -uHo1jWtll2Fr7eDLVdD4mPRPFC7V6mv6sFa9EN4tBN8eheQxHJvzwnnU7X28prfI -/hWnwPWScVvttqBSsq1h2CUpVu2zGVToeCJ9xl4r/NyDtM5TyMgz7RLrer0p8NSu -4Qp4ZXtxHDLduWcyMUHLGTprW05yjj9UVq89xfaCOqFSpx5i4oxotYm1PoOacHmN -RMp9vaYMAmopoxIEYX6fDg5T3sQ5cidZJEvU ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDEDCCAfigAwIBAgIQdxGVILXsVcogexr+Ia2MZDANBgkqhkiG9w0BAQsFADAi -MQ8wDQYDVQQKEwZEb2NrZXIxDzANBgNVBAMTBkRvY2tlcjAeFw0yMzAzMjcxMTA5 -NTBaFw0zMzAzMjQxMTA5NTBaMCIxDzANBgNVBAoTBkRvY2tlcjEPMA0GA1UEAxMG -RG9ja2VyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq3sA/g7Srrkz -uEf1Qa2jAw93EfEJvxU1ZmZ30aB7KLgHN2TznxAGYtNekAu88CV4H3PKS44BZOar -wOo3KL4wQffLt7lmsRJG1KOfyiAmjmvidP5JSeRdTiBtj4CCVoi3EE6BZXPpZjst -9OSOlld2bWWXHb2ZdoY3ZAhZ9rn3tVwyfoLKpuESp1WZSFHPIdcuoMmZPtqD0bSi -5hc4gVFNLlZOAILvUkXxcHKUgLHZg0YEDQWsYjqh8EYp5LUK2tt4Mpz0HwAt9siE -VxHGIsiEqG1ajmxZiS28nlRWc4JRlOdmy5x1TPzJTDy+49gxB4njp1nRUtUgzmaG -QHhml35xHQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAqQwDwYDVR0TAQH/BAUwAwEB -/zAdBgNVHQ4EFgQU8WDDaoMtsJIJj2S//m0wk0KOrpUwDQYJKoZIhvcNAQELBQAD -ggEBAKZJfQjjfqn0K/UlzmrGcRkhrLbJNUfCD6TvxD75MoGtEe+VUEjljm1JHSbj -DrevDyTnak1W4o5/dcy0h6kI6lhHgObbcoAV5CxQ4+HHmeowA/fzedbnIdnHwtNg -SUJEslqoJSiYiiFQLV/yWWfBCWpbIgpDrADU7x9Ccxt6INuxrxOQwf1LZnmVbYs0 -1Mb/O1UFnvW7MeVSR4Nb/4lw6lol+mrR1iF8tTQ+rk4sBdCxw2aU48x3Pjqm+XpV -PIm9uRUr4tRDyQfmBZuxWTNJ9NSx5zVpLEPhDmyOW5wlSw+aKGscu9+RjBx/gXPk -sK8jZi441ojEJ7OaggGPheO3mCU= ------END CERTIFICATE----- diff --git a/e2e/testdata/notary/notary-server.key b/e2e/testdata/notary/notary-server.key deleted file mode 100644 index 7f7562a9defd..000000000000 --- a/e2e/testdata/notary/notary-server.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEA+FmlTsNEr/afBtO76RViFT9cBEOV5VKIutDlExHULPbELgta -Q4RVKPhoIGjzk7rokDGn5ZFXChURTat2QreTj3zdevfj7urke/iGbCWDojtAtFEl -qkSArkblEH/1pCe2iXGHkTQpqgXg8k/bnLgUcJSvi13hDKK1ogKwKm44A3peCYT2 -wz1MF/4ASNNLHDC+ATMOoe/Ki+XukD6lA/ahMoGt/oo3yEcM5eab2B3xJP3Iouux -haGoTEss+cAPiVARwpnb2D6JN3h42glRdy2m5j7cimoFxBIurfmY0oE/8VByjqDe -rbr9wHjNYbFOveMYmqyazyp0UsZnYqIEK36gMwIDAQABAoIBAQDy7W2f763+mbTQ -zshepQX+Vq3BlgLIAMWyR6fr0WLEYNVhXMV8ibNrkiD4ovCwLwJSGeBr1JFZUWZN -nUze0gdLMg7LvDN/ftDk2yNSIhfy1xbhywaW2M8uqjZiv2genKIXK7A6PtYKdBmn -rKnbUMzdmvNj1f7Ph1E4Gn0L5ChybJDJrq6wQjuTdZ6RmkGkbid0L+47Uv+6xBm9 -hgBPVXd8auQAYGmyXZwvfga5ZjfRMI4wvWkvjOAQcJtxxgOnLT1KDjYV+L70PWul -bYoKX0sNkFEP9tOq2pD9XVBuTVQxcYeztv0Vz+kG66Ju1KKCAnUYFhRt055zZLfm -WDYlWm0BAoGBAPvGW9LvzwCDE9QUcR46nG1ZihheJyGKwWVK+ZjYkUU9nLbrIpOD -/jmihoHHhKBC6YOfHHY73LtZ22fgXEu6ivDzZtTxBErXbdRpEKktJebRK7gPkfsB -PNQ8CRd/DxRC/JuVFR76OPsbZWhXCaeC7PRdyAtvU9toT1jIQf+a4OhBAoGBAPyE -kxEoNO1KhWtgByUlsPzvq9PaTjwW/LpmEoo0FBUhYRPxYzVuYrE0BBflDR6JcMRR -oE9CXYGjtVPB44gT7pHVP09f3Ugrxk7X+t8wy3PWUTaTprmmEGqF0TzfdH4oQz0Y -v1khwuIu6rRlddGEiCKldXxn+gJy9E70yO4bm4tzAoGAL/XFIBVWVT6i1E9gjOWV -Tq8zwxiMU7Ney7DQgvEeGxZ1d9Kqr3cBQnFXNfmPpgeY+92fSlZ04atoRA1VB4ft -V6DGAeI3cxo+bavl5JQZGDLYJSOyJyJBOByHjtZBRRbNj8WCVHhNymeZlZqe2C30 -fUgwBx2Z172y/7KF/+680QECgYEA1GhUKQ9wDdYsiliZSgb9bJXSLH8qZeNULRrl -J3mNFwUf2p2mvPAgdjxx4QOb2H716z1aIrGJZB4nzc9/LBzQBb2h5ouV4DpqMjH8 -5bbuvH6fi9ABY5Irpt7vVUwFeoU1ofPqKPh8LLQYWywpQddAiBwzyjTQGTVHCg9f -4OI6Ib8CgYAptl24MGOc6BminKgsux+vNS9X1WwIADiHDyWBPHeQgLX8bYegswq9 -/6uGXJQgdFBhfLuoTBBN0ia/0QQhDezzrqnERddciuL2zxFxEETdpIuxm4lhieX7 -9LqnFcjxM4sLCg4SDSRX+nburiCnLDQiaBzhARooMJO48luTZUiWYQ== ------END RSA PRIVATE KEY----- diff --git a/e2e/testdata/notary/root-ca.cert b/e2e/testdata/notary/root-ca.cert deleted file mode 100644 index e7411c14bef8..000000000000 --- a/e2e/testdata/notary/root-ca.cert +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDEDCCAfigAwIBAgIQdxGVILXsVcogexr+Ia2MZDANBgkqhkiG9w0BAQsFADAi -MQ8wDQYDVQQKEwZEb2NrZXIxDzANBgNVBAMTBkRvY2tlcjAeFw0yMzAzMjcxMTA5 -NTBaFw0zMzAzMjQxMTA5NTBaMCIxDzANBgNVBAoTBkRvY2tlcjEPMA0GA1UEAxMG -RG9ja2VyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq3sA/g7Srrkz -uEf1Qa2jAw93EfEJvxU1ZmZ30aB7KLgHN2TznxAGYtNekAu88CV4H3PKS44BZOar -wOo3KL4wQffLt7lmsRJG1KOfyiAmjmvidP5JSeRdTiBtj4CCVoi3EE6BZXPpZjst -9OSOlld2bWWXHb2ZdoY3ZAhZ9rn3tVwyfoLKpuESp1WZSFHPIdcuoMmZPtqD0bSi -5hc4gVFNLlZOAILvUkXxcHKUgLHZg0YEDQWsYjqh8EYp5LUK2tt4Mpz0HwAt9siE -VxHGIsiEqG1ajmxZiS28nlRWc4JRlOdmy5x1TPzJTDy+49gxB4njp1nRUtUgzmaG -QHhml35xHQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAqQwDwYDVR0TAQH/BAUwAwEB -/zAdBgNVHQ4EFgQU8WDDaoMtsJIJj2S//m0wk0KOrpUwDQYJKoZIhvcNAQELBQAD -ggEBAKZJfQjjfqn0K/UlzmrGcRkhrLbJNUfCD6TvxD75MoGtEe+VUEjljm1JHSbj -DrevDyTnak1W4o5/dcy0h6kI6lhHgObbcoAV5CxQ4+HHmeowA/fzedbnIdnHwtNg -SUJEslqoJSiYiiFQLV/yWWfBCWpbIgpDrADU7x9Ccxt6INuxrxOQwf1LZnmVbYs0 -1Mb/O1UFnvW7MeVSR4Nb/4lw6lol+mrR1iF8tTQ+rk4sBdCxw2aU48x3Pjqm+XpV -PIm9uRUr4tRDyQfmBZuxWTNJ9NSx5zVpLEPhDmyOW5wlSw+aKGscu9+RjBx/gXPk -sK8jZi441ojEJ7OaggGPheO3mCU= ------END CERTIFICATE----- diff --git a/e2e/trust/main_test.go b/e2e/trust/main_test.go deleted file mode 100644 index 5881adcdac8a..000000000000 --- a/e2e/trust/main_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package trust - -import ( - "fmt" - "os" - "testing" - - "github.com/docker/cli/internal/test/environment" -) - -func TestMain(m *testing.M) { - if err := environment.Setup(); err != nil { - fmt.Println(err.Error()) - os.Exit(3) - } - os.Exit(m.Run()) -} diff --git a/e2e/trust/revoke_test.go b/e2e/trust/revoke_test.go deleted file mode 100644 index 8ee5dc4ad973..000000000000 --- a/e2e/trust/revoke_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package trust - -import ( - "testing" - - "github.com/docker/cli/e2e/internal/fixtures" - "github.com/docker/cli/internal/test/environment" - "gotest.tools/v3/assert" - is "gotest.tools/v3/assert/cmp" - "gotest.tools/v3/fs" - "gotest.tools/v3/icmd" - "gotest.tools/v3/skip" -) - -const ( - revokeImage = "registry:5000/revoke:v1" - revokeRepo = "registry:5000/revokerepo" -) - -func TestRevokeImage(t *testing.T) { - skip.If(t, environment.RemoteDaemon()) - - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - setupTrustedImagesForRevoke(t, dir) - result := icmd.RunCmd( - icmd.Command("docker", "trust", "revoke", revokeImage), - fixtures.WithPassphrase("root_password", "repo_password"), - fixtures.WithNotary, fixtures.WithConfig(dir.Path())) - result.Assert(t, icmd.Success) - assert.Check(t, is.Contains(result.Stdout(), "Successfully deleted signature for registry:5000/revoke:v1")) -} - -func TestRevokeRepo(t *testing.T) { - skip.If(t, environment.RemoteDaemon()) - - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - setupTrustedImagesForRevokeRepo(t, dir) - result := icmd.RunCmd( - icmd.Command("docker", "trust", "revoke", revokeRepo, "-y"), - fixtures.WithPassphrase("root_password", "repo_password"), - fixtures.WithNotary, fixtures.WithConfig(dir.Path())) - result.Assert(t, icmd.Success) - assert.Check(t, is.Contains(result.Stdout(), "Successfully deleted signature for registry:5000/revoke")) -} - -func setupTrustedImagesForRevoke(t *testing.T, dir fs.Dir) { - t.Helper() - icmd.RunCmd(icmd.Command("docker", "pull", fixtures.AlpineImage)).Assert(t, icmd.Success) - icmd.RunCommand("docker", "tag", fixtures.AlpineImage, revokeImage).Assert(t, icmd.Success) - icmd.RunCmd( - icmd.Command("docker", "-D", "trust", "sign", revokeImage), - fixtures.WithPassphrase("root_password", "repo_password"), - fixtures.WithConfig(dir.Path()), fixtures.WithNotary).Assert(t, icmd.Success) -} - -func setupTrustedImagesForRevokeRepo(t *testing.T, dir fs.Dir) { - t.Helper() - icmd.RunCmd(icmd.Command("docker", "pull", fixtures.AlpineImage)).Assert(t, icmd.Success) - icmd.RunCommand("docker", "tag", fixtures.AlpineImage, revokeRepo+":v1").Assert(t, icmd.Success) - icmd.RunCmd( - icmd.Command("docker", "-D", "trust", "sign", revokeRepo+":v1"), - fixtures.WithPassphrase("root_password", "repo_password"), - fixtures.WithConfig(dir.Path()), fixtures.WithNotary).Assert(t, icmd.Success) - icmd.RunCmd(icmd.Command("docker", "pull", fixtures.BusyboxImage)).Assert(t, icmd.Success) - icmd.RunCommand("docker", "tag", fixtures.BusyboxImage, revokeRepo+":v2").Assert(t, icmd.Success) - icmd.RunCmd( - icmd.Command("docker", "-D", "trust", "sign", revokeRepo+":v2"), - fixtures.WithPassphrase("root_password", "repo_password"), - fixtures.WithConfig(dir.Path()), fixtures.WithNotary).Assert(t, icmd.Success) -} diff --git a/e2e/trust/sign_test.go b/e2e/trust/sign_test.go deleted file mode 100644 index 44bb007826c2..000000000000 --- a/e2e/trust/sign_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package trust - -import ( - "testing" - - "github.com/docker/cli/e2e/internal/fixtures" - "github.com/docker/cli/internal/test/environment" - "gotest.tools/v3/assert" - is "gotest.tools/v3/assert/cmp" - "gotest.tools/v3/fs" - "gotest.tools/v3/icmd" - "gotest.tools/v3/skip" -) - -const ( - localImage = "registry:5000/signlocal:v1" - signImage = "registry:5000/sign:v1" -) - -func TestSignLocalImage(t *testing.T) { - skip.If(t, environment.RemoteDaemon()) - // Digests in golden files are linux/amd64 specific. - // TODO: Fix this test and make it work on all platforms. - environment.SkipIfNotPlatform(t, "linux/amd64") - - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - icmd.RunCmd(icmd.Command("docker", "pull", fixtures.AlpineImage)).Assert(t, icmd.Success) - icmd.RunCommand("docker", "tag", fixtures.AlpineImage, signImage).Assert(t, icmd.Success) - result := icmd.RunCmd( - icmd.Command("docker", "trust", "sign", signImage), - fixtures.WithPassphrase("root_password", "repo_password"), - fixtures.WithConfig(dir.Path()), fixtures.WithNotary) - result.Assert(t, icmd.Success) - assert.Check(t, is.Contains(result.Stdout(), "v1: digest: sha256:"+fixtures.AlpineSha)) -} - -func TestSignWithLocalFlag(t *testing.T) { - skip.If(t, environment.RemoteDaemon()) - // Digests in golden files are linux/amd64 specific. - // TODO: Fix this test and make it work on all platforms. - environment.SkipIfNotPlatform(t, "linux/amd64") - - dir := fixtures.SetupConfigFile(t) - defer dir.Remove() - setupTrustedImageForOverwrite(t, dir) - result := icmd.RunCmd( - icmd.Command("docker", "trust", "sign", "--local", localImage), - fixtures.WithPassphrase("root_password", "repo_password"), - fixtures.WithConfig(dir.Path()), fixtures.WithNotary) - result.Assert(t, icmd.Success) - assert.Check(t, is.Contains(result.Stdout(), "v1: digest: sha256:"+fixtures.BusyboxSha)) -} - -func setupTrustedImageForOverwrite(t *testing.T, dir fs.Dir) { - t.Helper() - icmd.RunCmd(icmd.Command("docker", "pull", fixtures.AlpineImage)).Assert(t, icmd.Success) - icmd.RunCommand("docker", "tag", fixtures.AlpineImage, localImage).Assert(t, icmd.Success) - result := icmd.RunCmd( - icmd.Command("docker", "-D", "trust", "sign", localImage), - fixtures.WithPassphrase("root_password", "repo_password"), - fixtures.WithConfig(dir.Path()), fixtures.WithNotary) - result.Assert(t, icmd.Success) - assert.Check(t, is.Contains(result.Stdout(), "v1: digest: sha256:"+fixtures.AlpineSha)) - icmd.RunCmd(icmd.Command("docker", "pull", fixtures.BusyboxImage)).Assert(t, icmd.Success) - icmd.RunCommand("docker", "tag", fixtures.BusyboxImage, localImage).Assert(t, icmd.Success) -} diff --git a/internal/test/cli.go b/internal/test/cli.go index 8b506e14149c..5df19f9ddd1c 100644 --- a/internal/test/cli.go +++ b/internal/test/cli.go @@ -2,7 +2,6 @@ package test import ( "bytes" - "errors" "io" "strings" @@ -13,33 +12,26 @@ import ( manifeststore "github.com/docker/cli/cli/manifest/store" registryclient "github.com/docker/cli/cli/registry/client" "github.com/docker/cli/cli/streams" - "github.com/docker/cli/cli/trust" "github.com/docker/docker/api" "github.com/docker/docker/client" - notaryclient "github.com/theupdateframework/notary/client" ) -// NotaryClientFuncType defines a function that returns a fake notary client -type NotaryClientFuncType func(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (notaryclient.Repository, error) - // FakeCli emulates the default DockerCli type FakeCli struct { command.DockerCli - client client.APIClient - configfile *configfile.ConfigFile - out *streams.Out - outBuffer *bytes.Buffer - err *streams.Out - errBuffer *bytes.Buffer - in *streams.In - server command.ServerInfo - notaryClientFunc NotaryClientFuncType - manifestStore manifeststore.Store - registryClient registryclient.RegistryClient - contentTrust bool - contextStore store.Store - currentContext string - dockerEndpoint docker.Endpoint + client client.APIClient + configfile *configfile.ConfigFile + out *streams.Out + outBuffer *bytes.Buffer + err *streams.Out + errBuffer *bytes.Buffer + in *streams.In + server command.ServerInfo + manifestStore manifeststore.Store + registryClient registryclient.RegistryClient + contextStore store.Store + currentContext string + dockerEndpoint docker.Endpoint } // NewFakeCli returns a fake for the command.Cli interface @@ -165,19 +157,6 @@ func (c *FakeCli) ResetOutputBuffers() { c.errBuffer.Reset() } -// SetNotaryClient sets the internal getter for retrieving a NotaryClient -func (c *FakeCli) SetNotaryClient(notaryClientFunc NotaryClientFuncType) { - c.notaryClientFunc = notaryClientFunc -} - -// NotaryClient returns an err for testing unless defined -func (c *FakeCli) NotaryClient(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (notaryclient.Repository, error) { - if c.notaryClientFunc != nil { - return c.notaryClientFunc(imgRefAndAuth, actions) - } - return nil, errors.New("no notary client available unless defined") -} - // ManifestStore returns a fake store used for testing func (c *FakeCli) ManifestStore() manifeststore.Store { return c.manifestStore @@ -198,16 +177,6 @@ func (c *FakeCli) SetRegistryClient(registryClient registryclient.RegistryClient c.registryClient = registryClient } -// ContentTrustEnabled on the fake cli -func (c *FakeCli) ContentTrustEnabled() bool { - return c.contentTrust -} - -// EnableContentTrust on the fake cli -func EnableContentTrust(c *FakeCli) { - c.contentTrust = true -} - // BuildKitEnabled on the fake cli func (*FakeCli) BuildKitEnabled() (bool, error) { return true, nil diff --git a/internal/test/notary/client.go b/internal/test/notary/client.go deleted file mode 100644 index a948ad62cf54..000000000000 --- a/internal/test/notary/client.go +++ /dev/null @@ -1,556 +0,0 @@ -package notary - -import ( - "github.com/docker/cli/cli/trust" - "github.com/theupdateframework/notary/client" - "github.com/theupdateframework/notary/client/changelist" - "github.com/theupdateframework/notary/cryptoservice" - "github.com/theupdateframework/notary/storage" - "github.com/theupdateframework/notary/trustmanager" - "github.com/theupdateframework/notary/tuf/data" - "github.com/theupdateframework/notary/tuf/signed" -) - -// GetOfflineNotaryRepository returns a OfflineNotaryRepository -func GetOfflineNotaryRepository(trust.ImageRefAndAuth, []string) (client.Repository, error) { - return OfflineNotaryRepository{}, nil -} - -// OfflineNotaryRepository is a mock Notary repository that is offline -type OfflineNotaryRepository struct{} - -// Initialize creates a new repository by using rootKey as the root Key for the -// TUF repository. -func (OfflineNotaryRepository) Initialize([]string, ...data.RoleName) error { - return storage.ErrOffline{} -} - -// InitializeWithCertificate initializes the repository with root keys and their corresponding certificates -func (OfflineNotaryRepository) InitializeWithCertificate([]string, []data.PublicKey, ...data.RoleName) error { - return storage.ErrOffline{} -} - -// Publish pushes the local changes in signed material to the remote notary-server -// Conceptually it performs an operation similar to a `git rebase` -func (OfflineNotaryRepository) Publish() error { - return storage.ErrOffline{} -} - -// AddTarget creates new changelist entries to add a target to the given roles -// in the repository when the changelist gets applied at publish time. -func (OfflineNotaryRepository) AddTarget(*client.Target, ...data.RoleName) error { - return nil -} - -// RemoveTarget creates new changelist entries to remove a target from the given -// roles in the repository when the changelist gets applied at publish time. -func (OfflineNotaryRepository) RemoveTarget(string, ...data.RoleName) error { - return nil -} - -// ListTargets lists all targets for the current repository. The list of -// roles should be passed in order from highest to lowest priority. -func (OfflineNotaryRepository) ListTargets(...data.RoleName) ([]*client.TargetWithRole, error) { - return nil, storage.ErrOffline{} -} - -// GetTargetByName returns a target by the given name. -func (OfflineNotaryRepository) GetTargetByName(string, ...data.RoleName) (*client.TargetWithRole, error) { - return nil, storage.ErrOffline{} -} - -// GetAllTargetMetadataByName searches the entire delegation role tree to find the specified target by name for all -// roles, and returns a list of TargetSignedStructs for each time it finds the specified target. -func (OfflineNotaryRepository) GetAllTargetMetadataByName(string) ([]client.TargetSignedStruct, error) { - return nil, storage.ErrOffline{} -} - -// GetChangelist returns the list of the repository's unpublished changes -func (OfflineNotaryRepository) GetChangelist() (changelist.Changelist, error) { - return changelist.NewMemChangelist(), nil -} - -// ListRoles returns a list of RoleWithSignatures objects for this repo -func (OfflineNotaryRepository) ListRoles() ([]client.RoleWithSignatures, error) { - return nil, storage.ErrOffline{} -} - -// GetDelegationRoles returns the keys and roles of the repository's delegations -func (OfflineNotaryRepository) GetDelegationRoles() ([]data.Role, error) { - return nil, storage.ErrOffline{} -} - -// AddDelegation creates changelist entries to add provided delegation public keys and paths. -func (OfflineNotaryRepository) AddDelegation(data.RoleName, []data.PublicKey, []string) error { - return nil -} - -// AddDelegationRoleAndKeys creates a changelist entry to add provided delegation public keys. -func (OfflineNotaryRepository) AddDelegationRoleAndKeys(data.RoleName, []data.PublicKey) error { - return nil -} - -// AddDelegationPaths creates a changelist entry to add provided paths to an existing delegation. -func (OfflineNotaryRepository) AddDelegationPaths(data.RoleName, []string) error { - return nil -} - -// RemoveDelegationKeysAndPaths creates changelist entries to remove provided delegation key IDs and paths. -func (OfflineNotaryRepository) RemoveDelegationKeysAndPaths(data.RoleName, []string, []string) error { - return nil -} - -// RemoveDelegationRole creates a changelist to remove all paths and keys from a role, and delete the role in its entirety. -func (OfflineNotaryRepository) RemoveDelegationRole(data.RoleName) error { - return nil -} - -// RemoveDelegationPaths creates a changelist entry to remove provided paths from an existing delegation. -func (OfflineNotaryRepository) RemoveDelegationPaths(data.RoleName, []string) error { - return nil -} - -// RemoveDelegationKeys creates a changelist entry to remove provided keys from an existing delegation. -func (OfflineNotaryRepository) RemoveDelegationKeys(data.RoleName, []string) error { - return nil -} - -// ClearDelegationPaths creates a changelist entry to remove all paths from an existing delegation. -func (OfflineNotaryRepository) ClearDelegationPaths(data.RoleName) error { - return nil -} - -// Witness creates change objects to witness (i.e. re-sign) the given -// roles on the next publish. One change is created per role -func (OfflineNotaryRepository) Witness(...data.RoleName) ([]data.RoleName, error) { - return nil, nil -} - -// RotateKey rotates a private key and returns the public component from the remote server -func (OfflineNotaryRepository) RotateKey(data.RoleName, bool, []string) error { - return storage.ErrOffline{} -} - -// GetCryptoService is the getter for the repository's CryptoService -func (OfflineNotaryRepository) GetCryptoService() signed.CryptoService { - return nil -} - -// SetLegacyVersions allows the number of legacy versions of the root -// to be inspected for old signing keys to be configured. -func (OfflineNotaryRepository) SetLegacyVersions(int) {} - -// GetGUN is a getter for the GUN object from a Repository -func (OfflineNotaryRepository) GetGUN() data.GUN { - return data.GUN("gun") -} - -// GetUninitializedNotaryRepository returns an UninitializedNotaryRepository -func GetUninitializedNotaryRepository(trust.ImageRefAndAuth, []string) (client.Repository, error) { - return UninitializedNotaryRepository{}, nil -} - -// UninitializedNotaryRepository is a mock Notary repository that is uninintialized -// it builds on top of the OfflineNotaryRepository, instead returning ErrRepositoryNotExist -// for any online operation -type UninitializedNotaryRepository struct { - OfflineNotaryRepository -} - -// Initialize creates a new repository by using rootKey as the root Key for the -// TUF repository. -func (UninitializedNotaryRepository) Initialize([]string, ...data.RoleName) error { - return client.ErrRepositoryNotExist{} -} - -// InitializeWithCertificate initializes the repository with root keys and their corresponding certificates -func (UninitializedNotaryRepository) InitializeWithCertificate([]string, []data.PublicKey, ...data.RoleName) error { - return client.ErrRepositoryNotExist{} -} - -// Publish pushes the local changes in signed material to the remote notary-server -// Conceptually it performs an operation similar to a `git rebase` -func (UninitializedNotaryRepository) Publish() error { - return client.ErrRepositoryNotExist{} -} - -// ListTargets lists all targets for the current repository. The list of -// roles should be passed in order from highest to lowest priority. -func (UninitializedNotaryRepository) ListTargets(...data.RoleName) ([]*client.TargetWithRole, error) { - return nil, client.ErrRepositoryNotExist{} -} - -// GetTargetByName returns a target by the given name. -func (UninitializedNotaryRepository) GetTargetByName(string, ...data.RoleName) (*client.TargetWithRole, error) { - return nil, client.ErrRepositoryNotExist{} -} - -// GetAllTargetMetadataByName searches the entire delegation role tree to find the specified target by name for all -// roles, and returns a list of TargetSignedStructs for each time it finds the specified target. -func (UninitializedNotaryRepository) GetAllTargetMetadataByName(string) ([]client.TargetSignedStruct, error) { - return nil, client.ErrRepositoryNotExist{} -} - -// ListRoles returns a list of RoleWithSignatures objects for this repo -func (UninitializedNotaryRepository) ListRoles() ([]client.RoleWithSignatures, error) { - return nil, client.ErrRepositoryNotExist{} -} - -// GetDelegationRoles returns the keys and roles of the repository's delegations -func (UninitializedNotaryRepository) GetDelegationRoles() ([]data.Role, error) { - return nil, client.ErrRepositoryNotExist{} -} - -// RotateKey rotates a private key and returns the public component from the remote server -func (UninitializedNotaryRepository) RotateKey(data.RoleName, bool, []string) error { - return client.ErrRepositoryNotExist{} -} - -// GetEmptyTargetsNotaryRepository returns an EmptyTargetsNotaryRepository -func GetEmptyTargetsNotaryRepository(trust.ImageRefAndAuth, []string) (client.Repository, error) { - return EmptyTargetsNotaryRepository{}, nil -} - -// EmptyTargetsNotaryRepository is a mock Notary repository that is initialized -// but does not have any signed targets -type EmptyTargetsNotaryRepository struct { - OfflineNotaryRepository -} - -// Initialize creates a new repository by using rootKey as the root Key for the -// TUF repository. -func (EmptyTargetsNotaryRepository) Initialize([]string, ...data.RoleName) error { - return nil -} - -// InitializeWithCertificate initializes the repository with root keys and their corresponding certificates -func (EmptyTargetsNotaryRepository) InitializeWithCertificate([]string, []data.PublicKey, ...data.RoleName) error { - return nil -} - -// Publish pushes the local changes in signed material to the remote notary-server -// Conceptually it performs an operation similar to a `git rebase` -func (EmptyTargetsNotaryRepository) Publish() error { - return nil -} - -// ListTargets lists all targets for the current repository. The list of -// roles should be passed in order from highest to lowest priority. -func (EmptyTargetsNotaryRepository) ListTargets(...data.RoleName) ([]*client.TargetWithRole, error) { - return []*client.TargetWithRole{}, nil -} - -// GetTargetByName returns a target by the given name. -func (EmptyTargetsNotaryRepository) GetTargetByName(name string, _ ...data.RoleName) (*client.TargetWithRole, error) { - return nil, client.ErrNoSuchTarget(name) -} - -// GetAllTargetMetadataByName searches the entire delegation role tree to find the specified target by name for all -// roles, and returns a list of TargetSignedStructs for each time it finds the specified target. -func (EmptyTargetsNotaryRepository) GetAllTargetMetadataByName(name string) ([]client.TargetSignedStruct, error) { - return nil, client.ErrNoSuchTarget(name) -} - -// ListRoles returns a list of RoleWithSignatures objects for this repo -func (EmptyTargetsNotaryRepository) ListRoles() ([]client.RoleWithSignatures, error) { - rootRole := data.Role{ - RootRole: data.RootRole{ - KeyIDs: []string{"rootID"}, - Threshold: 1, - }, - Name: data.CanonicalRootRole, - } - - targetsRole := data.Role{ - RootRole: data.RootRole{ - KeyIDs: []string{"targetsID"}, - Threshold: 1, - }, - Name: data.CanonicalTargetsRole, - } - return []client.RoleWithSignatures{ - {Role: rootRole}, - {Role: targetsRole}, - }, nil -} - -// GetDelegationRoles returns the keys and roles of the repository's delegations -func (EmptyTargetsNotaryRepository) GetDelegationRoles() ([]data.Role, error) { - return []data.Role{}, nil -} - -// RotateKey rotates a private key and returns the public component from the remote server -func (EmptyTargetsNotaryRepository) RotateKey(data.RoleName, bool, []string) error { - return nil -} - -// GetLoadedNotaryRepository returns a LoadedNotaryRepository -func GetLoadedNotaryRepository(trust.ImageRefAndAuth, []string) (client.Repository, error) { - return LoadedNotaryRepository{}, nil -} - -// LoadedNotaryRepository is a mock Notary repository that is loaded with targets, delegations, and keys -type LoadedNotaryRepository struct { - EmptyTargetsNotaryRepository - statefulCryptoService signed.CryptoService -} - -// LoadedNotaryRepository has three delegations: -// - targets/releases: includes keys A and B -// - targets/alice: includes key A -// - targets/bob: includes key B -var loadedReleasesRole = data.DelegationRole{ - BaseRole: data.BaseRole{ - Name: "targets/releases", - Keys: map[string]data.PublicKey{"A": nil, "B": nil}, - Threshold: 1, - }, -} - -var loadedAliceRole = data.DelegationRole{ - BaseRole: data.BaseRole{ - Name: "targets/alice", - Keys: map[string]data.PublicKey{"A": nil}, - Threshold: 1, - }, -} - -var loadedBobRole = data.DelegationRole{ - BaseRole: data.BaseRole{ - Name: "targets/bob", - Keys: map[string]data.PublicKey{"B": nil}, - Threshold: 1, - }, -} - -var loadedDelegationRoles = []data.Role{ - { - Name: loadedReleasesRole.Name, - RootRole: data.RootRole{ - KeyIDs: []string{"A", "B"}, - Threshold: 1, - }, - }, - { - Name: loadedAliceRole.Name, - RootRole: data.RootRole{ - KeyIDs: []string{"A"}, - Threshold: 1, - }, - }, - { - Name: loadedBobRole.Name, - RootRole: data.RootRole{ - KeyIDs: []string{"B"}, - Threshold: 1, - }, - }, -} - -var loadedTargetsRole = data.DelegationRole{ - BaseRole: data.BaseRole{ - Name: data.CanonicalTargetsRole, - Keys: map[string]data.PublicKey{"C": nil}, - Threshold: 1, - }, -} - -// LoadedNotaryRepository has three targets: -// - red: signed by targets/releases, targets/alice, targets/bob -// - blue: signed by targets/releases, targets/alice -// - green: signed by targets/releases -var loadedRedTarget = client.Target{ - Name: "red", - Hashes: data.Hashes{"sha256": []byte("red-digest")}, -} - -var loadedBlueTarget = client.Target{ - Name: "blue", - Hashes: data.Hashes{"sha256": []byte("blue-digest")}, -} - -var loadedGreenTarget = client.Target{ - Name: "green", - Hashes: data.Hashes{"sha256": []byte("green-digest")}, -} - -var loadedTargets = []client.TargetSignedStruct{ - // red is signed by all three delegations - {Target: loadedRedTarget, Role: loadedReleasesRole}, - {Target: loadedRedTarget, Role: loadedAliceRole}, - {Target: loadedRedTarget, Role: loadedBobRole}, - - // blue is signed by targets/releases, targets/alice - {Target: loadedBlueTarget, Role: loadedReleasesRole}, - {Target: loadedBlueTarget, Role: loadedAliceRole}, - - // green is signed by targets/releases - {Target: loadedGreenTarget, Role: loadedReleasesRole}, -} - -// ListRoles returns a list of RoleWithSignatures objects for this repo -func (LoadedNotaryRepository) ListRoles() ([]client.RoleWithSignatures, error) { - rootRole := data.Role{ - RootRole: data.RootRole{ - KeyIDs: []string{"rootID"}, - Threshold: 1, - }, - Name: data.CanonicalRootRole, - } - - targetsRole := data.Role{ - RootRole: data.RootRole{ - KeyIDs: []string{"targetsID"}, - Threshold: 1, - }, - Name: data.CanonicalTargetsRole, - } - - aliceRole := data.Role{ - RootRole: data.RootRole{ - KeyIDs: []string{"A"}, - Threshold: 1, - }, - Name: data.RoleName("targets/alice"), - } - - bobRole := data.Role{ - RootRole: data.RootRole{ - KeyIDs: []string{"B"}, - Threshold: 1, - }, - Name: data.RoleName("targets/bob"), - } - - releasesRole := data.Role{ - RootRole: data.RootRole{ - KeyIDs: []string{"A", "B"}, - Threshold: 1, - }, - Name: data.RoleName("targets/releases"), - } - // have releases only signed off by Alice last - releasesSig := []data.Signature{{KeyID: "A"}} - - return []client.RoleWithSignatures{ - {Role: rootRole}, - {Role: targetsRole}, - {Role: aliceRole}, - {Role: bobRole}, - {Role: releasesRole, Signatures: releasesSig}, - }, nil -} - -// ListTargets lists all targets for the current repository. The list of -// roles should be passed in order from highest to lowest priority. -func (LoadedNotaryRepository) ListTargets(roles ...data.RoleName) ([]*client.TargetWithRole, error) { - filteredTargets := []*client.TargetWithRole{} - for _, tgt := range loadedTargets { - if len(roles) == 0 || (len(roles) > 0 && roles[0] == tgt.Role.Name) { - filteredTargets = append(filteredTargets, &client.TargetWithRole{Target: tgt.Target, Role: tgt.Role.Name}) - } - } - return filteredTargets, nil -} - -// GetTargetByName returns a target by the given name. -func (LoadedNotaryRepository) GetTargetByName(name string, roles ...data.RoleName) (*client.TargetWithRole, error) { - for _, tgt := range loadedTargets { - if name == tgt.Target.Name { - if len(roles) == 0 || (len(roles) > 0 && roles[0] == tgt.Role.Name) { - return &client.TargetWithRole{Target: tgt.Target, Role: tgt.Role.Name}, nil - } - } - } - return nil, client.ErrNoSuchTarget(name) -} - -// GetAllTargetMetadataByName searches the entire delegation role tree to find the specified target by name for all -// roles, and returns a list of TargetSignedStructs for each time it finds the specified target. -func (LoadedNotaryRepository) GetAllTargetMetadataByName(name string) ([]client.TargetSignedStruct, error) { - if name == "" { - return loadedTargets, nil - } - filteredTargets := []client.TargetSignedStruct{} - for _, tgt := range loadedTargets { - if name == tgt.Target.Name { - filteredTargets = append(filteredTargets, tgt) - } - } - if len(filteredTargets) == 0 { - return nil, client.ErrNoSuchTarget(name) - } - return filteredTargets, nil -} - -// GetGUN is a getter for the GUN object from a Repository -func (LoadedNotaryRepository) GetGUN() data.GUN { - return "signed-repo" -} - -// GetDelegationRoles returns the keys and roles of the repository's delegations -func (LoadedNotaryRepository) GetDelegationRoles() ([]data.Role, error) { - return loadedDelegationRoles, nil -} - -const testPass = "password" - -func testPassRetriever(string, string, bool, int) (string, bool, error) { - return testPass, false, nil -} - -// GetCryptoService is the getter for the repository's CryptoService -func (l LoadedNotaryRepository) GetCryptoService() signed.CryptoService { - if l.statefulCryptoService == nil { - // give it an in-memory cryptoservice with a root key and targets key - l.statefulCryptoService = cryptoservice.NewCryptoService(trustmanager.NewKeyMemoryStore(testPassRetriever)) - l.statefulCryptoService.AddKey(data.CanonicalRootRole, l.GetGUN(), nil) - l.statefulCryptoService.AddKey(data.CanonicalTargetsRole, l.GetGUN(), nil) - } - return l.statefulCryptoService -} - -// GetLoadedWithNoSignersNotaryRepository returns a LoadedWithNoSignersNotaryRepository -func GetLoadedWithNoSignersNotaryRepository(trust.ImageRefAndAuth, []string) (client.Repository, error) { - return LoadedWithNoSignersNotaryRepository{}, nil -} - -// LoadedWithNoSignersNotaryRepository is a mock Notary repository that is loaded with targets but no delegations -// it only contains the green target -type LoadedWithNoSignersNotaryRepository struct { - LoadedNotaryRepository -} - -// ListTargets lists all targets for the current repository. The list of -// roles should be passed in order from highest to lowest priority. -func (LoadedWithNoSignersNotaryRepository) ListTargets(roles ...data.RoleName) ([]*client.TargetWithRole, error) { - filteredTargets := []*client.TargetWithRole{} - for _, tgt := range loadedTargets { - if len(roles) == 0 || (len(roles) > 0 && roles[0] == tgt.Role.Name) { - filteredTargets = append(filteredTargets, &client.TargetWithRole{Target: tgt.Target, Role: tgt.Role.Name}) - } - } - return filteredTargets, nil -} - -// GetTargetByName returns a target by the given name. -func (LoadedWithNoSignersNotaryRepository) GetTargetByName(name string, _ ...data.RoleName) (*client.TargetWithRole, error) { - if name == "" || name == loadedGreenTarget.Name { - return &client.TargetWithRole{Target: loadedGreenTarget, Role: data.CanonicalTargetsRole}, nil - } - return nil, client.ErrNoSuchTarget(name) -} - -// GetAllTargetMetadataByName searches the entire delegation role tree to find the specified target by name for all -// roles, and returns a list of TargetSignedStructs for each time it finds the specified target. -func (LoadedWithNoSignersNotaryRepository) GetAllTargetMetadataByName(name string) ([]client.TargetSignedStruct, error) { - if name == "" || name == loadedGreenTarget.Name { - return []client.TargetSignedStruct{{Target: loadedGreenTarget, Role: loadedTargetsRole}}, nil - } - return nil, client.ErrNoSuchTarget(name) -} - -// GetDelegationRoles returns the keys and roles of the repository's delegations -func (LoadedWithNoSignersNotaryRepository) GetDelegationRoles() ([]data.Role, error) { - return []data.Role{}, nil -} diff --git a/scripts/build/.variables b/scripts/build/.variables index c42d305a3b1d..d37c945893c6 100755 --- a/scripts/build/.variables +++ b/scripts/build/.variables @@ -96,11 +96,11 @@ if test -n "${PLATFORM}"; then GO_LDFLAGS="$GO_LDFLAGS -X \"github.com/docker/cli/cli/version.PlatformName=${PLATFORM}\"" fi if [ "$CGO_ENABLED" = "1" ] && [ "$GO_LINKMODE" = "static" ] && [ "$(go env GOOS)" = "linux" ]; then - GO_LDFLAGS="$GO_LDFLAGS -extldflags -static" + GO_LDFLAGS="$GO_LDFLAGS -linkmode external -extldflags -static" fi if [ "$CGO_ENABLED" = "1" ] && [ "$GO_LINKMODE" = "static" ]; then - # compiling statically with CGO enabled requires osusergo to be set. - GO_BUILDTAGS="$GO_BUILDTAGS osusergo" + # compiling statically with CGO enabled requires osusergo and netgo to be set. + GO_BUILDTAGS="$GO_BUILDTAGS osusergo netgo" fi if [ -n "$GO_STRIP" ]; then # if stripping enabled and building with llvm < 12 against darwin/amd64 diff --git a/scripts/build/binary b/scripts/build/binary index 44cd4f14fdd8..139cc517fbdf 100755 --- a/scripts/build/binary +++ b/scripts/build/binary @@ -7,10 +7,6 @@ set -eu . ./scripts/build/.variables -if [ "$CGO_ENABLED" = "1" ] && [ "$(go env GOOS)" != "windows" ]; then - GO_BUILDTAGS="$GO_BUILDTAGS pkcs11" -fi - echo "Building $GO_LINKMODE $(basename "${TARGET}")" export GO111MODULE=auto diff --git a/vendor.mod b/vendor.mod index 1776b66f290e..bc509ae8a019 100644 --- a/vendor.mod +++ b/vendor.mod @@ -43,7 +43,6 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.9.1 github.com/spf13/pflag v1.0.6 - github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346 github.com/xeipuuv/gojsonschema v1.2.0 go.opentelemetry.io/otel v1.31.0 @@ -70,9 +69,9 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect - github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect github.com/docker/go-metrics v0.0.1 // indirect + github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -81,7 +80,6 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.18.0 // indirect - github.com/miekg/pkcs11 v1.1.1 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/sys/symlink v0.3.0 // indirect github.com/moby/sys/user v0.4.0 // indirect diff --git a/vendor.sum b/vendor.sum index 224b7cfc338a..07f6c0caf2c4 100644 --- a/vendor.sum +++ b/vendor.sum @@ -4,34 +4,18 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8af github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d h1:hi6J4K6DKrR4/ljxn6SF6nURyu785wKMuQcjt7H3VCQ= -github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bugsnag/bugsnag-go v1.0.5 h1:NIoY2u+am1/GRgUZa+ata8UUrRBuCK4pLq0/lcvMF7M= -github.com/bugsnag/bugsnag-go v1.0.5/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= -github.com/cloudflare/cfssl v1.6.4 h1:NMOvfrEjFfC63K3SGXgAnFdsgkmiq4kATme5BfcqrO8= -github.com/cloudflare/cfssl v1.6.4/go.mod h1:8b3CQMxfWPAeom3zBnGJ6sd+G1NkL5TXqmDXacb+1J0= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= @@ -43,43 +27,33 @@ github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLV github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/cli-docs-tool v0.10.0 h1:bOD6mKynPQgojQi3s2jgcUWGp/Ebqy1SeCr9VfKQLLU= github.com/docker/cli-docs-tool v0.10.0/go.mod h1:5EM5zPnT2E7yCLERZmrDA234Vwn09fzRHP4aX1qwp1U= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw= github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= -github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= -github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= -github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= @@ -92,25 +66,17 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= -github.com/google/certificate-transparency-go v1.1.4 h1:hCyXHDbtqlr/lMXU0D4WgbalXL0Zk4dSWWMbPV8VrqY= -github.com/google/certificate-transparency-go v1.1.4/go.mod h1:D6lvbfwckhNrbM9WVl1EVeMOyzC19mpIjMOI4nxBHtQ= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= @@ -119,27 +85,14 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8 h1:CZkYfurY6KGhVtlalI4QwQ6T0Cu6iuY3e0x5RLu96WE= -github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= -github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d h1:jRQLvyVGL+iVtDElaEIDdKwpPqUIZJfzkNLV34htpEc= -github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jmoiron/sqlx v1.3.3 h1:j82X0bf7oQ27XeqxicSZsTU5suPwKElg3oyxNn43iTk= -github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -147,27 +100,15 @@ github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zt github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/magiconair/properties v1.5.3 h1:C8fxWnhYyME3n0klPOhVM7PtYUB3eV1W3DeFmN3j53Y= -github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= -github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KHeTRY+7I= -github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= @@ -201,18 +142,10 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -220,23 +153,19 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= -github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= @@ -248,39 +177,22 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94 h1:JmfC365KywYwHB946TTiQWEb8kqPY+pybPLoGE9GgVk= -github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= -github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431 h1:XTHrT015sxHyJ5FnQ0AeemSspZWaDq7DoTRW0EVsDCE= -github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c h1:2EejZtjFjKJGk71ANb+wtFK5EjUzUkEM3R0xnp559xg= -github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a h1:tlJ7tGUHvcvL1v3yR6NcCc9nOqh2L+CG6HWrYQtwzQ0= -github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a/go.mod h1:Y94A6rPp2OwNfP/7vmf8O2xx2IykP8pPXQ1DLouGnEw= github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346 h1:TvtdmeYsYEij78hS4oxnwikoiLdIrgav3BA+CbhaDAI= github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346/go.mod h1:xKQhd7snlzKFuUi1taTGWjpRE8iFTA06DeacYi3CVFQ= -github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b h1:FsyNrX12e5BkplJq7wKOLk0+C6LZ+KGXvuEcKUYm5ss= -github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -290,10 +202,6 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zmap/zcrypto v0.0.0-20210511125630-18f1e0152cfc h1:zkGwegkOW709y0oiAraH/3D8njopUR/pARHv4tZZ6pw= -github.com/zmap/zcrypto v0.0.0-20210511125630-18f1e0152cfc/go.mod h1:FM4U1E3NzlNMRnSUTU3P1UdukWhYGifqEsjk9fn7BCk= -github.com/zmap/zlint/v3 v3.1.0 h1:WjVytZo79m/L1+/Mlphl09WBob6YTGljN5IGWZFpAv0= -github.com/zmap/zlint/v3 v3.1.0/go.mod h1:L7t8s3sEKkb0A2BxGy1IWrxt1ZATa1R4QfJZaQOD3zU= go.etcd.io/etcd/raft/v3 v3.5.16 h1:zBXA3ZUpYs1AwiLGPafYAKKl/CORn/uaxYDwlNwndAk= go.etcd.io/etcd/raft/v3 v3.5.16/go.mod h1:P4UP14AxofMJ/54boWilabqqWoW9eLodl6I5GdGzazI= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= @@ -322,16 +230,12 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -340,7 +244,6 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -349,20 +252,16 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -383,36 +282,19 @@ google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 h1: google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38/go.mod h1:vuAjtvlwkDKF6L1GQ0SokiRLCGFfeBUXWr/aFFkHACc= google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI= google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= -google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/cenkalti/backoff.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII= -gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1 h1:d4KQkxAaAiRY2h5Zqis161Pv91A37uZyJOx73duwUwM= -gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= -k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= -k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= tags.cncf.io/container-device-interface v0.8.0 h1:8bCFo/g9WODjWx3m6EYl3GfUG31eKJbaggyBDxEldRc= tags.cncf.io/container-device-interface v0.8.0/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y= diff --git a/vendor/github.com/docker/distribution/uuid/uuid.go b/vendor/github.com/docker/distribution/uuid/uuid.go deleted file mode 100644 index d433ccaf512d..000000000000 --- a/vendor/github.com/docker/distribution/uuid/uuid.go +++ /dev/null @@ -1,126 +0,0 @@ -// Package uuid provides simple UUID generation. Only version 4 style UUIDs -// can be generated. -// -// Please see http://tools.ietf.org/html/rfc4122 for details on UUIDs. -package uuid - -import ( - "crypto/rand" - "fmt" - "io" - "os" - "syscall" - "time" -) - -const ( - // Bits is the number of bits in a UUID - Bits = 128 - - // Size is the number of bytes in a UUID - Size = Bits / 8 - - format = "%08x-%04x-%04x-%04x-%012x" -) - -var ( - // ErrUUIDInvalid indicates a parsed string is not a valid uuid. - ErrUUIDInvalid = fmt.Errorf("invalid uuid") - - // Loggerf can be used to override the default logging destination. Such - // log messages in this library should be logged at warning or higher. - Loggerf = func(format string, args ...interface{}) {} -) - -// UUID represents a UUID value. UUIDs can be compared and set to other values -// and accessed by byte. -type UUID [Size]byte - -// Generate creates a new, version 4 uuid. -func Generate() (u UUID) { - const ( - // ensures we backoff for less than 450ms total. Use the following to - // select new value, in units of 10ms: - // n*(n+1)/2 = d -> n^2 + n - 2d -> n = (sqrt(8d + 1) - 1)/2 - maxretries = 9 - backoff = time.Millisecond * 10 - ) - - var ( - totalBackoff time.Duration - count int - retries int - ) - - for { - // This should never block but the read may fail. Because of this, - // we just try to read the random number generator until we get - // something. This is a very rare condition but may happen. - b := time.Duration(retries) * backoff - time.Sleep(b) - totalBackoff += b - - n, err := io.ReadFull(rand.Reader, u[count:]) - if err != nil { - if retryOnError(err) && retries < maxretries { - count += n - retries++ - Loggerf("error generating version 4 uuid, retrying: %v", err) - continue - } - - // Any other errors represent a system problem. What did someone - // do to /dev/urandom? - panic(fmt.Errorf("error reading random number generator, retried for %v: %v", totalBackoff.String(), err)) - } - - break - } - - u[6] = (u[6] & 0x0f) | 0x40 // set version byte - u[8] = (u[8] & 0x3f) | 0x80 // set high order byte 0b10{8,9,a,b} - - return u -} - -// Parse attempts to extract a uuid from the string or returns an error. -func Parse(s string) (u UUID, err error) { - if len(s) != 36 { - return UUID{}, ErrUUIDInvalid - } - - // create stack addresses for each section of the uuid. - p := make([][]byte, 5) - - if _, err := fmt.Sscanf(s, format, &p[0], &p[1], &p[2], &p[3], &p[4]); err != nil { - return u, err - } - - copy(u[0:4], p[0]) - copy(u[4:6], p[1]) - copy(u[6:8], p[2]) - copy(u[8:10], p[3]) - copy(u[10:16], p[4]) - - return -} - -func (u UUID) String() string { - return fmt.Sprintf(format, u[:4], u[4:6], u[6:8], u[8:10], u[10:]) -} - -// retryOnError tries to detect whether or not retrying would be fruitful. -func retryOnError(err error) bool { - switch err := err.(type) { - case *os.PathError: - return retryOnError(err.Err) // unpack the target error - case syscall.Errno: - if err == syscall.EPERM { - // EPERM represents an entropy pool exhaustion, a condition under - // which we backoff and retry. - return true - } - } - - return false -} diff --git a/vendor/github.com/docker/go/LICENSE b/vendor/github.com/docker/go/LICENSE deleted file mode 100644 index 74487567632c..000000000000 --- a/vendor/github.com/docker/go/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/docker/go/canonical/json/decode.go b/vendor/github.com/docker/go/canonical/json/decode.go deleted file mode 100644 index 72b981c53595..000000000000 --- a/vendor/github.com/docker/go/canonical/json/decode.go +++ /dev/null @@ -1,1168 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Represents JSON data structure using native Go types: booleans, floats, -// strings, arrays, and maps. - -package json - -import ( - "bytes" - "encoding" - "encoding/base64" - "errors" - "fmt" - "reflect" - "runtime" - "strconv" - "unicode" - "unicode/utf16" - "unicode/utf8" -) - -// Unmarshal parses the JSON-encoded data and stores the result -// in the value pointed to by v. -// -// Unmarshal uses the inverse of the encodings that -// Marshal uses, allocating maps, slices, and pointers as necessary, -// with the following additional rules: -// -// To unmarshal JSON into a pointer, Unmarshal first handles the case of -// the JSON being the JSON literal null. In that case, Unmarshal sets -// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into -// the value pointed at by the pointer. If the pointer is nil, Unmarshal -// allocates a new value for it to point to. -// -// To unmarshal JSON into a struct, Unmarshal matches incoming object -// keys to the keys used by Marshal (either the struct field name or its tag), -// preferring an exact match but also accepting a case-insensitive match. -// Unmarshal will only set exported fields of the struct. -// -// To unmarshal JSON into an interface value, -// Unmarshal stores one of these in the interface value: -// -// bool, for JSON booleans -// float64, for JSON numbers -// string, for JSON strings -// []interface{}, for JSON arrays -// map[string]interface{}, for JSON objects -// nil for JSON null -// -// To unmarshal a JSON array into a slice, Unmarshal resets the slice length -// to zero and then appends each element to the slice. -// As a special case, to unmarshal an empty JSON array into a slice, -// Unmarshal replaces the slice with a new empty slice. -// -// To unmarshal a JSON array into a Go array, Unmarshal decodes -// JSON array elements into corresponding Go array elements. -// If the Go array is smaller than the JSON array, -// the additional JSON array elements are discarded. -// If the JSON array is smaller than the Go array, -// the additional Go array elements are set to zero values. -// -// To unmarshal a JSON object into a string-keyed map, Unmarshal first -// establishes a map to use, If the map is nil, Unmarshal allocates a new map. -// Otherwise Unmarshal reuses the existing map, keeping existing entries. -// Unmarshal then stores key-value pairs from the JSON object into the map. -// -// If a JSON value is not appropriate for a given target type, -// or if a JSON number overflows the target type, Unmarshal -// skips that field and completes the unmarshaling as best it can. -// If no more serious errors are encountered, Unmarshal returns -// an UnmarshalTypeError describing the earliest such error. -// -// The JSON null value unmarshals into an interface, map, pointer, or slice -// by setting that Go value to nil. Because null is often used in JSON to mean -// ``not present,'' unmarshaling a JSON null into any other Go type has no effect -// on the value and produces no error. -// -// When unmarshaling quoted strings, invalid UTF-8 or -// invalid UTF-16 surrogate pairs are not treated as an error. -// Instead, they are replaced by the Unicode replacement -// character U+FFFD. -// -func Unmarshal(data []byte, v interface{}) error { - // Check for well-formedness. - // Avoids filling out half a data structure - // before discovering a JSON syntax error. - var d decodeState - err := checkValid(data, &d.scan) - if err != nil { - return err - } - - d.init(data) - return d.unmarshal(v) -} - -// Unmarshaler is the interface implemented by objects -// that can unmarshal a JSON description of themselves. -// The input can be assumed to be a valid encoding of -// a JSON value. UnmarshalJSON must copy the JSON data -// if it wishes to retain the data after returning. -type Unmarshaler interface { - UnmarshalJSON([]byte) error -} - -// An UnmarshalTypeError describes a JSON value that was -// not appropriate for a value of a specific Go type. -type UnmarshalTypeError struct { - Value string // description of JSON value - "bool", "array", "number -5" - Type reflect.Type // type of Go value it could not be assigned to - Offset int64 // error occurred after reading Offset bytes -} - -func (e *UnmarshalTypeError) Error() string { - return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() -} - -// An UnmarshalFieldError describes a JSON object key that -// led to an unexported (and therefore unwritable) struct field. -// (No longer used; kept for compatibility.) -type UnmarshalFieldError struct { - Key string - Type reflect.Type - Field reflect.StructField -} - -func (e *UnmarshalFieldError) Error() string { - return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() -} - -// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. -// (The argument to Unmarshal must be a non-nil pointer.) -type InvalidUnmarshalError struct { - Type reflect.Type -} - -func (e *InvalidUnmarshalError) Error() string { - if e.Type == nil { - return "json: Unmarshal(nil)" - } - - if e.Type.Kind() != reflect.Ptr { - return "json: Unmarshal(non-pointer " + e.Type.String() + ")" - } - return "json: Unmarshal(nil " + e.Type.String() + ")" -} - -func (d *decodeState) unmarshal(v interface{}) (err error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - err = r.(error) - } - }() - - rv := reflect.ValueOf(v) - if rv.Kind() != reflect.Ptr || rv.IsNil() { - return &InvalidUnmarshalError{reflect.TypeOf(v)} - } - - d.scan.reset() - // We decode rv not rv.Elem because the Unmarshaler interface - // test must be applied at the top level of the value. - d.value(rv) - return d.savedError -} - -// A Number represents a JSON number literal. -type Number string - -// String returns the literal text of the number. -func (n Number) String() string { return string(n) } - -// Float64 returns the number as a float64. -func (n Number) Float64() (float64, error) { - return strconv.ParseFloat(string(n), 64) -} - -// Int64 returns the number as an int64. -func (n Number) Int64() (int64, error) { - return strconv.ParseInt(string(n), 10, 64) -} - -// isValidNumber reports whether s is a valid JSON number literal. -func isValidNumber(s string) bool { - // This function implements the JSON numbers grammar. - // See https://tools.ietf.org/html/rfc7159#section-6 - // and http://json.org/number.gif - - if s == "" { - return false - } - - // Optional - - if s[0] == '-' { - s = s[1:] - if s == "" { - return false - } - } - - // Digits - switch { - default: - return false - - case s[0] == '0': - s = s[1:] - - case '1' <= s[0] && s[0] <= '9': - s = s[1:] - for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { - s = s[1:] - } - } - - // . followed by 1 or more digits. - if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' { - s = s[2:] - for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { - s = s[1:] - } - } - - // e or E followed by an optional - or + and - // 1 or more digits. - if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') { - s = s[1:] - if s[0] == '+' || s[0] == '-' { - s = s[1:] - if s == "" { - return false - } - } - for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { - s = s[1:] - } - } - - // Make sure we are at the end. - return s == "" -} - -// decodeState represents the state while decoding a JSON value. -type decodeState struct { - data []byte - off int // read offset in data - scan scanner - nextscan scanner // for calls to nextValue - savedError error - useNumber bool - canonical bool -} - -// errPhase is used for errors that should not happen unless -// there is a bug in the JSON decoder or something is editing -// the data slice while the decoder executes. -var errPhase = errors.New("JSON decoder out of sync - data changing underfoot?") - -func (d *decodeState) init(data []byte) *decodeState { - d.data = data - d.off = 0 - d.savedError = nil - return d -} - -// error aborts the decoding by panicking with err. -func (d *decodeState) error(err error) { - panic(err) -} - -// saveError saves the first err it is called with, -// for reporting at the end of the unmarshal. -func (d *decodeState) saveError(err error) { - if d.savedError == nil { - d.savedError = err - } -} - -// next cuts off and returns the next full JSON value in d.data[d.off:]. -// The next value is known to be an object or array, not a literal. -func (d *decodeState) next() []byte { - c := d.data[d.off] - item, rest, err := nextValue(d.data[d.off:], &d.nextscan) - if err != nil { - d.error(err) - } - d.off = len(d.data) - len(rest) - - // Our scanner has seen the opening brace/bracket - // and thinks we're still in the middle of the object. - // invent a closing brace/bracket to get it out. - if c == '{' { - d.scan.step(&d.scan, '}') - } else { - d.scan.step(&d.scan, ']') - } - - return item -} - -// scanWhile processes bytes in d.data[d.off:] until it -// receives a scan code not equal to op. -// It updates d.off and returns the new scan code. -func (d *decodeState) scanWhile(op int) int { - var newOp int - for { - if d.off >= len(d.data) { - newOp = d.scan.eof() - d.off = len(d.data) + 1 // mark processed EOF with len+1 - } else { - c := d.data[d.off] - d.off++ - newOp = d.scan.step(&d.scan, c) - } - if newOp != op { - break - } - } - return newOp -} - -// value decodes a JSON value from d.data[d.off:] into the value. -// it updates d.off to point past the decoded value. -func (d *decodeState) value(v reflect.Value) { - if !v.IsValid() { - _, rest, err := nextValue(d.data[d.off:], &d.nextscan) - if err != nil { - d.error(err) - } - d.off = len(d.data) - len(rest) - - // d.scan thinks we're still at the beginning of the item. - // Feed in an empty string - the shortest, simplest value - - // so that it knows we got to the end of the value. - if d.scan.redo { - // rewind. - d.scan.redo = false - d.scan.step = stateBeginValue - } - d.scan.step(&d.scan, '"') - d.scan.step(&d.scan, '"') - - n := len(d.scan.parseState) - if n > 0 && d.scan.parseState[n-1] == parseObjectKey { - // d.scan thinks we just read an object key; finish the object - d.scan.step(&d.scan, ':') - d.scan.step(&d.scan, '"') - d.scan.step(&d.scan, '"') - d.scan.step(&d.scan, '}') - } - - return - } - - switch op := d.scanWhile(scanSkipSpace); op { - default: - d.error(errPhase) - - case scanBeginArray: - d.array(v) - - case scanBeginObject: - d.object(v) - - case scanBeginLiteral: - d.literal(v) - } -} - -type unquotedValue struct{} - -// valueQuoted is like value but decodes a -// quoted string literal or literal null into an interface value. -// If it finds anything other than a quoted string literal or null, -// valueQuoted returns unquotedValue{}. -func (d *decodeState) valueQuoted() interface{} { - switch op := d.scanWhile(scanSkipSpace); op { - default: - d.error(errPhase) - - case scanBeginArray: - d.array(reflect.Value{}) - - case scanBeginObject: - d.object(reflect.Value{}) - - case scanBeginLiteral: - switch v := d.literalInterface().(type) { - case nil, string: - return v - } - } - return unquotedValue{} -} - -// indirect walks down v allocating pointers as needed, -// until it gets to a non-pointer. -// if it encounters an Unmarshaler, indirect stops and returns that. -// if decodingNull is true, indirect stops at the last pointer so it can be set to nil. -func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) { - // If v is a named type and is addressable, - // start with its address, so that if the type has pointer methods, - // we find them. - if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { - v = v.Addr() - } - for { - // Load value from interface, but only if the result will be - // usefully addressable. - if v.Kind() == reflect.Interface && !v.IsNil() { - e := v.Elem() - if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) { - v = e - continue - } - } - - if v.Kind() != reflect.Ptr { - break - } - - if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() { - break - } - if v.IsNil() { - v.Set(reflect.New(v.Type().Elem())) - } - if v.Type().NumMethod() > 0 { - if u, ok := v.Interface().(Unmarshaler); ok { - return u, nil, reflect.Value{} - } - if u, ok := v.Interface().(encoding.TextUnmarshaler); ok { - return nil, u, reflect.Value{} - } - } - v = v.Elem() - } - return nil, nil, v -} - -// array consumes an array from d.data[d.off-1:], decoding into the value v. -// the first byte of the array ('[') has been read already. -func (d *decodeState) array(v reflect.Value) { - // Check for unmarshaler. - u, ut, pv := d.indirect(v, false) - if u != nil { - d.off-- - err := u.UnmarshalJSON(d.next()) - if err != nil { - d.error(err) - } - return - } - if ut != nil { - d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)}) - d.off-- - d.next() - return - } - - v = pv - - // Check type of target. - switch v.Kind() { - case reflect.Interface: - if v.NumMethod() == 0 { - // Decoding into nil interface? Switch to non-reflect code. - v.Set(reflect.ValueOf(d.arrayInterface())) - return - } - // Otherwise it's invalid. - fallthrough - default: - d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)}) - d.off-- - d.next() - return - case reflect.Array: - case reflect.Slice: - break - } - - i := 0 - for { - // Look ahead for ] - can only happen on first iteration. - op := d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - - // Back up so d.value can have the byte we just read. - d.off-- - d.scan.undo(op) - - // Get element of array, growing if necessary. - if v.Kind() == reflect.Slice { - // Grow slice if necessary - if i >= v.Cap() { - newcap := v.Cap() + v.Cap()/2 - if newcap < 4 { - newcap = 4 - } - newv := reflect.MakeSlice(v.Type(), v.Len(), newcap) - reflect.Copy(newv, v) - v.Set(newv) - } - if i >= v.Len() { - v.SetLen(i + 1) - } - } - - if i < v.Len() { - // Decode into element. - d.value(v.Index(i)) - } else { - // Ran out of fixed array: skip. - d.value(reflect.Value{}) - } - i++ - - // Next token must be , or ]. - op = d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - if op != scanArrayValue { - d.error(errPhase) - } - } - - if i < v.Len() { - if v.Kind() == reflect.Array { - // Array. Zero the rest. - z := reflect.Zero(v.Type().Elem()) - for ; i < v.Len(); i++ { - v.Index(i).Set(z) - } - } else { - v.SetLen(i) - } - } - if i == 0 && v.Kind() == reflect.Slice { - v.Set(reflect.MakeSlice(v.Type(), 0, 0)) - } -} - -var nullLiteral = []byte("null") - -// object consumes an object from d.data[d.off-1:], decoding into the value v. -// the first byte ('{') of the object has been read already. -func (d *decodeState) object(v reflect.Value) { - // Check for unmarshaler. - u, ut, pv := d.indirect(v, false) - if u != nil { - d.off-- - err := u.UnmarshalJSON(d.next()) - if err != nil { - d.error(err) - } - return - } - if ut != nil { - d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) - d.off-- - d.next() // skip over { } in input - return - } - v = pv - - // Decoding into nil interface? Switch to non-reflect code. - if v.Kind() == reflect.Interface && v.NumMethod() == 0 { - v.Set(reflect.ValueOf(d.objectInterface())) - return - } - - // Check type of target: struct or map[string]T - switch v.Kind() { - case reflect.Map: - // map must have string kind - t := v.Type() - if t.Key().Kind() != reflect.String { - d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) - d.off-- - d.next() // skip over { } in input - return - } - if v.IsNil() { - v.Set(reflect.MakeMap(t)) - } - case reflect.Struct: - - default: - d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) - d.off-- - d.next() // skip over { } in input - return - } - - var mapElem reflect.Value - - for { - // Read opening " of string key or closing }. - op := d.scanWhile(scanSkipSpace) - if op == scanEndObject { - // closing } - can only happen on first iteration. - break - } - if op != scanBeginLiteral { - d.error(errPhase) - } - - // Read key. - start := d.off - 1 - op = d.scanWhile(scanContinue) - item := d.data[start : d.off-1] - key, ok := unquoteBytes(item) - if !ok { - d.error(errPhase) - } - - // Figure out field corresponding to key. - var subv reflect.Value - destring := false // whether the value is wrapped in a string to be decoded first - - if v.Kind() == reflect.Map { - elemType := v.Type().Elem() - if !mapElem.IsValid() { - mapElem = reflect.New(elemType).Elem() - } else { - mapElem.Set(reflect.Zero(elemType)) - } - subv = mapElem - } else { - var f *field - fields := cachedTypeFields(v.Type(), false) - for i := range fields { - ff := &fields[i] - if bytes.Equal(ff.nameBytes, key) { - f = ff - break - } - if f == nil && ff.equalFold(ff.nameBytes, key) { - f = ff - } - } - if f != nil { - subv = v - destring = f.quoted - for _, i := range f.index { - if subv.Kind() == reflect.Ptr { - if subv.IsNil() { - subv.Set(reflect.New(subv.Type().Elem())) - } - subv = subv.Elem() - } - subv = subv.Field(i) - } - } - } - - // Read : before value. - if op == scanSkipSpace { - op = d.scanWhile(scanSkipSpace) - } - if op != scanObjectKey { - d.error(errPhase) - } - - // Read value. - if destring { - switch qv := d.valueQuoted().(type) { - case nil: - d.literalStore(nullLiteral, subv, false) - case string: - d.literalStore([]byte(qv), subv, true) - default: - d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into %v", subv.Type())) - } - } else { - d.value(subv) - } - - // Write value back to map; - // if using struct, subv points into struct already. - if v.Kind() == reflect.Map { - kv := reflect.ValueOf(key).Convert(v.Type().Key()) - v.SetMapIndex(kv, subv) - } - - // Next token must be , or }. - op = d.scanWhile(scanSkipSpace) - if op == scanEndObject { - break - } - if op != scanObjectValue { - d.error(errPhase) - } - } -} - -// literal consumes a literal from d.data[d.off-1:], decoding into the value v. -// The first byte of the literal has been read already -// (that's how the caller knows it's a literal). -func (d *decodeState) literal(v reflect.Value) { - // All bytes inside literal return scanContinue op code. - start := d.off - 1 - op := d.scanWhile(scanContinue) - - // Scan read one byte too far; back up. - d.off-- - d.scan.undo(op) - - d.literalStore(d.data[start:d.off], v, false) -} - -// convertNumber converts the number literal s to a float64 or a Number -// depending on the setting of d.useNumber. -func (d *decodeState) convertNumber(s string) (interface{}, error) { - if d.useNumber { - return Number(s), nil - } - f, err := strconv.ParseFloat(s, 64) - if err != nil { - return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)} - } - return f, nil -} - -var numberType = reflect.TypeOf(Number("")) - -// literalStore decodes a literal stored in item into v. -// -// fromQuoted indicates whether this literal came from unwrapping a -// string from the ",string" struct tag option. this is used only to -// produce more helpful error messages. -func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) { - // Check for unmarshaler. - if len(item) == 0 { - //Empty string given - d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - return - } - wantptr := item[0] == 'n' // null - u, ut, pv := d.indirect(v, wantptr) - if u != nil { - err := u.UnmarshalJSON(item) - if err != nil { - d.error(err) - } - return - } - if ut != nil { - if item[0] != '"' { - if fromQuoted { - d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) - } - return - } - s, ok := unquoteBytes(item) - if !ok { - if fromQuoted { - d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.error(errPhase) - } - } - err := ut.UnmarshalText(s) - if err != nil { - d.error(err) - } - return - } - - v = pv - - switch c := item[0]; c { - case 'n': // null - switch v.Kind() { - case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: - v.Set(reflect.Zero(v.Type())) - // otherwise, ignore null for primitives/string - } - case 't', 'f': // true, false - value := c == 't' - switch v.Kind() { - default: - if fromQuoted { - d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)}) - } - case reflect.Bool: - v.SetBool(value) - case reflect.Interface: - if v.NumMethod() == 0 { - v.Set(reflect.ValueOf(value)) - } else { - d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)}) - } - } - - case '"': // string - s, ok := unquoteBytes(item) - if !ok { - if fromQuoted { - d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.error(errPhase) - } - } - switch v.Kind() { - default: - d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) - case reflect.Slice: - if v.Type().Elem().Kind() != reflect.Uint8 { - d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) - break - } - b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) - n, err := base64.StdEncoding.Decode(b, s) - if err != nil { - d.saveError(err) - break - } - v.SetBytes(b[:n]) - case reflect.String: - v.SetString(string(s)) - case reflect.Interface: - if v.NumMethod() == 0 { - v.Set(reflect.ValueOf(string(s))) - } else { - d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) - } - } - - default: // number - if c != '-' && (c < '0' || c > '9') { - if fromQuoted { - d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.error(errPhase) - } - } - s := string(item) - switch v.Kind() { - default: - if v.Kind() == reflect.String && v.Type() == numberType { - v.SetString(s) - if !isValidNumber(s) { - d.error(fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", item)) - } - break - } - if fromQuoted { - d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.error(&UnmarshalTypeError{"number", v.Type(), int64(d.off)}) - } - case reflect.Interface: - n, err := d.convertNumber(s) - if err != nil { - d.saveError(err) - break - } - if v.NumMethod() != 0 { - d.saveError(&UnmarshalTypeError{"number", v.Type(), int64(d.off)}) - break - } - v.Set(reflect.ValueOf(n)) - - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - n, err := strconv.ParseInt(s, 10, 64) - if err != nil || v.OverflowInt(n) { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) - break - } - v.SetInt(n) - - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - n, err := strconv.ParseUint(s, 10, 64) - if err != nil || v.OverflowUint(n) { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) - break - } - v.SetUint(n) - - case reflect.Float32, reflect.Float64: - n, err := strconv.ParseFloat(s, v.Type().Bits()) - if err != nil || v.OverflowFloat(n) { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) - break - } - v.SetFloat(n) - } - } -} - -// The xxxInterface routines build up a value to be stored -// in an empty interface. They are not strictly necessary, -// but they avoid the weight of reflection in this common case. - -// valueInterface is like value but returns interface{} -func (d *decodeState) valueInterface() interface{} { - switch d.scanWhile(scanSkipSpace) { - default: - d.error(errPhase) - panic("unreachable") - case scanBeginArray: - return d.arrayInterface() - case scanBeginObject: - return d.objectInterface() - case scanBeginLiteral: - return d.literalInterface() - } -} - -// arrayInterface is like array but returns []interface{}. -func (d *decodeState) arrayInterface() []interface{} { - var v = make([]interface{}, 0) - for { - // Look ahead for ] - can only happen on first iteration. - op := d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - - // Back up so d.value can have the byte we just read. - d.off-- - d.scan.undo(op) - - v = append(v, d.valueInterface()) - - // Next token must be , or ]. - op = d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - if op != scanArrayValue { - d.error(errPhase) - } - } - return v -} - -// objectInterface is like object but returns map[string]interface{}. -func (d *decodeState) objectInterface() map[string]interface{} { - m := make(map[string]interface{}) - for { - // Read opening " of string key or closing }. - op := d.scanWhile(scanSkipSpace) - if op == scanEndObject { - // closing } - can only happen on first iteration. - break - } - if op != scanBeginLiteral { - d.error(errPhase) - } - - // Read string key. - start := d.off - 1 - op = d.scanWhile(scanContinue) - item := d.data[start : d.off-1] - key, ok := unquote(item) - if !ok { - d.error(errPhase) - } - - // Read : before value. - if op == scanSkipSpace { - op = d.scanWhile(scanSkipSpace) - } - if op != scanObjectKey { - d.error(errPhase) - } - - // Read value. - m[key] = d.valueInterface() - - // Next token must be , or }. - op = d.scanWhile(scanSkipSpace) - if op == scanEndObject { - break - } - if op != scanObjectValue { - d.error(errPhase) - } - } - return m -} - -// literalInterface is like literal but returns an interface value. -func (d *decodeState) literalInterface() interface{} { - // All bytes inside literal return scanContinue op code. - start := d.off - 1 - op := d.scanWhile(scanContinue) - - // Scan read one byte too far; back up. - d.off-- - d.scan.undo(op) - item := d.data[start:d.off] - - switch c := item[0]; c { - case 'n': // null - return nil - - case 't', 'f': // true, false - return c == 't' - - case '"': // string - s, ok := unquote(item) - if !ok { - d.error(errPhase) - } - return s - - default: // number - if c != '-' && (c < '0' || c > '9') { - d.error(errPhase) - } - n, err := d.convertNumber(string(item)) - if err != nil { - d.saveError(err) - } - return n - } -} - -// getu4 decodes \uXXXX from the beginning of s, returning the hex value, -// or it returns -1. -func getu4(s []byte) rune { - if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { - return -1 - } - r, err := strconv.ParseUint(string(s[2:6]), 16, 64) - if err != nil { - return -1 - } - return rune(r) -} - -// unquote converts a quoted JSON string literal s into an actual string t. -// The rules are different than for Go, so cannot use strconv.Unquote. -func unquote(s []byte) (t string, ok bool) { - s, ok = unquoteBytes(s) - t = string(s) - return -} - -func unquoteBytes(s []byte) (t []byte, ok bool) { - if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { - return - } - s = s[1 : len(s)-1] - - // Check for unusual characters. If there are none, - // then no unquoting is needed, so return a slice of the - // original bytes. - r := 0 - for r < len(s) { - c := s[r] - if c == '\\' || c == '"' || c < ' ' { - break - } - if c < utf8.RuneSelf { - r++ - continue - } - rr, size := utf8.DecodeRune(s[r:]) - if rr == utf8.RuneError && size == 1 { - break - } - r += size - } - if r == len(s) { - return s, true - } - - b := make([]byte, len(s)+2*utf8.UTFMax) - w := copy(b, s[0:r]) - for r < len(s) { - // Out of room? Can only happen if s is full of - // malformed UTF-8 and we're replacing each - // byte with RuneError. - if w >= len(b)-2*utf8.UTFMax { - nb := make([]byte, (len(b)+utf8.UTFMax)*2) - copy(nb, b[0:w]) - b = nb - } - switch c := s[r]; { - case c == '\\': - r++ - if r >= len(s) { - return - } - switch s[r] { - default: - return - case '"', '\\', '/', '\'': - b[w] = s[r] - r++ - w++ - case 'b': - b[w] = '\b' - r++ - w++ - case 'f': - b[w] = '\f' - r++ - w++ - case 'n': - b[w] = '\n' - r++ - w++ - case 'r': - b[w] = '\r' - r++ - w++ - case 't': - b[w] = '\t' - r++ - w++ - case 'u': - r-- - rr := getu4(s[r:]) - if rr < 0 { - return - } - r += 6 - if utf16.IsSurrogate(rr) { - rr1 := getu4(s[r:]) - if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { - // A valid pair; consume. - r += 6 - w += utf8.EncodeRune(b[w:], dec) - break - } - // Invalid surrogate; fall back to replacement rune. - rr = unicode.ReplacementChar - } - w += utf8.EncodeRune(b[w:], rr) - } - - // Quote, control characters are invalid. - case c == '"', c < ' ': - return - - // ASCII - case c < utf8.RuneSelf: - b[w] = c - r++ - w++ - - // Coerce to well-formed UTF-8. - default: - rr, size := utf8.DecodeRune(s[r:]) - r += size - w += utf8.EncodeRune(b[w:], rr) - } - } - return b[0:w], true -} diff --git a/vendor/github.com/docker/go/canonical/json/encode.go b/vendor/github.com/docker/go/canonical/json/encode.go deleted file mode 100644 index f3491b160363..000000000000 --- a/vendor/github.com/docker/go/canonical/json/encode.go +++ /dev/null @@ -1,1250 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package json implements encoding and decoding of JSON objects as defined in -// RFC 4627. The mapping between JSON objects and Go values is described -// in the documentation for the Marshal and Unmarshal functions. -// -// See "JSON and Go" for an introduction to this package: -// https://golang.org/doc/articles/json_and_go.html -package json - -import ( - "bytes" - "encoding" - "encoding/base64" - "fmt" - "math" - "reflect" - "runtime" - "sort" - "strconv" - "strings" - "sync" - "unicode" - "unicode/utf8" -) - -// Marshal returns the JSON encoding of v. -// -// Marshal traverses the value v recursively. -// If an encountered value implements the Marshaler interface -// and is not a nil pointer, Marshal calls its MarshalJSON method -// to produce JSON. If no MarshalJSON method is present but the -// value implements encoding.TextMarshaler instead, Marshal calls -// its MarshalText method. -// The nil pointer exception is not strictly necessary -// but mimics a similar, necessary exception in the behavior of -// UnmarshalJSON. -// -// Otherwise, Marshal uses the following type-dependent default encodings: -// -// Boolean values encode as JSON booleans. -// -// Floating point, integer, and Number values encode as JSON numbers. -// -// String values encode as JSON strings coerced to valid UTF-8, -// replacing invalid bytes with the Unicode replacement rune. -// The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e" -// to keep some browsers from misinterpreting JSON output as HTML. -// Ampersand "&" is also escaped to "\u0026" for the same reason. -// -// Array and slice values encode as JSON arrays, except that -// []byte encodes as a base64-encoded string, and a nil slice -// encodes as the null JSON object. -// -// Struct values encode as JSON objects. Each exported struct field -// becomes a member of the object unless -// - the field's tag is "-", or -// - the field is empty and its tag specifies the "omitempty" option. -// The empty values are false, 0, any -// nil pointer or interface value, and any array, slice, map, or string of -// length zero. The object's default key string is the struct field name -// but can be specified in the struct field's tag value. The "json" key in -// the struct field's tag value is the key name, followed by an optional comma -// and options. Examples: -// -// // Field is ignored by this package. -// Field int `json:"-"` -// -// // Field appears in JSON as key "myName". -// Field int `json:"myName"` -// -// // Field appears in JSON as key "myName" and -// // the field is omitted from the object if its value is empty, -// // as defined above. -// Field int `json:"myName,omitempty"` -// -// // Field appears in JSON as key "Field" (the default), but -// // the field is skipped if empty. -// // Note the leading comma. -// Field int `json:",omitempty"` -// -// The "string" option signals that a field is stored as JSON inside a -// JSON-encoded string. It applies only to fields of string, floating point, -// integer, or boolean types. This extra level of encoding is sometimes used -// when communicating with JavaScript programs: -// -// Int64String int64 `json:",string"` -// -// The key name will be used if it's a non-empty string consisting of -// only Unicode letters, digits, dollar signs, percent signs, hyphens, -// underscores and slashes. -// -// Anonymous struct fields are usually marshaled as if their inner exported fields -// were fields in the outer struct, subject to the usual Go visibility rules amended -// as described in the next paragraph. -// An anonymous struct field with a name given in its JSON tag is treated as -// having that name, rather than being anonymous. -// An anonymous struct field of interface type is treated the same as having -// that type as its name, rather than being anonymous. -// -// The Go visibility rules for struct fields are amended for JSON when -// deciding which field to marshal or unmarshal. If there are -// multiple fields at the same level, and that level is the least -// nested (and would therefore be the nesting level selected by the -// usual Go rules), the following extra rules apply: -// -// 1) Of those fields, if any are JSON-tagged, only tagged fields are considered, -// even if there are multiple untagged fields that would otherwise conflict. -// 2) If there is exactly one field (tagged or not according to the first rule), that is selected. -// 3) Otherwise there are multiple fields, and all are ignored; no error occurs. -// -// Handling of anonymous struct fields is new in Go 1.1. -// Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of -// an anonymous struct field in both current and earlier versions, give the field -// a JSON tag of "-". -// -// Map values encode as JSON objects. -// The map's key type must be string; the map keys are used as JSON object -// keys, subject to the UTF-8 coercion described for string values above. -// -// Pointer values encode as the value pointed to. -// A nil pointer encodes as the null JSON object. -// -// Interface values encode as the value contained in the interface. -// A nil interface value encodes as the null JSON object. -// -// Channel, complex, and function values cannot be encoded in JSON. -// Attempting to encode such a value causes Marshal to return -// an UnsupportedTypeError. -// -// JSON cannot represent cyclic data structures and Marshal does not -// handle them. Passing cyclic structures to Marshal will result in -// an infinite recursion. -// -func Marshal(v interface{}) ([]byte, error) { - return marshal(v, false) -} - -// MarshalIndent is like Marshal but applies Indent to format the output. -func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { - b, err := Marshal(v) - if err != nil { - return nil, err - } - var buf bytes.Buffer - err = Indent(&buf, b, prefix, indent) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// MarshalCanonical is like Marshal but encodes into Canonical JSON. -// Read more at: http://wiki.laptop.org/go/Canonical_JSON -func MarshalCanonical(v interface{}) ([]byte, error) { - return marshal(v, true) -} - -func marshal(v interface{}, canonical bool) ([]byte, error) { - e := &encodeState{canonical: canonical} - err := e.marshal(v) - if err != nil { - return nil, err - } - return e.Bytes(), nil -} - -// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029 -// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029 -// so that the JSON will be safe to embed inside HTML