From 6a70271ff283ac335369478c6280a7e9fe5771cf Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Thu, 23 Dec 2021 17:27:22 +0100 Subject: [PATCH 1/3] engine.#LoadSecret support Fixes #1305 Signed-off-by: Andrea Luzzardi --- docs/reference/europa/dagger/engine.md | 12 ++++++ plan/task/loadsecret.go | 54 ++++++++++++++++++++++++++ stdlib/europa/dagger/engine/secret.cue | 15 +++++++ tests/tasks.bats | 6 +++ tests/tasks/loadsecret/loadsecret.cue | 37 ++++++++++++++++++ 5 files changed, 124 insertions(+) create mode 100644 plan/task/loadsecret.go create mode 100644 stdlib/europa/dagger/engine/secret.cue create mode 100644 tests/tasks/loadsecret/loadsecret.cue diff --git a/docs/reference/europa/dagger/engine.md b/docs/reference/europa/dagger/engine.md index c7f64d24..34ee1528 100644 --- a/docs/reference/europa/dagger/engine.md +++ b/docs/reference/europa/dagger/engine.md @@ -138,6 +138,18 @@ _No input._ _No output._ +## engine.#LoadSecret + +Load a secret from a filesystem tree + +### engine.#LoadSecret Inputs + +_No input._ + +### engine.#LoadSecret Outputs + +_No output._ + ## engine.#Merge Merge multiple FS trees into one diff --git a/plan/task/loadsecret.go b/plan/task/loadsecret.go new file mode 100644 index 00000000..794e83b3 --- /dev/null +++ b/plan/task/loadsecret.go @@ -0,0 +1,54 @@ +package task + +import ( + "context" + "fmt" + "io/fs" + "strings" + + "go.dagger.io/dagger/compiler" + "go.dagger.io/dagger/plancontext" + "go.dagger.io/dagger/solver" +) + +func init() { + Register("LoadSecret", func() Task { return &loadSecretTask{} }) +} + +type loadSecretTask struct { +} + +func (t *loadSecretTask) Run(ctx context.Context, pctx *plancontext.Context, s solver.Solver, v *compiler.Value) (*compiler.Value, error) { + path, err := v.Lookup("path").String() + if err != nil { + return nil, err + } + + input, err := pctx.FS.FromValue(v.Lookup("input")) + if err != nil { + return nil, err + } + inputFS := solver.NewBuildkitFS(input.Result()) + + // FIXME: we should create an intermediate image containing only `path`. + // That way, on cache misses, we'll only download the layer with the file contents rather than the entire FS. + contents, err := fs.ReadFile(inputFS, path) + if err != nil { + return nil, fmt.Errorf("ReadFile %s: %w", path, err) + } + plaintext := string(contents) + + trimSpace, err := v.Lookup("trimSpace").Bool() + if err != nil { + return nil, err + } + if trimSpace { + plaintext = strings.TrimSpace(plaintext) + } + + secret := pctx.Secrets.New(plaintext) + + return compiler.NewValue().FillFields(map[string]interface{}{ + "contents": secret.MarshalCUE(), + }) +} diff --git a/stdlib/europa/dagger/engine/secret.cue b/stdlib/europa/dagger/engine/secret.cue new file mode 100644 index 00000000..a4cbe811 --- /dev/null +++ b/stdlib/europa/dagger/engine/secret.cue @@ -0,0 +1,15 @@ +package engine + +// Load a secret from a filesystem tree +#LoadSecret: { + $dagger: task: _name: "LoadSecret" + + // Filesystem tree holding the secret + input: #FS + // Path of the secret to read + path: string + // Whether to trim leading and trailing space characters from secret value + trimSpace: *true | false + // Contents of the secret + contents: #Secret +} diff --git a/tests/tasks.bats b/tests/tasks.bats index c93a408e..765aa61d 100644 --- a/tests/tasks.bats +++ b/tests/tasks.bats @@ -125,3 +125,9 @@ setup() { run "$DAGGER" --europa up ./tasks/httpfetch/not_exist.cue assert_failure } + +@test "task: #LoadSecret" { + cd "$TESTDIR"/tasks/loadsecret + + "$DAGGER" --europa up ./loadsecret.cue +} diff --git a/tests/tasks/loadsecret/loadsecret.cue b/tests/tasks/loadsecret/loadsecret.cue new file mode 100644 index 00000000..8ca558a5 --- /dev/null +++ b/tests/tasks/loadsecret/loadsecret.cue @@ -0,0 +1,37 @@ +package main + +import ( + "alpha.dagger.io/europa/dagger/engine" +) + +engine.#Plan & { + actions: { + image: engine.#Pull & { + source: "alpine:3.15.0@sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3" + } + + generate: engine.#Exec & { + input: image.output + args: ["sh", "-c", "echo test > /secret"] + } + + load: engine.#LoadSecret & { + input: generate.output + path: "/secret" + } + + verify: engine.#Exec & { + input: image.output + mounts: secret: { + dest: "/run/secrets/test" + contents: load.contents + } + args: [ + "sh", "-c", + #""" + test "$(cat /run/secrets/test)" = "test" + """#, + ] + } + } +} From 5f840723e21524ec8413fed1af90f2e3d7d005b6 Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Fri, 7 Jan 2022 11:52:16 -0800 Subject: [PATCH 2/3] engine: LoadSecret->NewSecret Signed-off-by: Andrea Luzzardi --- docs/reference/europa/dagger/engine.md | 24 +++++++++---------- plan/task/{loadsecret.go => newsecret.go} | 8 +++---- stdlib/europa/dagger/engine/secret.cue | 8 +++---- tests/tasks.bats | 6 ++--- .../newsecret.cue} | 4 ++-- 5 files changed, 25 insertions(+), 25 deletions(-) rename plan/task/{loadsecret.go => newsecret.go} (78%) rename tests/tasks/{loadsecret/loadsecret.cue => newsecret/newsecret.cue} (90%) diff --git a/docs/reference/europa/dagger/engine.md b/docs/reference/europa/dagger/engine.md index 34ee1528..b591dd29 100644 --- a/docs/reference/europa/dagger/engine.md +++ b/docs/reference/europa/dagger/engine.md @@ -138,18 +138,6 @@ _No input._ _No output._ -## engine.#LoadSecret - -Load a secret from a filesystem tree - -### engine.#LoadSecret Inputs - -_No input._ - -### engine.#LoadSecret Outputs - -_No output._ - ## engine.#Merge Merge multiple FS trees into one @@ -186,6 +174,18 @@ _No input._ _No output._ +## engine.#NewSecret + +Create a new a secret from a filesystem tree + +### engine.#NewSecret Inputs + +_No input._ + +### engine.#NewSecret Outputs + +_No output._ + ## engine.#Plan A deployment plan executed by `dagger up` diff --git a/plan/task/loadsecret.go b/plan/task/newsecret.go similarity index 78% rename from plan/task/loadsecret.go rename to plan/task/newsecret.go index 794e83b3..0fbaf8ec 100644 --- a/plan/task/loadsecret.go +++ b/plan/task/newsecret.go @@ -12,13 +12,13 @@ import ( ) func init() { - Register("LoadSecret", func() Task { return &loadSecretTask{} }) + Register("NewSecret", func() Task { return &newSecretTask{} }) } -type loadSecretTask struct { +type newSecretTask struct { } -func (t *loadSecretTask) Run(ctx context.Context, pctx *plancontext.Context, s solver.Solver, v *compiler.Value) (*compiler.Value, error) { +func (t *newSecretTask) Run(ctx context.Context, pctx *plancontext.Context, s solver.Solver, v *compiler.Value) (*compiler.Value, error) { path, err := v.Lookup("path").String() if err != nil { return nil, err @@ -49,6 +49,6 @@ func (t *loadSecretTask) Run(ctx context.Context, pctx *plancontext.Context, s s secret := pctx.Secrets.New(plaintext) return compiler.NewValue().FillFields(map[string]interface{}{ - "contents": secret.MarshalCUE(), + "output": secret.MarshalCUE(), }) } diff --git a/stdlib/europa/dagger/engine/secret.cue b/stdlib/europa/dagger/engine/secret.cue index a4cbe811..9a202622 100644 --- a/stdlib/europa/dagger/engine/secret.cue +++ b/stdlib/europa/dagger/engine/secret.cue @@ -1,8 +1,8 @@ package engine -// Load a secret from a filesystem tree -#LoadSecret: { - $dagger: task: _name: "LoadSecret" +// Create a new a secret from a filesystem tree +#NewSecret: { + $dagger: task: _name: "NewSecret" // Filesystem tree holding the secret input: #FS @@ -11,5 +11,5 @@ package engine // Whether to trim leading and trailing space characters from secret value trimSpace: *true | false // Contents of the secret - contents: #Secret + output: #Secret } diff --git a/tests/tasks.bats b/tests/tasks.bats index 765aa61d..193e7e57 100644 --- a/tests/tasks.bats +++ b/tests/tasks.bats @@ -126,8 +126,8 @@ setup() { assert_failure } -@test "task: #LoadSecret" { - cd "$TESTDIR"/tasks/loadsecret +@test "task: #NewSecret" { + cd "$TESTDIR"/tasks/newsecret - "$DAGGER" --europa up ./loadsecret.cue + "$DAGGER" --europa up ./newsecret.cue } diff --git a/tests/tasks/loadsecret/loadsecret.cue b/tests/tasks/newsecret/newsecret.cue similarity index 90% rename from tests/tasks/loadsecret/loadsecret.cue rename to tests/tasks/newsecret/newsecret.cue index 8ca558a5..5bebcbb2 100644 --- a/tests/tasks/loadsecret/loadsecret.cue +++ b/tests/tasks/newsecret/newsecret.cue @@ -15,7 +15,7 @@ engine.#Plan & { args: ["sh", "-c", "echo test > /secret"] } - load: engine.#LoadSecret & { + load: engine.#NewSecret & { input: generate.output path: "/secret" } @@ -24,7 +24,7 @@ engine.#Plan & { input: image.output mounts: secret: { dest: "/run/secrets/test" - contents: load.contents + contents: load.output } args: [ "sh", "-c", From a61291a8870334a1ac3ce15132ee97906bc9065c Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Fri, 7 Jan 2022 11:57:48 -0800 Subject: [PATCH 3/3] engine: redact dynamic secrets (e.g. #NewSecret) Signed-off-by: Andrea Luzzardi --- client/client.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/client.go b/client/client.go index 4e411743..bd8cc25b 100644 --- a/client/client.go +++ b/client/client.go @@ -213,8 +213,10 @@ func (c *Client) logSolveStatus(ctx context.Context, pctx *plancontext.Context, } // Just like sprintf, but redacts secrets automatically - secrets := pctx.Secrets.List() secureSprintf := func(format string, a ...interface{}) string { + // Load a fresh copy of secrets (since they can be dynamically added). + secrets := pctx.Secrets.List() + s := fmt.Sprintf(format, a...) for _, secret := range secrets { s = strings.ReplaceAll(s, secret.PlainText(), "***")