github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/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  	"fmt"
    10  	"io/ioutil"
    11  	"os"
    12  	"path/filepath"
    13  
    14  	"github.com/juju/utils"
    15  
    16  	"github.com/juju/juju/cert"
    17  	"github.com/juju/juju/utils/ssh"
    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  // ReadAuthorizedKeys implements the standard juju behaviour for finding
    28  // authorized_keys. It returns a set of keys in in authorized_keys format
    29  // (see sshd(8) for a description).  If path is non-empty, it names the
    30  // file to use; otherwise the user's .ssh directory will be searched.
    31  // Home directory expansion will be performed on the path if it starts with
    32  // a ~; if the expanded path is relative, it will be interpreted relative
    33  // to $HOME/.ssh.
    34  //
    35  // The result of utils/ssh.PublicKeyFiles will always be prepended to the
    36  // result. In practice, this means ReadAuthorizedKeys never returns an
    37  // error when the call originates in the CLI.
    38  func ReadAuthorizedKeys(path string) (string, error) {
    39  	files := ssh.PublicKeyFiles()
    40  	if path == "" {
    41  		files = append(files, "id_dsa.pub", "id_rsa.pub", "identity.pub")
    42  	} else {
    43  		files = append(files, path)
    44  	}
    45  	var firstError error
    46  	var keyData []byte
    47  	for _, f := range files {
    48  		f, err := utils.NormalizePath(f)
    49  		if err != nil {
    50  			if firstError == nil {
    51  				firstError = err
    52  			}
    53  			continue
    54  		}
    55  		if !filepath.IsAbs(f) {
    56  			f = filepath.Join(utils.Home(), ".ssh", f)
    57  		}
    58  		data, err := ioutil.ReadFile(f)
    59  		if err != nil {
    60  			if firstError == nil && !os.IsNotExist(err) {
    61  				firstError = err
    62  			}
    63  			continue
    64  		}
    65  		keyData = append(keyData, bytes.Trim(data, "\n")...)
    66  		keyData = append(keyData, '\n')
    67  	}
    68  	if len(keyData) == 0 {
    69  		if firstError == nil {
    70  			firstError = fmt.Errorf("no public ssh keys found")
    71  		}
    72  		return "", firstError
    73  	}
    74  	return string(keyData), nil
    75  }
    76  
    77  // verifyKeyPair verifies that the certificate and key parse correctly.
    78  // The key is optional - if it is provided, we also check that the key
    79  // matches the certificate.
    80  func verifyKeyPair(certb, key string) error {
    81  	if key != "" {
    82  		_, err := tls.X509KeyPair([]byte(certb), []byte(key))
    83  		return err
    84  	}
    85  	_, err := cert.ParseCert(certb)
    86  	return err
    87  }
    88  
    89  // ConcatAuthKeys concatenates the two sets of authorised keys, interposing
    90  // a newline if necessary, because authorised keys are newline-separated.
    91  func ConcatAuthKeys(a, b string) string {
    92  	if a == "" {
    93  		return b
    94  	}
    95  	if b == "" {
    96  		return a
    97  	}
    98  	if a[len(a)-1] != '\n' {
    99  		return a + "\n" + b
   100  	}
   101  	return a + b
   102  }