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 }