From ac5ca1f05cbdb55560945a0eb705b0efc6c2387d Mon Sep 17 00:00:00 2001 From: Mark Kelly Date: Fri, 25 Jan 2019 12:42:41 +0000 Subject: [PATCH] AWS Profiles --- README.rst | 17 +++++++++++++++++ cmd/sops/main.go | 18 +++++++++++++----- config/config.go | 10 ++++++---- keyservice/keyservice.go | 7 ++++--- keyservice/keyservice.pb.go | 7 ++++--- keyservice/server.go | 2 ++ kms/keysource.go | 10 ++++++---- kms/keysource_test.go | 2 +- stores/stores.go | 3 +++ 9 files changed, 56 insertions(+), 20 deletions(-) diff --git a/README.rst b/README.rst index 965712343..9d940357f 100644 --- a/README.rst +++ b/README.rst @@ -326,6 +326,23 @@ When removing keys, it is recommended to rotate the data key using ``-r``, otherwise owners of the removed key may have add access to the data key in the past. +KMS AWS Profiles +~~~~~~~~~~~~~~~~ + +If you want to use a specific profile, you can do so with `aws_profile`: + +.. code:: yaml + + sops: + kms: + - arn: arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e + aws_profile: foo + +If no AWS profile is set, default credentials will be used. + +Similarly the `--aws-profile` flag can be set with the command line with any of the KMS commands. + + Assuming roles and using KMS in various AWS accounts ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/cmd/sops/main.go b/cmd/sops/main.go index 31b3720c8..0a744f812 100644 --- a/cmd/sops/main.go +++ b/cmd/sops/main.go @@ -29,11 +29,11 @@ import ( "go.mozilla.org/sops/logging" "go.mozilla.org/sops/pgp" "go.mozilla.org/sops/stores/dotenv" + "go.mozilla.org/sops/stores/ini" "go.mozilla.org/sops/stores/json" yamlstores "go.mozilla.org/sops/stores/yaml" "google.golang.org/grpc" "gopkg.in/urfave/cli.v1" - "go.mozilla.org/sops/stores/ini" ) var log *logrus.Logger @@ -161,6 +161,10 @@ func main() { Name: "kms", Usage: "the KMS ARNs the new group should contain. Can be specified more than once", }, + cli.StringFlag{ + Name: "aws-profile", + Usage: "The AWS profile to use for requests to AWS", + }, cli.StringSliceFlag{ Name: "gcp-kms", Usage: "the GCP KMS Resource ID the new group should contain. Can be specified more than once", @@ -192,7 +196,7 @@ func main() { group = append(group, pgp.NewMasterKeyFromFingerprint(fp)) } for _, arn := range kmsArns { - group = append(group, kms.NewMasterKeyFromArn(arn, kms.ParseKMSContext(c.String("encryption-context")))) + 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)) @@ -305,6 +309,10 @@ func main() { Usage: "comma separated list of KMS ARNs", EnvVar: "SOPS_KMS_ARN", }, + cli.StringFlag{ + Name: "aws-profile", + Usage: "The AWS profile to use for requests to AWS", + }, cli.StringFlag{ Name: "gcp-kms", Usage: "comma separated list of GCP KMS resource IDs", @@ -502,7 +510,7 @@ func main() { if c.Bool("rotate") { var addMasterKeys []keys.MasterKey kmsEncryptionContext := kms.ParseKMSContext(c.String("encryption-context")) - for _, k := range kms.MasterKeysFromArnString(c.String("add-kms"), kmsEncryptionContext) { + for _, k := range kms.MasterKeysFromArnString(c.String("add-kms"), kmsEncryptionContext, c.String("aws-profile")) { addMasterKeys = append(addMasterKeys, k) } for _, k := range pgp.MasterKeysFromFingerprintString(c.String("add-pgp")) { @@ -520,7 +528,7 @@ func main() { } var rmMasterKeys []keys.MasterKey - for _, k := range kms.MasterKeysFromArnString(c.String("rm-kms"), kmsEncryptionContext) { + for _, k := range kms.MasterKeysFromArnString(c.String("rm-kms"), kmsEncryptionContext, c.String("aws-profile")) { rmMasterKeys = append(rmMasterKeys, k) } for _, k := range pgp.MasterKeysFromFingerprintString(c.String("rm-pgp")) { @@ -754,7 +762,7 @@ func keyGroups(c *cli.Context, file string) ([]sops.KeyGroup, error) { return nil, common.NewExitError("Invalid KMS encryption context format", codes.ErrorInvalidKMSEncryptionContextFormat) } if c.String("kms") != "" { - for _, k := range kms.MasterKeysFromArnString(c.String("kms"), kmsEncryptionContext) { + for _, k := range kms.MasterKeysFromArnString(c.String("kms"), kmsEncryptionContext, c.String("aws-profile")) { kmsKeys = append(kmsKeys, k) } } diff --git a/config/config.go b/config/config.go index a729f1823..07628ef0f 100644 --- a/config/config.go +++ b/config/config.go @@ -75,9 +75,10 @@ type gcpKmsKey struct { } type kmsKey struct { - Arn string `yaml:"arn"` - Role string `yaml:"role,omitempty"` - Context map[string]*string `yaml:"context"` + Arn string `yaml:"arn"` + Role string `yaml:"role,omitempty"` + Context map[string]*string `yaml:"context"` + AwsProfile string `yaml:"aws_profile"` } type azureKVKey struct { @@ -90,6 +91,7 @@ type creationRule struct { FilenameRegex string `yaml:"filename_regex"` PathRegex string `yaml:"path_regex"` KMS string + AwsProfile string `yaml:"aws_profile"` PGP string GCPKMS string `yaml:"gcp_kms"` AzureKeyVault string `yaml:"azure_keyvault"` @@ -175,7 +177,7 @@ func loadForFileFromBytes(confBytes []byte, filePath string, kmsEncryptionContex for _, k := range pgp.MasterKeysFromFingerprintString(rule.PGP) { keyGroup = append(keyGroup, k) } - for _, k := range kms.MasterKeysFromArnString(rule.KMS, kmsEncryptionContext) { + for _, k := range kms.MasterKeysFromArnString(rule.KMS, kmsEncryptionContext, rule.AwsProfile) { keyGroup = append(keyGroup, k) } for _, k := range gcpkms.MasterKeysFromResourceIDString(rule.GCPKMS) { diff --git a/keyservice/keyservice.go b/keyservice/keyservice.go index 7d426b65a..0f9e27471 100644 --- a/keyservice/keyservice.go +++ b/keyservice/keyservice.go @@ -41,9 +41,10 @@ func KeyFromMasterKey(mk keys.MasterKey) Key { return Key{ KeyType: &Key_KmsKey{ KmsKey: &KmsKey{ - Arn: mk.Arn, - Role: mk.Role, - Context: ctx, + Arn: mk.Arn, + Role: mk.Role, + Context: ctx, + AwsProfile: mk.AwsProfile, }, }, } diff --git a/keyservice/keyservice.pb.go b/keyservice/keyservice.pb.go index b13f7552c..d39c654ab 100644 --- a/keyservice/keyservice.pb.go +++ b/keyservice/keyservice.pb.go @@ -240,9 +240,10 @@ func (m *PgpKey) GetFingerprint() string { } type KmsKey struct { - Arn string `protobuf:"bytes,1,opt,name=arn" json:"arn,omitempty"` - Role string `protobuf:"bytes,2,opt,name=role" json:"role,omitempty"` - Context map[string]string `protobuf:"bytes,3,rep,name=context" json:"context,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Arn string `protobuf:"bytes,1,opt,name=arn" json:"arn,omitempty"` + Role string `protobuf:"bytes,2,opt,name=role" json:"role,omitempty"` + Context map[string]string `protobuf:"bytes,3,rep,name=context" json:"context,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + AwsProfile string `protobuf:"bytes,4,opt,name=aws_profile" json:"aws_profile,omitempty"` } func (m *KmsKey) Reset() { *m = KmsKey{} } diff --git a/keyservice/server.go b/keyservice/server.go index e4e42b111..35849c6e9 100644 --- a/keyservice/server.go +++ b/keyservice/server.go @@ -37,6 +37,7 @@ func (ks *Server) encryptWithKms(key *KmsKey, plaintext []byte) ([]byte, error) Arn: key.Arn, Role: key.Role, EncryptionContext: ctx, + AwsProfile: key.AwsProfile, } err := kmsKey.Encrypt(plaintext) if err != nil { @@ -85,6 +86,7 @@ func (ks *Server) decryptWithKms(key *KmsKey, ciphertext []byte) ([]byte, error) Arn: key.Arn, Role: key.Role, EncryptionContext: ctx, + AwsProfile: key.AwsProfile, } kmsKey.EncryptedKey = string(ciphertext) plaintext, err := kmsKey.Decrypt() diff --git a/kms/keysource.go b/kms/keysource.go index adf2b6e7f..bddaa1aa9 100644 --- a/kms/keysource.go +++ b/kms/keysource.go @@ -42,6 +42,7 @@ type MasterKey struct { EncryptedKey string CreationDate time.Time EncryptionContext map[string]*string + AwsProfile string } // EncryptedDataKey returns the encrypted data key this master key holds @@ -131,7 +132,7 @@ func NewMasterKey(arn string, role string, context map[string]*string) *MasterKe } // NewMasterKeyFromArn takes an ARN string and returns a new MasterKey for that ARN -func NewMasterKeyFromArn(arn string, context map[string]*string) *MasterKey { +func NewMasterKeyFromArn(arn string, context map[string]*string, awsProfile string) *MasterKey { k := &MasterKey{} arn = strings.Replace(arn, " ", "", -1) roleIndex := strings.Index(arn, "+arn:aws:iam::") @@ -143,17 +144,18 @@ func NewMasterKeyFromArn(arn string, context map[string]*string) *MasterKey { } k.EncryptionContext = context k.CreationDate = time.Now().UTC() + k.AwsProfile = awsProfile return k } // MasterKeysFromArnString takes a comma separated list of AWS KMS ARNs and returns a slice of new MasterKeys for those ARNs -func MasterKeysFromArnString(arn string, context map[string]*string) []*MasterKey { +func MasterKeysFromArnString(arn string, context map[string]*string, awsProfile string) []*MasterKey { var keys []*MasterKey if arn == "" { return keys } for _, s := range strings.Split(arn, ",") { - keys = append(keys, NewMasterKeyFromArn(s, context)) + keys = append(keys, NewMasterKeyFromArn(s, context, awsProfile)) } return keys } @@ -185,7 +187,7 @@ func (key MasterKey) createSession() (*session.Session, error) { if matches == nil { return nil, fmt.Errorf("No valid ARN found in %q", key.Arn) } - config := aws.Config{Region: aws.String(matches[1])} + config := aws.Config{Region: aws.String(matches[1]), Credentials: credentials.NewSharedCredentials("", key.AwsProfile)} opts := session.Options{ Config: config, AssumeRoleTokenProvider: stscreds.StdinTokenProvider, diff --git a/kms/keysource_test.go b/kms/keysource_test.go index bdb1aafd0..785389cbc 100644 --- a/kms/keysource_test.go +++ b/kms/keysource_test.go @@ -48,7 +48,7 @@ func TestKMS(t *testing.T) { func TestKMSKeySourceFromString(t *testing.T) { s := "arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e+arn:aws:iam::927034868273:role/sops-dev, arn:aws:kms:ap-southeast-1:656532927350:key/9006a8aa-0fa6-4c14-930e-a2dfb916de1d" - ks := MasterKeysFromArnString(s, nil) + ks := MasterKeysFromArnString(s, nil, "foo") k1 := ks[0] k2 := ks[1] expectedArn1 := "arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e" diff --git a/stores/stores.go b/stores/stores.go index 7693572c2..4ab207737 100644 --- a/stores/stores.go +++ b/stores/stores.go @@ -67,6 +67,7 @@ type kmskey struct { Context map[string]*string `yaml:"context,omitempty" json:"context,omitempty"` CreatedAt string `yaml:"created_at" json:"created_at"` EncryptedDataKey string `yaml:"enc" json:"enc"` + AwsProfile string `yaml:"aws_profile" json:"aws_profile"` } type gcpkmskey struct { @@ -135,6 +136,7 @@ func kmsKeysFromGroup(group sops.KeyGroup) (keys []kmskey) { EncryptedDataKey: key.EncryptedKey, Context: key.EncryptionContext, Role: key.Role, + AwsProfile: key.AwsProfile, }) } } @@ -265,6 +267,7 @@ func (kmsKey *kmskey) toInternal() (*kms.MasterKey, error) { EncryptedKey: kmsKey.EncryptedDataKey, CreationDate: creationDate, Arn: kmsKey.Arn, + AwsProfile: kmsKey.AwsProfile, }, nil }