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
91 changes: 84 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, HuaweiCloud KMS, age, and PGP.
formats and encrypts with AWS KMS, GCP KMS, Azure Key Vault, HuaweiCloud KMS, STACKIT KMS, age, and PGP.
(`demo <https://www.youtube.com/watch?v=YTEVyLXFiq0>`_)

.. image:: https://i.imgur.com/X0TM5NI.gif
Expand Down Expand Up @@ -604,13 +604,65 @@ You can also configure HuaweiCloud KMS keys in the ``.sops.yaml`` config file:
hckms:
- tr-west-1:abc12345-6789-0123-4567-890123456789,tr-west-2:def67890-1234-5678-9012-345678901234

Encrypting using STACKIT KMS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The STACKIT KMS integration uses the
`STACKIT SDK for Go <https://github.com/stackitcloud/stackit-sdk-go>`_
default credential provider chain which tries several authentication methods, in this order:

1. Static token or key flow credentials
2. Environment variable ``STACKIT_SERVICE_ACCOUNT_TOKEN``
3. Credentials file at ``~/.stackit/credentials.json``
4. Token flow via service account key

For more details, see the `STACKIT KMS documentation <https://docs.stackit.cloud/products/security/kms/>`_.

STACKIT KMS uses a resource ID in the format:
``projects/<projectId>/regions/<regionId>/keyRings/<keyRingId>/keys/<keyId>/versions/<versionNumber>``

You can list your KMS keys using the STACKIT CLI:

.. code::

stackit kms key-ring list --project-id PROJECT_ID --region eu01
stackit kms key list --project-id PROJECT_ID --region eu01 --key-ring-id KEYRING_ID
stackit kms key version list --project-id PROJECT_ID --region eu01 --key-ring-id KEYRING_ID --key-id KEY_ID

Now you can encrypt a file using:

.. code:: sh

$ sops encrypt --stackit-kms projects/my-project-id/regions/eu01/keyRings/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb/keys/cccccccc-4444-5555-6666-dddddddddddd/versions/1 test.yaml > test.enc.yaml

Or using the environment variable:

.. code:: sh

$ export SOPS_STACKIT_KMS_IDS="projects/my-project-id/regions/eu01/keyRings/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb/keys/cccccccc-4444-5555-6666-dddddddddddd/versions/1"
$ sops encrypt test.yaml > test.enc.yaml

And decrypt it using:

.. code:: sh

$ sops decrypt test.enc.yaml

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

.. code:: yaml

creation_rules:
- path_regex: \.stackit\.yaml$
stackit_kms: projects/my-project-id/regions/eu01/keyRings/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb/keys/cccccccc-4444-5555-6666-dddddddddddd/versions/1

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``, ``--hckms`` or ``--azure-kv``, or from
command line arguments ``--kms``, ``--pgp``, ``--gcp-kms``, ``--hckms``, ``--stackit-kms`` or ``--azure-kv``, or from
the environment variables ``SOPS_KMS_ARN``, ``SOPS_PGP_FP``, ``SOPS_GCP_KMS_IDS``,
``SOPS_HUAWEICLOUD_KMS_IDS``, ``SOPS_AZURE_KEYVAULT_URLS``. That information is stored in the file under the
``SOPS_HUAWEICLOUD_KMS_IDS``, ``SOPS_STACKIT_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 @@ -654,9 +706,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-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``
``--add-gcp-kms``, ``--add-hckms``, ``--add-stackit-kms``, ``--add-azure-kv``, ``--rm-kms``, ``--rm-pgp``, ``--rm-gcp-kms``,
``--rm-hckms``, ``--rm-stackit-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``, ``--hckms``, ``--stackit-kms`` 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 @@ -832,7 +884,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`` ``--hckms`` ``--pgp`` and ``--age`` parameters for creation
It is often tedious to specify the ``--kms`` ``--gcp-kms`` ``--hckms`` ``--stackit-kms`` ``--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 @@ -878,6 +930,10 @@ can manage the three sets of configurations for the three types of files:
- path_regex: \.hckms\.yaml$
hckms: tr-west-1:abc12345-6789-0123-4567-890123456789,tr-west-2:def67890-1234-5678-9012-345678901234

# stackit files using STACKIT KMS
- path_regex: \.stackit\.yaml$
stackit_kms: projects/my-project-id/regions/eu01/keyRings/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb/keys/cccccccc-4444-5555-6666-dddddddddddd/versions/1

# 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 @@ -1883,6 +1939,16 @@ To directly specify a single key group, you can use the following keys:
- tr-west-1:abc12345-6789-0123-4567-890123456789
- tr-west-1:def67890-1234-5678-9012-345678901234

* ``stackit_kms`` (comma-separated string, or list of strings): list of STACKIT KMS resource IDs
(format: ``projects/<projectId>/regions/<regionId>/keyRings/<keyRingId>/keys/<keyId>/versions/<versionNumber>``).
Example:

