From 25108d7784b02700916b93d433256cabba751037 Mon Sep 17 00:00:00 2001 From: Kevin Labesse Date: Sun, 4 Dec 2022 01:25:10 +0100 Subject: [PATCH 1/3] feat(gcp-kms): impersonate service account Signed-off-by: Kevin Labesse --- .gitignore | 1 + cmd/sops/main.go | 22 +++++--- config/config.go | 43 ++++++++------- gcpkms/keysource.go | 28 ++++++++-- gcpkms/keysource_test.go | 4 +- go.mod | 2 +- go.sum | 2 + keyservice/keyservice.go | 3 +- keyservice/keyservice.pb.go | 104 ++++++++++++++++++++---------------- keyservice/keyservice.proto | 1 + keyservice/server.go | 15 +++--- stores/stores.go | 24 +++++---- 12 files changed, 150 insertions(+), 99 deletions(-) diff --git a/.gitignore b/.gitignore index d7e97440f..e5083f708 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ Cargo.lock vendor/ coverage.txt profile.out +functional-tests/sops diff --git a/cmd/sops/main.go b/cmd/sops/main.go index 2deea8671..b299aac09 100644 --- a/cmd/sops/main.go +++ b/cmd/sops/main.go @@ -1,4 +1,4 @@ -package main //import "go.mozilla.org/sops/v3/cmd/sops" +package main // import "go.mozilla.org/sops/v3/cmd/sops" import ( encodingjson "encoding/json" @@ -15,6 +15,8 @@ import ( "github.com/sirupsen/logrus" "github.com/urfave/cli" + "google.golang.org/grpc" + "go.mozilla.org/sops/v3" "go.mozilla.org/sops/v3/aes" "go.mozilla.org/sops/v3/age" @@ -38,7 +40,6 @@ import ( "go.mozilla.org/sops/v3/stores/dotenv" "go.mozilla.org/sops/v3/stores/json" "go.mozilla.org/sops/v3/version" - "google.golang.org/grpc" ) var log *logrus.Logger @@ -383,6 +384,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: "gcp-impersonate-service-account", + Usage: "the GCP service account to use for requests to GCP", + }, cli.StringSliceFlag{ Name: "azure-kv", Usage: "the Azure Key Vault key URL the new group should contain. Can be specified more than once", @@ -423,7 +428,7 @@ func main() { group = append(group, kms.NewMasterKeyFromArn(arn, kms.ParseKMSContext(c.String("encryption-context")), c.String("aws-profile"))) } for _, kms := range gcpKmses { - group = append(group, gcpkms.NewMasterKeyFromResourceID(kms)) + group = append(group, gcpkms.NewMasterKeyFromResourceID(kms, c.String("gcp-impersonate-service-account"))) } for _, uri := range vaultURIs { k, err := hcvault.NewMasterKeyFromURI(uri) @@ -575,6 +580,11 @@ func main() { Usage: "comma separated list of GCP KMS resource IDs", EnvVar: "SOPS_GCP_KMS_IDS", }, + cli.StringSliceFlag{ + Name: "gcp-impersonate-service-account", + Usage: "the GCP service account to use for requests to GCP", + EnvVar: "SOPS_GCP_IMPERSONATE_SERVICE_ACCOUNT", + }, cli.StringFlag{ Name: "azure-kv", Usage: "comma separated list of Azure Key Vault URLs", @@ -836,7 +846,7 @@ func main() { for _, k := range pgp.MasterKeysFromFingerprintString(c.String("add-pgp")) { addMasterKeys = append(addMasterKeys, k) } - for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String("add-gcp-kms")) { + for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String("add-gcp-kms"), c.String("gcp-impersonate-service-account")) { addMasterKeys = append(addMasterKeys, k) } azureKeys, err := azkv.MasterKeysFromURLs(c.String("add-azure-kv")) @@ -868,7 +878,7 @@ func main() { for _, k := range pgp.MasterKeysFromFingerprintString(c.String("rm-pgp")) { rmMasterKeys = append(rmMasterKeys, k) } - for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String("rm-gcp-kms")) { + for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String("rm-gcp-kms"), c.String("gcp-impersonate-service-account")) { rmMasterKeys = append(rmMasterKeys, k) } azureKeys, err = azkv.MasterKeysFromURLs(c.String("rm-azure-kv")) @@ -1099,7 +1109,7 @@ func keyGroups(c *cli.Context, file string) ([]sops.KeyGroup, error) { } } if c.String("gcp-kms") != "" { - for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String("gcp-kms")) { + for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String("gcp-kms"), c.String("gcp-impersonate-service-account")) { cloudKmsKeys = append(cloudKmsKeys, k) } } diff --git a/config/config.go b/config/config.go index a4db7b868..4f3e86580 100644 --- a/config/config.go +++ b/config/config.go @@ -1,7 +1,7 @@ /* Package config provides a way to find and load SOPS configuration files */ -package config //import "go.mozilla.org/sops/v3/config" +package config // import "go.mozilla.org/sops/v3/config" import ( "fmt" @@ -13,6 +13,8 @@ import ( "strings" "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" + "go.mozilla.org/sops/v3" "go.mozilla.org/sops/v3/age" "go.mozilla.org/sops/v3/azkv" @@ -22,7 +24,6 @@ import ( "go.mozilla.org/sops/v3/logging" "go.mozilla.org/sops/v3/pgp" "go.mozilla.org/sops/v3/publish" - "gopkg.in/yaml.v3" ) var log *logrus.Logger @@ -79,7 +80,8 @@ type keyGroup struct { } type gcpKmsKey struct { - ResourceID string `yaml:"resource_id"` + ResourceID string `yaml:"resource_id"` + ImpersonateServiceAccount string `yaml:"gcp_impersonate_service_account"` } type kmsKey struct { @@ -110,20 +112,21 @@ type destinationRule struct { } type creationRule struct { - PathRegex string `yaml:"path_regex"` - KMS string - AwsProfile string `yaml:"aws_profile"` - Age string `yaml:"age"` - PGP string - GCPKMS string `yaml:"gcp_kms"` - AzureKeyVault string `yaml:"azure_keyvault"` - VaultURI string `yaml:"hc_vault_transit_uri"` - KeyGroups []keyGroup `yaml:"key_groups"` - ShamirThreshold int `yaml:"shamir_threshold"` - UnencryptedSuffix string `yaml:"unencrypted_suffix"` - EncryptedSuffix string `yaml:"encrypted_suffix"` - UnencryptedRegex string `yaml:"unencrypted_regex"` - EncryptedRegex string `yaml:"encrypted_regex"` + PathRegex string `yaml:"path_regex"` + KMS string + AwsProfile string `yaml:"aws_profile"` + Age string `yaml:"age"` + PGP string + GCPKMS string `yaml:"gcp_kms"` + GCPImpersonateServiceAccount string `yaml:"gcp_impersonate_service_account"` + AzureKeyVault string `yaml:"azure_keyvault"` + VaultURI string `yaml:"hc_vault_transit_uri"` + KeyGroups []keyGroup `yaml:"key_groups"` + ShamirThreshold int `yaml:"shamir_threshold"` + UnencryptedSuffix string `yaml:"unencrypted_suffix"` + EncryptedSuffix string `yaml:"encrypted_suffix"` + UnencryptedRegex string `yaml:"unencrypted_regex"` + EncryptedRegex string `yaml:"encrypted_regex"` } // Load loads a sops config file into a temporary struct @@ -168,7 +171,7 @@ func getKeyGroupsFromCreationRule(cRule *creationRule, kmsEncryptionContext map[ keyGroup = append(keyGroup, kms.NewMasterKey(k.Arn, k.Role, k.Context)) } for _, k := range group.GCPKMS { - keyGroup = append(keyGroup, gcpkms.NewMasterKeyFromResourceID(k.ResourceID)) + keyGroup = append(keyGroup, gcpkms.NewMasterKeyFromResourceID(k.ResourceID, k.ImpersonateServiceAccount)) } for _, k := range group.AzureKV { keyGroup = append(keyGroup, azkv.NewMasterKey(k.VaultURL, k.Key, k.Version)) @@ -200,7 +203,7 @@ func getKeyGroupsFromCreationRule(cRule *creationRule, kmsEncryptionContext map[ for _, k := range kms.MasterKeysFromArnString(cRule.KMS, kmsEncryptionContext, cRule.AwsProfile) { keyGroup = append(keyGroup, k) } - for _, k := range gcpkms.MasterKeysFromResourceIDString(cRule.GCPKMS) { + for _, k := range gcpkms.MasterKeysFromResourceIDString(cRule.GCPKMS, cRule.GCPImpersonateServiceAccount) { keyGroup = append(keyGroup, k) } azureKeys, err := azkv.MasterKeysFromURLs(cRule.AzureKeyVault) @@ -329,7 +332,7 @@ func parseCreationRuleForFile(conf *configFile, confPath, filePath string, kmsEn } // compare file path relative to path of config file - filePath = strings.TrimPrefix(filePath, configDir + string(filepath.Separator)) + filePath = strings.TrimPrefix(filePath, configDir+string(filepath.Separator)) var rule *creationRule diff --git a/gcpkms/keysource.go b/gcpkms/keysource.go index d549b32b8..ba2171afe 100644 --- a/gcpkms/keysource.go +++ b/gcpkms/keysource.go @@ -1,6 +1,7 @@ package gcpkms // import "go.mozilla.org/sops/v3/gcpkms" import ( + "bytes" "context" "encoding/base64" "fmt" @@ -11,6 +12,7 @@ import ( kms "cloud.google.com/go/kms/apiv1" "github.com/sirupsen/logrus" + "google.golang.org/api/impersonate" "google.golang.org/api/option" kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1" "google.golang.org/grpc" @@ -55,27 +57,32 @@ type MasterKey struct { // Mostly useful for testing at present, to wire the client to a mock // server. grpcConn *grpc.ClientConn + + // ImpersonateServiceAccount is the name of the service account + // to impersonate with GCP KMS client + ImpersonateServiceAccount string } // NewMasterKeyFromResourceID creates a new MasterKey with the provided resource // ID. -func NewMasterKeyFromResourceID(resourceID string) *MasterKey { +func NewMasterKeyFromResourceID(resourceID, impersonateServiceAccount string) *MasterKey { k := &MasterKey{} resourceID = strings.Replace(resourceID, " ", "", -1) k.ResourceID = resourceID k.CreationDate = time.Now().UTC() + k.ImpersonateServiceAccount = impersonateServiceAccount return k } // MasterKeysFromResourceIDString takes a comma separated list of GCP KMS // resource IDs and returns a slice of new MasterKeys for them. -func MasterKeysFromResourceIDString(resourceID string) []*MasterKey { +func MasterKeysFromResourceIDString(resourceID, impersonateServiceAccount string) []*MasterKey { var keys []*MasterKey if resourceID == "" { return keys } for _, s := range strings.Split(resourceID, ",") { - keys = append(keys, NewMasterKeyFromResourceID(s)) + keys = append(keys, NewMasterKeyFromResourceID(s, impersonateServiceAccount)) } return keys } @@ -207,16 +214,28 @@ func (key *MasterKey) newKMSClient() (*kms.KeyManagementClient, error) { return nil, fmt.Errorf("no valid resource ID found in %q", key.ResourceID) } + ctx := context.Background() var opts []option.ClientOption switch { case key.credentialJSON != nil: opts = append(opts, option.WithCredentialsJSON(key.credentialJSON)) + case key.ImpersonateServiceAccount != "": + ts, err := impersonate.CredentialsTokenSource(ctx, impersonate.CredentialsConfig{ + TargetPrincipal: key.ImpersonateServiceAccount, + // https://developers.google.com/identity/protocols/oauth2/scopes#cloudkms + Scopes: []string{"https://www.googleapis.com/auth/cloudkms"}, + }) + if err != nil { + return nil, fmt.Errorf("cannot impersonate service account '%s': %w", key.ImpersonateServiceAccount, err) + } + + opts = append(opts, option.WithTokenSource(ts)) default: credentials, err := getGoogleCredentials() if err != nil { return nil, err } - if credentials != nil { + if bytes.Compare(credentials, nil) != 0 { opts = append(opts, option.WithCredentialsJSON(key.credentialJSON)) } } @@ -224,7 +243,6 @@ func (key *MasterKey) newKMSClient() (*kms.KeyManagementClient, error) { opts = append(opts, option.WithGRPCConn(key.grpcConn)) } - ctx := context.Background() client, err := kms.NewKeyManagementClient(ctx, opts...) if err != nil { return nil, err diff --git a/gcpkms/keysource_test.go b/gcpkms/keysource_test.go index 2904eb34b..a098a59d9 100644 --- a/gcpkms/keysource_test.go +++ b/gcpkms/keysource_test.go @@ -24,7 +24,7 @@ var ( func TestMasterKeysFromResourceIDString(t *testing.T) { s := "projects/sops-testing1/locations/global/keyRings/creds/cryptoKeys/key1, projects/sops-testing2/locations/global/keyRings/creds/cryptoKeys/key2" - ks := MasterKeysFromResourceIDString(s) + ks := MasterKeysFromResourceIDString(s, "") k1 := ks[0] k2 := ks[1] expectedResourceID1 := "projects/sops-testing1/locations/global/keyRings/creds/cryptoKeys/key1" @@ -97,7 +97,7 @@ func TestMasterKey_SetEncryptedDataKey(t *testing.T) { func TestMasterKey_ToString(t *testing.T) { rsrcId := testResourceID - key := NewMasterKeyFromResourceID(rsrcId) + key := NewMasterKeyFromResourceID(rsrcId, "") assert.Equal(t, rsrcId, key.ToString()) } diff --git a/go.mod b/go.mod index 281a63533..5807560da 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,7 @@ require ( google.golang.org/api v0.87.0 google.golang.org/genproto v0.0.0-20220712132514-bdd2acd4974d google.golang.org/grpc v1.48.0 - google.golang.org/protobuf v1.28.0 + google.golang.org/protobuf v1.28.1 gopkg.in/ini.v1 v1.66.4 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 523e358be..3a3eae3fa 100644 --- a/go.sum +++ b/go.sum @@ -1042,6 +1042,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/keyservice/keyservice.go b/keyservice/keyservice.go index 103e3e6dc..025326102 100644 --- a/keyservice/keyservice.go +++ b/keyservice/keyservice.go @@ -31,7 +31,8 @@ func KeyFromMasterKey(mk keys.MasterKey) Key { return Key{ KeyType: &Key_GcpKmsKey{ GcpKmsKey: &GcpKmsKey{ - ResourceId: mk.ResourceID, + ResourceId: mk.ResourceID, + GcpImpersonateServiceAccount: mk.ImpersonateServiceAccount, }, }, } diff --git a/keyservice/keyservice.pb.go b/keyservice/keyservice.pb.go index ead3ccfd1..b09c55298 100644 --- a/keyservice/keyservice.pb.go +++ b/keyservice/keyservice.pb.go @@ -1,14 +1,13 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.23.0 -// protoc v3.13.0 +// protoc-gen-go v1.28.1 +// protoc v3.21.9 // source: keyservice/keyservice.proto package keyservice import ( context "context" - proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -25,16 +24,13 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - type Key struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to KeyType: + // // *Key_KmsKey // *Key_PgpKey // *Key_GcpKmsKey @@ -288,7 +284,8 @@ type GcpKmsKey struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ResourceId string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + ResourceId string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + GcpImpersonateServiceAccount string `protobuf:"bytes,2,opt,name=gcp_impersonate_service_account,json=gcpImpersonateServiceAccount,proto3" json:"gcp_impersonate_service_account,omitempty"` } func (x *GcpKmsKey) Reset() { @@ -330,6 +327,13 @@ func (x *GcpKmsKey) GetResourceId() string { return "" } +func (x *GcpKmsKey) GetGcpImpersonateServiceAccount() string { + if x != nil { + return x.GcpImpersonateServiceAccount + } + return "" +} + type VaultKey struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -744,47 +748,53 @@ var file_keyservice_keyservice_proto_rawDesc = []byte{ 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x2c, 0x0a, 0x09, 0x47, 0x63, 0x70, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12, + 0x38, 0x01, 0x22, 0x73, 0x0a, 0x09, 0x47, 0x63, 0x70, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, - 0x22, 0x6b, 0x0a, 0x08, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, - 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x50, 0x61, - 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x0a, - 0x10, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, - 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x26, 0x0a, 0x06, - 0x41, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, - 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, - 0x69, 0x65, 0x6e, 0x74, 0x22, 0x46, 0x0a, 0x0e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x04, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1c, - 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x31, 0x0a, 0x0f, - 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, - 0x48, 0x0a, 0x0e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x16, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x04, - 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, - 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, - 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, 0x2f, 0x0a, 0x0f, 0x44, 0x65, 0x63, - 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, - 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x32, 0x6c, 0x0a, 0x0a, 0x4b, 0x65, - 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x12, 0x0f, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x07, 0x44, 0x65, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x12, 0x0f, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x45, 0x0a, 0x1f, 0x67, 0x63, 0x70, 0x5f, 0x69, 0x6d, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, + 0x61, 0x74, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1c, 0x67, 0x63, 0x70, 0x49, 0x6d, + 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x6b, 0x0a, 0x08, 0x56, 0x61, 0x75, 0x6c, 0x74, + 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x76, 0x61, 0x75, 0x6c, + 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, + 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, + 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x0a, 0x10, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x4b, 0x65, 0x79, + 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x61, 0x75, 0x6c, + 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x61, 0x75, + 0x6c, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x22, 0x26, 0x0a, 0x06, 0x41, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, + 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x22, 0x46, 0x0a, 0x0e, 0x45, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x04, 0x2e, 0x4b, 0x65, 0x79, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x22, 0x31, 0x0a, 0x0f, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, + 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, + 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, 0x48, 0x0a, 0x0e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x04, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, + 0x22, 0x2f, 0x0a, 0x0f, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x32, 0x6c, 0x0a, 0x0a, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, + 0x2e, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x12, 0x0f, 0x2e, 0x45, 0x6e, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x45, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x2e, 0x0a, 0x07, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x12, 0x0f, 0x2e, 0x44, 0x65, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x44, 0x65, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, + 0x0e, 0x5a, 0x0c, 0x2e, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/keyservice/keyservice.proto b/keyservice/keyservice.proto index 1d91a5709..0b7ec2b79 100644 --- a/keyservice/keyservice.proto +++ b/keyservice/keyservice.proto @@ -24,6 +24,7 @@ message KmsKey { message GcpKmsKey { string resource_id = 1; + string gcp_impersonate_service_account = 2; } message VaultKey { diff --git a/keyservice/server.go b/keyservice/server.go index 08249ff24..a8514a3bb 100644 --- a/keyservice/server.go +++ b/keyservice/server.go @@ -3,16 +3,17 @@ package keyservice import ( "fmt" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "go.mozilla.org/sops/v3/age" "go.mozilla.org/sops/v3/azkv" "go.mozilla.org/sops/v3/gcpkms" "go.mozilla.org/sops/v3/hcvault" "go.mozilla.org/sops/v3/kms" "go.mozilla.org/sops/v3/pgp" - "golang.org/x/net/context" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) // Server is a key service server that uses SOPS MasterKeys to fulfill requests @@ -41,7 +42,8 @@ func (ks *Server) encryptWithKms(key *KmsKey, plaintext []byte) ([]byte, error) func (ks *Server) encryptWithGcpKms(key *GcpKmsKey, plaintext []byte) ([]byte, error) { gcpKmsKey := gcpkms.MasterKey{ - ResourceID: key.ResourceId, + ResourceID: key.ResourceId, + ImpersonateServiceAccount: key.GcpImpersonateServiceAccount, } err := gcpKmsKey.Encrypt(plaintext) if err != nil { @@ -104,7 +106,8 @@ func (ks *Server) decryptWithKms(key *KmsKey, ciphertext []byte) ([]byte, error) func (ks *Server) decryptWithGcpKms(key *GcpKmsKey, ciphertext []byte) ([]byte, error) { gcpKmsKey := gcpkms.MasterKey{ - ResourceID: key.ResourceId, + ResourceID: key.ResourceId, + ImpersonateServiceAccount: key.GcpImpersonateServiceAccount, } gcpKmsKey.EncryptedKey = string(ciphertext) plaintext, err := gcpKmsKey.Decrypt() diff --git a/stores/stores.go b/stores/stores.go index da8781ab2..71f5b5365 100644 --- a/stores/stores.go +++ b/stores/stores.go @@ -10,9 +10,8 @@ of the purpose of this package is to make it easy to change the SOPS file format package stores import ( - "time" - "fmt" + "time" "go.mozilla.org/sops/v3" "go.mozilla.org/sops/v3/age" @@ -79,9 +78,10 @@ type kmskey struct { } type gcpkmskey struct { - ResourceID string `yaml:"resource_id" json:"resource_id"` - CreatedAt string `yaml:"created_at" json:"created_at"` - EncryptedDataKey string `yaml:"enc" json:"enc"` + ResourceID string `yaml:"resource_id" json:"resource_id"` + CreatedAt string `yaml:"created_at" json:"created_at"` + EncryptedDataKey string `yaml:"enc" json:"enc"` + GCPImpersonateServiceAccount string `yaml:"gcp_impersonate_service_account" json:"gcp_impersonate_service_account"` } type vaultkey struct { @@ -175,9 +175,10 @@ func gcpkmsKeysFromGroup(group sops.KeyGroup) (keys []gcpkmskey) { switch key := key.(type) { case *gcpkms.MasterKey: keys = append(keys, gcpkmskey{ - ResourceID: key.ResourceID, - CreatedAt: key.CreationDate.Format(time.RFC3339), - EncryptedDataKey: key.EncryptedKey, + ResourceID: key.ResourceID, + CreatedAt: key.CreationDate.Format(time.RFC3339), + EncryptedDataKey: key.EncryptedKey, + GCPImpersonateServiceAccount: key.ImpersonateServiceAccount, }) } } @@ -365,9 +366,10 @@ func (gcpKmsKey *gcpkmskey) toInternal() (*gcpkms.MasterKey, error) { return nil, err } return &gcpkms.MasterKey{ - ResourceID: gcpKmsKey.ResourceID, - EncryptedKey: gcpKmsKey.EncryptedDataKey, - CreationDate: creationDate, + ResourceID: gcpKmsKey.ResourceID, + EncryptedKey: gcpKmsKey.EncryptedDataKey, + CreationDate: creationDate, + ImpersonateServiceAccount: gcpKmsKey.GCPImpersonateServiceAccount, }, nil } From 74a1e717e4ea07fee2b7da9cf10ed326e97f28a5 Mon Sep 17 00:00:00 2001 From: Kevin Labesse Date: Sun, 4 Dec 2022 12:21:22 +0100 Subject: [PATCH 2/3] docs(README): gcp impersonate service account Signed-off-by: Kevin Labesse --- README.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.rst b/README.rst index 6de0b120c..3feba3784 100644 --- a/README.rst +++ b/README.rst @@ -214,6 +214,18 @@ you can enable application default credentials using the sdk:: $ gcloud auth application-default login +If you want to `impersonate a service account `, +you can do so with `gcp_impersonate_service_account`: + +.. code:: yaml + + sops: + kms: + - gcp_kms: projects/my-project/locations/global/keyRings/sops/cryptoKeys/sops-key + gcp_impersonate_service_account: foo@my-project.iam.gserviceaccount.com + +Similarly the `--gcp-impersonate-service-account` flag can be set with the command line with any of the GCP KMS commands. + Encrypting/decrypting with GCP KMS requires a KMS ResourceID. You can use the cloud console the get the ResourceID or you can create one using the gcloud sdk: From d5297917fafbd1a30f86a79fb1525719adb9752d Mon Sep 17 00:00:00 2001 From: Kevin Labesse Date: Sun, 4 Dec 2022 12:23:13 +0100 Subject: [PATCH 3/3] fix(README): typo .rst Signed-off-by: Kevin Labesse --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 3feba3784..b95dcf2f8 100644 --- a/README.rst +++ b/README.rst @@ -214,7 +214,7 @@ you can enable application default credentials using the sdk:: $ gcloud auth application-default login -If you want to `impersonate a service account `, +If you want to `impersonate a service account `_, you can do so with `gcp_impersonate_service_account`: .. code:: yaml