Add internal SSH server as a replacement for ctl.

This commit is contained in:
Mikkel Krautz 2011-11-09 18:10:55 +01:00
parent 1c5325cba1
commit 244027d41b
5 changed files with 221 additions and 41 deletions

View file

@ -54,9 +54,8 @@ GOFILES = \
freeze.go \
gencert.go \
register.go \
ctlrpc.go \
ctl.go \
args.go
ssh.go \
args.go \
ifeq ($(SQLITE),1)
GOFILES += murmurdb.go

20
args.go
View file

@ -11,8 +11,7 @@ import (
type args struct {
ShowHelp bool
DataDir string
CtlNet string
CtlAddr string
SshAddr string
RegenKeys bool
SQLiteDB string
CleanUp bool
@ -26,20 +25,6 @@ func defaultDataDir() string {
return filepath.Join(os.Getenv("HOME"), dirname)
}
func defaultCtlNet() string {
if runtime.GOOS == "windows" {
return "tcp"
}
return "unix"
}
func defaultCtlAddr() string {
if runtime.GOOS == "windows" {
return "localhost:5454"
}
return filepath.Join(defaultDataDir(), ".ctl")
}
func Usage() {
fmt.Fprintf(os.Stderr, "usage: grumble [options]\n")
fmt.Fprintf(os.Stderr, "remote control: grumble [options] ctl [ctlopts]\n")
@ -51,8 +36,7 @@ var Args args
func init() {
flag.BoolVar(&Args.ShowHelp, "help", false, "Show this help")
flag.StringVar(&Args.DataDir, "datadir", defaultDataDir(), "Directory to use for server storage")
flag.StringVar(&Args.CtlNet, "ctlnet", defaultCtlNet(), "Network to use for ctl socket")
flag.StringVar(&Args.CtlAddr, "ctladdr", defaultCtlAddr(), "Address to use for ctl socket")
flag.StringVar(&Args.SshAddr, "ssh", "localhost:46545", "Address to use for SSH admin prompt")
flag.BoolVar(&Args.RegenKeys, "regenkeys", false, "Force Grumble to regenerate its global RSA keypair and certificate")
// SQLite related

View file

@ -9,8 +9,6 @@ import (
"fmt"
"grumble/blobstore"
"log"
"net"
"net/rpc"
"os"
"path/filepath"
"regexp"
@ -27,13 +25,6 @@ func main() {
return
}
for i, str := range os.Args {
if str == "ctl" {
GrumbleCtl(os.Args[i+1:])
return
}
}
log.SetPrefix("[G] ")
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
log.Printf("Grumble")
@ -167,17 +158,7 @@ func main() {
go s.ListenAndMurmur()
}
if Args.CtlNet == "unix" {
os.Remove(Args.CtlAddr)
}
lis, err := net.Listen(Args.CtlNet, Args.CtlAddr)
if err != nil {
log.Panicf("Unable to listen on ctl socket: %v", err)
}
ctl := &ControlRPC{}
rpc.RegisterName("ctl", ctl)
go rpc.Accept(lis)
go RunSSH()
if len(servers) > 0 {
go SignalHandler()

View file

@ -48,6 +48,11 @@ const (
StateClientDead
)
type KeyValuePair struct {
Key string
Value string
}
// A Murmur server instance
type Server struct {
Id int64

211
ssh.go Normal file
View file

@ -0,0 +1,211 @@
package main
import (
"bytes"
"crypto/rand"
"errors"
"exp/ssh"
"fmt"
"io"
"io/ioutil"
"log"
"path/filepath"
"strings"
)
func passwordAuth(username, password string) bool {
if username == "admin" && password == "admin" {
return true
}
return false
}
type SshCmdReply interface {
WriteString(s string) (int, error)
}
type SshCmdFunc func(reply SshCmdReply, args []string) error
type SshCmd struct {
CmdFunc SshCmdFunc
Args string
Description string
}
func (c SshCmd) Call(reply SshCmdReply, args []string) error {
return c.CmdFunc(reply, args)
}
var cmdMap = map[string]SshCmd{}
func RegisterSSHCmd(name string, cmdFunc SshCmdFunc, args string, desc string) {
cmdMap[name] = SshCmd{
CmdFunc: cmdFunc,
Args: args,
Description: desc,
}
}
func RunSSH() {
RegisterSSHCmd("help",
HelpCmd,
"[cmd]",
"Shows this help (or help for a given command)")
RegisterSSHCmd("start",
StartServerCmd,
"<id>",
"Starts the server with the given id")
RegisterSSHCmd("stop",
StopServerCmd,
"<id>",
"Stops the server with the given id")
RegisterSSHCmd("supw",
SetSuperUserPasswordCmd,
"<id> <password>",
"Set the SuperUser password for server with the given id")
RegisterSSHCmd("setconf",
SetConfCmd,
"<id> <key> <value>",
"Set a config value for the server with the given id")
RegisterSSHCmd("getconf",
GetConfCmd,
"<id> <key> <value>",
"Get a config value for the server with the given id")
pemBytes, err := ioutil.ReadFile(filepath.Join(Args.DataDir, "key"))
if err != nil {
log.Fatal(err)
}
cfg := new(ssh.ServerConfig)
cfg.Rand = rand.Reader
cfg.PasswordCallback = passwordAuth
cfg.SetRSAPrivateKey(pemBytes)
listener, err := ssh.Listen("tcp", Args.SshAddr, cfg)
if err != nil {
log.Fatal(err)
}
log.Printf("Listening for SSH connections on '%v'", Args.SshAddr)
for {
conn, err := listener.Accept()
if err != nil {
log.Fatalf("ssh: unable to accept incoming connection: %v", err)
}
err = conn.Handshake()
if err == io.EOF {
continue
} else if err != nil {
log.Fatalf("ssh: unable to perform handshake: %v", err)
}
go func() {
for {
channel, err := conn.Accept()
if err == io.EOF {
return
} else if err != nil {
log.Fatalf("ssh: unable to accept channel: %v", err)
}
go handleChannel(channel)
}
}()
}
}
func handleChannel(channel ssh.Channel) {
if channel.ChannelType() == "session" {
channel.Accept()
shell := ssh.NewServerShell(channel, "G> ")
go func() {
defer channel.Close()
for {
line, err := shell.ReadLine()
if err == io.EOF {
break
} else if err != nil {
log.Printf("ssh: error in reading from channel: %v", err)
break
}
line = strings.TrimSpace(line)
args := strings.Split(line, " ")
if len(args) < 1 {
continue
}
if args[0] == "exit" {
return
}
if cmd, ok := cmdMap[args[0]]; ok {
buf := new(bytes.Buffer)
err = cmd.Call(buf, args)
if err != nil {
_, err = shell.Write([]byte(fmt.Sprintf("error: %v\r\n", err.Error())))
if err != nil {
return
}
continue
}
_, err = shell.Write(buf.Bytes())
if err != nil {
return
}
} else {
_, err = shell.Write([]byte("error: unknown command\r\n"))
}
}
}()
return
}
channel.Reject(ssh.UnknownChannelType, "unknown channel type")
}
func HelpCmd(reply SshCmdReply, args []string) error {
if len(args) == 1 {
for cmdName, cmd := range cmdMap {
reply.WriteString("\r\n")
reply.WriteString(" " + cmdName + " " + cmd.Args + "\r\n")
reply.WriteString(" " + cmd.Description + "\r\n")
}
} else if len(args) > 1 {
cmdName := args[1]
if cmd, ok := cmdMap[cmdName]; ok {
reply.WriteString("\r\n")
reply.WriteString(" " + cmdName + " " + cmd.Args + "\r\n")
reply.WriteString(" " + cmd.Description + "\r\n")
} else {
return errors.New("no such command name")
}
}
reply.WriteString("\r\n")
return nil
}
func StartServerCmd(reply SshCmdReply, args []string) error {
return errors.New("not implemented")
}
func StopServerCmd(reply SshCmdReply, args []string) error {
return errors.New("not implemented")
}
func SetSuperUserPasswordCmd(reply SshCmdReply, args []string) error {
return errors.New("not implemented")
}
func SetConfCmd(reply SshCmdReply, args []string) error {
return errors.New("not implemented")
}
func GetConfCmd(reply SshCmdReply, args []string) error {
return errors.New("not implemented")
}