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 }