github.com/pkg/sftp@v1.13.6/examples/go-sftp-server/main.go (about) 1 // An example SFTP server implementation using the golang SSH package. 2 // Serves the whole filesystem visible to the user, and has a hard-coded username and password, 3 // so not for real use! 4 package main 5 6 import ( 7 "flag" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "log" 12 "net" 13 "os" 14 15 "github.com/pkg/sftp" 16 "golang.org/x/crypto/ssh" 17 ) 18 19 // Based on example server code from golang.org/x/crypto/ssh and server_standalone 20 func main() { 21 22 var ( 23 readOnly bool 24 debugStderr bool 25 ) 26 27 flag.BoolVar(&readOnly, "R", false, "read-only server") 28 flag.BoolVar(&debugStderr, "e", false, "debug to stderr") 29 flag.Parse() 30 31 debugStream := ioutil.Discard 32 if debugStderr { 33 debugStream = os.Stderr 34 } 35 36 // An SSH server is represented by a ServerConfig, which holds 37 // certificate details and handles authentication of ServerConns. 38 config := &ssh.ServerConfig{ 39 PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { 40 // Should use constant-time compare (or better, salt+hash) in 41 // a production setting. 42 fmt.Fprintf(debugStream, "Login: %s\n", c.User()) 43 if c.User() == "testuser" && string(pass) == "tiger" { 44 return nil, nil 45 } 46 return nil, fmt.Errorf("password rejected for %q", c.User()) 47 }, 48 } 49 50 privateBytes, err := ioutil.ReadFile("id_rsa") 51 if err != nil { 52 log.Fatal("Failed to load private key", err) 53 } 54 55 private, err := ssh.ParsePrivateKey(privateBytes) 56 if err != nil { 57 log.Fatal("Failed to parse private key", err) 58 } 59 60 config.AddHostKey(private) 61 62 // Once a ServerConfig has been configured, connections can be 63 // accepted. 64 listener, err := net.Listen("tcp", "0.0.0.0:2022") 65 if err != nil { 66 log.Fatal("failed to listen for connection", err) 67 } 68 fmt.Printf("Listening on %v\n", listener.Addr()) 69 70 nConn, err := listener.Accept() 71 if err != nil { 72 log.Fatal("failed to accept incoming connection", err) 73 } 74 75 // Before use, a handshake must be performed on the incoming 76 // net.Conn. 77 _, chans, reqs, err := ssh.NewServerConn(nConn, config) 78 if err != nil { 79 log.Fatal("failed to handshake", err) 80 } 81 fmt.Fprintf(debugStream, "SSH server established\n") 82 83 // The incoming Request channel must be serviced. 84 go ssh.DiscardRequests(reqs) 85 86 // Service the incoming Channel channel. 87 for newChannel := range chans { 88 // Channels have a type, depending on the application level 89 // protocol intended. In the case of an SFTP session, this is "subsystem" 90 // with a payload string of "<length=4>sftp" 91 fmt.Fprintf(debugStream, "Incoming channel: %s\n", newChannel.ChannelType()) 92 if newChannel.ChannelType() != "session" { 93 newChannel.Reject(ssh.UnknownChannelType, "unknown channel type") 94 fmt.Fprintf(debugStream, "Unknown channel type: %s\n", newChannel.ChannelType()) 95 continue 96 } 97 channel, requests, err := newChannel.Accept() 98 if err != nil { 99 log.Fatal("could not accept channel.", err) 100 } 101 fmt.Fprintf(debugStream, "Channel accepted\n") 102 103 // Sessions have out-of-band requests such as "shell", 104 // "pty-req" and "env". Here we handle only the 105 // "subsystem" request. 106 go func(in <-chan *ssh.Request) { 107 for req := range in { 108 fmt.Fprintf(debugStream, "Request: %v\n", req.Type) 109 ok := false 110 switch req.Type { 111 case "subsystem": 112 fmt.Fprintf(debugStream, "Subsystem: %s\n", req.Payload[4:]) 113 if string(req.Payload[4:]) == "sftp" { 114 ok = true 115 } 116 } 117 fmt.Fprintf(debugStream, " - accepted: %v\n", ok) 118 req.Reply(ok, nil) 119 } 120 }(requests) 121 122 serverOptions := []sftp.ServerOption{ 123 sftp.WithDebug(debugStream), 124 } 125 126 if readOnly { 127 serverOptions = append(serverOptions, sftp.ReadOnly()) 128 fmt.Fprintf(debugStream, "Read-only server\n") 129 } else { 130 fmt.Fprintf(debugStream, "Read write server\n") 131 } 132 133 server, err := sftp.NewServer( 134 channel, 135 serverOptions..., 136 ) 137 if err != nil { 138 log.Fatal(err) 139 } 140 if err := server.Serve(); err == io.EOF { 141 server.Close() 142 log.Print("sftp client exited session.") 143 } else if err != nil { 144 log.Fatal("sftp server completed with error:", err) 145 } 146 } 147 }