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