From c704452edfa9588441916f36e4ef10b737d4552d Mon Sep 17 00:00:00 2001 From: Aishwarya Lad <67022814+Aishwarya-Lad@users.noreply.github.com> Date: Thu, 17 Oct 2024 19:30:09 +0530 Subject: [PATCH 1/2] fix:[CI-13924]:add support for base64 encoded env secret docker secret vars --- app.go | 12 +++++++ docker.go | 94 +++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 72 insertions(+), 34 deletions(-) diff --git a/app.go b/app.go index b7c6b22..1ddbaa3 100644 --- a/app.go +++ b/app.go @@ -275,6 +275,16 @@ func Run() { Usage: "secret key value pair eg id=MYSECRET", EnvVar: "PLUGIN_SECRET", }, + cli.BoolFlag{ + Name: "encoded-secrets-from-env", + Usage: "base64 encoded values", + EnvVar: "PLUGIN_ENCODED_ENV_SECRET", + }, + cli.BoolFlag{ + Name: "decode-env-secret", + Usage: "decode env values default-false", + EnvVar: "PLUGIN_DECODE_ENV_SECRET", + }, cli.StringSliceFlag{ Name: "secrets-from-env", Usage: "secret key value pair eg secret_name=secret", @@ -403,6 +413,8 @@ func run(c *cli.Context) error { Platform: c.String("platform"), SSHAgentKey: c.String("ssh-agent-key"), BuildxLoad: c.Bool("buildx-load"), + DecodeEnvSecret: c.Bool("decode-env-secret"), + EncodedSecretEnvs: c.StringSlice("encoded-secrets-from-env") }, Daemon: Daemon{ Registry: c.String("docker.registry"), diff --git a/docker.go b/docker.go index 7284161..22acaa8 100644 --- a/docker.go +++ b/docker.go @@ -1,8 +1,10 @@ package docker import ( + "encoding/base64" "encoding/json" "fmt" + "log" "os" "os/exec" "path/filepath" @@ -52,34 +54,35 @@ type ( // Build defines Docker build parameters. Build struct { - Remote string // Git remote URL - Name string // Docker build using default named tag - Dockerfile string // Docker build Dockerfile - Context string // Docker build context - Tags []string // Docker build tags - Args []string // Docker build args - ArgsEnv []string // Docker build args from env - Target string // Docker build target - Squash bool // Docker build squash - Pull bool // Docker build pull - CacheFrom []string // Docker buildx cache-from - CacheTo []string // Docker buildx cache-to - Compress bool // Docker build compress - Repo string // Docker build repository - LabelSchema []string // label-schema Label map - AutoLabel bool // auto-label bool - Labels []string // Label map - Link string // Git repo link - NoCache bool // Docker build no-cache - Secret string // secret keypair - SecretEnvs []string // Docker build secrets with env var as source - SecretFiles []string // Docker build secrets with file as source - AddHost []string // Docker build add-host - Quiet bool // Docker build quiet - Platform string // Docker build platform - SSHAgentKey string // Docker build ssh agent key - SSHKeyPath string // Docker build ssh key path - BuildxLoad bool // Docker buildx --load + Remote string // Git remote URL + Name string // Docker build using default named tag + Dockerfile string // Docker build Dockerfile + Context string // Docker build context + Tags []string // Docker build tags + Args []string // Docker build args + ArgsEnv []string // Docker build args from env + Target string // Docker build target + Squash bool // Docker build squash + Pull bool // Docker build pull + CacheFrom []string // Docker buildx cache-from + CacheTo []string // Docker buildx cache-to + Compress bool // Docker build compress + Repo string // Docker build repository + LabelSchema []string // label-schema Label map + AutoLabel bool // auto-label bool + Labels []string // Label map + Link string // Git repo link + NoCache bool // Docker build no-cache + Secret string // secret keypair + SecretEnvs []string // Docker build secrets with env var as source + SecretFiles []string // Docker build secrets with file as source + AddHost []string // Docker build add-host + Quiet bool // Docker build quiet + Platform string // Docker build platform + SSHAgentKey string // Docker build ssh agent key + SSHKeyPath string // Docker build ssh key path + BuildxLoad bool // Docker buildx --load + DecodeEnvSecret bool // Decode the secret value in env } // Plugin defines the Docker plugin parameters. @@ -164,7 +167,6 @@ func (p Plugin) Exec() error { default: fmt.Println("Registry credentials or Docker config not provided. Guest mode enabled.") } - // create Auth Config File if p.Login.Config != "" { os.MkdirAll(dockerHome, 0600) @@ -507,7 +509,7 @@ func commandBuildx(build Build, builder Builder, dryrun bool, metadataFile strin args = append(args, "--secret", build.Secret) } for _, secret := range build.SecretEnvs { - if arg, err := getSecretStringCmdArg(secret); err == nil { + if arg, err := getSecretStringCmdArg(secret, build.DecodeEnvSecret, build.EncodedSecretEnvs); err == nil { args = append(args, "--secret", arg) } } @@ -555,19 +557,35 @@ func commandBuildx(build Build, builder Builder, dryrun bool, metadataFile strin return exec.Command(dockerExe, args...) } -func getSecretStringCmdArg(kvp string) (string, error) { - return getSecretCmdArg(kvp, false) +func getSecretStringCmdArg(kvp string, decode bool, e_kvp string) (string, error) { + if decode { + // Handle base64 encoded secrets + decodedResult, err := getSecretCmdArg(e_kvp, false, true) + if err != nil { + return "", fmt.Errorf("error decoding secret: %w", err) + } + // Handle regular secrets + regularResult, err := getSecretCmdArg(kvp, false, false) + if err != nil { + return "", fmt.Errorf("error getting regular secret: %w", err) + } + // Combine calling for base64 and regular docker secrets + return regularResult + decodedResult, nil + } + // Original behavior + return getSecretCmdArg(kvp, false, false) } func getSecretFileCmdArg(kvp string) (string, error) { - return getSecretCmdArg(kvp, true) + return getSecretCmdArg(kvp, true, false) } -func getSecretCmdArg(kvp string, file bool) (string, error) { +func getSecretCmdArg(kvp string, file bool, decode bool) (string, error) { delimIndex := strings.IndexByte(kvp, '=') if delimIndex == -1 { return "", fmt.Errorf("%s is not a valid secret", kvp) } + log.Printf("kvp string : ", kvp) key := kvp[:delimIndex] value := kvp[delimIndex+1:] @@ -579,6 +597,14 @@ func getSecretCmdArg(kvp string, file bool) (string, error) { if file { return fmt.Sprintf("id=%s,src=%s", key, value), nil } + if decode { + decodedValue, err := base64.StdEncoding.DecodeString(value) + log.Printf("decoded value of secret : ", decodedValue) + if err != nil { + return "", fmt.Errorf("failed to decode base64 value: %v", err) + } + value = string(decodedValue) + } return fmt.Sprintf("id=%s,env=%s", key, value), nil } From c456686ae2e67645038a7ef35b3570a80ff77a1b Mon Sep 17 00:00:00 2001 From: Aishwarya Lad <67022814+Aishwarya-Lad@users.noreply.github.com> Date: Fri, 18 Oct 2024 10:40:13 +0530 Subject: [PATCH 2/2] add unit tests --- app.go | 2 +- docker.go | 89 +++++++++++++++++++++++++------------------------- docker_test.go | 32 ++++++++++++++++++ 3 files changed, 77 insertions(+), 46 deletions(-) diff --git a/app.go b/app.go index 1ddbaa3..60c78db 100644 --- a/app.go +++ b/app.go @@ -414,7 +414,7 @@ func run(c *cli.Context) error { SSHAgentKey: c.String("ssh-agent-key"), BuildxLoad: c.Bool("buildx-load"), DecodeEnvSecret: c.Bool("decode-env-secret"), - EncodedSecretEnvs: c.StringSlice("encoded-secrets-from-env") + EncodedSecretEnvs: c.StringSlice("encoded-secrets-from-env"), }, Daemon: Daemon{ Registry: c.String("docker.registry"), diff --git a/docker.go b/docker.go index 22acaa8..96e836a 100644 --- a/docker.go +++ b/docker.go @@ -4,7 +4,7 @@ import ( "encoding/base64" "encoding/json" "fmt" - "log" + //"log" "os" "os/exec" "path/filepath" @@ -54,35 +54,36 @@ type ( // Build defines Docker build parameters. Build struct { - Remote string // Git remote URL - Name string // Docker build using default named tag - Dockerfile string // Docker build Dockerfile - Context string // Docker build context - Tags []string // Docker build tags - Args []string // Docker build args - ArgsEnv []string // Docker build args from env - Target string // Docker build target - Squash bool // Docker build squash - Pull bool // Docker build pull - CacheFrom []string // Docker buildx cache-from - CacheTo []string // Docker buildx cache-to - Compress bool // Docker build compress - Repo string // Docker build repository - LabelSchema []string // label-schema Label map - AutoLabel bool // auto-label bool - Labels []string // Label map - Link string // Git repo link - NoCache bool // Docker build no-cache - Secret string // secret keypair - SecretEnvs []string // Docker build secrets with env var as source - SecretFiles []string // Docker build secrets with file as source - AddHost []string // Docker build add-host - Quiet bool // Docker build quiet - Platform string // Docker build platform - SSHAgentKey string // Docker build ssh agent key - SSHKeyPath string // Docker build ssh key path - BuildxLoad bool // Docker buildx --load - DecodeEnvSecret bool // Decode the secret value in env + Remote string // Git remote URL + Name string // Docker build using default named tag + Dockerfile string // Docker build Dockerfile + Context string // Docker build context + Tags []string // Docker build tags + Args []string // Docker build args + ArgsEnv []string // Docker build args from env + Target string // Docker build target + Squash bool // Docker build squash + Pull bool // Docker build pull + CacheFrom []string // Docker buildx cache-from + CacheTo []string // Docker buildx cache-to + Compress bool // Docker build compress + Repo string // Docker build repository + LabelSchema []string // label-schema Label map + AutoLabel bool // auto-label bool + Labels []string // Label map + Link string // Git repo link + NoCache bool // Docker build no-cache + Secret string // secret keypair + SecretEnvs []string // Docker build secrets with env var as source + SecretFiles []string // Docker build secrets with file as source + AddHost []string // Docker build add-host + Quiet bool // Docker build quiet + Platform string // Docker build platform + SSHAgentKey string // Docker build ssh agent key + SSHKeyPath string // Docker build ssh key path + BuildxLoad bool // Docker buildx --load + DecodeEnvSecret bool // Decode the secret value in env + EncodedSecretEnvs []string // Docker build secrets that are encoded using base64 } // Plugin defines the Docker plugin parameters. @@ -509,10 +510,18 @@ func commandBuildx(build Build, builder Builder, dryrun bool, metadataFile strin args = append(args, "--secret", build.Secret) } for _, secret := range build.SecretEnvs { - if arg, err := getSecretStringCmdArg(secret, build.DecodeEnvSecret, build.EncodedSecretEnvs); err == nil { + if arg, err := getSecretStringCmdArg(secret, build.DecodeEnvSecret); err == nil { args = append(args, "--secret", arg) } } + // Handle base64 encoded secrets + if build.DecodeEnvSecret { + for _, encodedSecret := range build.EncodedSecretEnvs { + if arg, err := getSecretStringCmdArg(encodedSecret, build.DecodeEnvSecret); err == nil { + args = append(args, "--secret", arg) + } + } + } for _, secret := range build.SecretFiles { if arg, err := getSecretFileCmdArg(secret); err == nil { args = append(args, "--secret", arg) @@ -557,20 +566,10 @@ func commandBuildx(build Build, builder Builder, dryrun bool, metadataFile strin return exec.Command(dockerExe, args...) } -func getSecretStringCmdArg(kvp string, decode bool, e_kvp string) (string, error) { +func getSecretStringCmdArg(kvp string, decode bool) (string, error) { if decode { // Handle base64 encoded secrets - decodedResult, err := getSecretCmdArg(e_kvp, false, true) - if err != nil { - return "", fmt.Errorf("error decoding secret: %w", err) - } - // Handle regular secrets - regularResult, err := getSecretCmdArg(kvp, false, false) - if err != nil { - return "", fmt.Errorf("error getting regular secret: %w", err) - } - // Combine calling for base64 and regular docker secrets - return regularResult + decodedResult, nil + return getSecretCmdArg(kvp, false, true) } // Original behavior return getSecretCmdArg(kvp, false, false) @@ -585,7 +584,7 @@ func getSecretCmdArg(kvp string, file bool, decode bool) (string, error) { if delimIndex == -1 { return "", fmt.Errorf("%s is not a valid secret", kvp) } - log.Printf("kvp string : ", kvp) + //log.Printf("kvp string : ", kvp) key := kvp[:delimIndex] value := kvp[delimIndex+1:] @@ -599,7 +598,7 @@ func getSecretCmdArg(kvp string, file bool, decode bool) (string, error) { } if decode { decodedValue, err := base64.StdEncoding.DecodeString(value) - log.Printf("decoded value of secret : ", decodedValue) + //log.Printf("decoded value of secret : ", decodedValue) if err != nil { return "", fmt.Errorf("failed to decode base64 value: %v", err) } diff --git a/docker_test.go b/docker_test.go index b4094aa..5a5ff24 100644 --- a/docker_test.go +++ b/docker_test.go @@ -207,6 +207,38 @@ func TestCommandBuildx(t *testing.T) { "--metadata-file /tmp/metadata.json", ), }, + // Add this new test case + { + name: "decoded secret from env var", + build: Build{ + Name: "plugins/drone-docker:latest", + Dockerfile: "Dockerfile", + Context: ".", + SecretEnvs: []string{ + "foo_secret=FOO_SECRET_ENV_VAR", + }, + EncodedSecretEnvs: []string{ + "encoded_secret=RU5DT0RFRF9TRUNSRVRfRU5WX1ZBUg==", // Base64 for "ENCODED_SECRET_ENV_VAR" + }, + DecodeEnvSecret: true, + Repo: "plugins/drone-docker", + Tags: []string{"latest"}, + }, + want: exec.Command( + dockerExe, + "buildx", + "build", + "--rm=true", + "-f", + "Dockerfile", + "-t", + "plugins/drone-docker:latest", + "--push", + ".", + "--secret", "id=foo_secret,env=FOO_SECRET_ENV_VAR", + "--secret", "id=encoded_secret,env=ENCODED_SECRET_ENV_VAR", + ), + }, } for _, tc := range tcs {