Skip to content

Commit 6d531d9

Browse files
committed
build: remove DCT support for classic builder
Docker Content Trust is currently only implemented for the classic builder, but is known to not work with multi-stage builds, and requires rewriting the Dockerfile, which is brittle because the Dockerfile syntax evolved with the introduction of BuildKit as default builder. Given that the classic builder is deprecated, and only used for Windows images, which are not verified by content trust; # docker pull --disable-content-trust=false mcr.microsoft.com/windows/servercore:ltsc2025 Error: remote trust data does not exist for mcr.microsoft.com/windows/servercore: mcr.microsoft.com does not have trust data for mcr.microsoft.com/windows/servercore With content trust not implemented in BuildKit, and not implemented in docker compose, this resulted in an inconsistent behavior. This patch removes content-trust support for "docker build". As this is a client-side feature, users who require this feature can still use an older CLI to to start the build. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
1 parent 71bc8ab commit 6d531d9

File tree

8 files changed

+2
-159
lines changed

8 files changed

+2
-159
lines changed

cli/command/image/build.go

Lines changed: 2 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package image
22

33
import (
4-
"archive/tar"
5-
"bufio"
64
"bytes"
75
"context"
86
"encoding/json"
@@ -20,9 +18,7 @@ import (
2018
"github.com/docker/cli/cli/command/completion"
2119
"github.com/docker/cli/cli/command/image/build"
2220
"github.com/docker/cli/cli/streams"
23-
"github.com/docker/cli/cli/trust"
2421
"github.com/docker/cli/internal/jsonstream"
25-
"github.com/docker/cli/internal/lazyregexp"
2622
"github.com/docker/cli/opts"
2723
buildtypes "github.com/docker/docker/api/types/build"
2824
"github.com/docker/docker/api/types/container"
@@ -65,7 +61,6 @@ type buildOptions struct {
6561
target string
6662
imageIDFile string
6763
platform string
68-
untrusted bool
6964
}
7065

7166
// dockerfileFromStdin returns true when the user specified that the Dockerfile
@@ -144,7 +139,8 @@ func NewBuildCommand(dockerCli command.Cli) *cobra.Command {
144139
flags.SetAnnotation("target", annotation.ExternalURL, []string{"https://docs.docker.com/reference/cli/docker/buildx/build/#target"})
145140
flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file")
146141

147-
command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled())
142+
flags.Bool("disable-content-trust", dockerCli.ContentTrustEnabled(), "Skip image verification (deprecated)")
143+
_ = flags.MarkHidden("disable-content-trust")
148144

149145
flags.StringVar(&options.platform, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable")
150146
flags.SetAnnotation("platform", "version", []string{"1.38"})
@@ -286,26 +282,6 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
286282
ctx, cancel := context.WithCancel(ctx)
287283
defer cancel()
288284

289-
var resolvedTags []*resolvedTag
290-
if !options.untrusted {
291-
translator := func(ctx context.Context, ref reference.NamedTagged) (reference.Canonical, error) {
292-
return TrustedReference(ctx, dockerCli, ref)
293-
}
294-
// if there is a tar wrapper, the dockerfile needs to be replaced inside it
295-
if buildCtx != nil {
296-
// Wrap the tar archive to replace the Dockerfile entry with the rewritten
297-
// Dockerfile which uses trusted pulls.
298-
buildCtx = replaceDockerfileForContentTrust(ctx, buildCtx, relDockerfile, translator, &resolvedTags)
299-
} else if dockerfileCtx != nil {
300-
// if there was not archive context still do the possible replacements in Dockerfile
301-
newDockerfile, _, err := rewriteDockerfileFromForContentTrust(ctx, dockerfileCtx, translator)
302-
if err != nil {
303-
return err
304-
}
305-
dockerfileCtx = io.NopCloser(bytes.NewBuffer(newDockerfile))
306-
}
307-
}
308-
309285
if options.compress {
310286
buildCtx, err = build.Compress(buildCtx)
311287
if err != nil {
@@ -402,21 +378,10 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
402378
return err
403379
}
404380
}
405-
if !options.untrusted {
406-
// Since the build was successful, now we must tag any of the resolved
407-
// images from the above Dockerfile rewrite.
408-
for _, resolved := range resolvedTags {
409-
if err := trust.TagTrusted(ctx, dockerCli.Client(), dockerCli.Err(), resolved.digestRef, resolved.tagRef); err != nil {
410-
return err
411-
}
412-
}
413-
}
414381

415382
return nil
416383
}
417384

418-
type translatorFunc func(context.Context, reference.NamedTagged) (reference.Canonical, error)
419-
420385
// validateTag checks if the given image name can be resolved.
421386
func validateTag(rawRepo string) (string, error) {
422387
_, err := reference.ParseNormalizedNamed(rawRepo)
@@ -427,118 +392,6 @@ func validateTag(rawRepo string) (string, error) {
427392
return rawRepo, nil
428393
}
429394

430-
var dockerfileFromLinePattern = lazyregexp.New(`(?i)^[\s]*FROM[ \f\r\t\v]+(?P<image>[^ \f\r\t\v\n#]+)`)
431-
432-
// resolvedTag records the repository, tag, and resolved digest reference
433-
// from a Dockerfile rewrite.
434-
type resolvedTag struct {
435-
digestRef reference.Canonical
436-
tagRef reference.NamedTagged
437-
}
438-
439-
// noBaseImageSpecifier is the symbol used by the FROM
440-
// command to specify that no base image is to be used.
441-
const noBaseImageSpecifier = "scratch"
442-
443-
// rewriteDockerfileFromForContentTrust rewrites the given Dockerfile by resolving images in
444-
// "FROM <image>" instructions to a digest reference. `translator` is a
445-
// function that takes a repository name and tag reference and returns a
446-
// trusted digest reference.
447-
// This should be called *only* when content trust is enabled
448-
func rewriteDockerfileFromForContentTrust(ctx context.Context, dockerfile io.Reader, translator translatorFunc) (newDockerfile []byte, resolvedTags []*resolvedTag, err error) {
449-
scanner := bufio.NewScanner(dockerfile)
450-
buf := bytes.NewBuffer(nil)
451-
452-
// Scan the lines of the Dockerfile, looking for a "FROM" line.
453-
for scanner.Scan() {
454-
line := scanner.Text()
455-
456-
matches := dockerfileFromLinePattern.FindStringSubmatch(line)
457-
if matches != nil && matches[1] != noBaseImageSpecifier {
458-
// Replace the line with a resolved "FROM repo@digest"
459-
var ref reference.Named
460-
ref, err = reference.ParseNormalizedNamed(matches[1])
461-
if err != nil {
462-
return nil, nil, err
463-
}
464-
ref = reference.TagNameOnly(ref)
465-
if ref, ok := ref.(reference.NamedTagged); ok {
466-
trustedRef, err := translator(ctx, ref)
467-
if err != nil {
468-
return nil, nil, err
469-
}
470-
471-
line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, "FROM "+reference.FamiliarString(trustedRef))
472-
resolvedTags = append(resolvedTags, &resolvedTag{
473-
digestRef: trustedRef,
474-
tagRef: ref,
475-
})
476-
}
477-
}
478-
479-
_, err := fmt.Fprintln(buf, line)
480-
if err != nil {
481-
return nil, nil, err
482-
}
483-
}
484-
485-
return buf.Bytes(), resolvedTags, scanner.Err()
486-
}
487-
488-
// replaceDockerfileForContentTrust wraps the given input tar archive stream and
489-
// uses the translator to replace the Dockerfile which uses a trusted reference.
490-
// Returns a new tar archive stream with the replaced Dockerfile.
491-
func replaceDockerfileForContentTrust(ctx context.Context, inputTarStream io.ReadCloser, dockerfileName string, translator translatorFunc, resolvedTags *[]*resolvedTag) io.ReadCloser {
492-
pipeReader, pipeWriter := io.Pipe()
493-
go func() {
494-
tarReader := tar.NewReader(inputTarStream)
495-
tarWriter := tar.NewWriter(pipeWriter)
496-
497-
defer inputTarStream.Close()
498-
499-
for {
500-
hdr, err := tarReader.Next()
501-
if err == io.EOF {
502-
// Signals end of archive.
503-
_ = tarWriter.Close()
504-
_ = pipeWriter.Close()
505-
return
506-
}
507-
if err != nil {
508-
_ = pipeWriter.CloseWithError(err)
509-
return
510-
}
511-
512-
content := io.Reader(tarReader)
513-
if hdr.Name == dockerfileName {
514-
// This entry is the Dockerfile. Since the tar archive was
515-
// generated from a directory on the local filesystem, the
516-
// Dockerfile will only appear once in the archive.
517-
var newDockerfile []byte
518-
newDockerfile, *resolvedTags, err = rewriteDockerfileFromForContentTrust(ctx, content, translator)
519-
if err != nil {
520-
_ = pipeWriter.CloseWithError(err)
521-
return
522-
}
523-
hdr.Size = int64(len(newDockerfile))
524-
content = bytes.NewBuffer(newDockerfile)
525-
}
526-
527-
if err := tarWriter.WriteHeader(hdr); err != nil {
528-
_ = pipeWriter.CloseWithError(err)
529-
return
530-
}
531-
532-
if _, err := io.Copy(tarWriter, content); err != nil {
533-
_ = pipeWriter.CloseWithError(err)
534-
return
535-
}
536-
}
537-
}()
538-
539-
return pipeReader
540-
}
541-
542395
func imageBuildOptions(dockerCli command.Cli, options buildOptions) buildtypes.ImageBuildOptions {
543396
configFile := dockerCli.ConfigFile()
544397
return buildtypes.ImageBuildOptions{

cli/command/image/build_test.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) {
4747
options.compress = true
4848
options.dockerfileName = "-"
4949
options.context = dir.Path()
50-
options.untrusted = true
5150
assert.NilError(t, runBuild(context.TODO(), cli, options))
5251

5352
expected := []string{fakeBuild.options.Dockerfile, ".dockerignore", "foo"}
@@ -74,7 +73,6 @@ func TestRunBuildResetsUidAndGidInContext(t *testing.T) {
7473

7574
options := newBuildOptions()
7675
options.context = dir.Path()
77-
options.untrusted = true
7876
assert.NilError(t, runBuild(context.TODO(), cli, options))
7977

8078
headers := fakeBuild.headers(t)
@@ -109,7 +107,6 @@ COPY data /data
109107
options := newBuildOptions()
110108
options.context = dir.Path()
111109
options.dockerfileName = df.Path()
112-
options.untrusted = true
113110
assert.NilError(t, runBuild(context.TODO(), cli, options))
114111

115112
expected := []string{fakeBuild.options.Dockerfile, ".dockerignore", "data"}
@@ -170,7 +167,6 @@ RUN echo hello world
170167
cli := test.NewFakeCli(&fakeClient{imageBuildFunc: fakeBuild.build})
171168
options := newBuildOptions()
172169
options.context = tmpDir.Join("context-link")
173-
options.untrusted = true
174170
assert.NilError(t, runBuild(context.TODO(), cli, options))
175171

176172
assert.DeepEqual(t, fakeBuild.filenames(t), []string{"Dockerfile"})

contrib/completion/bash/docker

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2803,7 +2803,6 @@ _docker_image_build() {
28032803
"
28042804

28052805
local boolean_options="
2806-
--disable-content-trust=false
28072806
--force-rm
28082807
--help
28092808
--no-cache

contrib/completion/fish/docker.fish

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l cpu-quota -d
139139
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -s c -l cpu-shares -d 'CPU shares (relative weight)'
140140
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)'
141141
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)'
142-
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l disable-content-trust -d 'Skip image verification'
143142
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -s f -l file -d "Name of the Dockerfile (Default is ‘PATH/Dockerfile’)"
144143
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l force-rm -d 'Always remove intermediate containers'
145144
complete -c docker -A -f -n '__fish_seen_subcommand_from build' -l help -d 'Print usage'

contrib/completion/zsh/_docker

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1005,7 +1005,6 @@ __docker_image_subcommand() {
10051005
"($help)--cpu-rt-runtime=[Limit the CPU real-time runtime]:CPU real-time runtime in microseconds: " \
10061006
"($help)--cpuset-cpus=[CPUs in which to allow execution]:CPUs: " \
10071007
"($help)--cpuset-mems=[MEMs in which to allow execution]:MEMs: " \
1008-
"($help)--disable-content-trust[Skip image verification]" \
10091008
"($help -f --file)"{-f=,--file=}"[Name of the Dockerfile]:Dockerfile:_files" \
10101009
"($help)--force-rm[Always remove intermediate containers]" \
10111010
"($help)--isolation=[Container isolation technology]:isolation:(default hyperv process)" \

docs/reference/commandline/build.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ Build an image from a Dockerfile
2121
| `-c`, `--cpu-shares` | `int64` | `0` | CPU shares (relative weight) |
2222
| `--cpuset-cpus` | `string` | | CPUs in which to allow execution (0-3, 0,1) |
2323
| `--cpuset-mems` | `string` | | MEMs in which to allow execution (0-3, 0,1) |
24-
| `--disable-content-trust` | `bool` | `true` | Skip image verification |
2524
| [`-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`) |
2625
| `--force-rm` | `bool` | | Always remove intermediate containers |
2726
| `--iidfile` | `string` | | Write the image ID to the file |

docs/reference/commandline/builder_build.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ Build an image from a Dockerfile
2121
| `-c`, `--cpu-shares` | `int64` | `0` | CPU shares (relative weight) |
2222
| `--cpuset-cpus` | `string` | | CPUs in which to allow execution (0-3, 0,1) |
2323
| `--cpuset-mems` | `string` | | MEMs in which to allow execution (0-3, 0,1) |
24-
| `--disable-content-trust` | `bool` | `true` | Skip image verification |
2524
| [`-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`) |
2625
| `--force-rm` | `bool` | | Always remove intermediate containers |
2726
| `--iidfile` | `string` | | Write the image ID to the file |

docs/reference/commandline/image_build.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ Build an image from a Dockerfile
2121
| `-c`, `--cpu-shares` | `int64` | `0` | CPU shares (relative weight) |
2222
| `--cpuset-cpus` | `string` | | CPUs in which to allow execution (0-3, 0,1) |
2323
| `--cpuset-mems` | `string` | | MEMs in which to allow execution (0-3, 0,1) |
24-
| `--disable-content-trust` | `bool` | `true` | Skip image verification |
2524
| [`-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`) |
2625
| `--force-rm` | `bool` | | Always remove intermediate containers |
2726
| `--iidfile` | `string` | | Write the image ID to the file |

0 commit comments

Comments
 (0)