diff --git a/README.md b/README.md index c15439f0f..cc06f58e4 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ expect - see [Known Issues](#known-issues). - [Flag `FF_KANIKO_OCI_WARMER`](#flag-ff_kaniko_oci_warmer) - [Flag `FF_KANIKO_RUN_VIA_TINI`](#flag-ff_kaniko_run_via_tini) - [Flag `FF_KANIKO_COPY_CHMOD_ON_IMPLICIT_DIRS`](#flag-ff_kaniko_copy_chmod_on_implicit_dirs) + - [Flag `FF_KANIKO_CHOWN_ON_IMPLICIT_DIRS`](#flag-ff_kaniko_chown_on_implicit_dirs) - [Flag `FF_KANIKO_CLEAN_KANIKO_DIR`](#flag-ff_kaniko_clean_kaniko_dir) - [Flag `FF_KANIKO_NO_PROPAGATE_ANNOTATIONS`](#flag-ff_kaniko_no_propagate_annotations) - [Flag `FF_KANIKO_OCI_SCRATCH_BASE`](#flag-ff_kaniko_oci_scratch_base) @@ -1176,6 +1177,12 @@ When files are copied into a non-existing directory, both kaniko and buildkit wi Set this flag to `true` to implement COPY chmod like buildkit. Defaults to `false`. Currently no plans to activate. +#### Flag `FF_KANIKO_CHOWN_ON_IMPLICIT_DIRS` + +When `WORKDIR` creates a directory whose parents do not exist yet, kaniko creates the parents but leaves them owned by root, assigning the active `USER` only to the final directory. buildkit assigns the active `USER` to every directory it creates, so `WORKDIR /work/dir` under `USER 1000` leaves `/work` owned by root in kaniko but by `1000` in buildkit. +Set this flag to `true` to chown every implicitly created directory to the active user, matching buildkit. Defaults to `false`. +Currently no plans to activate. + #### Flag `FF_KANIKO_CLEAN_KANIKO_DIR` When using `--cleanup`, kaniko cleans the container filesystem at the end of the build. Set this flag to `true` to also remove kaniko's own working directory artifacts from `/kaniko` (the Dockerfile copy, build context, intermediate stages, inter-stage dependencies, layers cache, and secrets). This is useful when reusing a kaniko container across multiple builds. diff --git a/integration/dockerfiles/Dockerfile_test_issue_mz864 b/integration/dockerfiles/Dockerfile_test_issue_mz864 new file mode 100644 index 000000000..496c242b0 --- /dev/null +++ b/integration/dockerfiles/Dockerfile_test_issue_mz864 @@ -0,0 +1,8 @@ +FROM alpine + +# mz864: WORKDIR chowns only the leaf directory it creates. +# Diverges from buildkit on kaniko v1.28.0: the implicit parent /work stays +# root owned while buildkit assigns the active USER to every dir it creates. +USER 1000 +WORKDIR /work/dir4 +RUN stat -c '%u %g %n' /work /work/dir4 diff --git a/integration/images.go b/integration/images.go index d562468ee..6f4d29f46 100644 --- a/integration/images.go +++ b/integration/images.go @@ -105,6 +105,7 @@ var KanikoEnv = []string{ "FF_KANIKO_COPY_AS_ROOT=1", "FF_KANIKO_RUN_VIA_TINI=1", "FF_KANIKO_COPY_CHMOD_ON_IMPLICIT_DIRS=1", + "FF_KANIKO_CHOWN_ON_IMPLICIT_DIRS=1", "FF_KANIKO_OCI_SCRATCH_BASE=1", "FF_KANIKO_INFER_CROSS_STAGE_CACHE_KEY=1", "FF_KANIKO_CACHE_LOOKAHEAD=1", diff --git a/pkg/util/fs_util.go b/pkg/util/fs_util.go index 24a5d0446..c1147f4ff 100644 --- a/pkg/util/fs_util.go +++ b/pkg/util/fs_util.go @@ -1088,6 +1088,18 @@ func MkdirAllWithPermissions(path string, mode os.FileMode, uid, gid int64) erro return fmt.Errorf("error calling stat on %s: %w", path, err) } + var parents []string + if config.EnvBool("FF_KANIKO_CHOWN_ON_IMPLICIT_DIRS") { + for dir := filepath.Dir(path); dir != "/" && dir != "." && dir != ""; dir = filepath.Dir(dir) { + _, serr := os.Lstat(dir) + if os.IsNotExist(serr) { + parents = append(parents, dir) + } else { + break + } + } + } + // mkdir respects the process' umask // so we can't copy the correct permissions from source // if umask is set to anything other than 0 @@ -1107,6 +1119,12 @@ func MkdirAllWithPermissions(path string, mode os.FileMode, uid, gid int64) erro if err := os.Chown(path, int(uid), int(gid)); err != nil { return err } + for _, dir := range parents { + err = os.Chown(dir, int(uid), int(gid)) + if err != nil { + return err + } + } return nil }