Skip to content

Commit

Permalink
adds SSH key management and refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
xetys committed Jan 24, 2018
1 parent c39536a commit 5975114
Show file tree
Hide file tree
Showing 11 changed files with 400 additions and 4 deletions.
3 changes: 2 additions & 1 deletion cmd/clusterCreate.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package cmd

import (
"github.com/spf13/cobra"
"log"
)

// clusterCreateCmd represents the clusterCreate command
Expand All @@ -29,7 +30,7 @@ Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
cmd.Usage()
log.Fatalln("not implemented!")
},
}

Expand Down
5 changes: 2 additions & 3 deletions cmd/clusterKubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@
package cmd

import (
"fmt"

"github.com/spf13/cobra"
"log"
)

// clusterKubeconfigCmd represents the clusterKubeconfig command
Expand All @@ -31,7 +30,7 @@ Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("clusterKubeconfig called")
log.Fatalln("not implemented!")
},
}

Expand Down
51 changes: 51 additions & 0 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@ type HetznerContext struct {
Name string `json:"name"`
}

type SSHKey struct {
Name string `json:"name"`
PrivateKeyPath string `json:"private_key_path"`
PublicKeyPath string `json:"public_key_path"`
}

type HetznerConfig struct {
ActiveContextName string `json:"active_context_name"`
Contexts []HetznerContext `json:"contexts"`
SSHKeys []SSHKey `json:"ssh_keys"`
}

type AppConfig struct {
Expand Down Expand Up @@ -55,6 +62,33 @@ func (config *HetznerConfig) AddContext(context HetznerContext) {
config.Contexts = append(config.Contexts, context)
}

func (config *HetznerConfig) AddSSHKey(key SSHKey) {
config.SSHKeys = append(config.SSHKeys, key)
}

func (config *HetznerConfig) DeleteSSHKey(name string) error {

index, _ := config.FindSSHKeyByName(name)

if index == -1 {
return errors.New("ssh key not found")
}

config.SSHKeys = append(config.SSHKeys[:index], config.SSHKeys[index+1:]...)

return nil
}
func (config *HetznerConfig) FindSSHKeyByName(name string) (int, *SSHKey) {
index := -1
for i, v := range config.SSHKeys {
if v.Name == name {
index = i
return index, &v
}
}
return index, nil
}

func (app *AppConfig) SwitchContextByName(name string) error {
ctx, err := app.FindContextByName(name)

Expand All @@ -65,6 +99,13 @@ func (app *AppConfig) SwitchContextByName(name string) error {
app.CurrentContext = ctx
app.Config.ActiveContextName = ctx.Name


opts := []hcloud.ClientOption{
hcloud.WithToken(ctx.Token),
}

AppConf.Client = hcloud.NewClient(opts...)

return nil
}

Expand All @@ -80,6 +121,13 @@ func (app *AppConfig) FindContextByName(name string) (*HetznerContext, error) {
return nil, errors.New(fmt.Sprintf("context '%s' not found", name))
}

func (app *AppConfig) assertActiveContext() error {
if app.CurrentContext == nil {
return errors.New("no context selected")
}
return nil
}

func init() {
usr, err := user.Current()
if err != nil {
Expand Down Expand Up @@ -121,5 +169,8 @@ func makeConfigIfNotExists() {
}

json.Unmarshal(configFileContent, &AppConf.Config)
if err := AppConf.SwitchContextByName(AppConf.Config.ActiveContextName); err != nil {
log.Fatal(err)
}
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
47 changes: 47 additions & 0 deletions cmd/sshKey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright © 2018 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"github.com/spf13/cobra"
)

// sshKeyCmd represents the sshKey command
var sshKeyCmd = &cobra.Command{
Use: "ssh-key",
Short: "view and manage SSH keys",
Long: `This sub-command handles both, the public key entry in Hetzner Cloud and private key location of your machine.
Note, that the private key never gets uploaded anywhere. The path is used to connect to the servers`,
Run: func(cmd *cobra.Command, args []string) {
cmd.Usage()
},
}

func init() {
rootCmd.AddCommand(sshKeyCmd)
sshKeyCmd.Flags().StringP("name", "n", "", "")


// Here you will define your flags and configuration settings.

// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// sshKeyCmd.PersistentFlags().String("foo", "", "A help for foo")

// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// sshKeyCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
149 changes: 149 additions & 0 deletions cmd/sshKeyAdd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Copyright © 2018 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"fmt"

"github.com/spf13/cobra"
"errors"
"os"
"github.com/hetznercloud/hcloud-go/hcloud"
"io/ioutil"
"log"
"strings"
"github.com/mitchellh/go-homedir"
)

// sshKeyAddCmd represents the sshKeyAdd command
var sshKeyAddCmd = &cobra.Command{
Use: "add",
Short: "adds a new SSH key to the Hetzner Cloud project and local configuration",
Long: `This sub-command saves the path of the provided SSH private key in a configuration file on your local machine.
Then it uploads it corresponding public key with the provided name to the Hetzner Cloud project, associated by the current context.
Note: the private key is never uploaded to any server at any time.`,
PreRunE: validateFlags,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("sshKeyAdd called")
name, _ := cmd.Flags().GetString("name")
publicKeyPath, _ := cmd.Flags().GetString("public-key-path")
privateKeyPath, _ := cmd.Flags().GetString("private-key-path")

// Find home directory.
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}

privateKeyPath = strings.Replace(privateKeyPath,"~", home, 1)
publicKeyPath = strings.Replace(publicKeyPath,"~", home, 1)

var (
data []byte
)
if publicKeyPath == "-" {
data, err = ioutil.ReadAll(os.Stdin)
} else {
data, err = ioutil.ReadFile(publicKeyPath)
}
if err != nil {
log.Fatalln(err)
}
publicKey := string(data)

opts := hcloud.SSHKeyCreateOpts{
Name: name,
PublicKey: publicKey,
}

context := AppConf.Context
client := AppConf.Client
sshKey, _, err := client.SSHKey.Create(context, opts)


if err != nil {
log.Fatalln(err)
}

AppConf.Config.AddSSHKey(SSHKey{
Name: name,
PrivateKeyPath: privateKeyPath,
PublicKeyPath: publicKeyPath,
})

AppConf.Config.WriteCurrentConfig()

fmt.Printf("SSH key %s(%d) created\n", name, sshKey.ID)
},
}

func validateFlags(cmd *cobra.Command, args []string) error {
if err := AppConf.assertActiveContext(); err != nil {
return err
}

if name, _ := cmd.Flags().GetString("name"); name == "" {
return errors.New("flag --name is required")
}

privateKeyPath, _ := cmd.Flags().GetString("private-key-path")
if privateKeyPath == "" {
return errors.New("flag --private-key-path cannot be empty")
}

publicKeyPath, _ := cmd.Flags().GetString("public-key-path")
if publicKeyPath == "" {
return errors.New("flag --public-key-path cannot be empty")
}

// Find home directory.
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}

privateKeyPath = strings.Replace(privateKeyPath,"~", home, 1)
publicKeyPath = strings.Replace(publicKeyPath,"~", home, 1)
if _, err := os.Stat(privateKeyPath); os.IsNotExist(err) {
return errors.New(fmt.Sprintf("could not find private key '%s'", privateKeyPath))

}

if _, err := os.Stat(publicKeyPath); os.IsNotExist(err) {
return errors.New(fmt.Sprintf("could not find public key '%s'", publicKeyPath))
}

return nil
}

func init() {
sshKeyCmd.AddCommand(sshKeyAddCmd)
sshKeyAddCmd.Flags().StringP("name", "n", "", "the name of the key")
sshKeyAddCmd.Flags().String("private-key-path", "~/.ssh/id_rsa", "the path to the private key")
sshKeyAddCmd.Flags().String("public-key-path", "~/.ssh/id_rsa.pub", "the path to the public key")

// Here you will define your flags and configuration settings.

// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// sshKeyAddCmd.PersistentFlags().String("foo", "", "A help for foo")

// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// sshKeyAddCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
Loading

0 comments on commit 5975114

Please sign in to comment.