Skip to content

Commit ff923bf

Browse files
[feat]: [CI-15039]: Support secret decryption on delegate for DLC (#51)
* [feat]: [CI-15039]: Support secret decryption on delegate for DLC * Update docker.go * Update docker.go * Update docker_test.go * Update docker_test.go
1 parent 0c17d46 commit ff923bf

File tree

3 files changed

+206
-59
lines changed

3 files changed

+206
-59
lines changed

app.go

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,21 @@ func Run() {
395395
Usage: "Buildkit response header timeout override. Default value is 30s",
396396
EnvVar: "PLUGIN_BUILDKIT_RESPONSE_HEADER_TIMEOUT",
397397
},
398+
cli.StringFlag{
399+
Name: "harness-self-hosted-s3-access-key",
400+
Usage: "build target",
401+
EnvVar: "PLUGIN_HARNESS_SELF_HOSTED_S3_ACCESS_KEY",
402+
},
403+
cli.StringFlag{
404+
Name: "harness-self-hosted-s3-secret-key",
405+
Usage: "build target",
406+
EnvVar: "PLUGIN_HARNESS_SELF_HOSTED_S3_SECRET_KEY",
407+
},
408+
cli.StringFlag{
409+
Name: "harness-self-hosted-gcp-json-key",
410+
Usage: "build target",
411+
EnvVar: "PLUGIN_HARNESS_SELF_HOSTED_GCP_JSON_KEY",
412+
},
398413
}
399414

