Skip to content

Commit

Permalink
add ssh subcommand
Browse files Browse the repository at this point in the history
Signed-off-by: Pedro Castillo <[email protected]>
  • Loading branch information
peterctl committed Feb 26, 2019
1 parent e259f03 commit 2dd8dc8
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 205 deletions.
108 changes: 108 additions & 0 deletions engines/docker/ssh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package docker

import (
"io/ioutil"
"os"
"os/signal"
"syscall"

"golang.org/x/crypto/ssh"

"github.com/govm-project/govm/internal"
"github.com/govm-project/govm/pkg/homedir"
"github.com/govm-project/govm/pkg/termutil"
)

func (e *Engine) SSHVM(namespace, id, user, key string, term *termutil.Terminal) error {
container, err := e.docker.Inspect(id)
if err != nil {
fullName := internal.GenerateContainerName(namespace, id)
container, err = e.docker.Inspect(fullName)
if err != nil {
return err
}
}

ip := container.NetworkSettings.IPAddress
keyPath := homedir.ExpandPath(key)

privateKey, err := ioutil.ReadFile(keyPath)
if err != nil {
return err
}

signer, err := ssh.ParsePrivateKey(privateKey)
if err != nil {
return err
}

config := ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
config.SetDefaults()

conn, err := ssh.Dial("tcp", ip+":22", &config)
if err != nil {
return err
}
defer conn.Close()

sess, err := conn.NewSession()
if err != nil {
return err
}
defer sess.Close()

sess.Stdin = term.In()
sess.Stdout = term.Out()
sess.Stderr = term.Err()

sz, err := term.GetWinsize()
if err != nil {
return err
}

err = term.MakeRaw()
if err != nil {
return err
}
defer term.Restore()

err = sess.RequestPty(os.Getenv("TERM"), int(sz.Height), int(sz.Width), nil)
if err != nil {
return err
}

err = sess.Shell()
if err != nil {
return err
}

// If our terminal window changes, signal the ssh connection
stopch := make(chan struct{})
defer close(stopch)
go func() {
sigch := make(chan os.Signal)
signal.Notify(sigch, syscall.SIGWINCH)
defer signal.Stop(sigch)
defer close(sigch)
outer:
for {
select {
case <-sigch:
sz, err := term.GetWinsize()
if err == nil {
sess.WindowChange(int(sz.Height), int(sz.Width))
}
case <-stopch:
break outer
}
}
}()

return sess.Wait()
}
12 changes: 7 additions & 5 deletions engines/engine.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package main
package engines

import (
"github.com/govm-project/govm/pkg/termutil"
"github.com/govm-project/govm/vm"
)

// VMEngine stands as an abstraction for VMs management engines
type VMEngine interface {
Create(spec vm.Instance) (string, error)
Start(namespace, id string) error
Delete(namespace, id string) error
List(namespace string, all bool) ([]vm.Instance, error)
CreateVM(spec vm.Instance) (string, error)
StartVM(namespace, id string) error
DeleteVM(namespace, id string) error
SSHVM(namespace, id, user, key string, term *termutil.Terminal) error
ListVM(namespace string, all bool) ([]vm.Instance, error)
}
1 change: 1 addition & 0 deletions pkg/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func New() (*cli.App, error) {
&removeCommand,
&startCommand,
&composeCommand,
&sshCommand,
},
}, nil
}
107 changes: 0 additions & 107 deletions pkg/cli/connect.go

This file was deleted.

127 changes: 34 additions & 93 deletions pkg/cli/ssh.go
Original file line number Diff line number Diff line change
@@ -1,104 +1,45 @@
package cli

import (
"bufio"
"fmt"
"io/ioutil"
"log"
"os"

"golang.org/x/crypto/ssh"
"github.com/govm-project/govm/engines/docker"
"github.com/govm-project/govm/pkg/termutil"
cli "gopkg.in/urfave/cli.v2"
)

type password string

func (p password) Password(user string) (password string, err error) {
return string(p), nil
}

// TODO: Reduce cyclomatic complexity
func getNewSSHConn(username, hostname, key string) { // nolint: gocyclo
//var hostKey ssh.PublicKey

privateKeyBytes, err := ioutil.ReadFile(key)
if err != nil {
log.Fatalf("Error on reading private key file: %v", err)
}

// Create the Signer for this private key.
signer, err := ssh.ParsePrivateKey(privateKeyBytes)
if err != nil {
log.Fatalf("unable to parse private key: %v", err)
}

// Create client config
config := &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
//ssh.Password("password"),
ssh.PublicKeys(signer),
var sshCommand = cli.Command{
Name: "ssh",
Usage: "ssh into a running VM",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "user",
Aliases: []string{"u"},
Usage: "login as this username",
},
//HostKeyCallback: ssh.FixedHostKey(hostKey),
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
config.SetDefaults()
// Connect to ssh server
conn, err := ssh.Dial("tcp", hostname+":22", config)
if err != nil {
log.Fatal("unable to connect: ", err)
}
defer func() {
err := conn.Close()
// TODO: Change to warning when log package is changed
log.Println(err)
}()

// Create a session
session, err := conn.NewSession()
if err != nil {
log.Fatal("unable to create session: ", err)
}
defer func() {
err := session.Close()
// TODO: Change to warning when log package is changed
log.Println(err)
}()

// Set IO
session.Stdout = os.Stdout
session.Stderr = os.Stderr
in, err := session.StdinPipe()
if err != nil {
log.Fatal(err)
}

// Set up terminal modes
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
}

// Request pseudo terminal
if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
log.Fatalf("request for pseudo terminal failed: %s", err)
}

// Start remote shell
if err := session.Shell(); err != nil {
log.Fatalf("failed to start shell: %s", err)
}

// Accepting commands
for {
reader := bufio.NewReader(os.Stdin)
str, err := reader.ReadString('\n')
if err != nil {
log.Println("Error reading command")
&cli.StringFlag{
Name: "key",
Aliases: []string{"k"},
Usage: "ssh private key file",
Value: "~/.ssh/id_rsa",
},
},
Action: func(c *cli.Context) error {
if c.Args().Len() != 1 {
return fmt.Errorf("VM name required")
}
_, err = fmt.Fprint(in, str)
if err != nil {
log.Println("Error reading command")
name := c.Args().First()
namespace := c.String("namespace")
user := c.String("user")
if user == "" {
return fmt.Errorf("--user argument required")
}
}
key := c.String("key")
term := termutil.StdTerminal()

engine := docker.Engine{}
engine.Init()

return engine.SSHVM(namespace, name, user, key, term)
},
}
8 changes: 8 additions & 0 deletions pkg/types/network.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package types

// Network represents a network for VMs
type Network struct {
ID string `yaml:"id" json:"id"`
Subnet string `yaml:"subnet" json:"subnet"`
DNS []string `yaml:"dns" json:"dns"`
}
Loading

0 comments on commit 2dd8dc8

Please sign in to comment.