aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing/transport/ssh
diff options
context:
space:
mode:
Diffstat (limited to 'plumbing/transport/ssh')
-rw-r--r--plumbing/transport/ssh/auth_method.go33
-rw-r--r--plumbing/transport/ssh/auth_method_test.go11
-rw-r--r--plumbing/transport/ssh/common.go8
-rw-r--r--plumbing/transport/ssh/upload_pack_test.go19
4 files changed, 57 insertions, 14 deletions
diff --git a/plumbing/transport/ssh/auth_method.go b/plumbing/transport/ssh/auth_method.go
index ad92ee1..a3e1ad1 100644
--- a/plumbing/transport/ssh/auth_method.go
+++ b/plumbing/transport/ssh/auth_method.go
@@ -1,6 +1,8 @@
package ssh
import (
+ "crypto/x509"
+ "encoding/pem"
"errors"
"fmt"
"io/ioutil"
@@ -9,9 +11,11 @@ import (
"os/user"
"path/filepath"
- "github.com/src-d/crypto/ssh/knownhosts"
+ "gopkg.in/src-d/go-git.v4/plumbing/transport"
+
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
+ "golang.org/x/crypto/ssh/knownhosts"
)
const DefaultUsername = "git"
@@ -22,6 +26,7 @@ var ErrEmptySSHAgentAddr = errors.New("SSH_AUTH_SOCK env variable is required")
// must implement. The clientConfig method returns the ssh client
// configuration needed to establish an ssh connection.
type AuthMethod interface {
+ transport.AuthMethod
clientConfig() *ssh.ClientConfig
hostKeyCallback() (ssh.HostKeyCallback, error)
}
@@ -112,9 +117,22 @@ type PublicKeys struct {
baseAuthMethod
}
-// NewPublicKeys returns a PublicKeys from a PEM encoded private key. It
-// supports RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys.
-func NewPublicKeys(user string, pemBytes []byte) (AuthMethod, error) {
+// NewPublicKeys returns a PublicKeys from a PEM encoded private key. An
+// encryption password should be given if the pemBytes contains a password
+// encrypted PEM block otherwise password should be empty. It supports RSA
+// (PKCS#1), DSA (OpenSSL), and ECDSA private keys.
+func NewPublicKeys(user string, pemBytes []byte, password string) (AuthMethod, error) {
+ block, _ := pem.Decode(pemBytes)
+ if x509.IsEncryptedPEMBlock(block) {
+ key, err := x509.DecryptPEMBlock(block, []byte(password))
+ if err != nil {
+ return nil, err
+ }
+
+ block = &pem.Block{Type: block.Type, Bytes: key}
+ pemBytes = pem.EncodeToMemory(block)
+ }
+
signer, err := ssh.ParsePrivateKey(pemBytes)
if err != nil {
return nil, err
@@ -124,14 +142,15 @@ func NewPublicKeys(user string, pemBytes []byte) (AuthMethod, error) {
}
// NewPublicKeysFromFile returns a PublicKeys from a file containing a PEM
-// encoded private key.
-func NewPublicKeysFromFile(user string, pemFile string) (AuthMethod, error) {
+// encoded private key. An encryption password should be given if the pemBytes
+// contains a password encrypted PEM block otherwise password should be empty.
+func NewPublicKeysFromFile(user, pemFile, password string) (AuthMethod, error) {
bytes, err := ioutil.ReadFile(pemFile)
if err != nil {
return nil, err
}
- return NewPublicKeys(user, bytes)
+ return NewPublicKeys(user, bytes, password)
}
func (a *PublicKeys) Name() string {
diff --git a/plumbing/transport/ssh/auth_method_test.go b/plumbing/transport/ssh/auth_method_test.go
index 43b34df..6f3d82f 100644
--- a/plumbing/transport/ssh/auth_method_test.go
+++ b/plumbing/transport/ssh/auth_method_test.go
@@ -109,7 +109,14 @@ func (s *SuiteCommon) TestNewSSHAgentAuth(c *C) {
}
func (*SuiteCommon) TestNewPublicKeys(c *C) {
- auth, err := NewPublicKeys("foo", testdata.PEMBytes["rsa"])
+ auth, err := NewPublicKeys("foo", testdata.PEMBytes["rsa"], "")
+ c.Assert(err, IsNil)
+ c.Assert(auth, NotNil)
+}
+
+func (*SuiteCommon) TestNewPublicKeysWithEncryptedPEM(c *C) {
+ f := testdata.PEMEncryptedKeys[0]
+ auth, err := NewPublicKeys("foo", f.PEMBytes, f.EncryptionKey)
c.Assert(err, IsNil)
c.Assert(auth, NotNil)
}
@@ -122,7 +129,7 @@ func (*SuiteCommon) TestNewPublicKeysFromFile(c *C) {
c.Assert(f.Close(), IsNil)
defer os.RemoveAll(f.Name())
- auth, err := NewPublicKeysFromFile("foo", f.Name())
+ auth, err := NewPublicKeysFromFile("foo", f.Name(), "")
c.Assert(err, IsNil)
c.Assert(auth, NotNil)
}
diff --git a/plumbing/transport/ssh/common.go b/plumbing/transport/ssh/common.go
index 9b484f9..7b44a91 100644
--- a/plumbing/transport/ssh/common.go
+++ b/plumbing/transport/ssh/common.go
@@ -14,6 +14,12 @@ import (
// DefaultClient is the default SSH client.
var DefaultClient = common.NewClient(&runner{})
+// DefaultAuthBuilder is the function used to create a default AuthMethod, when
+// the user doesn't provide any.
+var DefaultAuthBuilder = func(user string) (AuthMethod, error) {
+ return NewSSHAgentAuth(user)
+}
+
type runner struct{}
func (r *runner) Command(cmd string, ep transport.Endpoint, auth transport.AuthMethod) (common.Command, error) {
@@ -119,7 +125,7 @@ func (c *command) setAuthFromEndpoint() error {
}
var err error
- c.auth, err = NewSSHAgentAuth(u)
+ c.auth, err = DefaultAuthBuilder(u)
return err
}
diff --git a/plumbing/transport/ssh/upload_pack_test.go b/plumbing/transport/ssh/upload_pack_test.go
index 54d523a..cb9baa5 100644
--- a/plumbing/transport/ssh/upload_pack_test.go
+++ b/plumbing/transport/ssh/upload_pack_test.go
@@ -16,10 +16,7 @@ type UploadPackSuite struct {
var _ = Suite(&UploadPackSuite{})
func (s *UploadPackSuite) SetUpSuite(c *C) {
- if os.Getenv("SSH_AUTH_SOCK") == "" {
- c.Skip("SSH_AUTH_SOCK is not set")
- }
-
+ s.setAuthBuilder(c)
s.UploadPackSuite.Client = DefaultClient
ep, err := transport.NewEndpoint("git@github.com:git-fixtures/basic.git")
@@ -34,3 +31,17 @@ func (s *UploadPackSuite) SetUpSuite(c *C) {
c.Assert(err, IsNil)
s.UploadPackSuite.NonExistentEndpoint = ep
}
+
+func (s *UploadPackSuite) setAuthBuilder(c *C) {
+ privateKey := os.Getenv("SSH_TEST_PRIVATE_KEY")
+ if privateKey != "" {
+ DefaultAuthBuilder = func(user string) (AuthMethod, error) {
+ return NewPublicKeysFromFile(user, privateKey, "")
+ }
+ }
+
+ if privateKey == "" && os.Getenv("SSH_AUTH_SOCK") == "" {
+ c.Skip("SSH_AUTH_SOCK or SSH_TEST_PRIVATE_KEY are required")
+ return
+ }
+}