func (suite *ServerSuite) createConfig() Config { // Create logger writer := log.NewConcurrentWriter(os.Stdout) // writer := log.NewConcurrentWriter(ioutil.Discard) logger := log.NewLogger(writer, "sshh") // logger := log.DefaultLog // Get signer signer, err := ssh.ParsePrivateKey([]byte(serverKey)) if err != nil { suite.Fail("Private key could not be parsed", err.Error()) } // Create config cfg := Config{ Deadline: time.Second, Handlers: map[string]SSHHandler{ "echo": &EchoHandler{log.New("echo")}, "bad": &BadHandler{}, }, Logger: logger, Bind: ":9022", PrivateKey: signer, PasswordCallback: passwordCallback, PublicKeyCallback: publicKeyCallback, } return cfg }
func (suite *ServerSuite) TestWildcard() { writer := log.NewConcurrentWriter(os.Stdout) logger := log.NewLogger(writer, "sshh_test") r := router.New(logger, nil, nil) r.Register("/echo", &EchoHandler{log.New("echo")}) r.Register("/bad", &BadHandler{}) acceptErr := errors.New("accept error") ch := &sshmocks.MockNewChannel{ TypeName: "*", AcceptErr: acceptErr, } ch.On("ChannelType").Return("*") ch.On("Reject", ssh.UnknownChannelType, "*").Return(errors.New("unknown reason 1000")) conn := &sshmocks.MockConn{} conn.On("Close").Return(nil) serverConn := ssh.ServerConn{ Conn: conn, } // Create dispatcher dispatcher := &UrlDispatcher{Logger: log.NullLog, Router: r} dispatcher.Dispatch(context.Background(), &serverConn, ch) // assert that the expectations were met ch.AssertCalled(suite.T(), "ChannelType") ch.AssertCalled(suite.T(), "Reject", ssh.UnknownChannelType, "*") conn.AssertCalled(suite.T(), "Close") }
func TestConfig(t *testing.T) { var authLogCalled bool var authLogCallback = func(conn ssh.ConnMetadata, method string, err error) { authLogCalled = true } // Create logger writer := log.NewConcurrentWriter(ioutil.Discard) logger := log.NewLogger(writer, "sshh") // Get signer signer, err := ssh.ParsePrivateKey([]byte(serverKey)) if err != nil { t.Fatalf("Private key could not be parsed", err.Error()) } r := router.New(logger, nil, nil) r.Register("/echo", &EchoHandler{log.New("echo")}) cfg := Config{ Deadline: time.Second, Dispatcher: &UrlDispatcher{ Router: r, Logger: logger, }, // Handlers: map[string]SSHHandler{ // "echo": &EchoHandler{log.New("echo")}, // }, Logger: logger, Bind: ":9022", PrivateKey: signer, AuthLogCallback: authLogCallback, PasswordCallback: passwordCallback, PublicKeyCallback: publicKeyCallback, } // Assertions assert.Equal(t, time.Second, cfg.Deadline, "Deadline should be 1s") assert.Equal(t, ":9022", cfg.Bind, "Bind should be :9022") // Create SSH config c := cfg.SSHConfig() assert.NotNil(t, c, "SSH config should not be nil") assert.Equal(t, passwordCallback, c.PasswordCallback, "PasswordCallback should use the one we passed in") assert.Equal(t, publicKeyCallback, c.PublicKeyCallback, "PublicKeyCallback should use the one we passed in") assert.Equal(t, authLogCallback, c.AuthLogCallback, "AuthLogCallback should use the one we passed in") // // Test Handlers // h, ok := cfg.Handler("echo") // assert.True(t, ok, "Echo handler should be registered") // assert.NotNil(t, h, "Echo handler should not be nil") // h, ok = cfg.Handler("shell") // assert.False(t, ok, "Shell handler should not be registered") // assert.Nil(t, h, "Shell handler should be nil") }
func (suite *ServerSuite) createConfig() Config { // Create logger writer := log.NewConcurrentWriter(os.Stdout) // writer := log.NewConcurrentWriter(ioutil.Discard) logger := log.NewLogger(writer, "sshh") // logger := log.DefaultLog // Get signer signer, err := ssh.ParsePrivateKey([]byte(serverKey)) if err != nil { suite.Fail("Private key could not be parsed", err.Error()) } r := router.New(logger, nil, nil) r.Register("/echo", &EchoHandler{log.New("echo")}) r.Register("/bad", &BadHandler{}) // Create config cfg := Config{ Context: context.Background(), Deadline: time.Second, Dispatcher: &UrlDispatcher{ Router: r, Logger: logger, }, // Handlers: map[string]SSHHandler{ // "echo": &EchoHandler{log.New("echo")}, // "bad": &BadHandler{}, // }, Logger: logger, Bind: ":9022", PrivateKey: signer, PasswordCallback: passwordCallback, PublicKeyCallback: publicKeyCallback, } return cfg }
"github.com/blacklabeldata/kappa/skl" "github.com/blacklabeldata/xbinary" "golang.org/x/crypto/ssh" // "golang.org/x/crypto/ssh/terminal" "github.com/subsilent/crypto/ssh/terminal" ) // ClientCmd is the CLI command var ClientCmd = &cobra.Command{ Use: "client [ssh://username@host:port]", Short: "client starts a terminal with the given kappa server", Long: ``, Run: func(cmd *cobra.Command, args []string) { // Create logger writer := log.NewConcurrentWriter(os.Stdout) logger := log.NewLogger(writer, "cli") err := InitializeClientConfig(logger) if err != nil { return } // Get SSH Key file keyFile := viper.GetString("ClientKey") // logger.Info("Reading private key", "file", sshKeyFile) // Read SSH Key keyBytes, err := ioutil.ReadFile(keyFile) if err != nil { fmt.Println("Private key could not be read:", err.Error())
func New(c *Config) (cer Cerebrum, err error) { // Create logger if c.LogOutput == nil { c.LogOutput = log.NewConcurrentWriter(os.Stderr) } logger := log.NewLogger(c.LogOutput, "kappa") // Create data directory if err = os.MkdirAll(c.DataPath, 0755); err != nil { logger.Warn("Could not create data directory", "err", err) return } // Setup reconciler serfEventCh := make(chan serf.Event, 256) reconcilerCh := make(chan serf.Member, 32) ctx, cancel := context.WithCancel(context.Background()) cereb := &cerebrum{ config: c, logger: logger, dialer: NewDialer(NewPool(c.LogOutput, 5*time.Minute, c.TLSConfig)), serfEventCh: serfEventCh, reconcileCh: reconcilerCh, grim: grim.ReaperWithContext(ctx), context: ctx, cancel: cancel, } // Create serf server err = cereb.setupRaft() if err != nil { err = logger.Error("Failed to start serf: %v", err) return nil, err } isLeader := func() bool { return cereb.raft.State() == raft.Leader } reconciler := &Reconciler{reconcilerCh, isLeader} cereb.serfer = serfer.NewSerfer(serfEventCh, serfer.SerfEventHandler{ Logger: log.NewLogger(c.LogOutput, CerebrumEventPrefix), ServicePrefix: CerebrumEventPrefix, ReconcileOnJoin: true, ReconcileOnLeave: true, ReconcileOnFail: true, ReconcileOnUpdate: true, ReconcileOnReap: true, NodeJoined: c.NodeJoined, NodeUpdated: c.NodeUpdated, NodeLeft: c.NodeLeft, NodeFailed: c.NodeFailed, NodeReaped: c.NodeReaped, UserEvent: c.UserEvent, UnknownEventHandler: c.UnknownEventHandler, Reconciler: reconciler, IsLeader: isLeader, IsLeaderEvent: func(name string) bool { return name == CerebrumLeaderEvent }, LeaderElectionHandler: cereb, }) // Create serf server cereb.serf, err = cereb.setupSerf() if err != nil { err = logger.Error("Failed to start serf: %v", err) return nil, err } cer = cereb return cer, nil }
func main() { // Create logger writer := log.NewConcurrentWriter(os.Stdout) logger := log.NewLogger(writer, "sshh") // Get private key privateKey, err := ssh.ParsePrivateKey([]byte(privateKey)) if err != nil { logger.Warn("Private key could not be parsed", "error", err.Error()) } // Setup server config config := sshh.Config{ Deadline: time.Second, Logger: logger, Bind: ":9022", Handlers: map[string]sshh.SSHHandler{ "session": NewShellHandler(logger), }, PrivateKey: privateKey, PasswordCallback: func(conn ssh.ConnMetadata, password []byte) (perm *ssh.Permissions, err error) { if conn.User() == "admin" && string(password) == "password" { // Add username to permissions perm = &ssh.Permissions{ Extensions: map[string]string{ "username": conn.User(), }, } } else { err = fmt.Errorf("Invalid username or password") } return }, AuthLogCallback: func(conn ssh.ConnMetadata, method string, err error) { if err == nil { logger.Info("Successful login", "user", conn.User(), "method", method) } }, // PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (perm *ssh.Permissions, err error) { // return nil, fmt.Errorf("Unauthorized") // }, } // Create SSH server sshServer, err := sshh.NewSSHServer(&config) if err != nil { logger.Error("SSH Server could not be configured", "error", err.Error()) return } // Start servers sshServer.Start() // Handle signals sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt, os.Kill) // Wait for signal logger.Info("Ready to serve requests") // Block until signal is received <-sig // Stop listening for signals and close channel signal.Stop(sig) close(sig) // Shut down SSH server logger.Info("Shutting down servers.") sshServer.Stop() }
func NewServer(c *DatabaseConfig) (server *Server, err error) { // Create logger if c.LogOutput == nil { c.LogOutput = log.NewConcurrentWriter(os.Stdout) } logger := log.NewLogger(c.LogOutput, "kappa") // Create data directory if err = os.MkdirAll(c.DataPath, 0755); err != nil { logger.Warn("Could not create data directory", "err", err) // logger.Warn("Could not create data directory", "err", err.Error()) return } // Connect to database cwd, err := os.Getwd() if err != nil { logger.Error("Could not get working directory", "error", err.Error()) return } file := path.Join(cwd, c.DataPath, "meta.db") logger.Info("Connecting to database", "file", file) system, err := datamodel.NewSystem(file) if err != nil { logger.Error("Could not connect to database", "error", err.Error()) return } // Get SSH Key file sshKeyFile := c.SSHPrivateKeyFile logger.Info("Reading private key", "file", sshKeyFile) privateKey, err := auth.ReadPrivateKey(logger, sshKeyFile) if err != nil { return } // Get admin certificate adminCertFile := c.AdminCertificateFile logger.Info("Reading admin public key", "file", adminCertFile) // Read admin certificate cert, err := ioutil.ReadFile(adminCertFile) if err != nil { logger.Error("admin certificate could not be read", "filename", c.AdminCertificateFile) return } // Add admin cert to key ring userStore, err := system.Users() if err != nil { logger.Error("could not get user store", "error", err.Error()) return } // Create admin account admin, err := userStore.Create("admin") if err != nil { logger.Error("error creating admin account", "error", err.Error()) return } // Add admin certificate keyRing := admin.KeyRing() fingerprint, err := keyRing.AddPublicKey(cert) if err != nil { logger.Error("admin certificate could not be added", "error", err.Error()) return } logger.Info("Added admin certificate", "fingerprint", fingerprint) // Read root cert rootPem, err := ioutil.ReadFile(c.CACertificateFile) if err != nil { logger.Error("root certificate could not be read", "filename", c.CACertificateFile) return } // Create certificate pool roots := x509.NewCertPool() if ok := roots.AppendCertsFromPEM(rootPem); !ok { logger.Error("failed to parse root certificate") return } // Setup SSH Server sshLogger := log.NewLogger(c.LogOutput, "ssh") pubKeyCallback, err := PublicKeyCallback(system) if err != nil { logger.Error("failed to create PublicKeyCallback", err) return } // Setup server config config := sshh.Config{ Deadline: c.SSHConnectionDeadline, Logger: sshLogger, Bind: c.SSHBindAddress, PrivateKey: privateKey, PublicKeyCallback: pubKeyCallback, AuthLogCallback: func(meta ssh.ConnMetadata, method string, err error) { if err == nil { sshLogger.Info("login success", "user", meta.User()) } else if err != nil && method == "publickey" { sshLogger.Info("login failure", "user", meta.User(), "err", err.Error()) } }, Handlers: map[string]sshh.SSHHandler{ "kappa-client": &EchoHandler{}, }, } // Create SSH server sshServer, err := sshh.NewSSHServer(&config) if err != nil { logger.Error("SSH Server could not be configured", "error", err.Error()) return } // Setup Serf handlers mgr := NewNodeList() reconcilerCh := make(chan serf.Member, 32) serfEventCh := make(chan serf.Event, 256) userEventCh := make(chan serf.UserEvent, 256) serfer := serfer.NewSerfer(serfEventCh, serfer.SerfEventHandler{ Logger: log.NewLogger(c.LogOutput, "serf"), ServicePrefix: "kappa", ReconcileOnJoin: true, ReconcileOnLeave: true, ReconcileOnFail: true, ReconcileOnUpdate: true, ReconcileOnReap: true, NodeJoined: &SerfNodeJoinHandler{ mgr, log.NewLogger(c.LogOutput, "serf:node-join")}, NodeUpdated: &SerfNodeUpdateHandler{ mgr, log.NewLogger(c.LogOutput, "serf:node-update")}, NodeLeft: &SerfNodeLeaveHandler{ mgr, log.NewLogger(c.LogOutput, "serf:node-left")}, NodeFailed: &SerfNodeLeaveHandler{ mgr, log.NewLogger(c.LogOutput, "serf:node-fail")}, NodeReaped: &SerfNodeLeaveHandler{ mgr, log.NewLogger(c.LogOutput, "serf:node-reap")}, UserEvent: &SerfUserEventHandler{ log.NewLogger(c.LogOutput, "serf:user-events"), userEventCh}, UnknownEventHandler: &SerfUserEventHandler{ log.NewLogger(c.LogOutput, "serf:unknown-event"), userEventCh}, Reconciler: &SerfReconciler{reconcilerCh}, IsLeader: func() bool { // TODO: Replace with Raft IsLeader check return true }, }) // Create database server s := &Server{ config: c, logger: logger, sshServer: &sshServer, serfer: serfer, localKappas: make(map[string]*NodeDetails), serfEventCh: serfEventCh, kappaEventCh: userEventCh, reconcileCh: reconcilerCh, } // Create serf server s.serf, err = s.setupSerf() if err != nil { err = logger.Error("Failed to start serf: %v", err) return } return s, nil }