Skip to content

Commit

Permalink
Add support for AWS KMS asymmetric keypair backend
Browse files Browse the repository at this point in the history
  • Loading branch information
syndbg committed Aug 15, 2021
1 parent bf8011c commit 0e2f33d
Show file tree
Hide file tree
Showing 61 changed files with 1,255 additions and 5,545 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ public.pem
/vaulted
/vaulted.exe
/vaulted.exe~
aws-public.pem
2 changes: 1 addition & 1 deletion .go-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.16.0
1.16.4
21 changes: 9 additions & 12 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,21 @@ linters:
- nlreturn
# NOTE: False-positives
- nestif
# NOTE: Doesn't play nice with `stacktrace` pkg
- wrapcheck
# NOTE: More opinionated than useful
- revive
# NOTE: Very bad practice in terms of readability and code consistency.
# Questionable benefit of saving 1 line of code.
- ifshort
issues:
exclude-rules:
- text: "don't use an underscore in package name"
linters:
- revive
- text: "weak cryptographic primitive"
linters:
- gosec
# NOTE: Ignore duplicate false positives
- path: pkg/vaulted/content/legacy_encrypted_content_service.go
- text: "appendAssign: append result not assigned to the same slice"
linters:
- dupl
- gocritic
- path: pkg/vaulted/content/v1_encrypted_content_service.go
linters:
- dupl
Expand All @@ -113,12 +116,6 @@ issues:
- path: cmd/terraform/vault/rekey.go
linters:
- dupl
- path: cmd/terraform/vault/ini.go
linters:
- dupl
- path: cmd/legacy/vault/ini.go
linters:
- dupl
- path: _test\.go
linters:
- gocyclo
Expand Down
17 changes: 11 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,23 @@ Change line format:

### Changed

* Replaced HCLv1 parser with HCLv2 one ; Ref: https://github.com/sumup-oss/vaulted/pull/16
## v0.3.0

### Removed
### Added

* Commands from `legacy` sub-command are now removed. We're not using them internally. The migration from legacy to v1 secret format command(s) are there to help you transition ; Ref: https://github.com/sumup-oss/vaulted/pull/15
* HCLv1 parsing and support for Terraform earlier than 0.12 ; Ref: https://github.com/sumup-oss/vaulted/pull/16

## v0.3.0
* AWS KMS asymmetric keypair encryption & decryption support ; Ref: https://github.com/sumup-oss/vaulted/pull/33

### Changed

* Commands from `terraform` sub-command are now part of `terraform vault`. This is to accommodate for future `terraform X` command where X might be another provider ; Ref: https://github.com/sumup-oss/vaulted/pull/5
* Replaced HCLv1 parser with HCLv2 one ; Ref: https://github.com/sumup-oss/vaulted/pull/16
* Untangled the API implemented by other users of vaulted like terraform providers. Result is now it's easier to implement different strategies, like in the future - GCP KMS support. ; Ref: https://github.com/sumup-oss/vaulted/pull/33

### Removed

* Commands from `legacy` sub-command are now removed. We're not using them internally. The migration from legacy to v1 secret format command(s) are there to help you transition ; Ref: https://github.com/sumup-oss/vaulted/pull/15
* HCLv1 parsing and support for Terraform earlier than 0.12 ; Ref: https://github.com/sumup-oss/vaulted/pull/16
* `ini` commands. Ref: https://github.com/sumup-oss/vaulted/pull/33

## v0.2.1

Expand Down
132 changes: 80 additions & 52 deletions cmd/decrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,47 +15,85 @@
package cmd

import (
"context"
stdRsa "crypto/rsa"
"fmt"

"github.com/palantir/stacktrace"
"github.com/spf13/cobra"
"github.com/sumup-oss/go-pkgs/os"

"github.com/sumup-oss/vaulted/cmd/external_interfaces"
"github.com/sumup-oss/vaulted/internal/cli"
"github.com/sumup-oss/vaulted/pkg/aes"
"github.com/sumup-oss/vaulted/pkg/base64"
"github.com/sumup-oss/vaulted/pkg/pkcs7"
"github.com/sumup-oss/vaulted/pkg/rsa"
"github.com/sumup-oss/vaulted/pkg/aws"
"github.com/sumup-oss/vaulted/pkg/vaulted/content"
"github.com/sumup-oss/vaulted/pkg/vaulted/header"
"github.com/sumup-oss/vaulted/pkg/vaulted/passphrase"
"github.com/sumup-oss/vaulted/pkg/vaulted/payload"
)

// nolint:lll
const decryptExample = `
# Decryption using local RSA asymmetric keypair. This requires the "--in" to have been encrypted using local RSA public key.
> vaulted decrypt --private-key-path ./my-pubkey.pem --in ./mysecret-enc.base64 --out ./mysecret.txt
# Decryption using AWS KMS asymmetric keypair. This requires the "--in" to have been encrypted using local AWS KMS asymmetric public key.
# Make sure to set the correct AWS_REGION and AWS_PROFILE where the AWS KMS key is present.
> AWS_REGION=eu-west-1 AWS_PROFILE=secretprofile vaulted decrypt --aws-kms-id=alias/yourkey --in ./mysecret-enc.base64 --out ./mysecret.txt
`

func NewDecryptCommand(
osExecutor os.OsExecutor,
rsaSvc external_interfaces.RsaService,
b64Svc external_interfaces.Base64Service,
aesSvc external_interfaces.AesService,
) *cobra.Command {
cmdInstance := &cobra.Command{
Use: "decrypt --private-key-path ./private.pem --in ./mysecret-enc.base64 --out ./mysecret.txt",
Short: "Decrypt a file/value",
Long: "Decrypt a file/value using AES-256GCM symmetric encryption. " +
"Passphrase is encrypted with RSA asymmetric keypair.",
Example: decryptExample,
RunE: func(cmdInstance *cobra.Command, args []string) error {
privateKeyPath := cmdInstance.Flag("private-key-path").Value.String()
awsKmsKeyID := cmdInstance.Flag("aws-kms-key-id").Value.String()
awsRegion := cmdInstance.Flag("aws-region").Value.String()

rsaSvc := rsa.NewRsaService(osExecutor)
// NOTE: Read early to avoid needless decryption
privKey, err := rsaSvc.ReadPrivateKeyFromPath(privateKeyPath)
if err != nil {
return stacktrace.Propagate(
err,
"failed to read specified private key",
var privKey *stdRsa.PrivateKey
var err error

var decryptionService *payload.DecryptionService
contentDecrypter := content.NewV1Service(b64Svc, aesSvc)

if privateKeyPath != "" {
privKey, err = rsaSvc.ReadPrivateKeyFromPath(privateKeyPath)
if err != nil {
return stacktrace.Propagate(
err,
"failed to read specified private key",
)
}

decryptionService = payload.NewDecryptionService(
passphrase.NewDecryptionRsaPKCS1v15Service(privKey, rsaSvc),
contentDecrypter,
)
}
} else if awsKmsKeyID != "" {
var awsSvc *aws.Service
awsSvc, err = aws.NewService(context.Background(), awsRegion)
if err != nil {
return stacktrace.Propagate(err, "failed to create aws service")
}

inFilePathArg := cmdInstance.Flag("in").Value.String()
decryptionService = payload.NewDecryptionService(
passphrase.NewDecryptionAwsKmsService(awsSvc, awsKmsKeyID),
contentDecrypter,
)
}

var serializedEncryptedPayload []byte

// NOTE: Read early to avoid needless decryption
inFilePathArg := cmdInstance.Flag("in").Value.String()
if inFilePathArg == "" {
serializedEncryptedPayload, err = cli.ReadFromStdin(
osExecutor,
Expand All @@ -77,35 +115,17 @@ func NewDecryptCommand(
}
}

base64Svc := base64.NewBase64Service()
pkcs7Svc := pkcs7.NewPkcs7Service()
aesSvc := aes.NewAesService(pkcs7Svc)
payloadSerdeSvc := payload.NewSerdeService(b64Svc)

encryptedPassphraseSvc := passphrase.NewEncryptedPassphraseService(
base64Svc,
rsaSvc,
)

encryptedContentSvc := content.NewV1EncryptedContentService(
base64Svc,
aesSvc,
)

encryptedPayloadSvc := payload.NewEncryptedPayloadService(
header.NewHeaderService(),
encryptedPassphraseSvc,
encryptedContentSvc,
)

encryptedPayload, err := encryptedPayloadSvc.Deserialize(serializedEncryptedPayload)
encryptedPayload, err := payloadSerdeSvc.Deserialize(serializedEncryptedPayload)
if err != nil {
return stacktrace.Propagate(
err,
"failed to deserialize base64-encoded encrypted payload",
)
}

payload, err := encryptedPayloadSvc.Decrypt(privKey, encryptedPayload)
payloadInstance, err := decryptionService.Decrypt(encryptedPayload)
if err != nil {
return stacktrace.Propagate(
err,
Expand All @@ -115,25 +135,23 @@ func NewDecryptCommand(

outFilePath := cmdInstance.Flag("out").Value.String()
if outFilePath == "" {
fmt.Fprintln(osExecutor.Stdout(), "Decrypted payload below:")
_, _ = fmt.Fprintln(osExecutor.Stdout(), "Decrypted payload below:")

// NOTE: Explicitly print as string representation
fmt.Fprintln(osExecutor.Stdout(), string(payload.Content.Plaintext))
} else {
err := osExecutor.WriteFile(
outFilePath,
payload.Content.Plaintext,
0644,
)
if err != nil {
return stacktrace.Propagate(
err,
"failed to write decrypted payload",
)
}
_, _ = fmt.Fprintln(osExecutor.Stdout(), string(payloadInstance.Content.Plaintext))

return nil
}

return nil
err = osExecutor.WriteFile(
outFilePath,
payloadInstance.Content.Plaintext,
0644,
)
return stacktrace.Propagate(
err,
"failed to write decrypted payload",
)
},
}

Expand All @@ -142,8 +160,18 @@ func NewDecryptCommand(
"",
"Path to RSA private key used to decrypt encrypted payload.",
)
//nolint:errcheck
cmdInstance.MarkPersistentFlagRequired("private-key-path")

cmdInstance.PersistentFlags().String(
"aws-kms-key-id",
"",
"AWS Asymmetric Customer Managed Key ID",
)

cmdInstance.PersistentFlags().String(
"aws-region",
"",
"AWS Region to use for KMS. Can also be provided by `AWS_REGION` environment variable.",
)

cmdInstance.PersistentFlags().String(
"in",
Expand Down
Loading

0 comments on commit 0e2f33d

Please sign in to comment.