Skip to content

Commit f6f28fc

Browse files
committed
Allow SOPS to use custom AWS KMS and STS Endpoint
Signed-off-by: anandavj <anandaprabu.trityavijaya@gmail.com>
1 parent db5356d commit f6f28fc

8 files changed

Lines changed: 87 additions & 30 deletions

File tree

cmd/sops/main.go

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,14 @@ func main() {
501501
Name: "aws-profile",
502502
Usage: "The AWS profile to use for requests to AWS",
503503
},
504+
cli.StringFlag{
505+
Name: "aws-kms-endpoint",
506+
Usage: "The AWS KMS Endpoint to use for requests to AWS. Ex: https://kms.ap-southeast-2.amazonaws.com",
507+
},
508+
cli.StringFlag{
509+
Name: "aws-sts-endpoint",
510+
Usage: "The AWS STS Endpoint to use for requests to AWS. Ex: https://sts.ap-southeast-2.amazonaws.com",
511+
},
504512
cli.StringSliceFlag{
505513
Name: "gcp-kms",
506514
Usage: "the GCP KMS Resource ID the new group should contain. Can be specified more than once",
@@ -545,7 +553,7 @@ func main() {
545553
group = append(group, pgp.NewMasterKeyFromFingerprint(fp))
546554
}
547555
for _, arn := range kmsArns {
548-
group = append(group, kms.NewMasterKeyFromArn(arn, kms.ParseKMSContext(c.String("encryption-context")), c.String("aws-profile")))
556+
group = append(group, kms.NewMasterKeyFromArn(arn, kms.ParseKMSContext(c.String("encryption-context")), c.String("aws-profile"), c.String("aws-kms-endpoint"), c.String("aws-sts-endpoint")))
549557
}
550558
for _, kms := range gcpKmses {
551559
group = append(group, gcpkms.NewMasterKeyFromResourceID(kms))
@@ -852,6 +860,14 @@ func main() {
852860
Name: "aws-profile",
853861
Usage: "The AWS profile to use for requests to AWS",
854862
},
863+
cli.StringFlag{
864+
Name: "aws-kms-endpoint",
865+
Usage: "The AWS KMS Endpoint to use for requests to AWS",
866+
},
867+
cli.StringFlag{
868+
Name: "aws-sts-endpoint",
869+
Usage: "The AWS STS Endpoint to use for requests to AWS",
870+
},
855871
cli.StringFlag{
856872
Name: "gcp-kms",
857873
Usage: "comma separated list of GCP KMS resource IDs",
@@ -1169,6 +1185,14 @@ func main() {
11691185
Name: "aws-profile",
11701186
Usage: "The AWS profile to use for requests to AWS",
11711187
},
1188+
cli.StringFlag{
1189+
Name: "aws-kms-endpoint",
1190+
Usage: "The AWS KMS Endpoint to use for requests to AWS",
1191+
},
1192+
cli.StringFlag{
1193+
Name: "aws-sts-endpoint",
1194+
Usage: "The AWS STS Endpoint to use for requests to AWS",
1195+
},
11721196
cli.StringFlag{
11731197
Name: "gcp-kms",
11741198
Usage: "comma separated list of GCP KMS resource IDs",
@@ -1529,6 +1553,14 @@ func main() {
15291553
Name: "aws-profile",
15301554
Usage: "The AWS profile to use for requests to AWS",
15311555
},
1556+
cli.StringFlag{
1557+
Name: "aws-kms-endpoint",
1558+
Usage: "The AWS KMS Endpoint to use for requests to AWS",
1559+
},
1560+
cli.StringFlag{
1561+
Name: "aws-sts-endpoint",
1562+
Usage: "The AWS STS Endpoint to use for requests to AWS",
1563+
},
15321564
cli.StringFlag{
15331565
Name: "gcp-kms",
15341566
Usage: "comma separated list of GCP KMS resource IDs",
@@ -2006,7 +2038,7 @@ func getEncryptConfig(c *cli.Context, fileName string) (encryptConfig, error) {
20062038

20072039
func getMasterKeys(c *cli.Context, kmsEncryptionContext map[string]*string, kmsOptionName string, pgpOptionName string, gcpKmsOptionName string, azureKvOptionName string, hcVaultTransitOptionName string, ageOptionName string) ([]keys.MasterKey, error) {
20082040
var masterKeys []keys.MasterKey
2009-
for _, k := range kms.MasterKeysFromArnString(c.String(kmsOptionName), kmsEncryptionContext, c.String("aws-profile")) {
2041+
for _, k := range kms.MasterKeysFromArnString(c.String(kmsOptionName), kmsEncryptionContext, c.String("aws-profile"), c.String("aws-kms-endpoint"), c.String("aws-sts-endpoint")) {
20102042
masterKeys = append(masterKeys, k)
20112043
}
20122044
for _, k := range pgp.MasterKeysFromFingerprintString(c.String(pgpOptionName)) {
@@ -2185,7 +2217,7 @@ func keyGroups(c *cli.Context, file string) ([]sops.KeyGroup, error) {
21852217
return nil, common.NewExitError("Invalid KMS encryption context format", codes.ErrorInvalidKMSEncryptionContextFormat)
21862218
}
21872219
if c.String("kms") != "" {
2188-
for _, k := range kms.MasterKeysFromArnString(c.String("kms"), kmsEncryptionContext, c.String("aws-profile")) {
2220+
for _, k := range kms.MasterKeysFromArnString(c.String("kms"), kmsEncryptionContext, c.String("aws-profile"), c.String("aws-kms-endpoint"), c.String("aws-sts-endpoint")) {
21892221
kmsKeys = append(kmsKeys, k)
21902222
}
21912223
}

config/config.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ type kmsKey struct {
112112
Role string `yaml:"role,omitempty"`
113113
Context map[string]*string `yaml:"context"`
114114
AwsProfile string `yaml:"aws_profile"`
115+
AwsKmsEndpoint string `yaml:"aws_kms_endpoint"`
116+
AwsStsEndpoint string `yaml:"aws_sts_endpoint"`
115117
}
116118

117119
type azureKVKey struct {
@@ -138,6 +140,8 @@ type creationRule struct {
138140
PathRegex string `yaml:"path_regex"`
139141
KMS string
140142
AwsProfile string `yaml:"aws_profile"`
143+
AwsKmsEndpoint string `yaml:"aws_kms_endpoint"`
144+
AwsStsEndpoint string `yaml:"aws_sts_endpoint"`
141145
Age string `yaml:"age"`
142146
PGP string
143147
GCPKMS string `yaml:"gcp_kms"`
@@ -226,7 +230,7 @@ func extractMasterKeys(group keyGroup) (sops.KeyGroup, error) {
226230
keyGroup = append(keyGroup, pgp.NewMasterKeyFromFingerprint(k))
227231
}
228232
for _, k := range group.KMS {
229-
keyGroup = append(keyGroup, kms.NewMasterKeyWithProfile(k.Arn, k.Role, k.Context, k.AwsProfile))
233+
keyGroup = append(keyGroup, kms.NewMasterKeyWithProfile(k.Arn, k.Role, k.Context, k.AwsProfile, k.AwsKmsEndpoint, k.AwsStsEndpoint))
230234
}
231235
for _, k := range group.GCPKMS {
232236
keyGroup = append(keyGroup, gcpkms.NewMasterKeyFromResourceID(k.ResourceID))
@@ -269,7 +273,7 @@ func getKeyGroupsFromCreationRule(cRule *creationRule, kmsEncryptionContext map[
269273
for _, k := range pgp.MasterKeysFromFingerprintString(cRule.PGP) {
270274
keyGroup = append(keyGroup, k)
271275
}
272-
for _, k := range kms.MasterKeysFromArnString(cRule.KMS, kmsEncryptionContext, cRule.AwsProfile) {
276+
for _, k := range kms.MasterKeysFromArnString(cRule.KMS, kmsEncryptionContext, cRule.AwsProfile, cRule.AwsKmsEndpoint, cRule.AwsStsEndpoint) {
273277
keyGroup = append(keyGroup, k)
274278
}
275279
for _, k := range gcpkms.MasterKeysFromResourceIDString(cRule.GCPKMS) {

keyservice/keyservice.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ func KeyFromMasterKey(mk keys.MasterKey) Key {
5757
Role: mk.Role,
5858
Context: ctx,
5959
AwsProfile: mk.AwsProfile,
60+
AwsKmsEndpoint: mk.AwsKmsEndpoint,
61+
AwsStsEndpoint: mk.AwsStsEndpoint,
6062
},
6163
},
6264
}

keyservice/keyservice.pb.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

keyservice/server.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,5 +323,7 @@ func kmsKeyToMasterKey(key *KmsKey) kms.MasterKey {
323323
Role: key.Role,
324324
EncryptionContext: ctx,
325325
AwsProfile: key.AwsProfile,
326+
AwsKmsEndpoint: key.AwsKmsEndpoint,
327+
AwsStsEndpoint: key.AwsStsEndpoint,
326328
}
327329
}

kms/keysource.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -73,35 +73,38 @@ type MasterKey struct {
7373
// using CredentialsProvider.ApplyToMasterKey. If nil, the default client is used
7474
// which utilizes runtime environmental values.
7575
credentialsProvider aws.CredentialsProvider
76-
// baseEndpoint can be used to override the endpoint the AWS client resolves
77-
// to by default. This is mostly used for testing purposes as it can not be
78-
// injected using e.g. an environment variable. The field is not publicly
79-
// exposed, nor configurable.
80-
baseEndpoint string
76+
// AwsKmsEndpoint can be used to override the endpoint the AWS client resolves
77+
// to by default. This is mostly used for custom AWS that has custom endpoint.
78+
AwsKmsEndpoint string
79+
// AwsStsEndpoint can be used to override the endpoint the AWS client resolves
80+
// to by default. This is mostly used for custom AWS that has custom endpoint.
81+
AwsStsEndpoint string
8182
}
8283

8384
// NewMasterKey creates a new MasterKey from an ARN, role and context, setting
8485
// the creation date to the current date.
85-
func NewMasterKey(arn string, role string, context map[string]*string) *MasterKey {
86+
func NewMasterKey(arn string, role string, context map[string]*string, awsKmsEndpoint string, awsStsEndpoint string) *MasterKey {
8687
return &MasterKey{
8788
Arn: arn,
8889
Role: role,
8990
EncryptionContext: context,
9091
CreationDate: time.Now().UTC(),
92+
AwsKmsEndpoint: awsKmsEndpoint,
93+
AwsStsEndpoint: awsStsEndpoint,
9194
}
9295
}
9396

9497
// NewMasterKeyWithProfile creates a new MasterKey from an ARN, role, context
9598
// and awsProfile, setting the creation date to the current date.
96-
func NewMasterKeyWithProfile(arn string, role string, context map[string]*string, awsProfile string) *MasterKey {
97-
k := NewMasterKey(arn, role, context)
99+
func NewMasterKeyWithProfile(arn string, role string, context map[string]*string, awsProfile string, awsKmsEndpoint string, awsStsEndpoint string) *MasterKey {
100+
k := NewMasterKey(arn, role, context, awsKmsEndpoint, awsStsEndpoint)
98101
k.AwsProfile = awsProfile
99102
return k
100103
}
101104

102105
// NewMasterKeyFromArn takes an ARN string and returns a new MasterKey for that
103106
// ARN.
104-
func NewMasterKeyFromArn(arn string, context map[string]*string, awsProfile string) *MasterKey {
107+
func NewMasterKeyFromArn(arn string, context map[string]*string, awsProfile string, awsKmsEndpoint string, awsStsEndpoint string) *MasterKey {
105108
key := &MasterKey{}
106109
arn = strings.Replace(arn, " ", "", -1)
107110
key.Arn = arn
@@ -114,18 +117,20 @@ func NewMasterKeyFromArn(arn string, context map[string]*string, awsProfile stri
114117
key.EncryptionContext = context
115118
key.CreationDate = time.Now().UTC()
116119
key.AwsProfile = awsProfile
120+
key.AwsKmsEndpoint = awsKmsEndpoint
121+
key.AwsStsEndpoint = awsStsEndpoint
117122
return key
118123
}
119124

120125
// MasterKeysFromArnString takes a comma separated list of AWS KMS ARNs, and
121126
// returns a slice of new MasterKeys for those ARNs.
122-
func MasterKeysFromArnString(arn string, context map[string]*string, awsProfile string) []*MasterKey {
127+
func MasterKeysFromArnString(arn string, context map[string]*string, awsProfile string, awsKmsEndpoint string, awsStsEndpoint string) []*MasterKey {
123128
var keys []*MasterKey
124129
if arn == "" {
125130
return keys
126131
}
127132
for _, s := range strings.Split(arn, ",") {
128-
keys = append(keys, NewMasterKeyFromArn(s, context, awsProfile))
133+
keys = append(keys, NewMasterKeyFromArn(s, context, awsProfile, awsKmsEndpoint, awsStsEndpoint))
129134
}
130135
return keys
131136
}
@@ -340,8 +345,8 @@ func (key MasterKey) createKMSConfig() (*aws.Config, error) {
340345
// createClient creates a new AWS KMS client with the provided config.
341346
func (key MasterKey) createClient(config *aws.Config) *kms.Client {
342347
return kms.NewFromConfig(*config, func(o *kms.Options) {
343-
if key.baseEndpoint != "" {
344-
o.BaseEndpoint = aws.String(key.baseEndpoint)
348+
if key.AwsKmsEndpoint != "" {
349+
o.BaseEndpoint = aws.String(key.AwsKmsEndpoint)
345350
}
346351
})
347352
}
@@ -359,7 +364,11 @@ func (key MasterKey) createSTSConfig(config *aws.Config) (*aws.Config, error) {
359364
RoleSessionName: &name,
360365
}
361366

362-
client := sts.NewFromConfig(*config)
367+
client := sts.NewFromConfig(*config, func(o *sts.Options) {
368+
if key.AwsStsEndpoint != "" {
369+
o.BaseEndpoint = aws.String(key.AwsStsEndpoint)
370+
}
371+
})
363372
out, err := client.AssumeRole(context.TODO(), input)
364373
if err != nil {
365374
return nil, fmt.Errorf("failed to assume role '%s': %w", key.Role, err)

kms/keysource_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ func TestNewMasterKey(t *testing.T) {
116116
"foo": aws.String("bar"),
117117
}
118118
)
119-
key := NewMasterKey(dummyARN, dummyRole, dummyEncryptionContext)
119+
key := NewMasterKey(dummyARN, dummyRole, dummyEncryptionContext, "", "")
120120
assert.Equal(t, dummyARN, key.Arn)
121121
assert.Equal(t, dummyRole, key.Role)
122122
assert.Equal(t, dummyEncryptionContext, key.EncryptionContext)
@@ -131,7 +131,7 @@ func TestNewMasterKeyWithProfile(t *testing.T) {
131131
}
132132
dummyProfile = "a-profile"
133133
)
134-
key := NewMasterKeyWithProfile(dummyARN, dummyRole, dummyEncryptionContext, dummyProfile)
134+
key := NewMasterKeyWithProfile(dummyARN, dummyRole, dummyEncryptionContext, dummyProfile, "", "")
135135
assert.Equal(t, dummyARN, key.Arn)
136136
assert.Equal(t, dummyRole, key.Role)
137137
assert.Equal(t, dummyEncryptionContext, key.EncryptionContext)
@@ -147,7 +147,7 @@ func TestNewMasterKeyFromArn(t *testing.T) {
147147
}
148148
dummyProfile = "a-profile"
149149
)
150-
key := NewMasterKeyFromArn(dummyARN, dummyEncryptionContext, dummyProfile)
150+
key := NewMasterKeyFromArn(dummyARN, dummyEncryptionContext, dummyProfile, "", "")
151151
assert.Equal(t, dummyARN, key.Arn)
152152
assert.Equal(t, dummyEncryptionContext, key.EncryptionContext)
153153
assert.Equal(t, dummyProfile, key.AwsProfile)
@@ -156,20 +156,20 @@ func TestNewMasterKeyFromArn(t *testing.T) {
156156
})
157157

158158
t.Run("arn with spaces", func(t *testing.T) {
159-
key := NewMasterKeyFromArn(" arn:aws:kms:us-west-2 :107501996527:key/612d5f 0p-p1l3-45e6-aca6-a5b00569 3a48 ", nil, "")
159+
key := NewMasterKeyFromArn(" arn:aws:kms:us-west-2 :107501996527:key/612d5f 0p-p1l3-45e6-aca6-a5b00569 3a48 ", nil, "", "", "")
160160
assert.Equal(t, "arn:aws:kms:us-west-2:107501996527:key/612d5f0p-p1l3-45e6-aca6-a5b005693a48", key.Arn)
161161
})
162162

163163
t.Run("arn with role", func(t *testing.T) {
164-
key := NewMasterKeyFromArn("arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500+arn:aws:iam::927034868273:role/sops-dev-xyz", nil, "")
164+
key := NewMasterKeyFromArn("arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500+arn:aws:iam::927034868273:role/sops-dev-xyz", nil, "", "", "")
165165
assert.Equal(t, "arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500", key.Arn)
166166
assert.Equal(t, "arn:aws:iam::927034868273:role/sops-dev-xyz", key.Role)
167167
})
168168
}
169169

170170
func TestMasterKeysFromArnString(t *testing.T) {
171171
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"
172-
ks := MasterKeysFromArnString(s, nil, "foo")
172+
ks := MasterKeysFromArnString(s, nil, "foo", "", "")
173173
k1 := ks[0]
174174
k2 := ks[1]
175175

@@ -359,15 +359,15 @@ func TestMasterKey_EncryptDecrypt_RoundTrip(t *testing.T) {
359359
}
360360

361361
func TestMasterKey_NeedsRotation(t *testing.T) {
362-
key := NewMasterKeyFromArn(dummyARN, nil, "")
362+
key := NewMasterKeyFromArn(dummyARN, nil, "", "", "")
363363
assert.False(t, key.NeedsRotation())
364364

365365
key.CreationDate = key.CreationDate.Add(-(kmsTTL + time.Second))
366366
assert.True(t, key.NeedsRotation())
367367
}
368368

369369
func TestMasterKey_ToString(t *testing.T) {
370-
key := NewMasterKeyFromArn(dummyARN, nil, "")
370+
key := NewMasterKeyFromArn(dummyARN, nil, "", "", "")
371371
assert.Equal(t, dummyARN, key.ToString())
372372
}
373373

@@ -518,15 +518,15 @@ func TestMasterKey_createSTSConfig(t *testing.T) {
518518
err = fmt.Errorf("an error")
519519
return
520520
}
521-
key := NewMasterKeyFromArn(dummyARN, nil, "")
521+
key := NewMasterKeyFromArn(dummyARN, nil, "", "", "")
522522
cfg, err := key.createSTSConfig(nil)
523523
assert.Error(t, err)
524524
assert.ErrorContains(t, err, "failed to construct STS session name")
525525
assert.Nil(t, cfg)
526526
})
527527

528528
t.Run("role assumption error", func(t *testing.T) {
529-
key := NewMasterKeyFromArn(dummyARN, nil, "")
529+
key := NewMasterKeyFromArn(dummyARN, nil, "", "", "")
530530
key.Role = "role"
531531
got, err := key.createSTSConfig(&aws.Config{})
532532
assert.Error(t, err)
@@ -592,7 +592,7 @@ func createTestMasterKey(arn string) MasterKey {
592592
return MasterKey{
593593
Arn: arn,
594594
credentialsProvider: credentials.NewStaticCredentialsProvider("id", "secret", ""),
595-
baseEndpoint: testKMSServerURL,
595+
AwsKmsEndpoint: testKMSServerURL,
596596
}
597597
}
598598

stores/stores.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ type kmskey struct {
8484
CreatedAt string `yaml:"created_at" json:"created_at"`
8585
EncryptedDataKey string `yaml:"enc" json:"enc"`
8686
AwsProfile string `yaml:"aws_profile" json:"aws_profile"`
87+
AwsKmsEndpoint string `yaml:"aws_kms_endpoint" json:"aws_kms_endpoint"`
88+
AwsStsEndpoint string `yaml:"aws_sts_endpoint" json:"aws_sts_endpoint"`
8789
}
8890

8991
type gcpkmskey struct {
@@ -175,6 +177,8 @@ func kmsKeysFromGroup(group sops.KeyGroup) (keys []kmskey) {
175177
Context: key.EncryptionContext,
176178
Role: key.Role,
177179
AwsProfile: key.AwsProfile,
180+
AwsKmsEndpoint: key.AwsKmsEndpoint,
181+
AwsStsEndpoint: key.AwsStsEndpoint,
178182
})
179183
}
180184
}
@@ -376,6 +380,8 @@ func (kmsKey *kmskey) toInternal() (*kms.MasterKey, error) {
376380
CreationDate: creationDate,
377381
Arn: kmsKey.Arn,
378382
AwsProfile: kmsKey.AwsProfile,
383+
AwsKmsEndpoint: kmsKey.AwsKmsEndpoint,
384+
AwsStsEndpoint: kmsKey.AwsStsEndpoint,
379385
}, nil
380386
}
381387

0 commit comments

Comments
 (0)