Merge pull request #233 from quexten/feature/import-ssh
Implement ssh import
This commit is contained in:
commit
9a0ffded83
|
@ -16,12 +16,24 @@ import (
|
||||||
func handleAddSSH(msg messages.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext *sockets.CallingContext) (response messages.IPCMessage, err error) {
|
func handleAddSSH(msg messages.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext *sockets.CallingContext) (response messages.IPCMessage, err error) {
|
||||||
req := messages.ParsePayload(msg).(messages.CreateSSHKeyRequest)
|
req := messages.ParsePayload(msg).(messages.CreateSSHKeyRequest)
|
||||||
|
|
||||||
cipher, publicKey := ssh.NewSSHKeyCipher(req.Name, vault.Keyring)
|
cipher, publicKey, err := ssh.NewSSHKeyCipher(req.Name, vault.Keyring)
|
||||||
|
if err != nil {
|
||||||
|
response, err = messages.IPCMessageFromPayload(messages.ActionResponse{
|
||||||
|
Success: false,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
_, err = messages.IPCMessageFromPayload(messages.ActionResponse{
|
_, err = messages.IPCMessageFromPayload(messages.ActionResponse{
|
||||||
Success: true,
|
Success: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
response, err = messages.IPCMessageFromPayload(messages.ActionResponse{
|
||||||
|
Success: false,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := cfg.GetToken()
|
token, err := cfg.GetToken()
|
||||||
|
@ -56,7 +68,49 @@ func handleListSSH(msg messages.IPCMessage, cfg *config.Config, vault *vault.Vau
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleImportSSH(msg messages.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext *sockets.CallingContext) (response messages.IPCMessage, err error) {
|
||||||
|
req := messages.ParsePayload(msg).(messages.ImportSSHKeyRequest)
|
||||||
|
|
||||||
|
cipher, _, err := ssh.SSHKeyCipherFromKey(req.Name, req.Key, vault.Keyring)
|
||||||
|
if err != nil {
|
||||||
|
response, err = messages.IPCMessageFromPayload(messages.ActionResponse{
|
||||||
|
Success: false,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = messages.IPCMessageFromPayload(messages.ActionResponse{
|
||||||
|
Success: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
response, err = messages.IPCMessageFromPayload(messages.ActionResponse{
|
||||||
|
Success: false,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := cfg.GetToken()
|
||||||
|
if err != nil {
|
||||||
|
actionsLog.Warn(err.Error())
|
||||||
|
}
|
||||||
|
ctx := context.WithValue(context.TODO(), bitwarden.AuthToken{}, token.AccessToken)
|
||||||
|
postedCipher, err := bitwarden.PostCipher(ctx, cipher, cfg)
|
||||||
|
if err == nil {
|
||||||
|
vault.AddOrUpdateSecureNote(postedCipher)
|
||||||
|
} else {
|
||||||
|
actionsLog.Warn("Error posting ssh key cipher: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err = messages.IPCMessageFromPayload(messages.ImportSSHKeyResponse{
|
||||||
|
Success: true,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.CreateSSHKeyRequest{}), ensureEverything(systemauth.SSHKey, handleAddSSH))
|
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.CreateSSHKeyRequest{}), ensureEverything(systemauth.SSHKey, handleAddSSH))
|
||||||
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.GetSSHKeysRequest{}), ensureIsNotLocked(ensureIsLoggedIn(handleListSSH)))
|
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.GetSSHKeysRequest{}), ensureIsNotLocked(ensureIsLoggedIn(handleListSSH)))
|
||||||
|
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.ImportSSHKeyRequest{}), ensureEverything(systemauth.SSHKey, handleImportSSH))
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,55 @@ import (
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewSSHKeyCipher(name string, keyring *crypto.Keyring) (models.Cipher, string) {
|
// todo refactor to share code
|
||||||
|
func SSHKeyCipherFromKey(name string, privateKey string, keyring *crypto.Keyring) (models.Cipher, string, error) {
|
||||||
|
signer, err := ssh.ParsePrivateKey([]byte(privateKey))
|
||||||
|
if err != nil {
|
||||||
|
return models.Cipher{}, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey := signer.PublicKey()
|
||||||
|
encryptedName, _ := crypto.EncryptWith([]byte(name), crypto.AesCbc256_HmacSha256_B64, keyring.GetAccountKey())
|
||||||
|
encryptedPublicKeyKey, _ := crypto.EncryptWith([]byte("public-key"), crypto.AesCbc256_HmacSha256_B64, keyring.GetAccountKey())
|
||||||
|
encryptedPublicKeyValue, _ := crypto.EncryptWith([]byte(string(ssh.MarshalAuthorizedKey(pubKey))), crypto.AesCbc256_HmacSha256_B64, keyring.GetAccountKey())
|
||||||
|
encryptedCustomTypeKey, _ := crypto.EncryptWith([]byte("custom-type"), crypto.AesCbc256_HmacSha256_B64, keyring.GetAccountKey())
|
||||||
|
encryptedCustomTypeValue, _ := crypto.EncryptWith([]byte("ssh-key"), crypto.AesCbc256_HmacSha256_B64, keyring.GetAccountKey())
|
||||||
|
encryptedPrivateKeyKey, _ := crypto.EncryptWith([]byte("private-key"), crypto.AesCbc256_HmacSha256_B64, keyring.GetAccountKey())
|
||||||
|
encryptedPrivateKeyValue, _ := crypto.EncryptWith([]byte(privateKey), crypto.AesCbc256_HmacSha256_B64, keyring.GetAccountKey())
|
||||||
|
|
||||||
|
cipher := models.Cipher{
|
||||||
|
Type: models.CipherNote,
|
||||||
|
Name: encryptedName,
|
||||||
|
Notes: &encryptedPublicKeyValue,
|
||||||
|
ID: nil,
|
||||||
|
Favorite: false,
|
||||||
|
OrganizationID: nil,
|
||||||
|
SecureNote: &models.SecureNoteCipher{
|
||||||
|
Type: 0,
|
||||||
|
},
|
||||||
|
Fields: []models.Field{
|
||||||
|
{
|
||||||
|
Type: 0,
|
||||||
|
Name: encryptedCustomTypeKey,
|
||||||
|
Value: encryptedCustomTypeValue,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: 0,
|
||||||
|
Name: encryptedPublicKeyKey,
|
||||||
|
Value: encryptedPublicKeyValue,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: 1,
|
||||||
|
Name: encryptedPrivateKeyKey,
|
||||||
|
Value: encryptedPrivateKeyValue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return cipher, string(ssh.MarshalAuthorizedKey(pubKey)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSSHKeyCipher(name string, keyring *crypto.Keyring) (models.Cipher, string, error) {
|
||||||
var reader io.Reader = rand.Reader
|
var reader io.Reader = rand.Reader
|
||||||
pub, priv, err := ed25519.GenerateKey(reader)
|
pub, priv, err := ed25519.GenerateKey(reader)
|
||||||
|
|
||||||
|
@ -72,5 +119,5 @@ func NewSSHKeyCipher(name string, keyring *crypto.Keyring) (models.Cipher, strin
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return cipher, string(ssh.MarshalAuthorizedKey(publicKey))
|
return cipher, string(ssh.MarshalAuthorizedKey(publicKey)), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
@ -92,6 +93,59 @@ var listSSHCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var importSSHCmd = &cobra.Command{
|
||||||
|
Use: "import",
|
||||||
|
Short: "Imports an SSH key into your vault",
|
||||||
|
Long: `Imports an SSH key into your vault.`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
filename := args[0]
|
||||||
|
fmt.Println("Importing SSH key from " + filename)
|
||||||
|
|
||||||
|
name, _ := cmd.Flags().GetString("name")
|
||||||
|
if name == "" {
|
||||||
|
name = "Imported SSH Key"
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||||
|
fmt.Println("Error: File does not exist")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.ReadFrom(file)
|
||||||
|
key := buf.String()
|
||||||
|
|
||||||
|
result, err := commandClient.SendToAgent(messages.ImportSSHKeyRequest{
|
||||||
|
Key: key,
|
||||||
|
Name: name,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
handleSendToAgentError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch result.(type) {
|
||||||
|
case messages.ImportSSHKeyResponse:
|
||||||
|
response := result.(messages.ImportSSHKeyResponse)
|
||||||
|
if response.Success {
|
||||||
|
fmt.Println("Success")
|
||||||
|
} else {
|
||||||
|
fmt.Println("Error: " + response.ErrorMsg)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case messages.ActionResponse:
|
||||||
|
fmt.Println("Error: " + result.(messages.ActionResponse).Message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(sshCmd)
|
rootCmd.AddCommand(sshCmd)
|
||||||
sshCmd.AddCommand(sshAddCmd)
|
sshCmd.AddCommand(sshAddCmd)
|
||||||
|
@ -99,4 +153,6 @@ func init() {
|
||||||
_ = sshAddCmd.MarkFlagRequired("name")
|
_ = sshAddCmd.MarkFlagRequired("name")
|
||||||
sshAddCmd.PersistentFlags().Bool("clipboard", false, "Copy the public key to the clipboard")
|
sshAddCmd.PersistentFlags().Bool("clipboard", false, "Copy the public key to the clipboard")
|
||||||
sshCmd.AddCommand(listSSHCmd)
|
sshCmd.AddCommand(listSSHCmd)
|
||||||
|
importSSHCmd.PersistentFlags().String("name", "", "")
|
||||||
|
sshCmd.AddCommand(importSSHCmd)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,16 @@ type GetSSHKeysResponse struct {
|
||||||
Keys []string
|
Keys []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ImportSSHKeyRequest struct {
|
||||||
|
Key string
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImportSSHKeyResponse struct {
|
||||||
|
Success bool
|
||||||
|
ErrorMsg string
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registerPayloadParser(func(payload []byte) (interface{}, error) {
|
registerPayloadParser(func(payload []byte) (interface{}, error) {
|
||||||
var req CreateSSHKeyRequest
|
var req CreateSSHKeyRequest
|
||||||
|
@ -53,4 +63,22 @@ func init() {
|
||||||
}
|
}
|
||||||
return req, nil
|
return req, nil
|
||||||
}, GetSSHKeysResponse{})
|
}, GetSSHKeysResponse{})
|
||||||
|
|
||||||
|
registerPayloadParser(func(payload []byte) (interface{}, error) {
|
||||||
|
var req ImportSSHKeyRequest
|
||||||
|
err := json.Unmarshal(payload, &req)
|
||||||
|
if err != nil {
|
||||||
|
panic("Unmarshal: " + err.Error())
|
||||||
|
}
|
||||||
|
return req, nil
|
||||||
|
}, ImportSSHKeyRequest{})
|
||||||
|
|
||||||
|
registerPayloadParser(func(payload []byte) (interface{}, error) {
|
||||||
|
var req ImportSSHKeyResponse
|
||||||
|
err := json.Unmarshal(payload, &req)
|
||||||
|
if err != nil {
|
||||||
|
panic("Unmarshal: " + err.Error())
|
||||||
|
}
|
||||||
|
return req, nil
|
||||||
|
}, ImportSSHKeyResponse{})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue