From 7a01670397ee3c0ffc1e3c7fb9543862442e878a Mon Sep 17 00:00:00 2001 From: Emmanuel Odeke <1977446+desdeel2d0m@users.noreply.github.com> Date: Sun, 18 Sep 2016 01:01:53 -0700 Subject: [PATCH] x/crypto/ssh: public key authentication example Fixes golang/go#13902. Adds public key authentication to the password authentication example. Change-Id: I4af0ca627fb15b617cc1ba1c6e0954b013f4d94f Reviewed-on: https://go-review.googlesource.com/29374 Reviewed-by: Han-Wen Nienhuys Run-TryBot: Han-Wen Nienhuys TryBot-Result: Gobot Gobot --- ssh/example_test.go | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/ssh/example_test.go b/ssh/example_test.go index 8b8a2fe..5315e45 100644 --- a/ssh/example_test.go +++ b/ssh/example_test.go @@ -17,9 +17,29 @@ import ( ) 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. @@ -28,6 +48,14 @@ func ExampleNewServerConn() { } 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 nil, nil + } + return nil, fmt.Errorf("unknown public key for %q", c.User()) + }, } privateBytes, err := ioutil.ReadFile("id_rsa") @@ -62,6 +90,8 @@ func ExampleNewServerConn() { // The incoming Request channel must be serviced. go ssh.DiscardRequests(reqs) + // Service the incoming Channel channel. + // Service the incoming Channel channel. for newChannel := range chans { // Channels have a type, depending on the application level @@ -74,7 +104,7 @@ func ExampleNewServerConn() { } channel, requests, err := newChannel.Accept() if err != nil { - log.Fatal("could not accept channel: ", err) + log.Fatalf("Could not accept channel: %v", err) } // Sessions have out-of-band requests such as "shell", @@ -82,18 +112,7 @@ func ExampleNewServerConn() { // "shell" request. go func(in <-chan *ssh.Request) { for req := range in { - ok := false - switch req.Type { - case "shell": - ok = true - if len(req.Payload) > 0 { - // We don't accept any - // commands, only the - // default shell. - ok = false - } - } - req.Reply(ok, nil) + req.Reply(req.Type == "shell", nil) } }(requests)