2017-12-11 18:28:08 +00:00
package hosts
import (
"context"
"fmt"
"io/ioutil"
2020-07-21 20:35:36 +00:00
"net"
2017-12-11 18:28:08 +00:00
"os"
"path/filepath"
"github.com/docker/docker/client"
"github.com/rancher/rke/docker"
2018-01-09 22:10:56 +00:00
"github.com/rancher/rke/log"
2020-07-21 20:35:36 +00:00
"github.com/rancher/rke/metadata"
2018-10-04 08:54:04 +00:00
"github.com/rancher/rke/util"
2017-12-11 18:28:08 +00:00
"github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
2018-02-26 21:27:51 +00:00
"golang.org/x/crypto/ssh/agent"
2017-12-11 18:28:08 +00:00
)
2018-10-04 08:54:04 +00:00
func ( h * Host ) TunnelUp ( ctx context . Context , dialerFactory DialerFactory , clusterPrefixPath string , clusterVersion string ) error {
2017-12-11 18:28:08 +00:00
if h . DClient != nil {
return nil
}
2018-01-09 22:10:56 +00:00
log . Infof ( ctx , "[dialer] Setup tunnel for host [%s]" , h . Address )
2017-12-16 03:38:15 +00:00
httpClient , err := h . newHTTPClient ( dialerFactory )
2017-12-11 18:28:08 +00:00
if err != nil {
2017-12-16 03:38:15 +00:00
return fmt . Errorf ( "Can't establish dialer connection: %v" , err )
2017-12-11 18:28:08 +00:00
}
// set Docker client
logrus . Debugf ( "Connecting to Docker API for host [%s]" , h . Address )
2019-08-19 22:53:51 +00:00
h . DClient , err = client . NewClientWithOpts (
2021-05-27 18:20:11 +00:00
client . WithAPIVersionNegotiation ( ) ,
2019-08-19 22:53:51 +00:00
client . WithHTTPClient ( httpClient ) )
2017-12-11 18:28:08 +00:00
if err != nil {
return fmt . Errorf ( "Can't initiate NewClient: %v" , err )
}
2018-10-04 08:54:04 +00:00
if err := checkDockerVersion ( ctx , h , clusterVersion ) ; err != nil {
2018-06-25 22:50:45 +00:00
return err
}
2020-07-21 20:35:36 +00:00
h . SetPrefixPath ( clusterPrefixPath )
2018-06-25 22:50:45 +00:00
return nil
2017-12-22 01:01:53 +00:00
}
2018-10-04 08:54:04 +00:00
func ( h * Host ) TunnelUpLocal ( ctx context . Context , clusterVersion string ) error {
2017-12-22 01:01:53 +00:00
var err error
if h . DClient != nil {
return nil
}
// set Docker client
logrus . Debugf ( "Connecting to Docker API for host [%s]" , h . Address )
h . DClient , err = client . NewEnvClient ( )
if err != nil {
return fmt . Errorf ( "Can't initiate NewClient: %v" , err )
}
2018-10-04 08:54:04 +00:00
return checkDockerVersion ( ctx , h , clusterVersion )
2017-12-22 01:01:53 +00:00
}
2018-10-04 08:54:04 +00:00
func checkDockerVersion ( ctx context . Context , h * Host , clusterVersion string ) error {
2018-01-09 22:10:56 +00:00
info , err := h . DClient . Info ( ctx )
2017-12-11 18:28:08 +00:00
if err != nil {
return fmt . Errorf ( "Can't retrieve Docker Info: %v" , err )
}
2020-03-03 23:07:24 +00:00
logrus . Debugf ( "Docker Info found for host [%s]: %#v" , h . Address , info )
2019-09-04 10:05:38 +00:00
h . DockerInfo = info
2019-07-25 11:13:24 +00:00
if h . IgnoreDockerVersion {
return nil
}
2018-10-04 08:54:04 +00:00
K8sSemVer , err := util . StrToSemVer ( clusterVersion )
if err != nil {
return fmt . Errorf ( "Error while parsing cluster version [%s]: %v" , clusterVersion , err )
}
K8sVersion := fmt . Sprintf ( "%d.%d" , K8sSemVer . Major , K8sSemVer . Minor )
2017-12-11 18:28:08 +00:00
isvalid , err := docker . IsSupportedDockerVersion ( info , K8sVersion )
if err != nil {
return fmt . Errorf ( "Error while determining supported Docker version [%s]: %v" , info . ServerVersion , err )
}
2019-07-25 11:13:24 +00:00
if ! isvalid {
2020-03-03 23:07:24 +00:00
return fmt . Errorf ( "Unsupported Docker version found [%s] on host [%s], supported versions are %v" , info . ServerVersion , h . Address , metadata . K8sVersionToDockerVersions [ K8sVersion ] )
2017-12-11 18:28:08 +00:00
}
return nil
}
func parsePrivateKey ( keyBuff string ) ( ssh . Signer , error ) {
return ssh . ParsePrivateKey ( [ ] byte ( keyBuff ) )
}
2019-01-28 13:54:00 +00:00
func getSSHConfig ( username , sshPrivateKeyString string , sshCertificateString string , useAgentAuth bool ) ( * ssh . ClientConfig , error ) {
2018-02-26 21:27:51 +00:00
config := & ssh . ClientConfig {
User : username ,
2017-12-11 18:28:08 +00:00
HostKeyCallback : ssh . InsecureIgnoreHostKey ( ) ,
}
2018-03-06 00:52:43 +00:00
// Kind of a double check now.
if useAgentAuth {
if sshAgentSock := os . Getenv ( "SSH_AUTH_SOCK" ) ; sshAgentSock != "" {
sshAgent , err := net . Dial ( "unix" , sshAgentSock )
if err != nil {
return config , fmt . Errorf ( "Cannot connect to SSH Auth socket %q: %s" , sshAgentSock , err )
}
2017-12-11 18:28:08 +00:00
2018-03-06 00:52:43 +00:00
config . Auth = append ( config . Auth , ssh . PublicKeysCallback ( agent . NewClient ( sshAgent ) . Signers ) )
2018-02-26 21:27:51 +00:00
2018-03-06 00:52:43 +00:00
logrus . Debugf ( "using %q SSH_AUTH_SOCK" , sshAgentSock )
return config , nil
}
2017-12-11 18:28:08 +00:00
}
2018-02-26 21:27:51 +00:00
2018-05-08 22:30:50 +00:00
signer , err := parsePrivateKey ( sshPrivateKeyString )
2018-02-26 21:27:51 +00:00
if err != nil {
return config , err
2017-12-11 18:28:08 +00:00
}
2019-01-28 13:54:00 +00:00
if len ( sshCertificateString ) > 0 {
key , _ , _ , _ , err := ssh . ParseAuthorizedKey ( [ ] byte ( sshCertificateString ) )
if err != nil {
return config , fmt . Errorf ( "Unable to parse SSH certificate: %v" , err )
}
if _ , ok := key . ( * ssh . Certificate ) ; ! ok {
return config , fmt . Errorf ( "Unable to cast public key to SSH Certificate" )
}
signer , err = ssh . NewCertSigner ( key . ( * ssh . Certificate ) , signer )
if err != nil {
return config , err
}
}
2018-02-26 21:27:51 +00:00
config . Auth = append ( config . Auth , ssh . PublicKeys ( signer ) )
2017-12-11 18:28:08 +00:00
2018-02-26 21:27:51 +00:00
return config , nil
}
2018-01-09 19:50:10 +00:00
2018-06-25 19:01:02 +00:00
func privateKeyPath ( sshKeyPath string ) ( string , error ) {
2017-12-11 18:28:08 +00:00
if sshKeyPath [ : 2 ] == "~/" {
2018-05-23 09:34:57 +00:00
sshKeyPath = filepath . Join ( userHome ( ) , sshKeyPath [ 2 : ] )
2017-12-11 18:28:08 +00:00
}
2018-06-25 19:01:02 +00:00
buff , err := ioutil . ReadFile ( sshKeyPath )
if err != nil {
return "" , fmt . Errorf ( "Error while reading SSH key file: %v" , err )
}
return string ( buff ) , nil
2017-12-11 18:28:08 +00:00
}
2018-05-23 09:34:57 +00:00
2019-01-28 13:54:00 +00:00
func certificatePath ( sshCertPath string ) ( string , error ) {
if sshCertPath [ : 2 ] == "~/" {
sshCertPath = filepath . Join ( userHome ( ) , sshCertPath [ 2 : ] )
}
buff , err := ioutil . ReadFile ( sshCertPath )
if err != nil {
return "" , fmt . Errorf ( "Error while reading SSH certificate file: %v" , err )
}
return string ( buff ) , nil
}
2018-05-23 09:34:57 +00:00
func userHome ( ) string {
if home := os . Getenv ( "HOME" ) ; home != "" {
return home
}
homeDrive := os . Getenv ( "HOMEDRIVE" )
homePath := os . Getenv ( "HOMEPATH" )
if homeDrive != "" && homePath != "" {
return homeDrive + homePath
}
return os . Getenv ( "USERPROFILE" )
}