...

Source file src/golang.org/x/crypto/ssh/example_test.go

Documentation: golang.org/x/crypto/ssh

     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  	"crypto/rand"
    11  	"crypto/rsa"
    12  	"fmt"
    13  	"log"
    14  	"net"
    15  	"net/http"
    16  	"os"
    17  	"path/filepath"
    18  	"strings"
    19  	"sync"
    20  
    21  	"golang.org/x/crypto/ssh"
    22  	"golang.org/x/crypto/ssh/terminal"
    23  )
    24  
    25  func ExampleNewServerConn() {
    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 := os.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 := os.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  	config.AddHostKey(private)
    82  
    83  	// Once a ServerConfig has been configured, connections can be
    84  	// accepted.
    85  	listener, err := net.Listen("tcp", "0.0.0.0:2022")
    86  	if err != nil {
    87  		log.Fatal("failed to listen for connection: ", err)
    88  	}
    89  	nConn, err := listener.Accept()
    90  	if err != nil {
    91  		log.Fatal("failed to accept incoming connection: ", err)
    92  	}
    93  
    94  	// Before use, a handshake must be performed on the incoming
    95  	// net.Conn.
    96  	conn, chans, reqs, err := ssh.NewServerConn(nConn, config)
    97  	if err != nil {
    98  		log.Fatal("failed to handshake: ", err)
    99  	}
   100  	log.Printf("logged in with key %s", conn.Permissions.Extensions["pubkey-fp"])
   101  
   102  	var wg sync.WaitGroup
   103  	defer wg.Wait()
   104  
   105  	// The incoming Request channel must be serviced.
   106  	wg.Add(1)
   107  	go func() {
   108  		ssh.DiscardRequests(reqs)
   109  		wg.Done()
   110  	}()
   111  
   112  	// Service the incoming Channel channel.
   113  	for newChannel := range chans {
   114  		// Channels have a type, depending on the application level
   115  		// protocol intended. In the case of a shell, the type is
   116  		// "session" and ServerShell may be used to present a simple
   117  		// terminal interface.
   118  		if newChannel.ChannelType() != "session" {
   119  			newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
   120  			continue
   121  		}
   122  		channel, requests, err := newChannel.Accept()
   123  		if err != nil {
   124  			log.Fatalf("Could not accept channel: %v", err)
   125  		}
   126  
   127  		// Sessions have out-of-band requests such as "shell",
   128  		// "pty-req" and "env".  Here we handle only the
   129  		// "shell" request.
   130  		wg.Add(1)
   131  		go func(in <-chan *ssh.Request) {
   132  			for req := range in {
   133  				req.Reply(req.Type == "shell", nil)
   134  			}
   135  			wg.Done()
   136  		}(requests)
   137  
   138  		term := terminal.NewTerminal(channel, "> ")
   139  
   140  		wg.Add(1)
   141  		go func() {
   142  			defer func() {
   143  				channel.Close()
   144  				wg.Done()
   145  			}()
   146  			for {
   147  				line, err := term.ReadLine()
   148  				if err != nil {
   149  					break
   150  				}
   151  				fmt.Println(line)
   152  			}
   153  		}()
   154  	}
   155  }
   156  
   157  func ExampleServerConfig_AddHostKey() {
   158  	// Minimal ServerConfig supporting only password authentication.
   159  	config := &ssh.ServerConfig{
   160  		PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
   161  			// Should use constant-time compare (or better, salt+hash) in
   162  			// a production setting.
   163  			if c.User() == "testuser" && string(pass) == "tiger" {
   164  				return nil, nil
   165  			}
   166  			return nil, fmt.Errorf("password rejected for %q", c.User())
   167  		},
   168  	}
   169  
   170  	privateBytes, err := os.ReadFile("id_rsa")
   171  	if err != nil {
   172  		log.Fatal("Failed to load private key: ", err)
   173  	}
   174  
   175  	private, err := ssh.ParsePrivateKey(privateBytes)
   176  	if err != nil {
   177  		log.Fatal("Failed to parse private key: ", err)
   178  	}
   179  	// Restrict host key algorithms to disable ssh-rsa.
   180  	signer, err := ssh.NewSignerWithAlgorithms(private.(ssh.AlgorithmSigner), []string{ssh.KeyAlgoRSASHA256, ssh.KeyAlgoRSASHA512})
   181  	if err != nil {
   182  		log.Fatal("Failed to create private key with restricted algorithms: ", err)
   183  	}
   184  	config.AddHostKey(signer)
   185  }
   186  
   187  func ExampleClientConfig_HostKeyCallback() {
   188  	// Every client must provide a host key check.  Here is a
   189  	// simple-minded parse of OpenSSH's known_hosts file
   190  	host := "hostname"
   191  	file, err := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"))
   192  	if err != nil {
   193  		log.Fatal(err)
   194  	}
   195  	defer file.Close()
   196  
   197  	scanner := bufio.NewScanner(file)
   198  	var hostKey ssh.PublicKey
   199  	for scanner.Scan() {
   200  		fields := strings.Split(scanner.Text(), " ")
   201  		if len(fields) != 3 {
   202  			continue
   203  		}
   204  		if strings.Contains(fields[0], host) {
   205  			var err error
   206  			hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes())
   207  			if err != nil {
   208  				log.Fatalf("error parsing %q: %v", fields[2], err)
   209  			}
   210  			break
   211  		}
   212  	}
   213  
   214  	if hostKey == nil {
   215  		log.Fatalf("no hostkey for %s", host)
   216  	}
   217  
   218  	config := ssh.ClientConfig{
   219  		User:            os.Getenv("USER"),
   220  		HostKeyCallback: ssh.FixedHostKey(hostKey),
   221  	}
   222  
   223  	_, err = ssh.Dial("tcp", host+":22", &config)
   224  	log.Println(err)
   225  }
   226  
   227  func ExampleDial() {
   228  	var hostKey ssh.PublicKey
   229  	// An SSH client is represented with a ClientConn.
   230  	//
   231  	// To authenticate with the remote server you must pass at least one
   232  	// implementation of AuthMethod via the Auth field in ClientConfig,
   233  	// and provide a HostKeyCallback.
   234  	config := &ssh.ClientConfig{
   235  		User: "username",
   236  		Auth: []ssh.AuthMethod{
   237  			ssh.Password("yourpassword"),
   238  		},
   239  		HostKeyCallback: ssh.FixedHostKey(hostKey),
   240  	}
   241  	client, err := ssh.Dial("tcp", "yourserver.com:22", config)
   242  	if err != nil {
   243  		log.Fatal("Failed to dial: ", err)
   244  	}
   245  	defer client.Close()
   246  
   247  	// Each ClientConn can support multiple interactive sessions,
   248  	// represented by a Session.
   249  	session, err := client.NewSession()
   250  	if err != nil {
   251  		log.Fatal("Failed to create session: ", err)
   252  	}
   253  	defer session.Close()
   254  
   255  	// Once a Session is created, you can execute a single command on
   256  	// the remote side using the Run method.
   257  	var b bytes.Buffer
   258  	session.Stdout = &b
   259  	if err := session.Run("/usr/bin/whoami"); err != nil {
   260  		log.Fatal("Failed to run: " + err.Error())
   261  	}
   262  	fmt.Println(b.String())
   263  }
   264  
   265  func ExamplePublicKeys() {
   266  	var hostKey ssh.PublicKey
   267  	// A public key may be used to authenticate against the remote
   268  	// server by using an unencrypted PEM-encoded private key file.
   269  	//
   270  	// If you have an encrypted private key, the crypto/x509 package
   271  	// can be used to decrypt it.
   272  	key, err := os.ReadFile("/home/user/.ssh/id_rsa")
   273  	if err != nil {
   274  		log.Fatalf("unable to read private key: %v", err)
   275  	}
   276  
   277  	// Create the Signer for this private key.
   278  	signer, err := ssh.ParsePrivateKey(key)
   279  	if err != nil {
   280  		log.Fatalf("unable to parse private key: %v", err)
   281  	}
   282  
   283  	config := &ssh.ClientConfig{
   284  		User: "user",
   285  		Auth: []ssh.AuthMethod{
   286  			// Use the PublicKeys method for remote authentication.
   287  			ssh.PublicKeys(signer),
   288  		},
   289  		HostKeyCallback: ssh.FixedHostKey(hostKey),
   290  	}
   291  
   292  	// Connect to the remote server and perform the SSH handshake.
   293  	client, err := ssh.Dial("tcp", "host.com:22", config)
   294  	if err != nil {
   295  		log.Fatalf("unable to connect: %v", err)
   296  	}
   297  	defer client.Close()
   298  }
   299  
   300  func ExampleClient_Listen() {
   301  	var hostKey ssh.PublicKey
   302  	config := &ssh.ClientConfig{
   303  		User: "username",
   304  		Auth: []ssh.AuthMethod{
   305  			ssh.Password("password"),
   306  		},
   307  		HostKeyCallback: ssh.FixedHostKey(hostKey),
   308  	}
   309  	// Dial your ssh server.
   310  	conn, err := ssh.Dial("tcp", "localhost:22", config)
   311  	if err != nil {
   312  		log.Fatal("unable to connect: ", err)
   313  	}
   314  	defer conn.Close()
   315  
   316  	// Request the remote side to open port 8080 on all interfaces.
   317  	l, err := conn.Listen("tcp", "0.0.0.0:8080")
   318  	if err != nil {
   319  		log.Fatal("unable to register tcp forward: ", err)
   320  	}
   321  	defer l.Close()
   322  
   323  	// Serve HTTP with your SSH server acting as a reverse proxy.
   324  	http.Serve(l, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
   325  		fmt.Fprintf(resp, "Hello world!\n")
   326  	}))
   327  }
   328  
   329  func ExampleSession_RequestPty() {
   330  	var hostKey ssh.PublicKey
   331  	// Create client config
   332  	config := &ssh.ClientConfig{
   333  		User: "username",
   334  		Auth: []ssh.AuthMethod{
   335  			ssh.Password("password"),
   336  		},
   337  		HostKeyCallback: ssh.FixedHostKey(hostKey),
   338  	}
   339  	// Connect to ssh server
   340  	conn, err := ssh.Dial("tcp", "localhost:22", config)
   341  	if err != nil {
   342  		log.Fatal("unable to connect: ", err)
   343  	}
   344  	defer conn.Close()
   345  	// Create a session
   346  	session, err := conn.NewSession()
   347  	if err != nil {
   348  		log.Fatal("unable to create session: ", err)
   349  	}
   350  	defer session.Close()
   351  	// Set up terminal modes
   352  	modes := ssh.TerminalModes{
   353  		ssh.ECHO:          0,     // disable echoing
   354  		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
   355  		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
   356  	}
   357  	// Request pseudo terminal
   358  	if err := session.RequestPty("xterm", 40, 80, modes); err != nil {
   359  		log.Fatal("request for pseudo terminal failed: ", err)
   360  	}
   361  	// Start remote shell
   362  	if err := session.Shell(); err != nil {
   363  		log.Fatal("failed to start shell: ", err)
   364  	}
   365  }
   366  
   367  func ExampleCertificate_SignCert() {
   368  	// Sign a certificate with a specific algorithm.
   369  	privateKey, err := rsa.GenerateKey(rand.Reader, 3072)
   370  	if err != nil {
   371  		log.Fatal("unable to generate RSA key: ", err)
   372  	}
   373  	publicKey, err := ssh.NewPublicKey(&privateKey.PublicKey)
   374  	if err != nil {
   375  		log.Fatal("unable to get RSA public key: ", err)
   376  	}
   377  	caKey, err := rsa.GenerateKey(rand.Reader, 3072)
   378  	if err != nil {
   379  		log.Fatal("unable to generate CA key: ", err)
   380  	}
   381  	signer, err := ssh.NewSignerFromKey(caKey)
   382  	if err != nil {
   383  		log.Fatal("unable to generate signer from key: ", err)
   384  	}
   385  	mas, err := ssh.NewSignerWithAlgorithms(signer.(ssh.AlgorithmSigner), []string{ssh.KeyAlgoRSASHA256})
   386  	if err != nil {
   387  		log.Fatal("unable to create signer with algoritms: ", err)
   388  	}
   389  	certificate := ssh.Certificate{
   390  		Key:      publicKey,
   391  		CertType: ssh.UserCert,
   392  	}
   393  	if err := certificate.SignCert(rand.Reader, mas); err != nil {
   394  		log.Fatal("unable to sign certificate: ", err)
   395  	}
   396  	// Save the public key to a file and check that rsa-sha-256 is used for
   397  	// signing:
   398  	// ssh-keygen -L -f <path to the file>
   399  	fmt.Println(string(ssh.MarshalAuthorizedKey(&certificate)))
   400  }
   401  

View as plain text