func TestHostKick(t *testing.T) { key, err := sshd.NewRandomSigner(512) if err != nil { t.Fatal(err) } auth := NewAuth() config := sshd.MakeAuth(auth) config.AddHostKey(key) s, err := sshd.ListenSSH(":0", config) if err != nil { t.Fatal(err) } defer s.Close() addr := s.Addr().String() host := NewHost(s, nil) go host.Serve() connected := make(chan struct{}) done := make(chan struct{}) go func() { // First client err = sshd.ConnectShell(addr, "foo", func(r io.Reader, w io.WriteCloser) { // Make op member, _ := host.Room.MemberById("foo") member.Op = true // Block until second client is here connected <- struct{}{} w.Write([]byte("/kick bar\r\n")) }) if err != nil { t.Fatal(err) } }() go func() { // Second client err = sshd.ConnectShell(addr, "bar", func(r io.Reader, w io.WriteCloser) { <-connected // Consume while we're connected. Should break when kicked. ioutil.ReadAll(r) }) if err != nil { t.Fatal(err) } close(done) }() select { case <-done: case <-time.After(time.Second * 1): t.Fatal("Timeout.") } }
func TestHostWhitelist(t *testing.T) { key, err := sshd.NewRandomSigner(512) if err != nil { t.Fatal(err) } auth := NewAuth() config := sshd.MakeAuth(auth) config.AddHostKey(key) s, err := sshd.ListenSSH(":0", config) if err != nil { t.Fatal(err) } defer s.Close() host := NewHost(s, auth) go host.Serve() target := s.Addr().String() err = sshd.ConnectShell(target, "foo", func(r io.Reader, w io.WriteCloser) {}) if err != nil { t.Error(err) } clientkey, err := rsa.GenerateKey(rand.Reader, 512) if err != nil { t.Fatal(err) } clientpubkey, _ := ssh.NewPublicKey(clientkey.Public()) auth.Whitelist(clientpubkey, 0) err = sshd.ConnectShell(target, "foo", func(r io.Reader, w io.WriteCloser) {}) if err == nil { t.Error("Failed to block unwhitelisted connection.") } }
func TestHostNameCollision(t *testing.T) { key, err := sshd.NewRandomSigner(512) if err != nil { t.Fatal(err) } config := sshd.MakeNoAuth() config.AddHostKey(key) s, err := sshd.ListenSSH(":0", config) if err != nil { t.Fatal(err) } defer s.Close() host := NewHost(s, nil) go host.Serve() done := make(chan struct{}, 1) // First client go func() { err = sshd.ConnectShell(s.Addr().String(), "foo", func(r io.Reader, w io.WriteCloser) { scanner := bufio.NewScanner(r) // Consume the initial buffer scanner.Scan() actual := scanner.Text() if !strings.HasPrefix(actual, "[foo] ") { t.Errorf("First client failed to get 'foo' name: %q", actual) } actual = stripPrompt(actual) expected := " * foo joined. (Connected: 1)" if actual != expected { t.Errorf("Got %q; expected %q", actual, expected) } // Ready for second client done <- struct{}{} scanner.Scan() actual = stripPrompt(scanner.Text()) expected = " * Guest1 joined. (Connected: 2)" if actual != expected { t.Errorf("Got %q; expected %q", actual, expected) } // Wrap it up. close(done) }) if err != nil { t.Fatal(err) } }() // Wait for first client <-done // Second client err = sshd.ConnectShell(s.Addr().String(), "foo", func(r io.Reader, w io.WriteCloser) { scanner := bufio.NewScanner(r) // Consume the initial buffer scanner.Scan() actual := scanner.Text() if !strings.HasPrefix(actual, "[Guest1] ") { t.Errorf("Second client did not get Guest1 name.") } }) if err != nil { t.Fatal(err) } <-done }
func main() { options := Options{} parser := flags.NewParser(&options, flags.Default) p, err := parser.Parse() if err != nil { if p == nil { fmt.Print(err) } os.Exit(1) return } if options.Pprof != 0 { go func() { fmt.Println(http.ListenAndServe(fmt.Sprintf("localhost:%d", options.Pprof), nil)) }() } // Figure out the log level numVerbose := len(options.Verbose) if numVerbose > len(logLevels) { numVerbose = len(logLevels) - 1 } logLevel := logLevels[numVerbose] sshchat.SetLogger(golog.New(os.Stderr, logLevel)) if logLevel == log.Debug { // Enable logging from submodules chat.SetLogger(os.Stderr) sshd.SetLogger(os.Stderr) } privateKeyPath := options.Identity if strings.HasPrefix(privateKeyPath, "~/") { user, err := user.Current() if err == nil { privateKeyPath = strings.Replace(privateKeyPath, "~", user.HomeDir, 1) } } privateKey, err := ReadPrivateKey(privateKeyPath) if err != nil { fail(2, "Couldn't read private key: %v\n", err) } signer, err := ssh.ParsePrivateKey(privateKey) if err != nil { fail(3, "Failed to parse key: %v\n", err) } auth := sshchat.NewAuth() config := sshd.MakeAuth(auth) config.AddHostKey(signer) s, err := sshd.ListenSSH(options.Bind, config) if err != nil { fail(4, "Failed to listen on socket: %v\n", err) } defer s.Close() s.RateLimit = sshd.NewInputLimiter fmt.Printf("Listening for connections on %v\n", s.Addr().String()) host := sshchat.NewHost(s, auth) host.SetTheme(message.Themes[0]) err = fromFile(options.Admin, func(line []byte) error { key, _, _, _, err := ssh.ParseAuthorizedKey(line) if err != nil { return err } auth.Op(key, 0) return nil }) if err != nil { fail(5, "Failed to load admins: %v\n", err) } err = fromFile(options.Whitelist, func(line []byte) error { key, _, _, _, err := ssh.ParseAuthorizedKey(line) if err != nil { return err } auth.Whitelist(key, 0) return nil }) if err != nil { fail(6, "Failed to load whitelist: %v\n", err) } if options.Motd != "" { motd, err := ioutil.ReadFile(options.Motd) if err != nil { fail(7, "Failed to load MOTD file: %v\n", err) } motdString := strings.TrimSpace(string(motd)) // hack to normalize line endings into \r\n motdString = strings.Replace(motdString, "\r\n", "\n", -1) motdString = strings.Replace(motdString, "\n", "\r\n", -1) host.SetMotd(motdString) } if options.Log == "-" { host.SetLogging(os.Stdout) } else if options.Log != "" { fp, err := os.OpenFile(options.Log, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { fail(8, "Failed to open log file for writing: %v", err) } host.SetLogging(fp) } go host.Serve() // Construct interrupt handler sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt) <-sig // Wait for ^C signal fmt.Fprintln(os.Stderr, "Interrupt signal detected, shutting down.") os.Exit(0) }