Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enable ssh agent #369

Closed
wants to merge 17 commits into from
6 changes: 6 additions & 0 deletions cmd/drone-docker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,11 @@ func main() {
Usage: "secret key value pairs eg secret_name=/path/to/secret",
EnvVar: "PLUGIN_SECRETS_FROM_FILE",
},
cli.StringFlag{
Name: "ssh-agent",
Usage: "mount ssh agent",
EnvVar: "PLUGIN_SSH_AGENT",
},
cli.StringFlag{
Name: "drone-card-path",
Usage: "card path location to write to",
Expand Down Expand Up @@ -310,6 +315,7 @@ func run(c *cli.Context) error {
Secret: c.String("secret"),
SecretEnvs: c.StringSlice("secrets-from-env"),
SecretFiles: c.StringSlice("secrets-from-file"),
SSHAgent: c.String("ssh-agent"),
AddHost: c.StringSlice("add-host"),
Quiet: c.Bool("quiet"),
},
Expand Down
63 changes: 61 additions & 2 deletions docker.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package docker

import (
"encoding/base64"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
Expand All @@ -11,6 +13,11 @@ import (
"time"
)

const (
SSHAgentSockPath = "/tmp/drone-ssh-agent-sock"
SSHPrivateKeyFromEnv = "SSH_KEY"
)

type (
// Daemon defines Docker daemon parameters.
Daemon struct {
Expand Down Expand Up @@ -63,6 +70,7 @@ type (
SecretFiles []string // Docker build secrets with file as source
AddHost []string // Docker build add-host
Quiet bool // Docker build quiet
SSHAgent string // Docker build ssh
}

// Plugin defines the Docker plugin parameters.
Expand Down Expand Up @@ -105,6 +113,10 @@ type (

// Exec executes the plugin step
func (p Plugin) Exec() error {

fmt.Printf("exec build: %#v", p.Build)
fmt.Printf("exec env: %#v", os.Environ())

// start the Docker daemon server
if !p.Daemon.Disabled {
p.startDaemon()
Expand Down Expand Up @@ -178,6 +190,16 @@ 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 p.Build.SSHAgent != "" {
// TODO check in with one of the drone devs...this should not be necessary. I'm probably doing something
// wrong with the cli framework
p.Build.SSHAgent = strings.TrimSuffix(p.Build.SSHAgent, "]")
Copy link
Author

@bkk-bcd bkk-bcd Jul 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what I'm missing here @tphoney -- I'm getting my values for this flag wrapped in []

p.Build.SSHAgent = strings.TrimPrefix(p.Build.SSHAgent, "[")
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 {
Expand Down Expand Up @@ -324,6 +346,9 @@ func commandBuild(build Build) *exec.Cmd {
if build.Quiet {
args = append(args, "--quiet")
}
if build.SSHAgent != "" {
args = append(args, "--ssh", build.SSHAgent)
}

if build.AutoLabel {
labelSchema := []string{
Expand All @@ -349,8 +374,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 || build.SSHAgent != "" {
os.Setenv("DOCKER_BUILDKIT", "1")
}
return exec.Command(dockerExe, args...)
Expand Down Expand Up @@ -503,6 +528,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) {
Expand Down
20 changes: 20 additions & 0 deletions docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,26 @@ func TestCommandBuild(t *testing.T) {
".",
),
},
{
name: "ssh agent",
build: Build{
Name: "plugins/drone-docker:latest",
Dockerfile: "Dockerfile",
Context: ".",
SSHAgent: "default",
},
want: exec.Command(
dockerExe,
"build",
"--rm=true",
"-f",
"Dockerfile",
"-t",
"plugins/drone-docker:latest",
".",
"--ssh default",
),
},
}

for _, tc := range tcs {
Expand Down