Skip to content

Commit f58ca22

Browse files
fix(executor): use 0644/0755 for the default artifact mode, not 0600/0700
An artifact-plugin sidecar reads a saved output through its own mount of the main container's filesystem, and is not guaranteed to run as the same user as the container that loaded the input artifact. A 0600 default made that read fail with permission denied, caught by TestArtifactsSuite/TestOutputOnInputPlugin. 0644/0755 stays readable across containers while still restricting writes to the owner. Also rewrites the mode-selection if/else-if/else as a switch to satisfy golangci-lint's gocritic ifElseChain check. Signed-off-by: Charles Cheng <chengxisheng777@gmail.com>
1 parent 5f561e1 commit f58ca22

4 files changed

Lines changed: 17 additions & 13 deletions

File tree

docs/upgrading.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,20 @@ This variable controlled whether to write workflow updates back to the informer
2929
Alternative mechanisms now prevent reprocessing, making both behaviors unnecessary.
3030
If you have this variable set, it can be safely removed from your configuration.
3131

32-
### Input artifacts without `mode` now default to `0600`
32+
### Input artifacts without `mode` now default to `0644`
3333

34-
An input artifact that does not set `mode` is now given a fixed set of permissions once it has been loaded: `0600` for its files and `0700` for its directories ([#14792](https://github.com/argoproj/argo-workflows/issues/14792)).
34+
An input artifact that does not set `mode` is now given a fixed set of permissions once it has been loaded: `0644` for its files and `0755` for its directories ([#14792](https://github.com/argoproj/argo-workflows/issues/14792)).
3535
This covers the whole artifact, not only its top level, since an artifact can be a directory.
3636
Symlinks within an artifact are left alone, and artifacts loaded by an artifact plugin keep their existing `0666` default.
3737
Setting `mode` still overrides all of this, and `recurseMode` continues to control how an explicit `mode` is applied.
3838

3939
Previously no default was applied, so the permissions an artifact ended up with depended on how it had been stored rather than on the template consuming it.
4040
A tarball (the default `archive` strategy) restores the modes recorded in its headers, so a file saved as `0644` was loaded as `0644`; the same file saved with `archive: none: {}` is stored without any permission metadata and was loaded as `0600`.
4141
Object stores generally cannot preserve filesystem permissions, and some artifact repositories (`http`, for example) have nowhere to record them at all, so the producing template's permissions were never something a consuming template could rely on.
42+
The new default is world-readable rather than owner-only, since a step that saves the artifact through an artifact plugin reads it from a separate sidecar container that is not guaranteed to run as the same user as the container that loaded it.
4243

43-
Set `mode` (and `recurseMode` for a directory) explicitly on any input artifact that needs more than owner access.
44-
The two cases most likely to need it are a file that has to stay executable, and a `main` container that runs as a different user than the one that loaded the artifact — `0600` and `0700` are owner-only, so a differing `runAsUser` can no longer read the artifact:
44+
Set `mode` (and `recurseMode` for a directory) explicitly on any input artifact that needs more than this.
45+
The main case that still needs it is a file that has to stay executable, or one that a container running as a different user than the one that loaded it needs to write to — `0644` gives the owner no execute bit and gives no one but the owner write access:
4546

4647
```yaml
4748
inputs:

docs/walk-through/artifacts.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ For example:
132132
It is good practice to specify the `mode` of the input file, to ensure your container code can always interact with it (reading/writing/executing) as expected.
133133
[This article](https://www.redhat.com/en/blog/linux-file-permissions-explained) explains file permissions and octal values.
134134

135-
An input artifact that does not specify `mode` is loaded with a fixed set of permissions: `0600` for its files and `0700` for its directories.
135+
An input artifact that does not specify `mode` is loaded with a fixed set of permissions: `0644` for its files and `0755` for its directories.
136136
Do not rely on the permissions the template that produced the artifact gave it: an artifact repository generally cannot store file permissions, so they only survive if the artifact was archived (see [Archive Strategy](#archive-strategy) above).
137137

138138
For example, to allow the user to execute `/bin/kubectl`, we set `mode: 0755`.

workflow/executor/executor.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -344,20 +344,21 @@ func (we *WorkflowExecutor) loadArtifact(ctx context.Context, pluginName wfv1.Ar
344344
}
345345

346346
logger.WithField("path", artPath).Info(ctx, "Successfully download file")
347-
if art.Mode != nil {
347+
switch {
348+
case art.Mode != nil:
348349
err = chmod(artPath, *art.Mode, art.RecurseMode)
349350
if err != nil {
350351
return err
351352
}
352-
} else if driverArt.Plugin != nil {
353+
case driverArt.Plugin != nil:
353354
// For plugin artifacts without explicit mode, ensure the file is writable
354355
// by setting mode to 0666 so the main container can read/write it
355356
err = chmod(artPath, 0666, art.RecurseMode)
356357
if err != nil {
357358
logger.WithError(err).Error(ctx, "Failed to chmod plugin artifact")
358359
return err
359360
}
360-
} else {
361+
default:
361362
err = chmodDefault(artPath)
362363
if err != nil {
363364
return err
@@ -1338,11 +1339,13 @@ func unpack(srcPath string, destPath string, decompressor func(string, string) e
13381339

13391340
const (
13401341
// defaultArtifactFileMode is applied to the files of an input artifact that does not
1341-
// set `mode`.
1342-
defaultArtifactFileMode = 0o600
1342+
// set `mode`. It is world-readable rather than owner-only because an artifact-plugin
1343+
// output step reads the file from a separate sidecar container, which is not
1344+
// guaranteed to run as the same user as the container that loaded the artifact.
1345+
defaultArtifactFileMode = 0o644
13431346
// defaultArtifactDirMode is defaultArtifactFileMode with the execute bit added, as a
13441347
// directory has to be traversable to be of any use.
1345-
defaultArtifactDirMode = 0o700
1348+
defaultArtifactDirMode = 0o755
13461349
)
13471350

13481351
// chmodDefault gives an input artifact that does not set `mode` a well known set of

workflow/executor/executor_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ func TestChmodDefault(t *testing.T) {
498498

499499
info, err := os.Stat(artPath)
500500
require.NoError(t, err)
501-
assert.Equal(t, "-rw-------", info.Mode().String())
501+
assert.Equal(t, "-rw-r--r--", info.Mode().String())
502502
})
503503

504504
// A directory artifact, as `untar` or a directory download leaves it. The whole
@@ -513,7 +513,7 @@ func TestChmodDefault(t *testing.T) {
513513

514514
require.NoError(t, chmodDefault(root))
515515

516-
want := map[string]string{root: "drwx------", nested: "drwx------", file: "-rw-------"}
516+
want := map[string]string{root: "drwxr-xr-x", nested: "drwxr-xr-x", file: "-rw-r--r--"}
517517
for path, mode := range want {
518518
info, err := os.Stat(path)
519519
require.NoError(t, err)

0 commit comments

Comments
 (0)