mirror of
https://github.com/getsops/sops.git
synced 2026-02-05 12:45:21 +01:00
Add support for Azure Key Vault
This commit is contained in:
5
Makefile
5
Makefile
@@ -30,9 +30,12 @@ test:
|
||||
showcoverage: test
|
||||
$(GO) tool cover -html=coverage.out
|
||||
|
||||
generate:
|
||||
generate: keyservice/keyservice.pb.go
|
||||
$(GO) generate
|
||||
|
||||
%.pb.go: %.proto
|
||||
protoc --go_out=plugins=grpc:. $<
|
||||
|
||||
functional-tests:
|
||||
$(GO) build -o functional-tests/sops go.mozilla.org/sops/cmd/sops
|
||||
cd functional-tests && cargo test
|
||||
|
||||
187
azkv/keysource.go
Normal file
187
azkv/keysource.go
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
Package azkv contains an implementation of the go.mozilla.org/sops/keys.MasterKey interface that encrypts and decrypts the
|
||||
data key using Azure Key Vault with the Azure Go SDK.
|
||||
*/
|
||||
package azkv //import "go.mozilla.org/sops/azkv"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mozilla.org/sops/logging"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/services/keyvault/2016-10-01/keyvault"
|
||||
"github.com/Azure/azure-sdk-for-go/services/keyvault/auth"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var log *logrus.Logger
|
||||
|
||||
func init() {
|
||||
log = logging.NewLogger("AZKV")
|
||||
}
|
||||
|
||||
// MasterKey is a Azure Key Vault key used to encrypt and decrypt sops' data key.
|
||||
type MasterKey struct {
|
||||
VaultURL string
|
||||
Name string
|
||||
Version string
|
||||
|
||||
EncryptedKey string
|
||||
CreationDate time.Time
|
||||
}
|
||||
|
||||
func newKeyVaultClient() (keyvault.BaseClient, error) {
|
||||
var err error
|
||||
c := keyvault.New()
|
||||
c.Authorizer, err = auth.NewAuthorizerFromEnvironment()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create Azure authorizer")
|
||||
return c, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// NewMasterKey creates a new MasterKey from an URL, key name and version, setting the creation date to the current date
|
||||
func NewMasterKey(vaultURL string, keyName string, keyVersion string) *MasterKey {
|
||||
return &MasterKey{
|
||||
VaultURL: vaultURL,
|
||||
Name: keyName,
|
||||
Version: keyVersion,
|
||||
CreationDate: time.Now().UTC(),
|
||||
}
|
||||
}
|
||||
|
||||
// MasterKeysFromURLs takes a comma separated list of Azure Key Vault URLs and returns a slice of new MasterKeys for them
|
||||
func MasterKeysFromURLs(urls string) []*MasterKey {
|
||||
var keys []*MasterKey
|
||||
if urls == "" {
|
||||
return keys
|
||||
}
|
||||
for _, s := range strings.Split(urls, ",") {
|
||||
keys = append(keys, NewMasterKeyFromURL(s))
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// NewMasterKeyFromResourceID takes an Azure Key Vault key URL and returns a new MasterKey
|
||||
// URL format is {vaultUrl}/keys/{key-name}/{key-version}
|
||||
func NewMasterKeyFromURL(url string) *MasterKey {
|
||||
k := &MasterKey{}
|
||||
re := regexp.MustCompile("^(https://[^/]+)/keys/([^/]+)/([^/]+)$")
|
||||
parts := re.FindStringSubmatch(url)
|
||||
if parts == nil || len(parts) < 2 {
|
||||
log.Error("No match!")
|
||||
// !?
|
||||
}
|
||||
|
||||
k.VaultURL = parts[1]
|
||||
k.Name = parts[2]
|
||||
k.Version = parts[3]
|
||||
k.CreationDate = time.Now().UTC()
|
||||
return k
|
||||
}
|
||||
|
||||
// EncryptedDataKey returns the encrypted data key this master key holds
|
||||
func (key *MasterKey) EncryptedDataKey() []byte {
|
||||
return []byte(key.EncryptedKey)
|
||||
}
|
||||
|
||||
// SetEncryptedDataKey sets the encrypted data key for this master key
|
||||
func (key *MasterKey) SetEncryptedDataKey(enc []byte) {
|
||||
key.EncryptedKey = string(enc)
|
||||
}
|
||||
|
||||
// Encrypt takes a sops data key, encrypts it with Key Vault and stores the result in the EncryptedKey field
|
||||
func (key *MasterKey) Encrypt(dataKey []byte) error {
|
||||
c, err := newKeyVaultClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data := base64.RawURLEncoding.EncodeToString(dataKey)
|
||||
p := keyvault.KeyOperationsParameters{Value: &data, Algorithm: keyvault.RSAOAEP256}
|
||||
|
||||
res, err := c.Encrypt(context.Background(), key.VaultURL, key.Name, key.Version, p)
|
||||
if err != nil {
|
||||
log.WithError(err).WithFields(logrus.Fields{
|
||||
"key": key.Name,
|
||||
"version": key.Version,
|
||||
}).Error("Encryption failed")
|
||||
return fmt.Errorf("Failed to encrypt data: %v", err)
|
||||
}
|
||||
|
||||
key.EncryptedKey = *res.Result
|
||||
log.WithFields(logrus.Fields{
|
||||
"key": key.Name,
|
||||
"version": key.Version,
|
||||
}).Info("Encryption succeeded")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncryptIfNeeded encrypts the provided sops' data key and encrypts it if it hasn't been encrypted yet
|
||||
func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
|
||||
if key.EncryptedKey == "" {
|
||||
return key.Encrypt(dataKey)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts the EncryptedKey field with Azure Key Vault and returns the result.
|
||||
func (key *MasterKey) Decrypt() ([]byte, error) {
|
||||
c, err := newKeyVaultClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := keyvault.KeyOperationsParameters{Value: &key.EncryptedKey, Algorithm: keyvault.RSAOAEP256}
|
||||
|
||||
res, err := c.Decrypt(context.TODO(), key.VaultURL, key.Name, key.Version, p)
|
||||
if err != nil {
|
||||
log.WithError(err).WithFields(logrus.Fields{
|
||||
"key": key.Name,
|
||||
"version": key.Version,
|
||||
}).Error("Decryption failed")
|
||||
return nil, fmt.Errorf("Error decrypting key: %v", err)
|
||||
}
|
||||
|
||||
plaintext, err := base64.RawURLEncoding.DecodeString(*res.Result)
|
||||
if err != nil {
|
||||
log.WithError(err).WithFields(logrus.Fields{
|
||||
"key": key.Name,
|
||||
"version": key.Version,
|
||||
}).Error("Decryption failed")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"key": key.Name,
|
||||
"version": key.Version,
|
||||
}).Info("Decryption succeeded")
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// NeedsRotation returns whether the data key needs to be rotated or not.
|
||||
func (key *MasterKey) NeedsRotation() bool {
|
||||
return time.Since(key.CreationDate) > (time.Hour * 24 * 30 * 6)
|
||||
}
|
||||
|
||||
// ToString converts the key to a string representation
|
||||
func (key *MasterKey) ToString() string {
|
||||
return fmt.Sprintf("%s/keys/%s/%s", key.VaultURL, key.Name, key.Version)
|
||||
}
|
||||
|
||||
// ToMap converts the MasterKey to a map for serialization purposes
|
||||
func (key MasterKey) ToMap() map[string]interface{} {
|
||||
out := make(map[string]interface{})
|
||||
out["vaultUrl"] = key.VaultURL
|
||||
out["key"] = key.Name
|
||||
out["version"] = key.Version
|
||||
out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339)
|
||||
out["enc"] = key.EncryptedKey
|
||||
return out
|
||||
}
|
||||
102
azkv/keysource_test.go
Normal file
102
azkv/keysource_test.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package azkv
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAzureKeySourceFromUrl(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
input string
|
||||
expectedFoundKeys int
|
||||
expectedKeys []MasterKey
|
||||
}{
|
||||
{
|
||||
name: "Single url",
|
||||
input: "https://test.vault.azure.net/keys/test-key/a2a690a4fcc04166b739da342a912c90",
|
||||
expectedFoundKeys: 1,
|
||||
expectedKeys: []MasterKey{
|
||||
{
|
||||
VaultURL: "https://test.vault.azure.net",
|
||||
Name: "test-key",
|
||||
Version: "a2a690a4fcc04166b739da342a912c90",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple url",
|
||||
input: "https://test.vault.azure.net/keys/test-key/a2a690a4fcc04166b739da342a912c90,https://test2.vault.azure.net/keys/another-test-key/cf0021e8b743453bae758e7fbf71b60e",
|
||||
expectedFoundKeys: 2,
|
||||
expectedKeys: []MasterKey{
|
||||
{
|
||||
VaultURL: "https://test.vault.azure.net",
|
||||
Name: "test-key",
|
||||
Version: "a2a690a4fcc04166b739da342a912c90",
|
||||
},
|
||||
{
|
||||
VaultURL: "https://test2.vault.azure.net",
|
||||
Name: "another-test-key",
|
||||
Version: "cf0021e8b743453bae758e7fbf71b60e",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
keys := MasterKeysFromURLs(c.input)
|
||||
if c.expectedFoundKeys != len(keys) {
|
||||
t.Errorf("Unexpected number of keys returned, expected %d, got %d", c.expectedFoundKeys, len(keys))
|
||||
}
|
||||
for idx := range keys {
|
||||
assert.Equal(t, c.expectedKeys[idx].VaultURL, keys[idx].VaultURL)
|
||||
assert.Equal(t, c.expectedKeys[idx].Name, keys[idx].Name)
|
||||
assert.Equal(t, c.expectedKeys[idx].Version, keys[idx].Version)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyToMap(t *testing.T) {
|
||||
key := MasterKey{
|
||||
CreationDate: time.Date(2016, time.October, 31, 10, 0, 0, 0, time.UTC),
|
||||
VaultURL: "https://test.vault.azure.net",
|
||||
Name: "test-key",
|
||||
Version: "1",
|
||||
EncryptedKey: "this is encrypted",
|
||||
}
|
||||
assert.Equal(t, map[string]interface{}{
|
||||
"vaultUrl": key.VaultURL,
|
||||
"key": key.Name,
|
||||
"version": key.Version,
|
||||
"enc": "this is encrypted",
|
||||
"created_at": "2016-10-31T10:00:00Z",
|
||||
}, key.ToMap())
|
||||
}
|
||||
|
||||
var azureKeyAcceptanceTestUrl = flag.String("azure-key", "", "URL to Azure Key Vault (note that this can incur real costs!)")
|
||||
|
||||
func TestRoundtrip(t *testing.T) {
|
||||
if *azureKeyAcceptanceTestUrl == "" {
|
||||
t.Skip("Azure URL not provided, skipping acceptance test")
|
||||
}
|
||||
|
||||
input := []byte("test-string")
|
||||
|
||||
key := NewMasterKeyFromURL(*azureKeyAcceptanceTestUrl)
|
||||
err := key.Encrypt(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
output, err := key.Decrypt()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, input, output)
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.mozilla.org/sops/aes"
|
||||
_ "go.mozilla.org/sops/audit"
|
||||
"go.mozilla.org/sops/azkv"
|
||||
"go.mozilla.org/sops/cmd/sops/codes"
|
||||
"go.mozilla.org/sops/cmd/sops/common"
|
||||
"go.mozilla.org/sops/cmd/sops/subcommand/groups"
|
||||
@@ -59,7 +60,7 @@ func main() {
|
||||
},
|
||||
}
|
||||
app.Name = "sops"
|
||||
app.Usage = "sops - encrypted file editor with AWS KMS, GCP KMS and GPG support"
|
||||
app.Usage = "sops - encrypted file editor with AWS KMS, GCP KMS, Azure Key Vault and GPG support"
|
||||
app.ArgsUsage = "sops [options] file"
|
||||
app.Version = version
|
||||
app.Authors = []cli.Author{
|
||||
@@ -78,6 +79,10 @@ func main() {
|
||||
(you need to setup google application default credentials. See
|
||||
https://developers.google.com/identity/protocols/application-default-credentials)
|
||||
|
||||
To encrypt or decrypt a document with Azure Key Vault, specify the
|
||||
Azure Key Vault key URL in the --azure-kv flag or in the SOPS_AZURE_KEYVAULT_URL
|
||||
environment variable.
|
||||
|
||||
To encrypt or decrypt using PGP, specify the PGP fingerprint in the
|
||||
-p flag or in the SOPS_PGP_FP environment variable.
|
||||
|
||||
@@ -88,7 +93,7 @@ func main() {
|
||||
or decrypting existing documents can be done with "sops file" or
|
||||
"sops -d file" respectively. The KMS and PGP keys listed in the encrypted
|
||||
documents are used then. To manage master keys in existing documents, use
|
||||
the "add-{kms,pgp,gcp-kms}" and "rm-{kms,pgp,gcp-kms}" flags.
|
||||
the "add-{kms,pgp,gcp-kms,azure-kv}" and "rm-{kms,pgp,gcp-kms,azure-kv}" flags.
|
||||
|
||||
To use a different GPG binary than the one in your PATH, set SOPS_GPG_EXEC.
|
||||
|
||||
@@ -160,6 +165,10 @@ func main() {
|
||||
Name: "gcp-kms",
|
||||
Usage: "the GCP KMS Resource ID the new group should contain. Can be specified more than once",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "azure-kv",
|
||||
Usage: "the Azure Key Vault key URL the new group should contain. Can be specified more than once",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "in-place, i",
|
||||
Usage: "write output back to the same file instead of stdout",
|
||||
@@ -176,6 +185,7 @@ func main() {
|
||||
Action: func(c *cli.Context) error {
|
||||
pgpFps := c.StringSlice("pgp")
|
||||
kmsArns := c.StringSlice("kms")
|
||||
azkvs := c.StringSlice("azkv")
|
||||
var group sops.KeyGroup
|
||||
for _, fp := range pgpFps {
|
||||
group = append(group, pgp.NewMasterKeyFromFingerprint(fp))
|
||||
@@ -183,6 +193,10 @@ func main() {
|
||||
for _, arn := range kmsArns {
|
||||
group = append(group, kms.NewMasterKeyFromArn(arn, kms.ParseKMSContext(c.String("encryption-context"))))
|
||||
}
|
||||
// NOTE: Why isn't GCP here?
|
||||
for _, url := range azkvs {
|
||||
group = append(group, azkv.NewMasterKeyFromURL(url))
|
||||
}
|
||||
return groups.Add(groups.AddOpts{
|
||||
InputPath: c.String("file"),
|
||||
InPlace: c.Bool("in-place"),
|
||||
@@ -288,6 +302,11 @@ func main() {
|
||||
Usage: "comma separated list of GCP KMS resource IDs",
|
||||
EnvVar: "SOPS_GCP_KMS_IDS",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "azure-kv",
|
||||
Usage: "comma separated list of Azure Key Vault URLs",
|
||||
EnvVar: "SOPS_AZURE_KEYVAULT_URLS",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "pgp, p",
|
||||
Usage: "comma separated list of PGP fingerprints",
|
||||
@@ -321,6 +340,14 @@ func main() {
|
||||
Name: "rm-gcp-kms",
|
||||
Usage: "remove the provided comma-separated list of GCP KMS key resource IDs from the list of master keys on the given file",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "add-azure-kv",
|
||||
Usage: "add the provided comma-separated list of Azure Key Vault key URLs to the list of master keys on the given file",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "rm-azure-kv",
|
||||
Usage: "remove the provided comma-separated list of Azure Key Vault key URLs from the list of master keys on the given file",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "add-kms",
|
||||
Usage: "add the provided comma-separated list of KMS ARNs to the list of master keys on the given file",
|
||||
@@ -380,7 +407,8 @@ func main() {
|
||||
}
|
||||
fileName := c.Args()[0]
|
||||
if _, err := os.Stat(fileName); os.IsNotExist(err) {
|
||||
if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" {
|
||||
if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-azure-kv") != "" ||
|
||||
c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-azure-kv") != "" {
|
||||
return common.NewExitError("Error: cannot add or remove keys on non-existent files, use `--kms` and `--pgp` instead.", codes.CannotChangeKeysFromNonExistentFile)
|
||||
}
|
||||
if c.Bool("encrypt") || c.Bool("decrypt") || c.Bool("rotate") {
|
||||
@@ -468,6 +496,9 @@ func main() {
|
||||
for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String("add-gcp-kms")) {
|
||||
addMasterKeys = append(addMasterKeys, k)
|
||||
}
|
||||
for _, k := range azkv.MasterKeysFromURLs(c.String("add-azure-kv")) {
|
||||
addMasterKeys = append(addMasterKeys, k)
|
||||
}
|
||||
|
||||
var rmMasterKeys []keys.MasterKey
|
||||
for _, k := range kms.MasterKeysFromArnString(c.String("rm-kms"), kmsEncryptionContext) {
|
||||
@@ -479,6 +510,9 @@ func main() {
|
||||
for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String("rm-gcp-kms")) {
|
||||
rmMasterKeys = append(rmMasterKeys, k)
|
||||
}
|
||||
for _, k := range azkv.MasterKeysFromURLs(c.String("rm-azure-kv")) {
|
||||
rmMasterKeys = append(rmMasterKeys, k)
|
||||
}
|
||||
output, err = rotate(rotateOpts{
|
||||
OutputStore: outputStore,
|
||||
InputStore: inputStore,
|
||||
@@ -673,6 +707,7 @@ func keyGroups(c *cli.Context, file string) ([]sops.KeyGroup, error) {
|
||||
var kmsKeys []keys.MasterKey
|
||||
var pgpKeys []keys.MasterKey
|
||||
var cloudKmsKeys []keys.MasterKey
|
||||
var azkvKeys []keys.MasterKey
|
||||
kmsEncryptionContext := kms.ParseKMSContext(c.String("encryption-context"))
|
||||
if c.String("encryption-context") != "" && kmsEncryptionContext == nil {
|
||||
return nil, common.NewExitError("Invalid KMS encryption context format", codes.ErrorInvalidKMSEncryptionContextFormat)
|
||||
@@ -687,12 +722,17 @@ func keyGroups(c *cli.Context, file string) ([]sops.KeyGroup, error) {
|
||||
cloudKmsKeys = append(cloudKmsKeys, k)
|
||||
}
|
||||
}
|
||||
if c.String("azure-kv") != "" {
|
||||
for _, k := range azkv.MasterKeysFromURLs(c.String("azure-kv")) {
|
||||
azkvKeys = append(azkvKeys, k)
|
||||
}
|
||||
}
|
||||
if c.String("pgp") != "" {
|
||||
for _, k := range pgp.MasterKeysFromFingerprintString(c.String("pgp")) {
|
||||
pgpKeys = append(pgpKeys, k)
|
||||
}
|
||||
}
|
||||
if c.String("kms") == "" && c.String("pgp") == "" && c.String("gcp-kms") == "" {
|
||||
if c.String("kms") == "" && c.String("pgp") == "" && c.String("gcp-kms") == "" && c.String("azure-kv") == "" {
|
||||
conf, err := loadConfig(c, file, kmsEncryptionContext)
|
||||
// config file might just not be supplied, without any error
|
||||
if conf == nil {
|
||||
@@ -707,6 +747,7 @@ func keyGroups(c *cli.Context, file string) ([]sops.KeyGroup, error) {
|
||||
var group sops.KeyGroup
|
||||
group = append(group, kmsKeys...)
|
||||
group = append(group, cloudKmsKeys...)
|
||||
group = append(group, azkvKeys...)
|
||||
group = append(group, pgpKeys...)
|
||||
return []sops.KeyGroup{group}, nil
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/mozilla-services/yaml"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.mozilla.org/sops"
|
||||
"go.mozilla.org/sops/azkv"
|
||||
"go.mozilla.org/sops/gcpkms"
|
||||
"go.mozilla.org/sops/kms"
|
||||
"go.mozilla.org/sops/logging"
|
||||
@@ -63,9 +64,10 @@ type configFile struct {
|
||||
}
|
||||
|
||||
type keyGroup struct {
|
||||
KMS []kmsKey
|
||||
GCPKMS []gcpKmsKey `yaml:"gcp_kms"`
|
||||
PGP []string
|
||||
KMS []kmsKey
|
||||
GCPKMS []gcpKmsKey `yaml:"gcp_kms"`
|
||||
AzureKV []azureKVKey `yaml:"azure_keyvault"`
|
||||
PGP []string
|
||||
}
|
||||
|
||||
type gcpKmsKey struct {
|
||||
@@ -78,12 +80,19 @@ type kmsKey struct {
|
||||
Context map[string]*string `yaml:"context"`
|
||||
}
|
||||
|
||||
type azureKVKey struct {
|
||||
VaultURL string `yaml:"vaultUrl"`
|
||||
Key string `yaml:"key"`
|
||||
Version string `yaml:"version"`
|
||||
}
|
||||
|
||||
type creationRule struct {
|
||||
FilenameRegex string `yaml:"filename_regex"`
|
||||
PathRegex string `yaml:"path_regex"`
|
||||
KMS string
|
||||
PGP string
|
||||
GCPKMS string `yaml:"gcp_kms"`
|
||||
AzureKeyVault string `yaml:"azure_keyvault"`
|
||||
KeyGroups []keyGroup `yaml:"key_groups"`
|
||||
ShamirThreshold int `yaml:"shamir_threshold"`
|
||||
UnencryptedSuffix string `yaml:"unencrypted_suffix"`
|
||||
@@ -172,6 +181,9 @@ func loadForFileFromBytes(confBytes []byte, filePath string, kmsEncryptionContex
|
||||
for _, k := range gcpkms.MasterKeysFromResourceIDString(rule.GCPKMS) {
|
||||
keyGroup = append(keyGroup, k)
|
||||
}
|
||||
for _, k := range azkv.MasterKeysFromURLs(rule.AzureKeyVault) {
|
||||
keyGroup = append(keyGroup, k)
|
||||
}
|
||||
groups = append(groups, keyGroup)
|
||||
}
|
||||
return &Config{
|
||||
|
||||
@@ -7,6 +7,7 @@ package keyservice
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.mozilla.org/sops/azkv"
|
||||
"go.mozilla.org/sops/gcpkms"
|
||||
"go.mozilla.org/sops/keys"
|
||||
"go.mozilla.org/sops/kms"
|
||||
@@ -46,6 +47,16 @@ func KeyFromMasterKey(mk keys.MasterKey) Key {
|
||||
},
|
||||
},
|
||||
}
|
||||
case *azkv.MasterKey:
|
||||
return Key{
|
||||
KeyType: &Key_AzureKeyvaultKey{
|
||||
AzureKeyvaultKey: &AzureKeyVaultKey{
|
||||
VaultUrl: mk.VaultURL,
|
||||
Name: mk.Name,
|
||||
Version: mk.Version,
|
||||
},
|
||||
},
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("Tried to convert unknown MasterKey type %T to keyservice.Key", mk))
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: keyservice.proto
|
||||
// source: keyservice/keyservice.proto
|
||||
|
||||
/*
|
||||
Package keyservice is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
keyservice.proto
|
||||
keyservice/keyservice.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Key
|
||||
PgpKey
|
||||
KmsKey
|
||||
GcpKmsKey
|
||||
AzureKeyVaultKey
|
||||
EncryptRequest
|
||||
EncryptResponse
|
||||
DecryptRequest
|
||||
@@ -44,6 +45,7 @@ type Key struct {
|
||||
// *Key_KmsKey
|
||||
// *Key_PgpKey
|
||||
// *Key_GcpKmsKey
|
||||
// *Key_AzureKeyvaultKey
|
||||
KeyType isKey_KeyType `protobuf_oneof:"key_type"`
|
||||
}
|
||||
|
||||
@@ -65,10 +67,14 @@ type Key_PgpKey struct {
|
||||
type Key_GcpKmsKey struct {
|
||||
GcpKmsKey *GcpKmsKey `protobuf:"bytes,3,opt,name=gcp_kms_key,json=gcpKmsKey,oneof"`
|
||||
}
|
||||
type Key_AzureKeyvaultKey struct {
|
||||
AzureKeyvaultKey *AzureKeyVaultKey `protobuf:"bytes,4,opt,name=azure_keyvault_key,json=azureKeyvaultKey,oneof"`
|
||||
}
|
||||
|
||||
func (*Key_KmsKey) isKey_KeyType() {}
|
||||
func (*Key_PgpKey) isKey_KeyType() {}
|
||||
func (*Key_GcpKmsKey) isKey_KeyType() {}
|
||||
func (*Key_KmsKey) isKey_KeyType() {}
|
||||
func (*Key_PgpKey) isKey_KeyType() {}
|
||||
func (*Key_GcpKmsKey) isKey_KeyType() {}
|
||||
func (*Key_AzureKeyvaultKey) isKey_KeyType() {}
|
||||
|
||||
func (m *Key) GetKeyType() isKey_KeyType {
|
||||
if m != nil {
|
||||
@@ -98,12 +104,20 @@ func (m *Key) GetGcpKmsKey() *GcpKmsKey {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Key) GetAzureKeyvaultKey() *AzureKeyVaultKey {
|
||||
if x, ok := m.GetKeyType().(*Key_AzureKeyvaultKey); ok {
|
||||
return x.AzureKeyvaultKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// XXX_OneofFuncs is for the internal use of the proto package.
|
||||
func (*Key) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
|
||||
return _Key_OneofMarshaler, _Key_OneofUnmarshaler, _Key_OneofSizer, []interface{}{
|
||||
(*Key_KmsKey)(nil),
|
||||
(*Key_PgpKey)(nil),
|
||||
(*Key_GcpKmsKey)(nil),
|
||||
(*Key_AzureKeyvaultKey)(nil),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +140,11 @@ func _Key_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
|
||||
if err := b.EncodeMessage(x.GcpKmsKey); err != nil {
|
||||
return err
|
||||
}
|
||||
case *Key_AzureKeyvaultKey:
|
||||
b.EncodeVarint(4<<3 | proto.WireBytes)
|
||||
if err := b.EncodeMessage(x.AzureKeyvaultKey); err != nil {
|
||||
return err
|
||||
}
|
||||
case nil:
|
||||
default:
|
||||
return fmt.Errorf("Key.KeyType has unexpected type %T", x)
|
||||
@@ -160,6 +179,14 @@ func _Key_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (b
|
||||
err := b.DecodeMessage(msg)
|
||||
m.KeyType = &Key_GcpKmsKey{msg}
|
||||
return true, err
|
||||
case 4: // key_type.azure_keyvault_key
|
||||
if wire != proto.WireBytes {
|
||||
return true, proto.ErrInternalBadWireType
|
||||
}
|
||||
msg := new(AzureKeyVaultKey)
|
||||
err := b.DecodeMessage(msg)
|
||||
m.KeyType = &Key_AzureKeyvaultKey{msg}
|
||||
return true, err
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
@@ -184,6 +211,11 @@ func _Key_OneofSizer(msg proto.Message) (n int) {
|
||||
n += proto.SizeVarint(3<<3 | proto.WireBytes)
|
||||
n += proto.SizeVarint(uint64(s))
|
||||
n += s
|
||||
case *Key_AzureKeyvaultKey:
|
||||
s := proto.Size(x.AzureKeyvaultKey)
|
||||
n += proto.SizeVarint(4<<3 | proto.WireBytes)
|
||||
n += proto.SizeVarint(uint64(s))
|
||||
n += s
|
||||
case nil:
|
||||
default:
|
||||
panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
|
||||
@@ -255,6 +287,38 @@ func (m *GcpKmsKey) GetResourceId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type AzureKeyVaultKey struct {
|
||||
VaultUrl string `protobuf:"bytes,1,opt,name=vault_url,json=vaultUrl" json:"vault_url,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"`
|
||||
Version string `protobuf:"bytes,3,opt,name=version" json:"version,omitempty"`
|
||||
}
|
||||
|
||||
func (m *AzureKeyVaultKey) Reset() { *m = AzureKeyVaultKey{} }
|
||||
func (m *AzureKeyVaultKey) String() string { return proto.CompactTextString(m) }
|
||||
func (*AzureKeyVaultKey) ProtoMessage() {}
|
||||
func (*AzureKeyVaultKey) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
|
||||
|
||||
func (m *AzureKeyVaultKey) GetVaultUrl() string {
|
||||
if m != nil {
|
||||
return m.VaultUrl
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *AzureKeyVaultKey) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *AzureKeyVaultKey) GetVersion() string {
|
||||
if m != nil {
|
||||
return m.Version
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type EncryptRequest struct {
|
||||
Key *Key `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"`
|
||||
Plaintext []byte `protobuf:"bytes,2,opt,name=plaintext,proto3" json:"plaintext,omitempty"`
|
||||
@@ -263,7 +327,7 @@ type EncryptRequest struct {
|
||||
func (m *EncryptRequest) Reset() { *m = EncryptRequest{} }
|
||||
func (m *EncryptRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*EncryptRequest) ProtoMessage() {}
|
||||
func (*EncryptRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
|
||||
func (*EncryptRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
|
||||
|
||||
func (m *EncryptRequest) GetKey() *Key {
|
||||
if m != nil {
|
||||
@@ -286,7 +350,7 @@ type EncryptResponse struct {
|
||||
func (m *EncryptResponse) Reset() { *m = EncryptResponse{} }
|
||||
func (m *EncryptResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*EncryptResponse) ProtoMessage() {}
|
||||
func (*EncryptResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
|
||||
func (*EncryptResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
|
||||
|
||||
func (m *EncryptResponse) GetCiphertext() []byte {
|
||||
if m != nil {
|
||||
@@ -303,7 +367,7 @@ type DecryptRequest struct {
|
||||
func (m *DecryptRequest) Reset() { *m = DecryptRequest{} }
|
||||
func (m *DecryptRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*DecryptRequest) ProtoMessage() {}
|
||||
func (*DecryptRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
|
||||
func (*DecryptRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
|
||||
|
||||
func (m *DecryptRequest) GetKey() *Key {
|
||||
if m != nil {
|
||||
@@ -326,7 +390,7 @@ type DecryptResponse struct {
|
||||
func (m *DecryptResponse) Reset() { *m = DecryptResponse{} }
|
||||
func (m *DecryptResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*DecryptResponse) ProtoMessage() {}
|
||||
func (*DecryptResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
|
||||
func (*DecryptResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
|
||||
|
||||
func (m *DecryptResponse) GetPlaintext() []byte {
|
||||
if m != nil {
|
||||
@@ -340,6 +404,7 @@ func init() {
|
||||
proto.RegisterType((*PgpKey)(nil), "PgpKey")
|
||||
proto.RegisterType((*KmsKey)(nil), "KmsKey")
|
||||
proto.RegisterType((*GcpKmsKey)(nil), "GcpKmsKey")
|
||||
proto.RegisterType((*AzureKeyVaultKey)(nil), "AzureKeyVaultKey")
|
||||
proto.RegisterType((*EncryptRequest)(nil), "EncryptRequest")
|
||||
proto.RegisterType((*EncryptResponse)(nil), "EncryptResponse")
|
||||
proto.RegisterType((*DecryptRequest)(nil), "DecryptRequest")
|
||||
@@ -448,37 +513,42 @@ var _KeyService_serviceDesc = grpc.ServiceDesc{
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "keyservice.proto",
|
||||
Metadata: "keyservice/keyservice.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("keyservice.proto", fileDescriptor0) }
|
||||
func init() { proto.RegisterFile("keyservice/keyservice.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 405 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0xcd, 0xae, 0x93, 0x40,
|
||||
0x14, 0x2e, 0xe5, 0x4a, 0xe5, 0x70, 0x73, 0x21, 0x93, 0x1b, 0x73, 0xd3, 0x18, 0x6d, 0x66, 0xd5,
|
||||
0x98, 0x66, 0x8c, 0xb8, 0x31, 0x5d, 0xaa, 0xd5, 0x1a, 0x36, 0x06, 0x1f, 0x80, 0x54, 0x7a, 0x44,
|
||||
0x02, 0x85, 0x71, 0x98, 0x36, 0xce, 0x13, 0xf8, 0x0e, 0x3e, 0xad, 0x61, 0x18, 0x68, 0xa9, 0x9b,
|
||||
0xbb, 0x3b, 0xf3, 0xf1, 0xcd, 0xf7, 0x33, 0x1c, 0x08, 0x0a, 0x54, 0x0d, 0x8a, 0x53, 0x9e, 0x22,
|
||||
0xe3, 0xa2, 0x96, 0x35, 0xfd, 0x63, 0x81, 0x1d, 0xa1, 0x22, 0x14, 0x66, 0xc5, 0xa1, 0x49, 0x0a,
|
||||
0x54, 0x0f, 0xd6, 0xc2, 0x5a, 0x7a, 0xe1, 0x8c, 0x45, 0x87, 0x26, 0x42, 0xb5, 0x9d, 0xc4, 0x4e,
|
||||
0xa1, 0xa7, 0x96, 0xc3, 0x33, 0xae, 0x39, 0x53, 0xc3, 0xf9, 0x9a, 0x71, 0xc3, 0xe1, 0x7a, 0x22,
|
||||
0x2b, 0xf0, 0xb2, 0x94, 0x27, 0xbd, 0x96, 0xad, 0x79, 0xc0, 0x3e, 0xa7, 0x7c, 0x90, 0x73, 0xb3,
|
||||
0xfe, 0xf0, 0x1e, 0xe0, 0x69, 0x81, 0x2a, 0x91, 0x8a, 0x23, 0x7d, 0x05, 0x4e, 0xa7, 0x46, 0x16,
|
||||
0xe0, 0xfd, 0xc8, 0xab, 0x0c, 0x05, 0x17, 0x79, 0x25, 0x75, 0x1e, 0x37, 0xbe, 0x84, 0xe8, 0x5f,
|
||||
0x0b, 0x9c, 0x4e, 0x82, 0x04, 0x60, 0xef, 0x44, 0x65, 0x48, 0xed, 0x48, 0x08, 0xdc, 0x88, 0xba,
|
||||
0x44, 0x9d, 0xd1, 0x8d, 0xf5, 0x4c, 0x18, 0xcc, 0xd2, 0xba, 0x92, 0xf8, 0x5b, 0x3e, 0xd8, 0x0b,
|
||||
0x7b, 0xe9, 0x85, 0xf7, 0xa6, 0x1e, 0xfb, 0xd0, 0xc1, 0x9b, 0x4a, 0x0a, 0x15, 0xf7, 0xa4, 0xf9,
|
||||
0x1a, 0x6e, 0x2f, 0x3f, 0xb4, 0x2e, 0xfd, 0xd3, 0xb8, 0x71, 0x3b, 0x92, 0x7b, 0x78, 0x72, 0xda,
|
||||
0x95, 0xc7, 0xde, 0xa6, 0x3b, 0xac, 0xa7, 0xef, 0x2c, 0xba, 0x02, 0x77, 0xa8, 0x4b, 0x5e, 0x82,
|
||||
0x27, 0xb0, 0xa9, 0x8f, 0x22, 0xc5, 0x24, 0xdf, 0x1b, 0x01, 0xe8, 0xa1, 0x2f, 0x7b, 0xfa, 0x09,
|
||||
0xee, 0x36, 0x55, 0x2a, 0x14, 0x97, 0x31, 0xfe, 0x3a, 0x62, 0x23, 0xc9, 0xb3, 0xb3, 0x97, 0x17,
|
||||
0xde, 0xb0, 0x08, 0x55, 0xe7, 0xf8, 0x1c, 0x5c, 0x5e, 0xee, 0xf2, 0xae, 0x45, 0xeb, 0x7a, 0x1b,
|
||||
0x9f, 0x01, 0xfa, 0x06, 0xfc, 0x41, 0xa7, 0xe1, 0x75, 0xd5, 0x20, 0x79, 0x01, 0x90, 0xe6, 0xfc,
|
||||
0x27, 0x0a, 0x7d, 0xc3, 0xd2, 0x37, 0x2e, 0x10, 0xba, 0x85, 0xbb, 0x8f, 0xf8, 0x28, 0xeb, 0xb1,
|
||||
0xd2, 0xf4, 0x3f, 0xa5, 0xd7, 0xe0, 0x0f, 0x4a, 0xc6, 0x7c, 0x94, 0xd6, 0xba, 0x4a, 0x1b, 0x96,
|
||||
0x00, 0x11, 0xaa, 0x6f, 0xdd, 0x2a, 0xb6, 0x7f, 0xc7, 0x64, 0x27, 0x3e, 0x1b, 0xbf, 0xc6, 0x3c,
|
||||
0x60, 0x57, 0xb5, 0xe8, 0xa4, 0xe5, 0x1b, 0x3b, 0xe2, 0xb3, 0x71, 0x85, 0x79, 0xc0, 0xae, 0x92,
|
||||
0xd0, 0xc9, 0x77, 0x47, 0xef, 0xfa, 0xdb, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa7, 0xb0, 0x93,
|
||||
0xf5, 0xff, 0x02, 0x00, 0x00,
|
||||
// 485 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0x4d, 0x6f, 0xd4, 0x30,
|
||||
0x10, 0x6d, 0x36, 0xcb, 0x6e, 0x33, 0xa9, 0xba, 0xc1, 0xaa, 0xd0, 0x6a, 0x8b, 0x60, 0xe5, 0x53,
|
||||
0x85, 0x2a, 0x57, 0x2c, 0x17, 0xd4, 0x5b, 0x81, 0x42, 0x51, 0x2e, 0x28, 0x08, 0x6e, 0x68, 0x15,
|
||||
0xd2, 0x21, 0x44, 0xc9, 0x26, 0xc6, 0x71, 0x22, 0xcc, 0x4f, 0xe1, 0x27, 0xf1, 0xab, 0x90, 0x1d,
|
||||
0x27, 0xfb, 0xc1, 0x85, 0xdb, 0xf8, 0xf9, 0xcd, 0x9b, 0x79, 0xcf, 0x32, 0x9c, 0xe7, 0xa8, 0x6a,
|
||||
0x14, 0x6d, 0x96, 0xe0, 0xd5, 0xb6, 0x64, 0x5c, 0x54, 0xb2, 0xa2, 0x7f, 0x1c, 0x70, 0x43, 0x54,
|
||||
0x84, 0xc2, 0x34, 0xdf, 0xd4, 0xeb, 0x1c, 0xd5, 0xdc, 0x59, 0x3a, 0x17, 0xfe, 0x6a, 0xca, 0xc2,
|
||||
0x4d, 0x1d, 0xa2, 0xba, 0x3b, 0x8a, 0x26, 0xb9, 0xa9, 0x34, 0x87, 0xa7, 0xdc, 0x70, 0x46, 0x96,
|
||||
0xf3, 0x21, 0xe5, 0x96, 0xc3, 0x4d, 0x45, 0x2e, 0xc1, 0x4f, 0x13, 0xbe, 0xee, 0xb5, 0x5c, 0xc3,
|
||||
0x03, 0xf6, 0x2e, 0xe1, 0x83, 0x9c, 0x97, 0xf6, 0x07, 0x72, 0x03, 0x24, 0xfe, 0xd5, 0x08, 0xd4,
|
||||
0xdc, 0x36, 0x6e, 0x0a, 0x69, 0x9a, 0xc6, 0xa6, 0xe9, 0x21, 0xbb, 0xd1, 0x57, 0x21, 0xaa, 0xcf,
|
||||
0xfa, 0xa6, 0xeb, 0x0d, 0x62, 0x8b, 0xb5, 0x16, 0x7b, 0x05, 0x70, 0x9c, 0xa3, 0x5a, 0x4b, 0xc5,
|
||||
0x91, 0x3e, 0x83, 0x49, 0xb7, 0x10, 0x59, 0x82, 0xff, 0x2d, 0x2b, 0x53, 0x14, 0x5c, 0x64, 0xa5,
|
||||
0x34, 0x96, 0xbc, 0x68, 0x17, 0xa2, 0xbf, 0x1d, 0x98, 0xd8, 0x2d, 0x02, 0x70, 0x63, 0x51, 0x5a,
|
||||
0x92, 0x2e, 0x09, 0x81, 0xb1, 0xa8, 0x0a, 0x34, 0x36, 0xbd, 0xc8, 0xd4, 0x84, 0xc1, 0x34, 0xa9,
|
||||
0x4a, 0x89, 0x3f, 0xe5, 0xdc, 0x5d, 0xba, 0x17, 0xfe, 0xea, 0xcc, 0x26, 0xc4, 0x5e, 0x77, 0xf0,
|
||||
0x6d, 0x29, 0x85, 0x8a, 0x7a, 0xd2, 0xe2, 0x1a, 0x4e, 0x76, 0x2f, 0xf4, 0x94, 0x3e, 0x5d, 0x2f,
|
||||
0xd2, 0x25, 0x39, 0x83, 0x07, 0x6d, 0x5c, 0x34, 0xfd, 0x98, 0xee, 0x70, 0x3d, 0x7a, 0xe9, 0xd0,
|
||||
0x4b, 0xf0, 0x86, 0xc4, 0xc8, 0x53, 0xf0, 0x05, 0xd6, 0x55, 0x23, 0x12, 0x5c, 0x67, 0xf7, 0x56,
|
||||
0x00, 0x7a, 0xe8, 0xfd, 0x3d, 0xfd, 0x02, 0xc1, 0x61, 0x54, 0xe4, 0x1c, 0xbc, 0x2e, 0xd0, 0x46,
|
||||
0x14, 0xb6, 0xe5, 0xd8, 0x00, 0x9f, 0x44, 0xa1, 0xed, 0x95, 0xf1, 0x66, 0xb0, 0xa7, 0x6b, 0x32,
|
||||
0x87, 0x69, 0x8b, 0xa2, 0xce, 0xaa, 0xd2, 0x3c, 0x9a, 0x17, 0xf5, 0x47, 0xfa, 0x16, 0x4e, 0x6f,
|
||||
0xcb, 0x44, 0x28, 0x2e, 0x23, 0xfc, 0xd1, 0x60, 0x2d, 0xc9, 0xa3, 0xad, 0x15, 0x7f, 0x35, 0x66,
|
||||
0x21, 0xaa, 0xce, 0xd0, 0x63, 0xf0, 0x78, 0x11, 0x67, 0x5d, 0x48, 0x5a, 0xfc, 0x24, 0xda, 0x02,
|
||||
0xf4, 0x39, 0xcc, 0x06, 0x9d, 0x9a, 0x57, 0x65, 0x8d, 0xe4, 0x09, 0x40, 0x92, 0xf1, 0xef, 0x28,
|
||||
0x4c, 0x87, 0x63, 0x3a, 0x76, 0x10, 0x7a, 0x07, 0xa7, 0x6f, 0xf0, 0xbf, 0x46, 0xef, 0x2b, 0x8d,
|
||||
0xfe, 0x51, 0xba, 0x82, 0xd9, 0xa0, 0x64, 0x87, 0xef, 0x6d, 0xeb, 0x1c, 0x6c, 0xbb, 0x2a, 0x00,
|
||||
0x42, 0x54, 0x1f, 0xbb, 0xcf, 0xa2, 0x1f, 0xdf, 0xee, 0x4e, 0x66, 0x6c, 0x3f, 0x8d, 0x45, 0xc0,
|
||||
0x0e, 0x6c, 0xd1, 0x23, 0xcd, 0xb7, 0xe3, 0xc8, 0x8c, 0xed, 0x5b, 0x58, 0x04, 0xec, 0x60, 0x13,
|
||||
0x7a, 0xf4, 0x75, 0x62, 0x7e, 0xe3, 0x8b, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xeb, 0x29, 0x21,
|
||||
0x4e, 0xac, 0x03, 0x00, 0x00,
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ message Key {
|
||||
KmsKey kms_key = 1;
|
||||
PgpKey pgp_key = 2;
|
||||
GcpKmsKey gcp_kms_key = 3;
|
||||
AzureKeyVaultKey azure_keyvault_key = 4;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +23,12 @@ message GcpKmsKey {
|
||||
string resource_id = 1;
|
||||
}
|
||||
|
||||
message AzureKeyVaultKey {
|
||||
string vault_url = 1;
|
||||
string name = 2;
|
||||
string version = 3;
|
||||
}
|
||||
|
||||
message EncryptRequest {
|
||||
Key key = 1;
|
||||
bytes plaintext = 2;
|
||||
|
||||
@@ -3,6 +3,7 @@ package keyservice
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.mozilla.org/sops/azkv"
|
||||
"go.mozilla.org/sops/gcpkms"
|
||||
"go.mozilla.org/sops/kms"
|
||||
"go.mozilla.org/sops/pgp"
|
||||
@@ -55,6 +56,19 @@ func (ks *Server) encryptWithGcpKms(key *GcpKmsKey, plaintext []byte) ([]byte, e
|
||||
return []byte(gcpKmsKey.EncryptedKey), nil
|
||||
}
|
||||
|
||||
func (ks *Server) encryptWithAzureKeyVault(key *AzureKeyVaultKey, plaintext []byte) ([]byte, error) {
|
||||
azkvKey := azkv.MasterKey{
|
||||
VaultURL: key.VaultUrl,
|
||||
Name: key.Name,
|
||||
Version: key.Version,
|
||||
}
|
||||
err := azkvKey.Encrypt(plaintext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(azkvKey.EncryptedKey), nil
|
||||
}
|
||||
|
||||
func (ks *Server) decryptWithPgp(key *PgpKey, ciphertext []byte) ([]byte, error) {
|
||||
pgpKey := pgp.NewMasterKeyFromFingerprint(key.Fingerprint)
|
||||
pgpKey.EncryptedKey = string(ciphertext)
|
||||
@@ -86,6 +100,17 @@ func (ks *Server) decryptWithGcpKms(key *GcpKmsKey, ciphertext []byte) ([]byte,
|
||||
return []byte(plaintext), err
|
||||
}
|
||||
|
||||
func (ks *Server) decryptWithAzureKeyVault(key *AzureKeyVaultKey, ciphertext []byte) ([]byte, error) {
|
||||
azkvKey := azkv.MasterKey{
|
||||
VaultURL: key.VaultUrl,
|
||||
Name: key.Name,
|
||||
Version: key.Version,
|
||||
}
|
||||
azkvKey.EncryptedKey = string(ciphertext)
|
||||
plaintext, err := azkvKey.Decrypt()
|
||||
return []byte(plaintext), err
|
||||
}
|
||||
|
||||
// Encrypt takes an encrypt request and encrypts the provided plaintext with the provided key, returning the encrypted
|
||||
// result
|
||||
func (ks Server) Encrypt(ctx context.Context,
|
||||
@@ -117,6 +142,14 @@ func (ks Server) Encrypt(ctx context.Context,
|
||||
response = &EncryptResponse{
|
||||
Ciphertext: ciphertext,
|
||||
}
|
||||
case *Key_AzureKeyvaultKey:
|
||||
ciphertext, err := ks.encryptWithAzureKeyVault(k.AzureKeyvaultKey, req.Plaintext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response = &EncryptResponse{
|
||||
Ciphertext: ciphertext,
|
||||
}
|
||||
case nil:
|
||||
return nil, status.Errorf(codes.NotFound, "Must provide a key")
|
||||
default:
|
||||
@@ -139,6 +172,8 @@ func keyToString(key Key) string {
|
||||
return fmt.Sprintf("AWS KMS key with ARN %s", k.KmsKey.Arn)
|
||||
case *Key_GcpKmsKey:
|
||||
return fmt.Sprintf("GCP KMS key with resource ID %s", k.GcpKmsKey.ResourceId)
|
||||
case *Key_AzureKeyvaultKey:
|
||||
return fmt.Sprintf("Azure Key Vault key with URL %s/keys/%s/%s", k.AzureKeyvaultKey.VaultUrl, k.AzureKeyvaultKey.Name, k.AzureKeyvaultKey.Version)
|
||||
default:
|
||||
return fmt.Sprintf("Unknown key type")
|
||||
}
|
||||
@@ -191,6 +226,14 @@ func (ks Server) Decrypt(ctx context.Context,
|
||||
response = &DecryptResponse{
|
||||
Plaintext: plaintext,
|
||||
}
|
||||
case *Key_AzureKeyvaultKey:
|
||||
plaintext, err := ks.decryptWithAzureKeyVault(k.AzureKeyvaultKey, req.Ciphertext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response = &DecryptResponse{
|
||||
Plaintext: plaintext,
|
||||
}
|
||||
case nil:
|
||||
return nil, grpc.Errorf(codes.NotFound, "Must provide a key")
|
||||
default:
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"go.mozilla.org/sops"
|
||||
"go.mozilla.org/sops/azkv"
|
||||
"go.mozilla.org/sops/gcpkms"
|
||||
"go.mozilla.org/sops/kms"
|
||||
"go.mozilla.org/sops/pgp"
|
||||
@@ -38,6 +39,7 @@ type Metadata struct {
|
||||
KeyGroups []keygroup `yaml:"key_groups,omitempty" json:"key_groups,omitempty"`
|
||||
KMSKeys []kmskey `yaml:"kms" json:"kms"`
|
||||
GCPKMSKeys []gcpkmskey `yaml:"gcp_kms" json:"gcp_kms"`
|
||||
AzureKeyVaultKeys []azkvkey `yaml:"azure_kv" json:"azure_kv"`
|
||||
LastModified string `yaml:"lastmodified" json:"lastmodified"`
|
||||
MessageAuthenticationCode string `yaml:"mac" json:"mac"`
|
||||
PGPKeys []pgpkey `yaml:"pgp" json:"pgp"`
|
||||
@@ -47,9 +49,10 @@ type Metadata struct {
|
||||
}
|
||||
|
||||
type keygroup struct {
|
||||
PGPKeys []pgpkey `yaml:"pgp,omitempty" json:"pgp,omitempty"`
|
||||
KMSKeys []kmskey `yaml:"kms,omitempty" json:"kms,omitempty"`
|
||||
GCPKMSKeys []gcpkmskey `yaml:"gcp_kms,omitempty" json:"gcp_kms,omitempty"`
|
||||
PGPKeys []pgpkey `yaml:"pgp,omitempty" json:"pgp,omitempty"`
|
||||
KMSKeys []kmskey `yaml:"kms,omitempty" json:"kms,omitempty"`
|
||||
GCPKMSKeys []gcpkmskey `yaml:"gcp_kms,omitempty" json:"gcp_kms,omitempty"`
|
||||
AzureKeyVaultKeys []azkvkey `yaml:"azure_kv,omitempty" json:"azure_kv,omitempty"`
|
||||
}
|
||||
|
||||
type pgpkey struct {
|
||||
@@ -72,6 +75,14 @@ type gcpkmskey struct {
|
||||
EncryptedDataKey string `yaml:"enc" json:"enc"`
|
||||
}
|
||||
|
||||
type azkvkey struct {
|
||||
VaultURL string `yaml:"vault_url" json:"vault_url"`
|
||||
Name string `yaml:"name" json:"name"`
|
||||
Version string `yaml:"version" json:"version"`
|
||||
CreatedAt string `yaml:"created_at" json:"created_at"`
|
||||
EncryptedDataKey string `yaml:"enc" json:"enc"`
|
||||
}
|
||||
|
||||
// MetadataFromInternal converts an internal SOPS metadata representation to a representation appropriate for storage
|
||||
func MetadataFromInternal(sopsMetadata sops.Metadata) Metadata {
|
||||
var m Metadata
|
||||
@@ -86,12 +97,14 @@ func MetadataFromInternal(sopsMetadata sops.Metadata) Metadata {
|
||||
m.PGPKeys = pgpKeysFromGroup(group)
|
||||
m.KMSKeys = kmsKeysFromGroup(group)
|
||||
m.GCPKMSKeys = gcpkmsKeysFromGroup(group)
|
||||
m.AzureKeyVaultKeys = azkvKeysFromGroup(group)
|
||||
} else {
|
||||
for _, group := range sopsMetadata.KeyGroups {
|
||||
m.KeyGroups = append(m.KeyGroups, keygroup{
|
||||
KMSKeys: kmsKeysFromGroup(group),
|
||||
PGPKeys: pgpKeysFromGroup(group),
|
||||
GCPKMSKeys: gcpkmsKeysFromGroup(group),
|
||||
KMSKeys: kmsKeysFromGroup(group),
|
||||
PGPKeys: pgpKeysFromGroup(group),
|
||||
GCPKMSKeys: gcpkmsKeysFromGroup(group),
|
||||
AzureKeyVaultKeys: azkvKeysFromGroup(group),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -142,6 +155,22 @@ func gcpkmsKeysFromGroup(group sops.KeyGroup) (keys []gcpkmskey) {
|
||||
return
|
||||
}
|
||||
|
||||
func azkvKeysFromGroup(group sops.KeyGroup) (keys []azkvkey) {
|
||||
for _, key := range group {
|
||||
switch key := key.(type) {
|
||||
case *azkv.MasterKey:
|
||||
keys = append(keys, azkvkey{
|
||||
VaultURL: key.VaultURL,
|
||||
Name: key.Name,
|
||||
Version: key.Version,
|
||||
CreatedAt: key.CreationDate.Format(time.RFC3339),
|
||||
EncryptedDataKey: key.EncryptedKey,
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ToInternal converts a storage-appropriate Metadata struct to a SOPS internal representation
|
||||
func (m *Metadata) ToInternal() (sops.Metadata, error) {
|
||||
lastModified, err := time.Parse(time.RFC3339, m.LastModified)
|
||||
@@ -169,7 +198,7 @@ func (m *Metadata) ToInternal() (sops.Metadata, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func internalGroupFrom(kmsKeys []kmskey, pgpKeys []pgpkey, gcpKmsKeys []gcpkmskey) (sops.KeyGroup, error) {
|
||||
func internalGroupFrom(kmsKeys []kmskey, pgpKeys []pgpkey, gcpKmsKeys []gcpkmskey, azkvKeys []azkvkey) (sops.KeyGroup, error) {
|
||||
var internalGroup sops.KeyGroup
|
||||
for _, kmsKey := range kmsKeys {
|
||||
k, err := kmsKey.toInternal()
|
||||
@@ -185,6 +214,13 @@ func internalGroupFrom(kmsKeys []kmskey, pgpKeys []pgpkey, gcpKmsKeys []gcpkmske
|
||||
}
|
||||
internalGroup = append(internalGroup, k)
|
||||
}
|
||||
for _, azkvKey := range azkvKeys {
|
||||
k, err := azkvKey.toInternal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
internalGroup = append(internalGroup, k)
|
||||
}
|
||||
for _, pgpKey := range pgpKeys {
|
||||
k, err := pgpKey.toInternal()
|
||||
if err != nil {
|
||||
@@ -197,8 +233,8 @@ func internalGroupFrom(kmsKeys []kmskey, pgpKeys []pgpkey, gcpKmsKeys []gcpkmske
|
||||
|
||||
func (m *Metadata) internalKeygroups() ([]sops.KeyGroup, error) {
|
||||
var internalGroups []sops.KeyGroup
|
||||
if len(m.PGPKeys) > 0 || len(m.KMSKeys) > 0 || len(m.GCPKMSKeys) > 0 {
|
||||
internalGroup, err := internalGroupFrom(m.KMSKeys, m.PGPKeys, m.GCPKMSKeys)
|
||||
if len(m.PGPKeys) > 0 || len(m.KMSKeys) > 0 || len(m.GCPKMSKeys) > 0 || len(m.AzureKeyVaultKeys) > 0 {
|
||||
internalGroup, err := internalGroupFrom(m.KMSKeys, m.PGPKeys, m.GCPKMSKeys, m.AzureKeyVaultKeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -206,7 +242,7 @@ func (m *Metadata) internalKeygroups() ([]sops.KeyGroup, error) {
|
||||
return internalGroups, nil
|
||||
} else if len(m.KeyGroups) > 0 {
|
||||
for _, group := range m.KeyGroups {
|
||||
internalGroup, err := internalGroupFrom(group.KMSKeys, group.PGPKeys, group.GCPKMSKeys)
|
||||
internalGroup, err := internalGroupFrom(group.KMSKeys, group.PGPKeys, group.GCPKMSKeys, group.AzureKeyVaultKeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -244,6 +280,20 @@ func (gcpKmsKey *gcpkmskey) toInternal() (*gcpkms.MasterKey, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (azkvKey *azkvkey) toInternal() (*azkv.MasterKey, error) {
|
||||
creationDate, err := time.Parse(time.RFC3339, azkvKey.CreatedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &azkv.MasterKey{
|
||||
VaultURL: azkvKey.VaultURL,
|
||||
Name: azkvKey.Name,
|
||||
Version: azkvKey.Version,
|
||||
EncryptedKey: azkvKey.EncryptedDataKey,
|
||||
CreationDate: creationDate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (pgpKey *pgpkey) toInternal() (*pgp.MasterKey, error) {
|
||||
creationDate, err := time.Parse(time.RFC3339, pgpKey.CreatedAt)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user