Merge pull request #233 from quexten/feature/import-ssh

Implement ssh import
This commit is contained in:
Bernd Schoolmann 2024-06-02 23:20:03 +02:00 committed by GitHub
commit 9a0ffded83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 189 additions and 4 deletions

View File

@ -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))
} }

View File

@ -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
} }

View File

@ -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)
} }

View File

@ -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{})
} }