Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ expect - see [Known Issues](#known-issues).
- [Flag `FF_KANIKO_CROSS_REPO_MOUNT`](#flag-ff_kaniko_cross_repo_mount)
- [Flag `FF_KANIKO_PATH_SCOPED_REGISTRY_AUTH`](#flag-ff_kaniko_path_scoped_registry_auth)
- [Flag `FF_KANIKO_DEPRECATE_LAYERLESS_CACHE_ENTRIES`](#flag-ff_kaniko_deprecate_layerless_cache_entries)
- [Flag `FF_KANIKO_ADD_CHECKSUM`](#flag-ff_kaniko_add_checksum)
- [Assertion Overrides](#assertion-overrides)
- [Telemetry](#telemetry)
- [Debug Image](#debug-image)
Expand Down Expand Up @@ -1488,6 +1489,13 @@ Before `v1.25.0` a command that changed no files, `WORKDIR /` for example, was c
Defaults to `false`.
Becomes default in `v1.29.0`.

#### Flag `FF_KANIKO_ADD_CHECKSUM`

`ADD --checksum=sha256:<hex> <url> <dest>` states that the download has to hash to the given digest. With this flag off kaniko parses the flag and never checks it, so a substituted download is added to the image and the build succeeds.
Set this flag to `true` to verify the download and fail the build on a mismatch.
Defaults to `false`.
Becomes default in `v1.29.0`.

### Assertion Overrides

Kaniko checks internal invariants at runtime. If one is violated the build stops with a message like:
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ require (
github.com/moby/moby/api v1.55.0
github.com/moby/patternmatcher v0.6.1
github.com/moby/sys/signal v0.7.1
github.com/opencontainers/go-digest v1.0.0
github.com/osscontainertools/docker-credential-acr v0.8.0
github.com/otiai10/copy v1.14.1
github.com/sirupsen/logrus v1.10.1
Expand Down Expand Up @@ -121,7 +122,6 @@ require (
github.com/moby/sys/sequential v0.7.0 // indirect
github.com/moby/sys/user v0.4.1 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/otiai10/mint v1.6.3 // indirect
github.com/pjbgf/sha1cd v0.6.0 // indirect
Expand Down
12 changes: 12 additions & 0 deletions integration/dockerfiles/Dockerfile_test_issue_3373
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM busybox:1.31

# 3373: ADD --checksum was parsed and never verified, so a substituted download
# passed. Every digest here matches the release asset, so the build succeeds and
# the added files are the ones docker adds.
ARG DIGEST
ENV URL=https://github.com/GoogleCloudPlatform/docker-credential-gcr/releases/download/v1.4.3/docker-credential-gcr_linux_386-1.4.3.tar.gz
ENV ENVDIGEST=sha256:a680e90a30faae7b193edf63b088a11edf856f731d93c25f1fd7c1a21cf82d06

ADD --checksum=sha256:a680e90a30faae7b193edf63b088a11edf856f731d93c25f1fd7c1a21cf82d06 $URL /sha256.tar.gz
ADD --checksum=$ENVDIGEST $URL /env.tar.gz
ADD --checksum=$DIGEST $URL /arg.tar.gz
5 changes: 5 additions & 0 deletions integration/dockerfiles/Dockerfile_test_issue_3373_1
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM busybox:1.31

# 3373: first nibble of the digest flipped. docker rejects this, kaniko up to
# v1.28.3 accepted the download and built the image.
ADD --checksum=sha256:b680e90a30faae7b193edf63b088a11edf856f731d93c25f1fd7c1a21cf82d06 https://github.com/GoogleCloudPlatform/docker-credential-gcr/releases/download/v1.4.3/docker-credential-gcr_linux_386-1.4.3.tar.gz /release.tar.gz
5 changes: 5 additions & 0 deletions integration/dockerfiles/Dockerfile_test_issue_3373_2
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM busybox:1.31

# 3373: a checksum on a context file is not verifiable, docker fails with
# "checksum requires HTTP(S) or Git sources".
ADD --checksum=sha256:a680e90a30faae7b193edf63b088a11edf856f731d93c25f1fd7c1a21cf82d06 context/foo /foo
6 changes: 6 additions & 0 deletions integration/dockerfiles/Dockerfile_test_issue_3373_3
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM busybox:1.31

# 3373: the digest is the correct sha512 of the asset, but buildkit's http source
# always compares a sha256, so docker fails this with a digest mismatch. Honouring
# sha512 would build an image docker refuses to build.
ADD --checksum=sha512:2cdd08b2be9a7376b1db3e4d1278d1d9049ff79bb740d647c5444b30d348578b23c2f69589d1668e845ef554b281d727ed28301884c67a05463ee9555bf560bc https://github.com/GoogleCloudPlatform/docker-credential-gcr/releases/download/v1.4.3/docker-credential-gcr_linux_386-1.4.3.tar.gz /release.tar.gz
6 changes: 6 additions & 0 deletions integration/dockerfiles/Dockerfile_test_issue_3373_4
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM busybox:1.31

# 3373: one digest cannot validate two distinct files. docker rejects this with
# "checksum can't be specified for multiple sources", kaniko let it through and
# relied on the per-download check to fail whichever file did not match.
ADD --checksum=sha256:a680e90a30faae7b193edf63b088a11edf856f731d93c25f1fd7c1a21cf82d06 https://github.com/GoogleCloudPlatform/docker-credential-gcr/releases/download/v1.4.3/docker-credential-gcr_linux_386-1.4.3.tar.gz https://github.com/GoogleCloudPlatform/docker-credential-gcr/releases/download/v1.4.3/docker-credential-gcr_linux_amd64-1.4.3.tar.gz /dest/
7 changes: 7 additions & 0 deletions integration/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ var argsMap = map[string][]string{
},
"Dockerfile_test_multistage": {"file=/foo2"},
"Dockerfile_test_issue_mz655": {"BASE_TAG=1.37.0"},
"Dockerfile_test_issue_3373": {"DIGEST=sha256:a680e90a30faae7b193edf63b088a11edf856f731d93c25f1fd7c1a21cf82d06"},
}

var argsMapVersion1 = map[string][]string{
Expand Down Expand Up @@ -135,6 +136,7 @@ var KanikoEnv = []string{
"FF_KANIKO_PLATFORM_CACHE_KEY=1",
"FF_KANIKO_COPY_SKIP_SPECIAL_FILES=1",
"FF_KANIKO_NATIVE_COPY=1",
"FF_KANIKO_ADD_CHECKSUM=1",
"KANIKO_PRINT_PLAN=1",
"KANIKO_TELEMETRY_ENDPOINT",
"OTEL_EXPORTER_OTLP_HEADERS",
Expand Down Expand Up @@ -177,6 +179,7 @@ var additionalDockerFlagsMap = map[string][]string{
"Dockerfile_test_copy_bucket": dockerV2Flags,
"Dockerfile_test_cache_copy_oci": dockerV2Flags,
"Dockerfile_test_add_url_with_arg": dockerV2Flags,
"Dockerfile_test_issue_3373": dockerV2Flags,
"Dockerfile_test_add_dest_symlink_dir": dockerV2Flags,
"Dockerfile_test_add_chown_intermediate_dirs": dockerV2Flags,
"Dockerfile_test_arg_two_level": dockerV2Flags,
Expand Down Expand Up @@ -254,6 +257,10 @@ var additionalKanikoFlagsMap = map[string][]string{
var expectErr = map[string]int{
"Dockerfile_test_issue_cg326_1": 1,
"Dockerfile_test_add_404": 1,
"Dockerfile_test_issue_3373_1": 1,
"Dockerfile_test_issue_3373_2": 1,
"Dockerfile_test_issue_3373_3": 1,
"Dockerfile_test_issue_3373_4": 1,
}

var crossCompileArch = func() string {
Expand Down
24 changes: 23 additions & 1 deletion pkg/commands/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ limitations under the License.
package commands

import (
"errors"
"fmt"
"path/filepath"

v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/moby/buildkit/frontend/dockerfile/instructions"
"github.com/opencontainers/go-digest"
"github.com/osscontainertools/kaniko/pkg/assert"
kConfig "github.com/osscontainertools/kaniko/pkg/config"
"github.com/osscontainertools/kaniko/pkg/dockerfile"
Expand Down Expand Up @@ -63,6 +65,26 @@ func (a *AddCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui
return err
}

var checksum digest.Digest
if a.cmd.Checksum != "" && kConfig.FF.AddChecksum {
if len(srcs) > 1 {
return errors.New("checksum can't be specified for multiple sources")
}
resolved, err := util.ResolveEnvironmentReplacement(a.cmd.Checksum, replacementEnvs, false)
Comment thread
mzihlmann marked this conversation as resolved.
if err != nil {
return fmt.Errorf("resolving checksum: %w", err)
}
checksum, err = digest.Parse(resolved)
if err != nil {
return fmt.Errorf("invalid checksum digest format: %w", err)
}
for _, src := range srcs {
if !util.IsSrcRemoteFileURL(src) {
return fmt.Errorf("checksum requires HTTP(S) sources, got %s", src)
}
}
}

var unresolvedSrcs []string
// If any of the sources are local tar archives:
// 1. Unpack them to the specified destination
Expand All @@ -77,7 +99,7 @@ func (a *AddCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui
return err
}
logrus.Infof("Adding remote URL %s to %s", src, urlDest)
if err := util.DownloadFileToDest(src, urlDest, uid, gid, chmod.Apply(0o600)); err != nil {
if err := util.DownloadFileToDest(src, urlDest, uid, gid, chmod.Apply(0o600), checksum); err != nil {
return fmt.Errorf("downloading remote source file: %w", err)
}
a.snapshotFiles = append(a.snapshotFiles, urlDest)
Expand Down
2 changes: 2 additions & 0 deletions pkg/config/featureflags.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
)

type FeatureFlags struct {
AddChecksum bool
BuildkitArgEnvPrecedence bool
CacheLookahead bool
CacheProbeAfterMiss bool
Expand Down Expand Up @@ -103,6 +104,7 @@ func InitFeatureFlags() {
disabledFeatureFlags = nil

FF = FeatureFlags{
AddChecksum: featureFlag("FF_KANIKO_ADD_CHECKSUM", false),
BuildkitArgEnvPrecedence: featureFlag("FF_KANIKO_BUILDKIT_ARG_ENV_PRECEDENCE", true),
CacheLookahead: featureFlag("FF_KANIKO_CACHE_LOOKAHEAD", false),
CacheProbeAfterMiss: featureFlag("FF_KANIKO_CACHE_PROBE_AFTER_MISS", false),
Expand Down
20 changes: 18 additions & 2 deletions pkg/util/fs_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/moby/go-archive"
"github.com/moby/patternmatcher"
"github.com/moby/patternmatcher/ignorefile"
"github.com/opencontainers/go-digest"
"github.com/osscontainertools/kaniko/pkg/assert"
"github.com/osscontainertools/kaniko/pkg/config"
"github.com/osscontainertools/kaniko/pkg/timing"
Expand Down Expand Up @@ -726,7 +727,7 @@ func AddVolumePathToIgnoreList(path string) {
// 1. If <src> is a remote file URL:
// - destination will have permissions of 0600 by default if not specified with chmod
// - If remote file has HTTP Last-Modified header, we set the mtime of the file to that timestamp
func DownloadFileToDest(rawurl, dest string, uid, gid int64, chmod fs.FileMode) error {
func DownloadFileToDest(rawurl, dest string, uid, gid int64, chmod fs.FileMode, checksum digest.Digest) error {
resp, err := http.Get(rawurl) //nolint:noctx
if err != nil {
return err
Expand All @@ -737,9 +738,24 @@ func DownloadFileToDest(rawurl, dest string, uid, gid int64, chmod fs.FileMode)
return fmt.Errorf("invalid response status %d", resp.StatusCode)
}

if err := CreateFile(dest, resp.Body, chmod, 0o755, uint32(uid), uint32(gid)); err != nil {
body := io.Reader(resp.Body)
var digester digest.Digester
if checksum != "" {
// buildkit's http source hardcodes sha256, so any other algorithm can never match
digester = digest.Canonical.Digester()
body = io.TeeReader(resp.Body, digester.Hash())
}

if err := CreateFile(dest, body, chmod, 0o755, uint32(uid), uint32(gid)); err != nil {
return err
}

if digester != nil {
got := digester.Digest()
if got != checksum {
return fmt.Errorf("digest mismatch %s: %s", got, checksum)
}
}
mTime := time.Time{}
lastMod := resp.Header.Get("Last-Modified")
if lastMod != "" {
Expand Down
Loading