1. Project Overview & Quickstart (getsops/sops)
File: docs/release.md
Release procedure
This document describes the procedure for releasing a new version of SOPS. It
is intended for maintainers of the project, but may be useful for anyone
interested in the release process.
Overview
The release is performed by creating a signed tag for the release, and pushing
it to GitHub. This will automatically trigger a GitHub Actions workflow that
builds the binaries, packages, SBOMs, and other artifacts for the release
using GoReleaser, and uploads them to GitHub.
The configuration for GoReleaser is in the file.goreleaser.yaml. The configuration for the GitHub
Actions workflow is in the filerelease.yml.
This configuration is quite sophisticated, and ensures at least the following:
- The release is built for multiple platforms and architectures, including
Linux, macOS, and Windows, and for both AMD64 and ARM64. - The release includes multiple packages in Debian and RPM formats.
- For every binary, a corresponding SBOM is generated and published.
- For all binaries, a checksum file is generated and signed using
Cosign with GitHub OIDC. - Both Debian and Alpine Docker multi-arch images are built and pushed to GitHub
Container Registry and Quay.io. - The container images are signed using
Cosign with GitHub OIDC. - SLSA provenance metadata is generated for
release artifacts and container images.
Preparation
- Ensure that all changes intended for the release are merged into the
mainbranch. At present, this means that all pull requests attached to the
milestone for the release are merged. If there are any pull requests that
should not be included in the release, move them to a different milestone. - Create a pull request to update the
CHANGELOG.md
file. This should include a summary of all changes since the last release,
including references to any relevant pull requests. - In this same pull request, update the version number in
version/version.goto the new version number. - Get approval for the pull request from at least one other maintainer, and
merge it intomain. - Ensure CI passes on the
mainbranch.
Release
Ensure your local copy of the
mainbranch is up-to-date:shgit checkout main git pullCreate a signed tag for the release, using the following command:
shgit tag -s -m <version> <version>where
<version>is the version number of the release. The version number
should be in the formvX.Y.Z, whereX,Y, andZare integers. The
version number should be incremented according to
semantic versioning.Push the tag to GitHub:
shgit push origin <version>Ensure the release is built successfully on GitHub Actions. This will
automatically create a release on GitHub.In the getsops/docs repository,
mergemainintostableand pushstableto update
getsops.io.
File: shamir/README.md
Shamir's secret sharing
Forked from Vault
How it works
We want to split a secret into parts.
Any two points on the cartesian plane define a line. Three points define a
parabola. Four points define a cubic curve, and so on. In general, n points
define an function of degree (n - 1). If our secret was somehow an function of
degree (n - 1), we could just compute n different points of that function
and give n different people one point each. In order to recover the secret,
then we'd need all the n points. If we wanted to, we could compute more than n
points, but even then still only n points out of our whole set of computed
points would be required to recover the function.
A concrete example: our secret is the function y = 2x + 1. This function is of
degree 1, so we need at least 2 points to define it. For example, let's setx = 1, x = 2 and x = 3. From this follows that y = 3, y = 5 andy = 7, respectively. Now, with the information that our secret is of degree 1,
we can use any 2 of the 3 points we computed to recover our original function.
For example, let's use the points x = 1; y = 3 and x = 2; y = 5.
We know that first degree functions are lines, defined by their slope and their
intersection point with the y axis. We can easily compute the slope given our
two points: it's the change in y divided by the change in x:(5 - 3)/(2 - 1) = 2. Now, knowing the slope we can compute the intersection
point with the y axis by "working our way back". We know that at x = 1,y equals 3, so naturally because the slope is 2, at x = 0, y must be1.
Lagrange interpolation
The method we've used for this isn't very general: it only works for polynomials
of degree 1. Lagrange interpolation is a more general way that lets us obtain
the function of degree (n - 1) that passes through n arbitrary points.
Understanding how to perform Lagrange interpolation isn't really necessary to
understand Shamir's Secret Sharing: it's enough to know that there's only one
function of degree (n - 1) that passes through n given points and that
computing this function given the points is computationally efficient.
But for those interested, here's an explanation:
Let's say our points are (x_0, y_0),...,(x_j, y_j),...,(x_(n-1), y_(n-1)).
Then, the Lagrange polynomial L(x), the polynomial we're looking for, is
defined as follows:
L(x) = sum from j=0 to j=(n-1) of {y_j * l_j(x)}
where l_j(x) = product from m=0 to m=(n-1) except when m=j of {(x - x_m)/(x_j - x_m)}.
A concrete example, with 3 points:
x_0 = 1 y_0 = 1
x_1 = 2 y_1 = 4
x_2 = 3 y_2 = 9Let's apply the formula:
L(x) =
y_0 * l_0(x) +
y_1 * l_1(x) +
y_2 * l_2(x)Substitute y_j for the actual value:
L(x) =
1 * l_0(x) +
4 * l_1(x) +
9 * l_2(x)Replace l_j(x):
l_0(x) = (x - 2)/(1 - 2) * (x - 3)/(1 - 3) = 0.5x^2 - 2.5x + 3
l_1(x) = (x - 1)/(2 - 1) * (x - 3)/(2 - 3) = - x^2 + 4x - 3
l_2(x) = (x - 1)/(3 - 1) * (x - 2)/(3 - 2) = 0.5x^2 - 1.5x + 1L(x) =
1 * ( 0.5x^2 - 2.5x + 3) +
4 * ( -x^2 + 4x - 3) +
9 * ( 0.5x^2 - 1.5x + 1)L(x) =
( 0.5x^2 - 2.5x + 3) +
( -4x^2 + 16x - 12) +
( 4.5x^2 - 13.5x + 9)
= x^2 + 0x + 0
= x^2So the polynomial we were looking for is y = x^2.
Splitting a secret
So we have the ability of splitting a function into parts, but in the context
of computing we generally want to split a number, not a function. For this,
let's define a function of degree threshold. threshold is the amount of
parts we want to require in order to recover the secret. Let's set the parameter
of degree zero to our secret S and make the rest of the parameters random:
y = ax^(threshold) + bx^(threshold-1) + ... + zx^1 + S
With a, b, ... random.
Then, we want to generate our parts. For this, we evaluate our function at as
many points as we want parts. For example, say our secret is 123, we want 5
parts and a threshold of 2. Because the threshold is 2, we're going to need a
polynomial of degree 2:
y = ax^2 + bx + 123
We randomly set a = 7 and b = 1:
y = 7x^2 + x + 123
Because we want 5 parts, we need to compute 5 points:
x = 0 -> y = 123 # woops! This is the secret itself. Let's not use that one.
x = 1 -> y = 131
x = 2 -> y = 153
x = 3 -> y = 189
x = 4 -> y = 239
x = 5 -> y = 303And that's it. Each of the computed points is one part of the secret.
Combining a secret
Now that we have our parts, we have to define a way to recover them. Using
the example from the previous section, we only need any two points out of the
five we created to recover the secret, because we set the threshold to two.
So with any two of the five points we created, we can recover the original
polynomial, and because the secret is the free term in the polynomial, we can
recover the secret.
Finite fields
In the previous examples we've only used integers, and this unfortunately has
a flaw. First of all, it's impossible to uniformly sample integers to get
random coefficients for our generated polynomial. Additionally, if we don't
operate in a finite field, information about the secret is leaked for every part
someone recovers.
For these reasons, Vault's implementation of Shamir's Secret Sharing uses finite
field arithmetic, specifically in GF(2^8), with 229 as the generator. GF(2^8)
has 256 elements, so using this we can only split one byte at a time. This is
not a problem, though, as we can just split each byte in our secret
independently. This implementation uses tables to speed up the execution of
finite field arithmetic.
File: README.rst
SOPS: Secrets OPerationS
SOPS is an editor of encrypted files that supports YAML, JSON, ENV, INI and BINARY
formats and encrypts with AWS KMS, GCP KMS, Azure Key Vault, HuaweiCloud KMS, age, and PGP.
(demo <https://www.youtube.com/watch?v=YTEVyLXFiq0>_)
.. image:: https://i.imgur.com/X0TM5NI.gif
.. image:: https://pkg.go.dev/badge/github.com/getsops/sops/v3.svg
:target: https://pkg.go.dev/github.com/getsops/sops/v3
Documentation
You can find the SOPS documentation on getsops.io <https://getsops.io/>_ under "Docs" <https://getsops.io/docs/>_.
Security
Please report any security issues privately using GitHub's advisory form <https://github.com/getsops/sops/security/advisories>_.
License
Mozilla Public License Version 2.0
Authors
SOPS was initially launched as a project at Mozilla in 2015 and has been
graciously donated to the CNCF as a Sandbox project in 2023, now under the
stewardship of a new group of maintainers <https://github.com/getsops/community/blob/main/MAINTAINERS.md>_.
The original authors of the project were:
- Adrian Utrilla @autrilla
- Julien Vehent @jvehent
Furthermore, the project has been carried for a long time by AJ Bahnken @ajvb,
and had not been possible without the contributions of numerous contributors <https://github.com/getsops/sops/graphs/contributors>_.
Credits
SOPS was inspired by hiera-eyaml <https://github.com/TomPoulton/hiera-eyaml>,credstash <https://github.com/LuminalOSS/credstash>,sneaker <https://github.com/codahale/sneaker>,password store <http://www.passwordstore.org/> and too many years managing
PGP encrypted files by hand...
.. image:: docs/images/cncf-color-bg.svg
:width: 400
:alt: CNCF Sandbox Project
We are a Cloud Native Computing Foundation <https://cncf.io>_ sandbox project.
2. Official Technical Reference & Guides (getsops/docs)
File: README.md
SOPS Documentation Site
SOPS is an open source secrets manager.
This repository contains the documentation site for SOPS hosted at getsops.io.
If you are interested in contributing to the documentation,
look at the contribution guidelines on the getsops.io.
File: content/en/community/_index.md
title: Community menu: {main: {weight: 40}}
File: content/en/docs/contribution-guidelines/_index.md
title: Contribution Guidelines weight: 100
Code contributions
Check out CONTRIBUTING.md in the getsops/sops GitHub repository.
Documentation branching
There are two main documentation branches:
main: This is the branch where most work happens on.
It should document the current state of github.com/getsops/sops'smainbranch.
This branch is published under https://main.getsops.io.stable: This is the branch that is published at getsops.io.
Themainbranch is merged into it when a new SOPS release happens.
Some work, like fixing typos, adjusting the design, etc., can happen directly on the stable branch (afterwards it needs to be merged back into main).
Documentation contributions
We use Hugo to format and generate our website, the
Docsy theme for styling and site structure,
and Netlify to manage the deployment of the site.
Hugo is an open-source static site generator that provides us with templates,
content organisation in a standard directory structure, and a website generation
engine. You write the pages in Markdown (or HTML if you want), and Hugo wraps them up into a website.
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
GitHub Help for more
information on using pull requests.
Quick start with Netlify
Here's a quick guide to updating the docs. It assumes you're familiar with the
GitHub workflow and you're happy to use the automated preview of your doc
updates:
- Fork the getsops/docs repo on GitHub.
- Make your changes and send a pull request (PR).
- If you're not yet ready for a review, add "WIP" to the PR name to indicate it's a work in progress.
- Wait for the automated PR workflow to do some checks. When it's ready, you should see a comment like this: deploy/netlify โ Deploy preview ready!
- Click Details to the right of "Deploy preview ready" to see a preview of your updates.
- Continue updating your doc and pushing your changes until you're happy with the content.
- When you're ready for a review, add a comment to the PR, and remove any "WIP" markers.
Updating a single page
If you've just spotted something you'd like to change while using the docs, Docsy has a shortcut for you:
- Click Edit this page in the top right hand corner of the page.
- If you don't already have an up to date fork of the project repo, you are prompted to get one - click Fork this repository and propose changes or Update your Fork to get an up to date version of the project to edit. The appropriate page in your fork is displayed in edit mode.
- Follow the rest of the Quick start with Netlify process above to make, preview, and propose your changes.
Previewing your changes locally
If you want to run your own local Hugo server to preview your changes as you work:
Follow the instructions in Getting started to install Hugo and any other tools you need. You'll need at least Hugo version 0.45 (we recommend using the most recent available version), and it must be the extended version, which supports SCSS.
Fork the getsops/docs repo repo into your own project, then create a local copy using
git clone.textgit clone --depth 1 https://github.com/getsops/docs.gitRun
hugo serverin the site root directory. By default your site will be available at http://localhost:1313/. Now that you're serving your site locally, Hugo will watch for changes to the content and automatically refresh your site.Continue with the usual GitHub workflow to edit files, commit them, push the
changes up to your fork, and create a pull request.
Creating an issue
If you've found a problem in the docs, but you're not sure how to fix it yourself, please create an issue in the sops-docs repo. You can also create an issue about a specific page by clicking the Create Issue button in the top right hand corner of the page.
Useful resources
- Docsy user guide: All about Docsy, including how it manages navigation, look and feel, and multi-language support.
- Hugo documentation: Comprehensive reference for Hugo.
- Github Hello World!: A basic introduction to GitHub concepts and workflow.
File: content/en/docs/installation/_index.md
title: "Installation" weight: 30
Stable release
Binaries and packages of the latest stable release are available at
https://github.com/getsops/sops/releases.
Development branch
For the adventurous, unstable features are available in the
main branch, which you can install from source:
$ mkdir -p $GOPATH/src/github.com/getsops/sops/
$ git clone https://github.com/getsops/sops.git $GOPATH/src/github.com/getsops/sops/
$ cd $GOPATH/src/github.com/getsops/sops/
$ make install(requires Go >= 1.25)
If you don't have Go installed, set it up with:
$ {apt,yum,brew} install golang
$ echo 'export GOPATH=~/go' >> ~/.bashrc
$ source ~/.bashrc
$ mkdir $GOPATHOr whatever variation of the above fits your system and shell.
To use SOPS as a library, take a look at the decrypt
package.
File: content/en/docs/reference/_index.md
title: "References" weight: 50
Important information on types
YAML, JSON, ENV and INI type extensions
SOPS uses the file extension to decide which encryption method to use on
the file content. YAML, JSON, ENV, and INI files are treated as
trees of data, and key/values are extracted from the files to only
encrypt the leaf values. The tree structure is also used to check the
integrity of the file.
Therefore, if a file is encrypted using a specific format, it needs to
be decrypted in the same format. The easiest way to achieve this is to
conserve the original file extension after encrypting a file. For
example:
$ sops encrypt -i myfile.json
$ sops decrypt myfile.jsonIf 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:
$ sops encrypt myfile.json > myfile.json.enc
$ sops decrypt --input-type json myfile.json.encWhen operating on stdin, use the --input-type and --output-type
flags as follows:
$ cat myfile.json | sops decrypt --input-type json --output-type jsonJSON and JSON_binary indentation
SOPS indents JSON files by default using one tab. However, you can
change this default behaviour to use spaces by either using the
additional --indent=2 CLI option or by configuring .sops.yaml with
the code below.
The special value 0 disables indentation, and -1 uses a single tab.
stores:
json:
indent: 2
json_binary:
indent: 2YAML indentation
SOPS indents YAML files by default using 4 spaces. However, you can
change this default behaviour by either using the additional--indent=2 CLI option or by configuring .sops.yaml with:
stores:
yaml:
indent: 2๐ Note
The YAML emitter used by sops only supports values between 2 and 9. If
you specify 1, or 10 and larger, the indent will be 2.
YAML anchors
SOPS only supports a subset of YAML's many types. Encrypting YAML
files that contain strings, numbers and booleans will work fine, but
files that contain anchors will not work, because the anchors redefine
the structure of the file at load time.
This file will not work in SOPS:
bill-to: &id001
street: |
123 Tornado Alley
Suite 16
city: East Centerville
state: KS
ship-to: *id001SOPS uses the path to a value as additional data in the AEAD encryption,
and thus dynamic paths generated by anchors break the authentication
step.
JSON and TEXT file types do not support anchors and thus have no such
limitation.
YAML Streams
YAML supports having more than one "document" in a single file,
while formats like JSON do not. SOPS is able to handle both. This
means the following multi-document will be encrypted as expected:
---
data: foo
---
data: barNote that the sops metadata, i.e. the hash, etc, is computed for the
physical file rather than each internal "document".
Top-level arrays
YAML and JSON top-level arrays are not supported, because SOPS needs
a top-level sops key to store its metadata.
This file will not work in SOPS:
---
- some
- array
- elementsBut this one will work because the sops key can be added at the same
level as the data key.
data:
- some
- array
- elementsSimilarly, with JSON arrays, this document will not work:
[
"some",
"array",
"elements"
]But this one will work just fine:
{
"data": [
"some",
"array",
"elements"
]
}Config file format
This section describes the format of the SOPS config file.
The file must be named .sops.yaml (not .sops.yml!),
and SOPS will look for it in the current working directory and its parents,
using the first .sops.yaml file found.
A specific file can be set as the config file by passing the --config global option
or setting the SOPS_CONFIG environment variable.
The config file must be in the YAML format.
The following top-level keys are supported:
creation_rules: a list of creation rule objects.destination_rules: a list of destination rule objects.stores: configuration object for the stores.
The following subsections describe how these properties are used.
Creation rule object
A creation rule object has three types of keys:
- Keys that determine whether the creation rule matches.
- Keys that determine the (groups of) identities (keys) to encrypt with.
- Keys that determine which parts of and how a file is encrypted.
Matching
The keys related to file matching are:
path_regex: a regular expression matching files to encrypt.
If not specified, this creation rule will match all files.
Identities
One can either directly specify identities for a single key group, or specify a list of key groups.
If a list of key groups is given, the individual settings are ignored.
To directly specify a single key group, you can use the following keys:
kms(comma-separated string, or list of strings): list of AWS master keys.aws_profile(string): AWS profile to use for the AWS KMS keys.
Example:yamlcreation_rules: - kms: - arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e - arn:aws:kms:ap-southeast-1:656532927350:key/9006a8aa-0fa6-4c14-930e-a2dfb916de1d aws_profile: fooage(comma-separated string, or list of strings): list of Age public keys.
Example:yamlcreation_rules: - age: - age1s3cqcks5genc6ru8chl0hkkd04zmxvczsvdxq99ekffe4gmvjpzsedk23c - age1qe5lxzzeppw5k79vxn3872272sgy224g2nzqlzy3uljs84say3yqgvd0swpgp(comma-separated string, or list of strings): list of PGP/GPG key fingerprints.
Example:yamlcreation_rules: - pgp: - 85D77543B3D624B63CEA9E6DBC17301B491B3F21! - E60892BB9BD89A69F759A1A0A3D652173B763E8F!gcp_kms(comma-separated string, or list of strings): list of GCP KMS ResourceIDs.
Example:yamlcreation_rules: - gcp_kms: - projects/mygcproject/locations/global/keyRings/mykeyring/cryptoKeys/thekeyazure_keyvault(comma-separated string, or list of strings): list of Azure Key Vault resource identifiers.
Example:yamlcreation_rules: - azure_keyvault: - https://vault.url/keys/key-name/key-version # Key with version - https://vault.url/keys/key-name/ # key without version, the latest will be usedhc_vault_transit_uri(comma-separated string, or list of strings): list of HashiCorp Vault transit URIs.
Example:yamlcreation_rules: - hc_vault_transit_uri: - http://my.vault/v1/sops/keys/secondkeyhckms(list of strings): list of HuaweiCloud KMS key IDs (format:<region>:<key-uuid>).
Example:yamlcreation_rules: - hckms: - tr-west-1:abc12345-6789-0123-4567-890123456789 - tr-west-1:def67890-1234-5678-9012-345678901234
To specify a list of key groups, you can use the following key:
key_groups(list of key group objects): a list of key group objects.
See below for how such a resource should be represented.
Example:yamlcreation_rules: - key_groups: - kms: - arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e - arn:aws:kms:ap-southeast-1:656532927350:key/9006a8aa-0fa6-4c14-930e-a2dfb916de1d aws_profile: foo age: - age1s3cqcks5genc6ru8chl0hkkd04zmxvczsvdxq99ekffe4gmvjpzsedk23c - age1qe5lxzzeppw5k79vxn3872272sgy224g2nzqlzy3uljs84say3yqgvd0sw pgp: - 85D77543B3D624B63CEA9E6DBC17301B491B3F21! - E60892BB9BD89A69F759A1A0A3D652173B763E8F! gcp_kms: - projects/mygcproject/locations/global/keyRings/mykeyring/cryptoKeys/thekey azure_keyvault: - https://vault.url/keys/key-name/key-version # Key with version - https://vault.url/keys/key-name/ # key without version, the latest will be used hc_vault_transit_uri: - http://my.vault/v1/sops/keys/secondkey hckms: - tr-west-1:abc12345-6789-0123-4567-890123456789 merge: - pgp: - 85D77543B3D624B63CEA9E6DBC17301B491B3F21! - age: - age1s3cqcks5genc6ru8chl0hkkd04zmxvczsvdxq99ekffe4gmvjpzsedk23c - gcp_kms: - projects/mygcproject/locations/global/keyRings/mykeyring/cryptoKeys/thekey azure_keyvault: - https://vault.url/keys/key-name/key-version
Key group object
A key group contains multiple identities (keys), similar to a creation rule object.
Having more than one key group allows for the use of Shamir's secret sharing
which splits the file's encryption key up into multiple parts,
requiring more than one identity to access the file.
A key group supports the following keys:
kms(list of objects): list of AWS master keys.
Every object must have the following keys:arn(string): the ARN of the master key.role(string, can be omitted): the key's role.context(object mapping strings to strings): the key's encryption context.aws_profile(string): the AWS profile.
Example:
yaml- arn: arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500 role: arn:aws:iam::927034868273:role/sops-dev-xyz context: Environment: production Role: web-server aws_profile: foogcp_kms(list of objects): list of GCP KMS resource IDs.
Every object must have the following key:resource_id(string): the resource ID.
Example:
yaml- resource_id: projects/mygcproject/locations/global/keyRings/mykeyring/cryptoKeys/thekeyazure_keyvault(list of objects): list of Azure Key Vault resource identifiers.
Every object must have the following keys:vaultUrl(string): the vault URL.key(string): the key name.version(string, can be empty): the version of the key to use.
If empty, the latest key will be used on encryption.
Example:
yaml- vaultUrl: https://vault.url key: key-name version: key-version - vaultUrl: https://vault.url key: key-name version: ""hc_vault(list of strings): list of HashiCorp Vault transit URIs.hckms(list of objects): list of HuaweiCloud KMS key IDs.
Every object must have the following key:key_id(string): the key ID in format<region>:<key-uuid>.
Example:
yaml- key_id: tr-west-1:abc12345-6789-0123-4567-890123456789age(list of strings): list of Age public keys.pgp(list of strings): list of PGP/GPG key fingerprints.merge: a list of key group objects.
These will be merged (by concatenating the keys of the same type) into this key group.
This property allows for the concatenation of key groups using YAML anchors, aliases, and overrides.
Settings
The following keys configure encryption settings:
shamir_threshold(integer, default0): Must be0(disabled) or an integer greater or equal to 2.
Determines the number of key groups that must be present each to decrypt the file's key.mac_only_encrypted(boolean, defaultfalse): If set totrue, only encrypted strings will count towards the file's MAC.
If set tofalse, unencrypted values will also be part of the MAC computation.
The following keys configure the specific values in a file that should be encrypted.
Note that at most, one of these keys can be used.
unencrypted_suffix(string): A value is encrypted if its key does not end with this suffix.
All other values are encrypted.
Comments are always encrypted.encrypted_suffix(string): A value is encrypted if its key does end with this suffix.
All other values are not encrypted.
Comments are always encrypted.unencrypted_regex(string): A value is encrypted if its key does not match this regular expression.
All other values are encrypted.
Comments are always encrypted.encrypted_regex(string): A value is encrypted if its key matches this regular expression.
All other values are not encrypted.
Comments are always encrypted.unencrypted_comment_regex(string): A value or comment is not encrypted if it has a preceding comment
(or a trailing comment on the same line) matching this regular expression.
All other values and comments are encrypted.encrypted_comment_regex(string): A value or comment is encrypted if it has a preceding comment
(or a trailing comment on the same line) matching this regular expression.
All other comments and values are not encrypted.
The matching comment itself is not encrypted.
Destination rule object
Not yet documented.
Stores configuration object
The store configuration object can have the following keys:
dotenv: this is an object. Right now no keys are supported.ini: this is an object. Right now no keys are supported.json_binary: this is an object, supporting the following keys:indent(integer; default-1): the indentation to use in number of spaces.0means no indentation (everything will be in one line),
and-1means indentation by a single tabulator character.
json: this is an object, supporting the following keys:indent(integer; default-1): the indentation to use in number of spaces.0means no indentation (everything will be in one line),
and-1means indentation by a single tabulator character.
yaml: this is an object, supporting the following keys:indent(integer; default4): the indentation to use in number of spaces.
The YAML emitter used by sops only supports values between2and9.
If you specify1, or10and larger, the indent will be2.
Encryption Protocol
When SOPS creates a file, it generates a random 256 bit data key and
asks each KMS and PGP master key to encrypt the data key. The encrypted
version of the data key is stored in the sops metadata undersops.kms and sops.pgp.
For KMS:
sops:
kms:
- enc: CiC6yCOtzsnFhkfdIslYZ0bAf//gYLYCmIu87B3sy/5yYxKnAQEBAQB4usgjrc7JxYZH3SLJWGdGwH//4GC2ApiLvOwd7Mv+cmMAAAB+MHwGCSqGSIb3DQEHBqBvMG0CAQAwaAYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAyGdRODuYMHbA8Ozj8CARCAO7opMolPJUmBXd39Zlp0L2H9fzMKidHm1vvaF6nNFq0ClRY7FlIZmTm4JfnOebPseffiXFn9tG8cq7oi
enc_ts: 1439568549.245995
arn: arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27eFor PGP:
sops:
pgp:
- fp: 85D77543B3D624B63CEA9E6DBC17301B491B3F21
created_at: 1441570391.930042
enc: |
-----BEGIN PGP MESSAGE-----
Version: GnuPG v1
hQIMA0t4uZHfl9qgAQ//UvGAwGePyHuf2/zayWcloGaDs0MzI+zw6CmXvMRNPUsA
pAgRKczJmDu4+XzN+cxX5Iq9xEWIbny9B5rOjwTXT3qcUYZ4Gkzbq4MWkjuPp/Iv
qO4MJaYzoH5YxC4YORQ2LvzhA2YGsCzYnljmatGEUNg01yJ6r5mwFwDxl4Nc80Cn
RwnHuGExK8j1jYJZu/juK1qRbuBOAuruIPPWVdFB845PA7waacG1IdUW3ZtBkOy3
O0BIfG2ekRg0Nik6sTOhDUA+l2bewCcECI8FYCEjwHm9Sg5cxmP2V5m1mby+uKAm
kewaoOyjbmV1Mh3iI1b/AQMr+/6ZE9MT2KnsoWosYamFyjxV5r1ZZM7cWKnOT+tu
KOvGhTV1TeOfVpajNTNwtV/Oyh3mMLQ0F0HgCTqomQVqw5+sj7OWAASuD3CU/dyo
pcmY5Qe0TNL1JsMNEH8LJDqSh+E0hsUxdY1ouVsg3ysf6mdM8ciWb3WRGxih1Vmf
unfLy8Ly3V7ZIC8EHV8aLJqh32jIZV4i2zXIoO4ZBKrudKcECY1C2+zb/TziVAL8
qyPe47q8gi1rIyEv5uirLZjgpP+JkDUgoMnzlX334FZ9pWtQMYW4Y67urAI4xUq6
/q1zBAeHoeeeQK+YKDB7Ak/Y22YsiqQbNp2n4CKSKAE4erZLWVtDvSp+49SWmS/S
XgGi+13MaXIp0ecPKyNTBjF+NOw/I3muyKr8EbDHrd2XgIT06QXqjYLsCb1TZ0zm
xgXsOTY3b+ONQ2zjhcovanDp7/k77B+gFitLYKg4BLZsl7gJB12T8MQnpfSmRT4=
=oJgS
-----END PGP MESSAGE-----SOPS then opens a text editor on the newly created file. The user adds
data to the file and saves it when done.
Upon save, SOPS browses the entire file as a key/value tree. Every time
SOPS encounters a leaf value (a value that does not have children), it
encrypts the value with AES256_GCM using the data key and a 256 bit
random initialization vector.
Each file uses a single data key to encrypt all values of a document,
but each value receives a unique initialization vector and has unique
authentication data.
Additional data is used to guarantee the integrity of the encrypted data
and of the tree structure: when encrypting the tree, key names are
concatenated into a byte string that is used as AEAD additional data
(aad) when encrypting values. We expect that keys do not carry sensitive
information, and keeping them in cleartext allows for better diff and
overall readability.
Any valid KMS or PGP master key can later decrypt the data key and
access the data.
Multiple master keys allow for sharing encrypted files without sharing
master keys, and provide a disaster recovery solution. The recommended
way to use SOPS is to have two KMS master keys in different regions and
one PGP public key with the private key stored offline. If, by any
chance, both KMS master keys are lost, you can always recover the
encrypted data using the PGP private key.
Message Authentication Code
In addition to authenticating branches of the tree using keys as
additional data, SOPS computes a MAC on all the values to ensure that no
value has been added or removed fraudulently. The MAC is stored
encrypted with AES_GCM and the data key under tree -> sops -> mac.
This behavior can be modified using --mac-only-encrypted flag or.sops.yaml config file which makes SOPS compute a MAC only over values
it encrypted and not all values.
File: content/en/docs/security/_index.md
title: "Security" weight: 60
Reporting security issues
Please report any security issues privately using GitHub's advisory
form.
Motivation
๐ A note from the maintainers
This section was written by the original authors of SOPS while they
were working at Mozilla. It is kept here for historical reasons and to
provide technical background on the project. It is not necessarily
representative of the views of the current maintainers, nor are they
currently affiliated with Mozilla.
Automating the distribution of secrets and credentials to components of
an infrastructure is a hard problem. We know how to encrypt secrets and
share them between humans, but extending that trust to systems is
difficult. Particularly when these systems follow devops principles and
are created and destroyed without human intervention. The issue boils
down to establishing the initial trust of a system that just joined the
infrastructure, and providing it access to the secrets it needs to
configure itself.
The initial trust
In many infrastructures, even highly dynamic ones, the initial trust is
established by a human. An example is seen in Puppet by the way
certificates are issued: when a new system attempts to join a
Puppetmaster, an administrator must, by default, manually approve the
issuance of the certificate the system needs. This is cumbersome, and
many puppetmasters are configured to auto-sign new certificates to work
around that issue. This is obviously not recommended and far from ideal.
AWS provides a more flexible approach to trusting new systems. It uses a
powerful mechanism of roles and identities. In AWS, it is possible to
verify that a new system has been granted a specific role at creation,
and it is possible to map that role to specific resources. Instead of
trusting new systems directly, the administrator trusts the AWS
permission model and its automation infrastructure. As long as AWS keys
are safe, and the AWS API is secure, we can assume that trust is
maintained and systems are who they say they are.
KMS, Trust and secrets distribution
Using the AWS trust model, we can create fine grained access controls to
Amazon's Key Management Service (KMS). KMS is a service that encrypts
and decrypts data with AES_GCM, using keys that are never visible to
users of the service. Each KMS master key has a set of role-based access
controls, and individual roles are permitted to encrypt or decrypt using
the master key. KMS helps solve the problem of distributing keys, by
shifting it into an access control problem that can be solved using
AWS's trust model.
Operational requirements
When Mozilla's Services Operations team started revisiting the issue of
distributing secrets to EC2 instances, we set a goal to store these
secrets encrypted until the very last moment, when they need to be
decrypted on target systems. Not unlike many other organizations that
operate sufficiently complex automation, we found this to be a hard
problem with a number of prerequisites:
- Secrets must be stored in YAML files for easy integration into hiera
- Secrets must be stored in GIT, and when a new CloudFormation stack
is built, the current HEAD is pinned to the stack. (This allows
secrets to be changed in GIT without impacting the current stack
that may autoscale). - Entries must be encrypted separately. Encrypting entire files as
blobs makes git conflict resolution almost impossible. Encrypting
each entry separately is much easier to manage. - Secrets must always be encrypted on disk (admin laptop, upstream git
repo, jenkins and S3) and only be decrypted on the target systems
SOPS can be used to encrypt YAML, JSON, ENV, INI, and BINARY files. In BINARY mode,
the content of the file is treated as a blob, the same way PGP would
encrypt an entire file. In YAML, JSON, ENV, and INI modes, however, the content of
the file is manipulated as a tree where keys are stored in cleartext,
and values are encrypted. hiera-eyaml does something similar, and over
the years we learned to appreciate its benefits, namely:
- diffs are meaningful. If a single value of a file is modified, only
that value will show up in the diff. The diff is still limited to
only showing encrypted data, but that information is already more
granular that indicating that an entire file has changed. - conflicts are easier to resolve. If multiple users are working on
the same encrypted files, as long as they don't modify the same
values, changes are easy to merge. This is an improvement over the
PGP encryption approach where unsolvable conflicts often happen when
multiple users work on the same file.
OpenPGP integration
OpenPGP gets a lot of bad press for being an outdated crypto protocol,
and while true, what really made us look for alternatives is the
difficulty of managing and distributing keys to systems. With KMS, we
manage permissions to an API, not keys, and that's a lot easier to do.
But PGP is not dead yet, and we still rely on it heavily as a backup
solution: all our files are encrypted with KMS and with one PGP public
key, with its private key stored securely for emergency decryption in
the event that we lose all our KMS master keys.
SOPS can be used without KMS entirely, the same way you would use an
encrypted PGP file: by referencing the pubkeys of each individual who
has access to the file. It can easily be done by providing SOPS with a
comma-separated list of public keys when creating a new file:
$ sops edit --pgp "E60892BB9BD89A69F759A1A0A3D652173B763E8F,84050F1D61AF7C230A12217687DF65059EF093D3,85D77543B3D624B63CEA9E6DBC17301B491B3F21" mynewfile.yamlThreat Model
The security of the data stored using SOPS is as strong as the weakest
cryptographic mechanism. Values are encrypted using AES256_GCM which is
the strongest symmetric encryption algorithm known today. Data keys are
encrypted in either KMS, which also uses AES256_GCM, or PGP which uses
either RSA or ECDSA keys.
Going from the most likely to the least likely, the threats are as
follows:
Compromised AWS credentials grant access to KMS master key
An attacker with access to an AWS console can grant itself access to one
of the KMS master keys used to encrypt a sops data key. This threat
should be mitigated by protecting AWS accesses with strong controls,
such as multi-factor authentication, and also by performing regular
audits of permissions granted to AWS users.
Compromised PGP key
PGP keys are routinely mishandled, either because owners copy them from
machine to machine, or because the key is left forgotten on an unused
machine an attacker gains access to. When using PGP encryption, SOPS
users should take special care of PGP private keys, and store them on
smart cards or offline as often as possible.
Factorized RSA key
SOPS doesn't apply any restriction on the size or type of PGP keys. A
weak PGP keys, for example 512 bits RSA, could be factorized by an
attacker to gain access to the private key and decrypt the data key.
Users of SOPS should rely on strong keys, such as 2048+ bits RSA keys,
or 256+ bits ECDSA keys.
Weak AES cryptography
A vulnerability in AES256_GCM could potentially leak the data key or the
KMS master key used by a SOPS encrypted file. While no such
vulnerability exists today, we recommend that users keep their encrypted
files reasonably private.
File: content/en/docs/usage/advanced/_index.md
title: "Advanced usage" weight: 60
Encrypting and decrypting from other programs
When using sops in scripts or from other programs, there are often
situations where you do not want to write encrypted or decrypted data to
disk. The best way to avoid this is to pass data to SOPS via stdin, and
to let SOPS write data to stdout. By default, the encrypt and decrypt
operations write data to stdout already. To pass data via stdin, you
need to not provide an input filename. For encryption, you also must
provide the --filename-override option with the file's filename. The
filename will be used to determine the input and output types, and to
select the correct creation rule.
The simplest way to decrypt data from stdin is as follows:
$ cat encrypted-data | sops decrypt > decrypted-dataBy default, sops determines the input and output format from the
provided filename. Since in this case, no filename is provided, sops
will use the binary store which expects JSON input and outputs binary
data on decryption. This is often not what you want.
To avoid this, you can either provide a filename with --filename-override,
or explicitly control the input and output formats by passing--input-type and --output-type as appropriate:
$ cat encrypted-data | sops decrypt --filename-override filename.yaml > decrypted-data
$ cat encrypted-data | sops decrypt --input-type yaml --output-type yaml > decrypted-dataIn both cases, sops will assume that the data you provide is in YAML
format, and will encode the decrypted data in YAML as well. The second
form allows to use different formats for input and output.
To encrypt, it is important to note that SOPS also uses the filename to
look up the correct creation rule from .sops.yaml. Therefore, you must
provide the --filename-override parameter which allows you to tell
SOPS which filename to use to match creation rules:
$ echo 'foo: bar' | sops encrypt --filename-override path/filename.sops.yaml > encrypted-dataSOPS 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, the input store type can be adjusted by passing --input-type,
and the output store type by passing --output-type:
$ echo foo=bar | sops encrypt --filename-override path/filename.sops.yaml --input-type dotenv > encrypted-dataAuditing
Sometimes, users want to be able to tell what files were accessed by
whom in an environment they control. For this reason, SOPS can generate
audit logs to record activity on encrypted files. When enabled, SOPS
will write a log entry into a pre-configured PostgreSQL database when a
file is decrypted. The log includes a timestamp, the username SOPS is
running as, and the file that was decrypted.
In order to enable auditing, you must first create the database and
credentials using the schema found in audit/schema.sql. This schema
defines the tables that store the audit events and a role named sops
that only has permission to add entries to the audit event tables. The
default password for the role sops is sops. You should change this
password.
Once you have created the database, you have to tell SOPS how to connect
to it. Because we don't want users of SOPS to be able to control
auditing, the audit configuration file location is not configurable, and
must be at /etc/sops/audit.yaml. This file should have strict
permissions such that only the root user can modify it.
For example, to enable auditing to a PostgreSQL database named sops
running on localhost, using the user sops and the password sops,/etc/sops/audit.yaml should have the following contents:
backends:
postgres:
- connection_string: "postgres://sops:sops@localhost/sops?sslmode=verify-full"You can find more information on the connection_string format in the
PostgreSQL
docs.
Under the postgres map entry in the above YAML is a list, so one can
provide more than one backend, and SOPS will log to all of them:
backends:
postgres:
- connection_string: "postgres://sops:sops@localhost/sops?sslmode=verify-full"
- connection_string: "postgres://sops:sops@remotehost/sops?sslmode=verify-full"Passing Secrets to Other Processes
In addition to writing secrets to standard output and to files on disk,
SOPS has two commands for passing decrypted secrets to a new process:exec-env and exec-file. These commands will place all output into
the environment of a child process and into a temporary file,
respectively. For example, if a program looks for credentials in its
environment, exec-env can be used to ensure that the decrypted
contents are available only to this process and never written to disk.
# print secrets to stdout to confirm values
$ sops decrypt out.json
{
"database_password": "jf48t9wfw094gf4nhdf023r",
"AWS_ACCESS_KEY_ID": "AKIAIOSFODNN7EXAMPLE",
"AWS_SECRET_KEY": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
# decrypt out.json and run a command
# the command prints the environment variable and runs a script that uses it
$ sops exec-env out.json 'echo secret: $database_password; ./database-import'
secret: jf48t9wfw094gf4nhdf023r
# launch a shell with the secrets available in its environment
$ sops exec-env out.json 'sh'
sh-3.2# echo $database_password
jf48t9wfw094gf4nhdf023r
# the secret is not accessible anywhere else
sh-3.2$ exit
$ echo your password: $database_password
your password:If the command you want to run only operates on files, you can useexec-file instead. By default, SOPS will use a FIFO to pass the
contents of the decrypted file to the new program. Using a FIFO, secrets
are only passed in memory which has two benefits: the plaintext secrets
never touch the disk, and the child process can only read the secrets
once. In contexts where this won't work, eg platforms like Windows
where FIFOs unavailable or secret files that need to be available to the
child process longer term, the --no-fifo flag can be used to instruct
SOPS to use a traditional temporary file that will get cleaned up once
the process is finished executing. exec-file behaves similar tofind(1) in that {} is used as a placeholder in the command which
will be substituted with the temporary file path (whether a FIFO or an
actual file).
# operating on the same file as before, but as a file this time
$ sops exec-file out.json 'echo your temporary file: {}; cat {}'
your temporary file: /tmp/.sops894650499/tmp-file
{
"database_password": "jf48t9wfw094gf4nhdf023r",
"AWS_ACCESS_KEY_ID": "AKIAIOSFODNN7EXAMPLE",
"AWS_SECRET_KEY": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
# launch a shell with a variable TMPFILE pointing to the temporary file
$ sops exec-file --no-fifo out.json 'TMPFILE={} sh'
sh-3.2$ echo $TMPFILE
/tmp/.sops506055069/tmp-file291138648
sh-3.2$ cat $TMPFILE
{
"database_password": "jf48t9wfw094gf4nhdf023r",
"AWS_ACCESS_KEY_ID": "AKIAIOSFODNN7EXAMPLE",
"AWS_SECRET_KEY": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
sh-3.2$ ./program --config $TMPFILE
sh-3.2$ exit
# try to open the temporary file from earlier
$ cat /tmp/.sops506055069/tmp-file291138648
cat: /tmp/.sops506055069/tmp-file291138648: No such file or directoryAdditionally, on unix-like platforms, both exec-env and exec-file
support dropping privileges before executing the new program via the--user flag. This is particularly useful in cases where the
encrypted file is only readable by root, but the target program does not
need root privileges to function. This flag should be used where
possible for added security.
To overwrite the default file name (tmp-file) in exec-file use the--filename <filename> parameter.
# the encrypted file can't be read by the current user
$ cat out.json
cat: out.json: Permission denied
# execute sops as root, decrypt secrets, then drop privileges
$ sudo sops exec-env --user nobody out.json 'sh'
sh-3.2$ echo $database_password
jf48t9wfw094gf4nhdf023r
# dropped privileges, still can't load the original file
sh-3.2$ id
uid=4294967294(nobody) gid=4294967294(nobody) groups=4294967294(nobody)
sh-3.2$ cat out.json
cat: out.json: Permission deniedUsing SOPS' Key Service
There are situations where you might want to run SOPS on a machine that
doesn't have direct access to encryption keys such as PGP keys. Thesops key service allows you to forward a socket so that SOPS can
access encryption keys stored on a remote machine. This is similar to
GPG Agent, but more portable.
SOPS uses a client-server approach to encrypting and decrypting the data
key. By default, SOPS runs a local key service in-process. SOPS uses a
key service client to send an encrypt or decrypt request to a key
service, which then performs the operation. The requests are sent using
gRPC and Protocol Buffers. The requests contain an identifier for the
key they should perform the operation with, and the plaintext or
encrypted data key. The requests do not contain any cryptographic keys,
public or private.
WARNING: the key service connection currently does not use any sort of
authentication or encryption. Therefore, it is recommended that you make
sure the connection is authenticated and encrypted in some other way,
for example through an SSH tunnel.
Whenever we try to encrypt or decrypt a data key, SOPS will try to do so
first with the local key service (unless it's disabled), and if that
fails, it will try all other remote key services until one succeeds.
You can start a key service server by running sops keyservice.
You can specify the key services the sops binary uses with the--keyservice option. This flag can be specified more than once, so you can
use multiple key services. Alternatively, a single key service can be specified
by setting the SOPS_KEYSERVICE environment variable. The local key service
can be disabled with --enable-local-keyservice=false or by setting theSOPS_ENABLE_LOCAL_KEYSERVICE environment variable to false.
For example, to decrypt a file using both the local key service and the
key service exposed on the unix socket located in /tmp/sops.sock, you
can run:
$ sops decrypt --keyservice unix:///tmp/sops.sock file.yamlAnd 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:
$ sops decrypt --enable-local-keyservice=false --keyservice unix:///tmp/sops.sock file.yamlFile: content/en/docs/usage/common-operations/_index.md
title: "Common operations" weight: 30
Examples
Take a look into the examples
folder for detailed
use cases of SOPS in a CI environment. The section below describes
specific tips for common use cases.
Saving Output to a File
By default SOPS just dumps all the output to the standard output. We can
use the --output flag followed by a filename to save the output to the
file specified. Beware using both --in-place and --output flags will
result in an error.
Creating a new file
The command below creates a new file with a data key encrypted by KMS
and PGP.
$ sops edit --kms "arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500" --pgp C9CAB0AF1165060DB58D6D6B2653B624D620786D /path/to/new/file.yamlEncrypting an existing file
Similar to the previous command, we tell SOPS to use one KMS and one PGP
key. The path points to an existing cleartext file, so we give sops
the flag -e to 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 encrypt /path/to/existing/file.yaml > /path/to/new/encrypted/file.yamlDecrypt the file with -d.
$ sops decrypt /path/to/new/encrypted/file.yamlEncrypt or decrypt a file in place
Rather than redirecting the output of -e or -d, sops can replace
the original file after encrypting or decrypting it.
# file.yaml is in cleartext
$ sops encrypt -i /path/to/existing/file.yaml
# file.yaml is now encrypted
$ sops decrypt -i /path/to/existing/file.yaml
# file.yaml is back in cleartextEncrypting binary files
SOPS primary use case is encrypting YAML, JSON, ENV, and INI configuration files,
but it also has the ability to manage binary files. When encrypting a
binary, SOPS will read the data as bytes, encrypt it, store the
encrypted base64 under tree['data'] and write the result as JSON.
Note that the base64 encoding of encrypted data can actually make the
encrypted file larger than the cleartext one.
In-place encryption/decryption also works on binary files.
$ dd if=/dev/urandom of=/tmp/somerandom bs=1024
count=512
512+0 records in
512+0 records out
524288 bytes (524 kB) copied, 0.0466158 s, 11.2 MB/s
$ sha512sum /tmp/somerandom
9589bb20280e9d381f7a192000498c994e921b3cdb11d2ef5a986578dc2239a340b25ef30691bac72bdb14028270828dad7e8bd31e274af9828c40d216e60cbe /tmp/somerandom
$ sops encrypt -i /tmp/somerandom
please wait while a data encryption key is being generated and stored securely
$ sops decrypt -i /tmp/somerandom
$ sha512sum /tmp/somerandom
9589bb20280e9d381f7a192000498c994e921b3cdb11d2ef5a986578dc2239a340b25ef30691bac72bdb14028270828dad7e8bd31e274af9828c40d216e60cbe /tmp/somerandomExtract a sub-part of a document tree
SOPS can extract a specific part of a YAML or JSON document, by provided
the path in the --extract command line flag. This is useful to extract
specific values, like keys, without needing an extra parser.
$ sops decrypt --extract '["app2"]["key"]' ~/git/svc/sops/example.yaml
-----BEGIN RSA PRIVATE KEY-----
MIIBPAIBAAJBAPTMNIyHuZtpLYc7VsHQtwOkWYobkUblmHWRmbXzlAX6K8tMf3Wf
ImcbNkqAKnELzFAPSBeEMhrBN0PyOC9lYlMCAwEAAQJBALXD4sjuBn1E7Y9aGiMz
bJEBuZJ4wbhYxomVoQKfaCu+kH80uLFZKoSz85/ySauWE8LgZcMLIBoiXNhDKfQL
vHECIQD6tCG9NMFWor69kgbX8vK5Y+QL+kRq+9HK6yZ9a+hsLQIhAPn4Ie6HGTjw
fHSTXWZpGSan7NwTkIu4U5q2SlLjcZh/AiEA78NYRRBwGwAYNUqzutGBqyXKUl4u
Erb0xAEyVV7e8J0CIQC8VBY8f8yg+Y7Kxbw4zDYGyb3KkXL10YorpeuZR4LuQQIg
bKGPkMM4w5blyE1tqGN0T7sJwEx+EUOgacRNqM2ljVA=
-----END RSA PRIVATE KEY-----The tree path syntax uses regular python dictionary syntax, without the
variable name. Extract keys by naming them, and array elements by
numbering them.
$ sops decrypt --extract '["an_array"][1]' ~/git/svc/sops/example.yaml
secretuser2Set a sub-part in a document tree
SOPS can set a specific part of a YAML or JSON document, by providing
the path and value in the set command. This is useful to set specific
values, like keys, without needing an editor.
$ sops set ~/git/svc/sops/example.yaml '["app2"]["key"]' '"app2keystringvalue"'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.
$ sops set ~/git/svc/sops/example.yaml '["an_array"][1]' '"secretuser2"'The value must be formatted as json.
$ sops set ~/git/svc/sops/example.yaml '["an_array"][1]' '{"uid1":null,"uid2":1000,"uid3":["bob"]}'You can also provide the value from a file or stdin:
# Provide the value from a file
$ echo '{"uid1":null,"uid2":1000,"uid3":["bob"]}' > /tmp/example-value
$ sops set --value-file ~/git/svc/sops/example.yaml '["an_array"][1]' /tmp/example-value
# Provide the value from stdin
$ echo '{"uid1":null,"uid2":1000,"uid3":["bob"]}' | sops set --value-stdin ~/git/svc/sops/example.yaml '["an_array"][1]'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.
$ 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.
$ sops unset ~/git/svc/sops/example.yaml '["an_array"][1]'Showing diffs in cleartext in git
You most likely want to store encrypted files in a version controlled
repository. SOPS can be used with git to decrypt files when showing
diffs between versions. This is very handy for reviewing changes or
visualizing history.
To configure SOPS to decrypt files during diff, create a.gitattributes file at the root of your repository that contains a
filter and a command.
*.yaml diff=sopsdifferHere we only care about YAML files. sopsdiffer is an arbitrary name
that we map to a SOPS command in the git configuration file of the
repository.
$ git config diff.sopsdiffer.textconv "sops decrypt"
$ grep -A 1 sopsdiffer .git/config
[diff "sopsdiffer"]
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 git client interfaces, because they call git diff under
the hood!
Encrypting only parts of a file
๐ Note
This only works on YAML, JSON, ENV, and INI files, not on BINARY files.
By default, SOPS encrypts all the values of a YAML, JSON, ENV, or INI file and
leaves the keys in cleartext. In some instances, you may want to exclude
some values from being encrypted. This can be accomplished by adding the
suffix _unencrypted to any key of a file. When set, all values
underneath the key that set the _unencrypted suffix will be left in
cleartext.
Note that, while in cleartext, unencrypted content is still added to the
checksum of the file, and thus cannot be modified outside of SOPS
without breaking the file integrity check. This behavior can be modified
using --mac-only-encrypted flag or .sops.yaml config file which
makes SOPS compute a MAC only over values it encrypted and not all
values.
The unencrypted suffix can be set to a different value using the--unencrypted-suffix option.
Conversely, you can opt in to only encrypt some values in a YAML or JSON
file, by adding a chosen suffix to those keys and passing it to the--encrypted-suffix option.
A third method is to use the --encrypted-regex which will only encrypt
values under keys that match the supplied regular expression. For
example, this command:
$ sops encrypt --encrypted-regex '^(data|stringData)$' k8s-secrets.yamlwill 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 navigate the file, like metadata which contains the
secrets' names.
Conversely, you can opt in to only leave certain keys without encrypting
by using the --unencrypted-regex option, which will leave the values
unencrypted of those keys that match the supplied regular expression.
For example, this command:
$ sops encrypt --unencrypted-regex '^(description|metadata)$' k8s-secrets.yamlwill 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 six options
--unencrypted-suffix,--encrypted-suffix,--encrypted-regex,--unencrypted-regex,--encrypted-comment-regex,
and--unencrypted-comment-regexare mutually exclusive and
cannot all be used in the same file.
File: content/en/docs/usage/first-steps/_index.md
title: "First steps" weight: 10
First steps
For a quick presentation of SOPS, check out this Youtube tutorial:
If you're using AWS KMS, create one or multiple master keys in the IAM
console and export them, comma separated, in the SOPS_KMS_ARN env
variable. It is recommended to use at least two master keys in different
regions.
export SOPS_KMS_ARN="arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e,arn:aws:kms:ap-southeast-1:656532927350:key/9006a8aa-0fa6-4c14-930e-a2dfb916de1d"SOPS uses aws-sdk-go-v2 to
communicate with AWS KMS. It will automatically read the credentials
from the ~/.aws/credentials file which can be created with theaws configure command.
An example of the ~/.aws/credentials file is shown below:
$ cat ~/.aws/credentials
[default]
aws_access_key_id = AKI.....
aws_secret_access_key = mw......In addition to the ~/.aws/credentials file, you can also use theAWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables to
specify your credentials:
export AWS_ACCESS_KEY_ID="AKI......"
export AWS_SECRET_ACCESS_KEY="mw......"For more information and additional environment variables, see
specifying
credentials.
If you want to use PGP, export the fingerprints of the public keys,
comma separated, in the SOPS_PGP_FP env variable.
export SOPS_PGP_FP="85D77543B3D624B63CEA9E6DBC17301B491B3F21,E60892BB9BD89A69F759A1A0A3D652173B763E8F"๐ Note
You can use both PGP and KMS simultaneously.
Then simply call sops edit with a file path as argument. It will
handle the encryption/decryption transparently and open the cleartext
file in an editor
$ sops edit mynewtestfile.yaml
mynewtestfile.yaml doesn't exist, creating it.
please wait while an encryption key is being generated and stored in a secure fashion
file written to mynewtestfile.yamlEditing will happen in whatever $SOPS_EDITOR or $EDITOR is set to,
or, if it's not set, in vim, nano, or vi.
Keep in mind that SOPS will wait for the editor to exit,
and then try to reencrypt the file. Some GUI editors (atom, sublime)
spawn a child process and then exit immediately. They usually have an
option to wait for the main editor window to be closed before exiting.
See #127 for more
information.
The resulting encrypted file looks like this:
myapp1: ENC[AES256_GCM,data:Tr7o=,iv:1=,aad:No=,tag:k=]
app2:
db:
user: ENC[AES256_GCM,data:CwE4O1s=,iv:2k=,aad:o=,tag:w==]
password: ENC[AES256_GCM,data:p673w==,iv:YY=,aad:UQ=,tag:A=]
# private key for secret operations in app2
key: |-
ENC[AES256_GCM,data:Ea3kL5O5U8=,iv:DM=,aad:FKA=,tag:EA==]
an_array:
- ENC[AES256_GCM,data:v8jQ=,iv:HBE=,aad:21c=,tag:gA==]
- ENC[AES256_GCM,data:X10=,iv:o8=,aad:CQ=,tag:Hw==]
- ENC[AES256_GCM,data:KN=,iv:160=,aad:fI4=,tag:tNw==]
sops:
kms:
- created_at: 1441570389.775376
enc: CiC....Pm1Hm
arn: arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e
- created_at: 1441570391.925734
enc: Ci...awNx
arn: arn:aws:kms:ap-southeast-1:656532927350:key/9006a8aa-0fa6-4c14-930e-a2dfb916de1d
pgp:
- fp: 85D77543B3D624B63CEA9E6DBC17301B491B3F21
created_at: 1441570391.930042
enc: |
-----BEGIN PGP MESSAGE-----
hQIMA0t4uZHfl9qgAQ//UvGAwGePyHuf2/zayWcloGaDs0MzI+zw6CmXvMRNPUsA
...=oJgS
-----END PGP MESSAGE-----A copy of the encryption/decryption key is stored securely in each KMS
and PGP block. As long as one of the KMS or PGP method is still usable,
you will be able to access your data.
To decrypt a file in a cat fashion, use the -d flag:
$ sops decrypt mynewtestfile.yamlSOPS encrypted files contain the necessary information to decrypt their
content. All a user of SOPS needs is valid AWS credentials and the
necessary permissions on KMS keys.
Given that, the only command a SOPS user needs is:
$ sops edit <file><file> will be opened, decrypted, passed to a text
editor (vim by default), encrypted if modified, and saved back to its
original location. All of these steps, apart from the actual editing,
are transparent to the user.
The order in which available decryption methods are tried can be
specified with --decryption-order option or SOPS_DECRYPTION_ORDER
environment variable as a comma separated list. The default order isage,pgp. Offline methods are tried first and then the remaining ones.
File: content/en/docs/usage/identities/age/_index.md
title: "Age" weight: 20
Encrypting using age
age is a simple, modern, and secure tool
for encrypting files. It's recommended to use age over PGP, if
possible.
You can encrypt a file for one or more age recipients (comma separated)
using the --age option or the SOPS_AGE_RECIPIENTS environment
variable:
$ sops encrypt --age age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw test.yaml > test.enc.yamlWhen 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.
Linux
- Looks for
keys.txtin$XDG_CONFIG_HOME/sops/age/keys.txt; - Falls back to
$HOME/.config/sops/age/keys.txtif$XDG_CONFIG_HOMEisn't set.
- Looks for
macOS
- Looks for
keys.txtin$XDG_CONFIG_HOME/sops/age/keys.txt; - Falls back to
$HOME/Library/Application Support/sops/age/keys.txtif$XDG_CONFIG_HOMEisn't set.
- Looks for
Windows
- Looks for
keys.txtin%AppData%\\sops\\age\\keys.txt.
- Looks for
You can override the default lookup by:
- setting the environment variable SOPS_AGE_KEY_FILE;
- setting the SOPS_AGE_KEY environment variable;
- providing a command to output the age keys by setting the SOPS_AGE_KEY_CMD environment variable.
This command can read the age recipient for which to return the private key from the SOPS_AGE_RECIPIENT environment variable.
The contents of this key file should be a list of age X25519 identities,
one per line. Lines beginning with # are considered comments and
ignored. Each identity will be tried in sequence until one is able to
decrypt the data.
Encrypting with SSH keys via age is also supported by SOPS.
You can use SSH public keys (ssh-ed25519 AAAA..., ssh-rsa AAAA...)
as age recipients when encrypting a file.
When decrypting a file, SOPS will attempt to source the SSH private key as follows:
From the path specified in environment variable SOPS_AGE_SSH_PRIVATE_KEY_FILE.
From the output of the command specified in environment variable SOPS_AGE_SSH_PRIVATE_KEY_CMD.
๐ Note
The output of this command must provide a key that is not password protected.
From
~/.ssh/id_ed25519.From
~/.ssh/id_rsa.
Note that only ssh-rsa and ssh-ed25519 are supported.
A list of age recipients can be added to the .sops.yaml:
creation_rules:
- age: >-
age1s3cqcks5genc6ru8chl0hkkd04zmxvczsvdxq99ekffe4gmvjpzsedk23c,
age1qe5lxzzeppw5k79vxn3872272sgy224g2nzqlzy3uljs84say3yqgvd0swIt is also possible to use updatekeys, when adding or removing age recipients. For example:
$ sops updatekeys secret.enc.yaml
2022/02/09 16:32:02 Syncing keys for file /iac/solution1/secret.enc.yaml
The following changes will be made to the file's groups:
Group 1
age1s3cqcks5genc6ru8chl0hkkd04zmxvczsvdxq99ekffe4gmvjpzsedk23c
+++ age1qe5lxzzeppw5k79vxn3872272sgy224g2nzqlzy3uljs84say3yqgvd0sw
Is this okay? (y/n):y
2022/02/09 16:32:04 File /iac/solution1/secret.enc.yaml synced with new keys