Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions communicator/step_connect_ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ import (
// In general, you should use StepConnect.
type StepConnectSSH struct {
// All the fields below are documented on StepConnect
Config *Config
Host func(multistep.StateBag) (string, error)
SSHConfig func(multistep.StateBag) (*gossh.ClientConfig, error)
SSHPort func(multistep.StateBag) (int, error)
Config *Config
Host func(multistep.StateBag) (string, error)
SSHConfig func(multistep.StateBag) (*gossh.ClientConfig, error)
SSHConfigOtp func(multistep.StateBag) (*gossh.ClientConfig, error)
SSHPort func(multistep.StateBag) (int, error)
}

func (s *StepConnectSSH) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
Expand Down Expand Up @@ -217,6 +218,13 @@ func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, ctx context.Contex
Timeout: s.Config.SSHReadWriteTimeout,
Tunnels: tunnels,
}
if s.SSHConfigOtp != nil {
// Create dynamic SSH config function
sshConfigFunc := func() (*gossh.ClientConfig, error) {
return s.SSHConfigOtp(state)
}
config.SSHConfigOtp = sshConfigFunc
}

log.Printf("[INFO] Attempting SSH connection to %s...", address)
comm, err = ssh.New(address, config)
Expand Down
22 changes: 20 additions & 2 deletions sdk-internals/communicator/ssh/communicator.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,13 @@ type TunnelSpec struct {

// Config is the structure used to configure the SSH communicator.
type Config struct {
// The configuration of the Go SSH connection
// The configuration of the Go SSH connection (static fallback)
SSHConfig *ssh.ClientConfig

// SSHConfigOtp returns a new SSH config. This is called for each new connection
// to allow for dynamic credential generation (like one-time passwords)
SSHConfigOtp func() (*ssh.ClientConfig, error)

// Connection returns a new connection. The current connection
// in use will be closed as part of the Close method, or in the
// case an error occurs.
Expand Down Expand Up @@ -327,6 +331,20 @@ func (c *comm) reconnect() (err error) {

log.Printf("[DEBUG] handshaking with SSH")

// Get fresh SSH config if available, otherwise use static config
var sshConfig *ssh.ClientConfig
if c.config.SSHConfigOtp != nil {
log.Printf("[DEBUG] Getting fresh SSH config for connection")
sshConfig, err = c.config.SSHConfigOtp()
if err != nil {
log.Printf("[ERROR] Failed to get fresh SSH config: %s", err)
return err
}
} else {
log.Printf("[DEBUG] Using static SSH config")
sshConfig = 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
Expand All @@ -343,7 +361,7 @@ func (c *comm) reconnect() (err error) {
var req <-chan *ssh.Request

go func() {
sshConn, sshChan, req, err = ssh.NewClientConn(c.conn, c.address, c.config.SSHConfig)
sshConn, sshChan, req, err = ssh.NewClientConn(c.conn, c.address, sshConfig)
close(connectionEstablished)
}()

Expand Down