Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 73 additions & 13 deletions cmd/sops/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/getsops/sops/v3/cmd/sops/subcommand/updatekeys"
"github.com/getsops/sops/v3/config"
"github.com/getsops/sops/v3/gcpkms"
"github.com/getsops/sops/v3/hckms"
"github.com/getsops/sops/v3/hcvault"
"github.com/getsops/sops/v3/keys"
"github.com/getsops/sops/v3/keyservice"
Expand Down Expand Up @@ -90,13 +91,13 @@ func main() {
},
}
app.Name = "sops"
app.Usage = "sops - encrypted file editor with AWS KMS, GCP KMS, Azure Key Vault, age, and GPG support"
app.Usage = "sops - encrypted file editor with AWS KMS, GCP KMS, HuaweiCloud KMS, Azure Key Vault, age, and GPG support"
app.ArgsUsage = "sops [options] file"
app.Version = version.Version
app.Authors = []cli.Author{
{Name: "CNCF Maintainers"},
}
app.UsageText = `sops is an editor of encrypted files that supports AWS KMS, GCP, AZKV,
app.UsageText = `sops is an editor of encrypted files that supports AWS KMS, GCP, HuaweiCloud KMS, AZKV,
PGP, and Age

To encrypt or decrypt a document with AWS KMS, specify the KMS ARN
Expand All @@ -109,6 +110,12 @@ 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 HuaweiCloud KMS, specify the
HuaweiCloud KMS key ID (format: region:key-uuid) in the --hckms flag or in the
SOPS_HUAWEICLOUD_KMS_IDS environment variable.
(You need to setup HuaweiCloud credentials via environment variables:
HUAWEICLOUD_SDK_AK, HUAWEICLOUD_SDK_SK, HUAWEICLOUD_SDK_PROJECT_ID, or
use credentials file at ~/.huaweicloud/credentials)

