Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
packer
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kristopher Ruzic
packer
Commits
03850caf
Commit
03850caf
authored
Jul 02, 2015
by
Chris Bednarski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implemented timeout around the SSH handshake, including a unit test
parent
6ca48fa3
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
107 additions
and
11 deletions
+107
-11
communicator/ssh/communicator.go
communicator/ssh/communicator.go
+42
-4
communicator/ssh/communicator_test.go
communicator/ssh/communicator_test.go
+65
-7
No files found.
communicator/ssh/communicator.go
View file @
03850caf
...
...
@@ -5,9 +5,6 @@ import (
"bytes"
"errors"
"fmt"
"github.com/mitchellh/packer/packer"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"io"
"io/ioutil"
"log"
...
...
@@ -16,8 +13,15 @@ import (
"path/filepath"
"strconv"
"sync"
"time"
"github.com/mitchellh/packer/packer"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
)
var
ErrHandshakeTimeout
=
fmt
.
Errorf
(
"Timeout during SSH handshake"
)
type
comm
struct
{
client
*
ssh
.
Client
config
*
Config
...
...
@@ -40,6 +44,10 @@ type Config struct {
// DisableAgent, if true, will not forward the SSH agent.
DisableAgent
bool
// HandshakeTimeout limits the amount of time we'll wait to handshake before
// saying the connection failed.
HandshakeTimeout
time
.
Duration
}
// Creates a new packer.Communicator implementation over SSH. This takes
...
...
@@ -273,9 +281,39 @@ func (c *comm) reconnect() (err error) {
}
log
.
Printf
(
"handshaking with SSH"
)
sshConn
,
sshChan
,
req
,
err
:=
ssh
.
NewClientConn
(
c
.
conn
,
c
.
address
,
c
.
config
.
SSHConfig
)
// Default timeout to 1 minute if it wasn't specified (zero value). For
// when you need to handshake from low orbit.
var
duration
time
.
Duration
if
c
.
config
.
HandshakeTimeout
==
0
{
duration
=
1
*
time
.
Minute
}
else
{
duration
=
c
.
config
.
HandshakeTimeout
}
timeoutExceeded
:=
time
.
After
(
duration
)
connectionEstablished
:=
make
(
chan
bool
,
1
)
var
sshConn
ssh
.
Conn
var
sshChan
<-
chan
ssh
.
NewChannel
var
req
<-
chan
*
ssh
.
Request
go
func
()
{
sshConn
,
sshChan
,
req
,
err
=
ssh
.
NewClientConn
(
c
.
conn
,
c
.
address
,
c
.
config
.
SSHConfig
)
connectionEstablished
<-
true
}()
select
{
case
<-
connectionEstablished
:
// We don't need to do anything here. We just want select to block until
// we connect or timeout.
case
<-
timeoutExceeded
:
return
ErrHandshakeTimeout
}
if
err
!=
nil
{
log
.
Printf
(
"handshake error: %s"
,
err
)
return
}
log
.
Printf
(
"handshake complete!"
)
if
sshConn
!=
nil
{
...
...
communicator/ssh/communicator_test.go
View file @
03850caf
...
...
@@ -5,10 +5,12 @@ package ssh
import
(
"bytes"
"fmt"
"github.com/mitchellh/packer/packer"
"golang.org/x/crypto/ssh"
"net"
"testing"
"time"
"github.com/mitchellh/packer/packer"
"golang.org/x/crypto/ssh"
)
// private key for mock server
...
...
@@ -94,6 +96,28 @@ func newMockLineServer(t *testing.T) string {
return
l
.
Addr
()
.
String
()
}
func
newMockBrokenServer
(
t
*
testing
.
T
)
string
{
l
,
err
:=
net
.
Listen
(
"tcp"
,
"127.0.0.1:0"
)
if
err
!=
nil
{
t
.
Fatalf
(
"Unable tp listen for connection: %s"
,
err
)
}
go
func
()
{
defer
l
.
Close
()
c
,
err
:=
l
.
Accept
()
if
err
!=
nil
{
t
.
Errorf
(
"Unable to accept incoming connection: %s"
,
err
)
}
defer
c
.
Close
()
// This should block for a period of time longer than our timeout in
// the test case. That way we invoke a failure scenario.
time
.
Sleep
(
5
*
time
.
Second
)
t
.
Log
(
"Block on handshaking for SSH connection"
)
}()
return
l
.
Addr
()
.
String
()
}
func
TestCommIsCommunicator
(
t
*
testing
.
T
)
{
var
raw
interface
{}
raw
=
&
comm
{}
...
...
@@ -157,10 +181,44 @@ func TestStart(t *testing.T) {
t
.
Fatalf
(
"error connecting to SSH: %s"
,
err
)
}
var
cmd
packer
.
RemoteCmd
stdout
:=
new
(
bytes
.
Buffer
)
cmd
.
Command
=
"echo foo"
cmd
.
Stdout
=
stdout
cmd
:=
&
packer
.
RemoteCmd
{
Command
:
"echo foo"
,
Stdout
:
new
(
bytes
.
Buffer
),
}
client
.
Start
(
cmd
)
}
func
TestHandshakeTimeout
(
t
*
testing
.
T
)
{
clientConfig
:=
&
ssh
.
ClientConfig
{
User
:
"user"
,
Auth
:
[]
ssh
.
AuthMethod
{
ssh
.
Password
(
"pass"
),
},
}
client
.
Start
(
&
cmd
)
address
:=
newMockBrokenServer
(
t
)
conn
:=
func
()
(
net
.
Conn
,
error
)
{
conn
,
err
:=
net
.
Dial
(
"tcp"
,
address
)
if
err
!=
nil
{
t
.
Fatalf
(
"unable to dial to remote side: %s"
,
err
)
}
return
conn
,
err
}
config
:=
&
Config
{
Connection
:
conn
,
SSHConfig
:
clientConfig
,
HandshakeTimeout
:
50
*
time
.
Millisecond
,
}
_
,
err
:=
New
(
address
,
config
)
if
err
!=
ErrHandshakeTimeout
{
// Note: there's another error that can come back from this call:
// ssh: handshake failed: EOF
// This should appear in cases where the handshake fails because of
// malformed (or no) data sent back by the server, but should not happen
// in a timeout scenario.
t
.
Fatalf
(
"Expected handshake timeout, got: %s"
,
err
)
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment