Skip to content

Instantly share code, notes, and snippets.

@Oppodelldog
Last active April 23, 2022 08:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Oppodelldog/bc7de662d34016da20502f0d1b7356bb to your computer and use it in GitHub Desktop.
Save Oppodelldog/bc7de662d34016da20502f0d1b7356bb to your computer and use it in GitHub Desktop.
ssh-connect
package main
import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"golang.org/x/crypto/ssh"
"io"
"io/ioutil"
"log"
"net"
"os"
"sync"
)
//
// this program opens a client ssh connection, executes some command automatically and leaves the user a terminal prompt for further interaction.
//
// notes:
//
// start up ssh server in docker
// docker run --rm --name ssh-dev-test-server -p 2222:2222 -e "PUBLIC_KEY=ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC0n684ejJQOAk2yoKdd8elVwBf4AF5G+nUBDHsCQcKQqPDMYY1kl8fIu6sbEURIKg2IkaZqEPnnW4pWCQWNAYMFDpjXj/0TyKbXXAZZB852aQ+17eHzThZI2dKYwTjXMCRF679lPY9KwO7jMjLXKvR5FYVUF7bGiWkPznhVntSTILDOuHzO9E5FM4dSDfb/1cOEQ4644oQHw0h/lrqmK28pX1Xd7j3cNP6P3+aPUb7ZoKTTRletIOwu18VcZshVBPUCZqPZWQf8nD1nDVJMWqPbbwM3S6c1YAPDEmhVmPWmnQCaDmsFz7OudjvurWvqa+BAoQ2GGEfBjXUW5szfy03bd+lDqPmUwQVGC1eEIDFbHFkQ3WZA+lxgT3swMeUKKPjFvzdyafjEchBBb2haQNjVNHAdp03+LrvS/WcFUO2t3smbE7SSJMJossg5rpLsGDYdzXvp3bQNCQYqmBrOPQSCeIUZ+n2rvi2Bzv4XwcZwX200OIX2jsKM8xRkMJej8U= testmann@testmann-PC" -e USER_NAME=testmann -e TZ=Europe/Berlin -l droxy lscr.io/linuxserver/openssh-server:amd64-latest
//
// Known issues:
// * Forwarding of control sequences may not work properly (write 'exit' to close the session)
func main() {
pk, _ := ioutil.ReadFile("./id_rsa")
signer, err := ssh.ParsePrivateKey(pk)
if err != nil {
panic(err)
}
config := &ssh.ClientConfig{
User: "testmann",
BannerCallback: func(message string) error {
fmt.Println(message)
return nil
},
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
fmt.Printf("accepting host key from '%s' (%v) key: '%v'\n", hostname, remote, hex.EncodeToString(key.Marshal()))
return nil
},
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
}
client, err := ssh.Dial("tcp", "localhost:2222", config)
if err != nil {
panic("Failed to dial: " + err.Error())
}
session, err := client.NewSession()
if err != nil {
panic("Failed to create session: " + err.Error())
}
defer session.Close()
session.Stdout = os.Stdout
session.Stderr = os.Stderr
var stdinReader = NewBufferedReader(os.Stdin)
session.Stdin = stdinReader
modes := ssh.TerminalModes{
ssh.ECHO: 0, // disable echoing
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
if err := session.RequestPty("xterm", 50, 120, modes); err != nil {
log.Fatal("request for pseudo terminal failed: ", err)
}
if err := session.Shell(); err != nil {
log.Fatal("failed to start shell: ", err)
}
stdinReader.Write("cd /tmp\n")
err = session.Wait()
if err != nil {
log.Fatal("session error: ", err)
}
}
func NewBufferedReader(r io.Reader) *BufferedReader {
br := &BufferedReader{
source: r,
bufLock: &sync.Mutex{},
buf: &bytes.Buffer{},
}
go br.readPump(256)
return br
}
// BufferedReader buffers data read from the given source reader and provides it to consumers.
// The buffer allows adding custom data to it that is not originated from the source reader.
type BufferedReader struct {
bufLock *sync.Mutex
source io.Reader
buf *bytes.Buffer
}
func (r *BufferedReader) Read(p []byte) (int, error) {
r.bufLock.Lock()
defer r.bufLock.Unlock()
if r.buf.Len() == 0 {
return 0, nil
}
return r.buf.Read(p)
}
func (r *BufferedReader) Write(data string) (int, error) {
r.bufLock.Lock()
defer r.bufLock.Unlock()
return r.buf.WriteString(data)
}
func (r *BufferedReader) readPump(readSize int) {
for {
p := make([]byte, readSize)
nRead, errRead := r.source.Read(p)
if errRead != nil && !errors.Is(errRead, io.EOF) {
panic(errRead)
}
if nRead == 0 {
continue
}
r.bufLock.Lock()
nWrite, errWrite := r.buf.Write(p[:nRead])
if errWrite != nil {
panic(errWrite)
}
r.bufLock.Unlock()
if nRead != nWrite {
panic("invalid amount of data written")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment