Skip to content

Commit e76d85e

Browse files
committed
feat: add Tencent Cloud KMS support
Add support for Tencent Cloud Key Management Service (KMS) as a new encryption provider for SOPS. This implementation follows the same pattern as existing cloud KMS integrations. Features: - Encrypt/decrypt SOPS data keys using Tencent Cloud KMS - CLI flags: --tencent-kms, --add-tencent-kms, --rm-tencent-kms - Environment variable: SOPS_TENCENT_KMS_IDS - Configuration file support: tencent_kms key in .sops.yaml - Optional STS token support via TENCENTCLOUD_TOKEN - Customizable endpoint for CVM/TKE internal networks Authentication via environment variables: - TENCENTCLOUD_SECRET_ID (required) - TENCENTCLOUD_SECRET_KEY (required) - TENCENTCLOUD_TOKEN (optional, for STS) - TENCENTCLOUD_REGION (optional, default: ap-guangzhou) - TENCENTCLOUD_KMS_ENDPOINT (optional)
1 parent 28e5563 commit e76d85e

14 files changed

Lines changed: 1256 additions & 328 deletions

cmd/sops/main.go

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@ import (
3737
"github.com/getsops/sops/v3/hckms"
3838
"github.com/getsops/sops/v3/hcvault"
3939
"github.com/getsops/sops/v3/keys"
40+
"github.com/getsops/sops/v3/tencentkms"
4041
"github.com/getsops/sops/v3/keyservice"
4142
"github.com/getsops/sops/v3/kms"
4243
"github.com/getsops/sops/v3/logging"
44+
4345
"github.com/getsops/sops/v3/pgp"
4446
"github.com/getsops/sops/v3/stores"
4547
"github.com/getsops/sops/v3/stores/dotenv"
@@ -91,7 +93,8 @@ func main() {
9193
},
9294
}
9395
app.Name = "sops"
94-
app.Usage = "sops - encrypted file editor with AWS KMS, GCP KMS, HuaweiCloud KMS, Azure Key Vault, age, and GPG support"
96+
app.Usage = "sops - encrypted file editor with AWS KMS, GCP KMS, Tencent Cloud KMS, HuaweiCloud KMS, Azure Key Vault, age, and GPG support"
97+
9598
app.ArgsUsage = "sops [options] file"
9699
app.Version = version.Version
97100
app.Authors = []cli.Author{
@@ -110,7 +113,14 @@ func main() {
110113
(You need to setup Google application default credentials. See
111114
https://developers.google.com/identity/protocols/application-default-credentials)
112115
116+
To encrypt or decrypt a document with Tencent Cloud KMS, specify the
117+
Tencent Cloud KMS key ID in the --tencent-kms flag or in the
118+
SOPS_TENCENT_KMS_IDS environment variable.
119+
(You need to setup Tencent Cloud credentials via TENCENTCLOUD_SECRET_ID,
120+
TENCENTCLOUD_SECRET_KEY, optional TENCENTCLOUD_TOKEN for STS, and TENCENTCLOUD_REGION)
121+
113122
To encrypt or decrypt a document with HuaweiCloud KMS, specify the
123+
114124
HuaweiCloud KMS key ID (format: region:key-uuid) in the --hckms flag or in the
115125
SOPS_HUAWEICLOUD_KMS_IDS environment variable.
116126
(You need to setup HuaweiCloud credentials via environment variables:
@@ -955,6 +965,7 @@ func main() {
955965
Usage: "comma separated list of Azure Key Vault URLs",
956966
EnvVar: "SOPS_AZURE_KEYVAULT_URLS",
957967
},
968+
958969
cli.StringFlag{
959970
Name: "hc-vault-transit",
960971
Usage: "comma separated list of vault's key URI (e.g. 'https://vault.example.org:8200/v1/transit/keys/dev')",
@@ -970,6 +981,11 @@ func main() {
970981
Usage: "comma separated list of age recipients",
971982
EnvVar: "SOPS_AGE_RECIPIENTS",
972983
},
984+
cli.StringFlag{
985+
Name: "tencent-kms",
986+
Usage: "comma separated list of Tencent Cloud KMS key IDs",
987+
EnvVar: "SOPS_TENCENT_KMS_IDS",
988+
},
973989
cli.StringFlag{
974990
Name: "input-type",
975991
Usage: "currently json, yaml, dotenv and binary are supported. If not set, sops will use the file's extension to determine the type",
@@ -1306,6 +1322,7 @@ func main() {
13061322
Usage: "comma separated list of Azure Key Vault URLs",
13071323
EnvVar: "SOPS_AZURE_KEYVAULT_URLS",
13081324
},
1325+
13091326
cli.StringFlag{
13101327
Name: "hc-vault-transit",
13111328
Usage: "comma separated list of vault's key URI (e.g. 'https://vault.example.org:8200/v1/transit/keys/dev')",
@@ -1321,6 +1338,11 @@ func main() {
13211338
Usage: "comma separated list of age recipients",
13221339
EnvVar: "SOPS_AGE_RECIPIENTS",
13231340
},
1341+
cli.StringFlag{
1342+
Name: "tencent-kms",
1343+
Usage: "comma separated list of Tencent Cloud KMS key IDs",
1344+
EnvVar: "SOPS_TENCENT_KMS_IDS",
1345+
},
13241346
cli.StringFlag{
13251347
Name: "input-type",
13261348
Usage: "currently json, yaml, dotenv and binary are supported. If not set, sops will use the file's extension to determine the type",
@@ -1704,21 +1726,22 @@ func main() {
17041726
Name: "aws-profile",
17051727
Usage: "The AWS profile to use for requests to AWS",
17061728
},
1707-
cli.StringFlag{
1708-
Name: "gcp-kms",
1709-
Usage: "comma separated list of GCP KMS resource IDs",
1710-
EnvVar: "SOPS_GCP_KMS_IDS",
1711-
},
1712-
cli.StringFlag{
1713-
Name: "hckms",
1714-
Usage: "comma separated list of HuaweiCloud KMS key IDs (format: region:key-uuid)",
1715-
EnvVar: "SOPS_HUAWEICLOUD_KMS_IDS",
1716-
},
1717-
cli.StringFlag{
1718-
Name: "azure-kv",
1719-
Usage: "comma separated list of Azure Key Vault URLs",
1720-
EnvVar: "SOPS_AZURE_KEYVAULT_URLS",
1721-
},
1729+
cli.StringFlag{
1730+
Name: "gcp-kms",
1731+
Usage: "comma separated list of GCP KMS resource IDs",
1732+
EnvVar: "SOPS_GCP_KMS_IDS",
1733+
},
1734+
cli.StringFlag{
1735+
Name: "hckms",
1736+
Usage: "comma separated list of HuaweiCloud KMS key IDs (format: region:key-uuid)",
1737+
EnvVar: "SOPS_HUAWEICLOUD_KMS_IDS",
1738+
},
1739+
cli.StringFlag{
1740+
Name: "azure-kv",
1741+
Usage: "comma separated list of Azure Key Vault URLs",
1742+
EnvVar: "SOPS_AZURE_KEYVAULT_URLS",
1743+
},
1744+
17221745
cli.StringFlag{
17231746
Name: "hc-vault-transit",
17241747
Usage: "comma separated list of vault's key URI (e.g. 'https://vault.example.org:8200/v1/transit/keys/dev')",
@@ -1734,6 +1757,11 @@ func main() {
17341757
Usage: "comma separated list of age recipients",
17351758
EnvVar: "SOPS_AGE_RECIPIENTS",
17361759
},
1760+
cli.StringFlag{
1761+
Name: "tencent-kms",
1762+
Usage: "comma separated list of Tencent Cloud KMS key IDs",
1763+
EnvVar: "SOPS_TENCENT_KMS_IDS",
1764+
},
17371765
cli.BoolFlag{
17381766
Name: "in-place, i",
17391767
Usage: "write output back to the same file instead of stdout",
@@ -1810,6 +1838,14 @@ func main() {
18101838
Name: "rm-pgp",
18111839
Usage: "remove the provided comma-separated list of PGP fingerprints from the list of master keys on the given file",
18121840
},
1841+
cli.StringFlag{
1842+
Name: "add-tencent-kms",
1843+
Usage: "add the provided comma-separated list of Tencent Cloud KMS key IDs to the list of master keys on the given file",
1844+
},
1845+
cli.StringFlag{
1846+
Name: "rm-tencent-kms",
1847+
Usage: "remove the provided comma-separated list of Tencent Cloud KMS key IDs from the list of master keys on the given file",
1848+
},
18131849
cli.BoolFlag{
18141850
Name: "ignore-mac",
18151851
Usage: "ignore Message Authentication Code during decryption",
@@ -2235,7 +2271,7 @@ func getEncryptConfig(c *cli.Context, fileName string, inputStore common.Store,
22352271
}, nil
22362272
}
22372273

2238-
func getMasterKeys(c *cli.Context, kmsEncryptionContext map[string]*string, kmsOptionName string, pgpOptionName string, gcpKmsOptionName string, hckmsOptionName string, azureKvOptionName string, hcVaultTransitOptionName string, ageOptionName string) ([]keys.MasterKey, error) {
2274+
func getMasterKeys(c *cli.Context, kmsEncryptionContext map[string]*string, kmsOptionName string, pgpOptionName string, gcpKmsOptionName string, hckmsOptionName string, azureKvOptionName string, hcVaultTransitOptionName string, ageOptionName string, tencentKmsOptionName string) ([]keys.MasterKey, error) {
22392275
var masterKeys []keys.MasterKey
22402276
for _, k := range kms.MasterKeysFromArnString(c.String(kmsOptionName), kmsEncryptionContext, c.String("aws-profile")) {
22412277
masterKeys = append(masterKeys, k)
@@ -2274,16 +2310,19 @@ func getMasterKeys(c *cli.Context, kmsEncryptionContext map[string]*string, kmsO
22742310
for _, k := range ageKeys {
22752311
masterKeys = append(masterKeys, k)
22762312
}
2313+
for _, k := range tencentkms.MasterKeysFromKeyIDString(c.String(tencentKmsOptionName)) {
2314+
masterKeys = append(masterKeys, k)
2315+
}
22772316
return masterKeys, nil
22782317
}
22792318

22802319
func getRotateOpts(c *cli.Context, fileName string, inputStore common.Store, outputStore common.Store, svcs []keyservice.KeyServiceClient, decryptionOrder []string) (rotateOpts, error) {
22812320
kmsEncryptionContext := kms.ParseKMSContext(c.String("encryption-context"))
2282-
addMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "add-kms", "add-pgp", "add-gcp-kms", "add-hckms", "add-azure-kv", "add-hc-vault-transit", "add-age")
2321+
addMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "add-kms", "add-pgp", "add-gcp-kms", "add-hckms", "add-azure-kv", "add-hc-vault-transit", "add-age", "add-tencent-kms")
22832322
if err != nil {
22842323
return rotateOpts{}, err
22852324
}
2286-
rmMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "rm-kms", "rm-pgp", "rm-gcp-kms", "rm-hckms", "rm-azure-kv", "rm-hc-vault-transit", "rm-age")
2325+
rmMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "rm-kms", "rm-pgp", "rm-gcp-kms", "rm-hckms", "rm-azure-kv", "rm-hc-vault-transit", "rm-age", "rm-tencent-kms")
22872326
if err != nil {
22882327
return rotateOpts{}, err
22892328
}
@@ -2433,6 +2472,7 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so
24332472
var hcVaultMkKeys []keys.MasterKey
24342473
var hckmsMkKeys []keys.MasterKey
24352474
var ageMasterKeys []keys.MasterKey
2475+
var tencentkmsMkKeys []keys.MasterKey
24362476
kmsEncryptionContext := kms.ParseKMSContext(c.String("encryption-context"))
24372477
if c.String("encryption-context") != "" && kmsEncryptionContext == nil {
24382478
return nil, common.NewExitError("Invalid KMS encryption context format", codes.ErrorInvalidKMSEncryptionContextFormat)
@@ -2488,7 +2528,12 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so
24882528
ageMasterKeys = append(ageMasterKeys, k)
24892529
}
24902530
}
2491-
if c.String("kms") == "" && c.String("pgp") == "" && c.String("gcp-kms") == "" && c.String("hckms") == "" && c.String("azure-kv") == "" && c.String("hc-vault-transit") == "" && c.String("age") == "" {
2531+
if c.String("tencent-kms") != "" {
2532+
for _, k := range tencentkms.MasterKeysFromKeyIDString(c.String("tencent-kms")) {
2533+
tencentkmsMkKeys = append(tencentkmsMkKeys, k)
2534+
}
2535+
}
2536+
if c.String("kms") == "" && c.String("pgp") == "" && c.String("gcp-kms") == "" && c.String("hckms") == "" && c.String("azure-kv") == "" && c.String("hc-vault-transit") == "" && c.String("age") == "" && c.String("tencent-kms") == "" {
24922537
conf := optionalConfig
24932538
var err error
24942539
if conf == nil {
@@ -2512,6 +2557,7 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so
25122557
group = append(group, pgpKeys...)
25132558
group = append(group, hcVaultMkKeys...)
25142559
group = append(group, ageMasterKeys...)
2560+
group = append(group, tencentkmsMkKeys...)
25152561
log.Debugf("Master keys available: %+v", group)
25162562
return []sops.KeyGroup{group}, nil
25172563
}

config/config.go

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package config //import "github.com/getsops/sops/v3/config"
55

66
import (
77
"fmt"
8+
"github.com/getsops/sops/v3/tencentkms"
89
"os"
910
"path"
1011
"path/filepath"
@@ -130,14 +131,19 @@ type configFile struct {
130131
}
131132

132133
type keyGroup struct {
133-
Merge []keyGroup `yaml:"merge"`
134-
KMS []kmsKey `yaml:"kms"`
135-
GCPKMS []gcpKmsKey `yaml:"gcp_kms"`
136-
HCKms []hckmsKey `yaml:"hckms"`
137-
AzureKV []azureKVKey `yaml:"azure_keyvault"`
138-
Vault []string `yaml:"hc_vault"`
139-
Age []string `yaml:"age"`
140-
PGP []string `yaml:"pgp"`
134+
Merge []keyGroup `yaml:"merge"`
135+
KMS []kmsKey `yaml:"kms"`
136+
GCPKMS []gcpKmsKey `yaml:"gcp_kms"`
137+
HCKms []hckmsKey `yaml:"hckms"`
138+
AzureKV []azureKVKey `yaml:"azure_keyvault"`
139+
Vault []string `yaml:"hc_vault"`
140+
Age []string `yaml:"age"`
141+
PGP []string `yaml:"pgp"`
142+
TencentKMS []tencentKMSKey `yaml:"tencent_kms"`
143+
}
144+
145+
type tencentKMSKey struct {
146+
KeyID string `yaml:"key_id"`
141147
}
142148

143149
type gcpKmsKey struct {
@@ -185,6 +191,7 @@ type creationRule struct {
185191
HCKms []string `yaml:"hckms"`
186192
AzureKeyVault interface{} `yaml:"azure_keyvault"` // string or []string
187193
VaultURI interface{} `yaml:"hc_vault_transit_uri"` // string or []string
194+
TencentKMS []string `yaml:"tencent_kms"`
188195
KeyGroups []keyGroup `yaml:"key_groups"`
189196
ShamirThreshold int `yaml:"shamir_threshold"`
190197
UnencryptedSuffix string `yaml:"unencrypted_suffix"`
@@ -357,6 +364,9 @@ func extractMasterKeys(group keyGroup) (sops.KeyGroup, error) {
357364
return nil, err
358365
}
359366
}
367+
for _, k := range group.TencentKMS {
368+
keyGroup = append(keyGroup, tencentkms.NewMasterKeyFromKeyID(k.KeyID))
369+
}
360370
return deduplicateKeygroup(keyGroup), nil
361371
}
362372

@@ -445,6 +455,11 @@ func getKeyGroupsFromCreationRule(cRule *creationRule, kmsEncryptionContext map[
445455
for _, k := range vaultKeys {
446456
keyGroup = append(keyGroup, k)
447457
}
458+
// Tencent KMS
459+
tencentkmsMasterKeys := tencentkms.MasterKeysFromKeyIDString(strings.Join(cRule.TencentKMS, ","))
460+
for _, k := range tencentkmsMasterKeys {
461+
keyGroup = append(keyGroup, k)
462+
}
448463
groups = append(groups, keyGroup)
449464
}
450465
return groups, nil

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ require (
3333
github.com/pkg/errors v0.9.1
3434
github.com/sirupsen/logrus v1.9.3
3535
github.com/stretchr/testify v1.11.1
36+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.24
37+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.3.7
3638
github.com/urfave/cli v1.22.17
3739
go.yaml.in/yaml/v3 v3.0.4
3840
golang.org/x/crypto v0.46.0

go.sum

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,11 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
328328
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
329329
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
330330
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
331+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.7/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
332+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.24 h1:A0FLutAc8Qvzb4Ulz7e0otGwksM7dR9no8/AiIZj9kM=
333+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.24/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
334+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.3.7 h1:xcpb2zxYNLoARq+hk91TWDtthIeuTWkhmRkZmSdbOOw=
335+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.3.7/go.mod h1:88xdoOQOMaevSlWGF5Ig7g6iNOSjTvAaUzAcOS8xnJc=
331336
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
332337
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
333338
github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=

keyservice/keyservice.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package keyservice
66

77
import (
88
"fmt"
9+
"github.com/getsops/sops/v3/tencentkms"
910

1011
"github.com/getsops/sops/v3/age"
1112
"github.com/getsops/sops/v3/azkv"
@@ -87,6 +88,14 @@ func KeyFromMasterKey(mk keys.MasterKey) Key {
8788
},
8889
},
8990
}
91+
case *tencentkms.MasterKey:
92+
return Key{
93+
KeyType: &Key_TencentKey{
94+
TencentKey: &TencentKey{
95+
KeyId: mk.KeyID,
96+
},
97+
},
98+
}
9099
default:
91100
panic(fmt.Sprintf("Tried to convert unknown MasterKey type %T to keyservice.Key", mk))
92101
}

0 commit comments

Comments
 (0)