-
-
Notifications
You must be signed in to change notification settings - Fork 118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SSH library #72
Comments
I try to add the ssh and ssh/agent library - k4ml@9f64636 Now I'm stuck with this error on go build:-
I'm trying to make this anko script to work:-
|
please keep package namespaces as same as golang. |
Do you think this can be closed? |
@mattn any thoughts? |
I have achieved some decent mileage implementing Anko extensions using factory methods and registering them in the Anko func NewSSHCommandClient(host, userName string) *SSHCommandClient in the package _ = e.Define("sshcommand", sshcommand.NewSSHCommandClient) My script then looks like this: sshConn = sshcommand("my-host", "my-user").Connect()
printf("SSH Connected: %s\n", sshConn)
shell = sshConn.Shell()
output = shell.WriteCommandWithRead("pwd")
printf("PWD: %s\n", output) It's a bit rough but it works. I put more cleanliness into extensions I use more, like Redis/DynamoDB etc. SSHCommand Code: package sshcommand
import (
"bytes"
"fmt"
"github.com/elliotchance/sshtunnel"
"golang.org/x/crypto/ssh"
"io"
"log"
"net"
"pql/util"
"strings"
"time"
)
var (
modes = ssh.TerminalModes{
ssh.ECHO: 0, // Disable echoing
ssh.IGNCR: 1, // Ignore CR on input.
}
)
type SSHCommandClient struct {
host string
port int
userName string
userPassword string
privateKey string
sshConfig *ssh.ClientConfig
connection *ssh.Client
timeout time.Duration
}
func NewSSHCommandClient(host, userName string) *SSHCommandClient {
return &SSHCommandClient{
host: host,
port: 22,
userName: userName,
timeout: 10 * time.Second,
}
}
func (s *SSHCommandClient) String() string {
return fmt.Sprintf("%s@%s:%d?connected=%t", s.userName, s.host, s.port, s.connection != nil)
}
func (s *SSHCommandClient) Connect() *SSHCommandClient {
s.init()
if client, err := ssh.Dial("tcp", net.JoinHostPort(s.host, fmt.Sprintf("%d", s.port)), s.sshConfig); err != nil {
panic(err)
} else {
s.connection = client
}
return s
}
func (s *SSHCommandClient) init() {
// Authentication
config := &ssh.ClientConfig{
User: s.userName,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: s.timeout,
BannerCallback: func(message string) error {
log.Printf("Banner: %s\n", message)
return nil
},
}
if s.userPassword != "" {
config.Auth = []ssh.AuthMethod{
ssh.Password(s.userPassword),
}
} else if s.privateKey != "" {
bts := []byte(util.StringFromFile(s.privateKey))
if key, err := ssh.ParsePrivateKey(bts); err != nil {
panic(err)
} else {
config.Auth = []ssh.AuthMethod{
ssh.PublicKeys(key),
}
}
} else {
config.Auth = []ssh.AuthMethod{
sshtunnel.SSHAgent(),
}
}
s.sshConfig = config
}
func (s *SSHCommandClient) WithPort(p int) *SSHCommandClient {
s.port = p
return s
}
func (s *SSHCommandClient) WithTimeout(t string) *SSHCommandClient {
if to, err := time.ParseDuration(t); err == nil {
s.timeout = to
}
return s
}
func (s *SSHCommandClient) WithPrivateKey(key string) *SSHCommandClient {
s.privateKey = key
return s
}
func (s *SSHCommandClient) WithPassword(pass string) *SSHCommandClient {
s.privateKey = pass
return s
}
func (s *SSHCommandClient) Exec(command string) string {
// Create a session. It is one session per command.
if session, err := s.connection.NewSession(); err != nil {
panic(err)
} else {
defer session.Close()
if bs, err := session.CombinedOutput(command); err != nil {
panic(err)
} else {
return string(bs)
}
}
}
func (s *SSHCommandClient) Close() {
s.connection.Close()
}
type SSHShell struct {
client *SSHCommandClient
session *ssh.Session
stdIn io.WriteCloser
stdOutErr io.Reader
ps1 string
}
func (h *SSHShell) Prompt() string {
return h.ps1
}
func (h *SSHShell) Close() {
h.session.Close()
}
func (h *SSHShell) WriteCommandWithRead(cmd string) string {
h.WriteCommand(cmd)
return h.ReadOutput()
}
func (h *SSHShell) WriteCommand(cmd string) {
if _, err := h.stdIn.Write([]byte(cmd)); err != nil {
panic(err)
}
}
func (h *SSHShell) ReadOutput() string {
var b strings.Builder
for {
buff := make([]byte, 1024, 1024)
if n, err := h.stdOutErr.Read(buff); err != nil {
if n > 0 {
b.Write(buff[:n])
}
if err == io.EOF {
break
} else {
panic(err)
}
} else {
if n > 0 {
s := string(buff[:n])
b.WriteString(s)
if strings.Contains(strings.TrimSpace(s), h.ps1) {
break
}
}
}
}
return b.String()
}
func (s *SSHCommandClient) Shell() *SSHShell {
// Create a session. It is one session per command.
if session, err := s.connection.NewSession(); err != nil {
panic(err)
} else {
if writer, err := session.StdinPipe(); err != nil {
panic(err)
} else {
if stdOut, err := session.StdoutPipe(); err != nil {
panic(err)
} else {
if stdErr, err := session.StderrPipe(); err != nil {
panic(err)
} else {
if err := session.RequestPty("vt100", 80, 120, modes); err != nil {
panic(err)
} else {
if err := session.Shell(); err != nil {
panic(err)
} else {
return &SSHShell{
ps1: "$",
client: s,
session: session,
stdIn: writer,
stdOutErr: io.MultiReader(stdOut, stdErr),
}
}
}
}
}
}
}
}
func (s *SSHCommandClient) Start(command string) string {
// Create a session. It is one session per command.
if session, err := s.connection.NewSession(); err != nil {
panic(err)
} else {
defer session.Close()
var b bytes.Buffer
session.Stdout = &b
session.Stderr = &b
if err := session.Start(command); err != nil {
panic(err)
} else {
return string(b.String())
}
}
}
func remoteRun(user string, addr string, privateKey string, cmd string) (string, error) {
// privateKey could be read from a file, or retrieved from another storage
// source, such as the Secret Service / GNOME Keyring
key, err := ssh.ParsePrivateKey([]byte(privateKey))
if err != nil {
return "", err
}
// Authentication
config := &ssh.ClientConfig{
User: user,
// https://github.com/golang/go/issues/19767
// as clientConfig is non-permissive by default
// you can set ssh.InsercureIgnoreHostKey to allow any host
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Auth: []ssh.AuthMethod{
ssh.PublicKeys(key),
},
//alternatively, you could use a password
/*
Auth: []ssh.AuthMethod{
ssh.Password("PASSWORD"),
},
*/
}
// Connect
client, err := ssh.Dial("tcp", net.JoinHostPort(addr, "22"), config)
if err != nil {
return "", err
}
// Create a session. It is one session per command.
session, err := client.NewSession()
if err != nil {
return "", err
}
defer session.Close()
var b bytes.Buffer // import "bytes"
session.Stdout = &b // get output
// you can also pass what gets input to the stdin, allowing you to pipe
// content from client to server
// session.Stdin = bytes.NewBufferString("My input")
// Shell starts a login shell on the remote host. A Session only
// accepts one call to Run, Start, Shell, Output, or CombinedOutput.
// Start runs cmd on the remote host. Typically, the remote
// server passes cmd to the shell for interpretation.
// A Session only accepts one call to Run, Start or Shell.
// CombinedOutput runs cmd on the remote host and returns its combined
// standard output and standard error.
// Finally, run the command
bs, err := session.CombinedOutput(cmd)
return string(bs), err
}
|
I wonder if it possible to add ssh library. Currently making ssh connection and run remote command is too much boilerplate in Go - https://gist.github.com/erikdubbelboer/f62a109d8e8798a11eb89ed494491953.
If we can simplify this in anko, it can be a practical alternative for some scripting work in Go (anko).
The text was updated successfully, but these errors were encountered: