Skip to content

Commit 71275f9

Browse files
authored
Merge pull request moby#5105 from daghack/secrets-used-in-args-rule
Lint Rule for catching common secret related env/arg keys
2 parents 4ef8d0d + 390611d commit 71275f9

File tree

6 files changed

+200
-3
lines changed

6 files changed

+200
-3
lines changed

frontend/dockerfile/dockerfile2llb/convert.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"os"
1212
"path"
1313
"path/filepath"
14+
"regexp"
1415
"runtime"
1516
"sort"
1617
"strconv"
@@ -54,6 +55,11 @@ const (
5455
sbomScanStage = "BUILDKIT_SBOM_SCAN_STAGE"
5556
)
5657

58+
var (
59+
secretsRegexpOnce sync.Once
60+
secretsRegexp *regexp.Regexp
61+
)
62+
5763
var nonEnvArgs = map[string]struct{}{
5864
sbomScanContext: {},
5965
sbomScanStage: {},
@@ -1122,6 +1128,7 @@ func dispatchEnv(d *dispatchState, c *instructions.EnvCommand, lint *linter.Lint
11221128
msg := linter.RuleLegacyKeyValueFormat.Format(c.Name())
11231129
lint.Run(&linter.RuleLegacyKeyValueFormat, c.Location(), msg)
11241130
}
1131+
validateNoSecretKey("ENV", e.Key, c.Location(), lint)
11251132
commitMessage.WriteString(" " + e.String())
11261133
d.state = d.state.AddEnv(e.Key, e.Value)
11271134
d.image.Config.Env = addEnv(d.image.Config.Env, e.Key, e.Value)
@@ -1700,6 +1707,7 @@ func dispatchShell(d *dispatchState, c *instructions.ShellCommand) error {
17001707
func dispatchArg(d *dispatchState, c *instructions.ArgCommand, opt *dispatchOpt) error {
17011708
commitStrs := make([]string, 0, len(c.Args))
17021709
for _, arg := range c.Args {
1710+
validateNoSecretKey("ARG", arg.Key, c.Location(), opt.lint)
17031711
_, hasValue := opt.buildArgValues[arg.Key]
17041712
hasDefault := arg.Value != nil
17051713

@@ -2344,6 +2352,37 @@ func validateBaseImagePlatform(name string, expected, actual ocispecs.Platform,
23442352
}
23452353
}
23462354

2355+
func getSecretsRegex() *regexp.Regexp {
2356+
// Check for either full value or first/last word.
2357+
// Examples: api_key, DATABASE_PASSWORD, GITHUB_TOKEN, secret_MESSAGE, AUTH
2358+
// Case insensitive.
2359+
secretsRegexpOnce.Do(func() {
2360+
secretTokens := []string{
2361+
"apikey",
2362+
"auth",
2363+
"credential",
2364+
"credentials",
2365+
"key",
2366+
"password",
2367+
"pword",
2368+
"passwd",
2369+
"secret",
2370+
"token",
2371+
}
2372+
pattern := `(?i)(?:_|^)(?:` + strings.Join(secretTokens, "|") + `)(?:_|$)`
2373+
secretsRegexp = regexp.MustCompile(pattern)
2374+
})
2375+
return secretsRegexp
2376+
}
2377+
2378+
func validateNoSecretKey(instruction, key string, location []parser.Range, lint *linter.Linter) {
2379+
pattern := getSecretsRegex()
2380+
if pattern.MatchString(key) {
2381+
msg := linter.RuleSecretsUsedInArgOrEnv.Format(instruction, key)
2382+
lint.Run(&linter.RuleSecretsUsedInArgOrEnv, location, msg)
2383+
}
2384+
}
2385+
23472386
type emptyEnvs struct{}
23482387

23492388
func (emptyEnvs) Get(string) (string, bool) {

frontend/dockerfile/dockerfile_lint_test.go

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,90 @@ var lintTests = integration.TestFuncs(
4141
testBaseImagePlatformMismatch,
4242
testAllTargetUnmarshal,
4343
testRedundantTargetPlatform,
44+
testSecretsUsedInArgOrEnv,
4445
)
4546

47+
func testSecretsUsedInArgOrEnv(t *testing.T, sb integration.Sandbox) {
48+
dockerfile := []byte(`
49+
FROM scratch
50+
ARG SECRET_PASSPHRASE
51+
ENV SUPER_Secret=foo
52+
ENV password=bar secret=baz
53+
ARG super_duper_secret_token=foo auth=bar
54+
ENV apikey=bar sunflower=foo
55+
ENV git_key=
56+
`)
57+
checkLinterWarnings(t, sb, &lintTestParams{
58+
Dockerfile: dockerfile,
59+
Warnings: []expectedLintWarning{
60+
{
61+
RuleName: "SecretsUsedInArgOrEnv",
62+
Description: "Sensitive data should not be used in the ARG or ENV commands",
63+
Detail: `Do not use ARG or ENV instructions for sensitive data (ARG "SECRET_PASSPHRASE")`,
64+
URL: "https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/",
65+
Level: 1,
66+
Line: 3,
67+
},
68+
{
69+
RuleName: "SecretsUsedInArgOrEnv",
70+
Description: "Sensitive data should not be used in the ARG or ENV commands",
71+
Detail: `Do not use ARG or ENV instructions for sensitive data (ENV "SUPER_Secret")`,
72+
URL: "https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/",
73+
Level: 1,
74+
Line: 4,
75+
},
76+
{
77+
RuleName: "SecretsUsedInArgOrEnv",
78+
Description: "Sensitive data should not be used in the ARG or ENV commands",
79+
Detail: `Do not use ARG or ENV instructions for sensitive data (ENV "password")`,
80+
URL: "https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/",
81+
Level: 1,
82+
Line: 5,
83+
},
84+
{
85+
RuleName: "SecretsUsedInArgOrEnv",
86+
Description: "Sensitive data should not be used in the ARG or ENV commands",
87+
Detail: `Do not use ARG or ENV instructions for sensitive data (ENV "secret")`,
88+
URL: "https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/",
89+
Level: 1,
90+
Line: 5,
91+
},
92+
{
93+
RuleName: "SecretsUsedInArgOrEnv",
94+
Description: "Sensitive data should not be used in the ARG or ENV commands",
95+
Detail: `Do not use ARG or ENV instructions for sensitive data (ARG "super_duper_secret_token")`,
96+
URL: "https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/",
97+
Level: 1,
98+
Line: 6,
99+
},
100+
{
101+
RuleName: "SecretsUsedInArgOrEnv",
102+
Description: "Sensitive data should not be used in the ARG or ENV commands",
103+
Detail: `Do not use ARG or ENV instructions for sensitive data (ARG "auth")`,
104+
URL: "https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/",
105+
Level: 1,
106+
Line: 6,
107+
},
108+
{
109+
RuleName: "SecretsUsedInArgOrEnv",
110+
Description: "Sensitive data should not be used in the ARG or ENV commands",
111+
Detail: `Do not use ARG or ENV instructions for sensitive data (ENV "apikey")`,
112+
URL: "https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/",
113+
Level: 1,
114+
Line: 7,
115+
},
116+
{
117+
RuleName: "SecretsUsedInArgOrEnv",
118+
Description: "Sensitive data should not be used in the ARG or ENV commands",
119+
Detail: `Do not use ARG or ENV instructions for sensitive data (ENV "git_key")`,
120+
URL: "https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/",
121+
Level: 1,
122+
Line: 8,
123+
},
124+
},
125+
})
126+
}
127+
46128
func testAllTargetUnmarshal(t *testing.T, sb integration.Sandbox) {
47129
dockerfile := []byte(`
48130
FROM scratch AS first
@@ -858,7 +940,7 @@ HEALTHCHECK CMD ["/myotherapp"]
858940
func testLegacyKeyValueFormat(t *testing.T, sb integration.Sandbox) {
859941
dockerfile := []byte(`
860942
FROM scratch
861-
ENV key value
943+
ENV testkey value
862944
LABEL key value
863945
`)
864946
checkLinterWarnings(t, sb, &lintTestParams{
@@ -885,7 +967,7 @@ LABEL key value
885967

886968
dockerfile = []byte(`
887969
FROM scratch
888-
ENV key=value
970+
ENV testkey=value
889971
LABEL key=value
890972
`)
891973
checkLinterWarnings(t, sb, &lintTestParams{Dockerfile: dockerfile})
@@ -896,7 +978,7 @@ LABEL key=value
896978
FROM scratch AS a
897979
898980
FROM a AS b
899-
ENV key value
981+
ENV testkey value
900982
LABEL key value
901983
902984
FROM a AS c

frontend/dockerfile/docs/rules/_index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,9 @@ $ docker build --check .
8484
<td><a href="./redundant-target-platform/">RedundantTargetPlatform</a></td>
8585
<td>Setting platform to predefined $TARGETPLATFORM in FROM is redundant as this is the default behavior</td>
8686
</tr>
87+
<tr>
88+
<td><a href="./secrets-used-in-arg-or-env/">SecretsUsedInArgOrEnv</a></td>
89+
<td>Sensitive data should not be used in the ARG or ENV commands</td>
90+
</tr>
8791
</tbody>
8892
</table>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
title: SecretsUsedInArgOrEnv
3+
description: Sensitive data should not be used in the ARG or ENV commands
4+
aliases:
5+
- /go/dockerfile/rule/secrets-used-in-arg-or-env/
6+
---
7+
8+
## Output
9+
10+
```text
11+
Potentially sensitive data should not be used in the ARG or ENV commands
12+
```
13+
14+
## Description
15+
16+
While it is common to pass secrets to running processes
17+
through environment variables during local development,
18+
setting secrets in a Dockerfile using `ENV` or `ARG`
19+
is insecure because they persist in the final image.
20+
This rule reports violations where `ENV` and `ARG` keys
21+
indicate that they contain sensitive data.
22+
23+
Instead of `ARG` or `ENV`, you should use secret mounts,
24+
which expose secrets to your builds in a secure manner,
25+
and do not persist in the final image or its metadata.
26+
See [Build secrets](https://docs.docker.com/build/building/secrets/).
27+
28+
## Examples
29+
30+
❌ Bad: `AWS_SECRET_ACCESS_KEY` is a secret value.
31+
32+
```dockerfile
33+
FROM scratch
34+
ARG AWS_SECRET_ACCESS_KEY
35+
```
36+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
## Output
2+
3+
```text
4+
Potentially sensitive data should not be used in the ARG or ENV commands
5+
```
6+
7+
## Description
8+
9+
While it is common to pass secrets to running processes
10+
through environment variables during local development,
11+
setting secrets in a Dockerfile using `ENV` or `ARG`
12+
is insecure because they persist in the final image.
13+
This rule reports violations where `ENV` and `ARG` keys
14+
indicate that they contain sensitive data.
15+
16+
Instead of `ARG` or `ENV`, you should use secret mounts,
17+
which expose secrets to your builds in a secure manner,
18+
and do not persist in the final image or its metadata.
19+
See [Build secrets](https://docs.docker.com/build/building/secrets/).
20+
21+
## Examples
22+
23+
❌ Bad: `AWS_SECRET_ACCESS_KEY` is a secret value.
24+
25+
```dockerfile
26+
FROM scratch
27+
ARG AWS_SECRET_ACCESS_KEY
28+
```

frontend/dockerfile/linter/ruleset.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,12 @@ var (
132132
return fmt.Sprintf("Setting platform to predefined %s in FROM is redundant as this is the default behavior", platformVar)
133133
},
134134
}
135+
RuleSecretsUsedInArgOrEnv = LinterRule[func(string, string) string]{
136+
Name: "SecretsUsedInArgOrEnv",
137+
Description: "Sensitive data should not be used in the ARG or ENV commands",
138+
URL: "https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/",
139+
Format: func(instruction, secretKey string) string {
140+
return fmt.Sprintf("Do not use ARG or ENV instructions for sensitive data (%s %q)", instruction, secretKey)
141+
},
142+
}
135143
)

0 commit comments

Comments
 (0)