From c80a2e4495f877bc0f6a522e99b511de6c0d525d Mon Sep 17 00:00:00 2001
From: Aditya Rajan <arajan@redhat.com>
Date: Tue, 30 Nov 2021 14:15:53 +0530
Subject: [PATCH] podman-remote: prevent leaking secret into image

Prevents temp secrets leaking into image by moving it away from context
directory to parent builder directory. Builder directory automatically
gets cleaned up when we are done with the build.

Signed-off-by: Aditya Rajan <arajan@redhat.com>
---
 pkg/api/handlers/compat/images_build.go          | 14 +++++++++++++-
 .../e2e/build/Dockerfile.with-secret-verify-leak |  3 +++
 test/e2e/build_test.go                           | 16 ++++++++++++++++
 3 files changed, 32 insertions(+), 1 deletion(-)
 create mode 100644 test/e2e/build/Dockerfile.with-secret-verify-leak

diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go
index 200d721922..3aa1af71a4 100644
--- a/pkg/api/handlers/compat/images_build.go
+++ b/pkg/api/handlers/compat/images_build.go
@@ -259,7 +259,19 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
 					arr := strings.SplitN(token, "=", 2)
 					if len(arr) > 1 {
 						if arr[0] == "src" {
-							modifiedSrc := fmt.Sprintf("src=%s", filepath.Join(contextDirectory, arr[1]))
+							/* move secret away from contextDir */
+							/* to make sure we dont accidentally commit temporary secrets to image*/
+							builderDirectory, _ := filepath.Split(contextDirectory)
+							// following path is outside build context
+							newSecretPath := filepath.Join(builderDirectory, arr[1])
+							oldSecretPath := filepath.Join(contextDirectory, arr[1])
+							err := os.Rename(oldSecretPath, newSecretPath)
+							if err != nil {
+								utils.BadRequest(w, "secrets", query.Secrets, err)
+								return
+							}
+
+							modifiedSrc := fmt.Sprintf("src=%s", newSecretPath)
 							modifiedOpt = append(modifiedOpt, modifiedSrc)
 						} else {
 							modifiedOpt = append(modifiedOpt, token)
diff --git a/test/e2e/build/Dockerfile.with-secret-verify-leak b/test/e2e/build/Dockerfile.with-secret-verify-leak
new file mode 100644
index 0000000000..0957ac6a60
--- /dev/null
+++ b/test/e2e/build/Dockerfile.with-secret-verify-leak
@@ -0,0 +1,3 @@
+FROM alpine
+COPY * /
+RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go
index ad401ca83e..c541f25ae8 100644
--- a/test/e2e/build_test.go
+++ b/test/e2e/build_test.go
@@ -82,6 +82,22 @@ var _ = Describe("Podman build", func() {
 		Expect(session).Should(Exit(0))
 	})
 
+	It("podman build with a secret from file and verify if secret file is not leaked into image", func() {
+		session := podmanTest.Podman([]string{"build", "-f", "build/Dockerfile.with-secret-verify-leak", "-t", "secret-test-leak", "--secret", "id=mysecret,src=build/secret.txt", "build/"})
+		session.WaitWithDefaultTimeout()
+		Expect(session).Should(Exit(0))
+		Expect(session.OutputToString()).To(ContainSubstring("somesecret"))
+
+		session = podmanTest.Podman([]string{"run", "--rm", "secret-test-leak", "ls"})
+		session.WaitWithDefaultTimeout()
+		Expect(session).Should(Exit(0))
+		Expect(session.OutputToString()).To(Not(ContainSubstring("podman-build-secret")))
+
+		session = podmanTest.Podman([]string{"rmi", "secret-test-leak"})
+		session.WaitWithDefaultTimeout()
+		Expect(session).Should(Exit(0))
+	})
+
 	It("podman build with logfile", func() {
 		logfile := filepath.Join(podmanTest.TempDir, "logfile")
 		session := podmanTest.Podman([]string{"build", "--pull-never", "--tag", "test", "--logfile", logfile, "build/basicalpine"})