2017-10-29 09:45:21 +00:00
|
|
|
package hosts
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
2018-03-27 21:07:16 +00:00
|
|
|
"time"
|
2017-10-29 09:45:21 +00:00
|
|
|
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
|
|
)
|
|
|
|
|
2018-03-27 21:07:16 +00:00
|
|
|
const (
|
|
|
|
DockerDialerTimeout = 30
|
|
|
|
)
|
|
|
|
|
2017-12-16 03:38:15 +00:00
|
|
|
type DialerFactory func(h *Host) (func(network, address string) (net.Conn, error), error)
|
2017-12-11 18:28:08 +00:00
|
|
|
|
2017-12-16 03:38:15 +00:00
|
|
|
type dialer struct {
|
2018-03-06 00:52:43 +00:00
|
|
|
signer ssh.Signer
|
|
|
|
sshKeyString string
|
|
|
|
sshAddress string
|
|
|
|
sshPassphrase []byte
|
|
|
|
username string
|
|
|
|
netConn string
|
|
|
|
dockerSocket string
|
|
|
|
useSSHAgentAuth bool
|
2017-10-29 09:45:21 +00:00
|
|
|
}
|
|
|
|
|
2018-02-26 21:27:51 +00:00
|
|
|
func newDialer(h *Host, kind string) (*dialer, error) {
|
2017-12-16 03:38:15 +00:00
|
|
|
dialer := &dialer{
|
2018-03-06 00:52:43 +00:00
|
|
|
sshAddress: fmt.Sprintf("%s:%s", h.Address, h.Port),
|
|
|
|
username: h.User,
|
|
|
|
dockerSocket: h.DockerSocket,
|
|
|
|
sshKeyString: h.SSHKey,
|
|
|
|
netConn: "unix",
|
|
|
|
sshPassphrase: []byte(h.SavedKeyPhrase),
|
|
|
|
useSSHAgentAuth: h.SSHAgentAuth,
|
2017-12-11 18:28:08 +00:00
|
|
|
}
|
2017-10-29 09:45:21 +00:00
|
|
|
|
2018-02-26 21:27:51 +00:00
|
|
|
if dialer.sshKeyString == "" {
|
|
|
|
dialer.sshKeyString = privateKeyPath(h.SSHKeyPath)
|
2017-12-19 22:18:27 +00:00
|
|
|
}
|
2018-02-26 21:27:51 +00:00
|
|
|
|
|
|
|
switch kind {
|
|
|
|
case "network", "health":
|
|
|
|
dialer.netConn = "tcp"
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(dialer.dockerSocket) == 0 {
|
|
|
|
dialer.dockerSocket = "/var/run/docker.sock"
|
2017-12-19 22:18:27 +00:00
|
|
|
}
|
2018-02-26 21:27:51 +00:00
|
|
|
|
|
|
|
return dialer, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func SSHFactory(h *Host) (func(network, address string) (net.Conn, error), error) {
|
|
|
|
dialer, err := newDialer(h, "docker")
|
|
|
|
return dialer.Dial, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func LocalConnFactory(h *Host) (func(network, address string) (net.Conn, error), error) {
|
|
|
|
dialer, err := newDialer(h, "network")
|
|
|
|
return dialer.Dial, err
|
2017-12-19 22:18:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *dialer) DialDocker(network, addr string) (net.Conn, error) {
|
2018-02-26 21:27:51 +00:00
|
|
|
return d.Dial(network, addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *dialer) DialLocalConn(network, addr string) (net.Conn, error) {
|
|
|
|
return d.Dial(network, addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *dialer) Dial(network, addr string) (net.Conn, error) {
|
|
|
|
conn, err := d.getSSHTunnelConnection()
|
2017-10-29 09:45:21 +00:00
|
|
|
if err != nil {
|
2018-02-26 21:27:51 +00:00
|
|
|
return nil, fmt.Errorf("Failed to dial ssh using address [%s]: %v", d.sshAddress, err)
|
2017-10-29 09:45:21 +00:00
|
|
|
}
|
2018-02-26 21:27:51 +00:00
|
|
|
|
|
|
|
// Docker Socket....
|
|
|
|
if d.netConn == "unix" {
|
|
|
|
addr = d.dockerSocket
|
|
|
|
network = d.netConn
|
2017-10-29 09:45:21 +00:00
|
|
|
}
|
2018-02-26 21:27:51 +00:00
|
|
|
|
|
|
|
remote, err := conn.Dial(network, addr)
|
2017-10-29 09:45:21 +00:00
|
|
|
if err != nil {
|
2018-02-26 21:27:51 +00:00
|
|
|
return nil, fmt.Errorf("Failed to dial to %s: %v", addr, err)
|
2017-10-29 09:45:21 +00:00
|
|
|
}
|
|
|
|
return remote, err
|
|
|
|
}
|
2017-12-16 03:38:15 +00:00
|
|
|
|
2018-02-26 21:27:51 +00:00
|
|
|
func (d *dialer) getSSHTunnelConnection() (*ssh.Client, error) {
|
2018-03-06 00:52:43 +00:00
|
|
|
cfg, err := getSSHConfig(d.username, d.sshKeyString, d.sshPassphrase, d.useSSHAgentAuth)
|
2017-12-19 22:18:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Error configuring SSH: %v", err)
|
|
|
|
}
|
2018-02-26 21:27:51 +00:00
|
|
|
|
2017-12-19 22:18:27 +00:00
|
|
|
// Establish connection with SSH server
|
2018-02-26 21:27:51 +00:00
|
|
|
return ssh.Dial("tcp", d.sshAddress, cfg)
|
2017-12-19 22:18:27 +00:00
|
|
|
}
|
|
|
|
|
2017-12-16 03:38:15 +00:00
|
|
|
func (h *Host) newHTTPClient(dialerFactory DialerFactory) (*http.Client, error) {
|
2018-02-26 21:27:51 +00:00
|
|
|
factory := dialerFactory
|
|
|
|
if factory == nil {
|
2017-12-16 03:38:15 +00:00
|
|
|
factory = SSHFactory
|
|
|
|
}
|
|
|
|
|
|
|
|
dialer, err := factory(h)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-03-27 21:07:16 +00:00
|
|
|
dockerDialerTimeout := time.Second * DockerDialerTimeout
|
2017-12-16 03:38:15 +00:00
|
|
|
return &http.Client{
|
|
|
|
Transport: &http.Transport{
|
2018-03-27 21:07:16 +00:00
|
|
|
Dial: dialer,
|
|
|
|
TLSHandshakeTimeout: dockerDialerTimeout,
|
|
|
|
IdleConnTimeout: dockerDialerTimeout,
|
|
|
|
ResponseHeaderTimeout: dockerDialerTimeout,
|
2017-12-16 03:38:15 +00:00
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|