Skip to content

Commit

Permalink
Merge pull request containers#12414 from flouthoc/api-allow-secrets
Browse files Browse the repository at this point in the history
tunnel: allow `remote` and `API` to accept `--secrets`
  • Loading branch information
openshift-merge-robot authored Nov 30, 2021
2 parents 3fac03c + c80a2e4 commit 85101f6
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 0 deletions.
45 changes: 45 additions & 0 deletions pkg/api/handlers/compat/images_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
Target string `schema:"target"`
Timestamp int64 `schema:"timestamp"`
Ulimits string `schema:"ulimits"`
Secrets string `schema:"secrets"`
}{
Dockerfile: "Dockerfile",
Registry: "docker.io",
Expand Down Expand Up @@ -242,6 +243,49 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
dnssearch = m
}

var secrets = []string{}
if _, found := r.URL.Query()["secrets"]; found {
var m = []string{}
if err := json.Unmarshal([]byte(query.Secrets), &m); err != nil {
utils.BadRequest(w, "secrets", query.Secrets, err)
return
}

// for podman-remote all secrets must be picked from context director
// hence modify src so contextdir is added as prefix

for _, secret := range m {
secretOpt := strings.Split(secret, ",")
if len(secretOpt) > 0 {
modifiedOpt := []string{}
for _, token := range secretOpt {
arr := strings.SplitN(token, "=", 2)
if len(arr) > 1 {
if arr[0] == "src" {
/* 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)
}
}
}
secrets = append(secrets, strings.Join(modifiedOpt[:], ","))
}
}
}

var output string
if len(tags) > 0 {
possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, tags[0])
Expand Down Expand Up @@ -476,6 +520,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
SeccompProfilePath: seccomp,
ShmSize: strconv.Itoa(query.ShmSize),
Ulimit: ulimits,
Secrets: secrets,
},
CNIConfigDir: rtc.Network.CNIPluginDirs[0],
CNIPluginPath: util.DefaultCNIPluginPath,
Expand Down
54 changes: 54 additions & 0 deletions pkg/bindings/images/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"compress/gzip"
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
Expand Down Expand Up @@ -377,6 +378,59 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
}
params.Set("dockerfile", string(cFileJSON))
}

// build secrets are usually absolute host path or relative to context dir on host
// in any case move secret to current context and ship the tar.
if secrets := options.CommonBuildOpts.Secrets; len(secrets) > 0 {
secretsForRemote := []string{}

for _, secret := range secrets {
secretOpt := strings.Split(secret, ",")
if len(secretOpt) > 0 {
modifiedOpt := []string{}
for _, token := range secretOpt {
arr := strings.SplitN(token, "=", 2)
if len(arr) > 1 {
if arr[0] == "src" {
// read specified secret into a tmp file
// move tmp file to tar and change secret source to relative tmp file
tmpSecretFile, err := ioutil.TempFile(options.ContextDirectory, "podman-build-secret")
if err != nil {
return nil, err
}
defer os.Remove(tmpSecretFile.Name()) // clean up
defer tmpSecretFile.Close()
srcSecretFile, err := os.Open(arr[1])
if err != nil {
return nil, err
}
defer srcSecretFile.Close()
_, err = io.Copy(tmpSecretFile, srcSecretFile)
if err != nil {
return nil, err
}

//add tmp file to context dir
tarContent = append(tarContent, tmpSecretFile.Name())

modifiedSrc := fmt.Sprintf("src=%s", filepath.Base(tmpSecretFile.Name()))
modifiedOpt = append(modifiedOpt, modifiedSrc)
} else {
modifiedOpt = append(modifiedOpt, token)
}
}
}
secretsForRemote = append(secretsForRemote, strings.Join(modifiedOpt[:], ","))
}
}

c, err := jsoniter.MarshalToString(secretsForRemote)
if err != nil {
return nil, err
}
params.Add("secrets", c)
}

tarfile, err := nTar(append(excludes, dontexcludes...), tarContent...)
if err != nil {
logrus.Errorf("Cannot tar container entries %v error: %v", tarContent, err)
Expand Down
3 changes: 3 additions & 0 deletions test/e2e/build/Dockerfile.with-multiple-secret
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM alpine
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
RUN --mount=type=secret,id=mysecret2 cat /run/secrets/mysecret2
2 changes: 2 additions & 0 deletions test/e2e/build/Dockerfile.with-secret
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FROM alpine
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
3 changes: 3 additions & 0 deletions test/e2e/build/Dockerfile.with-secret-verify-leak
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM alpine
COPY * /
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
1 change: 1 addition & 0 deletions test/e2e/build/anothersecret.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
anothersecret
1 change: 1 addition & 0 deletions test/e2e/build/secret.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
somesecret
39 changes: 39 additions & 0 deletions test/e2e/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,45 @@ var _ = Describe("Podman build", func() {
Expect(session).Should(Exit(0))
})

It("podman build with a secret from file", func() {
session := podmanTest.Podman([]string{"build", "-f", "build/Dockerfile.with-secret", "-t", "secret-test", "--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{"rmi", "secret-test"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
})

It("podman build with multiple secrets from files", func() {
session := podmanTest.Podman([]string{"build", "-f", "build/Dockerfile.with-multiple-secret", "-t", "multiple-secret-test", "--secret", "id=mysecret,src=build/secret.txt", "--secret", "id=mysecret2,src=build/anothersecret.txt", "build/"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(ContainSubstring("somesecret"))
Expect(session.OutputToString()).To(ContainSubstring("anothersecret"))

session = podmanTest.Podman([]string{"rmi", "multiple-secret-test"})
session.WaitWithDefaultTimeout()
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"})
Expand Down

0 comments on commit 85101f6

Please sign in to comment.