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
105 changes: 98 additions & 7 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ SOPS: Secrets OPerationS
========================

**SOPS** is an editor of encrypted files that supports YAML, JSON, ENV, INI and BINARY
formats and encrypts with AWS KMS, GCP KMS, Azure Key Vault, age, and PGP.
formats and encrypts with AWS KMS, GCP KMS, Azure Key Vault, HuaweiCloud KMS, age, and PGP.
(`demo <https://www.youtube.com/watch?v=YTEVyLXFiq0>`_)

.. image:: https://i.imgur.com/X0TM5NI.gif
Expand Down Expand Up @@ -532,13 +532,77 @@ To easily deploy Vault locally: (DO NOT DO THIS FOR PRODUCTION!!!)

$ sops encrypt --verbose prod/raw.yaml > prod/encrypted.yaml

Encrypting using HuaweiCloud KMS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The HuaweiCloud KMS integration uses the
`default credential provider chain <https://github.com/huaweicloud/huaweicloud-sdk-go-v3/blob/master/core/auth/provider/provider.go>`_
which tries several authentication methods, in this order:

1. Environment variables: ``HUAWEICLOUD_SDK_AK``, ``HUAWEICLOUD_SDK_SK``, ``HUAWEICLOUD_SDK_PROJECT_ID``
2. Credentials file at ``~/.huaweicloud/credentials``
3. Instance metadata (when running on HuaweiCloud instances)

For example, you can use environment variables:

.. code:: bash

export HUAWEICLOUD_SDK_AK="your-access-key"
export HUAWEICLOUD_SDK_SK="your-secret-key"
export HUAWEICLOUD_SDK_PROJECT_ID="your-project-id"

Alternatively, you can create a credentials file at ``~/.huaweicloud/credentials``:

.. code:: sh

$ cat ~/.huaweicloud/credentials
[default]
ak = your-access-key
sk = your-secret-key
project_id = your-project-id

Encrypting/decrypting with HuaweiCloud KMS requires a KMS key ID in the format
``region:key-uuid``. You can get the key ID from the HuaweiCloud console or using
the HuaweiCloud API. The key ID format is ``region:key-uuid`` where:

- ``region`` is the HuaweiCloud region (e.g., ``tr-west-1``, ``cn-north-1``)
- ``key-uuid`` is the UUID of the KMS key (e.g., ``abc12345-6789-0123-4567-890123456789``)

Now you can encrypt a file using:

.. code:: sh

$ sops encrypt --hckms tr-west-1:abc12345-6789-0123-4567-890123456789 test.yaml > test.enc.yaml

Or using the environment variable:

.. code:: sh

$ export SOPS_HUAWEICLOUD_KMS_IDS="tr-west-1:abc12345-6789-0123-4567-890123456789"
$ sops encrypt test.yaml > test.enc.yaml

And decrypt it using:

.. code:: sh

$ sops decrypt test.enc.yaml

You can also configure HuaweiCloud KMS keys in the ``.sops.yaml`` config file:

.. code:: yaml

creation_rules:
- path_regex: \.hckms\.yaml$
hckms:
- tr-west-1:abc12345-6789-0123-4567-890123456789,tr-west-2:def67890-1234-5678-9012-345678901234

Adding and removing keys
~~~~~~~~~~~~~~~~~~~~~~~~

When creating new files, ``sops`` uses the PGP, KMS and GCP KMS defined in the
command line arguments ``--kms``, ``--pgp``, ``--gcp-kms`` or ``--azure-kv``, or from
command line arguments ``--kms``, ``--pgp``, ``--gcp-kms``, ``--hckms`` or ``--azure-kv``, or from
the environment variables ``SOPS_KMS_ARN``, ``SOPS_PGP_FP``, ``SOPS_GCP_KMS_IDS``,
``SOPS_AZURE_KEYVAULT_URLS``. That information is stored in the file under the
``SOPS_HUAWEICLOUD_KMS_IDS``, ``SOPS_AZURE_KEYVAULT_URLS``. That information is stored in the file under the
``sops`` section, such that decrypting files does not require providing those
parameters again.

