github.com/glycerine/xcryptossh@v7.0.4+incompatible/example_test.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ssh_test
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"context"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"log"
    14  	"net"
    15  	"net/http"
    16  	"os"
    17  	"path/filepath"
    18  	"strings"
    19  
    20  	"github.com/glycerine/xcryptossh/terminal"
    21  	"github.com/glycerine/xcryptossh"
    22  )
    23  
    24  func ExampleNewServerConn() {
    25  
    26  	// Public key authentication is done by comparing
    27  	// the public key of a received connection
    28  	// with the entries in the authorized_keys file.
    29  	authorizedKeysBytes, err := ioutil.ReadFile("authorized_keys")
    30  	if err != nil {
    31  		log.Fatalf("Failed to load authorized_keys, err: %v", err)
    32  	}
    33  
    34  	authorizedKeysMap := map[string]bool{}
    35  	for len(authorizedKeysBytes) > 0 {
    36  		pubKey, _, _, rest, err := ssh.ParseAuthorizedKey(authorizedKeysBytes)
    37  		if err != nil {
    38  			log.Fatal(err)
    39  		}
    40  
    41  		authorizedKeysMap[string(pubKey.Marshal())] = true
    42  		authorizedKeysBytes = rest
    43  	}
    44  
    45  	// An SSH server is represented by a ServerConfig, which holds
    46  	// certificate details and handles authentication of ServerConns.
    47  	config := &ssh.ServerConfig{
    48  		// Remove to disable password auth.
    49  		PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
    50  			// Should use constant-time compare (or better, salt+hash) in
    51  			// a production setting.
    52  			if c.User() == "testuser" && string(pass) == "tiger" {
    53  				return nil, nil
    54  			}
    55  			return nil, fmt.Errorf("password rejected for %q", c.User())
    56  		},
    57  
    58  		// Remove to disable public key auth.
    59  		PublicKeyCallback: func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
    60  			if authorizedKeysMap[string(pubKey.Marshal())] {
    61  				return &ssh.Permissions{
    62  					// Record the public key used for authentication.
    63  					Extensions: map[string]string{
    64  						"pubkey-fp": ssh.FingerprintSHA256(pubKey),
    65  					},
    66  				}, nil
    67  			}
    68  			return nil, fmt.Errorf("unknown public key for %q", c.User())
    69  		},
    70  	}
    71  
    72  	privateBytes, err := ioutil.ReadFile("id_rsa")
    73  	if err != nil {
    74  		log.Fatal("Failed to load private key: ", err)
    75  	}
    76  
    77  	private, err := ssh.ParsePrivateKey(privateBytes)
    78  	if err != nil {
    79  		log.Fatal("Failed to parse private key: ", err)
    80  	}
    81  
    82  	config.AddHostKey(private)
    83  
    84  	// Once a ServerConfig has been configured, connections can be
    85  	// accepted.
    86  	listener, err := net.Listen("tcp", "0.0.0.0:2022")
    87  	if err != nil {
    88  		log.Fatal("failed to listen for connection: ", err)
    89  	}
    90  	nConn, err := listener.Accept()
    91  	if err != nil {
    92  		log.Fatal("failed to accept incoming connection: ", err)
    93  	}
    94  
    95  	// Before use, a handshake must be performed on the incoming
    96  	// net.Conn.
    97  	ctx := context.Background()
    98  
    99  	conn, chans, reqs, err := ssh.NewServerConn(ctx, nConn, config)
   100  	if err != nil {
   101  		log.Fatal("failed to handshake: ", err)
   102  	}
   103  	log.Printf("logged in with key %s", conn.Permissions.Extensions["pubkey-fp"])
   104  
   105  	// The incoming Request channel must be serviced.
   106  	go ssh.DiscardRequests(ctx, reqs, config.Halt)
   107  
   108  	// Service the incoming Channel channel.
   109  	for newChannel := range chans {
   110  		// Channels have a type, depending on the application level
   111  		// protocol intended. In the case of a shell, the type is
   112  		// "session" and ServerShell may be used to present a simple
   113  		// terminal interface.
   114  		if newChannel.ChannelType() != "session" {
   115  			newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
   116  			continue
   117  		}
   118  		channel, requests, err := newChannel.Accept()
   119  		if err != nil {
   120  			log.Fatalf("Could not accept channel: %v", err)
   121  		}
   122  
   123  		// Sessions have out-of-band requests such as "shell",
   124  		// "pty-req" and "env".  Here we handle only the
   125  		// "shell" request.
   126  		go func(in <-chan *ssh.Request) {
   127  			for req := range in {
   128  				req.Reply(req.Type == "shell", nil)
   129  			}
   130  		}(requests)
   131  
   132  		term := terminal.NewTerminal(channel, "> ")
   133  
   134  		go func() {
   135  			defer channel.Close()
   136  			for {
   137  				line, err := term.ReadLine()
   138  				if err != nil {
   139  					break
   140  				}
   141  				fmt.Println(line)
   142  			}
   143  		}()
   144  	}
   145  }
   146  
   147  func ExampleHostKeyCheck() {
   148  	// Every client must provide a host key check.  Here is a
   149  	// simple-minded parse of OpenSSH's known_hosts file
   150  	host := "hostname"
   151  	file, err := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"))
   152  	if err != nil {
   153  		log.Fatal(err)
   154  	}
   155  	defer file.Close()
   156  
   157  	scanner := bufio.NewScanner(file)
   158  	var hostKey ssh.PublicKey
   159  	for scanner.Scan() {
   160  		fields := strings.Split(scanner.Text(), " ")
   161  		if len(fields) != 3 {
   162  			continue
   163  		}
   164  		if strings.Contains(fields[0], host) {
   165  			var err error
   166  			hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes())
   167  			if err != nil {
   168  				log.Fatalf("error parsing %q: %v", fields[2], err)
   169  			}
   170  			break
   171  		}
   172  	}
   173  
   174  	if hostKey == nil {
   175  		log.Fatalf("no hostkey for %s", host)
   176  	}
   177  
   178  	config := ssh.ClientConfig{
   179  		User:            os.Getenv("USER"),
   180  		HostKeyCallback: ssh.FixedHostKey(hostKey),
   181  	}
   182  	ctx := context.Background()
   183  	_, err = ssh.Dial(ctx, "tcp", host+":22", &config)
   184  	log.Println(err)
   185  }
   186  
   187  func ExampleDial() {
   188  	var hostKey ssh.PublicKey
   189  	// An SSH client is represented with a ClientConn.
   190  	//
   191  	// To authenticate with the remote server you must pass at least one
   192  	// implementation of AuthMethod via the Auth field in ClientConfig,
   193  	// and provide a HostKeyCallback.
   194  	config := &ssh.ClientConfig{
   195  		User: "username",
   196  		Auth: []ssh.AuthMethod{
   197  			ssh.Password("yourpassword"),
   198  		},
   199  		HostKeyCallback: ssh.FixedHostKey(hostKey),
   200  	}
   201  	ctx := context.Background()
   202  	client, err := ssh.Dial(ctx, "tcp", "yourserver.com:22", config)
   203  	if err != nil {
   204  		log.Fatal("Failed to dial: ", err)
   205  	}
   206  
   207  	// Each ClientConn can support multiple interactive sessions,
   208  	// represented by a Session.
   209  	session, err := client.NewSession(ctx)
   210  	if err != nil {
   211  		log.Fatal("Failed to create session: ", err)
   212  	}
   213  	defer session.Close()
   214  
   215  	// Once a Session is created, you can execute a single command on
   216  	// the remote side using the Run method.
   217  	var b bytes.Buffer
   218  	session.Stdout = &b
   219  	if err := session.Run("/usr/bin/whoami"); err != nil {
   220  		log.Fatal("Failed to run: " + err.Error())
   221  	}
   222  	fmt.Println(b.String())
   223  }
   224  
   225  func ExamplePublicKeys() {
   226  	var hostKey ssh.PublicKey
   227  	// A public key may be used to authenticate against the remote
   228  	// server by using an unencrypted PEM-encoded private key file.
   229  	//
   230  	// If you have an encrypted private key, the crypto/x509 package
   231  	// can be used to decrypt it.
   232  	key, err := ioutil.ReadFile("/home/user/.ssh/id_rsa")
   233  	if err != nil {
   234  		log.Fatalf("unable to read private key: %v", err)
   235  	}
   236  
   237  	// Create the Signer for this private key.
   238  	signer, err := ssh.ParsePrivateKey(key)
   239  	if err != nil {
   240  		log.Fatalf("unable to parse private key: %v", err)
   241  	}
   242  
   243  	config := &ssh.ClientConfig{
   244  		User: "user",
   245  		Auth: []ssh.AuthMethod{
   246  			// Use the PublicKeys method for remote authentication.
   247  			ssh.PublicKeys(signer),
   248  		},
   249  		HostKeyCallback: ssh.FixedHostKey(hostKey),
   250  	}
   251  
   252  	// Connect to the remote server and perform the SSH handshake.
   253  	ctx := context.Background()
   254  	client, err := ssh.Dial(ctx, "tcp", "host.com:22", config)
   255  	if err != nil {
   256  		log.Fatalf("unable to connect: %v", err)
   257  	}
   258  	defer client.Close()
   259  }
   260  
   261  func ExampleClient_Listen() {
   262  	var hostKey ssh.PublicKey
   263  	config := &ssh.ClientConfig{
   264  		User: "username",
   265  		Auth: []ssh.AuthMethod{
   266  			ssh.Password("password"),
   267  		},
   268  		HostKeyCallback: ssh.FixedHostKey(hostKey),
   269  	}
   270  	// Dial your ssh server.
   271  	ctx := context.Background()
   272  	conn, err := ssh.Dial(ctx, "tcp", "localhost:22", config)
   273  	if err != nil {
   274  		log.Fatal("unable to connect: ", err)
   275  	}
   276  	defer conn.Close()
   277  
   278  	// Request the remote side to open port 8080 on all interfaces.
   279  	l, err := conn.Listen("tcp", "0.0.0.0:8080")
   280  	if err != nil {
   281  		log.Fatal("unable to register tcp forward: ", err)
   282  	}
   283  	defer l.Close()
   284  
   285  	// Serve HTTP with your SSH server acting as a reverse proxy.
   286  	http.Serve(l, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
   287  		fmt.Fprintf(resp, "Hello world!\n")
   288  	}))
   289  }
   290  
   291  func ExampleSession_RequestPty() {
   292  	var hostKey ssh.PublicKey
   293  	// Create client config
   294  	config := &ssh.ClientConfig{
   295  		User: "username",
   296  		Auth: []ssh.AuthMethod{
   297  			ssh.Password("password"),
   298  		},
   299  		HostKeyCallback: ssh.FixedHostKey(hostKey),
   300  	}
   301  	// Connect to ssh server
   302  	ctx := context.Background()
   303  	conn, err := ssh.Dial(ctx, "tcp", "localhost:22", config)
   304  	if err != nil {
   305  		log.Fatal("unable to connect: ", err)
   306  	}
   307  	defer conn.Close()
   308  	// Create a session
   309  	session, err := conn.NewSession(ctx)
   310  	if err != nil {
   311  		log.Fatal("unable to create session: ", err)
   312  	}
   313  	defer session.Close()
   314  	// Set up terminal modes
   315  	modes := ssh.TerminalModes{
   316  		ssh.ECHO:          0,     // disable echoing
   317  		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
   318  		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
   319  	}
   320  	// Request pseudo terminal
   321  	if err := session.RequestPty("xterm", 40, 80, modes); err != nil {
   322  		log.Fatal("request for pseudo terminal failed: ", err)
   323  	}
   324  	// Start remote shell
   325  	if err := session.Shell(); err != nil {
   326  		log.Fatal("failed to start shell: ", err)
   327  	}
   328  }