SOCKS5 proxy support

This commit is contained in:
Paul Kilar 2017-10-10 15:04:15 +01:00
parent c85cf0483a
commit d9b404fa00
3 changed files with 46 additions and 0 deletions

View File

@ -6,6 +6,7 @@ import (
"time" "time"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
"golang.org/x/net/proxy"
) )
// ConnectFunc is a convenience method for returning a function // ConnectFunc is a convenience method for returning a function
@ -27,6 +28,25 @@ func ConnectFunc(network, addr string) func() (net.Conn, error) {
} }
} }
// ConnectFunc is a convenience method for returning a function
// that connects to a host using SOCKS5 proxy
func ProxyConnectFunc(socksProxy string, socksAuth *proxy.Auth, network, addr string) func() (net.Conn, error) {
return func() (net.Conn, error) {
// create a socks5 dialer
dialer, err := proxy.SOCKS5("tcp", socksProxy, socksAuth, proxy.Direct)
if err != nil {
return nil, fmt.Errorf("Can't connect to the proxy: %s", err)
}
c, err := dialer.Dial(network, addr)
if err != nil {
return nil, err
}
return c, nil
}
}
// BastionConnectFunc is a convenience method for returning a function // BastionConnectFunc is a convenience method for returning a function
// that connects to a host over a bastion connection. // that connects to a host over a bastion connection.
func BastionConnectFunc( func BastionConnectFunc(

View File

@ -33,6 +33,10 @@ type Config struct {
SSHBastionPassword string `mapstructure:"ssh_bastion_password"` SSHBastionPassword string `mapstructure:"ssh_bastion_password"`
SSHBastionPrivateKey string `mapstructure:"ssh_bastion_private_key_file"` SSHBastionPrivateKey string `mapstructure:"ssh_bastion_private_key_file"`
SSHFileTransferMethod string `mapstructure:"ssh_file_transfer_method"` SSHFileTransferMethod string `mapstructure:"ssh_file_transfer_method"`
SSHProxyHost string `mapstructure:"ssh_proxy_host"`
SSHProxyPort int `mapstructure:"ssh_proxy_port"`
SSHProxyUsername string `mapstructure:"ssh_proxy_username"`
SSHProxyPassword string `mapstructure:"ssh_proxy_password"`
// WinRM // WinRM
WinRMUser string `mapstructure:"winrm_username"` WinRMUser string `mapstructure:"winrm_username"`
@ -141,6 +145,12 @@ func (c *Config) prepareSSH(ctx *interpolate.Context) []error {
} }
} }
if c.SSHProxyHost != "" {
if c.SSHProxyPort == 0 {
c.SSHProxyPort = 1080
}
}
if c.SSHFileTransferMethod == "" { if c.SSHFileTransferMethod == "" {
c.SSHFileTransferMethod = "scp" c.SSHFileTransferMethod = "scp"
} }

View File

@ -15,6 +15,7 @@ import (
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
gossh "golang.org/x/crypto/ssh" gossh "golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent" "golang.org/x/crypto/ssh/agent"
"golang.org/x/net/proxy"
) )
// StepConnectSSH is a step that only connects to SSH. // StepConnectSSH is a step that only connects to SSH.
@ -88,6 +89,8 @@ func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, cancel <-chan stru
// do this one before entering the retry loop. // do this one before entering the retry loop.
var bProto, bAddr string var bProto, bAddr string
var bConf *gossh.ClientConfig var bConf *gossh.ClientConfig
var pAddr string
var pAuth *proxy.Auth
if s.Config.SSHBastionHost != "" { if s.Config.SSHBastionHost != "" {
// The protocol is hardcoded for now, but may be configurable one day // The protocol is hardcoded for now, but may be configurable one day
bProto = "tcp" bProto = "tcp"
@ -101,6 +104,16 @@ func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, cancel <-chan stru
bConf = conf bConf = conf
} }
if s.Config.SSHProxyHost != "" {
pAddr = fmt.Sprintf("%s:%d", s.Config.SSHProxyHost, s.Config.SSHProxyPort)
if s.Config.SSHProxyUsername != "" {
pAuth = new(proxy.Auth)
pAuth.User = s.Config.SSHBastionUsername
pAuth.Password = s.Config.SSHBastionPassword
}
}
handshakeAttempts := 0 handshakeAttempts := 0
var comm packer.Communicator var comm packer.Communicator
@ -146,6 +159,9 @@ func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, cancel <-chan stru
// We're using a bastion host, so use the bastion connfunc // We're using a bastion host, so use the bastion connfunc
connFunc = ssh.BastionConnectFunc( connFunc = ssh.BastionConnectFunc(
bProto, bAddr, bConf, "tcp", address) bProto, bAddr, bConf, "tcp", address)
} else if pAddr != "" {
// Connect via SOCKS5 proxy
connFunc = ssh.ProxyConnectFunc(pAddr, pAuth, "tcp", address)
} else { } else {
// No bastion host, connect directly // No bastion host, connect directly
connFunc = ssh.ConnectFunc("tcp", address) connFunc = ssh.ConnectFunc("tcp", address)