99 lines
2.4 KiB
Go
99 lines
2.4 KiB
Go
package system
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
"golang.org/x/crypto/ssh/agent"
|
|
"golang.org/x/crypto/ssh/knownhosts"
|
|
)
|
|
|
|
func getKnownHosts() (ssh.HostKeyCallback, error) {
|
|
homedir, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("need homedir: %s", err)
|
|
}
|
|
return knownhosts.New(fmt.Sprintf("%s/.ssh/known_hosts", homedir))
|
|
}
|
|
|
|
func expandHome(path string) string {
|
|
if strings.HasPrefix(path, "~/") {
|
|
dirname, _ := os.UserHomeDir()
|
|
path = filepath.Join(dirname, path[2:])
|
|
}
|
|
return path
|
|
}
|
|
|
|
func NewSSHConfigFromUrl(u *url.URL) (string, int, *ssh.ClientConfig, error) {
|
|
port := 22
|
|
portStr := u.Port()
|
|
if portStr != "" {
|
|
port, _ = strconv.Atoi(portStr)
|
|
}
|
|
|
|
knownHostsCallback, err := getKnownHosts()
|
|
if err != nil {
|
|
return "", -1, nil, fmt.Errorf("couldn't load known hosts: %s", err)
|
|
}
|
|
|
|
username := u.User.Username()
|
|
password, _ := u.User.Password()
|
|
sshConfig := &ssh.ClientConfig{
|
|
User: username,
|
|
HostKeyCallback: knownHostsCallback,
|
|
}
|
|
|
|
extraArguments, err := url.ParseQuery(u.RawQuery)
|
|
if err != nil {
|
|
return "", -1, nil, err
|
|
}
|
|
|
|
sshKeyPath, hasSshKeyPath := extraArguments["ssh_key_path"]
|
|
agentValue, hasAgent := extraArguments["agent"]
|
|
|
|
if username != "" && hasSshKeyPath {
|
|
log.Printf("using ssh key: %s", sshKeyPath)
|
|
key, err := ioutil.ReadFile(expandHome(sshKeyPath[0]))
|
|
if err != nil {
|
|
return "", -1, nil, fmt.Errorf("read private key: %s", err)
|
|
}
|
|
signer, err := ssh.ParsePrivateKey(key)
|
|
if err != nil {
|
|
return "", -1, nil, fmt.Errorf("parse private key: %v", err)
|
|
}
|
|
sshConfig.Auth = []ssh.AuthMethod{
|
|
ssh.PublicKeys(signer),
|
|
}
|
|
} else if hasAgent && agentValue[0] == "yes" {
|
|
socket := os.Getenv("SSH_AUTH_SOCK")
|
|
if socket == "" {
|
|
return "", -1, nil, fmt.Errorf("requested ssh-agent but $SSH_AUTH_SOCK is unset")
|
|
}
|
|
conn, err := net.Dial("unix", socket)
|
|
if err != nil {
|
|
return "", -1, nil, fmt.Errorf("Failed to open SSH_AUTH_SOCK: %v", err)
|
|
}
|
|
agentClient := agent.NewClient(conn)
|
|
sshConfig.Auth = []ssh.AuthMethod{
|
|
ssh.PublicKeysCallback(agentClient.Signers),
|
|
}
|
|
} else if username != "" && password != "" {
|
|
log.Printf("using ssh password")
|
|
sshConfig.Auth = []ssh.AuthMethod{
|
|
ssh.Password(password),
|
|
}
|
|
} else {
|
|
return "", -1, nil, fmt.Errorf("no credential supplied, need user with password or ?ssh_key_path=<path> or ?agent=yes")
|
|
}
|
|
|
|
return u.Host, port, sshConfig, nil
|
|
}
|