diff --git a/CHANGELOG.rst b/CHANGELOG.rst index bf14cc22f..64fa3c44f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,56 @@ Changelog ========= +3.9.0 +----- +Features: + +* Add ``--mac-only-encrypted`` to compute MAC only over values which end up encrypted (#973) +* Allow configuration of indentation for YAML and JSON stores (#1273, #1372) +* Introduce a ``--pristine`` flag to ``sops exec-env`` (#912) +* Allow to pass multiple paths to ``sops updatekeys`` (#1274) +* Allow to override ``fileName`` with different value (#1332) +* Sort masterkeys according to ``--decryption-order`` (#1345) +* Add separate subcommands for encryption, decryption, rotating, editing, and setting values (#1391) +* Add ``filestatus`` command (#545) +* Add command ``unset`` (#1475) +* Merge key for key groups and make keys unique (#1493) +* Support using comments to select parts to encrypt (#974, #1392) + +Deprecations: + +* Deprecate the ``--background`` option to ``exec-env`` and ``exec-file`` (#1379) + +Improvements: + +* Warn/fail if the wrong number of arguments is provided (#1342) +* Warn if more than one command is used (#1388) +* Dependency updates (#1327, #1328, #1330, #1336, #1334, #1344, #1348, #1354, #1357, #1360, #1373, #1381, #1383, #1385, #1408, #1428, #1429, #1427, #1439, #1454, #1460, #1466, #1489, #1519, #1525, #1528, #1540, #1543, #1545) +* Build with Go 1.21 (#1427) +* Improve README.rst (#1339, #1399, #1350) +* Fix typos (#1337, #1477, #1484) +* Polish the ``sops help`` output a bit (#1341, #1544) +* Improve and fix tests (#1346, #1349, #1370, #1390, #1396, #1492) +* Create a constant for the ``sops`` metadata key (#1398) +* Refactoring: move extraction of encryption and rotation options to separate functions (#1389) + +Bug fixes: + +* Respect ``aws_profile`` from keygroup config (#1049) +* Fix a bug where not having a config results in a panic (#1371) +* Consolidate Flatten/Unflatten pre/post processing (#1356) +* INI and DotEnv stores: ``shamir_threshold`` is an integer (#1394) +* Make check whether file contains invalid keys for encryption dependent on output store (#1393) +* Do not panic if ``updatekeys`` is used with a config that has no creation rules defined (#1506) +* ``exec-file``: if ``--filename`` is used, use the provided filename without random suffix (#1474) +* Do not use DotEnv store for ``exec-env``, but specialized environment serializing code (#1436) +* Decryption: do not fail if no matching ``creation_rule`` is present in config file (#1434) + +Project changes: + +* CI dependency updates (#1347, #1359, #1376, #1382, #1386, #1425, #1432, #1498, #1503, #1508, #1510, #1516, #1521, #1492, #1534) +* Adjust Makefile to new goreleaser 6.0.0 release (#1526) + 3.8.1 ----- Improvements: diff --git a/Makefile b/Makefile index dbeaf1762..f0e26d290 100644 --- a/Makefile +++ b/Makefile @@ -2,14 +2,14 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -PROJECT := github.com/MagnusTonnessen/sops +PROJECT := github.com/bekk/sops PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST)))) BIN_DIR := $(PROJECT_DIR)/bin GO := GOPROXY=https://proxy.golang.org go GO_TEST_FLAGS ?= -race -coverprofile=profile.out -covermode=atomic -GITHUB_REPOSITORY ?= github.com/MagnusTonnessen/sops +GITHUB_REPOSITORY ?= github.com/bekk/sops STATICCHECK := $(BIN_DIR)/staticcheck STATICCHECK_VERSION := latest @@ -67,7 +67,7 @@ checkmd: $(MD_FILES) .PHONY: test test: vendor gpg --import pgp/sops_functional_tests_key.asc 2>&1 1>/dev/null || exit 0 - $(GO) test $(GO_TEST_FLAGS) ./... + LANG=en_US.UTF-8 $(GO) test $(GO_TEST_FLAGS) ./... .PHONY: showcoverage showcoverage: test @@ -78,7 +78,7 @@ generate: keyservice/keyservice.pb.go $(GO) generate %.pb.go: %.proto - protoc --go-grpc_out=. --go_opt=paths=source_relative --go_out=. $< + protoc --go_out=plugins=grpc:. $< .PHONY: functional-tests functional-tests: @@ -94,7 +94,7 @@ functional-tests-all: .PHONY: release-snapshot release-snapshot: install-goreleaser install-syft - GITHUB_REPOSITORY=$(GITHUB_REPOSITORY) $(GORELEASER) release --clean --snapshot --skip-sign + GITHUB_REPOSITORY=$(GITHUB_REPOSITORY) $(GORELEASER) release --clean --snapshot --skip=sign .PHONY: clean clean: diff --git a/README.rst b/README.rst index d88c9b804..339f409d9 100644 --- a/README.rst +++ b/README.rst @@ -205,6 +205,7 @@ the ``--age`` option or the **SOPS_AGE_RECIPIENTS** environment variable: When decrypting a file with the corresponding identity, SOPS will look for a text file name ``keys.txt`` located in a ``sops`` subdirectory of your user configuration directory. On Linux, this would be ``$XDG_CONFIG_HOME/sops/age/keys.txt``. +If ``$XDG_CONFIG_HOME`` is not set ``$HOME/.config/sops/age/keys.txt`` is used instead. On macOS, this would be ``$HOME/Library/Application Support/sops/age/keys.txt``. On Windows, this would be ``%AppData%\sops\age\keys.txt``. You can specify the location of this file manually by setting the environment variable **SOPS_AGE_KEY_FILE**. @@ -220,8 +221,12 @@ Encrypting with SSH keys via age is not yet supported by SOPS. Encrypting using GCP KMS ~~~~~~~~~~~~~~~~~~~~~~~~ -GCP KMS uses `Application Default Credentials -`_. +GCP KMS has support for authorization with the use of `Application Default Credentials +`_ and using an oauth2 token. +Application default credentials precedes the use of access token. + +Using Application Default Credentials you can authorize by doing this: + If you already logged in using .. code:: sh @@ -234,6 +239,19 @@ you can enable application default credentials using the sdk: $ gcloud auth application-default login +Using oauth tokens you can authorize by doing this: + +.. code:: sh + + $ export GOOGLE_OAUTH_ACCESS_TOKEN= + +Or if you are logged in you can authorize by generating an access token: + +.. code:: sh + + $ export GOOGLE_OAUTH_ACCESS_TOKEN="$(gcloud auth print-access-token)" + + Encrypting/decrypting with GCP KMS requires a KMS ResourceID. You can use the cloud console the get the ResourceID or you can create one using the gcloud sdk: @@ -256,6 +274,7 @@ And decrypt it using:: $ sops decrypt test.enc.yaml + Encrypting using Azure Key Vault ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -668,6 +687,11 @@ 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. +.. note:: + + The file needs to be named ``.sops.yaml``. Other names (i.e. ``.sops.yml``) won't be automatically + discovered by SOPS. You'll need to pass the ``--config .sops.yml`` option for it to be picked up. + Let's take an example: * file named **something.dev.yaml** should use one set of KMS A, PGP and age @@ -1470,6 +1494,25 @@ The value must be formatted as json. $ sops set ~/git/svc/sops/example.yaml '["an_array"][1]' '{"uid1":null,"uid2":1000,"uid3":["bob"]}' +Unset a sub-part in a document tree +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Symmetrically, SOPS can unset a specific part of a YAML or JSON document, by providing +the path in the ``unset`` command. This is useful to unset specific values, like keys, without +needing an editor. + +.. code:: sh + + $ sops unset ~/git/svc/sops/example.yaml '["app2"]["key"]' + +The tree path syntax uses regular python dictionary syntax, without the +variable name. Set to keys by naming them, and array elements by +numbering them. + +.. code:: sh + + $ sops unset ~/git/svc/sops/example.yaml '["an_array"][1]' + Showing diffs in cleartext in git ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1545,9 +1588,19 @@ that match the supplied regular expression. For example, this command: will not encrypt the values under the ``description`` and ``metadata`` keys in a YAML file containing kubernetes secrets, while encrypting everything else. +For YAML files, another method is to use ``--encrypted-comment-regex`` which will +only encrypt comments and values which have a preceding comment matching the supplied +regular expression. + +Conversely, you can opt in to only left certain keys without encrypting by using the +``--unencrypted-comment-regex`` option, which will leave the values and comments +unencrypted when they have a preeceding comment, or a trailing comment on the same line, +that matches the supplied regular expression. + You can also specify these options in the ``.sops.yaml`` config file. -Note: these four options ``--unencrypted-suffix``, ``--encrypted-suffix``, ``--encrypted-regex`` and ``--unencrypted-regex`` are +Note: these six options ``--unencrypted-suffix``, ``--encrypted-suffix``, ``--encrypted-regex``, +``--unencrypted-regex``, ``--encrypted-comment-regex``, and ``--unencrypted-comment-regex`` are mutually exclusive and cannot all be used in the same file. Encryption Protocol diff --git a/cmd/sops/common/common.go b/cmd/sops/common/common.go index f4468a437..eecd34423 100644 --- a/cmd/sops/common/common.go +++ b/cmd/sops/common/common.go @@ -77,13 +77,12 @@ type DecryptTreeOpts struct { // IgnoreMac is whether or not to ignore the Message Authentication Code included in the SOPS tree IgnoreMac bool // Cipher is the cryptographic cipher to use to decrypt the values inside the tree - Cipher sops.Cipher - DecryptionCredentials map[string]string + Cipher sops.Cipher } // DecryptTree decrypts the tree passed in through the DecryptTreeOpts and additionally returns the decrypted data key func DecryptTree(opts DecryptTreeOpts) (dataKey []byte, err error) { - dataKey, err = opts.Tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices, opts.DecryptionOrder, opts.DecryptionCredentials) + dataKey, err = opts.Tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices, opts.DecryptionOrder) if err != nil { return nil, NewExitError(err, codes.CouldNotRetrieveKey) } @@ -225,13 +224,12 @@ func GetKMSKeyWithEncryptionCtx(tree *sops.Tree) (keyGroupIndex int, keyIndex in // GenericDecryptOpts represents decryption options and config type GenericDecryptOpts struct { - Cipher sops.Cipher - InputStore sops.Store - InputPath string - IgnoreMAC bool - KeyServices []keyservice.KeyServiceClient - DecryptionOrder []string - DecryptionCredentials map[string]string + Cipher sops.Cipher + InputStore sops.Store + InputPath string + IgnoreMAC bool + KeyServices []keyservice.KeyServiceClient + DecryptionOrder []string } // LoadEncryptedFileWithBugFixes is a wrapper around LoadEncryptedFile which includes @@ -301,7 +299,7 @@ func FixAWSKMSEncryptionContextBug(opts GenericDecryptOpts, tree *sops.Tree) (*s return nil, NewExitError(fmt.Sprintf("Failed to decrypt, meaning there is likely another problem from the encryption context bug: %s", err), codes.ErrorDecryptingTree) } - errs := tree.Metadata.UpdateMasterKeysWithKeyServices(dataKey, opts.KeyServices, opts.DecryptionCredentials) + errs := tree.Metadata.UpdateMasterKeysWithKeyServices(dataKey, opts.KeyServices) if len(errs) > 0 { err = fmt.Errorf("Could not re-encrypt data key: %s", errs) return nil, err diff --git a/cmd/sops/decrypt.go b/cmd/sops/decrypt.go index 2e39c0051..db038787b 100644 --- a/cmd/sops/decrypt.go +++ b/cmd/sops/decrypt.go @@ -15,19 +15,18 @@ const notBinaryHint = ("This is likely not an encrypted binary file?" + " If not, use --output-type to select the correct output type.") type decryptOpts struct { - Cipher sops.Cipher - InputStore sops.Store - OutputStore sops.Store - InputPath string - IgnoreMAC bool - Extract []interface{} - KeyServices []keyservice.KeyServiceClient - DecryptionOrder []string - DecryptionCredentials map[string]string + Cipher sops.Cipher + InputStore sops.Store + OutputStore sops.Store + InputPath string + IgnoreMAC bool + Extract []interface{} + KeyServices []keyservice.KeyServiceClient + DecryptionOrder []string } -func decrypt(opts decryptOpts) (decryptedFile []byte, err error) { - tree, err := common.LoadEncryptedFileWithBugFixes(common.GenericDecryptOpts{ +func decryptTree(opts decryptOpts) (tree *sops.Tree, err error) { + tree, err = common.LoadEncryptedFileWithBugFixes(common.GenericDecryptOpts{ Cipher: opts.Cipher, InputStore: opts.InputStore, InputPath: opts.InputPath, @@ -39,17 +38,25 @@ func decrypt(opts decryptOpts) (decryptedFile []byte, err error) { } _, err = common.DecryptTree(common.DecryptTreeOpts{ - Cipher: opts.Cipher, - IgnoreMac: opts.IgnoreMAC, - Tree: tree, - KeyServices: opts.KeyServices, - DecryptionOrder: opts.DecryptionOrder, - DecryptionCredentials: opts.DecryptionCredentials, + Cipher: opts.Cipher, + IgnoreMac: opts.IgnoreMAC, + Tree: tree, + KeyServices: opts.KeyServices, + DecryptionOrder: opts.DecryptionOrder, }) if err != nil { return nil, err } + return tree, nil +} + +func decrypt(opts decryptOpts) (decryptedFile []byte, err error) { + tree, err := decryptTree(opts) + if err != nil { + return nil, err + } + if len(opts.Extract) > 0 { return extract(tree, opts.Extract, opts.OutputStore) } diff --git a/cmd/sops/edit.go b/cmd/sops/edit.go index cd356734f..982cfb967 100644 --- a/cmd/sops/edit.go +++ b/cmd/sops/edit.go @@ -20,15 +20,14 @@ import ( ) type editOpts struct { - Cipher sops.Cipher - InputStore common.Store - OutputStore common.Store - InputPath string - IgnoreMAC bool - KeyServices []keyservice.KeyServiceClient - DecryptionOrder []string - ShowMasterKeys bool - DecryptionCredentials map[string]string + Cipher sops.Cipher + InputStore common.Store + OutputStore common.Store + InputPath string + IgnoreMAC bool + KeyServices []keyservice.KeyServiceClient + DecryptionOrder []string + ShowMasterKeys bool } type editExampleOpts struct { @@ -61,7 +60,7 @@ func editExample(opts editExampleOpts) ([]byte, error) { } // Generate a data key - dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices, opts.DecryptionCredentials) + dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices) if len(errs) > 0 { return nil, common.NewExitError(fmt.Sprintf("Error encrypting the data key with one or more master keys: %s", errs), codes.CouldNotRetrieveKey) } @@ -72,24 +71,22 @@ func editExample(opts editExampleOpts) ([]byte, error) { func edit(opts editOpts) ([]byte, error) { // Load the file tree, err := common.LoadEncryptedFileWithBugFixes(common.GenericDecryptOpts{ - Cipher: opts.Cipher, - InputStore: opts.InputStore, - InputPath: opts.InputPath, - IgnoreMAC: opts.IgnoreMAC, - KeyServices: opts.KeyServices, - DecryptionCredentials: opts.DecryptionCredentials, + Cipher: opts.Cipher, + InputStore: opts.InputStore, + InputPath: opts.InputPath, + IgnoreMAC: opts.IgnoreMAC, + KeyServices: opts.KeyServices, }) if err != nil { return nil, err } // Decrypt the file dataKey, err := common.DecryptTree(common.DecryptTreeOpts{ - Cipher: opts.Cipher, - IgnoreMac: opts.IgnoreMAC, - Tree: tree, - KeyServices: opts.KeyServices, - DecryptionOrder: opts.DecryptionOrder, - DecryptionCredentials: opts.DecryptionCredentials, + Cipher: opts.Cipher, + IgnoreMac: opts.IgnoreMAC, + Tree: tree, + KeyServices: opts.KeyServices, + DecryptionOrder: opts.DecryptionOrder, }) if err != nil { return nil, err diff --git a/cmd/sops/encrypt.go b/cmd/sops/encrypt.go index 353f90327..ace7d8c2c 100644 --- a/cmd/sops/encrypt.go +++ b/cmd/sops/encrypt.go @@ -15,14 +15,15 @@ import ( ) type encryptConfig struct { - UnencryptedSuffix string - EncryptedSuffix string - UnencryptedRegex string - EncryptedRegex string - MACOnlyEncrypted bool - KeyGroups []sops.KeyGroup - GroupThreshold int - EncryptionCredentials map[string]string + UnencryptedSuffix string + EncryptedSuffix string + UnencryptedRegex string + EncryptedRegex string + UnencryptedCommentRegex string + EncryptedCommentRegex string + MACOnlyEncrypted bool + KeyGroups []sops.KeyGroup + GroupThreshold int } type encryptOpts struct { @@ -62,14 +63,16 @@ func ensureNoMetadata(opts encryptOpts, branch sops.TreeBranch) error { func metadataFromEncryptionConfig(config encryptConfig) sops.Metadata { return sops.Metadata{ - KeyGroups: config.KeyGroups, - UnencryptedSuffix: config.UnencryptedSuffix, - EncryptedSuffix: config.EncryptedSuffix, - UnencryptedRegex: config.UnencryptedRegex, - EncryptedRegex: config.EncryptedRegex, - MACOnlyEncrypted: config.MACOnlyEncrypted, - Version: version.Version, - ShamirThreshold: config.GroupThreshold, + KeyGroups: config.KeyGroups, + UnencryptedSuffix: config.UnencryptedSuffix, + EncryptedSuffix: config.EncryptedSuffix, + UnencryptedRegex: config.UnencryptedRegex, + EncryptedRegex: config.EncryptedRegex, + UnencryptedCommentRegex: config.UnencryptedCommentRegex, + EncryptedCommentRegex: config.EncryptedCommentRegex, + MACOnlyEncrypted: config.MACOnlyEncrypted, + Version: version.Version, + ShamirThreshold: config.GroupThreshold, } } @@ -98,7 +101,7 @@ func encrypt(opts encryptOpts) (encryptedFile []byte, err error) { Metadata: metadataFromEncryptionConfig(opts.encryptConfig), FilePath: path, } - dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices, opts.EncryptionCredentials) + dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices) if len(errs) > 0 { err = fmt.Errorf("Could not generate data key: %s", errs) return nil, err diff --git a/cmd/sops/main.go b/cmd/sops/main.go index e898fc13c..155eaec78 100644 --- a/cmd/sops/main.go +++ b/cmd/sops/main.go @@ -26,6 +26,7 @@ import ( "github.com/getsops/sops/v3/cmd/sops/codes" "github.com/getsops/sops/v3/cmd/sops/common" "github.com/getsops/sops/v3/cmd/sops/subcommand/exec" + filestatuscmd "github.com/getsops/sops/v3/cmd/sops/subcommand/filestatus" "github.com/getsops/sops/v3/cmd/sops/subcommand/groups" keyservicecmd "github.com/getsops/sops/v3/cmd/sops/subcommand/keyservice" publishcmd "github.com/getsops/sops/v3/cmd/sops/subcommand/publish" @@ -179,31 +180,53 @@ func main() { return toExitError(err) } opts := decryptOpts{ - OutputStore: &dotenv.Store{}, - InputStore: inputStore, - InputPath: fileName, - Cipher: aes.NewCipher(), - KeyServices: svcs, - DecryptionOrder: order, - IgnoreMAC: c.Bool("ignore-mac"), - DecryptionCredentials: map[string]string{"gcp-kms": c.String("gcp-access-token")}, + OutputStore: &dotenv.Store{}, + InputStore: inputStore, + InputPath: fileName, + Cipher: aes.NewCipher(), + KeyServices: svcs, + DecryptionOrder: order, + IgnoreMAC: c.Bool("ignore-mac"), } if c.Bool("background") { log.Warn("exec-env's --background option is deprecated and will be removed in a future version of sops") } - output, err := decrypt(opts) + tree, err := decryptTree(opts) if err != nil { return toExitError(err) } + var env []string + for _, item := range tree.Branches[0] { + if dotenv.IsComplexValue(item.Value) { + return cli.NewExitError(fmt.Errorf("cannot use complex value in environment: %s", item.Value), codes.ErrorGeneric) + } + if _, ok := item.Key.(sops.Comment); ok { + continue + } + key, ok := item.Key.(string) + if !ok { + return cli.NewExitError(fmt.Errorf("cannot use non-string keys in environment, got %T", item.Key), codes.ErrorGeneric) + } + if strings.Contains(key, "=") { + return cli.NewExitError(fmt.Errorf("cannot use keys with '=' in environment: %s", key), codes.ErrorGeneric) + } + value, ok := item.Value.(string) + if !ok { + return cli.NewExitError(fmt.Errorf("cannot use non-string values in environment, got %T", item.Value), codes.ErrorGeneric) + } + env = append(env, fmt.Sprintf("%s=%s", key, value)) + } + if err := exec.ExecWithEnv(exec.ExecOpts{ Command: command, - Plaintext: output, + Plaintext: []byte{}, Background: c.Bool("background"), Pristine: c.Bool("pristine"), User: c.String("user"), + Env: env, }); err != nil { return toExitError(err) } @@ -238,7 +261,7 @@ func main() { }, cli.StringFlag{ Name: "filename", - Usage: "filename for the temporarily file (default: tmp-file)", + Usage: fmt.Sprintf("filename for the temporarily file (default: %s)", exec.FallbackFilename), }, }, keyserviceFlags...), Action: func(c *cli.Context) error { @@ -273,11 +296,6 @@ func main() { return toExitError(err) } - filename := c.String("filename") - if filename == "" { - filename = "tmp-file" - } - if c.Bool("background") { log.Warn("exec-file's --background option is deprecated and will be removed in a future version of sops") } @@ -288,7 +306,7 @@ func main() { Background: c.Bool("background"), Fifo: !c.Bool("no-fifo"), User: c.String("user"), - Filename: filename, + Filename: c.String("filename"), }); err != nil { return toExitError(err) } @@ -411,6 +429,38 @@ func main() { return nil }, }, + { + Name: "filestatus", + Usage: "check the status of the file, returning encryption status", + ArgsUsage: `file`, + Flags: []cli.Flag{}, + Action: func(c *cli.Context) error { + if c.NArg() < 1 { + return common.NewExitError("Error: no file specified", codes.NoFileSpecified) + } + + fileName := c.Args()[0] + inputStore := inputStore(c, fileName) + opts := filestatuscmd.Opts{ + InputStore: inputStore, + InputPath: fileName, + } + + status, err := filestatuscmd.FileStatus(opts) + if err != nil { + return err + } + + json, err := encodingjson.Marshal(status) + if err != nil { + return common.NewExitError(err, codes.ErrorGeneric) + } + + fmt.Println(string(json)) + + return nil + }, + }, { Name: "groups", Usage: "modify the groups on a SOPS file", @@ -511,14 +561,13 @@ func main() { } } return groups.Add(groups.AddOpts{ - InputPath: c.String("file"), - InPlace: c.Bool("in-place"), - InputStore: inputStore(c, c.String("file")), - OutputStore: outputStore(c, c.String("file")), - Group: group, - GroupThreshold: c.Int("shamir-secret-sharing-threshold"), - KeyServices: keyservices(c), - DecryptionCredentials: map[string]string{"gcp-kms": c.String("gcp-access-token")}, + InputPath: c.String("file"), + InPlace: c.Bool("in-place"), + InputStore: inputStore(c, c.String("file")), + OutputStore: outputStore(c, c.String("file")), + Group: group, + GroupThreshold: c.Int("shamir-secret-sharing-threshold"), + KeyServices: keyservices(c), }) }, }, @@ -551,14 +600,13 @@ func main() { } return groups.Delete(groups.DeleteOpts{ - InputPath: c.String("file"), - InPlace: c.Bool("in-place"), - InputStore: inputStore(c, c.String("file")), - OutputStore: outputStore(c, c.String("file")), - Group: uint(group), - GroupThreshold: c.Int("shamir-secret-sharing-threshold"), - KeyServices: keyservices(c), - DecryptionCredentials: map[string]string{"gcp-kms": c.String("gcp-access-token")}, + InputPath: c.String("file"), + InPlace: c.Bool("in-place"), + InputStore: inputStore(c, c.String("file")), + OutputStore: outputStore(c, c.String("file")), + Group: uint(group), + GroupThreshold: c.Int("shamir-secret-sharing-threshold"), + KeyServices: keyservices(c), }) }, }, @@ -595,13 +643,12 @@ func main() { failedCounter := 0 for _, path := range c.Args() { err := updatekeys.UpdateKeys(updatekeys.Opts{ - InputPath: path, - GroupQuorum: c.Int("shamir-secret-sharing-quorum"), - KeyServices: keyservices(c), - Interactive: !c.Bool("yes"), - ConfigPath: configPath, - InputType: c.String("input-type"), - DecryptionCredentials: map[string]string{"gcp-kms": c.String("gcp-access-token")}, + InputPath: path, + GroupQuorum: c.Int("shamir-secret-sharing-quorum"), + KeyServices: keyservices(c), + Interactive: !c.Bool("yes"), + ConfigPath: configPath, + InputType: c.String("input-type"), }) if c.NArg() == 1 { @@ -664,10 +711,6 @@ func main() { Usage: "comma separated list of decryption key types", EnvVar: "SOPS_DECRYPTION_ORDER", }, - cli.StringFlag{ - Name: "gcp-access-token", - Usage: "access token to use for GCP KMS access", - }, }, keyserviceFlags...), Action: func(c *cli.Context) error { if c.Bool("verbose") { @@ -707,15 +750,14 @@ func main() { return common.NewExitError(fmt.Errorf("error parsing --extract path: %s", err), codes.InvalidTreePathFormat) } output, err := decrypt(decryptOpts{ - OutputStore: outputStore, - InputStore: inputStore, - InputPath: fileName, - Cipher: aes.NewCipher(), - Extract: extract, - KeyServices: svcs, - DecryptionOrder: order, - IgnoreMAC: c.Bool("ignore-mac"), - DecryptionCredentials: map[string]string{"gcp-kms": c.String("gcp-access-token")}, + OutputStore: outputStore, + InputStore: inputStore, + InputPath: fileName, + Cipher: aes.NewCipher(), + Extract: extract, + KeyServices: svcs, + DecryptionOrder: order, + IgnoreMAC: c.Bool("ignore-mac"), }) if err != nil { return toExitError(err) @@ -777,10 +819,6 @@ func main() { Usage: "comma separated list of GCP KMS resource IDs", EnvVar: "SOPS_GCP_KMS_IDS", }, - cli.StringFlag{ - Name: "gcp-access-token", - Usage: "access token to use for GCP KMS access", - }, cli.StringFlag{ Name: "azure-kv", Usage: "comma separated list of Azure Key Vault URLs", @@ -837,10 +875,6 @@ func main() { Name: "filename-override", Usage: "Use this filename instead of the provided argument for loading configuration, and for determining input type and output type", }, - cli.StringFlag{ - Name: "encrypt-config", - Usage: "encrypt configuration as a YAML string instead of a file", - }, }, keyserviceFlags...), Action: func(c *cli.Context) error { if c.Bool("verbose") { @@ -864,22 +898,12 @@ func main() { if fileNameOverride == "" { fileNameOverride = fileName } - configString := c.String("encrypt-config") inputStore := inputStore(c, fileNameOverride) outputStore := outputStore(c, fileNameOverride) svcs := keyservices(c) - var encConfig encryptConfig - - if configString != "" { - encConfig, err = getEncryptConfigFromString(c, fileNameOverride, configString) - } - - if configString == "" || err != nil { - encConfig, err = getEncryptConfig(c, fileNameOverride) - } - + encConfig, err := getEncryptConfig(c, fileNameOverride) if err != nil { return toExitError(err) } @@ -1100,10 +1124,6 @@ func main() { Usage: "comma separated list of GCP KMS resource IDs", EnvVar: "SOPS_GCP_KMS_IDS", }, - cli.StringFlag{ - Name: "gcp-access-token", - Usage: "access token to use for GCP KMS access", - }, cli.StringFlag{ Name: "azure-kv", Usage: "comma separated list of Azure Key Vault URLs", @@ -1198,15 +1218,14 @@ func main() { _, statErr := os.Stat(fileName) fileExists := statErr == nil opts := editOpts{ - OutputStore: outputStore, - InputStore: inputStore, - InputPath: fileName, - Cipher: aes.NewCipher(), - KeyServices: svcs, - DecryptionOrder: order, - IgnoreMAC: c.Bool("ignore-mac"), - ShowMasterKeys: c.Bool("show-master-keys"), - DecryptionCredentials: map[string]string{"gcp-kms": c.String("gcp-access-token")}, + OutputStore: outputStore, + InputStore: inputStore, + InputPath: fileName, + Cipher: aes.NewCipher(), + KeyServices: svcs, + DecryptionOrder: order, + IgnoreMAC: c.Bool("ignore-mac"), + ShowMasterKeys: c.Bool("show-master-keys"), } if fileExists { output, err = edit(opts) @@ -1301,21 +1320,108 @@ func main() { return toExitError(err) } output, err := set(setOpts{ - OutputStore: outputStore, - InputStore: inputStore, - InputPath: fileName, - Cipher: aes.NewCipher(), - KeyServices: svcs, - DecryptionOrder: order, - IgnoreMAC: c.Bool("ignore-mac"), - Value: value, - TreePath: path, - DecryptionCredentials: map[string]string{"gcp-kms": c.String("gcp-access-token")}, + OutputStore: outputStore, + InputStore: inputStore, + InputPath: fileName, + Cipher: aes.NewCipher(), + KeyServices: svcs, + DecryptionOrder: order, + IgnoreMAC: c.Bool("ignore-mac"), + Value: value, + TreePath: path, }) if err != nil { return toExitError(err) } + // We open the file *after* the operations on the tree have been + // executed to avoid truncating it when there's errors + file, err := os.Create(fileName) + if err != nil { + return common.NewExitError(fmt.Sprintf("Could not open in-place file for writing: %s", err), codes.CouldNotWriteOutputFile) + } + defer file.Close() + _, err = file.Write(output) + if err != nil { + return toExitError(err) + } + log.Info("File written successfully") + return nil + }, + }, + { + Name: "unset", + Usage: `unset a specific key or branch in the input document.`, + ArgsUsage: `file index`, + Flags: append([]cli.Flag{ + cli.StringFlag{ + Name: "input-type", + Usage: "currently json, yaml, dotenv and binary are supported. If not set, sops will use the file's extension to determine the type", + }, + cli.StringFlag{ + Name: "output-type", + Usage: "currently json, yaml, dotenv and binary are supported. If not set, sops will use the input file's extension to determine the output format", + }, + cli.IntFlag{ + Name: "shamir-secret-sharing-threshold", + Usage: "the number of master keys required to retrieve the data key with shamir", + }, + cli.BoolFlag{ + Name: "ignore-mac", + Usage: "ignore Message Authentication Code during decryption", + }, + cli.StringFlag{ + Name: "decryption-order", + Usage: "comma separated list of decryption key types", + EnvVar: "SOPS_DECRYPTION_ORDER", + }, + cli.BoolFlag{ + Name: "idempotent", + Usage: "do nothing if the given index does not exist", + }, + }, keyserviceFlags...), + Action: func(c *cli.Context) error { + if c.Bool("verbose") { + logging.SetLevel(logrus.DebugLevel) + } + if c.NArg() != 2 { + return common.NewExitError("Error: no file specified, or index is missing", codes.NoFileSpecified) + } + fileName, err := filepath.Abs(c.Args()[0]) + if err != nil { + return toExitError(err) + } + + inputStore := inputStore(c, fileName) + outputStore := outputStore(c, fileName) + svcs := keyservices(c) + + path, err := parseTreePath(c.Args()[1]) + if err != nil { + return common.NewExitError("Invalid unset index format", codes.ErrorInvalidSetFormat) + } + + order, err := decryptionOrder(c.String("decryption-order")) + if err != nil { + return toExitError(err) + } + output, err := unset(unsetOpts{ + OutputStore: outputStore, + InputStore: inputStore, + InputPath: fileName, + Cipher: aes.NewCipher(), + KeyServices: svcs, + DecryptionOrder: order, + IgnoreMAC: c.Bool("ignore-mac"), + TreePath: path, + }) + if err != nil { + if _, ok := err.(*sops.SopsKeyNotFound); ok && c.Bool("idempotent") { + return nil + } + return toExitError(err) + } + // We open the file *after* the operations on the tree have been // executed to avoid truncating it when there's errors file, err := os.Create(fileName) @@ -1363,10 +1469,6 @@ func main() { Usage: "comma separated list of GCP KMS resource IDs", EnvVar: "SOPS_GCP_KMS_IDS", }, - cli.StringFlag{ - Name: "gcp-access-token", - Usage: "access token to use for GCP KMS access", - }, cli.StringFlag{ Name: "azure-kv", Usage: "comma separated list of Azure Key Vault URLs", @@ -1479,6 +1581,14 @@ func main() { Name: "encrypted-regex", Usage: "set the encrypted key regex. When specified, only keys matching the regex will be encrypted.", }, + cli.StringFlag{ + Name: "unencrypted-comment-regex", + Usage: "set the unencrypted comment suffix. When specified, only keys that have comment matching the regex will be left unencrypted.", + }, + cli.StringFlag{ + Name: "encrypted-comment-regex", + Usage: "set the encrypted comment suffix. When specified, only keys that have comment matching the regex will be encrypted.", + }, cli.StringFlag{ Name: "config", Usage: "path to sops' config file. If set, sops will not search for the config file recursively.", @@ -1519,6 +1629,12 @@ func main() { }, keyserviceFlags...) app.Action = func(c *cli.Context) error { + isDecryptMode := c.Bool("decrypt") + isEncryptMode := c.Bool("encrypt") + isRotateMode := c.Bool("rotate") + isSetMode := c.String("set") != "" + isEditMode := !isEncryptMode && !isDecryptMode && !isRotateMode && !isSetMode + if c.Bool("verbose") { logging.SetLevel(logrus.DebugLevel) } @@ -1538,7 +1654,7 @@ func main() { 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") != "" { return common.NewExitError("Error: cannot add or remove keys on non-existent files, use `--kms` and `--pgp` instead.", codes.CannotChangeKeysFromNonExistentFile) } - if c.Bool("encrypt") || c.Bool("decrypt") || c.Bool("rotate") { + if isEncryptMode || isDecryptMode || isRotateMode { return common.NewExitError("Error: cannot operate on non-existent file", codes.NoFileSpecified) } } @@ -1548,26 +1664,30 @@ func main() { } commandCount := 0 - if c.Bool("encrypt") { + if isDecryptMode { commandCount++ } - if c.Bool("decrypt") { + if isEncryptMode { commandCount++ } - if c.Bool("rotate") { + if isRotateMode { commandCount++ } - if c.String("set") != "" { + if isSetMode { commandCount++ } if commandCount > 1 { log.Warn("More than one command (--encrypt, --decrypt, --rotate, --set) has been specified. Only the changes made by the last one will be visible. Note that this behavior is deprecated and will cause an error eventually.") } - // Load configuration here for backwards compatibility (error out in case of bad config files) - _, err = loadConfig(c, fileNameOverride, nil) - if err != nil { - return toExitError(err) + // Load configuration here for backwards compatibility (error out in case of bad config files), + // but only when not just decrypting (https://github.com/getsops/sops/issues/868) + needsCreationRule := isEncryptMode || isRotateMode || isSetMode || isEditMode + if needsCreationRule { + _, err = loadConfig(c, fileNameOverride, nil) + if err != nil { + return toExitError(err) + } } inputStore := inputStore(c, fileNameOverride) @@ -1579,7 +1699,7 @@ func main() { return toExitError(err) } var output []byte - if c.Bool("encrypt") { + if isEncryptMode { encConfig, err := getEncryptConfig(c, fileNameOverride) if err != nil { return toExitError(err) @@ -1595,30 +1715,29 @@ func main() { // While this check is also done below, the `err` in this scope shadows // the `err` in the outer scope. **Only** do this in case --decrypt, // --rotate-, and --set are not specified, though, to keep old behavior. - if err != nil && !c.Bool("decrypt") && !c.Bool("rotate") && c.String("set") == "" { + if err != nil && !isDecryptMode && !isRotateMode && !isSetMode { return toExitError(err) } } - if c.Bool("decrypt") { + if isDecryptMode { var extract []interface{} extract, err = parseTreePath(c.String("extract")) if err != nil { return common.NewExitError(fmt.Errorf("error parsing --extract path: %s", err), codes.InvalidTreePathFormat) } output, err = decrypt(decryptOpts{ - OutputStore: outputStore, - InputStore: inputStore, - InputPath: fileName, - Cipher: aes.NewCipher(), - Extract: extract, - KeyServices: svcs, - DecryptionOrder: order, - IgnoreMAC: c.Bool("ignore-mac"), - DecryptionCredentials: map[string]string{"gcp-kms": c.String("gcp-access-token")}, + OutputStore: outputStore, + InputStore: inputStore, + InputPath: fileName, + Cipher: aes.NewCipher(), + Extract: extract, + KeyServices: svcs, + DecryptionOrder: order, + IgnoreMAC: c.Bool("ignore-mac"), }) } - if c.Bool("rotate") { + if isRotateMode { rotateOpts, err := getRotateOpts(c, fileName, inputStore, outputStore, svcs, order) if err != nil { return toExitError(err) @@ -1632,7 +1751,7 @@ func main() { } } - if c.String("set") != "" { + if isSetMode { var path []interface{} var value interface{} path, value, err = extractSetArguments(c.String("set")) @@ -1640,33 +1759,30 @@ func main() { return toExitError(err) } output, err = set(setOpts{ - OutputStore: outputStore, - InputStore: inputStore, - InputPath: fileName, - Cipher: aes.NewCipher(), - KeyServices: svcs, - DecryptionOrder: order, - IgnoreMAC: c.Bool("ignore-mac"), - Value: value, - TreePath: path, - DecryptionCredentials: map[string]string{"gcp-kms": c.String("gcp-access-token")}, + OutputStore: outputStore, + InputStore: inputStore, + InputPath: fileName, + Cipher: aes.NewCipher(), + KeyServices: svcs, + DecryptionOrder: order, + IgnoreMAC: c.Bool("ignore-mac"), + Value: value, + TreePath: path, }) } - isEditMode := !c.Bool("encrypt") && !c.Bool("decrypt") && !c.Bool("rotate") && c.String("set") == "" if isEditMode { _, statErr := os.Stat(fileName) fileExists := statErr == nil opts := editOpts{ - OutputStore: outputStore, - InputStore: inputStore, - InputPath: fileName, - Cipher: aes.NewCipher(), - KeyServices: svcs, - DecryptionOrder: order, - IgnoreMAC: c.Bool("ignore-mac"), - ShowMasterKeys: c.Bool("show-master-keys"), - DecryptionCredentials: map[string]string{"gcp-kms": c.String("gcp-access-token")}, + OutputStore: outputStore, + InputStore: inputStore, + InputPath: fileName, + Cipher: aes.NewCipher(), + KeyServices: svcs, + DecryptionOrder: order, + IgnoreMAC: c.Bool("ignore-mac"), + ShowMasterKeys: c.Bool("show-master-keys"), } if fileExists { output, err = edit(opts) @@ -1694,7 +1810,7 @@ func main() { // We open the file *after* the operations on the tree have been // executed to avoid truncating it when there's errors - if c.Bool("in-place") || isEditMode || c.String("set") != "" { + if c.Bool("in-place") || isEditMode || isSetMode { file, err := os.Create(fileName) if err != nil { return common.NewExitError(fmt.Sprintf("Could not open in-place file for writing: %s", err), codes.CouldNotWriteOutputFile) @@ -1731,6 +1847,8 @@ func getEncryptConfig(c *cli.Context, fileName string) (encryptConfig, error) { encryptedSuffix := c.String("encrypted-suffix") encryptedRegex := c.String("encrypted-regex") unencryptedRegex := c.String("unencrypted-regex") + encryptedCommentRegex := c.String("encrypted-comment-regex") + unencryptedCommentRegex := c.String("unencrypted-comment-regex") macOnlyEncrypted := c.Bool("mac-only-encrypted") conf, err := loadConfig(c, fileName, nil) if err != nil { @@ -1750,6 +1868,12 @@ func getEncryptConfig(c *cli.Context, fileName string) (encryptConfig, error) { if unencryptedRegex == "" { unencryptedRegex = conf.UnencryptedRegex } + if encryptedCommentRegex == "" { + encryptedCommentRegex = conf.EncryptedCommentRegex + } + if unencryptedCommentRegex == "" { + unencryptedCommentRegex = conf.UnencryptedCommentRegex + } if !macOnlyEncrypted { macOnlyEncrypted = conf.MACOnlyEncrypted } @@ -1768,85 +1892,15 @@ func getEncryptConfig(c *cli.Context, fileName string) (encryptConfig, error) { if unencryptedRegex != "" { cryptRuleCount++ } - - if cryptRuleCount > 1 { - return encryptConfig{}, common.NewExitError("Error: cannot use more than one of encrypted_suffix, unencrypted_suffix, encrypted_regex, or unencrypted_regex in the same file", codes.ErrorConflictingParameters) - } - - // only supply the default UnencryptedSuffix when EncryptedSuffix, EncryptedRegex, and others are not provided - if cryptRuleCount == 0 { - unencryptedSuffix = sops.DefaultUnencryptedSuffix - } - - var groups []sops.KeyGroup - groups, err = keyGroups(c, fileName) - if err != nil { - return encryptConfig{}, err - } - - var threshold int - threshold, err = shamirThreshold(c, fileName) - if err != nil { - return encryptConfig{}, err - } - - return encryptConfig{ - UnencryptedSuffix: unencryptedSuffix, - EncryptedSuffix: encryptedSuffix, - UnencryptedRegex: unencryptedRegex, - EncryptedRegex: encryptedRegex, - MACOnlyEncrypted: macOnlyEncrypted, - KeyGroups: groups, - GroupThreshold: threshold, - EncryptionCredentials: map[string]string{"gcp-kms": c.String("gcp-access-token")}, - }, nil -} - -func getEncryptConfigFromString(c *cli.Context, fileName string, configString string) (encryptConfig, error) { - unencryptedSuffix := c.String("unencrypted-suffix") - encryptedSuffix := c.String("encrypted-suffix") - encryptedRegex := c.String("encrypted-regex") - unencryptedRegex := c.String("unencrypted-regex") - macOnlyEncrypted := c.Bool("mac-only-encrypted") - conf, err := loadConfigFromString(configString, fileName, nil) - if err != nil { - return encryptConfig{}, toExitError(err) - } - if conf != nil { - // command line options have precedence - if unencryptedSuffix == "" { - unencryptedSuffix = conf.UnencryptedSuffix - } - if encryptedSuffix == "" { - encryptedSuffix = conf.EncryptedSuffix - } - if encryptedRegex == "" { - encryptedRegex = conf.EncryptedRegex - } - if unencryptedRegex == "" { - unencryptedRegex = conf.UnencryptedRegex - } - if !macOnlyEncrypted { - macOnlyEncrypted = conf.MACOnlyEncrypted - } - } - - cryptRuleCount := 0 - if unencryptedSuffix != "" { - cryptRuleCount++ - } - if encryptedSuffix != "" { - cryptRuleCount++ - } - if encryptedRegex != "" { + if encryptedCommentRegex != "" { cryptRuleCount++ } - if unencryptedRegex != "" { + if unencryptedCommentRegex != "" { cryptRuleCount++ } if cryptRuleCount > 1 { - return encryptConfig{}, common.NewExitError("Error: cannot use more than one of encrypted_suffix, unencrypted_suffix, encrypted_regex, or unencrypted_regex in the same file", codes.ErrorConflictingParameters) + return encryptConfig{}, common.NewExitError("Error: cannot use more than one of encrypted_suffix, unencrypted_suffix, encrypted_regex, unencrypted_regex, encrypted_comment_regex, or unencrypted_comment_regex in the same file", codes.ErrorConflictingParameters) } // only supply the default UnencryptedSuffix when EncryptedSuffix, EncryptedRegex, and others are not provided @@ -1855,26 +1909,27 @@ func getEncryptConfigFromString(c *cli.Context, fileName string, configString st } var groups []sops.KeyGroup - groups, err = keyGroupsFromString(c, configString, fileName) + groups, err = keyGroups(c, fileName) if err != nil { return encryptConfig{}, err } var threshold int - threshold, err = shamirThresholdFromString(configString, fileName) + threshold, err = shamirThreshold(c, fileName) if err != nil { return encryptConfig{}, err } return encryptConfig{ - UnencryptedSuffix: unencryptedSuffix, - EncryptedSuffix: encryptedSuffix, - UnencryptedRegex: unencryptedRegex, - EncryptedRegex: encryptedRegex, - MACOnlyEncrypted: macOnlyEncrypted, - KeyGroups: groups, - GroupThreshold: threshold, - EncryptionCredentials: map[string]string{"gcp-kms": c.String("gcp-access-token")}, + UnencryptedSuffix: unencryptedSuffix, + EncryptedSuffix: encryptedSuffix, + UnencryptedRegex: unencryptedRegex, + EncryptedRegex: encryptedRegex, + UnencryptedCommentRegex: unencryptedCommentRegex, + EncryptedCommentRegex: encryptedCommentRegex, + MACOnlyEncrypted: macOnlyEncrypted, + KeyGroups: groups, + GroupThreshold: threshold, }, nil } @@ -1924,16 +1979,15 @@ func getRotateOpts(c *cli.Context, fileName string, inputStore common.Store, out return rotateOpts{}, err } return rotateOpts{ - OutputStore: outputStore, - InputStore: inputStore, - InputPath: fileName, - Cipher: aes.NewCipher(), - KeyServices: svcs, - DecryptionOrder: decryptionOrder, - IgnoreMAC: c.Bool("ignore-mac"), - AddMasterKeys: addMasterKeys, - RemoveMasterKeys: rmMasterKeys, - DecryptionCredentials: map[string]string{"gcp-kms": c.String("gcp-access-token")}, + OutputStore: outputStore, + InputStore: inputStore, + InputPath: fileName, + Cipher: aes.NewCipher(), + KeyServices: svcs, + DecryptionOrder: decryptionOrder, + IgnoreMAC: c.Bool("ignore-mac"), + AddMasterKeys: addMasterKeys, + RemoveMasterKeys: rmMasterKeys, }, nil } @@ -2101,83 +2155,7 @@ func keyGroups(c *cli.Context, file string) ([]sops.KeyGroup, error) { conf, err := loadConfig(c, file, kmsEncryptionContext) // config file might just not be supplied, without any error if conf == nil { - errMsg := "config file not found and no keys provided through command line options" - if err != nil { - errMsg = fmt.Sprintf("%s: %s", errMsg, err) - } - return nil, fmt.Errorf(errMsg) - } - return conf.KeyGroups, err - } - var group sops.KeyGroup - group = append(group, kmsKeys...) - group = append(group, cloudKmsKeys...) - group = append(group, azkvKeys...) - group = append(group, pgpKeys...) - group = append(group, hcVaultMkKeys...) - group = append(group, ageMasterKeys...) - log.Debugf("Master keys available: %+v", group) - return []sops.KeyGroup{group}, nil -} - -func keyGroupsFromString(c *cli.Context, configString string, file string) ([]sops.KeyGroup, error) { - var kmsKeys []keys.MasterKey - var pgpKeys []keys.MasterKey - var cloudKmsKeys []keys.MasterKey - var azkvKeys []keys.MasterKey - var hcVaultMkKeys []keys.MasterKey - var ageMasterKeys []keys.MasterKey - kmsEncryptionContext := kms.ParseKMSContext(c.String("encryption-context")) - if c.String("encryption-context") != "" && kmsEncryptionContext == nil { - return nil, common.NewExitError("Invalid KMS encryption context format", codes.ErrorInvalidKMSEncryptionContextFormat) - } - if c.String("kms") != "" { - for _, k := range kms.MasterKeysFromArnString(c.String("kms"), kmsEncryptionContext, c.String("aws-profile")) { - kmsKeys = append(kmsKeys, k) - } - } - if c.String("gcp-kms") != "" { - for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String("gcp-kms")) { - cloudKmsKeys = append(cloudKmsKeys, k) - } - } - if c.String("azure-kv") != "" { - azureKeys, err := azkv.MasterKeysFromURLs(c.String("azure-kv")) - if err != nil { - return nil, err - } - for _, k := range azureKeys { - azkvKeys = append(azkvKeys, k) - } - } - if c.String("hc-vault-transit") != "" { - hcVaultKeys, err := hcvault.NewMasterKeysFromURIs(c.String("hc-vault-transit")) - if err != nil { - return nil, err - } - for _, k := range hcVaultKeys { - hcVaultMkKeys = append(hcVaultMkKeys, k) - } - } - if c.String("pgp") != "" { - for _, k := range pgp.MasterKeysFromFingerprintString(c.String("pgp")) { - pgpKeys = append(pgpKeys, k) - } - } - if c.String("age") != "" { - ageKeys, err := age.MasterKeysFromRecipients(c.String("age")) - if err != nil { - return nil, err - } - for _, k := range ageKeys { - 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") == "" { - conf, err := loadConfigFromString(configString, file, kmsEncryptionContext) - // config file might just not be supplied, without any error - if conf == nil { - errMsg := "config file not found and no keys provided through command line options" + errMsg := "config file not found, or has no creation rules, and no keys provided through command line options" if err != nil { errMsg = fmt.Sprintf("%s: %s", errMsg, err) } @@ -2218,15 +2196,6 @@ func loadConfig(c *cli.Context, file string, kmsEncryptionContext map[string]*st return conf, nil } -func loadConfigFromString(configString string, file string, kmsEncryptionContext map[string]*string) (*config.Config, error) { - var err error - conf, err := config.LoadCreationRuleFromConfigString(configString, file, kmsEncryptionContext) - if err != nil { - return nil, err - } - return conf, nil -} - func shamirThreshold(c *cli.Context, file string) (int, error) { if c.Int("shamir-secret-sharing-threshold") != 0 { return c.Int("shamir-secret-sharing-threshold"), nil @@ -2234,18 +2203,7 @@ func shamirThreshold(c *cli.Context, file string) (int, error) { conf, err := loadConfig(c, file, nil) if conf == nil { // This takes care of the following two case: - // 1. No config was provided. Err will be nil and ShamirThreshold will be the default value of 0. - // 2. We did find a config file, but failed to load it. In that case the calling function will print the error and exit. - return 0, err - } - return conf.ShamirThreshold, nil -} - -func shamirThresholdFromString(c string, file string) (int, error) { - conf, err := loadConfigFromString(c, file, nil) - if conf == nil { - // This takes care of the following two case: - // 1. No config was provided. Err will be nil and ShamirThreshold will be the default value of 0. + // 1. No config was provided, or contains no creation rules. Err will be nil and ShamirThreshold will be the default value of 0. // 2. We did find a config file, but failed to load it. In that case the calling function will print the error and exit. return 0, err } diff --git a/cmd/sops/rotate.go b/cmd/sops/rotate.go index a7d6f5f58..072b558e5 100644 --- a/cmd/sops/rotate.go +++ b/cmd/sops/rotate.go @@ -12,27 +12,25 @@ import ( ) type rotateOpts struct { - Cipher sops.Cipher - InputStore sops.Store - OutputStore sops.Store - InputPath string - IgnoreMAC bool - AddMasterKeys []keys.MasterKey - RemoveMasterKeys []keys.MasterKey - KeyServices []keyservice.KeyServiceClient - DecryptionOrder []string - DecryptionCredentials map[string]string + Cipher sops.Cipher + InputStore sops.Store + OutputStore sops.Store + InputPath string + IgnoreMAC bool + AddMasterKeys []keys.MasterKey + RemoveMasterKeys []keys.MasterKey + KeyServices []keyservice.KeyServiceClient + DecryptionOrder []string } func rotate(opts rotateOpts) ([]byte, error) { tree, err := common.LoadEncryptedFileWithBugFixes(common.GenericDecryptOpts{ - Cipher: opts.Cipher, - InputStore: opts.InputStore, - InputPath: opts.InputPath, - IgnoreMAC: opts.IgnoreMAC, - KeyServices: opts.KeyServices, - DecryptionOrder: opts.DecryptionOrder, - DecryptionCredentials: opts.DecryptionCredentials, + Cipher: opts.Cipher, + InputStore: opts.InputStore, + InputPath: opts.InputPath, + IgnoreMAC: opts.IgnoreMAC, + KeyServices: opts.KeyServices, + DecryptionOrder: opts.DecryptionOrder, }) if err != nil { return nil, err @@ -69,7 +67,7 @@ func rotate(opts rotateOpts) ([]byte, error) { } // Create a new data key - dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices, opts.DecryptionCredentials) + dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices) if len(errs) > 0 { err = fmt.Errorf("Could not generate data key: %s", errs) return nil, err diff --git a/cmd/sops/set.go b/cmd/sops/set.go index c1511cfe4..a6e8ed357 100644 --- a/cmd/sops/set.go +++ b/cmd/sops/set.go @@ -10,28 +10,26 @@ import ( ) type setOpts struct { - Cipher sops.Cipher - InputStore sops.Store - OutputStore sops.Store - InputPath string - IgnoreMAC bool - TreePath []interface{} - Value interface{} - KeyServices []keyservice.KeyServiceClient - DecryptionOrder []string - DecryptionCredentials map[string]string + Cipher sops.Cipher + InputStore sops.Store + OutputStore sops.Store + InputPath string + IgnoreMAC bool + TreePath []interface{} + Value interface{} + KeyServices []keyservice.KeyServiceClient + DecryptionOrder []string } func set(opts setOpts) ([]byte, error) { // Load the file // TODO: Issue #173: if the file does not exist, create it with the contents passed in as opts.Value tree, err := common.LoadEncryptedFileWithBugFixes(common.GenericDecryptOpts{ - Cipher: opts.Cipher, - InputStore: opts.InputStore, - InputPath: opts.InputPath, - IgnoreMAC: opts.IgnoreMAC, - KeyServices: opts.KeyServices, - DecryptionCredentials: opts.DecryptionCredentials, + Cipher: opts.Cipher, + InputStore: opts.InputStore, + InputPath: opts.InputPath, + IgnoreMAC: opts.IgnoreMAC, + KeyServices: opts.KeyServices, }) if err != nil { return nil, err @@ -39,12 +37,11 @@ func set(opts setOpts) ([]byte, error) { // Decrypt the file dataKey, err := common.DecryptTree(common.DecryptTreeOpts{ - Cipher: opts.Cipher, - IgnoreMac: opts.IgnoreMAC, - Tree: tree, - KeyServices: opts.KeyServices, - DecryptionOrder: opts.DecryptionOrder, - DecryptionCredentials: opts.DecryptionCredentials, + Cipher: opts.Cipher, + IgnoreMac: opts.IgnoreMAC, + Tree: tree, + KeyServices: opts.KeyServices, + DecryptionOrder: opts.DecryptionOrder, }) if err != nil { return nil, err diff --git a/cmd/sops/subcommand/exec/exec.go b/cmd/sops/subcommand/exec/exec.go index e7201107f..be74a31a4 100644 --- a/cmd/sops/subcommand/exec/exec.go +++ b/cmd/sops/subcommand/exec/exec.go @@ -3,6 +3,7 @@ package exec import ( "bytes" "os" + "path/filepath" "runtime" "strings" @@ -11,6 +12,10 @@ import ( "github.com/sirupsen/logrus" ) +const ( + FallbackFilename = "tmp-file" +) + var log *logrus.Logger func init() { @@ -25,13 +30,27 @@ type ExecOpts struct { Fifo bool User string Filename string + Env []string } func GetFile(dir, filename string) *os.File { - handle, err := os.CreateTemp(dir, filename) + // If no filename is provided, create a random one based on FallbackFilename + if filename == "" { + handle, err := os.CreateTemp(dir, FallbackFilename) + if err != nil { + log.Fatal(err) + } + return handle + } + // If a filename is provided, use that one + handle, err := os.Create(filepath.Join(dir, filename)) if err != nil { log.Fatal(err) } + // read+write for owner only + if err = handle.Chmod(0600); err != nil { + log.Fatal(err) + } return handle } @@ -55,18 +74,30 @@ func ExecWithFile(opts ExecOpts) error { if opts.Fifo { // fifo handling needs to be async, even opening to write // will block if there is no reader present - filename = GetPipe(dir, opts.Filename) + filename = opts.Filename + if filename == "" { + filename = FallbackFilename + } + filename = GetPipe(dir, filename) go WritePipe(filename, opts.Plaintext) } else { + // GetFile handles opts.Filename == "" specially, that's why we have + // to pass in opts.Filename without handling the fallback here handle := GetFile(dir, opts.Filename) handle.Write(opts.Plaintext) handle.Close() filename = handle.Name() } + var env []string + if !opts.Pristine { + env = os.Environ() + } + env = append(env, opts.Env...) + placeholdered := strings.Replace(opts.Command, "{}", filename, -1) cmd := BuildCommand(placeholdered) - cmd.Env = os.Environ() + cmd.Env = env if opts.Background { return cmd.Start() @@ -101,6 +132,8 @@ func ExecWithEnv(opts ExecOpts) error { env = append(env, string(line)) } + env = append(env, opts.Env...) + cmd := BuildCommand(opts.Command) cmd.Env = env diff --git a/cmd/sops/subcommand/filestatus/filestatus.go b/cmd/sops/subcommand/filestatus/filestatus.go new file mode 100644 index 000000000..3d40c96fa --- /dev/null +++ b/cmd/sops/subcommand/filestatus/filestatus.go @@ -0,0 +1,60 @@ +package filestatus + +import ( + "fmt" + + "github.com/getsops/sops/v3" + "github.com/getsops/sops/v3/cmd/sops/common" +) + +// Opts represent the input options for FileStatus +type Opts struct { + InputStore sops.Store + InputPath string +} + +// Status represents the status of a file +type Status struct { + // Encrypted represents whether the file provided is encrypted by SOPS + Encrypted bool `json:"encrypted"` +} + +// FileStatus checks encryption status of a file +func FileStatus(opts Opts) (Status, error) { + encrypted, err := cfs(opts.InputStore, opts.InputPath) + if err != nil { + return Status{}, fmt.Errorf("cannot check file status: %w", err) + } + return Status{Encrypted: encrypted}, nil +} + +// cfs checks and reports on file encryption status. +// +// It tries to decrypt the input file with the provided store. +// It returns true if the file contains sops metadata, false +// if it doesn't or Version or MessageAuthenticationCode are +// not found. +// It reports any error encountered different from +// sops.MetadataNotFound, as that is used to detect a sops +// encrypted file. +func cfs(s sops.Store, inputpath string) (bool, error) { + tree, err := common.LoadEncryptedFile(s, inputpath) + if err != nil && err == sops.MetadataNotFound { + return false, nil + } + if err != nil { + return false, fmt.Errorf("cannot load encrypted file: %w", err) + } + + // NOTE: even if it's a file that sops recognize as containing + // valid metadata, we want to ensure some metadata are present + // to report the file as encrypted. + if tree.Metadata.Version == "" { + return false, nil + } + if tree.Metadata.MessageAuthenticationCode == "" { + return false, nil + } + + return true, nil +} diff --git a/cmd/sops/subcommand/filestatus/filestatus_internal_test.go b/cmd/sops/subcommand/filestatus/filestatus_internal_test.go new file mode 100644 index 000000000..c058a506d --- /dev/null +++ b/cmd/sops/subcommand/filestatus/filestatus_internal_test.go @@ -0,0 +1,52 @@ +package filestatus + +import ( + "path" + "testing" + + "github.com/getsops/sops/v3/cmd/sops/common" + "github.com/getsops/sops/v3/config" + "github.com/stretchr/testify/require" +) + +const repoRoot = "../../../../" + +func fromRepoRoot(p string) string { + return path.Join(repoRoot, p) +} + +func TestFileStatus(t *testing.T) { + tests := []struct { + name string + file string + expectedEncrypted bool + }{ + { + name: "encrypted file should be reported as such", + file: "example.yaml", + expectedEncrypted: true, + }, + { + name: "plain text file should be reported as cleartext", + file: "functional-tests/res/plainfile.yaml", + }, + { + name: "file without mac should be reported as cleartext", + file: "functional-tests/res/plainfile.yaml", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := fromRepoRoot(tt.file) + s := common.DefaultStoreForPath(config.NewStoresConfig(), f) + encrypted, err := cfs(s, f) + require.Nil(t, err, "should not error") + if tt.expectedEncrypted { + require.True(t, encrypted, "file should have been reported as encrypted") + } else { + require.False(t, encrypted, "file should have been reported as cleartext") + } + }) + } +} diff --git a/cmd/sops/subcommand/groups/add.go b/cmd/sops/subcommand/groups/add.go index 95f0ecc4a..716d83ea6 100644 --- a/cmd/sops/subcommand/groups/add.go +++ b/cmd/sops/subcommand/groups/add.go @@ -10,15 +10,14 @@ import ( // AddOpts are the options for adding a key group to a SOPS file type AddOpts struct { - InputPath string - InputStore sops.Store - OutputStore sops.Store - Group sops.KeyGroup - GroupThreshold int - InPlace bool - KeyServices []keyservice.KeyServiceClient - DecryptionOrder []string - DecryptionCredentials map[string]string + InputPath string + InputStore sops.Store + OutputStore sops.Store + Group sops.KeyGroup + GroupThreshold int + InPlace bool + KeyServices []keyservice.KeyServiceClient + DecryptionOrder []string } // Add adds a key group to a SOPS file @@ -27,7 +26,7 @@ func Add(opts AddOpts) error { if err != nil { return err } - dataKey, err := tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices, opts.DecryptionOrder, nil) + dataKey, err := tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices, opts.DecryptionOrder) if err != nil { return err } @@ -36,7 +35,7 @@ func Add(opts AddOpts) error { if opts.GroupThreshold != 0 { tree.Metadata.ShamirThreshold = opts.GroupThreshold } - tree.Metadata.UpdateMasterKeysWithKeyServices(dataKey, opts.KeyServices, opts.DecryptionCredentials) + tree.Metadata.UpdateMasterKeysWithKeyServices(dataKey, opts.KeyServices) output, err := opts.OutputStore.EmitEncryptedFile(*tree) if err != nil { return err diff --git a/cmd/sops/subcommand/groups/delete.go b/cmd/sops/subcommand/groups/delete.go index 0d2681ba1..67f32ef25 100644 --- a/cmd/sops/subcommand/groups/delete.go +++ b/cmd/sops/subcommand/groups/delete.go @@ -12,15 +12,14 @@ import ( // DeleteOpts are the options for deleting a key group from a SOPS file type DeleteOpts struct { - InputPath string - InputStore sops.Store - OutputStore sops.Store - Group uint - GroupThreshold int - InPlace bool - KeyServices []keyservice.KeyServiceClient - DecryptionOrder []string - DecryptionCredentials map[string]string + InputPath string + InputStore sops.Store + OutputStore sops.Store + Group uint + GroupThreshold int + InPlace bool + KeyServices []keyservice.KeyServiceClient + DecryptionOrder []string } // Delete deletes a key group from a SOPS file @@ -29,7 +28,7 @@ func Delete(opts DeleteOpts) error { if err != nil { return err } - dataKey, err := tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices, opts.DecryptionOrder, nil) + dataKey, err := tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices, opts.DecryptionOrder) if err != nil { return err } @@ -45,7 +44,7 @@ func Delete(opts DeleteOpts) error { len(tree.Metadata.KeyGroups)) } - tree.Metadata.UpdateMasterKeysWithKeyServices(dataKey, opts.KeyServices, opts.DecryptionCredentials) + tree.Metadata.UpdateMasterKeysWithKeyServices(dataKey, opts.KeyServices) output, err := opts.OutputStore.EmitEncryptedFile(*tree) if err != nil { return err diff --git a/cmd/sops/subcommand/keyservice/keyservice.go b/cmd/sops/subcommand/keyservice/keyservice.go index b69e99908..c28f63690 100644 --- a/cmd/sops/subcommand/keyservice/keyservice.go +++ b/cmd/sops/subcommand/keyservice/keyservice.go @@ -34,7 +34,7 @@ func Run(opts Opts) error { } defer lis.Close() grpcServer := grpc.NewServer() - keyservice.RegisterKeyServiceServer(grpcServer, &keyservice.Server{ + keyservice.RegisterKeyServiceServer(grpcServer, keyservice.Server{ Prompt: opts.Prompt, }) log.Infof("Listening on %s://%s", opts.Network, opts.Address) diff --git a/cmd/sops/subcommand/publish/publish.go b/cmd/sops/subcommand/publish/publish.go index ba8f03be0..aed1118de 100644 --- a/cmd/sops/subcommand/publish/publish.go +++ b/cmd/sops/subcommand/publish/publish.go @@ -27,17 +27,16 @@ func init() { // Opts represents publish options and config type Opts struct { - Interactive bool - Cipher sops.Cipher - ConfigPath string - InputPath string - KeyServices []keyservice.KeyServiceClient - DecryptionOrder []string - InputStore sops.Store - OmitExtensions bool - Recursive bool - RootPath string - DecryptionCredentials map[string]string + Interactive bool + Cipher sops.Cipher + ConfigPath string + InputPath string + KeyServices []keyservice.KeyServiceClient + DecryptionOrder []string + InputStore sops.Store + OmitExtensions bool + Recursive bool + RootPath string } // Run publish operation @@ -83,12 +82,11 @@ func Run(opts Opts) error { if len(conf.KeyGroups[0]) != 0 { log.Debug("Re-encrypting tree before publishing") _, err = common.DecryptTree(common.DecryptTreeOpts{ - Cipher: opts.Cipher, - IgnoreMac: false, - Tree: tree, - KeyServices: opts.KeyServices, - DecryptionOrder: opts.DecryptionOrder, - DecryptionCredentials: opts.DecryptionCredentials, + Cipher: opts.Cipher, + IgnoreMac: false, + Tree: tree, + KeyServices: opts.KeyServices, + DecryptionOrder: opts.DecryptionOrder, }) if err != nil { return err @@ -114,7 +112,7 @@ func Run(opts Opts) error { ShamirThreshold: conf.ShamirThreshold, } - dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices, opts.DecryptionCredentials) + dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices) if len(errs) > 0 { err = fmt.Errorf("Could not generate data key: %s", errs) return err diff --git a/cmd/sops/subcommand/updatekeys/updatekeys.go b/cmd/sops/subcommand/updatekeys/updatekeys.go index c84155782..4b01e8ab7 100644 --- a/cmd/sops/subcommand/updatekeys/updatekeys.go +++ b/cmd/sops/subcommand/updatekeys/updatekeys.go @@ -14,14 +14,13 @@ import ( // Opts represents key operation options and config type Opts struct { - InputPath string - GroupQuorum int - KeyServices []keyservice.KeyServiceClient - DecryptionOrder []string - Interactive bool - ConfigPath string - InputType string - DecryptionCredentials map[string]string + InputPath string + GroupQuorum int + KeyServices []keyservice.KeyServiceClient + DecryptionOrder []string + Interactive bool + ConfigPath string + InputType string } // UpdateKeys update the keys for a given file @@ -56,6 +55,9 @@ func updateFile(opts Opts) error { if err != nil { return err } + if conf == nil { + return fmt.Errorf("The config file %s does not contain any creation rule", opts.ConfigPath) + } diffs := common.DiffKeyGroups(tree.Metadata.KeyGroups, conf.KeyGroups) keysWillChange := false @@ -85,7 +87,7 @@ func updateFile(opts Opts) error { return nil } } - key, err := tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices, opts.DecryptionOrder, nil) + key, err := tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices, opts.DecryptionOrder) if err != nil { return common.NewExitError(err, codes.CouldNotRetrieveKey) } @@ -94,7 +96,7 @@ func updateFile(opts Opts) error { tree.Metadata.ShamirThreshold = opts.GroupQuorum } tree.Metadata.ShamirThreshold = min(tree.Metadata.ShamirThreshold, len(tree.Metadata.KeyGroups)) - errs := tree.Metadata.UpdateMasterKeysWithKeyServices(key, opts.KeyServices, opts.DecryptionCredentials) + errs := tree.Metadata.UpdateMasterKeysWithKeyServices(key, opts.KeyServices) if len(errs) > 0 { return fmt.Errorf("error updating one or more master keys: %s", errs) } diff --git a/cmd/sops/unset.go b/cmd/sops/unset.go new file mode 100644 index 000000000..bb369748c --- /dev/null +++ b/cmd/sops/unset.go @@ -0,0 +1,67 @@ +package main + +import ( + "fmt" + + "github.com/getsops/sops/v3" + "github.com/getsops/sops/v3/cmd/sops/codes" + "github.com/getsops/sops/v3/cmd/sops/common" + "github.com/getsops/sops/v3/keyservice" +) + +type unsetOpts struct { + Cipher sops.Cipher + InputStore sops.Store + OutputStore sops.Store + InputPath string + IgnoreMAC bool + TreePath []interface{} + KeyServices []keyservice.KeyServiceClient + DecryptionOrder []string +} + +func unset(opts unsetOpts) ([]byte, error) { + // Load the file + tree, err := common.LoadEncryptedFileWithBugFixes(common.GenericDecryptOpts{ + Cipher: opts.Cipher, + InputStore: opts.InputStore, + InputPath: opts.InputPath, + IgnoreMAC: opts.IgnoreMAC, + KeyServices: opts.KeyServices, + }) + if err != nil { + return nil, err + } + + // Decrypt the file + dataKey, err := common.DecryptTree(common.DecryptTreeOpts{ + Cipher: opts.Cipher, + IgnoreMac: opts.IgnoreMAC, + Tree: tree, + KeyServices: opts.KeyServices, + DecryptionOrder: opts.DecryptionOrder, + }) + if err != nil { + return nil, err + } + + // Unset the value + newBranch, err := tree.Branches[0].Unset(opts.TreePath) + if err != nil { + return nil, err + } + tree.Branches[0] = newBranch + + err = common.EncryptTree(common.EncryptTreeOpts{ + DataKey: dataKey, Tree: tree, Cipher: opts.Cipher, + }) + if err != nil { + return nil, err + } + + encryptedFile, err := opts.OutputStore.EmitEncryptedFile(*tree) + if err != nil { + return nil, common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree) + } + return encryptedFile, err +} diff --git a/config/config.go b/config/config.go index 22105c55f..9620c0da9 100644 --- a/config/config.go +++ b/config/config.go @@ -94,6 +94,7 @@ type configFile struct { } type keyGroup struct { + Merge []keyGroup KMS []kmsKey GCPKMS []gcpKmsKey `yaml:"gcp_kms"` AzureKV []azureKVKey `yaml:"azure_keyvault"` @@ -134,21 +135,23 @@ type destinationRule struct { } type creationRule struct { - PathRegex string `yaml:"path_regex"` - KMS string - AwsProfile string `yaml:"aws_profile"` - Age string `yaml:"age"` - PGP string - GCPKMS string `yaml:"gcp_kms"` - AzureKeyVault string `yaml:"azure_keyvault"` - VaultURI string `yaml:"hc_vault_transit_uri"` - KeyGroups []keyGroup `yaml:"key_groups"` - ShamirThreshold int `yaml:"shamir_threshold"` - UnencryptedSuffix string `yaml:"unencrypted_suffix"` - EncryptedSuffix string `yaml:"encrypted_suffix"` - UnencryptedRegex string `yaml:"unencrypted_regex"` - EncryptedRegex string `yaml:"encrypted_regex"` - MACOnlyEncrypted bool `yaml:"mac_only_encrypted"` + PathRegex string `yaml:"path_regex"` + KMS string + AwsProfile string `yaml:"aws_profile"` + Age string `yaml:"age"` + PGP string + GCPKMS string `yaml:"gcp_kms"` + AzureKeyVault string `yaml:"azure_keyvault"` + VaultURI string `yaml:"hc_vault_transit_uri"` + KeyGroups []keyGroup `yaml:"key_groups"` + ShamirThreshold int `yaml:"shamir_threshold"` + UnencryptedSuffix string `yaml:"unencrypted_suffix"` + EncryptedSuffix string `yaml:"encrypted_suffix"` + UnencryptedRegex string `yaml:"unencrypted_regex"` + EncryptedRegex string `yaml:"encrypted_regex"` + UnencryptedCommentRegex string `yaml:"unencrypted_comment_regex"` + EncryptedCommentRegex string `yaml:"encrypted_comment_regex"` + MACOnlyEncrypted bool `yaml:"mac_only_encrypted"` } func NewStoresConfig() *StoresConfig { @@ -169,49 +172,85 @@ func (f *configFile) load(bytes []byte) error { // Config is the configuration for a given SOPS file type Config struct { - KeyGroups []sops.KeyGroup - ShamirThreshold int - UnencryptedSuffix string - EncryptedSuffix string - UnencryptedRegex string - EncryptedRegex string - MACOnlyEncrypted bool - Destination publish.Destination - OmitExtensions bool + KeyGroups []sops.KeyGroup + ShamirThreshold int + UnencryptedSuffix string + EncryptedSuffix string + UnencryptedRegex string + EncryptedRegex string + UnencryptedCommentRegex string + EncryptedCommentRegex string + MACOnlyEncrypted bool + Destination publish.Destination + OmitExtensions bool +} + +func deduplicateKeygroup(group sops.KeyGroup) sops.KeyGroup { + var deduplicatedKeygroup sops.KeyGroup + + unique := make(map[string]bool) + for _, v := range group { + key := fmt.Sprintf("%T/%v", v, v.ToString()) + if _, ok := unique[key]; ok { + // key already contained, therefore not unique + continue + } + + deduplicatedKeygroup = append(deduplicatedKeygroup, v) + unique[key] = true + } + + return deduplicatedKeygroup +} + +func extractMasterKeys(group keyGroup) (sops.KeyGroup, error) { + var keyGroup sops.KeyGroup + for _, k := range group.Merge { + subKeyGroup, err := extractMasterKeys(k) + if err != nil { + return nil, err + } + keyGroup = append(keyGroup, subKeyGroup...) + } + + for _, k := range group.Age { + keys, err := age.MasterKeysFromRecipients(k) + if err != nil { + return nil, err + } + for _, key := range keys { + keyGroup = append(keyGroup, key) + } + } + for _, k := range group.PGP { + keyGroup = append(keyGroup, pgp.NewMasterKeyFromFingerprint(k)) + } + for _, k := range group.KMS { + keyGroup = append(keyGroup, kms.NewMasterKeyWithProfile(k.Arn, k.Role, k.Context, k.AwsProfile)) + } + for _, k := range group.GCPKMS { + keyGroup = append(keyGroup, gcpkms.NewMasterKeyFromResourceID(k.ResourceID)) + } + for _, k := range group.AzureKV { + keyGroup = append(keyGroup, azkv.NewMasterKey(k.VaultURL, k.Key, k.Version)) + } + for _, k := range group.Vault { + if masterKey, err := hcvault.NewMasterKeyFromURI(k); err == nil { + keyGroup = append(keyGroup, masterKey) + } else { + return nil, err + } + } + return deduplicateKeygroup(keyGroup), nil } func getKeyGroupsFromCreationRule(cRule *creationRule, kmsEncryptionContext map[string]*string) ([]sops.KeyGroup, error) { var groups []sops.KeyGroup if len(cRule.KeyGroups) > 0 { for _, group := range cRule.KeyGroups { - var keyGroup sops.KeyGroup - for _, k := range group.Age { - keys, err := age.MasterKeysFromRecipients(k) - if err != nil { - return nil, err - } - for _, key := range keys { - keyGroup = append(keyGroup, key) - } - } - for _, k := range group.PGP { - keyGroup = append(keyGroup, pgp.NewMasterKeyFromFingerprint(k)) - } - for _, k := range group.KMS { - keyGroup = append(keyGroup, kms.NewMasterKeyWithProfile(k.Arn, k.Role, k.Context, k.AwsProfile)) - } - for _, k := range group.GCPKMS { - keyGroup = append(keyGroup, gcpkms.NewMasterKeyFromResourceID(k.ResourceID)) - } - for _, k := range group.AzureKV { - keyGroup = append(keyGroup, azkv.NewMasterKey(k.VaultURL, k.Key, k.Version)) - } - for _, k := range group.Vault { - if masterKey, err := hcvault.NewMasterKeyFromURI(k); err == nil { - keyGroup = append(keyGroup, masterKey) - } else { - return nil, err - } + keyGroup, err := extractMasterKeys(group) + if err != nil { + return nil, err } groups = append(groups, keyGroup) } @@ -269,16 +308,6 @@ func loadConfigFile(confPath string) (*configFile, error) { return conf, nil } -func loadConfigFromString(yamlString string) (*configFile, error) { - conf := &configFile{} - conf.Stores = *NewStoresConfig() - err := conf.load([]byte(yamlString)) - if err != nil { - return nil, fmt.Errorf("error loading config: %s", err) - } - return conf, nil -} - func configFromRule(rule *creationRule, kmsEncryptionContext map[string]*string) (*Config, error) { cryptRuleCount := 0 if rule.UnencryptedSuffix != "" { @@ -293,9 +322,15 @@ func configFromRule(rule *creationRule, kmsEncryptionContext map[string]*string) if rule.EncryptedRegex != "" { cryptRuleCount++ } + if rule.UnencryptedCommentRegex != "" { + cryptRuleCount++ + } + if rule.EncryptedCommentRegex != "" { + cryptRuleCount++ + } if cryptRuleCount > 1 { - return nil, fmt.Errorf("error loading config: cannot use more than one of encrypted_suffix, unencrypted_suffix, encrypted_regex, or unencrypted_regex for the same rule") + return nil, fmt.Errorf("error loading config: cannot use more than one of encrypted_suffix, unencrypted_suffix, encrypted_regex, unencrypted_regex, encrypted_comment_regex, or unencrypted_comment_regex for the same rule") } groups, err := getKeyGroupsFromCreationRule(rule, kmsEncryptionContext) @@ -304,13 +339,15 @@ func configFromRule(rule *creationRule, kmsEncryptionContext map[string]*string) } return &Config{ - KeyGroups: groups, - ShamirThreshold: rule.ShamirThreshold, - UnencryptedSuffix: rule.UnencryptedSuffix, - EncryptedSuffix: rule.EncryptedSuffix, - UnencryptedRegex: rule.UnencryptedRegex, - EncryptedRegex: rule.EncryptedRegex, - MACOnlyEncrypted: rule.MACOnlyEncrypted, + KeyGroups: groups, + ShamirThreshold: rule.ShamirThreshold, + UnencryptedSuffix: rule.UnencryptedSuffix, + EncryptedSuffix: rule.EncryptedSuffix, + UnencryptedRegex: rule.UnencryptedRegex, + EncryptedRegex: rule.EncryptedRegex, + UnencryptedCommentRegex: rule.UnencryptedCommentRegex, + EncryptedCommentRegex: rule.EncryptedCommentRegex, + MACOnlyEncrypted: rule.MACOnlyEncrypted, }, nil } @@ -420,15 +457,6 @@ func LoadCreationRuleForFile(confPath string, filePath string, kmsEncryptionCont return parseCreationRuleForFile(conf, confPath, filePath, kmsEncryptionContext) } -func LoadCreationRuleFromConfigString(configString string, filePath string, kmsEncryptionContext map[string]*string) (*Config, error) { - conf, err := loadConfigFromString(configString) - if err != nil { - return nil, err - } - - return parseCreationRuleForFile(conf, filePath, filePath, kmsEncryptionContext) -} - // LoadDestinationRuleForFile works the same as LoadCreationRuleForFile, but gets the "creation_rule" from the matching destination_rule's // "recreation_rule". func LoadDestinationRuleForFile(confPath string, filePath string, kmsEncryptionContext map[string]*string) (*Config, error) { diff --git a/config/config_test.go b/config/config_test.go index 8f4fb006b..abf2c66a5 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -120,6 +120,125 @@ creation_rules: - 'https://baz.vault:8200/v1/baz/keys/baz-key' `) +var sampleConfigWithMergeType = []byte(` +creation_rules: + - path_regex: "" + key_groups: + # key00 + - hc_vault: + - 'https://foo.vault:8200/v1/foo/keys/foo-key' + - merge: + - merge: + - kms: + # key01 + - arn: foo + aws_profile: foo + pgp: + # key02 + - foo + gcp_kms: + # key03 + - resource_id: foo + azure_keyvault: + # key04 + - vaultUrl: https://foo.vault.azure.net + key: foo-key + version: fooversion + hc_vault: + # key05 + - 'https://bar.vault:8200/v1/bar/keys/bar-key' + - kms: + # key06 + - arn: bar + aws_profile: bar + pgp: + # key07 + - bar + gcp_kms: + # key08 + - resource_id: bar + # key09 + - resource_id: baz + azure_keyvault: + # key10 + - vaultUrl: https://bar.vault.azure.net + key: bar-key + version: barversion + hc_vault: + # key01 - duplicate#1 + - 'https://baz.vault:8200/v1/baz/keys/baz-key' + kms: + # key11 + - arn: baz + aws_profile: baz + pgp: + # key12 + - baz + gcp_kms: + # key03 - duplicate#2 + # --> should be removed when loading config + - resource_id: bar + azure_keyvault: + # key04 - duplicate#3 + - vaultUrl: https://foo.vault.azure.net + key: foo-key + version: fooversion + hc_vault: + # key13 - duplicate#4 - but from different key_group + # --> should stay + - 'https://foo.vault:8200/v1/foo/keys/foo-key' + - kms: + # key14 + - arn: qux + aws_profile: qux + # key14 - duplicate#5 + - arn: baz + aws_profile: bar + pgp: + # key15 + - qux + gcp_kms: + # key16 + - resource_id: qux + # key17 + - resource_id: fnord + azure_keyvault: + # key18 + - vaultUrl: https://baz.vault.azure.net + key: baz-key + version: bazversion + hc_vault: + # key19 + - 'https://qux.vault:8200/v1/qux/keys/qux-key' + # everything below this should be loaded, + # since it is not in a merge block + kms: + # duplicated key06 + - arn: bar + aws_profile: bar + # key20 + - arn: fnord + aws_profile: fnord + pgp: + # duplicated key07 + - bar + gcp_kms: + # duplicated key08 + - resource_id: bar + # key21 + - resource_id: fnord + azure_keyvault: + # duplicated key10 + - vaultUrl: https://bar.vault.azure.net + key: bar-key + version: barversion + hc_vault: + # duplicated 'key01 - duplicate#2' + - 'https://baz.vault:8200/v1/baz/keys/baz-key' + # key22 + - 'https://fnord.vault:8200/v1/fnord/keys/fnord-key' +`) + var sampleConfigWithSuffixParameters = []byte(` creation_rules: - path_regex: foobar* @@ -166,6 +285,22 @@ creation_rules: mac_only_encrypted: true `) +var sampleConfigWithEncryptedCommentRegexParameters = []byte(` +creation_rules: + - path_regex: barbar* + kms: "1" + pgp: "2" + encrypted_comment_regex: "sops:enc" + `) + +var sampleConfigWithUnencryptedCommentRegexParameters = []byte(` +creation_rules: + - path_regex: barbar* + kms: "1" + pgp: "2" + unencrypted_comment_regex: "sops:dec" + `) + var sampleConfigWithInvalidParameters = []byte(` creation_rules: - path_regex: foobar* @@ -324,6 +459,14 @@ func TestLoadConfigFileWithGroups(t *testing.T) { assert.Equal(t, expected, conf) } +func TestLoadConfigFileWithMerge(t *testing.T) { + conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithMergeType, t), "/conf/path", "whatever", nil) + assert.Nil(t, err) + assert.Equal(t, 2, len(conf.KeyGroups)) + assert.Equal(t, 1, len(conf.KeyGroups[0])) + assert.Equal(t, 22, len(conf.KeyGroups[1])) +} + func TestLoadConfigFileWithNoMatchingRules(t *testing.T) { _, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithNoMatchingRules, t), "/conf/path", "foobar2000", nil) assert.NotNil(t, err) @@ -430,6 +573,18 @@ func TestLoadConfigFileWithMACOnlyEncrypted(t *testing.T) { assert.Equal(t, true, conf.MACOnlyEncrypted) } +func TestLoadConfigFileWithUnencryptedCommentRegex(t *testing.T) { + conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithUnencryptedCommentRegexParameters, t), "/conf/path", "barbar", nil) + assert.Equal(t, nil, err) + assert.Equal(t, "sops:dec", conf.UnencryptedCommentRegex) +} + +func TestLoadConfigFileWithEncryptedCommentRegex(t *testing.T) { + conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithEncryptedCommentRegexParameters, t), "/conf/path", "barbar", nil) + assert.Equal(t, nil, err) + assert.Equal(t, "sops:enc", conf.EncryptedCommentRegex) +} + func TestLoadConfigFileWithInvalidParameters(t *testing.T) { _, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithInvalidParameters, t), "/conf/path", "foobar", nil) assert.NotNil(t, err) diff --git a/functional-tests/res/plainfile.yaml b/functional-tests/res/plainfile.yaml new file mode 100644 index 000000000..bb56b0551 --- /dev/null +++ b/functional-tests/res/plainfile.yaml @@ -0,0 +1 @@ +hello: world diff --git a/functional-tests/src/lib.rs b/functional-tests/src/lib.rs index 46703a459..cca673edb 100644 --- a/functional-tests/src/lib.rs +++ b/functional-tests/src/lib.rs @@ -273,12 +273,12 @@ bar: baz", .arg(r#"{"aa": "aaa"}"#) .output() .expect("Error running sops"); - assert!(output.status.success(), "sops didn't exit successfully"); println!( "stdout: {}, stderr: {}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ); + assert!(output.status.success(), "sops didn't exit successfully"); let mut s = String::new(); File::open(file_path) .unwrap() @@ -317,12 +317,12 @@ bar: baz", .arg(r#"{"cc": "ccc"}"#) .output() .expect("Error running sops"); - assert!(output.status.success(), "sops didn't exit successfully"); println!( "stdout: {}, stderr: {}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ); + assert!(output.status.success(), "sops didn't exit successfully"); let mut s = String::new(); File::open(file_path) .unwrap() @@ -365,12 +365,12 @@ b: ba"# .arg(r#"{"aa": "aaa"}"#) .output() .expect("Error running sops"); - assert!(output.status.success(), "sops didn't exit successfully"); println!( "stdout: {}, stderr: {}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ); + assert!(output.status.success(), "sops didn't exit successfully"); let mut s = String::new(); File::open(file_path) .unwrap() @@ -413,12 +413,12 @@ b: ba"# .arg(r#"{"cc": "ccc"}"#) .output() .expect("Error running sops"); - assert!(output.status.success(), "sops didn't exit successfully"); println!( "stdout: {}, stderr: {}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ); + assert!(output.status.success(), "sops didn't exit successfully"); let mut s = String::new(); File::open(file_path) .unwrap() @@ -472,12 +472,12 @@ b: ba"# .arg(file_path.clone()) .output() .expect("Error running sops"); - assert!(output.status.success(), "sops didn't exit successfully"); println!( "stdout: {}, stderr: {}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ); + assert!(output.status.success(), "sops didn't exit successfully"); let mut s = String::new(); File::open(file_path) .unwrap() @@ -529,12 +529,12 @@ b: ba"# .arg(file_path.clone()) .output() .expect("Error running sops"); - assert!(output.status.success(), "sops didn't exit successfully"); println!( "stdout: {}, stderr: {}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ); + assert!(output.status.success(), "sops didn't exit successfully"); let mut s = String::new(); File::open(file_path) .unwrap() @@ -549,6 +549,224 @@ b: ba"# } } + #[test] + fn unset_json_file() { + // Test removal of tree branch + let file_path = + prepare_temp_file("test_unset.json", r#"{"a": 2, "b": "ba", "c": [1,2]}"#.as_bytes()); + assert!( + Command::new(SOPS_BINARY_PATH) + .arg("encrypt") + .arg("-i") + .arg(file_path.clone()) + .output() + .expect("Error running sops") + .status + .success(), + "sops didn't exit successfully" + ); + let output = Command::new(SOPS_BINARY_PATH) + .arg("unset") + .arg(file_path.clone()) + .arg(r#"["a"]"#) + .output() + .expect("Error running sops"); + println!( + "stdout: {}, stderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + assert!(output.status.success(), "sops didn't exit successfully"); + let mut content = String::new(); + File::open(file_path.clone()) + .unwrap() + .read_to_string(&mut content) + .unwrap(); + let data: Value = serde_json::from_str(&content).expect("Error parsing sops's JSON output"); + if let Value::Mapping(data) = data { + assert!(!data.contains_key(&Value::String("a".to_owned()))); + assert!(data.contains_key(&Value::String("b".to_owned()))); + } else { + panic!("Output JSON does not have the expected structure"); + } + + // Test idempotent unset + let output = Command::new(SOPS_BINARY_PATH) + .arg("unset") + .arg("--idempotent") + .arg(file_path.clone()) + .arg(r#"["a"]"#) + .output() + .expect("Error running sops"); + println!( + "stdout: {}, stderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + assert!(output.status.success(), "sops didn't exit successfully"); + let mut idempotent_content = String::new(); + File::open(file_path.clone()) + .unwrap() + .read_to_string(&mut idempotent_content) + .unwrap(); + assert!(idempotent_content == content); + + // Test removal of list item + let output = Command::new(SOPS_BINARY_PATH) + .arg("unset") + .arg(file_path.clone()) + .arg(r#"["c"][1]"#) + .output() + .expect("Error running sops"); + println!( + "stdout: {}, stderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + assert!(output.status.success(), "sops didn't exit successfully"); + let mut content = String::new(); + File::open(file_path.clone()) + .unwrap() + .read_to_string(&mut content) + .unwrap(); + let data: Value = serde_json::from_str(&content).expect("Error parsing sops's JSON output"); + if let Value::Mapping(data) = data { + assert_eq!(data["c"].as_sequence().unwrap().len(), 1); + } else { + panic!("Output JSON does not have the expected structure"); + } + + // Test idempotent unset list item + let output = Command::new(SOPS_BINARY_PATH) + .arg("unset") + .arg("--idempotent") + .arg(file_path.clone()) + .arg(r#"["c"][1]"#) + .output() + .expect("Error running sops"); + println!( + "stdout: {}, stderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + assert!(output.status.success(), "sops didn't exit successfully"); + let mut idempotent_content = String::new(); + File::open(file_path.clone()) + .unwrap() + .read_to_string(&mut idempotent_content) + .unwrap(); + assert!(idempotent_content == content); + } + + #[test] + fn unset_yaml_file() { + // Test removal of tree branch + let file_path = + prepare_temp_file("test_unset.yaml", r#"{"a": 2, "b": "ba", "c": [1,2]}"#.as_bytes()); + assert!( + Command::new(SOPS_BINARY_PATH) + .arg("encrypt") + .arg("-i") + .arg(file_path.clone()) + .output() + .expect("Error running sops") + .status + .success(), + "sops didn't exit successfully" + ); + let output = Command::new(SOPS_BINARY_PATH) + .arg("unset") + .arg(file_path.clone()) + .arg(r#"["a"]"#) + .output() + .expect("Error running sops"); + println!( + "stdout: {}, stderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + assert!(output.status.success(), "sops didn't exit successfully"); + let mut content = String::new(); + File::open(file_path.clone()) + .unwrap() + .read_to_string(&mut content) + .unwrap(); + let data: Value = serde_yaml::from_str(&content).expect("Error parsing sops's YAML output"); + if let Value::Mapping(data) = data { + assert!(!data.contains_key(&Value::String("a".to_owned()))); + assert!(data.contains_key(&Value::String("b".to_owned()))); + } else { + panic!("Output YAML does not have the expected structure"); + } + + // Test idempotent unset + let output = Command::new(SOPS_BINARY_PATH) + .arg("unset") + .arg("--idempotent") + .arg(file_path.clone()) + .arg(r#"["a"]"#) + .output() + .expect("Error running sops"); + println!( + "stdout: {}, stderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + assert!(output.status.success(), "sops didn't exit successfully"); + let mut idempotent_content = String::new(); + File::open(file_path.clone()) + .unwrap() + .read_to_string(&mut idempotent_content) + .unwrap(); + assert!(idempotent_content == content); + + // Test removal of list item + let output = Command::new(SOPS_BINARY_PATH) + .arg("unset") + .arg(file_path.clone()) + .arg(r#"["c"][0]"#) + .output() + .expect("Error running sops"); + println!( + "stdout: {}, stderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + assert!(output.status.success(), "sops didn't exit successfully"); + let mut content = String::new(); + File::open(file_path.clone()) + .unwrap() + .read_to_string(&mut content) + .unwrap(); + let data: Value = serde_yaml::from_str(&content).expect("Error parsing sops's YAML output"); + if let Value::Mapping(data) = data { + assert_eq!(data["c"].as_sequence().unwrap().len(), 1); + } else { + panic!("Output YAML does not have the expected structure"); + } + + // Test idempotent unset list item + let output = Command::new(SOPS_BINARY_PATH) + .arg("unset") + .arg("--idempotent") + .arg(file_path.clone()) + .arg(r#"["c"][1]"#) + .output() + .expect("Error running sops"); + println!( + "stdout: {}, stderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + assert!(output.status.success(), "sops didn't exit successfully"); + let mut idempotent_content = String::new(); + File::open(file_path.clone()) + .unwrap() + .read_to_string(&mut idempotent_content) + .unwrap(); + assert!(idempotent_content == content); + } + #[test] fn decrypt_file_no_mac() { let file_path = prepare_temp_file( @@ -876,12 +1094,12 @@ echo -E "${foo}" .arg(format!("/bin/bash {}", print_foo)) .output() .expect("Error running sops"); - assert!(output.status.success(), "sops didn't exit successfully"); println!( "stdout: {}, stderr: {}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ); + assert!(output.status.success(), "sops didn't exit successfully"); assert_eq!(String::from_utf8_lossy(&output.stdout), "bar\n"); let print_bar = prepare_temp_file( "print_bar.sh", @@ -896,13 +1114,13 @@ echo -E "${bar}" .arg(format!("/bin/bash {}", print_bar)) .output() .expect("Error running sops"); - assert!(output.status.success(), "sops didn't exit successfully"); println!( "stdout: {}, stderr: {}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ); - assert_eq!(String::from_utf8_lossy(&output.stdout), "baz\\nbam\n"); + assert!(output.status.success(), "sops didn't exit successfully"); + assert_eq!(String::from_utf8_lossy(&output.stdout), "baz\nbam\n"); } #[test] @@ -935,12 +1153,12 @@ bar: |- .arg("cat {}") .output() .expect("Error running sops"); - assert!(output.status.success(), "sops didn't exit successfully"); println!( "stdout: {}, stderr: {}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ); + assert!(output.status.success(), "sops didn't exit successfully"); assert_eq!( String::from_utf8_lossy(&output.stdout), r#"{ @@ -949,4 +1167,47 @@ bar: |- }"# ); } + + #[test] + fn exec_file_filename() { + let file_path = prepare_temp_file( + "test_exec_file_filename.yaml", + r#"foo: bar +bar: |- + baz + bam +"# + .as_bytes(), + ); + assert!( + Command::new(SOPS_BINARY_PATH) + .arg("-e") + .arg("-i") + .arg(file_path.clone()) + .output() + .expect("Error running sops") + .status + .success(), + "sops didn't exit successfully" + ); + let output = Command::new(SOPS_BINARY_PATH) + .arg("exec-file") + .arg("--no-fifo") + .arg("--filename") + .arg("foobar") + .arg(file_path.clone()) + .arg("echo {}") + .output() + .expect("Error running sops"); + assert!(output.status.success(), "sops didn't exit successfully"); + println!( + "stdout: {}, stderr: {}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + assert!( + String::from_utf8_lossy(&output.stdout).ends_with("foobar\n"), + "filename did not end with 'foobar'" + ); + } } diff --git a/gcpkms/keysource.go b/gcpkms/keysource.go index 733f53c20..0f8e39710 100644 --- a/gcpkms/keysource.go +++ b/gcpkms/keysource.go @@ -4,7 +4,6 @@ import ( "context" "encoding/base64" "fmt" - "golang.org/x/oauth2" "os" "regexp" "strings" @@ -13,6 +12,7 @@ import ( kms "cloud.google.com/go/kms/apiv1" "cloud.google.com/go/kms/apiv1/kmspb" "github.com/sirupsen/logrus" + "golang.org/x/oauth2" "google.golang.org/api/option" "google.golang.org/grpc" @@ -24,11 +24,9 @@ const ( // a path to a credentials file, or directly as the variable's value in JSON // format. SopsGoogleCredentialsEnv = "GOOGLE_CREDENTIALS" - - // SopsGoogleAccessTokenEnv can be set as an environment variable as - // directly the google access token, instead of a credentials file - SopsGoogleAccessTokenEnv = "CLOUDSDK_AUTH_ACCESS_TOKEN" - + // SopsGoogleCredentialsOAuthToken can be set as an environment variable as either + // a path to a file, or directly as the varialbe's value + SopsGoogleCredentialsOAuthToken = "GOOGLE_OAUTH_ACCESS_TOKEN" // KeyTypeIdentifier is the string used to identify a GCP KMS MasterKey. KeyTypeIdentifier = "gcp_kms" ) @@ -59,8 +57,6 @@ type MasterKey struct { // credentialJSON is the Service Account credentials JSON used for // authenticating towards the GCP KMS service. credentialJSON []byte - - AccessToken AccessToken // grpcConn can be used to inject a custom GCP client connection. // Mostly useful for testing at present, to wire the client to a mock // server. @@ -90,12 +86,6 @@ func MasterKeysFromResourceIDString(resourceID string) []*MasterKey { return keys } -type AccessToken string - -func (a AccessToken) ApplyToMasterKey(key *MasterKey) { - key.AccessToken = a -} - // CredentialJSON is the Service Account credentials JSON used for authenticating // towards the GCP KMS service. type CredentialJSON []byte @@ -230,29 +220,33 @@ func (key *MasterKey) newKMSClient() (*kms.KeyManagementClient, error) { var opts []option.ClientOption switch { - case key.AccessToken != "": - opts = append(opts, option.WithTokenSource(oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: string(key.AccessToken)}, - ))) case key.credentialJSON != nil: opts = append(opts, option.WithCredentialsJSON(key.credentialJSON)) default: - credentialsOption, err := getGoogleCredentials() - if err != nil { - return nil, err + credentials, err_credentials_file := getGoogleCredentials() + if credentials != nil { + opts = append(opts, option.WithCredentialsJSON(credentials)) + break + } + + at_credentials, err_credentials_token := getGoogleOAuthToken() + if at_credentials != nil { + opts = append(opts, option.WithTokenSource(at_credentials)) } - if credentialsOption != nil { - opts = append(opts, credentialsOption) + + if err_credentials_file != nil && err_credentials_token != nil { + return nil, fmt.Errorf("credentials: failed to get credentials for gcp kms, add default credentials or oauth access token") } } + if key.grpcConn != nil { opts = append(opts, option.WithGRPCConn(key.grpcConn)) } ctx := context.Background() - client, err := kms.NewKeyManagementClient(ctx, opts...) - if err != nil { - return nil, err + client, err_credentials := kms.NewKeyManagementClient(ctx, opts...) + if err_credentials != nil { + return nil, err_credentials } return client, nil @@ -262,27 +256,28 @@ func (key *MasterKey) newKMSClient() (*kms.KeyManagementClient, error) { // either the file contents of the path of a credentials file, or as value in // JSON format. It returns an error if the file cannot be read, and may return // a nil byte slice if no value is set. -func getGoogleCredentials() (option.ClientOption, error) { - if envCredentials, isSet := os.LookupEnv(SopsGoogleCredentialsEnv); isSet { - credentials := []byte(envCredentials) - if _, err := os.Stat(envCredentials); err == nil { - if credentials, err = os.ReadFile(envCredentials); err != nil { - return nil, err - } +func getGoogleCredentials() ([]byte, error) { + if defaultCredentials, ok := os.LookupEnv(SopsGoogleCredentialsEnv); ok && len(defaultCredentials) > 0 { + if _, err := os.Stat(defaultCredentials); err == nil { + return os.ReadFile(defaultCredentials) } - return option.WithCredentialsJSON(credentials), nil + + return []byte(defaultCredentials), nil } - if envToken, isSet := os.LookupEnv(SopsGoogleAccessTokenEnv); isSet { - token := []byte(envToken) - if _, err := os.Stat(envToken); err == nil { - if token, err = os.ReadFile(envToken); err != nil { - return nil, err - } - } + return nil, fmt.Errorf("could not find Google credential file") +} + +// getGoogleOAuthToken returns the SopsGoogleCredentialsOauthToken variable, +// as the oauth token. +// It returns an error and nil if the envrionment variable is not set. +func getGoogleOAuthToken() (oauth2.TokenSource, error) { + if token, isSet := os.LookupEnv(SopsGoogleCredentialsOAuthToken); isSet { tokenSource := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: string(token)}, + &oauth2.Token{AccessToken: token}, ) - return option.WithTokenSource(tokenSource), nil + + return tokenSource, nil } - return nil, nil + + return nil, fmt.Errorf("could not find Google OAuth token") } diff --git a/gcpkms/keysource_test.go b/gcpkms/keysource_test.go index 153bfb260..79015ff02 100644 --- a/gcpkms/keysource_test.go +++ b/gcpkms/keysource_test.go @@ -53,8 +53,9 @@ func TestMasterKey_Encrypt(t *testing.T) { }) key := MasterKey{ - grpcConn: newGRPCServer("0"), - ResourceID: testResourceID, + grpcConn: newGRPCServer("0"), + ResourceID: testResourceID, + credentialJSON: []byte("arbitrary credentials"), } err := key.Encrypt([]byte("encrypt")) assert.NoError(t, err) @@ -80,9 +81,10 @@ func TestMasterKey_Decrypt(t *testing.T) { Plaintext: []byte(decryptedData), }) key := MasterKey{ - grpcConn: newGRPCServer("0"), - ResourceID: testResourceID, - EncryptedKey: "encryptedKey", + grpcConn: newGRPCServer("0"), + ResourceID: testResourceID, + EncryptedKey: "encryptedKey", + credentialJSON: []byte("arbitrary credentials"), } data, err := key.Decrypt() assert.NoError(t, err) @@ -116,7 +118,7 @@ func TestMasterKey_ToMap(t *testing.T) { }, key.ToMap()) } -func TestMasterKey_createCloudKMSService(t *testing.T) { +func TestMasterKey_createCloudKMSService_withCredentialsFile(t *testing.T) { tests := []struct { key MasterKey errString string @@ -136,6 +138,12 @@ func TestMasterKey_createCloudKMSService(t *testing.T) { "type": "authorized_user"}`), }, }, + { + key: MasterKey{ + ResourceID: testResourceID, + }, + errString: "credentials: failed to get credentials", + }, } for _, tt := range tests { @@ -149,6 +157,29 @@ func TestMasterKey_createCloudKMSService(t *testing.T) { } } +func TestMasterKey_createCloudKMSService_withOauthToken(t *testing.T) { + t.Setenv(SopsGoogleCredentialsOAuthToken, "token") + + masterKey := MasterKey{ + ResourceID: testResourceID, + } + + _, err := masterKey.newKMSClient() + + assert.NoError(t, err) +} + +func TestMasterKey_createCloudKMSService_withoutCredentials(t *testing.T) { + masterKey := MasterKey{ + ResourceID: testResourceID, + } + + _, err := masterKey.newKMSClient() + + assert.Error(t, err) + assert.ErrorContains(t, err, "credentials: failed to get credentials") +} + func newGRPCServer(port string) *grpc.ClientConn { serv := grpc.NewServer() kmspb.RegisterKeyManagementServiceServer(serv, &mockKeyManagement) diff --git a/go.mod b/go.mod index 328416e11..61b189d5b 100644 --- a/go.mod +++ b/go.mod @@ -1,139 +1,137 @@ module github.com/getsops/sops/v3 -go 1.19 +go 1.22 + +toolchain go1.23.0 require ( - cloud.google.com/go/kms v1.15.7 - cloud.google.com/go/storage v1.38.0 - filippo.io/age v1.1.1 - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 + cloud.google.com/go/kms v1.19.0 + cloud.google.com/go/storage v1.43.0 + filippo.io/age v1.2.0 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.1.0 - github.com/ProtonMail/go-crypto v1.1.0-alpha.0-proton - github.com/aws/aws-sdk-go-v2 v1.25.0 - github.com/aws/aws-sdk-go-v2/config v1.27.0 - github.com/aws/aws-sdk-go-v2/credentials v1.17.0 - github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.0 - github.com/aws/aws-sdk-go-v2/service/kms v1.28.1 - github.com/aws/aws-sdk-go-v2/service/s3 v1.49.0 - github.com/aws/aws-sdk-go-v2/service/sts v1.27.0 + github.com/ProtonMail/go-crypto v1.1.0-alpha.5-proton + github.com/aws/aws-sdk-go-v2 v1.30.4 + github.com/aws/aws-sdk-go-v2/config v1.27.30 + github.com/aws/aws-sdk-go-v2/credentials v1.17.29 + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.14 + github.com/aws/aws-sdk-go-v2/service/kms v1.35.5 + github.com/aws/aws-sdk-go-v2/service/s3 v1.60.1 + github.com/aws/aws-sdk-go-v2/service/sts v1.30.5 github.com/blang/semver v3.5.1+incompatible - github.com/fatih/color v1.16.0 - github.com/getsops/gopgagent v0.0.0-20170926210634-4d7ea76ff71a - github.com/golang/protobuf v1.5.3 + github.com/fatih/color v1.17.0 + github.com/getsops/gopgagent v0.0.0-20240527072608-0c14999532fe + github.com/golang/protobuf v1.5.4 github.com/google/go-cmp v0.6.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/goware/prefixer v0.0.0-20160118172347-395022866408 github.com/hashicorp/go-cleanhttp v0.5.2 - github.com/hashicorp/vault/api v1.12.0 + github.com/hashicorp/vault/api v1.14.0 github.com/lib/pq v1.10.9 github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-wordwrap v1.0.1 - github.com/ory/dockertest/v3 v3.10.0 + github.com/ory/dockertest/v3 v3.11.0 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 - github.com/stretchr/testify v1.8.4 - github.com/urfave/cli v1.22.14 - golang.org/x/net v0.21.0 - golang.org/x/oauth2 v0.17.0 - golang.org/x/sys v0.17.0 - golang.org/x/term v0.17.0 - google.golang.org/api v0.165.0 - google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 - google.golang.org/grpc v1.61.1 - google.golang.org/protobuf v1.32.0 + github.com/stretchr/testify v1.9.0 + github.com/urfave/cli v1.22.15 + golang.org/x/net v0.28.0 + golang.org/x/oauth2 v0.22.0 + golang.org/x/sys v0.24.0 + golang.org/x/term v0.23.0 + google.golang.org/api v0.193.0 + google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 + google.golang.org/grpc v1.65.0 + google.golang.org/protobuf v1.34.2 gopkg.in/ini.v1 v1.67.0 gopkg.in/yaml.v3 v3.0.1 ) require ( - cloud.google.com/go v0.112.0 // indirect - cloud.google.com/go/compute v1.24.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v1.1.6 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect - github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 // indirect - github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 // indirect - github.com/Microsoft/go-winio v0.6.0 // indirect + cloud.google.com/go v0.115.1 // indirect + cloud.google.com/go/auth v0.9.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect + cloud.google.com/go/compute/metadata v0.5.0 // indirect + cloud.google.com/go/iam v1.1.13 // indirect + cloud.google.com/go/longrunning v0.5.12 // indirect + dario.cat/mergo v1.0.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.0 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.0 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.0 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.0 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.0 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.0 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.0 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.0 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.0 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.19.0 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.22.0 // indirect - github.com/aws/smithy-go v1.20.0 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.16 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.18 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.16 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 // indirect + github.com/aws/smithy-go v1.20.4 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect - github.com/cloudflare/circl v1.3.7 // indirect - github.com/containerd/continuity v0.3.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cloudflare/circl v1.3.9 // indirect + github.com/containerd/continuity v0.4.3 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/cli v20.10.17+incompatible // indirect - github.com/docker/docker v24.0.7+incompatible // indirect - github.com/docker/go-connections v0.4.0 // indirect - github.com/docker/go-units v0.4.0 // indirect + github.com/docker/cli v27.0.1+incompatible // indirect + github.com/docker/docker v27.1.1+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-jose/go-jose/v3 v3.0.1 // indirect - github.com/go-logr/logr v1.4.1 // indirect + github.com/go-jose/go-jose/v4 v4.0.2 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-viper/mapstructure/v2 v2.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v5 v5.2.0 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/s2a-go v0.1.7 // indirect + github.com/google/s2a-go v0.1.8 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.12.1 // indirect + github.com/googleapis/gax-go/v2 v2.13.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-hclog v1.2.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-retryablehttp v0.7.5 // indirect + github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.6 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/imdario/mergo v0.3.12 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/kr/pretty v0.2.1 // indirect - github.com/kr/text v0.2.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/term v0.5.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.2 // indirect - github.com/opencontainers/runc v1.1.12 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/opencontainers/runc v1.1.13 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 // indirect - go.opentelemetry.io/otel v1.23.1 // indirect - go.opentelemetry.io/otel/metric v1.23.1 // indirect - go.opentelemetry.io/otel/trace v1.23.1 // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/mod v0.9.0 // indirect - golang.org/x/sync v0.6.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.7.0 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9 // indirect - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + golang.org/x/crypto v0.26.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/text v0.17.0 // indirect + golang.org/x/time v0.6.0 // indirect + google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 01ad99732..e8028c30d 100644 --- a/go.sum +++ b/go.sum @@ -1,137 +1,145 @@ +c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805 h1:u2qwJeEvnypw+OCPUHmoZE3IqwfuN5kgDfo5MLzpNM0= +c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805/go.mod h1:FomMrUJ2Lxt5jCLmZkG3FHa72zUprnhd3v/Z18Snm4w= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= -cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= -cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= -cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= -cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= -cloud.google.com/go/kms v1.15.7 h1:7caV9K3yIxvlQPAcaFffhlT7d1qpxjB1wHBtjWa13SM= -cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI= -cloud.google.com/go/storage v1.38.0 h1:Az68ZRGlnNTpIBbLjSMIV2BDcwwXYlRlQzis0llkpJg= -cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY= -filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg= -filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2 h1:c4k2FIYIh4xtwqrQwV0Ct1v5+ehlNXj5NI/MWVsiTkQ= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2/go.mod h1:5FDJtLEO/GxwNgUxbwrY3LP0pEoThTQJtk2oysdXHxM= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc= +cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ= +cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc= +cloud.google.com/go/auth v0.9.0 h1:cYhKl1JUhynmxjXfrk4qdPc6Amw7i+GC9VLflgT0p5M= +cloud.google.com/go/auth v0.9.0/go.mod h1:2HsApZBr9zGZhC9QAXsYVYaWk8kNUt37uny+XVKi7wM= +cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= +cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= +cloud.google.com/go/iam v1.1.13 h1:7zWBXG9ERbMLrzQBRhFliAV+kjcRToDTgQT3CTwYyv4= +cloud.google.com/go/iam v1.1.13/go.mod h1:K8mY0uSXwEXS30KrnVb+j54LB/ntfZu1dr+4zFMNbus= +cloud.google.com/go/kms v1.19.0 h1:x0OVJDl6UH1BSX4THKlMfdcFWoE4ruh90ZHuilZekrU= +cloud.google.com/go/kms v1.19.0/go.mod h1:e4imokuPJUc17Trz2s6lEXFDt8bgDmvpVynH39bdrHM= +cloud.google.com/go/longrunning v0.5.12 h1:5LqSIdERr71CqfUsFlJdBpOkBH8FBCFD7P1nTWy3TYE= +cloud.google.com/go/longrunning v0.5.12/go.mod h1:S5hMV8CDJ6r50t2ubVJSKQVv5u0rmik5//KgLO3k4lU= +cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs= +cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +filippo.io/age v1.2.0 h1:vRDp7pUMaAJzXNIWJVAZnEf/Dyi4Vu4wI8S1LBzufhE= +filippo.io/age v1.2.0/go.mod h1:JL9ew2lTN+Pyft4RiNGguFfOpewKwSHm5ayKD/A4004= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.1.0 h1:DRiANoJTiW6obBQe3SqZizkuV1PEgfiiGivmVocDy64= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.1.0/go.mod h1:qLIye2hwb/ZouqhpSD9Zn3SJipvpEnz1Ywl3VUk9Y0s= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA= -github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.1 h1:9fXQS/0TtQmKXp8SureKouF+idbQvp7cPUxykiohnBs= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.1/go.mod h1:f+OaoSg0VQYPMqB0Jp2D54j1VHzITYcJaCNwV+k00ts= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= -github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= -github.com/ProtonMail/go-crypto v1.1.0-alpha.0-proton h1:P5Wd8eQ6zAzT4HpJI67FDKnTSf3xiJGQFqY1agDJPy4= -github.com/ProtonMail/go-crypto v1.1.0-alpha.0-proton/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/aws/aws-sdk-go-v2 v1.25.0 h1:sv7+1JVJxOu/dD/sz/csHX7jFqmP001TIY7aytBWDSQ= -github.com/aws/aws-sdk-go-v2 v1.25.0/go.mod h1:G104G1Aho5WqF+SR3mDIobTABQzpYV0WxMsKxlMggOA= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.0 h1:2UO6/nT1lCZq1LqM67Oa4tdgP1CvL1sLSxvuD+VrOeE= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.0/go.mod h1:5zGj2eA85ClyedTDK+Whsu+w9yimnVIZvhvBKrDquM8= -github.com/aws/aws-sdk-go-v2/config v1.27.0 h1:J5sdGCAHuWKIXLeXiqr8II/adSvetkx0qdZwdbXXpb0= -github.com/aws/aws-sdk-go-v2/config v1.27.0/go.mod h1:cfh8v69nuSUohNFMbIISP2fhmblGmYEOKs5V53HiHnk= -github.com/aws/aws-sdk-go-v2/credentials v1.17.0 h1:lMW2x6sKBsiAJrpi1doOXqWFyEPoE886DTb1X0wb7So= -github.com/aws/aws-sdk-go-v2/credentials v1.17.0/go.mod h1:uT41FIH8cCIxOdUYIL0PYyHlL1NoneDuDSCwg5VE/5o= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.0 h1:xWCwjjvVz2ojYTP4kBKUuUh9ZrXfcAXpflhOUUeXg1k= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.0/go.mod h1:j3fACuqXg4oMTQOR2yY7m0NmJY0yBK4L4sLsRXq1Ins= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.0 h1:FHVyVIJpOeQZCnYj9EVKTWahb4WDNFEUOKCx/dOUPcM= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.0/go.mod h1:SL/aJzGL0LsQPQ1y2HMNbJGrm/Xh6aVCGq6ki+DLGEw= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.0 h1:NPs/EqVO+ajwOoq56EfcGKa3L3ruWuazkIw1BqxwOPw= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.0/go.mod h1:D+duLy2ylgatV+yTlQ8JTuLfDD0BnFvnQRc+o6tbZ4M= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.0 h1:ks7KGMVUMoDzcxNWUlEdI+/lokMFD136EL6DWmUOV80= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.0/go.mod h1:hL6BWM/d/qz113fVitZjbXR0E+RCTU1+x+1Idyn5NgE= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.0 h1:TkbRExyKSVHELwG9gz2+gql37jjec2R5vus9faTomwE= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.0/go.mod h1:T3/9xMKudHhnj8it5EqIrhvv11tVZqWYkKcot+BFStc= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.0 h1:a33HuFlO0KsveiP90IUJh8Xr/cx9US2PqkSroaLc+o8= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.0/go.mod h1:SxIkWpByiGbhbHYTo9CMTUnx2G4p4ZQMrDPcRRy//1c= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.0 h1:UiSyK6ent6OKpkMJN3+k5HZ4sk4UfchEaaW5wv7SblQ= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.0/go.mod h1:l7kzl8n8DXoRyFz5cIMG70HnPauWa649TUhgw8Rq6lo= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.0 h1:SHN/umDLTmFTmYfI+gkanz6da3vK8Kvj/5wkqnTHbuA= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.0/go.mod h1:l8gPU5RYGOFHJqWEpPMoRTP0VoaWQSkJdKo+hwWnnDA= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.0 h1:l5puwOHr7IxECuPMIuZG7UKOzAnF24v6t4l+Z5Moay4= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.0/go.mod h1:Oov79flWa/n7Ni+lQC3z+VM7PoRM47omRqbJU9B5Y7E= -github.com/aws/aws-sdk-go-v2/service/kms v1.28.1 h1:+KE6+fDNH9gwg/t6DRddIZW7MJVqf3/IdZqeNTFehuA= -github.com/aws/aws-sdk-go-v2/service/kms v1.28.1/go.mod h1:Y/mkxhbaWCswchbBBLRwet6uYKl/026DZXS87c0DmuU= -github.com/aws/aws-sdk-go-v2/service/s3 v1.49.0 h1:VfU15izXQjz4m9y1DkbY79iylIiuPwWtrram4cSpWEI= -github.com/aws/aws-sdk-go-v2/service/s3 v1.49.0/go.mod h1:1o/W6JFUuREj2ExoQ21vHJgO7wakvjhol91M9eknFgs= -github.com/aws/aws-sdk-go-v2/service/sso v1.19.0 h1:u6OkVDxtBPnxPkZ9/63ynEe+8kHbtS5IfaC4PzVxzWM= -github.com/aws/aws-sdk-go-v2/service/sso v1.19.0/go.mod h1:YqbU3RS/pkDVu+v+Nwxvn0i1WB0HkNWEePWbmODEbbs= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.22.0 h1:6DL0qu5+315wbsAEEmzK+P9leRwNbkp+lGjPC+CEvb8= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.22.0/go.mod h1:olUAyg+FaoFaL/zFaeQQONjOZ9HXoxgvI/c7mQTYz7M= -github.com/aws/aws-sdk-go-v2/service/sts v1.27.0 h1:cjTRjh700H36MQ8M0LnDn33W3JmwC77mdxIIyPWCdpM= -github.com/aws/aws-sdk-go-v2/service/sts v1.27.0/go.mod h1:nXfOBMWPokIbOY+Gi7a1psWMSvskUCemZzI+SMB7Akc= -github.com/aws/smithy-go v1.20.0 h1:6+kZsCXZwKxZS9RfISnPc4EXlHoyAkm2hPuM8X2BrrQ= -github.com/aws/smithy-go v1.20.0/go.mod h1:uo5RKksAl4PzhqaAbjd4rLgFoq5koTsQKYuGe7dklGc= +github.com/ProtonMail/go-crypto v1.1.0-alpha.5-proton h1:KVBEgU3CJpmzLChnLiSuEyCuhGhcMt3eOST+7A+ckto= +github.com/ProtonMail/go-crypto v1.1.0-alpha.5-proton/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/aws/aws-sdk-go-v2 v1.30.4 h1:frhcagrVNrzmT95RJImMHgabt99vkXGslubDaDagTk8= +github.com/aws/aws-sdk-go-v2 v1.30.4/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 h1:70PVAiL15/aBMh5LThwgXdSQorVr91L127ttckI9QQU= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4/go.mod h1:/MQxMqci8tlqDH+pjmoLu1i0tbWCUP1hhyMRuFxpQCw= +github.com/aws/aws-sdk-go-v2/config v1.27.30 h1:AQF3/+rOgeJBQP3iI4vojlPib5X6eeOYoa/af7OxAYg= +github.com/aws/aws-sdk-go-v2/config v1.27.30/go.mod h1:yxqvuubha9Vw8stEgNiStO+yZpP68Wm9hLmcm+R/Qk4= +github.com/aws/aws-sdk-go-v2/credentials v1.17.29 h1:CwGsupsXIlAFYuDVHv1nnK0wnxO0wZ/g1L8DSK/xiIw= +github.com/aws/aws-sdk-go-v2/credentials v1.17.29/go.mod h1:BPJ/yXV92ZVq6G8uYvbU0gSl8q94UB63nMT5ctNO38g= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 h1:yjwoSyDZF8Jth+mUk5lSPJCkMC0lMy6FaCD51jm6ayE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12/go.mod h1:fuR57fAgMk7ot3WcNQfb6rSEn+SUffl7ri+aa8uKysI= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.14 h1:dQa4KkoEVgk3oLL9IeoW9qrXijyQ6lWa+DX6Vn32Lhw= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.14/go.mod h1:aRKW0B+zH8J6cz3FFiQ9JbUQc7UroLx6lwfvNqIsPOs= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 h1:TNyt/+X43KJ9IJJMjKfa3bNTiZbUP7DeCxfbTROESwY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16/go.mod h1:2DwJF39FlNAUiX5pAc0UNeiz16lK2t7IaFcm0LFHEgc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 h1:jYfy8UPmd+6kJW5YhY0L1/KftReOGxI/4NtVSTh9O/I= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16/go.mod h1:7ZfEPZxkW42Afq4uQB8H2E2e6ebh6mXTueEpYzjCzcs= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.16 h1:mimdLQkIX1zr8GIPY1ZtALdBQGxcASiBd2MOp8m/dMc= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.16/go.mod h1:YHk6owoSwrIsok+cAH9PENCOGoH5PU2EllX4vLtSrsY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 h1:KypMCbLPPHEmf9DgMGw51jMj77VfGPAN2Kv4cfhlfgI= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4/go.mod h1:Vz1JQXliGcQktFTN/LN6uGppAIRoLBR2bMvIMP0gOjc= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.18 h1:GckUnpm4EJOAio1c8o25a+b3lVfwVzC9gnSBqiiNmZM= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.18/go.mod h1:Br6+bxfG33Dk3ynmkhsW2Z/t9D4+lRqdLDNCKi85w0U= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 h1:tJ5RnkHCiSH0jyd6gROjlJtNwov0eGYNz8s8nFcR0jQ= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18/go.mod h1:++NHzT+nAF7ZPrHPsA+ENvsXkOO8wEu+C6RXltAG4/c= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.16 h1:jg16PhLPUiHIj8zYIW6bqzeQSuHVEiWnGA0Brz5Xv2I= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.16/go.mod h1:Uyk1zE1VVdsHSU7096h/rwnXDzOzYQVl+FNPhPw7ShY= +github.com/aws/aws-sdk-go-v2/service/kms v1.35.5 h1:XUomV7SiclZl1QuXORdGcfFqHxEHET7rmNGtxTfNB+M= +github.com/aws/aws-sdk-go-v2/service/kms v1.35.5/go.mod h1:A5CS0VRmxxj2YKYLCY08l/Zzbd01m6JZn0WzxgT1OCA= +github.com/aws/aws-sdk-go-v2/service/s3 v1.60.1 h1:mx2ucgtv+MWzJesJY9Ig/8AFHgoE5FwLXwUVgW/FGdI= +github.com/aws/aws-sdk-go-v2/service/s3 v1.60.1/go.mod h1:BSPI0EfnYUuNHPS0uqIo5VrRwzie+Fp+YhQOUs16sKI= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 h1:zCsFCKvbj25i7p1u94imVoO447I/sFv8qq+lGJhRN0c= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.5/go.mod h1:ZeDX1SnKsVlejeuz41GiajjZpRSWR7/42q/EyA/QEiM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 h1:SKvPgvdvmiTWoi0GAJ7AsJfOz3ngVkD/ERbs5pUnHNI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5/go.mod h1:20sz31hv/WsPa3HhU3hfrIet2kxM4Pe0r20eBZ20Tac= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.5 h1:OMsEmCyz2i89XwRwPouAJvhj81wINh+4UK+k/0Yo/q8= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.5/go.mod h1:vmSqFK+BVIwVpDAGZB3CoCXHzurt4qBE8lf+I/kRTh0= +github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4= +github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= -github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= +github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY= -github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= -github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= +github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= -github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M= -github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= -github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/cli v27.0.1+incompatible h1:d/OrlblkOTkhJ1IaAGD1bLgUBtFQC/oP0VjkFMIN+B0= +github.com/docker/cli v27.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= +github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/getsops/gopgagent v0.0.0-20170926210634-4d7ea76ff71a h1:qc+7TV35Pq/FlgqECyS5ywq8cSN9j1fwZg6uyZ7G0B0= -github.com/getsops/gopgagent v0.0.0-20170926210634-4d7ea76ff71a/go.mod h1:awFzISqLJoZLm+i9QQ4SgMNHDqljH6jWV0B36V5MrUM= -github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= -github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +github.com/getsops/gopgagent v0.0.0-20240527072608-0c14999532fe h1:QKe/kmAYbndxwu91TcjHERsnMh5SgOB1x/qicvOdUJ8= +github.com/getsops/gopgagent v0.0.0-20240527072608-0c14999532fe/go.mod h1:awFzISqLJoZLm+i9QQ4SgMNHDqljH6jWV0B36V5MrUM= +github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= +github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= +github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc= +github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= -github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= @@ -146,22 +154,20 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= -github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= +github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= +github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -169,8 +175,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go/v2 v2.12.1 h1:9F8GV9r9ztXyAi00gsMQHNoF51xPZm8uj1dpYt2ZETM= -github.com/googleapis/gax-go/v2 v2.12.1/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= +github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= +github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= github.com/goware/prefixer v0.0.0-20160118172347-395022866408 h1:Y9iQJfEqnN3/Nce9cOegemcy/9Ai5k3huT6E80F3zaw= github.com/goware/prefixer v0.0.0-20160118172347-395022866408/go.mod h1:PE1ycukgRPJ7bJ9a1fdfQ9j8i/cEcRAoLZzbxYpNB/s= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -178,13 +184,12 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw= -github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= -github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= +github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 h1:iBt4Ew4XEGLfh6/bPk4rSYmuZJGizr6/x/AEizP0CQc= @@ -195,32 +200,20 @@ github.com/hashicorp/go-sockaddr v1.0.6 h1:RSG8rKU28VTUTvEKghe5gIhIQpv8evvNpnDEy github.com/hashicorp/go-sockaddr v1.0.6/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/vault/api v1.12.0 h1:meCpJSesvzQyao8FCOgk2fGdoADAnbDu2WPJN1lDLJ4= -github.com/hashicorp/vault/api v1.12.0/go.mod h1:si+lJCYO7oGkIoNPAN8j3azBLTn9SjMGS+jFaHd1Cck= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/hashicorp/vault/api v1.14.0 h1:Ah3CFLixD5jmjusOgm8grfN9M0d+Y8fVR2SW0K6pJLU= +github.com/hashicorp/vault/api v1.14.0/go.mod h1:pV9YLxBGSz+cItFDd8Ii4G17waWOQ32zVjMWHe/cOqk= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -230,84 +223,81 @@ github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQ github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= -github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= -github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= -github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/runc v1.1.13 h1:98S2srgG9vw0zWcDpFMn5TRrh8kLxa/5OFUstuUhmRs= +github.com/opencontainers/runc v1.1.13/go.mod h1:R016aXacfp/gwQBYw2FDGa9m+n6atbLWrYY8hNMT/sA= +github.com/ory/dockertest/v3 v3.11.0 h1:OiHcxKAvSDUwsEVh2BjxQQc/5EHz9n0va9awCtNGuyA= +github.com/ory/dockertest/v3 v3.11.0/go.mod h1:VIPxS1gwT9NpPOrfD3rACs8Y9Z7yhzO4SB194iUDnUI= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= -github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/urfave/cli v1.22.15 h1:nuqt+pdC/KqswQKhETJjo7pvn/k4xMUxgW6liI7XpnM= +github.com/urfave/cli v1.22.15/go.mod h1:wSan1hmo5zeyLGBjRJbzRTNk8gwoYa2B9n4q9dmRIc0= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 h1:P+/g8GpuJGYbOp2tAdKrIPUX9JO02q8Q0YNlHolpibA= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0/go.mod h1:tIKj3DbO8N9Y2xo52og3irLsPI4GW02DSMtrVgNMgxg= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 h1:doUP+ExOpH3spVTLS0FcWGLnQrPct/hD/bCPbDRUEAU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0/go.mod h1:rdENBZMT2OE6Ne/KLwpiXudnAsbdrdBaqBvTN8M8BgA= -go.opentelemetry.io/otel v1.23.1 h1:Za4UzOqJYS+MUczKI320AtqZHZb7EqxO00jAHE0jmQY= -go.opentelemetry.io/otel v1.23.1/go.mod h1:Td0134eafDLcTS4y+zQ26GE8u3dEuRBiBCTUIRHaikA= -go.opentelemetry.io/otel/metric v1.23.1 h1:PQJmqJ9u2QaJLBOELl1cxIdPcpbwzbkjfEyelTl2rlo= -go.opentelemetry.io/otel/metric v1.23.1/go.mod h1:mpG2QPlAfnK8yNhNJAxDZruU9Y1/HubbC+KyH8FaCWI= -go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= -go.opentelemetry.io/otel/trace v1.23.1 h1:4LrmmEd8AU2rFvU1zegmvqW7+kWarxtNOPyeL6HmYY8= -go.opentelemetry.io/otel/trace v1.23.1/go.mod h1:4IpnpJFwr1mo/6HL8XIPJaE9y0+u1KcVmuW7dwFSVrI= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 h1:vS1Ao/R55RNV4O7TA2Qopok8yN+X0LIP6RVWLFkprck= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0/go.mod h1:BMsdeOxN04K0L5FNUBfjFdvwWGNe/rkmSwH4Aelu/X0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -317,92 +307,69 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= -golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -google.golang.org/api v0.165.0 h1:zd5d4JIIIaYYsfVy1HzoXYZ9rWCSBxxAglbczzo7Bgc= -google.golang.org/api v0.165.0/go.mod h1:2OatzO7ZDQsoS7IFf3rvsE17/TldiU3F/zxFHeqUB5o= +google.golang.org/api v0.193.0 h1:eOGDoJFsLU+HpCBaDJex2fWiYujAw9KbXgpOAMePoUs= +google.golang.org/api v0.193.0/go.mod h1:Po3YMV1XZx+mTku3cfJrlIYR03wiGrCOsdpC67hjZvw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= -google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= -google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9 h1:4++qSzdWBUy9/2x8L5KZgwZw+mjJZ2yDSCGMVM0YzRs= -google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:PVreiBMirk8ypES6aw9d4p6iiBNSIfZEBqr3UGoAi2E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 h1:hZB7eLIaYlW9qXRfCq/qDaPdbeY3757uARz5Vvfv+cY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk= +google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142 h1:oLiyxGgE+rt22duwci1+TG7bg2/L1LQsXwfjPlmuJA0= +google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142/go.mod h1:G11eXq53iI5Q+kyNOmCvnzBaxEA2Q/Ik5Tj7nqBE8j4= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY= -google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -412,23 +379,19 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/hcvault/keysource.go b/hcvault/keysource.go index a025413fe..c120d9c99 100644 --- a/hcvault/keysource.go +++ b/hcvault/keysource.go @@ -317,7 +317,7 @@ func vaultClient(address, token string) (*api.Client, error) { return client, nil } -// userVaultsToken returns the token from `$HOME/.vault-token` if the file +// userVaultToken returns the token from `$HOME/.vault-token` if the file // exists. It returns an error if the file exists but cannot be read from. // If the file does not exist, it returns an empty string. func userVaultToken() (string, error) { diff --git a/keyservice/client.go b/keyservice/client.go index 6ba02e64f..f0a29fd06 100644 --- a/keyservice/client.go +++ b/keyservice/client.go @@ -13,7 +13,7 @@ type LocalClient struct { // NewLocalClient creates a new local client func NewLocalClient() LocalClient { - return LocalClient{&Server{}} + return LocalClient{Server{}} } // NewCustomLocalClient creates a new local client with a non-default backing diff --git a/keyservice/keyservice.go b/keyservice/keyservice.go index 2886a5d6a..321af7942 100644 --- a/keyservice/keyservice.go +++ b/keyservice/keyservice.go @@ -17,7 +17,7 @@ import ( ) // KeyFromMasterKey converts a SOPS internal MasterKey to an RPC Key that can be serialized with Protocol Buffers -func KeyFromMasterKey(mk keys.MasterKey, credentials map[string]string) Key { +func KeyFromMasterKey(mk keys.MasterKey) Key { switch mk := mk.(type) { case *pgp.MasterKey: return Key{ @@ -31,8 +31,7 @@ func KeyFromMasterKey(mk keys.MasterKey, credentials map[string]string) Key { return Key{ KeyType: &Key_GcpKmsKey{ GcpKmsKey: &GcpKmsKey{ - ResourceId: mk.ResourceID, - AccessToken: credentials["gcp-kms"], + ResourceId: mk.ResourceID, }, }, } diff --git a/keyservice/keyservice.pb.go b/keyservice/keyservice.pb.go index 4ef24ff9e..ead3ccfd1 100644 --- a/keyservice/keyservice.pb.go +++ b/keyservice/keyservice.pb.go @@ -1,12 +1,17 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.32.0 -// protoc v4.25.3 +// protoc-gen-go v1.23.0 +// protoc v3.13.0 // source: keyservice/keyservice.proto package keyservice import ( + context "context" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -20,13 +25,16 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + type Key struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to KeyType: - // // *Key_KmsKey // *Key_PgpKey // *Key_GcpKmsKey @@ -280,8 +288,7 @@ type GcpKmsKey struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ResourceId string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` - AccessToken string `protobuf:"bytes,2,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` + ResourceId string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` } func (x *GcpKmsKey) Reset() { @@ -323,13 +330,6 @@ func (x *GcpKmsKey) GetResourceId() string { return "" } -func (x *GcpKmsKey) GetAccessToken() string { - if x != nil { - return x.AccessToken - } - return "" -} - type VaultKey struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -744,51 +744,47 @@ var file_keyservice_keyservice_proto_rawDesc = []byte{ 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x4f, 0x0a, 0x09, 0x47, 0x63, 0x70, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12, + 0x38, 0x01, 0x22, 0x2c, 0x0a, 0x09, 0x47, 0x63, 0x70, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, - 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x22, 0x6b, 0x0a, 0x08, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x12, - 0x23, 0x0a, 0x0d, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x70, - 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x67, 0x69, 0x6e, - 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4e, 0x61, 0x6d, 0x65, - 0x22, 0x5d, 0x0a, 0x10, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x75, 0x6c, - 0x74, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x75, 0x72, - 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x55, 0x72, - 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, - 0x26, 0x0a, 0x06, 0x41, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, - 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, - 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x22, 0x46, 0x0a, 0x0e, 0x45, 0x6e, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x04, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, - 0x31, 0x0a, 0x0f, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, - 0x78, 0x74, 0x22, 0x48, 0x0a, 0x0e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x04, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, - 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, 0x2f, 0x0a, 0x0f, - 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x1c, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x32, 0x6c, 0x0a, - 0x0a, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x45, - 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x12, 0x0f, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x07, 0x44, - 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x12, 0x0f, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x0d, 0x5a, 0x0b, 0x6b, - 0x65, 0x79, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x22, 0x6b, 0x0a, 0x08, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, + 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x50, 0x61, + 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x0a, + 0x10, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, + 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x26, 0x0a, 0x06, + 0x41, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, + 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, + 0x69, 0x65, 0x6e, 0x74, 0x22, 0x46, 0x0a, 0x0e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x04, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1c, + 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x31, 0x0a, 0x0f, + 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, + 0x48, 0x0a, 0x0e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x16, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x04, + 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, + 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, + 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, 0x2f, 0x0a, 0x0f, 0x44, 0x65, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x32, 0x6c, 0x0a, 0x0a, 0x4b, 0x65, + 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x12, 0x0f, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x07, 0x44, 0x65, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x12, 0x0f, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1005,3 +1001,119 @@ func file_keyservice_keyservice_proto_init() { file_keyservice_keyservice_proto_goTypes = nil file_keyservice_keyservice_proto_depIdxs = nil } + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// KeyServiceClient is the client API for KeyService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type KeyServiceClient interface { + Encrypt(ctx context.Context, in *EncryptRequest, opts ...grpc.CallOption) (*EncryptResponse, error) + Decrypt(ctx context.Context, in *DecryptRequest, opts ...grpc.CallOption) (*DecryptResponse, error) +} + +type keyServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewKeyServiceClient(cc grpc.ClientConnInterface) KeyServiceClient { + return &keyServiceClient{cc} +} + +func (c *keyServiceClient) Encrypt(ctx context.Context, in *EncryptRequest, opts ...grpc.CallOption) (*EncryptResponse, error) { + out := new(EncryptResponse) + err := c.cc.Invoke(ctx, "/KeyService/Encrypt", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *keyServiceClient) Decrypt(ctx context.Context, in *DecryptRequest, opts ...grpc.CallOption) (*DecryptResponse, error) { + out := new(DecryptResponse) + err := c.cc.Invoke(ctx, "/KeyService/Decrypt", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// KeyServiceServer is the server API for KeyService service. +type KeyServiceServer interface { + Encrypt(context.Context, *EncryptRequest) (*EncryptResponse, error) + Decrypt(context.Context, *DecryptRequest) (*DecryptResponse, error) +} + +// UnimplementedKeyServiceServer can be embedded to have forward compatible implementations. +type UnimplementedKeyServiceServer struct { +} + +func (*UnimplementedKeyServiceServer) Encrypt(context.Context, *EncryptRequest) (*EncryptResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Encrypt not implemented") +} +func (*UnimplementedKeyServiceServer) Decrypt(context.Context, *DecryptRequest) (*DecryptResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Decrypt not implemented") +} + +func RegisterKeyServiceServer(s *grpc.Server, srv KeyServiceServer) { + s.RegisterService(&_KeyService_serviceDesc, srv) +} + +func _KeyService_Encrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EncryptRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(KeyServiceServer).Encrypt(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/KeyService/Encrypt", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(KeyServiceServer).Encrypt(ctx, req.(*EncryptRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _KeyService_Decrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DecryptRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(KeyServiceServer).Decrypt(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/KeyService/Decrypt", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(KeyServiceServer).Decrypt(ctx, req.(*DecryptRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _KeyService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "KeyService", + HandlerType: (*KeyServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Encrypt", + Handler: _KeyService_Encrypt_Handler, + }, + { + MethodName: "Decrypt", + Handler: _KeyService_Decrypt_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "keyservice/keyservice.proto", +} diff --git a/keyservice/keyservice.proto b/keyservice/keyservice.proto index 0ad7dd580..1d91a5709 100644 --- a/keyservice/keyservice.proto +++ b/keyservice/keyservice.proto @@ -1,7 +1,5 @@ syntax = "proto3"; -option go_package="keyservice/"; - message Key { oneof key_type { KmsKey kms_key = 1; @@ -26,7 +24,6 @@ message KmsKey { message GcpKmsKey { string resource_id = 1; - string access_token = 2; } message VaultKey { diff --git a/keyservice/keyservice_grpc.pb.go b/keyservice/keyservice_grpc.pb.go deleted file mode 100644 index dcf8bd0cf..000000000 --- a/keyservice/keyservice_grpc.pb.go +++ /dev/null @@ -1,146 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.3.0 -// - protoc v4.25.3 -// source: keyservice/keyservice.proto - -package keyservice - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -const ( - KeyService_Encrypt_FullMethodName = "/KeyService/Encrypt" - KeyService_Decrypt_FullMethodName = "/KeyService/Decrypt" -) - -// KeyServiceClient is the client API for KeyService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type KeyServiceClient interface { - Encrypt(ctx context.Context, in *EncryptRequest, opts ...grpc.CallOption) (*EncryptResponse, error) - Decrypt(ctx context.Context, in *DecryptRequest, opts ...grpc.CallOption) (*DecryptResponse, error) -} - -type keyServiceClient struct { - cc grpc.ClientConnInterface -} - -func NewKeyServiceClient(cc grpc.ClientConnInterface) KeyServiceClient { - return &keyServiceClient{cc} -} - -func (c *keyServiceClient) Encrypt(ctx context.Context, in *EncryptRequest, opts ...grpc.CallOption) (*EncryptResponse, error) { - out := new(EncryptResponse) - err := c.cc.Invoke(ctx, KeyService_Encrypt_FullMethodName, in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *keyServiceClient) Decrypt(ctx context.Context, in *DecryptRequest, opts ...grpc.CallOption) (*DecryptResponse, error) { - out := new(DecryptResponse) - err := c.cc.Invoke(ctx, KeyService_Decrypt_FullMethodName, in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// KeyServiceServer is the server API for KeyService service. -// All implementations must embed UnimplementedKeyServiceServer -// for forward compatibility -type KeyServiceServer interface { - Encrypt(context.Context, *EncryptRequest) (*EncryptResponse, error) - Decrypt(context.Context, *DecryptRequest) (*DecryptResponse, error) - mustEmbedUnimplementedKeyServiceServer() -} - -// UnimplementedKeyServiceServer must be embedded to have forward compatible implementations. -type UnimplementedKeyServiceServer struct { -} - -func (UnimplementedKeyServiceServer) Encrypt(context.Context, *EncryptRequest) (*EncryptResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Encrypt not implemented") -} -func (UnimplementedKeyServiceServer) Decrypt(context.Context, *DecryptRequest) (*DecryptResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Decrypt not implemented") -} -func (UnimplementedKeyServiceServer) mustEmbedUnimplementedKeyServiceServer() {} - -// UnsafeKeyServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to KeyServiceServer will -// result in compilation errors. -type UnsafeKeyServiceServer interface { - mustEmbedUnimplementedKeyServiceServer() -} - -func RegisterKeyServiceServer(s grpc.ServiceRegistrar, srv KeyServiceServer) { - s.RegisterService(&KeyService_ServiceDesc, srv) -} - -func _KeyService_Encrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(EncryptRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(KeyServiceServer).Encrypt(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: KeyService_Encrypt_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(KeyServiceServer).Encrypt(ctx, req.(*EncryptRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _KeyService_Decrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DecryptRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(KeyServiceServer).Decrypt(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: KeyService_Decrypt_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(KeyServiceServer).Decrypt(ctx, req.(*DecryptRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// KeyService_ServiceDesc is the grpc.ServiceDesc for KeyService service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var KeyService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "KeyService", - HandlerType: (*KeyServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Encrypt", - Handler: _KeyService_Encrypt_Handler, - }, - { - MethodName: "Decrypt", - Handler: _KeyService_Decrypt_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "keyservice/keyservice.proto", -} diff --git a/keyservice/server.go b/keyservice/server.go index a20959062..9f2b486a6 100644 --- a/keyservice/server.go +++ b/keyservice/server.go @@ -20,8 +20,6 @@ type Server struct { Prompt bool } -func (ks *Server) mustEmbedUnimplementedKeyServiceServer() {} - func (ks *Server) encryptWithPgp(key *PgpKey, plaintext []byte) ([]byte, error) { pgpKey := pgp.NewMasterKeyFromFingerprint(key.Fingerprint) err := pgpKey.Encrypt(plaintext) @@ -42,8 +40,7 @@ func (ks *Server) encryptWithKms(key *KmsKey, plaintext []byte) ([]byte, error) func (ks *Server) encryptWithGcpKms(key *GcpKmsKey, plaintext []byte) ([]byte, error) { gcpKmsKey := gcpkms.MasterKey{ - ResourceID: key.ResourceId, - AccessToken: gcpkms.AccessToken(key.AccessToken), + ResourceID: key.ResourceId, } err := gcpKmsKey.Encrypt(plaintext) if err != nil { @@ -106,8 +103,7 @@ func (ks *Server) decryptWithKms(key *KmsKey, ciphertext []byte) ([]byte, error) func (ks *Server) decryptWithGcpKms(key *GcpKmsKey, ciphertext []byte) ([]byte, error) { gcpKmsKey := gcpkms.MasterKey{ - ResourceID: key.ResourceId, - AccessToken: gcpkms.AccessToken(key.AccessToken), + ResourceID: key.ResourceId, } gcpKmsKey.EncryptedKey = string(ciphertext) plaintext, err := gcpKmsKey.Decrypt() diff --git a/sops.go b/sops.go index ccabaffb1..4b97292a1 100644 --- a/sops.go +++ b/sops.go @@ -11,7 +11,7 @@ A Sops document is a Tree composed of a data branch with arbitrary key/value pai and a metadata branch with encryption and integrity information. In JSON and YAML formats, the structure of the cleartext tree is preserved, keys are -stored in cleartext and only values are encrypted. Keeping the values in cleartext +stored in cleartext and only values are encrypted. Keeping the keys in cleartext provides better readability when storing Sops documents in version controls, and allows for merging competing changes on documents. This is a major difference between Sops and other encryption tools that store documents as encrypted blobs. @@ -42,6 +42,7 @@ import ( "fmt" "reflect" "regexp" + "slices" "sort" "strconv" "strings" @@ -76,6 +77,15 @@ const MacMismatch = sopsError("MAC mismatch") // MetadataNotFound occurs when the input file is malformed and doesn't have sops metadata in it const MetadataNotFound = sopsError("sops metadata not found") +type SopsKeyNotFound struct { + Key interface{} + Msg string +} + +func (e *SopsKeyNotFound) Error() string { + return fmt.Sprintf(e.Msg, e.Key) +} + // MACOnlyEncryptedInitialization is a constant and known sequence of 32 bytes used to initialize // MAC which is computed only over values which end up encrypted. That assures that a MAC with the // setting enabled is always different from a MAC with this setting disabled. @@ -189,6 +199,53 @@ func (branch TreeBranch) Set(path []interface{}, value interface{}) TreeBranch { return set(branch, path, value).(TreeBranch) } +func unset(branch interface{}, path []interface{}) (interface{}, error) { + switch branch := branch.(type) { + case TreeBranch: + for i, item := range branch { + if item.Key == path[0] { + if len(path) == 1 { + branch = slices.Delete(branch, i, i+1) + } else { + v, err := unset(item.Value, path[1:]) + if err != nil { + return nil, err + } + branch[i].Value = v + } + return branch, nil + } + } + return nil, &SopsKeyNotFound{Msg: "Key not found: %s", Key: path[0]} + case []interface{}: + position := path[0].(int) + if position >= len(branch) { + return nil, &SopsKeyNotFound{Msg: "Index %d out of bounds", Key: path[0]} + } + if len(path) == 1 { + branch = slices.Delete(branch, position, position+1) + } else { + v, err := unset(branch[position], path[1:]) + if err != nil { + return nil, err + } + branch[position] = v + } + return branch, nil + default: + return nil, fmt.Errorf("Unsupported type: %T for item '%s'", branch, path[0]) + } +} + +// Unset removes a value on a given tree from the specified path +func (branch TreeBranch) Unset(path []interface{}) (TreeBranch, error) { + v, err := unset(branch, path) + if err != nil { + return nil, err + } + return v.(TreeBranch), nil +} + // Tree is the data structure used by sops to represent documents internally type Tree struct { Metadata Metadata @@ -228,24 +285,24 @@ func (branch TreeBranch) Truncate(path []interface{}) (interface{}, error) { return current, nil } -func (branch TreeBranch) walkValue(in interface{}, path []string, onLeaves func(in interface{}, path []string) (interface{}, error)) (interface{}, error) { +func (branch TreeBranch) walkValue(in interface{}, path []string, commentsStack [][]string, onLeaves func(in interface{}, path []string, commentsStack [][]string) (interface{}, error)) (interface{}, error) { switch in := in.(type) { case string: - return onLeaves(in, path) + return onLeaves(in, path, commentsStack) case []byte: - return onLeaves(string(in), path) + return onLeaves(string(in), path, commentsStack) case int: - return onLeaves(in, path) + return onLeaves(in, path, commentsStack) case bool: - return onLeaves(in, path) + return onLeaves(in, path, commentsStack) case float64: - return onLeaves(in, path) + return onLeaves(in, path, commentsStack) case Comment: - return onLeaves(in, path) + return onLeaves(in, path, commentsStack) case TreeBranch: - return branch.walkBranch(in, path, onLeaves) + return branch.walkBranch(in, path, commentsStack, onLeaves) case []interface{}: - return branch.walkSlice(in, path, onLeaves) + return branch.walkSlice(in, path, commentsStack, onLeaves) case nil: // the value returned remains the same since it doesn't make // sense to encrypt or decrypt a nil value @@ -255,21 +312,38 @@ func (branch TreeBranch) walkValue(in interface{}, path []string, onLeaves func( } } -func (branch TreeBranch) walkSlice(in []interface{}, path []string, onLeaves func(in interface{}, path []string) (interface{}, error)) ([]interface{}, error) { +func (branch TreeBranch) walkSlice(in []interface{}, path []string, commentsStack [][]string, onLeaves func(in interface{}, path []string, commentsStack [][]string) (interface{}, error)) ([]interface{}, error) { + // Because append returns a new slice, the original stack is not changed. + commentsStack = append(commentsStack, []string{}) for i, v := range in { - newV, err := branch.walkValue(v, path, onLeaves) + c, vIsComment := v.(Comment) + if vIsComment { + // If v is a comment, we add it to the slice of active comments. + // This allows us to also encrypt comments themselves by enabling encryption in a prior comment. + commentsStack[len(commentsStack)-1] = append(commentsStack[len(commentsStack)-1], c.Value) + } + newV, err := branch.walkValue(v, path, commentsStack, onLeaves) if err != nil { return nil, err } in[i] = newV + if !vIsComment { + // If v is not a comment, we clear the slice of active comments. + commentsStack[len(commentsStack)-1] = []string{} + } } return in, nil } -func (branch TreeBranch) walkBranch(in TreeBranch, path []string, onLeaves func(in interface{}, path []string) (interface{}, error)) (TreeBranch, error) { +func (branch TreeBranch) walkBranch(in TreeBranch, path []string, commentsStack [][]string, onLeaves func(in interface{}, path []string, commentsStack [][]string) (interface{}, error)) (TreeBranch, error) { + // Because append returns a new slice, the original stack is not changed. + commentsStack = append(commentsStack, []string{}) for i, item := range in { - if _, ok := item.Key.(Comment); ok { - enc, err := branch.walkValue(item.Key, path, onLeaves) + if c, ok := item.Key.(Comment); ok { + // If key is a comment, we add it to the slice of active comments. + // This allows us to also encrypt comments themselves by enabling encryption in a prior comment. + commentsStack[len(commentsStack)-1] = append(commentsStack[len(commentsStack)-1], c.Value) + enc, err := branch.walkValue(item.Key, path, commentsStack, onLeaves) if err != nil { return nil, err } @@ -283,26 +357,113 @@ func (branch TreeBranch) walkBranch(in TreeBranch, path []string, onLeaves func( return nil, fmt.Errorf("walkValue of Comment should be either Comment or string, was %T", enc) } } + c, valueIsComment := item.Value.(Comment) + if valueIsComment { + // If value is a comment, we add it to the slice of active comments. + // This allows us to also encrypt comments themselves by enabling encryption in a prior comment. + commentsStack[len(commentsStack)-1] = append(commentsStack[len(commentsStack)-1], c.Value) + } key, ok := item.Key.(string) if !ok { return nil, fmt.Errorf("Tree contains a non-string key (type %T): %s. Only string keys are"+ "supported", item.Key, item.Key) } - newV, err := branch.walkValue(item.Value, append(path, key), onLeaves) + newV, err := branch.walkValue(item.Value, append(path, key), commentsStack, onLeaves) if err != nil { return nil, err } in[i].Value = newV + if !valueIsComment { + // If value is not a comment, we clear the slice of active comments. + commentsStack[len(commentsStack)-1] = []string{} + } } return in, nil } +func (tree Tree) shouldBeEncrypted(path []string, commentsStack [][]string, isComment bool) bool { + encrypted := true + if tree.Metadata.UnencryptedSuffix != "" { + for _, v := range path { + if strings.HasSuffix(v, tree.Metadata.UnencryptedSuffix) { + encrypted = false + break + } + } + } + if tree.Metadata.EncryptedSuffix != "" { + encrypted = false + for _, v := range path { + if strings.HasSuffix(v, tree.Metadata.EncryptedSuffix) { + encrypted = true + break + } + } + } + if tree.Metadata.UnencryptedRegex != "" { + for _, p := range path { + matched, _ := regexp.Match(tree.Metadata.UnencryptedRegex, []byte(p)) + if matched { + encrypted = false + break + } + } + } + if tree.Metadata.EncryptedRegex != "" { + encrypted = false + for _, p := range path { + matched, _ := regexp.Match(tree.Metadata.EncryptedRegex, []byte(p)) + if matched { + encrypted = true + break + } + } + } + if tree.Metadata.UnencryptedCommentRegex != "" { + unencryptedComments: + for _, cs := range commentsStack { + for _, c := range cs { + matched, _ := regexp.Match(tree.Metadata.UnencryptedCommentRegex, []byte(c)) + if matched { + encrypted = false + break unencryptedComments + } + } + } + } + if tree.Metadata.EncryptedCommentRegex != "" { + lenCommentsStack := len(commentsStack) + lenLastCommentsStack := len(commentsStack[lenCommentsStack-1]) + encrypted = false + encryptedComments: + for i, cs := range commentsStack { + for j, c := range cs { + // A special case. We do not encrypt the comment line itself which matches the regex. + // So we skip the last line of the last set of comments. Only if the matches any previous + // line, we encrypt this comment. Otherwise we do not. + if isComment && i == lenCommentsStack-1 && j == lenLastCommentsStack-1 { + continue + } + matched, _ := regexp.Match(tree.Metadata.EncryptedCommentRegex, []byte(c)) + if matched { + encrypted = true + break encryptedComments + } + } + } + } + return encrypted +} + // Encrypt walks over the tree and encrypts all values with the provided cipher, // except those whose key ends with the UnencryptedSuffix specified on the // Metadata struct, those not ending with EncryptedSuffix, if EncryptedSuffix // is provided (by default it is not), those not matching EncryptedRegex, -// if EncryptedRegex is provided (by default it is not) or those matching -// UnencryptedRegex, if UnencryptedRegex is provided (by default it is not). +// if EncryptedRegex is provided (by default it is not), those matching UnencryptedRegex, +// if UnencryptedRegex is provided (by default it is not), those with their comment +// not matching EncryptedCommentRegex, if EncryptedCommentRegex is provided (by default +// it is not), or those with their comment matching UnencryptedCommentRegex, if +// UnencryptedCommentRegex is provided (by default it is not). // If encryption is successful, it returns the MAC for the encrypted tree // (all values if MACOnlyEncrypted is false, or only over values which end // up encrypted if MACOnlyEncrypted is true). @@ -317,47 +478,12 @@ func (tree Tree) Encrypt(key []byte, cipher Cipher) (string, error) { hash.Write(MACOnlyEncryptedInitialization) } walk := func(branch TreeBranch) error { - _, err := branch.walkBranch(branch, make([]string, 0), func(in interface{}, path []string) (interface{}, error) { - encrypted := true - if tree.Metadata.UnencryptedSuffix != "" { - for _, v := range path { - if strings.HasSuffix(v, tree.Metadata.UnencryptedSuffix) { - encrypted = false - break - } - } - } - if tree.Metadata.EncryptedSuffix != "" { - encrypted = false - for _, v := range path { - if strings.HasSuffix(v, tree.Metadata.EncryptedSuffix) { - encrypted = true - break - } - } - } - if tree.Metadata.UnencryptedRegex != "" { - for _, p := range path { - matched, _ := regexp.Match(tree.Metadata.UnencryptedRegex, []byte(p)) - if matched { - encrypted = false - break - } - } - } - if tree.Metadata.EncryptedRegex != "" { - encrypted = false - for _, p := range path { - matched, _ := regexp.Match(tree.Metadata.EncryptedRegex, []byte(p)) - if matched { - encrypted = true - break - } - } - } + _, err := branch.walkBranch(branch, make([]string, 0), make([][]string, 0), func(in interface{}, path []string, commentsStack [][]string) (interface{}, error) { + _, ok := in.(Comment) + encrypted := tree.shouldBeEncrypted(path, commentsStack, ok) if !tree.Metadata.MACOnlyEncrypted || encrypted { // Only add to MAC if not a comment - if _, ok := in.(Comment); !ok { + if !ok { bytes, err := ToBytes(in) if err != nil { return nil, fmt.Errorf("Could not convert %s to bytes: %s", in, err) @@ -372,6 +498,14 @@ func (tree Tree) Encrypt(key []byte, cipher Cipher) (string, error) { if err != nil { return nil, fmt.Errorf("Could not encrypt value: %s", err) } + if ok && tree.Metadata.UnencryptedCommentRegex != "" { + // If an encrypted comment matches tree.Metadata.UnencryptedCommentRegex, decryption will fail + // as the MAC does not match, and the commented value will not be decrypted. + matched, _ := regexp.Match(tree.Metadata.UnencryptedCommentRegex, []byte(in.(string))) + if matched { + return nil, fmt.Errorf("Encrypted comment %q matches UnencryptedCommentRegex! Make sure that UnencryptedCommentRegex cannot match an encrypted comment.", in) + } + } } return in, nil }) @@ -407,49 +541,14 @@ func (tree Tree) Decrypt(key []byte, cipher Cipher) (string, error) { hash.Write(MACOnlyEncryptedInitialization) } walk := func(branch TreeBranch) error { - _, err := branch.walkBranch(branch, make([]string, 0), func(in interface{}, path []string) (interface{}, error) { - encrypted := true - if tree.Metadata.UnencryptedSuffix != "" { - for _, p := range path { - if strings.HasSuffix(p, tree.Metadata.UnencryptedSuffix) { - encrypted = false - break - } - } - } - if tree.Metadata.EncryptedSuffix != "" { - encrypted = false - for _, p := range path { - if strings.HasSuffix(p, tree.Metadata.EncryptedSuffix) { - encrypted = true - break - } - } - } - if tree.Metadata.UnencryptedRegex != "" { - for _, p := range path { - matched, _ := regexp.Match(tree.Metadata.UnencryptedRegex, []byte(p)) - if matched { - encrypted = false - break - } - } - } - if tree.Metadata.EncryptedRegex != "" { - encrypted = false - for _, p := range path { - matched, _ := regexp.Match(tree.Metadata.EncryptedRegex, []byte(p)) - if matched { - encrypted = true - break - } - } - } + _, err := branch.walkBranch(branch, make([]string, 0), make([][]string, 0), func(in interface{}, path []string, commentsStack [][]string) (interface{}, error) { + c, ok := in.(Comment) + encrypted := tree.shouldBeEncrypted(path, commentsStack, ok) var v interface{} if encrypted { var err error pathString := strings.Join(path, ":") + ":" - if c, ok := in.(Comment); ok { + if ok { v, err = cipher.Decrypt(c.Value, key, pathString) if err != nil { // Assume the comment was not encrypted in the first place @@ -493,23 +592,23 @@ func (tree Tree) Decrypt(key []byte, cipher Cipher) (string, error) { } // GenerateDataKey generates a new random data key and encrypts it with all MasterKeys. -func (tree Tree) GenerateDataKey(encryptionCredentials map[string]string) ([]byte, []error) { +func (tree Tree) GenerateDataKey() ([]byte, []error) { newKey := make([]byte, 32) _, err := rand.Read(newKey) if err != nil { return nil, []error{fmt.Errorf("Could not generate random key: %s", err)} } - return newKey, tree.Metadata.UpdateMasterKeys(newKey, encryptionCredentials) + return newKey, tree.Metadata.UpdateMasterKeys(newKey) } // GenerateDataKeyWithKeyServices generates a new random data key and encrypts it with all MasterKeys. -func (tree *Tree) GenerateDataKeyWithKeyServices(svcs []keyservice.KeyServiceClient, encryptionCredentials map[string]string) ([]byte, []error) { +func (tree *Tree) GenerateDataKeyWithKeyServices(svcs []keyservice.KeyServiceClient) ([]byte, []error) { newKey := make([]byte, 32) _, err := rand.Read(newKey) if err != nil { return nil, []error{fmt.Errorf("Could not generate random key: %s", err)} } - return newKey, tree.Metadata.UpdateMasterKeysWithKeyServices(newKey, svcs, encryptionCredentials) + return newKey, tree.Metadata.UpdateMasterKeysWithKeyServices(newKey, svcs) } // Metadata holds information about a file encrypted by sops @@ -519,6 +618,8 @@ type Metadata struct { EncryptedSuffix string UnencryptedRegex string EncryptedRegex string + UnencryptedCommentRegex string + EncryptedCommentRegex string MessageAuthenticationCode string MACOnlyEncrypted bool Version string @@ -567,9 +668,9 @@ type ValueEmitter interface { EmitValue(interface{}) ([]byte, error) } -// CheckEncryped is the interface for testing whether a branch contains sops +// CheckEncrypted is the interface for testing whether a branch contains sops // metadata. This is used to check whether a file is already encrypted or not. -type CheckEncryped interface { +type CheckEncrypted interface { HasSopsTopLevelKey(TreeBranch) bool } @@ -580,7 +681,7 @@ type Store interface { EncryptedFileEmitter PlainFileEmitter ValueEmitter - CheckEncryped + CheckEncrypted } // MasterKeyCount returns the number of master keys available @@ -593,7 +694,7 @@ func (m *Metadata) MasterKeyCount() int { } // UpdateMasterKeysWithKeyServices encrypts the data key with all master keys using the provided key services -func (m *Metadata) UpdateMasterKeysWithKeyServices(dataKey []byte, svcs []keyservice.KeyServiceClient, encryptionCredentials map[string]string) (errs []error) { +func (m *Metadata) UpdateMasterKeysWithKeyServices(dataKey []byte, svcs []keyservice.KeyServiceClient) (errs []error) { if len(svcs) == 0 { return []error{ fmt.Errorf("no key services provided, cannot update master keys"), @@ -626,7 +727,7 @@ func (m *Metadata) UpdateMasterKeysWithKeyServices(dataKey []byte, svcs []keyser for i, group := range m.KeyGroups { part := parts[i] for _, key := range group { - svcKey := keyservice.KeyFromMasterKey(key, encryptionCredentials) + svcKey := keyservice.KeyFromMasterKey(key) var keyErrs []error encrypted := false for _, svc := range svcs { @@ -653,15 +754,15 @@ func (m *Metadata) UpdateMasterKeysWithKeyServices(dataKey []byte, svcs []keyser } // UpdateMasterKeys encrypts the data key with all master keys -func (m *Metadata) UpdateMasterKeys(dataKey []byte, encryptionCredentials map[string]string) (errs []error) { +func (m *Metadata) UpdateMasterKeys(dataKey []byte) (errs []error) { return m.UpdateMasterKeysWithKeyServices(dataKey, []keyservice.KeyServiceClient{ keyservice.NewLocalClient(), - }, encryptionCredentials) + }) } // GetDataKeyWithKeyServices retrieves the data key, asking KeyServices to decrypt it with each // MasterKey in the Metadata's KeySources until one of them succeeds. -func (m Metadata) GetDataKeyWithKeyServices(svcs []keyservice.KeyServiceClient, decryptionOrder []string, decryptionCredentials map[string]string) ([]byte, error) { +func (m Metadata) GetDataKeyWithKeyServices(svcs []keyservice.KeyServiceClient, decryptionOrder []string) ([]byte, error) { if m.DataKey != nil { return m.DataKey, nil } @@ -671,7 +772,7 @@ func (m Metadata) GetDataKeyWithKeyServices(svcs []keyservice.KeyServiceClient, } var parts [][]byte for i, group := range m.KeyGroups { - part, err := decryptKeyGroup(group, svcs, decryptionOrder, decryptionCredentials) + part, err := decryptKeyGroup(group, svcs, decryptionOrder) if err == nil { parts = append(parts, part) } @@ -701,14 +802,14 @@ func (m Metadata) GetDataKeyWithKeyServices(svcs []keyservice.KeyServiceClient, // decryptKeyGroup tries to decrypt the contents of the provided KeyGroup with // any of the MasterKeys in the KeyGroup with any of the provided key services, // returning as soon as one key service succeeds. -func decryptKeyGroup(group KeyGroup, svcs []keyservice.KeyServiceClient, decryptionOrder []string, decryptionCredentials map[string]string) ([]byte, error) { +func decryptKeyGroup(group KeyGroup, svcs []keyservice.KeyServiceClient, decryptionOrder []string) ([]byte, error) { var keyErrs []error // Sort MasterKeys in the group so we try them in specific order // Use sorted indices to avoid group slice modification indices := sortKeyGroupIndices(group, decryptionOrder) for _, indexVal := range indices { key := group[indexVal] - part, err := decryptKey(key, svcs, decryptionCredentials) + part, err := decryptKey(key, svcs) if err != nil { keyErrs = append(keyErrs, err) } else { @@ -751,8 +852,8 @@ func sortKeyGroupIndices(group KeyGroup, decryptionOrder []string) []int { // decryptKey tries to decrypt the contents of the provided MasterKey with any // of the key services, returning as soon as one key service succeeds. -func decryptKey(key keys.MasterKey, svcs []keyservice.KeyServiceClient, credentials map[string]string) ([]byte, error) { - svcKey := keyservice.KeyFromMasterKey(key, credentials) +func decryptKey(key keys.MasterKey, svcs []keyservice.KeyServiceClient) ([]byte, error) { + svcKey := keyservice.KeyFromMasterKey(key) var part []byte decryptErr := decryptKeyError{ keyName: key.ToString(), @@ -787,7 +888,7 @@ func decryptKey(key keys.MasterKey, svcs []keyservice.KeyServiceClient, credenti func (m Metadata) GetDataKey() ([]byte, error) { return m.GetDataKeyWithKeyServices([]keyservice.KeyServiceClient{ keyservice.NewLocalClient(), - }, nil, nil) + }, nil) } // ToBytes converts a string, int, float or bool to a byte representation. diff --git a/sops_test.go b/sops_test.go index 81ad23c99..4a58f0866 100644 --- a/sops_test.go +++ b/sops_test.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "reflect" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -38,6 +39,23 @@ func (c reverseCipher) Decrypt(value string, key []byte, path string) (plaintext return reverse(value), nil } +type encPrefixCipher struct{} + +func (c encPrefixCipher) Encrypt(value interface{}, key []byte, path string) (string, error) { + b, err := ToBytes(value) + if err != nil { + return "", err + } + return "ENC:" + string(b), nil +} +func (c encPrefixCipher) Decrypt(value string, key []byte, path string) (plaintext interface{}, err error) { + v, ok := strings.CutPrefix(value, "ENC:") + if !ok { + return nil, fmt.Errorf("String not prefixed with 'ENC:'") + } + return v, nil +} + func TestUnencryptedSuffix(t *testing.T) { branches := TreeBranches{ TreeBranch{ @@ -330,6 +348,295 @@ func TestMACOnlyEncryptedNoConfusion(t *testing.T) { } } +func TestEncryptedCommentRegex(t *testing.T) { + branches := TreeBranches{ + TreeBranch{ + TreeItem{ + Key: Comment{"sops:enc"}, + Value: nil, + }, + TreeItem{ + Key: "foo", + Value: "bar", + }, + TreeItem{ + Key: "bar", + Value: TreeBranch{ + TreeItem{ + Key: "foo", + Value: "bar", + }, + TreeItem{ + Key: Comment{"before"}, + Value: nil, + }, + TreeItem{ + Key: Comment{"sops:enc"}, + Value: nil, + }, + TreeItem{ + Key: "encrypted", + Value: "bar", + }, + }, + }, + TreeItem{ + Key: "array", + Value: []interface{}{ + "bar", + Comment{"sops:enc"}, + "baz", + }, + }, + TreeItem{ + Key: Comment{"sops:enc"}, + Value: nil, + }, + TreeItem{ + Key: Comment{"after"}, + Value: nil, + }, + TreeItem{ + Key: "encarray", + Value: []interface{}{ + "bar", + "baz", + }, + }, + }, + } + tree := Tree{Branches: branches, Metadata: Metadata{EncryptedCommentRegex: "sops:enc"}} + expected := TreeBranch{ + TreeItem{ + Key: Comment{"sops:enc"}, + Value: nil, + }, + TreeItem{ + Key: "foo", + Value: "rab", + }, + TreeItem{ + Key: "bar", + Value: TreeBranch{ + TreeItem{ + Key: "foo", + Value: "bar", + }, + TreeItem{ + Key: Comment{"before"}, + Value: nil, + }, + TreeItem{ + Key: Comment{"sops:enc"}, + Value: nil, + }, + TreeItem{ + Key: "encrypted", + Value: "rab", + }, + }, + }, + TreeItem{ + Key: "array", + Value: []interface{}{ + "bar", + Comment{"sops:enc"}, + "zab", + }, + }, + TreeItem{ + Key: Comment{"sops:enc"}, + Value: nil, + }, + TreeItem{ + Key: Comment{"retfa"}, + Value: nil, + }, + TreeItem{ + Key: "encarray", + Value: []interface{}{ + "rab", + "zab", + }, + }, + } + cipher := reverseCipher{} + _, err := tree.Encrypt(bytes.Repeat([]byte("f"), 32), cipher) + if err != nil { + t.Errorf("Encrypting the tree failed: %s", err) + } + if !reflect.DeepEqual(tree.Branches[0], expected) { + t.Errorf("Trees don't match: \ngot \t\t%+v,\n expected \t\t%+v", tree.Branches[0], expected) + } + _, err = tree.Decrypt(bytes.Repeat([]byte("f"), 32), cipher) + if err != nil { + t.Errorf("Decrypting the tree failed: %s", err) + } + expected[1].Value = "bar" + expected[2].Value.(TreeBranch)[3].Value = "bar" + expected[3].Value.([]interface{})[2] = "baz" + expected[5].Key = Comment{"after"} + expected[6].Value = []interface{}{ + "bar", + "baz", + } + if !reflect.DeepEqual(tree.Branches[0], expected) { + t.Errorf("Trees don't match: \ngot\t\t\t%+v,\nexpected\t\t%+v", tree.Branches[0], expected) + } +} + +func TestUnencryptedCommentRegex(t *testing.T) { + branches := TreeBranches{ + TreeBranch{ + TreeItem{ + Key: Comment{"sops:noenc"}, + Value: nil, + }, + TreeItem{ + Key: "foo", + Value: "bar", + }, + TreeItem{ + Key: "bar", + Value: TreeBranch{ + TreeItem{ + Key: "foo", + Value: "bar", + }, + TreeItem{ + Key: Comment{"before"}, + Value: nil, + }, + TreeItem{ + Key: Comment{"sops:noenc"}, + Value: nil, + }, + TreeItem{ + Key: "notencrypted", + Value: "bar", + }, + }, + }, + TreeItem{ + Key: "array", + Value: []interface{}{ + "bar", + Comment{"sops:noenc"}, + "baz", + }, + }, + TreeItem{ + Key: Comment{"sops:noenc"}, + Value: nil, + }, + TreeItem{ + Key: Comment{"after"}, + Value: nil, + }, + TreeItem{ + Key: "notencarray", + Value: []interface{}{ + "bar", + "baz", + }, + }, + }, + } + tree := Tree{Branches: branches, Metadata: Metadata{UnencryptedCommentRegex: "sops:noenc"}} + expected := TreeBranch{ + TreeItem{ + Key: Comment{"sops:noenc"}, + Value: nil, + }, + TreeItem{ + Key: "foo", + Value: "bar", + }, + TreeItem{ + Key: "bar", + Value: TreeBranch{ + TreeItem{ + Key: "foo", + Value: "rab", + }, + TreeItem{ + Key: Comment{"erofeb"}, + Value: nil, + }, + TreeItem{ + Key: Comment{"sops:noenc"}, + Value: nil, + }, + TreeItem{ + Key: "notencrypted", + Value: "bar", + }, + }, + }, + TreeItem{ + Key: "array", + Value: []interface{}{ + "rab", + Comment{"sops:noenc"}, + "baz", + }, + }, + TreeItem{ + Key: Comment{"sops:noenc"}, + Value: nil, + }, + TreeItem{ + Key: Comment{"after"}, + Value: nil, + }, + TreeItem{ + Key: "notencarray", + Value: []interface{}{ + "bar", + "baz", + }, + }, + } + cipher := reverseCipher{} + _, err := tree.Encrypt(bytes.Repeat([]byte("f"), 32), cipher) + if err != nil { + t.Errorf("Encrypting the tree failed: %s", err) + } + if !reflect.DeepEqual(tree.Branches[0], expected) { + t.Errorf("Trees don't match: \ngot \t\t%+v,\n expected \t\t%+v", tree.Branches[0], expected) + } + _, err = tree.Decrypt(bytes.Repeat([]byte("f"), 32), cipher) + if err != nil { + t.Errorf("Decrypting the tree failed: %s", err) + } + expected[2].Value.(TreeBranch)[0].Value = "bar" + expected[2].Value.(TreeBranch)[1].Key = Comment{"before"} + expected[3].Value.([]interface{})[0] = "bar" + if !reflect.DeepEqual(tree.Branches[0], expected) { + t.Errorf("Trees don't match: \ngot\t\t\t%+v,\nexpected\t\t%+v", tree.Branches[0], expected) + } +} + +func TestUnencryptedCommentRegexFail(t *testing.T) { + branches := TreeBranches{ + TreeBranch{ + TreeItem{ + Key: Comment{"sops:noenc"}, + Value: nil, + }, + TreeItem{ + Key: "foo", + Value: "bar", + }, + }, + } + tree := Tree{Branches: branches, Metadata: Metadata{UnencryptedCommentRegex: "ENC"}} + cipher := encPrefixCipher{} + _, err := tree.Encrypt(bytes.Repeat([]byte("f"), 32), cipher) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "Encrypted comment \"ENC:sops:noenc\" matches UnencryptedCommentRegex!") +} + type MockCipher struct{} func (m MockCipher) Encrypt(value interface{}, key []byte, path string) (string, error) { diff --git a/stores/dotenv/store.go b/stores/dotenv/store.go index 09f118aa8..1e533341e 100644 --- a/stores/dotenv/store.go +++ b/stores/dotenv/store.go @@ -134,7 +134,7 @@ func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) { func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) { buffer := bytes.Buffer{} for _, item := range in[0] { - if isComplexValue(item.Value) { + if IsComplexValue(item.Value) { return nil, fmt.Errorf("cannot use complex value in dotenv file: %s", item.Value) } var line string @@ -166,7 +166,7 @@ func (store *Store) EmitExample() []byte { return bytes } -func isComplexValue(v interface{}) bool { +func IsComplexValue(v interface{}) bool { switch v.(type) { case []interface{}: return true diff --git a/stores/stores.go b/stores/stores.go index 9ab308d14..169e8dbb5 100644 --- a/stores/stores.go +++ b/stores/stores.go @@ -56,6 +56,8 @@ type Metadata struct { EncryptedSuffix string `yaml:"encrypted_suffix,omitempty" json:"encrypted_suffix,omitempty"` UnencryptedRegex string `yaml:"unencrypted_regex,omitempty" json:"unencrypted_regex,omitempty"` EncryptedRegex string `yaml:"encrypted_regex,omitempty" json:"encrypted_regex,omitempty"` + UnencryptedCommentRegex string `yaml:"unencrypted_comment_regex,omitempty" json:"unencrypted_comment_regex,omitempty"` + EncryptedCommentRegex string `yaml:"encrypted_comment_regex,omitempty" json:"encrypted_comment_regex,omitempty"` MACOnlyEncrypted bool `yaml:"mac_only_encrypted,omitempty" json:"mac_only_encrypted,omitempty"` Version string `yaml:"version" json:"version"` } @@ -119,6 +121,8 @@ func MetadataFromInternal(sopsMetadata sops.Metadata) Metadata { m.EncryptedSuffix = sopsMetadata.EncryptedSuffix m.UnencryptedRegex = sopsMetadata.UnencryptedRegex m.EncryptedRegex = sopsMetadata.EncryptedRegex + m.UnencryptedCommentRegex = sopsMetadata.UnencryptedCommentRegex + m.EncryptedCommentRegex = sopsMetadata.EncryptedCommentRegex m.MessageAuthenticationCode = sopsMetadata.MessageAuthenticationCode m.MACOnlyEncrypted = sopsMetadata.MACOnlyEncrypted m.Version = sopsMetadata.Version @@ -260,9 +264,15 @@ func (m *Metadata) ToInternal() (sops.Metadata, error) { if m.EncryptedRegex != "" { cryptRuleCount++ } + if m.UnencryptedCommentRegex != "" { + cryptRuleCount++ + } + if m.EncryptedCommentRegex != "" { + cryptRuleCount++ + } if cryptRuleCount > 1 { - return sops.Metadata{}, fmt.Errorf("Cannot use more than one of encrypted_suffix, unencrypted_suffix, encrypted_regex or unencrypted_regex in the same file") + return sops.Metadata{}, fmt.Errorf("Cannot use more than one of encrypted_suffix, unencrypted_suffix, encrypted_regex, unencrypted_regex, encrypted_comment_regex, or unencrypted_comment_regex in the same file") } if cryptRuleCount == 0 { @@ -277,6 +287,8 @@ func (m *Metadata) ToInternal() (sops.Metadata, error) { EncryptedSuffix: m.EncryptedSuffix, UnencryptedRegex: m.UnencryptedRegex, EncryptedRegex: m.EncryptedRegex, + UnencryptedCommentRegex: m.UnencryptedCommentRegex, + EncryptedCommentRegex: m.EncryptedCommentRegex, MACOnlyEncrypted: m.MACOnlyEncrypted, LastModified: lastModified, }, nil diff --git a/version/version.go b/version/version.go index 744b46f71..a40cc8ec8 100644 --- a/version/version.go +++ b/version/version.go @@ -12,7 +12,7 @@ import ( ) // Version represents the value of the current semantic version. -var Version = "3.8.1" +var Version = "3.9.0" // PrintVersion prints the current version of sops. If the flag // `--disable-version-check` is set, the function will not attempt @@ -101,7 +101,7 @@ func RetrieveLatestVersionFromUpstream() (string, error) { // // Unlike RetrieveLatestVersionFromUpstream, it returns the tag (e.g. "v3.7.3"). func RetrieveLatestReleaseVersion() (tag, url string, err error) { - const repository = "mozilla/sops" + const repository = "getsops/sops" return newReleaseFetcher().LatestRelease(repository) }