From 4d92e8103df1c5fab4dd13d5687dfa8d626942c2 Mon Sep 17 00:00:00 2001 From: Ole Henning Date: Wed, 13 Oct 2021 13:54:11 +0200 Subject: [PATCH 1/3] Add option to mount host ssh agent (--ssh) --- cmd/drone-docker/main.go | 6 ++++++ docker.go | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/cmd/drone-docker/main.go b/cmd/drone-docker/main.go index a2156c32..e6812c28 100644 --- a/cmd/drone-docker/main.go +++ b/cmd/drone-docker/main.go @@ -274,6 +274,11 @@ func main() { Usage: "platform value to pass to docker", EnvVar: "PLUGIN_PLATFORM", }, + cli.StringSliceFlag{ + Name: "ssh-agent", + Usage: "mount ssh agent", + EnvVar: "PLUGIN_SSH_AGENT", + }, } if err := app.Run(os.Args); err != nil { @@ -318,6 +323,7 @@ func run(c *cli.Context) error { AddHost: c.StringSlice("add-host"), Quiet: c.Bool("quiet"), Platform: c.String("platform"), + SSHAgent: c.StringSlice("ssh-agent"), }, Daemon: docker.Daemon{ Registry: c.String("docker.registry"), diff --git a/docker.go b/docker.go index a19f51e2..cf1cea6b 100644 --- a/docker.go +++ b/docker.go @@ -64,6 +64,7 @@ type ( AddHost []string // Docker build add-host Quiet bool // Docker build quiet Platform string // Docker build platform + SSHAgent []string // Docker build ssh } // Plugin defines the Docker plugin parameters. @@ -328,6 +329,9 @@ func commandBuild(build Build) *exec.Cmd { if build.Platform != "" { args = append(args, "--platform", build.Platform) } + if build.SSHAgent != "" { + args = append(args, "--ssh", build.SSHAgent) + } if build.AutoLabel { labelSchema := []string{ From 84d8664c70076fe20318d2ff7c1f5c197efe61bb Mon Sep 17 00:00:00 2001 From: Trent Albright Date: Fri, 1 Jul 2022 10:06:57 -0400 Subject: [PATCH 2/3] support agent forwarding missed error check add debugging fix empty val for SSHAgent fix flag type remove [] debug base64 encode ssh key fix remove debug output code cleanup Update docker.go --- docker.go | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/docker.go b/docker.go index cf1cea6b..21f8a85d 100644 --- a/docker.go +++ b/docker.go @@ -1,8 +1,10 @@ package docker import ( + "encoding/base64" "fmt" "io/ioutil" + "log" "os" "os/exec" "path/filepath" @@ -11,6 +13,11 @@ import ( "time" ) +const ( + SSHAgentSockPath = "/tmp/drone-ssh-agent-sock" + SSHPrivateKeyFromEnv = "SSH_KEY" +) + type ( // Daemon defines Docker daemon parameters. Daemon struct { @@ -107,6 +114,7 @@ type ( // Exec executes the plugin step func (p Plugin) Exec() error { + // start the Docker daemon server if !p.Daemon.Disabled { p.startDaemon() @@ -180,6 +188,13 @@ func (p Plugin) Exec() error { cmds = append(cmds, commandPull(img)) } + // setup for using ssh agent (https://docs.docker.com/develop/develop-images/build_enhancements/#using-ssh-to-access-private-data-in-builds) + + if len(p.Build.SSHAgent) > 0 { + fmt.Printf("ssh agent set to \"%s\"\n", p.Build.SSHAgent) + cmds = append(cmds, commandSSHAgentForwardingSetup(p.Build)...) + } + cmds = append(cmds, commandBuild(p.Build)) // docker build for _, tag := range p.Build.Tags { @@ -329,8 +344,8 @@ func commandBuild(build Build) *exec.Cmd { if build.Platform != "" { args = append(args, "--platform", build.Platform) } - if build.SSHAgent != "" { - args = append(args, "--ssh", build.SSHAgent) + for _, sshagent := range build.SSHAgent { + args = append(args, "--ssh", sshagent) } if build.AutoLabel { @@ -357,8 +372,8 @@ func commandBuild(build Build) *exec.Cmd { } } - // we need to enable buildkit, for secret support - if build.Secret != "" || len(build.SecretEnvs) > 0 || len(build.SecretFiles) > 0 { + // we need to enable buildkit, for secret support and ssh agent support + if build.Secret != "" || len(build.SecretEnvs) > 0 || len(build.SecretFiles) > 0 || len(build.SSHAgent) > 0 { os.Setenv("DOCKER_BUILDKIT", "1") } return exec.Command(dockerExe, args...) @@ -511,6 +526,40 @@ func commandRmi(tag string) *exec.Cmd { return exec.Command(dockerExe, "rmi", tag) } +func commandSSHAgentForwardingSetup(build Build) []*exec.Cmd { + cmds := make([]*exec.Cmd, 0) + if err := writeSSHPrivateKey(); err != nil { + log.Fatalf("unable to setup ssh agent forwarding: %s", err) + } + os.Setenv("SSH_AUTH_SOCK", SSHAgentSockPath) + cmds = append(cmds, exec.Command("ssh-agent", "-a", SSHAgentSockPath)) + cmds = append(cmds, exec.Command("ssh-add")) + return cmds +} + +func writeSSHPrivateKey() error { + privateKeyBase64 := os.Getenv(SSHPrivateKeyFromEnv) + if privateKeyBase64 == "" { + return fmt.Errorf("%s must be defined and contain the base64 encoded private key to use for ssh agent forwarding", SSHPrivateKeyFromEnv) + } + var err error + privateKey, err := base64.StdEncoding.DecodeString(privateKeyBase64) + if err != nil { + return fmt.Errorf("unable to base64 decode private key") + } + home, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("unable to determine home directory: %s", err) + } + if err := os.MkdirAll(filepath.Join(home, ".ssh"), 0700); err != nil { + return fmt.Errorf("unable to create .ssh directory: %s", err) + } + if err := os.WriteFile(filepath.Join(home, ".ssh", "id_rsa"), privateKey, 0400); err != nil { + return fmt.Errorf("unable to write ssh key: %s", err) + } + return nil +} + // trace writes each command to stdout with the command wrapped in an xml // tag so that it can be extracted and displayed in the logs. func trace(cmd *exec.Cmd) { From d70414892d9499eac975454b9626e1ad903cf7ef Mon Sep 17 00:00:00 2001 From: TP Honey Date: Tue, 17 Jan 2023 14:12:59 +0000 Subject: [PATCH 3/3] ssh agent support --- .gitignore | 2 ++ cmd/drone-docker/main.go | 10 +++---- daemon.go | 7 +++-- docker.go | 64 +++++++++++++--------------------------- docker_test.go | 20 +++++++++++++ 5 files changed, 52 insertions(+), 51 deletions(-) diff --git a/.gitignore b/.gitignore index 5077f86a..f1211b00 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ release coverage.out vendor +.vscode/ +Dockerfile diff --git a/cmd/drone-docker/main.go b/cmd/drone-docker/main.go index e6812c28..679b24da 100644 --- a/cmd/drone-docker/main.go +++ b/cmd/drone-docker/main.go @@ -274,10 +274,10 @@ func main() { Usage: "platform value to pass to docker", EnvVar: "PLUGIN_PLATFORM", }, - cli.StringSliceFlag{ - Name: "ssh-agent", - Usage: "mount ssh agent", - EnvVar: "PLUGIN_SSH_AGENT", + cli.StringFlag{ + Name: "ssh-agent-key", + Usage: "ssh agent key to use", + EnvVar: "PLUGIN_SSH_AGENT_KEY", }, } @@ -323,7 +323,7 @@ func run(c *cli.Context) error { AddHost: c.StringSlice("add-host"), Quiet: c.Bool("quiet"), Platform: c.String("platform"), - SSHAgent: c.StringSlice("ssh-agent"), + SSHAgentKey: c.String("ssh-agent-key"), }, Daemon: docker.Daemon{ Registry: c.String("docker.registry"), diff --git a/daemon.go b/daemon.go index 5976cd1d..df235e88 100644 --- a/daemon.go +++ b/daemon.go @@ -1,9 +1,10 @@ +//go:build !windows // +build !windows package docker import ( - "io/ioutil" + "io" "os" ) @@ -17,8 +18,8 @@ func (p Plugin) startDaemon() { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr } else { - cmd.Stdout = ioutil.Discard - cmd.Stderr = ioutil.Discard + cmd.Stdout = io.Discard + cmd.Stderr = io.Discard } go func() { trace(cmd) diff --git a/docker.go b/docker.go index 21f8a85d..828b9dc8 100644 --- a/docker.go +++ b/docker.go @@ -1,10 +1,7 @@ package docker import ( - "encoding/base64" "fmt" - "io/ioutil" - "log" "os" "os/exec" "path/filepath" @@ -13,11 +10,6 @@ import ( "time" ) -const ( - SSHAgentSockPath = "/tmp/drone-ssh-agent-sock" - SSHPrivateKeyFromEnv = "SSH_KEY" -) - type ( // Daemon defines Docker daemon parameters. Daemon struct { @@ -71,7 +63,8 @@ type ( AddHost []string // Docker build add-host Quiet bool // Docker build quiet Platform string // Docker build platform - SSHAgent []string // Docker build ssh + SSHAgentKey string // Docker build ssh agent key + SSHKeyPath string // Docker build ssh key path } // Plugin defines the Docker plugin parameters. @@ -153,7 +146,7 @@ func (p Plugin) Exec() error { os.MkdirAll(dockerHome, 0600) path := filepath.Join(dockerHome, "config.json") - err := ioutil.WriteFile(path, []byte(p.Login.Config), 0600) + err := os.WriteFile(path, []byte(p.Login.Config), 0600) if err != nil { return fmt.Errorf("Error writing config.json: %s", err) } @@ -189,10 +182,12 @@ func (p Plugin) Exec() error { } // setup for using ssh agent (https://docs.docker.com/develop/develop-images/build_enhancements/#using-ssh-to-access-private-data-in-builds) - - if len(p.Build.SSHAgent) > 0 { - fmt.Printf("ssh agent set to \"%s\"\n", p.Build.SSHAgent) - cmds = append(cmds, commandSSHAgentForwardingSetup(p.Build)...) + if p.Build.SSHAgentKey != "" { + var sshErr error + p.Build.SSHKeyPath, sshErr = writeSSHPrivateKey(p.Build.SSHAgentKey) + if sshErr != nil { + return sshErr + } } cmds = append(cmds, commandBuild(p.Build)) // docker build @@ -344,8 +339,8 @@ func commandBuild(build Build) *exec.Cmd { if build.Platform != "" { args = append(args, "--platform", build.Platform) } - for _, sshagent := range build.SSHAgent { - args = append(args, "--ssh", sshagent) + if build.SSHKeyPath != "" { + args = append(args, "--ssh", build.SSHKeyPath) } if build.AutoLabel { @@ -373,7 +368,7 @@ func commandBuild(build Build) *exec.Cmd { } // we need to enable buildkit, for secret support and ssh agent support - if build.Secret != "" || len(build.SecretEnvs) > 0 || len(build.SecretFiles) > 0 || len(build.SSHAgent) > 0 { + if build.Secret != "" || len(build.SecretEnvs) > 0 || len(build.SecretFiles) > 0 || build.SSHAgentKey != "" { os.Setenv("DOCKER_BUILDKIT", "1") } return exec.Command(dockerExe, args...) @@ -526,38 +521,21 @@ func commandRmi(tag string) *exec.Cmd { return exec.Command(dockerExe, "rmi", tag) } -func commandSSHAgentForwardingSetup(build Build) []*exec.Cmd { - cmds := make([]*exec.Cmd, 0) - if err := writeSSHPrivateKey(); err != nil { - log.Fatalf("unable to setup ssh agent forwarding: %s", err) - } - os.Setenv("SSH_AUTH_SOCK", SSHAgentSockPath) - cmds = append(cmds, exec.Command("ssh-agent", "-a", SSHAgentSockPath)) - cmds = append(cmds, exec.Command("ssh-add")) - return cmds -} - -func writeSSHPrivateKey() error { - privateKeyBase64 := os.Getenv(SSHPrivateKeyFromEnv) - if privateKeyBase64 == "" { - return fmt.Errorf("%s must be defined and contain the base64 encoded private key to use for ssh agent forwarding", SSHPrivateKeyFromEnv) - } - var err error - privateKey, err := base64.StdEncoding.DecodeString(privateKeyBase64) - if err != nil { - return fmt.Errorf("unable to base64 decode private key") - } +func writeSSHPrivateKey(key string) (path string, err error) { home, err := os.UserHomeDir() if err != nil { - return fmt.Errorf("unable to determine home directory: %s", err) + return "", fmt.Errorf("unable to determine home directory: %s", err) } if err := os.MkdirAll(filepath.Join(home, ".ssh"), 0700); err != nil { - return fmt.Errorf("unable to create .ssh directory: %s", err) + return "", fmt.Errorf("unable to create .ssh directory: %s", err) } - if err := os.WriteFile(filepath.Join(home, ".ssh", "id_rsa"), privateKey, 0400); err != nil { - return fmt.Errorf("unable to write ssh key: %s", err) + pathToKey := filepath.Join(home, ".ssh", "id_rsa") + if err := os.WriteFile(pathToKey, []byte(key), 0400); err != nil { + return "", fmt.Errorf("unable to write ssh key %s: %s", pathToKey, err) } - return nil + path = fmt.Sprintf("default=%s", pathToKey) + + return path, nil } // trace writes each command to stdout with the command wrapped in an xml diff --git a/docker_test.go b/docker_test.go index d10cc52f..cba894e9 100644 --- a/docker_test.go +++ b/docker_test.go @@ -135,6 +135,26 @@ func TestCommandBuild(t *testing.T) { "test/platform", ), }, + { + name: "ssh agent", + build: Build{ + Name: "plugins/drone-docker:latest", + Dockerfile: "Dockerfile", + Context: ".", + SSHKeyPath: "id_rsa=/root/.ssh/id_rsa", + }, + want: exec.Command( + dockerExe, + "build", + "--rm=true", + "-f", + "Dockerfile", + "-t", + "plugins/drone-docker:latest", + ".", + "--ssh id_rsa=/root/.ssh/id_rsa", + ), + }, } for _, tc := range tcs {