Expand Down Expand Up @@ -582,9 +646,9 @@ disabled by supplying the ``-y`` flag.

The ``rotate`` command generates a new data encryption key and reencrypt all values
with the new key. At the same time, the command line flag ``--add-kms``, ``--add-pgp``,
``--add-gcp-kms``, ``--add-azure-kv``, ``--rm-kms``, ``--rm-pgp``, ``--rm-gcp-kms``
and ``--rm-azure-kv`` can be used to add and remove keys from a file. These flags use
the comma separated syntax as the ``--kms``, ``--pgp``, ``--gcp-kms`` and ``--azure-kv``
``--add-gcp-kms``, ``--add-hckms``, ``--add-azure-kv``, ``--rm-kms``, ``--rm-pgp``, ``--rm-gcp-kms``,
``--rm-hckms`` and ``--rm-azure-kv`` can be used to add and remove keys from a file. These flags use
the comma separated syntax as the ``--kms``, ``--pgp``, ``--gcp-kms``, ``--hckms`` and ``--azure-kv``
arguments when creating new files.

Use ``updatekeys`` if you want to add a key without rotating the data key.
Expand Down Expand Up @@ -760,7 +824,7 @@ stdout.
Using .sops.yaml conf to select KMS, PGP and age for new files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

It is often tedious to specify the ``--kms`` ``--gcp-kms`` ``--pgp`` and ``--age`` parameters for creation
It is often tedious to specify the ``--kms`` ``--gcp-kms`` ``--hckms`` ``--pgp`` and ``--age`` parameters for creation
of all new files. If your secrets are stored under a specific directory, like a
``git`` repository, you can create a ``.sops.yaml`` configuration file at the root
directory to define which keys are used for which filename.
Expand Down Expand Up @@ -802,6 +866,10 @@ can manage the three sets of configurations for the three types of files:
- path_regex: \.gcp\.yaml$
gcp_kms: projects/mygcproject/locations/global/keyRings/mykeyring/cryptoKeys/thekey

# hckms files using HuaweiCloud KMS
- path_regex: \.hckms\.yaml$
hckms: tr-west-1:abc12345-6789-0123-4567-890123456789,tr-west-2:def67890-1234-5678-9012-345678901234

# Finally, if the rules above have not matched, this one is a
# catchall that will encrypt the file using KMS set C as well as PGP
# The absence of a path_regex means it will match everything
Expand Down Expand Up @@ -1797,6 +1865,16 @@ To directly specify a single key group, you can use the following keys:
- hc_vault_transit_uri:
- http://my.vault/v1/sops/keys/secondkey

* ``hckms`` (list of strings): list of HuaweiCloud KMS key IDs (format: ``<region>:<key-uuid>``).
Example:

.. code:: yaml

creation_rules:
- hckms:
- tr-west-1:abc12345-6789-0123-4567-890123456789
- tr-west-1:def67890-1234-5678-9012-345678901234

To specify a list of key groups, you can use the following key:

* ``key_groups`` (list of key group objects): a list of key group objects.
Expand Down Expand Up @@ -1824,6 +1902,8 @@ To specify a list of key groups, you can use the following key:
- https://vault.url/keys/key-name/ # key without version, the latest will be used
hc_vault_transit_uri:
- http://my.vault/v1/sops/keys/secondkey
hckms:
- tr-west-1:abc12345-6789-0123-4567-890123456789

merge:
- pgp:
Expand Down Expand Up @@ -1901,6 +1981,17 @@ A key group supports the following keys:

* ``hc_vault`` (list of strings): list of HashiCorp Vault transit URIs.

* ``hckms`` (list of objects): list of HuaweiCloud KMS key IDs.
Every object must have the following key:

* ``key_id`` (string): the key ID in format ``<region>:<key-uuid>``.

Example:

.. code:: yaml

- key_id: tr-west-1:abc12345-6789-0123-4567-890123456789

* ``age`` (list of strings): list of Age public keys.

* ``pgp`` (list of strings): list of PGP/GPG key fingerprints.
Expand Down
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
Loading
Loading