400415
if err := app.Run(os.Args); err != nil {
@@ -424,35 +439,38 @@ func run(c *cli.Context) error {
424439
ArtifactFile: c.String("artifact-file"),
425440
CacheMetricsFile: c.String("cache-metrics-file"),
426441
Build: Build{
427-
Remote: c.String("remote.url"),
428-
Name: c.String("commit.sha"),
429-
Dockerfile: c.String("dockerfile"),
430-
Context: c.String("context"),
431-
Tags: c.StringSlice("tags"),
432-
Args: c.StringSlice("args"),
433-
ArgsEnv: c.StringSlice("args-from-env"),
434-
ArgsNew: c.Generic("args-new").(*CustomStringSliceFlag).GetValue(),
435-
IsMultipleBuildArgs: c.Bool("plugin-multiple-build-agrs"),
436-
Target: c.String("target"),
437-
Squash: c.Bool("squash"),
438-
Pull: c.BoolT("pull-image"),
439-
CacheFrom: c.Generic("cache-from").(*CustomStringSliceFlag).GetValue(),
440-
CacheTo: c.Generic("cache-to").(*CustomStringSliceFlag).GetValue(),
441-
Compress: c.Bool("compress"),
442-
Repo: c.String("repo"),
443-
Labels: c.StringSlice("custom-labels"),
444-
LabelSchema: c.StringSlice("label-schema"),
445-
AutoLabel: c.BoolT("auto-label"),
446-
Link: c.String("link"),
447-
NoCache: c.Bool("no-cache"),
448-
Secret: c.String("secret"),
449-
SecretEnvs: c.StringSlice("secrets-from-env"),
450-
SecretFiles: c.StringSlice("secrets-from-file"),
451-
AddHost: c.StringSlice("add-host"),
452-
Quiet: c.Bool("quiet"),
453-
Platform: c.String("platform"),
454-
SSHAgentKey: c.String("ssh-agent-key"),
455-
BuildxLoad: c.Bool("buildx-load"),
442+
Remote: c.String("remote.url"),
443+
Name: c.String("commit.sha"),
444+
Dockerfile: c.String("dockerfile"),
445+
Context: c.String("context"),
446+
Tags: c.StringSlice("tags"),
447+
Args: c.StringSlice("args"),
448+
ArgsEnv: c.StringSlice("args-from-env"),
449+
ArgsNew: c.Generic("args-new").(*CustomStringSliceFlag).GetValue(),
450+
IsMultipleBuildArgs: c.Bool("plugin-multiple-build-agrs"),
451+
Target: c.String("target"),
452+
Squash: c.Bool("squash"),
453+
Pull: c.BoolT("pull-image"),
454+
CacheFrom: c.Generic("cache-from").(*CustomStringSliceFlag).GetValue(),
455+
CacheTo: c.Generic("cache-to").(*CustomStringSliceFlag).GetValue(),
456+
Compress: c.Bool("compress"),
457+
Repo: c.String("repo"),
458+
Labels: c.StringSlice("custom-labels"),
459+
LabelSchema: c.StringSlice("label-schema"),
460+
AutoLabel: c.BoolT("auto-label"),
461+
Link: c.String("link"),
462+
NoCache: c.Bool("no-cache"),
463+
Secret: c.String("secret"),
464+
SecretEnvs: c.StringSlice("secrets-from-env"),
465+
SecretFiles: c.StringSlice("secrets-from-file"),
466+
AddHost: c.StringSlice("add-host"),
467+
Quiet: c.Bool("quiet"),
468+
Platform: c.String("platform"),
469+
SSHAgentKey: c.String("ssh-agent-key"),
470+
BuildxLoad: c.Bool("buildx-load"),
471+
HarnessSelfHostedS3AccessKey: c.String("harness-self-hosted-s3-access-key"),
472+
HarnessSelfHostedS3SecretKey: c.String("harness-self-hosted-s3-secret-key"),
473+
HarnessSelfHostedGcpJsonKey: c.String("harness-self-hosted-gcp-json-key"),
456474
},
457475
Daemon: Daemon{
458476
Registry: c.String("docker.registry"),

docker.go

Lines changed: 76 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package docker
22

33
import (
44
"bytes"
5+
"encoding/base64"
56
"encoding/json"
67
"fmt"
78
"os"
@@ -60,36 +61,39 @@ type (
6061

6162
// Build defines Docker build parameters.
6263
Build struct {
63-
Remote string // Git remote URL
64-
Name string // Docker build using default named tag
65-
Dockerfile string // Docker build Dockerfile
66-
Context string // Docker build context
67-
Tags []string // Docker build tags
68-
Args []string // Docker build args
69-
ArgsEnv []string // Docker build args from env
70-
ArgsNew []string // Docker build args with comma seperated values
71-
IsMultipleBuildArgs bool // env variable for fall back
72-
Target string // Docker build target
73-
Squash bool // Docker build squash
74-
Pull bool // Docker build pull
75-
CacheFrom []string // Docker buildx cache-from
76-
CacheTo []string // Docker buildx cache-to
77-
Compress bool // Docker build compress
78-
Repo string // Docker build repository
79-
LabelSchema []string // label-schema Label map
80-
AutoLabel bool // auto-label bool
81-
Labels []string // Label map
82-
Link string // Git repo link
83-
NoCache bool // Docker build no-cache
84-
Secret string // secret keypair
85-
SecretEnvs []string // Docker build secrets with env var as source
86-
SecretFiles []string // Docker build secrets with file as source
87-
AddHost []string // Docker build add-host
88-
Quiet bool // Docker build quiet
89-
Platform string // Docker build platform
90-
SSHAgentKey string // Docker build ssh agent key
91-
SSHKeyPath string // Docker build ssh key path
92-
BuildxLoad bool // Docker buildx --load
64+
Remote string // Git remote URL
65+
Name string // Docker build using default named tag
66+
Dockerfile string // Docker build Dockerfile
67+
Context string // Docker build context
68+
Tags []string // Docker build tags
69+
Args []string // Docker build args
70+
ArgsEnv []string // Docker build args from env
71+
ArgsNew []string // Docker build args with comma seperated values
72+
IsMultipleBuildArgs bool // env variable for fall back
73+
Target string // Docker build target
74+
Squash bool // Docker build squash
75+
Pull bool // Docker build pull
76+
CacheFrom []string // Docker buildx cache-from
77+
CacheTo []string // Docker buildx cache-to
78+
Compress bool // Docker build compress
79+
Repo string // Docker build repository
80+
LabelSchema []string // label-schema Label map
81+
AutoLabel bool // auto-label bool
82+
Labels []string // Label map
83+
Link string // Git repo link
84+
NoCache bool // Docker build no-cache
85+
Secret string // secret keypair
86+
SecretEnvs []string // Docker build secrets with env var as source
87+
SecretFiles []string // Docker build secrets with file as source
88+
AddHost []string // Docker build add-host
89+
Quiet bool // Docker build quiet
90+
Platform string // Docker build platform
91+
SSHAgentKey string // Docker build ssh agent key
92+
SSHKeyPath string // Docker build ssh key path
93+
BuildxLoad bool // Docker buildx --load
94+
HarnessSelfHostedS3AccessKey string // Harness self-hosted s3 access key
95+
HarnessSelfHostedS3SecretKey string // Harness self-hosted s3 secret key
96+
HarnessSelfHostedGcpJsonKey string // Harness self hosted gcp json region
9397
}
9498

9599
// Plugin defines the Docker plugin parameters.
@@ -562,6 +566,8 @@ func commandBuildx(build Build, builder Builder, dryrun bool, metadataFile strin
562566
"-f", build.Dockerfile,
563567
}
564568

569+
sanitizeCacheCommand(&build)
570+
565571
if builder.Name != "" {
566572
args = append(args, "--builder", builder.Name)
567573
}
@@ -664,6 +670,46 @@ func commandBuildx(build Build, builder Builder, dryrun bool, metadataFile strin
664670
return exec.Command(dockerExe, args...)
665671
}
666672

673+
func sanitizeCacheCommand(build *Build) {
674+
// Helper function to sanitize cache arguments
675+
sanitizeCacheArgs := func(args []string) []string {
676+
for i, arg := range args {
677+
678+
// Replace access_key_id if placeholder exists and the actual key is not empty
679+
if strings.Contains(arg, "access_key_id=harness_placeholder_aws_creds") && build.HarnessSelfHostedS3AccessKey != "" {
680+
arg = strings.Replace(arg, "access_key_id=harness_placeholder_aws_creds", "access_key_id="+build.HarnessSelfHostedS3AccessKey, 1)
681+
}
682+
683+
// Replace secret_access_key if placeholder exists and the actual key is not empty
684+
if strings.Contains(arg, "secret_access_key=harness_placeholder_aws_creds") && build.HarnessSelfHostedS3SecretKey != "" {
685+
arg = strings.Replace(arg, "secret_access_key=harness_placeholder_aws_creds", "secret_access_key="+build.HarnessSelfHostedS3SecretKey, 1)
686+
}
687+
688+
// Handle gcp_json_key
689+
if strings.Contains(arg, "gcp_json_key=harness_placeholder_gcp_creds") {
690+
if build.HarnessSelfHostedGcpJsonKey != "" {
691+
// Base64 encode the GCP JSON key
692+
encodedGCPJsonKey := base64.StdEncoding.EncodeToString([]byte(build.HarnessSelfHostedGcpJsonKey))
693+
// Replace the placeholder with the base64-encoded GCP JSON key
694+
arg = strings.Replace(arg, "gcp_json_key=harness_placeholder_gcp_creds", "gcp_json_key="+encodedGCPJsonKey, 1)
695+
} else {
696+
// Remove the gcp_json_key substring if the actual key is empty
697+
arg = strings.Replace(arg, ",gcp_json_key=harness_placeholder_gcp_creds", "", 1)
698+
arg = strings.Replace(arg, "gcp_json_key=harness_placeholder_gcp_creds,", "", 1)
699+
arg = strings.Replace(arg, "gcp_json_key=harness_placeholder_gcp_creds", "", 1)
700+
}
701+
}
702+
703+
// Update the argument
704+
args[i] = arg
705+
}
706+
return args
707+
}
708+
709+
build.CacheFrom = sanitizeCacheArgs(build.CacheFrom)
710+
build.CacheTo = sanitizeCacheArgs(build.CacheTo)
711+
}
712+
667713
func getSecretStringCmdArg(kvp string) (string, error) {
668714
return getSecretCmdArg(kvp, false)
669715
}

docker_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,89 @@ func TestCommandBuildx(t *testing.T) {
220220
}
221221
}
222222

223+
func TestSanitizeCacheCommand(t *testing.T) {
224+
tests := []struct {
225+
name string
226+
build Build
227+
expectedCacheFrom []string
228+
expectedCacheTo []string
229+
}{
230+
{
231+
name: "Replace AWS placeholders in CacheFrom and CacheTo",
232+
build: Build{
233+
CacheFrom: []string{"type=s3,access_key_id=harness_placeholder_aws_creds,secret_access_key=harness_placeholder_aws_creds"},
234+
CacheTo: []string{"type=s3,access_key_id=harness_placeholder_aws_creds,secret_access_key=harness_placeholder_aws_creds"},
235+
HarnessSelfHostedS3AccessKey: "actual_access_key",
236+
HarnessSelfHostedS3SecretKey: "actual_secret_key",
237+
},
238+
expectedCacheFrom: []string{"type=s3,access_key_id=actual_access_key,secret_access_key=actual_secret_key"},
239+
expectedCacheTo: []string{"type=s3,access_key_id=actual_access_key,secret_access_key=actual_secret_key"},
240+
},
241+
{
242+
name: "Replace GCP placeholder in CacheFrom",
243+
build: Build{
244+
CacheFrom: []string{"type=gcs,gcp_json_key=harness_placeholder_gcp_creds"},
245+
CacheTo: []string{},
246+
HarnessSelfHostedGcpJsonKey: "actual_gcp_key",
247+
},
248+
expectedCacheFrom: []string{"type=gcs,gcp_json_key=YWN0dWFsX2djcF9rZXk="},
249+
expectedCacheTo: []string{},
250+
},
251+
{
252+
name: "Remove GCP placeholder when key is empty",
253+
build: Build{
254+
CacheFrom: []string{"type=gcs,gcp_json_key=harness_placeholder_gcp_creds"},
255+
CacheTo: []string{"type=gcs,bucket=test,gcp_json_key=harness_placeholder_gcp_creds,prefix=dlc"},
256+
HarnessSelfHostedGcpJsonKey: "",
257+
},
258+
expectedCacheFrom: []string{"type=gcs"},
259+
expectedCacheTo: []string{"type=gcs,bucket=test,prefix=dlc"},
260+
},
261+
{
262+
name: "Multiple placeholders in CacheFrom",
263+
build: Build{
264+
CacheFrom: []string{"type=gcs,gcp_json_key=harness_placeholder_gcp_creds,access_key_id=harness_placeholder_aws_creds,secret_access_key=harness_placeholder_aws_creds"},
265+
CacheTo: []string{},
266+
HarnessSelfHostedS3AccessKey: "actual_access_key",
267+
HarnessSelfHostedS3SecretKey: "actual_secret_key",
268+
HarnessSelfHostedGcpJsonKey: "actual_gcp_key",
269+
},
270+
expectedCacheFrom: []string{"type=gcs,gcp_json_key=YWN0dWFsX2djcF9rZXk=,access_key_id=actual_access_key,secret_access_key=actual_secret_key"},
271+
expectedCacheTo: []string{},
272+
},
273+
{
274+
name: "No placeholders in CacheFrom and CacheTo",
275+
build: Build{
276+
CacheFrom: []string{"type=s3,bucket=test"},
277+
CacheTo: []string{"type=gcs,bucket=test"},
278+
},
279+
expectedCacheFrom: []string{"type=s3,bucket=test"},
280+
expectedCacheTo: []string{"type=gcs,bucket=test"},
281+
},
282+
{
283+
name: "Empty CacheFrom and CacheTo",
284+
build: Build{
285+
CacheFrom: []string{},
286+
CacheTo: []string{},
287+
},
288+
expectedCacheFrom: []string{},
289+
expectedCacheTo: []string{},
290+
},
291+
}
292+
293+
for _, tt := range tests {
294+
t.Run(tt.name, func(t *testing.T) {
295+
sanitizeCacheCommand(&tt.build)
296+
if !reflect.DeepEqual(tt.build.CacheFrom, tt.expectedCacheFrom) {
297+
t.Errorf("CacheFrom = %v, want %v", tt.build.CacheFrom, tt.expectedCacheFrom)
298+
}
299+
if !reflect.DeepEqual(tt.build.CacheTo, tt.expectedCacheTo) {
300+
t.Errorf("CacheTo = %v, want %v", tt.build.CacheTo, tt.expectedCacheTo)
301+
}
302+
})
303+
}
304+
}
305+
223306
func TestGetDigest(t *testing.T) {
224307

225308
tcs := []struct {

0 commit comments

Comments
 (0)