Browse code

Ignore built-in allowed build-args in image history

Removes the build-args from the image history if they are in the
BuiltinAllowedBuildArgs map unless they are explicitly defined in an ARG
instruction.

Signed-off-by: Dave Tucker <dt@docker.com>

Dave Tucker authored on 2017/03/06 23:46:15
Showing 4 changed files
... ...
@@ -38,6 +38,8 @@ var validCommitCommands = map[string]bool{
38 38
 }
39 39
 
40 40
 // BuiltinAllowedBuildArgs is list of built-in allowed build args
41
+// these args are considered transparent and are excluded from the image history.
42
+// Filtering from history is implemented in dispatchers.go
41 43
 var BuiltinAllowedBuildArgs = map[string]bool{
42 44
 	"HTTP_PROXY":  true,
43 45
 	"http_proxy":  true,
... ...
@@ -411,6 +411,26 @@ func run(b *Builder, args []string, attributes map[string]bool, original string)
411 411
 	// have the build-time env vars in it (if any) so that future cache look-ups
412 412
 	// properly match it.
413 413
 	b.runConfig.Env = env
414
+
415
+	// remove BuiltinAllowedBuildArgs (see: builder.go)  from the saveCmd
416
+	// these args are transparent so resulting image should be the same regardless of the value
417
+	if len(cmdBuildEnv) > 0 {
418
+		saveCmd = config.Cmd
419
+		tmpBuildEnv := make([]string, len(cmdBuildEnv))
420
+		copy(tmpBuildEnv, cmdBuildEnv)
421
+		for i, env := range tmpBuildEnv {
422
+			key := strings.SplitN(env, "=", 2)[0]
423
+			if _, ok := BuiltinAllowedBuildArgs[key]; ok {
424
+				// If an built-in arg is explicitly added in the Dockerfile, don't prune it
425
+				if _, ok := b.allowedBuildArgs[key]; !ok {
426
+					tmpBuildEnv = append(tmpBuildEnv[:i], tmpBuildEnv[i+1:]...)
427
+				}
428
+			}
429
+		}
430
+		sort.Strings(tmpBuildEnv)
431
+		tmpEnv := append([]string{fmt.Sprintf("|%d", len(tmpBuildEnv))}, tmpBuildEnv...)
432
+		saveCmd = strslice.StrSlice(append(tmpEnv, saveCmd...))
433
+	}
414 434
 	b.runConfig.Cmd = saveCmd
415 435
 	return b.commit(cID, cmd, "run")
416 436
 }
... ...
@@ -1396,6 +1396,35 @@ To use these, simply pass them on the command line using the flag:
1396 1396
 --build-arg <varname>=<value>
1397 1397
 ```
1398 1398
 
1399
+By default, these pre-defined variables are excluded from the output of
1400
+`docker history`. Excluding them reduces the risk of accidentally leaking
1401
+sensitive authentication information in an `HTTP_PROXY` variable.
1402
+
1403
+For example, consider building the following Dockerfile using
1404
+`--build-arg HTTP_PROXY=http://user:pass@proxy.lon.example.com`
1405
+
1406
+``` Dockerfile
1407
+FROM ubuntu
1408
+RUN echo "Hello World"
1409
+```
1410
+
1411
+In this case, the value of the `HTTP_PROXY` variable is not available in the
1412
+`docker history` and is not cached. If you were to change location, and your
1413
+proxy server changed to `http://user:pass@proxy.sfo.example.com`, a subsequent
1414
+build does not result in a cache miss.
1415
+
1416
+If you need to override this behaviour then you may do so by adding an `ARG`
1417
+statement in the Dockerfile as follows:
1418
+
1419
+``` Dockerfile
1420
+FROM ubuntu
1421
+ARG HTTP_PROXY
1422
+RUN echo "Hello World"
1423
+```
1424
+
1425
+When building this Dockerfile, the `HTTP_PROXY` is preserved in the
1426
+`docker history`, and changing its value invalidates the build cache.
1427
+
1399 1428
 ### Impact on build caching
1400 1429
 
1401 1430
 `ARG` variables are not persisted into the built image as `ENV` variables are.
... ...
@@ -1404,6 +1433,8 @@ Dockerfile defines an `ARG` variable whose value is different from a previous
1404 1404
 build, then a "cache miss" occurs upon its first usage, not its definition. In
1405 1405
 particular, all `RUN` instructions following an `ARG` instruction use the `ARG`
1406 1406
 variable implicitly (as an environment variable), thus can cause a cache miss.
1407
+All predefined `ARG` variables are exempt from caching unless there is a
1408
+matching `ARG` statement in the `Dockerfile`.
1407 1409
 
1408 1410
 For example, consider these two Dockerfile:
1409 1411
 
... ...
@@ -4290,6 +4290,36 @@ func (s *DockerSuite) TestBuildBuildTimeArgHistory(c *check.C) {
4290 4290
 	}
4291 4291
 }
4292 4292
 
4293
+func (s *DockerSuite) TestBuildTimeArgHistoryExclusions(c *check.C) {
4294
+	imgName := "bldargtest"
4295
+	envKey := "foo"
4296
+	envVal := "bar"
4297
+	proxy := "HTTP_PROXY=http://user:password@proxy.example.com"
4298
+	explicitProxyKey := "http_proxy"
4299
+	explicitProxyVal := "http://user:password@someproxy.example.com"
4300
+	dockerfile := fmt.Sprintf(`FROM busybox
4301
+		ARG %s
4302
+		ARG %s
4303
+		RUN echo "Testing Build Args!"`, envKey, explicitProxyKey)
4304
+	buildImage(imgName,
4305
+		withBuildFlags("--build-arg", fmt.Sprintf("%s=%s", envKey, envVal),
4306
+			"--build-arg", fmt.Sprintf("%s=%s", explicitProxyKey, explicitProxyVal),
4307
+			"--build-arg", proxy),
4308
+		withDockerfile(dockerfile),
4309
+	).Assert(c, icmd.Success)
4310
+
4311
+	out, _ := dockerCmd(c, "history", "--no-trunc", imgName)
4312
+	if strings.Contains(out, proxy) {
4313
+		c.Fatalf("failed to exclude proxy settings from history!")
4314
+	}
4315
+	if !strings.Contains(out, fmt.Sprintf("%s=%s", envKey, envVal)) {
4316
+		c.Fatalf("explicitly defined ARG %s is not in output", explicitProxyKey)
4317
+	}
4318
+	if !strings.Contains(out, fmt.Sprintf("%s=%s", envKey, envVal)) {
4319
+		c.Fatalf("missing build arguments from output")
4320
+	}
4321
+}
4322
+
4293 4323
 func (s *DockerSuite) TestBuildBuildTimeArgCacheHit(c *check.C) {
4294 4324
 	imgName := "bldargtest"
4295 4325
 	envKey := "foo"