.. code:: yaml

creation_rules:
- stackit_kms:
- projects/my-project-id/regions/eu01/keyRings/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb/keys/cccccccc-4444-5555-6666-dddddddddddd/versions/1

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 @@ -2000,6 +2066,17 @@ A key group supports the following keys:

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

* ``stackit_kms`` (list of objects): list of STACKIT KMS resource IDs.
Every object must have the following key:

* ``resource_id`` (string): the resource ID in format ``projects/<projectId>/regions/<regionId>/keyRings/<keyRingId>/keys/<keyId>/versions/<versionNumber>``.

Example:

.. code:: yaml

- resource_id: projects/my-project-id/regions/eu01/keyRings/aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb/keys/cccccccc-4444-5555-6666-dddddddddddd/versions/1

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

* ``pgp`` (list of strings): list of PGP/GPG key fingerprints.
Expand Down
93 changes: 81 additions & 12 deletions cmd/sops/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/getsops/sops/v3/gcpkms"
"github.com/getsops/sops/v3/hckms"
"github.com/getsops/sops/v3/hcvault"
"github.com/getsops/sops/v3/stackitkms"
"github.com/getsops/sops/v3/keys"
"github.com/getsops/sops/v3/keyservice"
"github.com/getsops/sops/v3/kms"
Expand Down Expand Up @@ -91,14 +92,14 @@ func main() {
},
}
app.Name = "sops"
app.Usage = "sops - encrypted file editor with AWS KMS, GCP KMS, HuaweiCloud KMS, Azure Key Vault, age, and GPG support"
app.Usage = "sops - encrypted file editor with AWS KMS, GCP KMS, HuaweiCloud KMS, STACKIT 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, HuaweiCloud KMS, AZKV,
PGP, and Age
app.UsageText = `sops is an editor of encrypted files that supports AWS KMS, GCP, HuaweiCloud KMS, STACKIT KMS,
AZKV, PGP, and Age

To encrypt or decrypt a document with AWS KMS, specify the KMS ARN
in the -k flag or in the SOPS_KMS_ARN environment variable.
Expand All @@ -117,6 +118,12 @@ func main() {
HUAWEICLOUD_SDK_AK, HUAWEICLOUD_SDK_SK, HUAWEICLOUD_SDK_PROJECT_ID, or
use credentials file at ~/.huaweicloud/credentials)

To encrypt or decrypt a document with STACKIT KMS, specify the
STACKIT KMS resource ID in the --stackit-kms flag or in the
SOPS_STACKIT_KMS_IDS environment variable.
(Authentication is handled by the STACKIT SDK via environment variables,
service account key files, or credentials file at ~/.stackit/credentials.json)

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
or in the SOPS_VAULT_URIS environment variable (for example
Expand All @@ -142,12 +149,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, --hckms, --hc-vault-transit, and --azure-kv flags are only
The -p, -k, --gcp-kms, --hckms, --stackit-kms, --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,hckms,azure-kv,hc-vault-transit}"
and "rm-{kms,pgp,gcp-kms,hckms,azure-kv,hc-vault-transit}" flags with --rotate
keys in existing documents, use the "add-{kms,pgp,gcp-kms,hckms,stackit-kms,azure-kv,hc-vault-transit}"
and "rm-{kms,pgp,gcp-kms,hckms,stackit-kms,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 @@ -582,6 +589,10 @@ func main() {
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: "stackit-kms",
Usage: "the STACKIT KMS resource ID 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 @@ -635,6 +646,15 @@ func main() {
}
group = append(group, k)
}
stackitKmsIds := c.StringSlice("stackit-kms")
for _, resID := range stackitKmsIds {
k, err := stackitkms.NewMasterKey(resID)
if err != nil {
log.WithError(err).Error("Failed to add key")
continue
}
group = append(group, k)
}
for _, url := range azkvs {
k, err := azkv.NewMasterKeyFromURL(url)
if err != nil {
Expand Down Expand Up @@ -950,6 +970,11 @@ func main() {
Usage: "comma separated list of HuaweiCloud KMS key IDs (format: region:key-uuid)",
EnvVar: "SOPS_HUAWEICLOUD_KMS_IDS",
},
cli.StringFlag{
Name: "stackit-kms",
Usage: "comma separated list of STACKIT KMS resource IDs",
EnvVar: "SOPS_STACKIT_KMS_IDS",
},
cli.StringFlag{
Name: "azure-kv",
Usage: "comma separated list of Azure Key Vault URLs",
Expand Down Expand Up @@ -1143,6 +1168,14 @@ func main() {
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-stackit-kms",
Usage: "add the provided comma-separated list of STACKIT KMS resource IDs to the list of master keys on the given file",
},
cli.StringFlag{
Name: "rm-stackit-kms",
Usage: "remove the provided comma-separated list of STACKIT KMS resource IDs 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 @@ -1209,8 +1242,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-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") != "" {
if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-hckms") != "" || c.String("add-stackit-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-hckms") != "" || c.String("rm-stackit-kms") != "" || 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 @@ -1301,6 +1334,11 @@ func main() {
Usage: "comma separated list of HuaweiCloud KMS key IDs (format: region:key-uuid)",
EnvVar: "SOPS_HUAWEICLOUD_KMS_IDS",
},
cli.StringFlag{
Name: "stackit-kms",
Usage: "comma separated list of STACKIT KMS resource IDs",
EnvVar: "SOPS_STACKIT_KMS_IDS",
},
cli.StringFlag{
Name: "azure-kv",
Usage: "comma separated list of Azure Key Vault URLs",
Expand Down Expand Up @@ -1714,6 +1752,11 @@ func main() {
Usage: "comma separated list of HuaweiCloud KMS key IDs (format: region:key-uuid)",
EnvVar: "SOPS_HUAWEICLOUD_KMS_IDS",
},
cli.StringFlag{
Name: "stackit-kms",
Usage: "comma separated list of STACKIT KMS resource IDs",
EnvVar: "SOPS_STACKIT_KMS_IDS",
},
cli.StringFlag{
Name: "azure-kv",
Usage: "comma separated list of Azure Key Vault URLs",
Expand Down Expand Up @@ -1770,6 +1813,14 @@ func main() {
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-stackit-kms",
Usage: "add the provided comma-separated list of STACKIT KMS resource IDs to the list of master keys on the given file",
},
cli.StringFlag{
Name: "rm-stackit-kms",
Usage: "remove the provided comma-separated list of STACKIT KMS resource IDs 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 @@ -2235,7 +2286,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, hckmsOptionName 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, stackitKmsOptionName 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 @@ -2253,6 +2304,13 @@ func getMasterKeys(c *cli.Context, kmsEncryptionContext map[string]*string, kmsO
for _, k := range hckmsKeys {
masterKeys = append(masterKeys, k)
}
stackitKmsKeys, err := stackitkms.NewMasterKeyFromResourceIDString(c.String(stackitKmsOptionName))
if err != nil {
return nil, err
}
for _, k := range stackitKmsKeys {
masterKeys = append(masterKeys, k)
}
azureKeys, err := azkv.MasterKeysFromURLs(c.String(azureKvOptionName))
if err != nil {
return nil, err
Expand All @@ -2279,11 +2337,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-hckms", "add-azure-kv", "add-hc-vault-transit", "add-age")
addMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "add-kms", "add-pgp", "add-gcp-kms", "add-hckms", "add-stackit-kms", "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-hckms", "rm-azure-kv", "rm-hc-vault-transit", "rm-age")
rmMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "rm-kms", "rm-pgp", "rm-gcp-kms", "rm-hckms", "rm-stackit-kms", "rm-azure-kv", "rm-hc-vault-transit", "rm-age")
if err != nil {
return rotateOpts{}, err
}
Expand Down Expand Up @@ -2432,6 +2490,7 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so
var azkvKeys []keys.MasterKey
var hcVaultMkKeys []keys.MasterKey
var hckmsMkKeys []keys.MasterKey
var stackitKmsMkKeys []keys.MasterKey
var ageMasterKeys []keys.MasterKey
kmsEncryptionContext := kms.ParseKMSContext(c.String("encryption-context"))
if c.String("encryption-context") != "" && kmsEncryptionContext == nil {
Expand All @@ -2456,6 +2515,15 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so
hckmsMkKeys = append(hckmsMkKeys, k)
}
}
if c.String("stackit-kms") != "" {
stackitKmsKeys, err := stackitkms.NewMasterKeyFromResourceIDString(c.String("stackit-kms"))
if err != nil {
return nil, err
}
for _, k := range stackitKmsKeys {
stackitKmsMkKeys = append(stackitKmsMkKeys, k)
}
}
if c.String("azure-kv") != "" {
azureKeys, err := azkv.MasterKeysFromURLs(c.String("azure-kv"))
if err != nil {
Expand Down Expand Up @@ -2488,7 +2556,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("hckms") == "" && 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("stackit-kms") == "" && c.String("azure-kv") == "" && c.String("hc-vault-transit") == "" && c.String("age") == "" {
conf := optionalConfig
var err error
if conf == nil {
Expand All @@ -2508,6 +2576,7 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so
group = append(group, kmsKeys...)
group = append(group, cloudKmsKeys...)
group = append(group, hckmsMkKeys...)
group = append(group, stackitKmsMkKeys...)
group = append(group, azkvKeys...)
group = append(group, pgpKeys...)
group = append(group, hcVaultMkKeys...)
Expand Down
Loading
Loading