diff --git a/docker.go b/docker.go index cf1cea6..21f8a85 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) {