github.com/koron/hk@v0.0.0-20150303213137-b8aeaa3ab34c/keys.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"io/ioutil"
     7  	"log"
     8  	"os"
     9  	"os/exec"
    10  	"path/filepath"
    11  	"syscall"
    12  	"text/tabwriter"
    13  
    14  	"github.com/heroku/hk/hkclient"
    15  )
    16  
    17  var (
    18  	sshPubKeyPath string
    19  )
    20  
    21  var cmdKeys = &Command{
    22  	Run:      runKeys,
    23  	Usage:    "keys",
    24  	Category: "account",
    25  	Short:    "list ssh public keys" + extra,
    26  	Long: `
    27  Keys lists SSH public keys associated with your Heroku account.
    28  
    29  Examples:
    30  
    31      $ hk keys
    32      5e:67:40:b6:79:db:56:47:cd:3a:a7:65:ab:ed:12:34  user@test.com
    33  `,
    34  }
    35  
    36  func runKeys(cmd *Command, args []string) {
    37  	if len(args) != 0 {
    38  		cmd.PrintUsage()
    39  		os.Exit(2)
    40  	}
    41  
    42  	keys, err := client.KeyList(nil)
    43  	must(err)
    44  
    45  	w := tabwriter.NewWriter(os.Stdout, 1, 2, 2, ' ', 0)
    46  	defer w.Flush()
    47  
    48  	for i := range keys {
    49  		listRec(w,
    50  			keys[i].Fingerprint,
    51  			keys[i].Email,
    52  		)
    53  	}
    54  }
    55  
    56  var cmdKeyAdd = &Command{
    57  	Run:      runKeyAdd,
    58  	Usage:    "key-add [<public-key-file>]",
    59  	Category: "account",
    60  	Short:    "add ssh public key" + extra,
    61  	Long: `
    62  Command key-add adds an ssh public key to your Heroku account.
    63  
    64  It tries these sources for keys, in order:
    65  
    66  1. public-key-file argument, if present
    67  2. output of ssh-add -L, if any
    68  3. file $HOME/.ssh/id_rsa.pub
    69  `,
    70  }
    71  
    72  func runKeyAdd(cmd *Command, args []string) {
    73  	if len(args) > 1 {
    74  		cmd.PrintUsage()
    75  		os.Exit(2)
    76  	}
    77  	if len(args) == 1 {
    78  		sshPubKeyPath = args[0]
    79  	}
    80  	keys, err := findKeys()
    81  	if err != nil {
    82  		if _, ok := err.(privKeyError); ok {
    83  			log.Println("refusing to upload")
    84  		}
    85  		printFatal(err.Error())
    86  	}
    87  
    88  	key, err := client.KeyCreate(string(keys))
    89  	must(err)
    90  	log.Printf("Key %s for %s added.", abbrev(key.Fingerprint, 15), key.Email)
    91  }
    92  
    93  func findKeys() ([]byte, error) {
    94  	if sshPubKeyPath != "" {
    95  		return sshReadPubKey(sshPubKeyPath)
    96  	}
    97  
    98  	out, err := exec.Command("ssh-add", "-L").Output()
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	if len(out) != 0 {
   103  		print(string(out))
   104  		return out, nil
   105  	}
   106  
   107  	key, err := sshReadPubKey(filepath.Join(hkclient.HomePath(), ".ssh", "id_rsa.pub"))
   108  	switch err {
   109  	case syscall.ENOENT:
   110  		return nil, errors.New("No SSH keys found")
   111  	case nil:
   112  		return key, nil
   113  	}
   114  	return nil, err
   115  }
   116  
   117  func sshReadPubKey(s string) ([]byte, error) {
   118  	f, err := os.Open(filepath.FromSlash(s))
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	key, err := ioutil.ReadAll(f)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  
   128  	if bytes.Contains(key, []byte("PRIVATE")) {
   129  		return nil, privKeyError(s)
   130  	}
   131  
   132  	return key, nil
   133  }
   134  
   135  type privKeyError string
   136  
   137  func (e privKeyError) Error() string {
   138  	return "appears to be a private key: " + string(e)
   139  }
   140  
   141  var cmdKeyRemove = &Command{
   142  	Run:      runKeyRemove,
   143  	Usage:    "key-remove <fingerprint>",
   144  	Category: "account",
   145  	Short:    "remove an ssh public key" + extra,
   146  	Long: `
   147  Command key-remove removes an ssh public key from your Heroku account.
   148  
   149  Examples:
   150  
   151      $ hk key-remove 5e:67:40:b6:79:db:56:47:cd:3a:a7:65:ab:ed:12:34
   152      Key 5e:67:40:b6:79:db… removed.
   153  `,
   154  }
   155  
   156  func runKeyRemove(cmd *Command, args []string) {
   157  	if len(args) != 1 {
   158  		cmd.PrintUsage()
   159  		os.Exit(2)
   160  	}
   161  	fingerprint := args[0]
   162  
   163  	err := client.KeyDelete(fingerprint)
   164  	must(err)
   165  	log.Printf("Key %s removed.", abbrev(fingerprint, 18))
   166  }