mirror of
https://github.com/getsops/sops.git
synced 2026-02-05 12:45:21 +01:00
Add and use encrypt and decrypt subcommands.
Signed-off-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
68
README.rst
68
README.rst
@@ -154,7 +154,7 @@ To decrypt a file in a ``cat`` fashion, use the ``-d`` flag:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ sops -d mynewtestfile.yaml
|
||||
$ sops decrypt mynewtestfile.yaml
|
||||
|
||||
SOPS encrypted files contain the necessary information to decrypt their content.
|
||||
All a user of SOPS needs is valid AWS credentials and the necessary
|
||||
@@ -200,7 +200,7 @@ the ``--age`` option or the **SOPS_AGE_RECIPIENTS** environment variable:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ sops --encrypt --age age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw test.yaml > test.enc.yaml
|
||||
$ sops encrypt --age age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw test.yaml > test.enc.yaml
|
||||
|
||||
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
|
||||
@@ -250,11 +250,11 @@ sdk:
|
||||
|
||||
Now you can encrypt a file using::
|
||||
|
||||
$ sops --encrypt --gcp-kms projects/my-project/locations/global/keyRings/sops/cryptoKeys/sops-key test.yaml > test.enc.yaml
|
||||
$ sops encrypt --gcp-kms projects/my-project/locations/global/keyRings/sops/cryptoKeys/sops-key test.yaml > test.enc.yaml
|
||||
|
||||
And decrypt it using::
|
||||
|
||||
$ sops --decrypt test.enc.yaml
|
||||
$ sops decrypt test.enc.yaml
|
||||
|
||||
Encrypting using Azure Key Vault
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -324,11 +324,11 @@ from the commandline:
|
||||
|
||||
Now you can encrypt a file using::
|
||||
|
||||
$ sops --encrypt --azure-kv https://sops.vault.azure.net/keys/sops-key/some-string test.yaml > test.enc.yaml
|
||||
$ sops encrypt --azure-kv https://sops.vault.azure.net/keys/sops-key/some-string test.yaml > test.enc.yaml
|
||||
|
||||
And decrypt it using::
|
||||
|
||||
$ sops --decrypt test.enc.yaml
|
||||
$ sops decrypt test.enc.yaml
|
||||
|
||||
|
||||
Encrypting and decrypting from other programs
|
||||
@@ -344,7 +344,7 @@ To decrypt data, you can simply do:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ cat encrypted-data | sops --decrypt /dev/stdin > decrypted-data
|
||||
$ cat encrypted-data | sops decrypt /dev/stdin > decrypted-data
|
||||
|
||||
To control the input and output format, pass ``--input-type`` and ``--output-type`` as appropriate. By default,
|
||||
``sops`` determines the input and output format from the provided filename, which is ``/dev/stdin`` here, and
|
||||
@@ -354,7 +354,7 @@ For example, to decrypt YAML data and obtain the decrypted result as YAML, use:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ cat encrypted-data | sops --input-type yaml --output-type yaml --decrypt /dev/stdin > decrypted-data
|
||||
$ cat encrypted-data | sops decrypt --input-type yaml --output-type yaml /dev/stdin > decrypted-data
|
||||
|
||||
To encrypt, it is important to note that SOPS also uses the filename to look up the correct creation rule from
|
||||
``.sops.yaml``. Likely ``/dev/stdin`` will not match a creation rule, or only match the fallback rule without
|
||||
@@ -363,7 +363,7 @@ parameter which allows you to tell SOPS which filename to use to match creation
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ echo 'foo: bar' | sops --filename-override path/filename.sops.yaml --encrypt /dev/stdin > encrypted-data
|
||||
$ echo 'foo: bar' | sops encrypt --filename-override path/filename.sops.yaml /dev/stdin > encrypted-data
|
||||
|
||||
SOPS will find a matching creation rule for ``path/filename.sops.yaml`` in ``.sops.yaml`` and use that one to
|
||||
encrypt the data from stdin. This filename will also be used to determine the input and output store. As always,
|
||||
@@ -372,7 +372,7 @@ the input store type can be adjusted by passing ``--input-type``, and the output
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ echo foo=bar | sops --filename-override path/filename.sops.yaml --input-type dotenv --encrypt /dev/stdin > encrypted-data
|
||||
$ echo foo=bar | sops encrypt --filename-override path/filename.sops.yaml --input-type dotenv /dev/stdin > encrypted-data
|
||||
|
||||
|
||||
Encrypting using Hashicorp Vault
|
||||
@@ -423,7 +423,7 @@ To easily deploy Vault locally: (DO NOT DO THIS FOR PRODUCTION!!!)
|
||||
$ vault write sops/keys/thirdkey type=chacha20-poly1305
|
||||
Success! Data written to: sops/keys/thirdkey
|
||||
|
||||
$ sops --encrypt --hc-vault-transit $VAULT_ADDR/v1/sops/keys/firstkey vault_example.yml
|
||||
$ sops encrypt --hc-vault-transit $VAULT_ADDR/v1/sops/keys/firstkey vault_example.yml
|
||||
|
||||
$ cat <<EOF > .sops.yaml
|
||||
creation_rules:
|
||||
@@ -433,7 +433,7 @@ To easily deploy Vault locally: (DO NOT DO THIS FOR PRODUCTION!!!)
|
||||
hc_vault_transit_uri: "$VAULT_ADDR/v1/sops/keys/thirdkey"
|
||||
EOF
|
||||
|
||||
$ sops --verbose -e prod/raw.yaml > prod/encrypted.yaml
|
||||
$ sops encrypt --verbose prod/raw.yaml > prod/encrypted.yaml
|
||||
|
||||
Adding and removing keys
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -888,7 +888,7 @@ You can then decrypt the file the same way as with any other SOPS file:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ sops -d example.json
|
||||
$ sops decrypt example.json
|
||||
|
||||
Key service
|
||||
~~~~~~~~~~~
|
||||
@@ -928,14 +928,14 @@ service exposed on the unix socket located in ``/tmp/sops.sock``, you can run:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ sops --keyservice unix:///tmp/sops.sock -d file.yaml`
|
||||
$ sops decrypt --keyservice unix:///tmp/sops.sock file.yaml`
|
||||
|
||||
And if you only want to use the key service exposed on the unix socket located
|
||||
in ``/tmp/sops.sock`` and not the local key service, you can run:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ sops --enable-local-keyservice=false --keyservice unix:///tmp/sops.sock -d file.yaml
|
||||
$ sops decrypt --enable-local-keyservice=false --keyservice unix:///tmp/sops.sock file.yaml
|
||||
|
||||
Auditing
|
||||
~~~~~~~~
|
||||
@@ -1002,7 +1002,7 @@ written to disk.
|
||||
.. code:: sh
|
||||
|
||||
# print secrets to stdout to confirm values
|
||||
$ sops -d out.json
|
||||
$ sops decrypt out.json
|
||||
{
|
||||
"database_password": "jf48t9wfw094gf4nhdf023r",
|
||||
"AWS_ACCESS_KEY_ID": "AKIAIOSFODNN7EXAMPLE",
|
||||
@@ -1152,7 +1152,7 @@ Below is an example of publishing to Vault (using token auth with a local dev in
|
||||
|
||||
$ export VAULT_TOKEN=...
|
||||
$ export VAULT_ADDR='http://127.0.0.1:8200'
|
||||
$ sops -d vault/test.yaml
|
||||
$ sops decrypt vault/test.yaml
|
||||
example_string: bar
|
||||
example_number: 42
|
||||
example_map:
|
||||
@@ -1193,23 +1193,23 @@ extension after encrypting a file. For example:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ sops -e -i myfile.json
|
||||
$ sops -d myfile.json
|
||||
$ sops encrypt -i myfile.json
|
||||
$ sops decrypt myfile.json
|
||||
|
||||
If you want to change the extension of the file once encrypted, you need to provide
|
||||
``sops`` with the ``--input-type`` flag upon decryption. For example:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ sops -e myfile.json > myfile.json.enc
|
||||
$ sops encrypt myfile.json > myfile.json.enc
|
||||
|
||||
$ sops -d --input-type json myfile.json.enc
|
||||
$ sops decrypt --input-type json myfile.json.enc
|
||||
|
||||
When operating on stdin, use the ``--input-type`` and ``--output-type`` flags as follows:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ cat myfile.json | sops --input-type json --output-type json -d /dev/stdin
|
||||
$ cat myfile.json | sops decrypt --input-type json --output-type json /dev/stdin
|
||||
|
||||
JSON and JSON_binary indentation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -1361,13 +1361,13 @@ encrypt the file, and redirect the output to a destination file.
|
||||
|
||||
$ export SOPS_KMS_ARN="arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500"
|
||||
$ export SOPS_PGP_FP="C9CAB0AF1165060DB58D6D6B2653B624D620786D"
|
||||
$ sops -e /path/to/existing/file.yaml > /path/to/new/encrypted/file.yaml
|
||||
$ sops encrypt /path/to/existing/file.yaml > /path/to/new/encrypted/file.yaml
|
||||
|
||||
Decrypt the file with ``-d``.
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ sops -d /path/to/new/encrypted/file.yaml
|
||||
$ sops decrypt /path/to/new/encrypted/file.yaml
|
||||
|
||||
Encrypt or decrypt a file in place
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -1378,9 +1378,9 @@ original file after encrypting or decrypting it.
|
||||
.. code:: sh
|
||||
|
||||
# file.yaml is in cleartext
|
||||
$ sops -e -i /path/to/existing/file.yaml
|
||||
$ sops encrypt -i /path/to/existing/file.yaml
|
||||
# file.yaml is now encrypted
|
||||
$ sops -d -i /path/to/existing/file.yaml
|
||||
$ sops decrypt -i /path/to/existing/file.yaml
|
||||
# file.yaml is back in cleartext
|
||||
|
||||
Encrypting binary files
|
||||
@@ -1407,10 +1407,10 @@ In-place encryption/decryption also works on binary files.
|
||||
$ sha512sum /tmp/somerandom
|
||||
9589bb20280e9d381f7a192000498c994e921b3cdb11d2ef5a986578dc2239a340b25ef30691bac72bdb14028270828dad7e8bd31e274af9828c40d216e60cbe /tmp/somerandom
|
||||
|
||||
$ sops -e -i /tmp/somerandom
|
||||
$ sops encrypt -i /tmp/somerandom
|
||||
please wait while a data encryption key is being generated and stored securely
|
||||
|
||||
$ sops -d -i /tmp/somerandom
|
||||
$ sops decrypt -i /tmp/somerandom
|
||||
|
||||
$ sha512sum /tmp/somerandom
|
||||
9589bb20280e9d381f7a192000498c994e921b3cdb11d2ef5a986578dc2239a340b25ef30691bac72bdb14028270828dad7e8bd31e274af9828c40d216e60cbe /tmp/somerandom
|
||||
@@ -1424,7 +1424,7 @@ values, like keys, without needing an extra parser.
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ sops -d --extract '["app2"]["key"]' ~/git/svc/sops/example.yaml
|
||||
$ sops decrypt --extract '["app2"]["key"]' ~/git/svc/sops/example.yaml
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIBPAIBAAJBAPTMNIyHuZtpLYc7VsHQtwOkWYobkUblmHWRmbXzlAX6K8tMf3Wf
|
||||
ImcbNkqAKnELzFAPSBeEMhrBN0PyOC9lYlMCAwEAAQJBALXD4sjuBn1E7Y9aGiMz
|
||||
@@ -1441,7 +1441,7 @@ them.
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ sops -d --extract '["an_array"][1]' ~/git/svc/sops/example.yaml
|
||||
$ sops decrypt --extract '["an_array"][1]' ~/git/svc/sops/example.yaml
|
||||
secretuser2
|
||||
|
||||
Set a sub-part in a document tree
|
||||
@@ -1488,11 +1488,11 @@ to a SOPS command in the git configuration file of the repository.
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ git config diff.sopsdiffer.textconv "sops -d"
|
||||
$ git config diff.sopsdiffer.textconv "sops decrypt"
|
||||
|
||||
$ grep -A 1 sopsdiffer .git/config
|
||||
[diff "sopsdiffer"]
|
||||
textconv = "sops -d"
|
||||
textconv = "sops decrypt"
|
||||
|
||||
With this in place, calls to ``git diff`` will decrypt both previous and current
|
||||
versions of the target file prior to displaying the diff. And it even works with
|
||||
@@ -1527,7 +1527,7 @@ keys that match the supplied regular expression. For example, this command:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ sops --encrypt --encrypted-regex '^(data|stringData)$' k8s-secrets.yaml
|
||||
$ sops encrypt --encrypted-regex '^(data|stringData)$' k8s-secrets.yaml
|
||||
|
||||
will encrypt the values under the ``data`` and ``stringData`` keys in a YAML file
|
||||
containing kubernetes secrets. It will not encrypt other values that help you to
|
||||
@@ -1539,7 +1539,7 @@ that match the supplied regular expression. For example, this command:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ sops --encrypt --unencrypted-regex '^(description|metadata)$' k8s-secrets.yaml
|
||||
$ sops encrypt --unencrypted-regex '^(description|metadata)$' k8s-secrets.yaml
|
||||
|
||||
will not encrypt the values under the ``description`` and ``metadata`` keys in a YAML file
|
||||
containing kubernetes secrets, while encrypting everything else.
|
||||
|
||||
278
cmd/sops/main.go
278
cmd/sops/main.go
@@ -128,7 +128,7 @@ func main() {
|
||||
|
||||
The -p, -k, --gcp-kms, --hc-vault-transit, and --azure-kv flags are only
|
||||
used to encrypt new documents. Editing or decrypting existing documents
|
||||
can be done with "sops file" or "sops -d file" respectively. The KMS and
|
||||
can be done with "sops file" or "sops decrypt file" respectively. The KMS and
|
||||
PGP keys listed in the encrypted documents are used then. To manage master
|
||||
keys in existing documents, use the "add-{kms,pgp,gcp-kms,azure-kv,hc-vault-transit}"
|
||||
and "rm-{kms,pgp,gcp-kms,azure-kv,hc-vault-transit}" flags with --rotate
|
||||
@@ -622,6 +622,282 @@ func main() {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "decrypt",
|
||||
Usage: "decrypt a file, and output the results to stdout",
|
||||
ArgsUsage: `file`,
|
||||
Flags: append([]cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "in-place, i",
|
||||
Usage: "write output back to the same file instead of stdout",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "extract",
|
||||
Usage: "extract a specific key or branch from the input document. Example: --extract '[\"somekey\"][0]'",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "output",
|
||||
Usage: "Save the output after decryption to the file specified",
|
||||
},
|
||||
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.BoolFlag{
|
||||
Name: "ignore-mac",
|
||||
Usage: "ignore Message Authentication Code during decryption",
|
||||
},
|
||||
cli.StringFlag{
|
||||
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: "decryption-order",
|
||||
Usage: "comma separated list of decryption key types",
|
||||
EnvVar: "SOPS_DECRYPTION_ORDER",
|
||||
},
|
||||
}, keyserviceFlags...),
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.Bool("verbose") {
|
||||
logging.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
if c.NArg() < 1 {
|
||||
return common.NewExitError("Error: no file specified", codes.NoFileSpecified)
|
||||
}
|
||||
warnMoreThanOnePositionalArgument(c)
|
||||
if c.Bool("in-place") && c.String("output") != "" {
|
||||
return common.NewExitError("Error: cannot operate on both --output and --in-place", codes.ErrorConflictingParameters)
|
||||
}
|
||||
fileName, err := filepath.Abs(c.Args()[0])
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
if _, err := os.Stat(fileName); os.IsNotExist(err) {
|
||||
return common.NewExitError("Error: cannot operate on non-existent file", codes.NoFileSpecified)
|
||||
}
|
||||
fileNameOverride := c.String("filename-override")
|
||||
if fileNameOverride == "" {
|
||||
fileNameOverride = fileName
|
||||
}
|
||||
|
||||
inputStore := inputStore(c, fileNameOverride)
|
||||
outputStore := outputStore(c, fileNameOverride)
|
||||
svcs := keyservices(c)
|
||||
|
||||
order, err := decryptionOrder(c.String("decryption-order"))
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
|
||||
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"),
|
||||
})
|
||||
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
|
||||
if c.Bool("in-place") {
|
||||
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
|
||||
}
|
||||
|
||||
outputFile := os.Stdout
|
||||
if c.String("output") != "" {
|
||||
file, err := os.Create(c.String("output"))
|
||||
if err != nil {
|
||||
return common.NewExitError(fmt.Sprintf("Could not open output file for writing: %s", err), codes.CouldNotWriteOutputFile)
|
||||
}
|
||||
defer file.Close()
|
||||
outputFile = file
|
||||
}
|
||||
_, err = outputFile.Write(output)
|
||||
return toExitError(err)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "encrypt",
|
||||
Usage: "encrypt a file, and output the results to stdout",
|
||||
ArgsUsage: `file`,
|
||||
Flags: append([]cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "in-place, i",
|
||||
Usage: "write output back to the same file instead of stdout",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "output",
|
||||
Usage: "Save the output after decryption to the file specified",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "kms, k",
|
||||
Usage: "comma separated list of KMS ARNs",
|
||||
EnvVar: "SOPS_KMS_ARN",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "aws-profile",
|
||||
Usage: "The AWS profile to use for requests to AWS",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "gcp-kms",
|
||||
Usage: "comma separated list of GCP KMS resource IDs",
|
||||
EnvVar: "SOPS_GCP_KMS_IDS",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "azure-kv",
|
||||
Usage: "comma separated list of Azure Key Vault URLs",
|
||||
EnvVar: "SOPS_AZURE_KEYVAULT_URLS",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "hc-vault-transit",
|
||||
Usage: "comma separated list of vault's key URI (e.g. 'https://vault.example.org:8200/v1/transit/keys/dev')",
|
||||
EnvVar: "SOPS_VAULT_URIS",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "pgp, p",
|
||||
Usage: "comma separated list of PGP fingerprints",
|
||||
EnvVar: "SOPS_PGP_FP",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "age, a",
|
||||
Usage: "comma separated list of age recipients",
|
||||
EnvVar: "SOPS_AGE_RECIPIENTS",
|
||||
},
|
||||
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.StringFlag{
|
||||
Name: "unencrypted-suffix",
|
||||
Usage: "override the unencrypted key suffix.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "encrypted-suffix",
|
||||
Usage: "override the encrypted key suffix. When empty, all keys will be encrypted, unless otherwise marked with unencrypted-suffix.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "unencrypted-regex",
|
||||
Usage: "set the unencrypted key regex. When specified, only keys matching the regex will be left unencrypted.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "encrypted-regex",
|
||||
Usage: "set the encrypted key regex. When specified, only keys matching the regex will be encrypted.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "encryption-context",
|
||||
Usage: "comma separated list of KMS encryption context key:value pairs",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "shamir-secret-sharing-threshold",
|
||||
Usage: "the number of master keys required to retrieve the data key with shamir",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "filename-override",
|
||||
Usage: "Use this filename instead of the provided argument for loading configuration, and for determining input type and output type",
|
||||
},
|
||||
}, keyserviceFlags...),
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.Bool("verbose") {
|
||||
logging.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
if c.NArg() < 1 {
|
||||
return common.NewExitError("Error: no file specified", codes.NoFileSpecified)
|
||||
}
|
||||
warnMoreThanOnePositionalArgument(c)
|
||||
if c.Bool("in-place") && c.String("output") != "" {
|
||||
return common.NewExitError("Error: cannot operate on both --output and --in-place", codes.ErrorConflictingParameters)
|
||||
}
|
||||
fileName, err := filepath.Abs(c.Args()[0])
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
if _, err := os.Stat(fileName); os.IsNotExist(err) {
|
||||
return common.NewExitError("Error: cannot operate on non-existent file", codes.NoFileSpecified)
|
||||
}
|
||||
fileNameOverride := c.String("filename-override")
|
||||
if fileNameOverride == "" {
|
||||
fileNameOverride = fileName
|
||||
}
|
||||
|
||||
inputStore := inputStore(c, fileNameOverride)
|
||||
outputStore := outputStore(c, fileNameOverride)
|
||||
svcs := keyservices(c)
|
||||
|
||||
encConfig, err := getEncryptConfig(c, fileNameOverride)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
output, err := encrypt(encryptOpts{
|
||||
OutputStore: outputStore,
|
||||
InputStore: inputStore,
|
||||
InputPath: fileName,
|
||||
Cipher: aes.NewCipher(),
|
||||
KeyServices: svcs,
|
||||
encryptConfig: encConfig,
|
||||
})
|
||||
|
||||
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
|
||||
if c.Bool("in-place") {
|
||||
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
|
||||
}
|
||||
|
||||
outputFile := os.Stdout
|
||||
if c.String("output") != "" {
|
||||
file, err := os.Create(c.String("output"))
|
||||
if err != nil {
|
||||
return common.NewExitError(fmt.Sprintf("Could not open output file for writing: %s", err), codes.CouldNotWriteOutputFile)
|
||||
}
|
||||
defer file.Close()
|
||||
outputFile = file
|
||||
}
|
||||
_, err = outputFile.Write(output)
|
||||
return toExitError(err)
|
||||
},
|
||||
},
|
||||
}
|
||||
app.Flags = append([]cli.Flag{
|
||||
cli.BoolFlag{
|
||||
|
||||
@@ -42,7 +42,7 @@ In both development and production, we will be storing the secrets file unencryp
|
||||
|
||||
As peace of mind, think about this:
|
||||
|
||||
- Unencrypted on disk is fine because if the attacker ever gains access to the server, then they can run ``sops --decrypt`` as well.
|
||||
- Unencrypted on disk is fine because if the attacker ever gains access to the server, then they can run ``sops decrypt`` as well.
|
||||
|
||||
Files
|
||||
-----
|
||||
@@ -69,7 +69,7 @@ For testing in a public CI, we can copy ``secret.enc.json`` to ``secret.json``.
|
||||
|
||||
..
|
||||
|
||||
For convenience, we can run ``CONFIG_COPY_ONLY=TRUE bin/decrypt-config.sh`` which will use ``cp`` rather than ``sops --decrypt``.
|
||||
For convenience, we can run ``CONFIG_COPY_ONLY=TRUE bin/decrypt-config.sh`` which will use ``cp`` rather than ``sops decrypt``.
|
||||
|
||||
For testing in a private CI where we need private information, see the `Production instructions <#production>`_.
|
||||
|
||||
|
||||
@@ -17,6 +17,6 @@ for file in $secret_files; do
|
||||
cp "$src_file" "$target_file"
|
||||
# Otherwise, decrypt it
|
||||
else
|
||||
sops --decrypt "$src_file" > "$target_file"
|
||||
sops decrypt "$src_file" > "$target_file"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -47,7 +47,7 @@ In both development and production, we will be storing the secrets file unencryp
|
||||
|
||||
As peace of mind, think about this:
|
||||
|
||||
- Unencrypted on disk is fine because if the attacker ever gains access to the server, then they can run ``sops --decrypt`` as well.
|
||||
- Unencrypted on disk is fine because if the attacker ever gains access to the server, then they can run ``sops decrypt`` as well.
|
||||
|
||||
Files
|
||||
-----
|
||||
@@ -78,7 +78,7 @@ For testing in a public CI, we can copy ``config.enc`` to ``config``. The secret
|
||||
|
||||
..
|
||||
|
||||
For convenience, we can run ``CONFIG_COPY_ONLY=TRUE bin/decrypt-config.sh`` which will use ``ln -s`` rather than ``sops --decrypt``.
|
||||
For convenience, we can run ``CONFIG_COPY_ONLY=TRUE bin/decrypt-config.sh`` which will use ``ln -s`` rather than ``sops decrypt``.
|
||||
|
||||
For testing in a private CI where we need private information, see the `Production instructions <#production>`_.
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ for src_file in config.enc/*; do
|
||||
# If the file is our secret, then decrypt it
|
||||
if echo "$src_filename" | grep -E "${secret_ext}$" &&
|
||||
test "$CONFIG_COPY_ONLY" != "TRUE"; then
|
||||
sops --decrypt "$src_file" > "$target_file"
|
||||
sops decrypt "$src_file" > "$target_file"
|
||||
# Otherwise, symlink to the original file
|
||||
else
|
||||
ln -s "../$src_file" "$target_file"
|
||||
|
||||
@@ -60,7 +60,7 @@ mod tests {
|
||||
}",
|
||||
);
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-e")
|
||||
.arg("encrypt")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
@@ -92,7 +92,7 @@ mod tests {
|
||||
);
|
||||
assert!(
|
||||
Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-e")
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
@@ -127,7 +127,7 @@ mod tests {
|
||||
);
|
||||
assert!(
|
||||
Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-e")
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
@@ -162,7 +162,7 @@ mod tests {
|
||||
);
|
||||
assert!(
|
||||
Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-e")
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
@@ -201,9 +201,9 @@ mod tests {
|
||||
);
|
||||
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
.arg("--kms")
|
||||
.arg(kms_arn)
|
||||
.arg("-e")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
@@ -231,7 +231,7 @@ mod tests {
|
||||
bar: baz",
|
||||
);
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-e")
|
||||
.arg("encrypt")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
@@ -257,7 +257,7 @@ bar: baz",
|
||||
prepare_temp_file("test_set_update.json", r#"{"a": 2, "b": "ba"}"#.as_bytes());
|
||||
assert!(
|
||||
Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-e")
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
@@ -300,7 +300,7 @@ bar: baz",
|
||||
prepare_temp_file("test_set_insert.json", r#"{"a": 2, "b": "ba"}"#.as_bytes());
|
||||
assert!(
|
||||
Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-e")
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
@@ -347,7 +347,7 @@ b: ba"#
|
||||
);
|
||||
assert!(
|
||||
Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-e")
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
@@ -394,7 +394,7 @@ b: ba"#
|
||||
);
|
||||
assert!(
|
||||
Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-e")
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
@@ -436,6 +436,63 @@ b: ba"#
|
||||
let file_path = prepare_temp_file(
|
||||
"test_set_string.yaml",
|
||||
r#"a: 2
|
||||
b: ba"#
|
||||
.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"
|
||||
);
|
||||
assert!(
|
||||
Command::new(SOPS_BINARY_PATH)
|
||||
.arg("--set")
|
||||
.arg(r#"["a"] "aaa""#)
|
||||
.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("decrypt")
|
||||
.arg("-i")
|
||||
.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)
|
||||
);
|
||||
let mut s = String::new();
|
||||
File::open(file_path)
|
||||
.unwrap()
|
||||
.read_to_string(&mut s)
|
||||
.unwrap();
|
||||
let data: Value = serde_yaml::from_str(&s).expect("Error parsing sops's YAML output");
|
||||
if let Value::Mapping(data) = data {
|
||||
let a = data.get(&Value::String("a".to_owned())).unwrap();
|
||||
assert_eq!(a, &Value::String("aaa".to_owned()));
|
||||
} else {
|
||||
panic!("Output JSON does not have the expected structure");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_yaml_file_string_compat() {
|
||||
let file_path = prepare_temp_file(
|
||||
"test_set_string_compat.yaml",
|
||||
r#"a: 2
|
||||
b: ba"#
|
||||
.as_bytes(),
|
||||
);
|
||||
@@ -496,7 +553,7 @@ b: ba"#
|
||||
);
|
||||
assert!(
|
||||
!Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-d")
|
||||
.arg("decrypt")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops")
|
||||
@@ -507,7 +564,7 @@ b: ba"#
|
||||
|
||||
assert!(
|
||||
Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-d")
|
||||
.arg("decrypt")
|
||||
.arg("--ignore-mac")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
@@ -522,7 +579,7 @@ b: ba"#
|
||||
fn encrypt_comments() {
|
||||
let file_path = "res/comments.yaml";
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-e")
|
||||
.arg("encrypt")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
@@ -541,7 +598,7 @@ b: ba"#
|
||||
fn encrypt_comments_list() {
|
||||
let file_path = "res/comments_list.yaml";
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-e")
|
||||
.arg("encrypt")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
@@ -560,7 +617,7 @@ b: ba"#
|
||||
fn decrypt_comments() {
|
||||
let file_path = "res/comments.enc.yaml";
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-d")
|
||||
.arg("decrypt")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
@@ -579,7 +636,7 @@ b: ba"#
|
||||
fn decrypt_comments_unencrypted_comments() {
|
||||
let file_path = "res/comments_unencrypted_comments.yaml";
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-d")
|
||||
.arg("decrypt")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
@@ -599,8 +656,8 @@ b: ba"#
|
||||
// The .sops.yaml file ensures this file is encrypted with two key groups, each with one GPG key
|
||||
let file_path = prepare_temp_file("test_roundtrip_keygroups.yaml", "a: secret".as_bytes());
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg("-e")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
@@ -609,7 +666,7 @@ b: ba"#
|
||||
"SOPS failed to encrypt a file with Shamir Secret Sharing"
|
||||
);
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-d")
|
||||
.arg("decrypt")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
@@ -629,8 +686,8 @@ b: ba"#
|
||||
"a: secret".as_bytes(),
|
||||
);
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg("-e")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
@@ -639,7 +696,7 @@ b: ba"#
|
||||
"SOPS failed to encrypt a file with Shamir Secret Sharing"
|
||||
);
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-d")
|
||||
.arg("decrypt")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
@@ -656,7 +713,7 @@ b: ba"#
|
||||
include_bytes!("../res/multiple_keys.yaml"),
|
||||
);
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-d")
|
||||
.arg("decrypt")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
@@ -673,16 +730,16 @@ b: ba"#
|
||||
"multiline: |\n multi\n line".as_bytes(),
|
||||
);
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg("-e")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
assert!(output.status.success(), "SOPS failed to encrypt a file");
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("decrypt")
|
||||
.arg("--extract")
|
||||
.arg("[\"multiline\"]")
|
||||
.arg("-d")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
@@ -696,8 +753,8 @@ b: ba"#
|
||||
let data = b"\"\"{}this_is_binary_data";
|
||||
let file_path = prepare_temp_file("test.binary", data);
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg("-e")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
@@ -706,7 +763,7 @@ b: ba"#
|
||||
"SOPS failed to encrypt a binary file"
|
||||
);
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-d")
|
||||
.arg("decrypt")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
@@ -732,19 +789,19 @@ b: ba"#
|
||||
);
|
||||
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
.arg("--kms")
|
||||
.arg(kms_arn)
|
||||
.arg("--encryption-context")
|
||||
.arg("foo:bar,one:two")
|
||||
.arg("-i")
|
||||
.arg("-e")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
assert!(output.status.success(), "sops didn't exit successfully");
|
||||
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("-d")
|
||||
.arg("decrypt")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
@@ -761,9 +818,9 @@ b: ba"#
|
||||
let input_path = prepare_temp_file("test_output_flag.binary", b"foo");
|
||||
let output_path = Path::join(TMP_DIR.path(), "output_flag.txt");
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
.arg("--output")
|
||||
.arg(&output_path)
|
||||
.arg("-e")
|
||||
.arg(input_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
|
||||
Reference in New Issue
Block a user