To encrypt or decrypt a document with HashiCorp Vault's Transit Secret
Engine, specify the Vault key URI name in the --hc-vault-transit flag
Expand All @@ -135,12 +142,12 @@ func main() {
To use multiple KMS or PGP keys, separate them by commas. For example:
$ sops -p "10F2...0A, 85D...B3F21" file.yaml

The -p, -k, --gcp-kms, --hc-vault-transit, and --azure-kv flags are only
The -p, -k, --gcp-kms, --hckms, --hc-vault-transit, and --azure-kv flags are only
used to encrypt new documents. Editing or decrypting existing documents
can be done with "sops file" or "sops decrypt 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,azure-kv,hc-vault-transit}"
and "rm-{kms,pgp,gcp-kms,azure-kv,hc-vault-transit}" flags with --rotate
keys in existing documents, use the "add-{kms,pgp,gcp-kms,hckms,azure-kv,hc-vault-transit}"
and "rm-{kms,pgp,gcp-kms,hckms,azure-kv,hc-vault-transit}" flags with --rotate
or the updatekeys command.

To use a different GPG binary than the one in your PATH, set SOPS_GPG_EXEC.
Expand Down Expand Up @@ -570,6 +577,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: "hckms",
Usage: "the HuaweiCloud KMS key ID (format: region:key-uuid) 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",
Expand Down Expand Up @@ -933,6 +944,11 @@ func main() {
Usage: "comma separated list of GCP KMS resource IDs",
EnvVar: "SOPS_GCP_KMS_IDS",
},
cli.StringFlag{
Name: "hckms",
Usage: "comma separated list of HuaweiCloud KMS key IDs (format: region:key-uuid)",
EnvVar: "SOPS_HUAWEICLOUD_KMS_IDS",
},
cli.StringFlag{
Name: "azure-kv",
Usage: "comma separated list of Azure Key Vault URLs",
Expand Down Expand Up @@ -1118,6 +1134,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-hckms",
Usage: "add the provided comma-separated list of HuaweiCloud KMS key IDs (format: region:key-uuid) to the list of master keys on the given file",
},
cli.StringFlag{
Name: "rm-hckms",
Usage: "remove the provided comma-separated list of HuaweiCloud KMS key IDs (format: region:key-uuid) 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",
Expand Down Expand Up @@ -1184,8 +1208,8 @@ func main() {
return toExitError(err)
}
if _, err := os.Stat(fileName); os.IsNotExist(err) {
if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-hc-vault-transit") != "" || c.String("add-azure-kv") != "" || c.String("add-age") != "" ||
c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-hc-vault-transit") != "" || c.String("rm-azure-kv") != "" || c.String("rm-age") != "" {
if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-hckms") != "" || c.String("add-hc-vault-transit") != "" || c.String("add-azure-kv") != "" || c.String("add-age") != "" ||
c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-hckms") != "" || c.String("rm-hc-vault-transit") != "" || c.String("rm-azure-kv") != "" || c.String("rm-age") != "" {
return common.NewExitError(fmt.Sprintf("Error: cannot add or remove keys on non-existent file %q, use the `edit` subcommand instead.", fileName), codes.CannotChangeKeysFromNonExistentFile)
}
}
Expand Down Expand Up @@ -1271,6 +1295,11 @@ func main() {
Usage: "comma separated list of GCP KMS resource IDs",
EnvVar: "SOPS_GCP_KMS_IDS",
},
cli.StringFlag{
Name: "hckms",
Usage: "comma separated list of HuaweiCloud KMS key IDs (format: region:key-uuid)",
EnvVar: "SOPS_HUAWEICLOUD_KMS_IDS",
},
cli.StringFlag{
Name: "azure-kv",
Usage: "comma separated list of Azure Key Vault URLs",
Expand Down Expand Up @@ -1679,6 +1708,11 @@ func main() {
Usage: "comma separated list of GCP KMS resource IDs",
EnvVar: "SOPS_GCP_KMS_IDS",
},
cli.StringFlag{
Name: "hckms",
Usage: "comma separated list of HuaweiCloud KMS key IDs (format: region:key-uuid)",
EnvVar: "SOPS_HUAWEICLOUD_KMS_IDS",
},
cli.StringFlag{
Name: "azure-kv",
Usage: "comma separated list of Azure Key Vault URLs",
Expand Down Expand Up @@ -1727,6 +1761,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-hckms",
Usage: "add the provided comma-separated list of HuaweiCloud KMS key IDs (format: region:key-uuid) to the list of master keys on the given file",
},
cli.StringFlag{
Name: "rm-hckms",
Usage: "remove the provided comma-separated list of HuaweiCloud KMS key IDs (format: region:key-uuid) 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",
Expand Down Expand Up @@ -1861,8 +1903,8 @@ func main() {
return toExitError(err)
}
if _, err := os.Stat(fileName); os.IsNotExist(err) {
if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-hc-vault-transit") != "" || c.String("add-azure-kv") != "" || c.String("add-age") != "" ||
c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-hc-vault-transit") != "" || c.String("rm-azure-kv") != "" || c.String("rm-age") != "" {
if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-hckms") != "" || c.String("add-hc-vault-transit") != "" || c.String("add-azure-kv") != "" || c.String("add-age") != "" ||
c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-hckms") != "" || c.String("rm-hc-vault-transit") != "" || c.String("rm-azure-kv") != "" || c.String("rm-age") != "" {
return common.NewExitError(fmt.Sprintf("Error: cannot add or remove keys on non-existent file %q, use `--kms` and `--pgp` instead.", fileName), codes.CannotChangeKeysFromNonExistentFile)
}
if isEncryptMode || isDecryptMode || isRotateMode {
Expand Down Expand Up @@ -2191,7 +2233,7 @@ func getEncryptConfig(c *cli.Context, fileName string, inputStore common.Store,
}, nil
}

func getMasterKeys(c *cli.Context, kmsEncryptionContext map[string]*string, kmsOptionName string, pgpOptionName string, gcpKmsOptionName string, azureKvOptionName string, hcVaultTransitOptionName string, ageOptionName string) ([]keys.MasterKey, error) {
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) {
var masterKeys []keys.MasterKey
for _, k := range kms.MasterKeysFromArnString(c.String(kmsOptionName), kmsEncryptionContext, c.String("aws-profile")) {
masterKeys = append(masterKeys, k)
Expand All @@ -2202,6 +2244,13 @@ func getMasterKeys(c *cli.Context, kmsEncryptionContext map[string]*string, kmsO
for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String(gcpKmsOptionName)) {
masterKeys = append(masterKeys, k)
}
hckmsKeys, err := hckms.NewMasterKeyFromKeyIDString(c.String(hckmsOptionName))
if err != nil {
return nil, err
}
for _, k := range hckmsKeys {
masterKeys = append(masterKeys, k)
}
azureKeys, err := azkv.MasterKeysFromURLs(c.String(azureKvOptionName))
if err != nil {
return nil, err
Expand All @@ -2228,11 +2277,11 @@ func getMasterKeys(c *cli.Context, kmsEncryptionContext map[string]*string, kmsO

func getRotateOpts(c *cli.Context, fileName string, inputStore common.Store, outputStore common.Store, svcs []keyservice.KeyServiceClient, decryptionOrder []string) (rotateOpts, error) {
kmsEncryptionContext := kms.ParseKMSContext(c.String("encryption-context"))
addMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "add-kms", "add-pgp", "add-gcp-kms", "add-azure-kv", "add-hc-vault-transit", "add-age")
addMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "add-kms", "add-pgp", "add-gcp-kms", "add-hckms", "add-azure-kv", "add-hc-vault-transit", "add-age")
if err != nil {
return rotateOpts{}, err
}
rmMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "rm-kms", "rm-pgp", "rm-gcp-kms", "rm-azure-kv", "rm-hc-vault-transit", "rm-age")
rmMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "rm-kms", "rm-pgp", "rm-gcp-kms", "rm-hckms", "rm-azure-kv", "rm-hc-vault-transit", "rm-age")
if err != nil {
return rotateOpts{}, err
}
Expand Down Expand Up @@ -2380,6 +2429,7 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so
var cloudKmsKeys []keys.MasterKey
var azkvKeys []keys.MasterKey
var hcVaultMkKeys []keys.MasterKey
var hckmsMkKeys []keys.MasterKey
var ageMasterKeys []keys.MasterKey
kmsEncryptionContext := kms.ParseKMSContext(c.String("encryption-context"))
if c.String("encryption-context") != "" && kmsEncryptionContext == nil {
Expand All @@ -2395,6 +2445,15 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so
cloudKmsKeys = append(cloudKmsKeys, k)
}
}
if c.String("hckms") != "" {
hckmsKeys, err := hckms.NewMasterKeyFromKeyIDString(c.String("hckms"))
if err != nil {
return nil, err
}
for _, k := range hckmsKeys {
hckmsMkKeys = append(hckmsMkKeys, k)
}
}
if c.String("azure-kv") != "" {
azureKeys, err := azkv.MasterKeysFromURLs(c.String("azure-kv"))
if err != nil {
Expand Down Expand Up @@ -2427,7 +2486,7 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so
ageMasterKeys = append(ageMasterKeys, k)
}
}
if c.String("kms") == "" && c.String("pgp") == "" && c.String("gcp-kms") == "" && c.String("azure-kv") == "" && c.String("hc-vault-transit") == "" && c.String("age") == "" {
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") == "" {
conf := optionalConfig
var err error
if conf == nil {
Expand All @@ -2446,6 +2505,7 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so
var group sops.KeyGroup
group = append(group, kmsKeys...)
group = append(group, cloudKmsKeys...)
group = append(group, hckmsMkKeys...)
group = append(group, azkvKeys...)
group = append(group, pgpKeys...)
group = append(group, hcVaultMkKeys...)
Expand Down
29 changes: 29 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/getsops/sops/v3/age"
"github.com/getsops/sops/v3/azkv"
"github.com/getsops/sops/v3/gcpkms"
"github.com/getsops/sops/v3/hckms"
"github.com/getsops/sops/v3/hcvault"
"github.com/getsops/sops/v3/kms"
"github.com/getsops/sops/v3/pgp"
Expand Down Expand Up @@ -132,6 +133,7 @@ type keyGroup struct {
Merge []keyGroup `yaml:"merge"`
KMS []kmsKey `yaml:"kms"`
GCPKMS []gcpKmsKey `yaml:"gcp_kms"`
HCKms []hckmsKey `yaml:"hckms"`
AzureKV []azureKVKey `yaml:"azure_keyvault"`
Vault []string `yaml:"hc_vault"`
Age []string `yaml:"age"`
Expand All @@ -155,6 +157,10 @@ type azureKVKey struct {
Version string `yaml:"version"`
}

type hckmsKey struct {
KeyID string `yaml:"key_id"`
}

type destinationRule struct {
PathRegex string `yaml:"path_regex"`
S3Bucket string `yaml:"s3_bucket"`
Expand All @@ -176,6 +182,7 @@ type creationRule struct {
Age interface{} `yaml:"age"` // string or []string
PGP interface{} `yaml:"pgp"` // string or []string
GCPKMS interface{} `yaml:"gcp_kms"` // string or []string
HCKms interface{} `yaml:"hckms"` // string or []string
AzureKeyVault interface{} `yaml:"azure_keyvault"` // string or []string
VaultURI interface{} `yaml:"hc_vault_transit_uri"` // string or []string
KeyGroups []keyGroup `yaml:"key_groups"`
Expand Down Expand Up @@ -214,6 +221,10 @@ func (c *creationRule) GetVaultURIs() ([]string, error) {
return parseKeyField(c.VaultURI, "hc_vault_transit_uri")
}

func (c *creationRule) GetHckmsKeys() ([]string, error) {
return parseKeyField(c.HCKms, "hckms")
}

// Utility function to handle both string and []string
func parseKeyField(field interface{}, fieldName string) ([]string, error) {
if field == nil {
Expand Down Expand Up @@ -329,6 +340,13 @@ func extractMasterKeys(group keyGroup) (sops.KeyGroup, error) {
for _, k := range group.GCPKMS {
keyGroup = append(keyGroup, gcpkms.NewMasterKeyFromResourceID(k.ResourceID))
}
for _, k := range group.HCKms {
key, err := hckms.NewMasterKey(k.KeyID)
if err != nil {
return nil, err
}
keyGroup = append(keyGroup, key)
}
for _, k := range group.AzureKV {
if key, err := azkv.NewMasterKeyWithOptionalVersion(k.VaultURL, k.Key, k.Version); err == nil {
keyGroup = append(keyGroup, key)
Expand Down Expand Up @@ -402,6 +420,17 @@ func getKeyGroupsFromCreationRule(cRule *creationRule, kmsEncryptionContext map[
for _, k := range gcpkms.MasterKeysFromResourceIDString(strings.Join(gcpkmsKeys, ",")) {
keyGroup = append(keyGroup, k)
}
hckmsKeys, err := getKeysWithValidation(cRule.GetHckmsKeys, "hckms")
if err != nil {
return nil, err
}
hckmsMasterKeys, err := hckms.NewMasterKeyFromKeyIDString(strings.Join(hckmsKeys, ","))
if err != nil {
return nil, err
}
for _, k := range hckmsMasterKeys {
keyGroup = append(keyGroup, k)
}
azKeys, err := getKeysWithValidation(cRule.GetAzureKeyVaultKeys, "azure_keyvault")
if err != nil {
return nil, err
Expand Down
10 changes: 10 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,13 @@ creation_rules:
kms: "1"
pgp: "2"
gcp_kms: "3"
hckms: "tr-west-1:test-key-1"
hc_vault_transit_uri: http://4:8200/v1/4/keys/4
- path_regex: ""
kms: foo
pgp: bar
gcp_kms: baz
hckms: "tr-west-1:test-key-2"
hc_vault_transit_uri: http://127.0.1.1/v1/baz/keys/baz
`)

Expand Down Expand Up @@ -114,6 +116,8 @@ creation_rules:
- bar
gcp_kms:
- resource_id: foo
hckms:
- key_id: tr-west-1:test-key-1
azure_keyvault:
- vaultUrl: https://foo.vault.azure.net
key: foo-key
Expand All @@ -128,6 +132,8 @@ creation_rules:
gcp_kms:
- resource_id: bar
- resource_id: baz
hckms:
- key_id: tr-west-1:test-key-2
azure_keyvault:
- vaultUrl: https://bar.vault.azure.net
key: bar-key
Expand Down Expand Up @@ -429,13 +435,15 @@ func TestLoadConfigFile(t *testing.T) {
KMS: "1",
PGP: "2",
GCPKMS: "3",
HCKms: "tr-west-1:test-key-1",
VaultURI: "http://4:8200/v1/4/keys/4",
},
{
PathRegex: "",
KMS: "foo",
PGP: "bar",
GCPKMS: "baz",
HCKms: "tr-west-1:test-key-2",
VaultURI: "http://127.0.1.1/v1/baz/keys/baz",
},
},
Expand Down Expand Up @@ -493,6 +501,7 @@ func TestLoadConfigFileWithGroups(t *testing.T) {
},
PGP: []string{"bar"},
GCPKMS: []gcpKmsKey{{ResourceID: "foo"}},
HCKms: []hckmsKey{{KeyID: "tr-west-1:test-key-1"}},
AzureKV: []azureKVKey{{VaultURL: "https://foo.vault.azure.net", Key: "foo-key", Version: "fooversion"}},
Vault: []string{"https://foo.vault:8200/v1/foo/keys/foo-key"},
},
Expand All @@ -503,6 +512,7 @@ func TestLoadConfigFileWithGroups(t *testing.T) {
{ResourceID: "bar"},
{ResourceID: "baz"},
},
HCKms: []hckmsKey{{KeyID: "tr-west-1:test-key-2"}},
AzureKV: []azureKVKey{{VaultURL: "https://bar.vault.azure.net", Key: "bar-key", Version: "barversion"}},
Vault: []string{"https://baz.vault:8200/v1/baz/keys/baz-key"},
},
Expand Down
Loading
Loading