github.com/pkg/sftp@v1.13.6/examples/request-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 net.Conn.
    76  	sconn, chans, reqs, err := ssh.NewServerConn(nConn, config)
    77  	if err != nil {
    78  		log.Fatal("failed to handshake", err)
    79  	}
    80  	log.Println("login detected:", sconn.User())
    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  		root := sftp.InMemHandler()
   123  		server := sftp.NewRequestServer(channel, root)
   124  		if err := server.Serve(); err == io.EOF {
   125  			server.Close()
   126  			log.Print("sftp client exited session.")
   127  		} else if err != nil {
   128  			log.Fatal("sftp server completed with error:", err)
   129  		}
   130  	}
   131  }