github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/environs/config/authkeys.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package config 5 6 import ( 7 "bytes" 8 "crypto/tls" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 13 "github.com/juju/errors" 14 "github.com/juju/utils" 15 "github.com/juju/utils/ssh" 16 17 "github.com/juju/juju/cert" 18 ) 19 20 const ( 21 // AuthKeysConfig is the configuration key for authorised keys. 22 AuthKeysConfig = "authorized-keys" 23 // JujuSystemKey is the SSH key comment for Juju system keys. 24 JujuSystemKey = "juju-system-key" 25 ) 26 27 var ErrNoAuthorizedKeys = errors.New("no public ssh keys found") 28 29 // ReadAuthorizedKeys implements the standard juju behaviour for finding 30 // authorized_keys. It returns a set of keys in in authorized_keys format 31 // (see sshd(8) for a description). If path is non-empty, it names the 32 // file to use; otherwise the user's .ssh directory will be searched. 33 // Home directory expansion will be performed on the path if it starts with 34 // a ~; if the expanded path is relative, it will be interpreted relative 35 // to $HOME/.ssh. 36 // 37 // The result of utils/ssh.PublicKeyFiles will always be prepended to the 38 // result. In practice, this means ReadAuthorizedKeys never returns an 39 // error when the call originates in the CLI. 40 // 41 // If no SSH keys are found, ReadAuthorizedKeys returns 42 // ErrNoAuthorizedKeys. 43 func ReadAuthorizedKeys(path string) (string, error) { 44 files := ssh.PublicKeyFiles() 45 if path == "" { 46 files = append(files, "id_dsa.pub", "id_rsa.pub", "identity.pub") 47 } else { 48 files = append(files, path) 49 } 50 var firstError error 51 var keyData []byte 52 for _, f := range files { 53 f, err := utils.NormalizePath(f) 54 if err != nil { 55 if firstError == nil { 56 firstError = err 57 } 58 continue 59 } 60 if !filepath.IsAbs(f) { 61 f = filepath.Join(utils.Home(), ".ssh", f) 62 } 63 data, err := ioutil.ReadFile(f) 64 if err != nil { 65 if firstError == nil && !os.IsNotExist(err) { 66 firstError = err 67 } 68 continue 69 } 70 keyData = append(keyData, bytes.Trim(data, "\n")...) 71 keyData = append(keyData, '\n') 72 } 73 if len(keyData) == 0 { 74 if firstError == nil { 75 firstError = ErrNoAuthorizedKeys 76 } 77 return "", firstError 78 } 79 return string(keyData), nil 80 } 81 82 // verifyKeyPair verifies that the certificate and key parse correctly. 83 // The key is optional - if it is provided, we also check that the key 84 // matches the certificate. 85 func verifyKeyPair(certb, key string) error { 86 if key != "" { 87 _, err := tls.X509KeyPair([]byte(certb), []byte(key)) 88 return err 89 } 90 _, err := cert.ParseCert(certb) 91 return err 92 } 93 94 // ConcatAuthKeys concatenates the two sets of authorised keys, interposing 95 // a newline if necessary, because authorised keys are newline-separated. 96 func ConcatAuthKeys(a, b string) string { 97 if a == "" { 98 return b 99 } 100 if b == "" { 101 return a 102 } 103 if a[len(a)-1] != '\n' { 104 return a + "\n" + b 105 } 106 return a + b 107 }