Skip to content

Commit

Permalink
Enable SSH agent support on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
shanman190 committed Apr 16, 2024
1 parent 3f7e9db commit d093019
Show file tree
Hide file tree
Showing 15 changed files with 1,290 additions and 9 deletions.
8 changes: 4 additions & 4 deletions cmd/machine/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ import (
"github.com/loft-sh/devpod/pkg/client"
"github.com/loft-sh/devpod/pkg/config"
devssh "github.com/loft-sh/devpod/pkg/ssh"
devsshagent "github.com/loft-sh/devpod/pkg/ssh/agent"
"github.com/loft-sh/devpod/pkg/workspace"
"github.com/loft-sh/log"
"github.com/mattn/go-isatty"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh"
agent "golang.org/x/crypto/ssh/agent"
"golang.org/x/term"
)

Expand Down Expand Up @@ -121,14 +121,14 @@ func StartSSHSession(ctx context.Context, user, command string, agentForwarding
)

// request agent forwarding
authSock := os.Getenv("SSH_AUTH_SOCK")
authSock := devsshagent.GetSSHAuthSocket()
if agentForwarding && authSock != "" {
err = agent.ForwardToRemote(sshClient, authSock)
err = devsshagent.ForwardToRemote(sshClient, authSock)
if err != nil {
return errors.Errorf("forward agent: %v", err)
}

err = agent.RequestAgentForwarding(session)
err = devsshagent.RequestAgentForwarding(session)
if err != nil {
return errors.Errorf("request agent forwarding: %v", err)
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ require (
go.opentelemetry.io/proto/otlp v0.12.0 // indirect
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
)

require (
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1412,6 +1412,8 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
Expand Down
8 changes: 4 additions & 4 deletions pkg/devcontainer/sshtunnel/sshtunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import (
client2 "github.com/loft-sh/devpod/pkg/client"
config2 "github.com/loft-sh/devpod/pkg/devcontainer/config"
devssh "github.com/loft-sh/devpod/pkg/ssh"
devsshagent "github.com/loft-sh/devpod/pkg/ssh/agent"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
gosshagent "golang.org/x/crypto/ssh/agent"
)

type AgentInjectFunc func(context.Context, string, *os.File, *os.File, io.WriteCloser) error
Expand Down Expand Up @@ -105,13 +105,13 @@ func ExecuteCommand(

log.Debugf("SSH session created")

identityAgent := os.Getenv("SSH_AUTH_SOCK")
identityAgent := devsshagent.GetSSHAuthSocket()
if identityAgent != "" {
err = gosshagent.ForwardToRemote(sshClient, identityAgent)
err = devsshagent.ForwardToRemote(sshClient, identityAgent)
if err != nil {
errChan <- errors.Wrap(err, "forward agent")
}
err = gosshagent.RequestAgentForwarding(sess)
err = devsshagent.RequestAgentForwarding(sess)
if err != nil {
errChan <- errors.Wrap(err, "request agent forwarding failed")
}
Expand Down
27 changes: 27 additions & 0 deletions pkg/ssh/agent/agent_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//go:build !windows

package agent

import (
"os"

"golang.org/x/crypto/ssh"
gosshagent "golang.org/x/crypto/ssh/agent"
)

func GetSSHAuthSocket() string {
sshAuthSocket := os.Getenv("SSH_AUTH_SOCK")
if sshAuthSocket != "" {
return sshAuthSocket
}

return ""
}

func ForwardToRemote(client *ssh.Client, addr string) error {
return gosshagent.ForwardToRemote(client, addr)
}

func RequestAgentForwarding(session *ssh.Session) error {
return gosshagent.RequestAgentForwarding(session)
}
80 changes: 80 additions & 0 deletions pkg/ssh/agent/agent_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package agent

import (
"io"
"os"
"sync"

"github.com/pkg/errors"
"golang.org/x/crypto/ssh"
gosshagent "golang.org/x/crypto/ssh/agent"
"gopkg.in/natefinch/npipe.v2"
)

const (
channelType = "[email protected]"
defaultNamedPipe = "\\\\.\\pipe\\openssh-ssh-agent"
)

func GetSSHAuthSocket() string {
sshAuthSocket := os.Getenv("SSH_AUTH_SOCK")
if sshAuthSocket != "" {
return sshAuthSocket
}
if _, err := os.Stat(defaultNamedPipe); err == nil {
return defaultNamedPipe
}

return ""
}

func ForwardToRemote(client *ssh.Client, addr string) error {
channels := client.HandleChannelOpen(channelType)
if channels == nil {
return errors.New("agent: already have handler for " + channelType)
}
conn, err := npipe.Dial(addr)
if err != nil {
return err
}
conn.Close()

go func() {
for ch := range channels {
channel, reqs, err := ch.Accept()
if err != nil {
continue
}
go ssh.DiscardRequests(reqs)
go forwardNamedPipe(channel, addr)
}
}()
return nil
}

func RequestAgentForwarding(session *ssh.Session) error {
return gosshagent.RequestAgentForwarding(session)
}

func forwardNamedPipe(channel ssh.Channel, addr string) {
conn, err := npipe.Dial(addr)
if err != nil {
return
}

var wg sync.WaitGroup
wg.Add(2)
go func() {
io.Copy(conn, channel)
wg.Done()
}()
go func() {
io.Copy(channel, conn)
channel.CloseWrite()
wg.Done()
}()

wg.Wait()
conn.Close()
channel.Close()
}
3 changes: 2 additions & 1 deletion pkg/ssh/ssh_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import (
"time"

"github.com/loft-sh/devpod/pkg/command"
devsshagent "github.com/loft-sh/devpod/pkg/ssh/agent"
"github.com/loft-sh/log"
"github.com/mitchellh/go-homedir"
"golang.org/x/crypto/ssh"
)

func AddPrivateKeysToAgent(ctx context.Context, log log.Logger) error {
if os.Getenv("SSH_AUTH_SOCK") == "" {
if devsshagent.GetSSHAuthSocket() == "" {
return fmt.Errorf("ssh-agent is not started")
} else if !command.Exists("ssh-add") {
return fmt.Errorf("ssh-add couldn't be found")
Expand Down
22 changes: 22 additions & 0 deletions vendor/gopkg.in/natefinch/npipe.v2/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions vendor/gopkg.in/natefinch/npipe.v2/LICENSE.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit d093019

Please sign in to comment.