|
|
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssh_test
import ( "bufio" "bytes" "fmt" "io/ioutil" "log" "net" "net/http" "os" "path/filepath" "strings"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/terminal" )
func ExampleNewServerConn() { // Public key authentication is done by comparing
// the public key of a received connection
// with the entries in the authorized_keys file.
authorizedKeysBytes, err := ioutil.ReadFile("authorized_keys") if err != nil { log.Fatalf("Failed to load authorized_keys, err: %v", err) }
authorizedKeysMap := map[string]bool{} for len(authorizedKeysBytes) > 0 { pubKey, _, _, rest, err := ssh.ParseAuthorizedKey(authorizedKeysBytes) if err != nil { log.Fatal(err) }
authorizedKeysMap[string(pubKey.Marshal())] = true authorizedKeysBytes = rest }
// An SSH server is represented by a ServerConfig, which holds
// certificate details and handles authentication of ServerConns.
config := &ssh.ServerConfig{ // Remove to disable password auth.
PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { // Should use constant-time compare (or better, salt+hash) in
// a production setting.
if c.User() == "testuser" && string(pass) == "tiger" { return nil, nil } return nil, fmt.Errorf("password rejected for %q", c.User()) },
// Remove to disable public key auth.
PublicKeyCallback: func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) { if authorizedKeysMap[string(pubKey.Marshal())] { return &ssh.Permissions{ // Record the public key used for authentication.
Extensions: map[string]string{ "pubkey-fp": ssh.FingerprintSHA256(pubKey), }, }, nil } return nil, fmt.Errorf("unknown public key for %q", c.User()) }, }
privateBytes, err := ioutil.ReadFile("id_rsa") if err != nil { log.Fatal("Failed to load private key: ", err) }
private, err := ssh.ParsePrivateKey(privateBytes) if err != nil { log.Fatal("Failed to parse private key: ", err) }
config.AddHostKey(private)
// Once a ServerConfig has been configured, connections can be
// accepted.
listener, err := net.Listen("tcp", "0.0.0.0:2022") if err != nil { log.Fatal("failed to listen for connection: ", err) } nConn, err := listener.Accept() if err != nil { log.Fatal("failed to accept incoming connection: ", err) }
// Before use, a handshake must be performed on the incoming
// net.Conn.
conn, chans, reqs, err := ssh.NewServerConn(nConn, config) if err != nil { log.Fatal("failed to handshake: ", err) } log.Printf("logged in with key %s", conn.Permissions.Extensions["pubkey-fp"])
// The incoming Request channel must be serviced.
go ssh.DiscardRequests(reqs)
// Service the incoming Channel channel.
for newChannel := range chans { // Channels have a type, depending on the application level
// protocol intended. In the case of a shell, the type is
// "session" and ServerShell may be used to present a simple
// terminal interface.
if newChannel.ChannelType() != "session" { newChannel.Reject(ssh.UnknownChannelType, "unknown channel type") continue } channel, requests, err := newChannel.Accept() if err != nil { log.Fatalf("Could not accept channel: %v", err) }
// Sessions have out-of-band requests such as "shell",
// "pty-req" and "env". Here we handle only the
// "shell" request.
go func(in <-chan *ssh.Request) { for req := range in { req.Reply(req.Type == "shell", nil) } }(requests)
term := terminal.NewTerminal(channel, "> ")
go func() { defer channel.Close() for { line, err := term.ReadLine() if err != nil { break } fmt.Println(line) } }() } }
func ExampleHostKeyCheck() { // Every client must provide a host key check. Here is a
// simple-minded parse of OpenSSH's known_hosts file
host := "hostname" file, err := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts")) if err != nil { log.Fatal(err) } defer file.Close()
scanner := bufio.NewScanner(file) var hostKey ssh.PublicKey for scanner.Scan() { fields := strings.Split(scanner.Text(), " ") if len(fields) != 3 { continue } if strings.Contains(fields[0], host) { var err error hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes()) if err != nil { log.Fatalf("error parsing %q: %v", fields[2], err) } break } }
if hostKey == nil { log.Fatalf("no hostkey for %s", host) }
config := ssh.ClientConfig{ User: os.Getenv("USER"), HostKeyCallback: ssh.FixedHostKey(hostKey), }
_, err = ssh.Dial("tcp", host+":22", &config) log.Println(err) }
func ExampleDial() { var hostKey ssh.PublicKey // An SSH client is represented with a ClientConn.
//
// To authenticate with the remote server you must pass at least one
// implementation of AuthMethod via the Auth field in ClientConfig,
// and provide a HostKeyCallback.
config := &ssh.ClientConfig{ User: "username", Auth: []ssh.AuthMethod{ ssh.Password("yourpassword"), }, HostKeyCallback: ssh.FixedHostKey(hostKey), } client, err := ssh.Dial("tcp", "yourserver.com:22", config) if err != nil { log.Fatal("Failed to dial: ", err) }
// Each ClientConn can support multiple interactive sessions,
// represented by a Session.
session, err := client.NewSession() if err != nil { log.Fatal("Failed to create session: ", err) } defer session.Close()
// Once a Session is created, you can execute a single command on
// the remote side using the Run method.
var b bytes.Buffer session.Stdout = &b if err := session.Run("/usr/bin/whoami"); err != nil { log.Fatal("Failed to run: " + err.Error()) } fmt.Println(b.String()) }
func ExamplePublicKeys() { var hostKey ssh.PublicKey // A public key may be used to authenticate against the remote
// server by using an unencrypted PEM-encoded private key file.
//
// If you have an encrypted private key, the crypto/x509 package
// can be used to decrypt it.
key, err := ioutil.ReadFile("/home/user/.ssh/id_rsa") if err != nil { log.Fatalf("unable to read private key: %v", err) }
// Create the Signer for this private key.
signer, err := ssh.ParsePrivateKey(key) if err != nil { log.Fatalf("unable to parse private key: %v", err) }
config := &ssh.ClientConfig{ User: "user", Auth: []ssh.AuthMethod{ // Use the PublicKeys method for remote authentication.
ssh.PublicKeys(signer), }, HostKeyCallback: ssh.FixedHostKey(hostKey), }
// Connect to the remote server and perform the SSH handshake.
client, err := ssh.Dial("tcp", "host.com:22", config) if err != nil { log.Fatalf("unable to connect: %v", err) } defer client.Close() }
func ExampleClient_Listen() { var hostKey ssh.PublicKey config := &ssh.ClientConfig{ User: "username", Auth: []ssh.AuthMethod{ ssh.Password("password"), }, HostKeyCallback: ssh.FixedHostKey(hostKey), } // Dial your ssh server.
conn, err := ssh.Dial("tcp", "localhost:22", config) if err != nil { log.Fatal("unable to connect: ", err) } defer conn.Close()
// Request the remote side to open port 8080 on all interfaces.
l, err := conn.Listen("tcp", "0.0.0.0:8080") if err != nil { log.Fatal("unable to register tcp forward: ", err) } defer l.Close()
// Serve HTTP with your SSH server acting as a reverse proxy.
http.Serve(l, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { fmt.Fprintf(resp, "Hello world!\n") })) }
func ExampleSession_RequestPty() { var hostKey ssh.PublicKey // Create client config
config := &ssh.ClientConfig{ User: "username", Auth: []ssh.AuthMethod{ ssh.Password("password"), }, HostKeyCallback: ssh.FixedHostKey(hostKey), } // Connect to ssh server
conn, err := ssh.Dial("tcp", "localhost:22", config) if err != nil { log.Fatal("unable to connect: ", err) } defer conn.Close() // Create a session
session, err := conn.NewSession() if err != nil { log.Fatal("unable to create session: ", err) } defer session.Close() // Set up terminal modes
modes := ssh.TerminalModes{ ssh.ECHO: 0, // disable echoing
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
} // Request pseudo terminal
if err := session.RequestPty("xterm", 40, 80, modes); err != nil { log.Fatal("request for pseudo terminal failed: ", err) } // Start remote shell
if err := session.Shell(); err != nil { log.Fatal("failed to start shell: ", err) } }
|