Skip to content
This repository was archived by the owner on Jun 3, 2025. It is now read-only.

Commit 778ac1c

Browse files
committed
Added --chmod for ADD and COPY commands. Fixes #2850
1 parent a0105f6 commit 778ac1c

8 files changed

Lines changed: 195 additions & 62 deletions

File tree

cmd/executor/cmd/root.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package cmd
1818

1919
import (
2020
"fmt"
21+
"io/fs"
2122
"os"
2223
"os/exec"
2324
"path/filepath"
@@ -300,7 +301,7 @@ func checkKanikoDir(dir string) error {
300301
if dir != constants.DefaultKanikoPath {
301302

302303
// The destination directory may be across a different partition, so we cannot simply rename/move the directory in this case.
303-
if _, err := util.CopyDir(constants.DefaultKanikoPath, dir, util.FileContext{}, util.DoNotChangeUID, util.DoNotChangeGID); err != nil {
304+
if _, err := util.CopyDir(constants.DefaultKanikoPath, dir, util.FileContext{}, util.DoNotChangeUID, util.DoNotChangeGID, fs.FileMode(0o600), true); err != nil {
304305
return err
305306
}
306307

@@ -321,7 +322,6 @@ func checkContained() bool {
321322

322323
// checkNoDeprecatedFlags return an error if deprecated flags are used.
323324
func checkNoDeprecatedFlags() {
324-
325325
// In version >=2.0.0 make it fail (`Warn` -> `Fatal`)
326326
if opts.CustomPlatformDeprecated != "" {
327327
logrus.Warn("Flag --customPlatform is deprecated. Use: --custom-platform")
@@ -391,12 +391,12 @@ func resolveEnvironmentBuildArgs(arguments []string, resolver func(string) strin
391391
// copy Dockerfile to /kaniko/Dockerfile so that if it's specified in the .dockerignore
392392
// it won't be copied into the image
393393
func copyDockerfile() error {
394-
if _, err := util.CopyFile(opts.DockerfilePath, config.DockerfilePath, util.FileContext{}, util.DoNotChangeUID, util.DoNotChangeGID); err != nil {
394+
if _, err := util.CopyFile(opts.DockerfilePath, config.DockerfilePath, util.FileContext{}, util.DoNotChangeUID, util.DoNotChangeGID, fs.FileMode(0o600), true); err != nil {
395395
return errors.Wrap(err, "copying dockerfile")
396396
}
397397
dockerignorePath := opts.DockerfilePath + ".dockerignore"
398398
if util.FilepathExists(dockerignorePath) {
399-
if _, err := util.CopyFile(dockerignorePath, config.DockerfilePath+".dockerignore", util.FileContext{}, util.DoNotChangeUID, util.DoNotChangeGID); err != nil {
399+
if _, err := util.CopyFile(dockerignorePath, config.DockerfilePath+".dockerignore", util.FileContext{}, util.DoNotChangeUID, util.DoNotChangeGID, fs.FileMode(0o600), true); err != nil {
400400
return errors.Wrap(err, "copying Dockerfile.dockerignore")
401401
}
402402
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
FROM alpine@sha256:5ce5f501c457015c4b91f91a15ac69157d9b06f1a75cf9107bf2b62e0843983a
2+
# Create dev user and group, with id 1001
3+
RUN yes | adduser -u 1001 dev
4+
RUN yes | adduser -u 1002 other
5+
6+
ADD --chmod=0666 context/foo /path/file666
7+
ADD context/foo /path/file
8+
9+
ADD --chmod=777 context/qux /path/dir777
10+
ADD context/qux /path/dir
11+
12+
# ADD tests
13+
RUN test "$(stat -c "%a" /path/file666)" = "666"
14+
RUN test "$(stat -c "%a" /path/file)" = "775"
15+
16+
RUN test "$(stat -c "%a" /path/dir777)" = "777"
17+
RUN ls -la /path/dir777/
18+
RUN test "$(stat -c "%a" /path/dir777/qup)" = "777"
19+
RUN test "$(stat -c "%a" /path/dir777/quw)" = "777"
20+
RUN test "$(stat -c "%a" /path/dir777/quw/que)" = "777"
21+
22+
RUN test "$(stat -c "%a" /path/dir)" = "775"
23+
RUN test "$(stat -c "%a" /path/dir/qup)" = "664"
24+
RUN test "$(stat -c "%a" /path/dir/quw)" = "775"
25+
RUN test "$(stat -c "%a" /path/dir/quw/que)" = "664"
26+
27+
# COPY tests
28+
29+
COPY --chmod=0755 context/foo /path/copyfile755
30+
COPY context/foo /path/copyfile
31+
COPY --chmod=755 context/qux /path/copydir755
32+
COPY context/qux /path/copydir
33+
34+
RUN test "$(stat -c "%a" /path/copyfile755)" = "755"
35+
RUN test "$(stat -c "%a" /path/copyfile)" = "775"
36+
37+
RUN test "$(stat -c "%a" /path/copydir755)" = "755"
38+
RUN test "$(stat -c "%a" /path/copydir755/qup)" = "755"
39+
RUN test "$(stat -c "%a" /path/copydir755/quw)" = "755"
40+
RUN test "$(stat -c "%a" /path/copydir755/quw/que)" = "755"
41+
42+
RUN test "$(stat -c "%a" /path/copydir)" = "775"
43+
RUN test "$(stat -c "%a" /path/copydir/qup)" = "664"
44+
RUN test "$(stat -c "%a" /path/copydir/quw)" = "775"
45+
RUN test "$(stat -c "%a" /path/copydir/quw/que)" = "664"

pkg/commands/add.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package commands
1818

1919
import (
20+
"io/fs"
2021
"path/filepath"
2122

2223
v1 "github.com/google/go-containerregistry/pkg/v1"
@@ -47,6 +48,14 @@ type AddCommand struct {
4748
func (a *AddCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.BuildArgs) error {
4849
replacementEnvs := buildArgs.ReplacementEnvs(config.Env)
4950

51+
chmod, useDefaultChmod, err := util.GetChmod(a.cmd.Chmod, replacementEnvs)
52+
if err != nil {
53+
return errors.Wrap(err, "getting permissions from chmod")
54+
}
55+
if useDefaultChmod {
56+
chmod = fs.FileMode(0o600)
57+
}
58+
5059
uid, gid, err := util.GetUserGroup(a.cmd.Chown, replacementEnvs)
5160
if err != nil {
5261
return errors.Wrap(err, "getting user group from chown")
@@ -71,7 +80,7 @@ func (a *AddCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui
7180
return err
7281
}
7382
logrus.Infof("Adding remote URL %s to %s", src, urlDest)
74-
if err := util.DownloadFileToDest(src, urlDest, uid, gid); err != nil {
83+
if err := util.DownloadFileToDest(src, urlDest, uid, gid, chmod); err != nil {
7584
return errors.Wrap(err, "downloading remote source file")
7685
}
7786
a.snapshotFiles = append(a.snapshotFiles, urlDest)
@@ -100,6 +109,7 @@ func (a *AddCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui
100109
cmd: &instructions.CopyCommand{
101110
SourcesAndDest: instructions.SourcesAndDest{SourcePaths: unresolvedSrcs, DestPath: dest},
102111
Chown: a.cmd.Chown,
112+
Chmod: a.cmd.Chmod,
103113
},
104114
fileContext: a.fileContext,
105115
}

pkg/commands/copy.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu
6464
return errors.Wrap(err, "resolving src")
6565
}
6666

67+
chmod, useDefaultChmod, err := util.GetChmod(c.cmd.Chmod, replacementEnvs)
68+
if err != nil {
69+
return errors.Wrap(err, "getting permissions from chmod")
70+
}
71+
6772
// For each source, iterate through and copy it over
6873
for _, src := range srcs {
6974
fullPath := filepath.Join(c.fileContext.Root, src)
@@ -93,7 +98,7 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu
9398
}
9499

95100
if fi.IsDir() {
96-
copiedFiles, err := util.CopyDir(fullPath, destPath, c.fileContext, uid, gid)
101+
copiedFiles, err := util.CopyDir(fullPath, destPath, c.fileContext, uid, gid, chmod, useDefaultChmod)
97102
if err != nil {
98103
return errors.Wrap(err, "copying dir")
99104
}
@@ -110,7 +115,7 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu
110115
c.snapshotFiles = append(c.snapshotFiles, destPath)
111116
} else {
112117
// ... Else, we want to copy over a file
113-
exclude, err := util.CopyFile(fullPath, destPath, c.fileContext, uid, gid)
118+
exclude, err := util.CopyFile(fullPath, destPath, c.fileContext, uid, gid, chmod, useDefaultChmod)
114119
if err != nil {
115120
return errors.Wrap(err, "copying file")
116121
}

pkg/util/command_util.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package util
1818

1919
import (
2020
"fmt"
21+
"io/fs"
2122
"net/url"
2223
"os"
2324
"os/user"
@@ -370,6 +371,24 @@ func GetUserGroup(chownStr string, env []string) (int64, int64, error) {
370371
return int64(uid32), int64(gid32), nil
371372
}
372373

374+
func GetChmod(chmodStr string, env []string) (chmod fs.FileMode, useDefault bool, err error) {
375+
if chmodStr == "" {
376+
return fs.FileMode(0o600), true, nil
377+
}
378+
379+
chmodStr, err = ResolveEnvironmentReplacement(chmodStr, env, false)
380+
if err != nil {
381+
return 0, false, err
382+
}
383+
384+
mode, err := strconv.ParseUint(chmodStr, 8, 32)
385+
if err != nil {
386+
return 0, false, errors.Wrap(err, "parsing value from chmod")
387+
}
388+
chmod = fs.FileMode(mode)
389+
return
390+
}
391+
373392
// Extract user and group id from a string formatted 'user:group'.
374393
// UserID and GroupID don't need to be present on the system.
375394
func getUIDAndGIDFromString(userGroupString string) (uint32, uint32, error) {

pkg/util/command_util_test.go

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package util
1818

1919
import (
2020
"fmt"
21+
"io/fs"
2122
"os/user"
2223
"reflect"
2324
"sort"
@@ -308,7 +309,8 @@ var updateConfigEnvTests = []struct {
308309
{
309310
Key: "foo",
310311
Value: "baz",
311-
}},
312+
},
313+
},
312314
config: &v1.Config{},
313315
replacementEnvs: []string{},
314316
expectedEnv: []string{"key=var", "foo=baz"},
@@ -326,7 +328,8 @@ var updateConfigEnvTests = []struct {
326328
{
327329
Key: "foo",
328330
Value: "$argarg",
329-
}},
331+
},
332+
},
330333
config: &v1.Config{},
331334
replacementEnvs: []string{"var=/test/with'chars'/", "not=used", "argarg=\"a\"b\""},
332335
expectedEnv: []string{"key=/var/run", "env=/test/with'chars'/", "foo=\"a\"b\""},
@@ -340,7 +343,8 @@ var updateConfigEnvTests = []struct {
340343
{
341344
Key: "bob",
342345
Value: "cool",
343-
}},
346+
},
347+
},
344348
config: &v1.Config{Env: []string{"bob=used", "more=test"}},
345349
replacementEnvs: []string{},
346350
expectedEnv: []string{"bob=cool", "more=test", "alice=nice"},
@@ -585,6 +589,43 @@ func TestGetUserGroup(t *testing.T) {
585589
}
586590
}
587591

592+
func TestGetChmod(t *testing.T) {
593+
tests := []struct {
594+
description string
595+
chmod string
596+
env []string
597+
expected fs.FileMode
598+
shdErr bool
599+
}{
600+
{
601+
description: "non empty chmod",
602+
chmod: "0755",
603+
env: []string{},
604+
expected: fs.FileMode(0o755),
605+
},
606+
{
607+
description: "non empty chmod with env replacement",
608+
chmod: "$foo",
609+
env: []string{"foo=0750"},
610+
expected: fs.FileMode(0o750),
611+
},
612+
{
613+
description: "empty chmod string",
614+
expected: fs.FileMode(0o600),
615+
},
616+
}
617+
for _, tc := range tests {
618+
t.Run(tc.description, func(t *testing.T) {
619+
defaultChmod := fs.FileMode(0o600)
620+
chmod, useDefault, err := GetChmod(tc.chmod, tc.env)
621+
if useDefault {
622+
chmod = defaultChmod
623+
}
624+
testutil.CheckErrorAndDeepEqual(t, tc.shdErr, err, tc.expected, chmod)
625+
})
626+
}
627+
}
628+
588629
func TestResolveEnvironmentReplacementList(t *testing.T) {
589630
type args struct {
590631
values []string
@@ -806,7 +847,6 @@ func TestLookupUser(t *testing.T) {
806847
testutil.CheckErrorAndDeepEqual(t, tt.wantErr, err, tt.expected, got)
807848
})
808849
}
809-
810850
}
811851

812852
func TestIsSrcRemoteFileURL(t *testing.T) {

0 commit comments

Comments
 (0)