Skip to content

Instantly share code, notes, and snippets.

@nictuku
Created April 8, 2012 15:43
Show Gist options
  • Star 39 You must be signed in to star a gist
  • Fork 12 You must be signed in to fork a gist
  • Save nictuku/2338048 to your computer and use it in GitHub Desktop.
Save nictuku/2338048 to your computer and use it in GitHub Desktop.
Go SSH server complete example
package main
import (
"fmt"
"io"
"io/ioutil"
"log"
"code.google.com/p/go.crypto/ssh"
"code.google.com/p/go.crypto/ssh/terminal"
)
func main() {
// An SSH server is represented by a ServerConfig, which holds
// certificate details and handles authentication of ServerConns.
config := &ssh.ServerConfig{
PasswordCallback: func(conn *ssh.ServerConn, user, pass string) bool {
return user == "testuser" && pass == "tiger"
},
}
pemBytes, err := ioutil.ReadFile("id_rsa")
if err != nil {
log.Fatal("Failed to load private key:", err)
}
if err = config.SetRSAPrivateKey(pemBytes); err != nil {
log.Fatal("Failed to parse private key:", err)
}
// Once a ServerConfig has been configured, connections can be
// accepted.
conn, err := ssh.Listen("tcp", "0.0.0.0:2022", config)
if err != nil {
log.Fatal("failed to listen for connection")
}
for {
// A ServerConn multiplexes several channels, which must
// themselves be Accepted.
log.Println("accept")
sConn, err := conn.Accept()
if err != nil {
log.Println("failed to accept incoming connection")
continue
}
if err := sConn.Handshake(); err != nil {
log.Println("failed to handshake")
continue
}
go handleServerConn(sConn)
}
}
func handleServerConn(sConn *ssh.ServerConn) {
defer sConn.Close()
for {
// Accept reads from the connection, demultiplexes packets
// to their corresponding channels and returns when a new
// channel request is seen. Some goroutine must always be
// calling Accept; otherwise no messages will be forwarded
// to the channels.
ch, err := sConn.Accept()
if err == io.EOF {
return
}
if err != nil {
log.Println("handleServerConn Accept:", err)
break
}
// Channels have a type, depending on the application level
// protocol intended. In the case of a shell, the type is
// "session" and ServerShell may be used to present a simple
// terminal interface.
if ch.ChannelType() != "session" {
ch.Reject(ssh.UnknownChannelType, "unknown channel type")
break
}
go handleChannel(ch)
}
}
func handleChannel(ch ssh.Channel) {
term := terminal.NewTerminal(ch, "> ")
serverTerm := &ssh.ServerTerminal{
Term: term,
Channel: ch,
}
ch.Accept()
defer ch.Close()
for {
line, err := serverTerm.ReadLine()
if err == io.EOF {
return
}
if err != nil {
log.Println("handleChannel readLine err:", err)
continue
}
fmt.Println(line)
}
}
@dariusc93
Copy link

Is this code even valid? I normally do "net.Listen("tcp", "0.0.0.0:2022")" and several things in this code dont exist in the ssh code (unless its something completely different)

@chalonga
Copy link

chalonga commented Aug 2, 2014

I would like to second the above comment.

sshd.go:19: cannot use func literal (type func(_ssh.ServerConn, string, string) bool) as type func(ssh.ConnMetadata, []byte) (_ssh.Permissions, error) in field value
sshd/sshd.go:32: undefined: ssh.Listen
sshd/sshd.go:61: sConn.Accept undefined (type *ssh.ServerConn has no field or method Accept)
sshd/sshd.go:83: undefined: ssh.ServerTerminal
sshd/sshd.go:87: ch.Accept undefined (type ssh.Channel has no field or method Accept)

@jpillora
Copy link

jpillora commented Dec 2, 2014

There was rewrite of the crypto/ssh package released around April 2014, I've made an updated example here: https://gist.github.com/jpillora/b480fde82bff51a06238 it creates a pty and links it to a new bash process.

@tscj3490
Copy link

How can we get client's command log in ssh server?
Please tell me about it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment