From 05bcf7252b9c38380af6beceb5c286b32c0aa127 Mon Sep 17 00:00:00 2001 From: Peter Galbavy Date: Tue, 1 Mar 2022 16:04:28 +0000 Subject: [PATCH] very early remote support ssh test code - just for reference for now --- cmd/geneos/list.go | 8 ++++ cmd/geneos/remote.go | 108 ++++++++++++++++++++++++++++++++++++++++++ cmd/geneos/ssh.go | 109 +++++++++++++++++++++++++++++++++++++++++++ go.mod | 3 ++ 4 files changed, 228 insertions(+) create mode 100644 cmd/geneos/remote.go create mode 100644 cmd/geneos/ssh.go diff --git a/cmd/geneos/list.go b/cmd/geneos/list.go index c871aeb..00bab39 100644 --- a/cmd/geneos/list.go +++ b/cmd/geneos/list.go @@ -67,6 +67,14 @@ func flagsList(command string, args []string) []string { } func commandLS(ct ComponentType, args []string, params []string) (err error) { + if ct == Remote { + // geneos ls remote [NAME] + if len(args) == 0 { + // list remotes + + } + } + switch { case listJSON: jsonEncoder = json.NewEncoder(log.Writer()) diff --git a/cmd/geneos/remote.go b/cmd/geneos/remote.go new file mode 100644 index 0000000..7a8923f --- /dev/null +++ b/cmd/geneos/remote.go @@ -0,0 +1,108 @@ +package main + +import "net/url" + +// remote support + +// "remote" is another component type, + +// e.g. +// geneos new remote X URL +// +// below is out of date + +// examples: +// +// geneos add remote name URL +// URL here may be ssh://user@host etc. +// URL can include path to ITRS_HOME / ITRSHome, e.g. +// ssh://user@server/home/geneos +// else it default to same as local +// +// non ssh schemes to follow +// ssh support for agents and private key files - no passwords +// known_hosts will be checked, no changes made, missing keys will +// result in error. user must add hosts before use (ssh-keyscan) +// +// geneos ls remote NAME +// XXX - geneos init remote NAME +// +// remote 'localhost' is always implied +// +// geneos ls remote +// ... list remote locations +// +// geneos start gateway [name]@remote +// +// XXX support gateway pairs for standby - how ? +// +// XXX remote netprobes, auto configure with gateway for SANs etc.? +// +// support existing geneos-utils installs on remote + +type Remotes struct { + Common + Home string `default:"{{join .Root \"remotes\" .Name}}"` + Hostname string + Port int `default:"22"` + Username string + ITRSHome string `default:"{{.Root}}"` +} + +func init() { + components[Remote] = ComponentFuncs{ + Instance: remoteInstance, + Command: nil, + Add: remoteAdd, + Clean: nil, + Reload: nil, + } +} + +func remoteInstance(name string) interface{} { + // Bootstrap + c := &Remotes{} + c.Root = RunningConfig.ITRSHome + c.Type = Remote.String() + c.Name = name + setDefaults(&c) + return c +} + +// +// 'geneos add remote NAME SSH-URL' +// +func remoteAdd(name string, username string, params []string) (c Instance, err error) { + if len(params) == 0 { + log.Fatalln("remote destination must be provided in the form of a URL") + } + + c = remoteInstance(name) + + u, err := url.Parse(params[0]) + if err != nil { + logDebug.Println(err) + return + } + + switch { + case u.Scheme == "ssh": + if u.Host == "" { + log.Fatalln("hostname must be provided") + } + setField(c, "Hostname", u.Host) + if u.Port() != "" { + setField(c, "Port", u.Port()) + } + if u.User.Username() != "" { + setField(c, "Username", u.User.Username()) + } + if u.Path != "" { + setField(c, "ITRSHome", u.Path) + } + return c, writeInstanceConfig(c) + default: + log.Fatalln("unsupport scheme (only ssh at the moment):", u.Scheme) + } + return +} diff --git a/cmd/geneos/ssh.go b/cmd/geneos/ssh.go new file mode 100644 index 0000000..640ae40 --- /dev/null +++ b/cmd/geneos/ssh.go @@ -0,0 +1,109 @@ +package main + +import ( + "bytes" + "fmt" + "net" + "os" + "path/filepath" + + "github.com/pkg/sftp" + "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" + "golang.org/x/crypto/ssh/knownhosts" +) + +var userSSHdir = ".ssh" + +var privateKeyFiles = []string{ + "id_rsa", + "id_ecdsa", + "id_ecdsa_sk", + "id_ed25519", + "id_ed25519_sk", + "id_dsa", +} + +// ssh utilities for remote connections + +func readSSHkeys(homedir string) (signers []ssh.Signer) { + for _, keyfile := range privateKeyFiles { + path := filepath.Join(homedir, ".ssh", keyfile) + key, err := os.ReadFile(path) + if err != nil { + logDebug.Println(err) + continue + } + signer, err := ssh.ParsePrivateKey(key) + if err != nil { + logDebug.Println(err) + continue + } + logDebug.Println("loaded private key from", path) + signers = append(signers, signer) + } + return +} + +func sshTest(username string, host string) { + socket := os.Getenv("SSH_AUTH_SOCK") + sshAgent, err := net.Dial("unix", socket) + if err != nil { + log.Fatalf("Failed to open SSH_AUTH_SOCK: %v", err) + } + + agentClient := agent.NewClient(sshAgent) + + homedir, err := os.UserHomeDir() + if err != nil { + log.Fatalln(err) + } + knownHostsFile := filepath.Join(homedir, ".ssh", "known_hosts") + logDebug.Println(knownHostsFile) + khcallback, err := knownhosts.New(knownHostsFile) + if err != nil { + log.Fatalln(err) + } + signers := readSSHkeys(homedir) + config := &ssh.ClientConfig{ + User: username, + Auth: []ssh.AuthMethod{ + ssh.PublicKeysCallback(agentClient.Signers), + ssh.PublicKeys(signers...), + }, + HostKeyCallback: khcallback, + } + conn, err := ssh.Dial("tcp", host, config) + if err != nil { + log.Fatal("unable to connect: ", err) + } + defer conn.Close() + + session, err := conn.NewSession() + if err != nil { + log.Fatalln(err) + } + defer session.Close() + + var b bytes.Buffer + session.Stdout = &b + if err := session.Run("ls -l"); err != nil { + log.Fatal("Failed to run: " + err.Error()) + } + fmt.Println(b.String()) + + client, err := sftp.NewClient(conn) + if err != nil { + log.Fatal(err) + } + defer client.Close() + + // walk a directory + w := client.Walk("/home/pi") + for w.Step() { + if w.Err() != nil { + continue + } + log.Println(w.Path()) + } +} diff --git a/go.mod b/go.mod index ed68a2c..ab815ec 100644 --- a/go.mod +++ b/go.mod @@ -16,9 +16,12 @@ retract ( require ( github.com/fsnotify/fsnotify v1.5.1 github.com/go-mail/mail/v2 v2.3.0 + github.com/pkg/sftp v1.13.4 + golang.org/x/crypto v0.0.0-20220214200702-86341886e292 ) require ( + github.com/kr/fs v0.1.0 // indirect golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/mail.v2 v2.3.1 // indirect