File sops-3.7.3.obscpio of Package sops
07070100000000000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001500000000sops-3.7.3/.circleci07070100000001000081A400000000000000000000000162794F9300000EB0000000000000000000000000000000000000002000000000sops-3.7.3/.circleci/config.ymlversion: 2.1
workflows:
build-and-deploy:
jobs:
- build
- push:
filters:
tags:
only: /^v.*/
branches:
ignore: /.*/
jobs:
build:
working_directory: /go/src/go.mozilla.org/sops
docker:
- image: circleci/golang:1.13
resource_class: large
steps:
- checkout
- setup_remote_docker:
version: 20.10.11
- run:
name: Build containers
command: |
docker build -t mozilla/sops .
docker tag mozilla/sops "mozilla/sops:$CIRCLE_SHA1"
- run:
name: Build containers (alpine)
command: |
# Just to ensure the container can be built.
docker build -f Dockerfile.alpine -t mozilla/sops:alpine .
push:
machine:
image: ubuntu-2004:202111-02
resource_class: large
steps:
- checkout
- run:
name: semver check
command: |
MAJOR=$(echo ${CIRCLE_TAG#v} | cut -d"." -f1)
MINOR=$(echo ${CIRCLE_TAG#v} | cut -d"." -f2)
PATCH=$(echo ${CIRCLE_TAG#v} | cut -d"." -f3)
echo "export MAJOR=${MAJOR}" >> $BASH_ENV
echo "export MINOR=${MINOR}" >> $BASH_ENV
echo "export PATCH=${PATCH}" >> $BASH_ENV
if [ -z $MAJOR ];then
cat \<< EOF
Failure Info:
This job uses the semver from the git TAG as the public version to publish.
- This should only run on workflows triggered by a tag.
- The tag name should be a semver like 'v1.2.3'
- The version should follow conventions documented at https://github.com/fsaintjacques/semver-tool
EOF
exit 1
fi
- run:
name: Build containers
command: |
docker build -t mozilla/sops .
docker build -f Dockerfile.alpine -t mozilla/sops:alpine .
- run:
name: Tag & Push containers
command: |
#latest
bin/ci/deploy_dockerhub.sh "latest"
bin/ci/deploy_dockerhub.sh "alpine"
# by sha
echo "Tag and push mozilla/sops:$CIRCLE_SHA1"
docker tag mozilla/sops "mozilla/sops:$CIRCLE_SHA1"
bin/ci/deploy_dockerhub.sh "$CIRCLE_SHA1"
# no sha for alpine
# by semver
# v1.2.3
if [ ! -z $PATCH ];then
echo "Tag and Push mozilla/sops:v$MAJOR.$MINOR.$PATCH"
docker tag mozilla/sops "mozilla/sops:v$MAJOR.$MINOR.$PATCH"
bin/ci/deploy_dockerhub.sh "v$MAJOR.$MINOR.$PATCH"
echo "Tag and Push mozilla/sops:v$MAJOR.$MINOR.$PATCH-alpine"
docker tag mozilla/sops:alpine "mozilla/sops:v$MAJOR.$MINOR.$PATCH-alpine"
bin/ci/deploy_dockerhub.sh "v$MAJOR.$MINOR.$PATCH-alpine"
fi
# v1.2
if [ ! -z $MINOR ];then
echo "Tag and Push mozilla/sops:v$MAJOR.$MINOR"
docker tag mozilla/sops "mozilla/sops:v$MAJOR.$MINOR"
bin/ci/deploy_dockerhub.sh "v$MAJOR.$MINOR"
echo "Tag and Push mozilla/sops:v$MAJOR.$MINOR-alpine"
docker tag mozilla/sops:alpine "mozilla/sops:v$MAJOR.$MINOR-alpine"
bin/ci/deploy_dockerhub.sh "v$MAJOR.$MINOR-alpine"
fi
# v1
echo "Tag and Push mozilla/sops:v$MAJOR"
docker tag mozilla/sops "mozilla/sops:v$MAJOR"
bin/ci/deploy_dockerhub.sh "v$MAJOR"
echo "Tag and Push mozilla/sops:v$MAJOR-alpine"
docker tag mozilla/sops:alpine "mozilla/sops:v$MAJOR-alpine"
bin/ci/deploy_dockerhub.sh "v$MAJOR-alpine"
07070100000002000081A400000000000000000000000162794F9300000025000000000000000000000000000000000000001900000000sops-3.7.3/.dockerignore/.git
/Dockerfile
/Dockerfile.alpine
07070100000003000041ED00000000000000000000000362794F9300000000000000000000000000000000000000000000001300000000sops-3.7.3/.github07070100000004000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001D00000000sops-3.7.3/.github/workflows07070100000005000081A400000000000000000000000162794F9300000D85000000000000000000000000000000000000002500000000sops-3.7.3/.github/workflows/cli.ymlname: CLI
on:
push:
branches:
- develop
- master
pull_request:
branches:
- develop
- master
jobs:
build:
name: Build and test ${{ matrix.os }} ${{ matrix.arch }}
runs-on: ubuntu-latest
strategy:
matrix:
os: [linux, darwin, windows]
arch: [amd64, arm64]
exclude:
- os: windows
arch: arm64
env:
VAULT_VERSION: "1.1.3"
VAULT_TOKEN: "root"
VAULT_ADDR: "http://127.0.0.1:8200"
steps:
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install git -y
- name: Set up Go 1.17
uses: actions/setup-go@v2
with:
go-version: 1.17
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Build Linux and Darwin
if: matrix.os != 'windows'
run: GOOS=${{ matrix.os }} GOARCH=${{ matrix.arch }} go build -o sops-${{ matrix.os }}-${{ matrix.arch }}-${{ github.sha }} -v ./cmd/sops
- name: Build Windows
if: matrix.os == 'windows'
run: GOOS=${{ matrix.os }} go build -o sops-${{ matrix.os }}-${{ github.sha }} -v ./cmd/sops
- name: Import test GPG keys
run: for i in 1 2 3 4 5; do gpg --import pgp/sops_functional_tests_key.asc && break || sleep 15; done
- name: Test
run: make test
- name: Upload artifact for Linux and Darwin
if: matrix.os != 'windows'
uses: actions/upload-artifact@v2
with:
name: sops-${{ matrix.os }}-${{ matrix.arch }}-${{ github.sha }}
path: sops-${{ matrix.os }}-${{ matrix.arch }}-${{ github.sha }}
- name: Upload artifact for Windows
if: matrix.os == 'windows'
uses: actions/upload-artifact@v2
with:
name: sops-${{ matrix.os }}-${{ github.sha }}
path: sops-${{ matrix.os }}-${{ github.sha }}
test:
name: Functional tests
runs-on: ubuntu-latest
needs: [build]
env:
VAULT_VERSION: "1.1.3"
VAULT_TOKEN: "root"
VAULT_ADDR: "http://127.0.0.1:8200"
steps:
- name: Install rustup
run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | bash -s -- -y --default-toolchain 1.47.0
- name: Check out code
uses: actions/checkout@v2
- uses: actions/download-artifact@v2
with:
name: sops-linux-amd64-${{ github.sha }}
- name: Move SOPS binary
run: mv sops-linux-amd64-${{ github.sha }} ./functional-tests/sops
- name: Make SOPS binary executable
run: chmod +x ./functional-tests/sops
- name: Download Vault
run: curl -O "https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip" && sudo unzip vault_${VAULT_VERSION}_linux_amd64.zip -d /usr/local/bin/
- name: Start Vault server
run: vault server -dev -dev-root-token-id="$VAULT_TOKEN" &
- name: Enable Vault KV
run: vault secrets enable -version=1 kv
- name: Import test GPG keys
run: for i in 1 2 3 4 5; do gpg --import pgp/sops_functional_tests_key.asc && break || sleep 15; done
- name: Run tests
run: cargo test
working-directory: ./functional-tests
07070100000006000081A400000000000000000000000162794F9300000C2E000000000000000000000000000000000000002900000000sops-3.7.3/.github/workflows/release.ymlname: Release
on:
push:
tags:
- "v*"
jobs:
tagged-release:
name: "Tagged Release"
runs-on: ubuntu-latest
steps:
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install git ruby rpm -y
- name: Install fpm
run: gem install fpm || sudo gem install fpm
- name: Set up Go 1.17
uses: actions/setup-go@v2
with:
go-version: 1.17
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Go vendor
run: go mod vendor
- name: Make release directory
run: mkdir dist
- name: Build deb and rpm
run: make deb-pkg rpm-pkg
- name: Move deb and rpm into release directory
run: mv *.deb *.rpm dist/
- name: Set RELEASE_VERSION
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Set RELEASE_NUMBER
run: echo "RELEASE_NUMBER=$(echo $RELEASE_VERSION | cut -c2-)" >> $GITHUB_ENV
- name: Build linux amd64 binary
run: GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -mod vendor -o dist/sops-${{ env.RELEASE_VERSION }}.linux.amd64 go.mozilla.org/sops/v3/cmd/sops && cp dist/sops-${{ env.RELEASE_VERSION }}.linux.amd64 dist/sops-${{ env.RELEASE_VERSION }}.linux
- name: Build linux arm64 binary
run: GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -mod vendor -o dist/sops-${{ env.RELEASE_VERSION }}.linux.arm64 go.mozilla.org/sops/v3/cmd/sops
- name: Build darwin amd64 binary
run: GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -mod vendor -o dist/sops-${{ env.RELEASE_VERSION }}.darwin.amd64 go.mozilla.org/sops/v3/cmd/sops
- name: Copy darwin amd64 to have a no-architecture labeled version
run: cp dist/sops-${{ env.RELEASE_VERSION }}.darwin.amd64 dist/sops-${{ env.RELEASE_VERSION }}.darwin
- name: Build darwin arm64 binary
run: GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build -mod vendor -o dist/sops-${{ env.RELEASE_VERSION }}.darwin.arm64 go.mozilla.org/sops/v3/cmd/sops
- name: Build windows binary
run: GOOS=windows CGO_ENABLED=0 go build -mod vendor -o dist/sops-${{ env.RELEASE_VERSION }}.exe go.mozilla.org/sops/v3/cmd/sops
- name: Create release
uses: "mozilla/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: true
files: |
dist/sops-${{ env.RELEASE_VERSION }}.exe
dist/sops-${{ env.RELEASE_VERSION }}.darwin.amd64
dist/sops-${{ env.RELEASE_VERSION }}.darwin.arm64
dist/sops-${{ env.RELEASE_VERSION }}.darwin
dist/sops-${{ env.RELEASE_VERSION }}.linux.amd64
dist/sops-${{ env.RELEASE_VERSION }}.linux.arm64
dist/sops-${{ env.RELEASE_VERSION }}.linux
dist/sops_${{ env.RELEASE_NUMBER }}_amd64.deb
dist/sops_${{ env.RELEASE_NUMBER }}_arm64.deb
dist/sops-${{ env.RELEASE_NUMBER }}-1.x86_64.rpm
dist/sops-${{ env.RELEASE_NUMBER }}-1.aarch64.rpm
07070100000007000081A400000000000000000000000162794F9300000033000000000000000000000000000000000000001600000000sops-3.7.3/.gitignoretarget
Cargo.lock
vendor/
coverage.txt
profile.out
07070100000008000081A400000000000000000000000162794F9300000081000000000000000000000000000000000000001600000000sops-3.7.3/.sops.yamlcreation_rules:
- pgp: >-
FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4,
D7229043384BCC60326C6FB9D8720D957C3D3074
07070100000009000081A400000000000000000000000162794F93000021EE000000000000000000000000000000000000001900000000sops-3.7.3/CHANGELOG.rstChangelog
=========
3.7.3
-----
Changes:
* Upgrade dependencies (#1024, #1045)
* Build alpine container in CI (#1018, #1032, #1025)
* keyservice: accept KeyServiceServer in LocalClient (#1035)
* Add support for GCP Service Account within `GOOGLE_CREDENTIALS` (#953)
Bug fixes:
* Upload the correct binary for the linux amd64 build (#1026)
* Fix bug when specifying multiple age recipients (#966)
* Allow for empty yaml maps (#908)
* Limit AWS role names to 64 characters (#1037)
3.7.2
-----
Changes:
* README updates (#861, #860)
* Various test fixes (#909, #906, #1008)
* Added Linux and Darwin arm64 releases (#911, #891)
* Upgrade to go v1.17 (#1012)
* Support SOPS_AGE_KEY environment variable (#1006)
Bug fixes:
* Make sure comments in yaml files are not duplicated (#866)
* Make sure configuration file paths work correctly relative to the config file in us (#853)
3.7.1
-----
Changes:
* Security fix
* Add release workflow (#843)
* Fix issue where CI wouldn't run against master (#848)
* Trim extra whitespace around age keys (#846)
3.7.0
-----
Features:
* Add support for age (#688)
* Add filename to exec-file (#761)
Changes:
* On failed decryption with GPG, return the error returned by GPG to the sops user (#762)
* Use yaml.v3 instead of modified yaml.v2 for handling YAML files (#791)
* Update aws-sdk-go to version v1.37.18 (#823)
Project Changes:
* Switch from TravisCI to Github Actions (#792)
3.6.1
-----
Features:
* Add support for --unencrypted-regex (#715)
Changes:
* Use keys.openpgp.org instead of gpg.mozilla.org (#732)
* Upgrade AWS SDK version (#714)
* Support --input-type for exec-file (#699)
Bug fixes:
* Fixes broken Vault tests (#731)
* Revert "Add standard newline/quoting behavior to dotenv store" (#706)
3.6.0
-----
Features:
* Support for encrypting data through the use of Hashicorp Vault (#655)
* `sops publish` now supports `--recursive` flag for publishing all files in a directory (#602)
* `sops publish` now supports `--omit-extensions` flag for omitting the extension in the destination path (#602)
* sops now supports JSON arrays of arrays (#642)
Improvements:
* Updates and standardization for the dotenv store (#612, #622)
* Close temp files after using them for edit command (#685)
Bug fixes:
* AWS SDK usage now correctly resolves the `~/.aws/config` file (#680)
* `sops updatekeys` now correctly matches config rules (#682)
* `sops updatekeys` now correctly uses the config path cli flag (#672)
* Partially empty sops config files don't break the use of sops anymore (#662)
* Fix possible infinite loop in PGP's passphrase prompt call (#690)
Project changes:
* Dockerfile now based off of golang version 1.14 (#649)
* Push alpine version of docker image to Dockerhub (#609)
* Push major, major.minor, and major.minor.patch tagged docker images to Dockerhub (#607)
* Removed out of date contact information (#668)
* Update authors in the cli help text (#645)
3.5.0
-----
Features:
* `sops exec-env` and `sops exec-file`, two new commands for utilizing sops secrets within a temporary file or env vars
Bug fixes:
* Sanitize AWS STS session name, as sops creates it based off of the machines hostname
* Fix for `decrypt.Data` to support `.ini` files
* Various package fixes related to switching to Go Modules
* Fixes for Vault-related tests running locally and in CI.
Project changes:
* Change to proper use of go modules, changing to primary module name to `go.mozilla.org/sops/v3`
* Change tags to requiring a `v` prefix.
* Add documentation for `sops updatekeys` command
3.4.0
-----
Features:
* `sops publish`, a new command for publishing sops encrypted secrets to S3, GCS, or Hashicorp Vault
* Support for multiple Azure authentication mechanisms
* Azure Keyvault support to the sops config file
* `encrypted_regex` option to the sops config file
Bug fixes:
* Return non-zero exit code for invalid CLI flags
* Broken path handling for sops editing on Windows
* `go lint/fmt` violations
* Check for pgp fingerprint before slicing it
Project changes:
* Build container using golang 1.12
* Switch to using go modules
* Hashicorp Vault server in Travis CI build
* Mozilla Publice License file to repo
* Replaced expiring test gpg keys
3.3.1
-----
Bug fixes:
* Make sure the pgp key fingerprint is longer than 16 characters before
slicing it. (#463)
* Allow for `--set` value to be a string. (#461)
Project changes:
* Using `develop` as a staging branch to create releases off of. What
is in `master` is now the current stable release.
* Upgrade to using Go 1.12 to build sops
* Updated all vendored packages
3.3.0
-----
New features:
* Multi-document support for YAML files
* Support referencing AWS KMS keys by their alias
* Support for INI files
* Support for AWS CLI profiles
* Comment support in .env files
* Added vi to the list of known editors
* Added a way to specify the GPG key server to use through the
SOPS_GPG_KEYSERVER environment variable
Bug fixes:
* Now uses $HOME instead of ~ (which didn't work) to find the GPG home
* Fix panic when vim was not available as an editor, but other
alternative editors were
* Fix issue with AWS KMS Encryption Contexts (#445) with more than one
context value failing to decrypt intermittently. Includes an
automatic fix for old files affected by this issue.
Project infrastructure changes:
* Added integration tests for AWS KMS
* Added Code of Conduct
3.2.0
-----
* Added --output flag to write output a file directly instead of
through stdout
* Added support for dotenv files
3.1.1
-----
* Fix incorrect version number from previous release
3.1.0
-----
* Add support for Azure Key Service
* Fix bug that prevented JSON escapes in input files from working
3.0.5
-----
* Prevent files from being encrypted twice
* Fix empty comments not being decrypted correctly
* If keyservicecmd returns an error, log it.
* Initial sops workspace auditing support (still wip)
* Refactor Store interface to reflect operations SOPS performs
3.0.3
-----
* --set now works with nested data structures and not just simple
values
* Changed default log level to warn instead of info
* Avoid creating empty files when using the editor mode to create new
files and not making any changes to the example files
* Output unformatted strings when using --extract instead of encoding
them to yaml
* Allow forcing binary input and output types from command line flags
* Deprecate filename_regex in favor of path_regex. filename_regex had
a bug and matched on the whole file path, when it should have only
matched on the file name. path_regex on the other hand is documented
to match on the whole file path.
* Add an encrypted-suffix option, the exact opposite of
unencrypted-suffix
* Allow specifying unencrypted_suffix and encrypted_suffix rules in
the .sops.yaml configuration file
* Introduce key service flag optionally prompting users on
encryption/decryption
3.0.1
-----
* Don't consider io.EOF returned by Decoder.Token as error
* add IsBinary: true to FileHints when encoding with crypto/openpgp
* some improvements to error messages
3.0.0
-----
* Shamir secret sharing scheme support allows SOPS to require multiple master
keys to access a data key and decrypt a file. See `sops groups -help` and the
documentation in README.
* Keyservice to forward access to a local master key on a socket, similar to
gpg-agent. See `sops keyservice --help` and the documentation in README.
* Encrypt comments by default
* Support for Google Compute Platform KMS
* Refactor of the store logic to separate the internal representation SOPS
has of files from the external representation used in JSON and YAML files
* Reencoding of versions as string on sops 1.X files.
**WARNING** this change breaks backward compatibility.
SOPS shows an error message with instructions on how to solve
this if it happens.
* Added command to reconfigure the keys used to encrypt/decrypt a file based on the .sops.yaml config file
* Retrieve missing PGP keys from gpg.mozilla.org
* Improved error messages for errors when decrypting files
2.0.0
-----
* [major] rewrite in Go
1.14
----
* [medium] Support AWS KMS Encryption Contexts
* [minor] Support insertion in encrypted documents via --set
* [minor] Read location of gpg binary from SOPS_GPG_EXEC env variables
1.13
----
* [minor] handle $EDITOR variable with parameters
1.12
----
* [minor] make sure filename_regex gets applied to file names, not paths
* [minor] move check of latest version under the -V flag
* [medium] fix handling of binary data to preserve file integrity
* [minor] try to use configuration when encrypting existing files
0707010000000A000081A400000000000000000000000162794F93000001EF000000000000000000000000000000000000001E00000000sops-3.7.3/CODE_OF_CONDUCT.md# Community Participation Guidelines
This repository is governed by Mozilla's code of conduct and etiquette guidelines.
For more details, please read the
[Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).
## How to Report
For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page.
0707010000000B000081A400000000000000000000000162794F9300000830000000000000000000000000000000000000001B00000000sops-3.7.3/CONTRIBUTING.md# Contributing to SOPS
Mozilla welcomes contributions from everyone. Here are a few guidelines and instructions if you're thinking of helping with the development of SOPS.
# Getting started
* Make sure you have Go 1.12 or greater installed. You can find information on how to install Go [here](https://golang.org/dl/)
* After following the [Go installation guide](https://golang.org/doc/install), run `go get go.mozilla.org/sops`. This will automatically clone this repository.
* Switch into sops's directory, which will be in `$GOPATH/src/go.mozilla.org/sops`.
* Run the tests with `make test`. They should all pass.
* Fork the project on GitHub.
* Add your fork to git's remotes:
* If you use SSH authentication: `git remote add <your username> git@github.com:<your username>/sops.git`.
* Otherwise: `git remote add <your username> https://github.com/<your username>/sops.git`.
* **Switch to the `develop` branch: `git checkout develop`**
* Make any changes you want to sops, commit them, and push them to your fork.
* **Create a pull request against `develop`**, and a contributor will come by and review your code. They may ask for some changes, and hopefully your contribution will be merged to the `develop` branch!
# Guidelines
* Unless it's particularly hard, changes that fix a bug should have a regression test to make sure that the bug is not introduced again.
* New features and changes to existing features should be documented, and, if possible, tested.
# Regenerating mocks
If you encounter an error like `kms/mocks/KMSAPI.go:1607: cannot use (*KMSAPI)(nil) (type *KMSAPI) as type kmsiface.KMSAPI in assignment: *KMSAPI does not implement kmsiface.KMSAPI (missing ListResourceTags method)`, you need to regenerate mocks, probably because the interface was changed by a vendoring update. There is a make command to do this for you. Simply run `make mock`, and the new mocks will be automatically generated.
# Communication
If you need any help contributing to sops, several contributors are on the `#go` channel on [Mozilla's IRC server](https://wiki.mozilla.org/IRC).
0707010000000C000081A400000000000000000000000162794F93000000DF000000000000000000000000000000000000001600000000sops-3.7.3/DockerfileFROM golang:1.17
COPY . /go/src/go.mozilla.org/sops
WORKDIR /go/src/go.mozilla.org/sops
RUN CGO_ENABLED=1 make install
RUN apt-get update
RUN apt-get install -y vim python3-pip emacs
RUN pip install awscli
ENV EDITOR vim
0707010000000D000081A400000000000000000000000162794F9300000156000000000000000000000000000000000000001D00000000sops-3.7.3/Dockerfile.alpineFROM golang:1.17-alpine3.15 AS builder
RUN apk --no-cache add make
COPY . /go/src/go.mozilla.org/sops
WORKDIR /go/src/go.mozilla.org/sops
RUN CGO_ENABLED=1 make install
FROM alpine:3.15
RUN apk --no-cache add \
vim ca-certificates
ENV EDITOR vim
COPY --from=builder /go/bin/sops /usr/local/bin/sops
ENTRYPOINT ["/usr/local/bin/sops"]
0707010000000E000081A400000000000000000000000162794F9300004155000000000000000000000000000000000000001300000000sops-3.7.3/LICENSEMozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
0707010000000F000081A400000000000000000000000162794F93000011E0000000000000000000000000000000000000001400000000sops-3.7.3/Makefile# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
PROJECT := go.mozilla.org/sops/v3
GO := GOPROXY=https://proxy.golang.org go
GOLINT := golint
all: test vet generate install functional-tests
origin-build: test vet generate install functional-tests-all
install:
$(GO) install go.mozilla.org/sops/v3/cmd/sops
tag: all
git tag -s $(TAGVER) -a -m "$(TAGMSG)"
lint:
$(GOLINT) $(PROJECT)
vendor:
$(GO) mod tidy
$(GO) mod vendor
vet:
$(GO) vet $(PROJECT)
test: vendor
gpg --import pgp/sops_functional_tests_key.asc 2>&1 1>/dev/null || exit 0
./test.sh
showcoverage: test
$(GO) tool cover -html=coverage.out
generate: keyservice/keyservice.pb.go
$(GO) generate
%.pb.go: %.proto
protoc --go_out=plugins=grpc:. $<
functional-tests:
$(GO) build -o functional-tests/sops go.mozilla.org/sops/v3/cmd/sops
cd functional-tests && cargo test
# Ignored tests are ones that require external services (e.g. AWS KMS)
# TODO: Once `--include-ignored` lands in rust stable, switch to that.
functional-tests-all:
$(GO) build -o functional-tests/sops go.mozilla.org/sops/v3/cmd/sops
cd functional-tests && cargo test && cargo test -- --ignored
# Creates variables during target re-definition. Basically this block allows the particular variables to be used in the final target
build-deb-%: OS = $(word 1,$(subst -, ,$*))
build-deb-%: ARCH = $(word 2,$(subst -, ,$*))
build-deb-%: FPM_ARCH = $(word 3,$(subst -, ,$*))
# Poor-mans function with parameters being split out from the variable part of it's name
build-deb-%:
rm -rf tmppkg
mkdir -p tmppkg/usr/local/bin
GOOS=$(OS) GOARCH="$(ARCH)" CGO_ENABLED=0 go build -mod vendor -o tmppkg/usr/local/bin/sops go.mozilla.org/sops/v3/cmd/sops
fpm -C tmppkg -n sops --license MPL2.0 --vendor mozilla \
--description "Sops is an editor of encrypted files that supports YAML, JSON and BINARY formats and encrypts with AWS KMS and PGP." \
-m "AJ Bahnken <ajvb+sops@mozilla.com>" \
--url https://go.mozilla.org/sops \
--architecture $(FPM_ARCH) \
-v "$$(grep '^const Version' version/version.go |cut -d \" -f 2)" \
-s dir -t deb .
# Create .deb packages for multiple architectures
deb-pkg: vendor build-deb-linux-amd64-x86_64 build-deb-linux-arm64-arm64
# Creates variables during target re-definition. Basically this block allows the particular variables to be used in the final target
build-rpm-%: OS = $(word 1,$(subst -, ,$*))
build-rpm-%: ARCH = $(word 2,$(subst -, ,$*))
build-rpm-%: FPM_ARCH = $(word 3,$(subst -, ,$*))
# Poor-mans function with parameters being split out from the variable part of it's name
build-rpm-%:
rm -rf tmppkg
mkdir -p tmppkg/usr/local/bin
GOOS=$(OS) GOARCH="$(ARCH)" CGO_ENABLED=0 go build -mod vendor -o tmppkg/usr/local/bin/sops go.mozilla.org/sops/v3/cmd/sops
fpm -C tmppkg -n sops --license MPL2.0 --vendor mozilla \
--description "Sops is an editor of encrypted files that supports YAML, JSON and BINARY formats and encrypts with AWS KMS and PGP." \
-m "AJ Bahnken <ajvb+sops@mozilla.com>" \
--url https://go.mozilla.org/sops \
--architecture $(FPM_ARCH) \
--rpm-os $(OS) \
-v "$$(grep '^const Version' version/version.go |cut -d \" -f 2)" \
-s dir -t rpm .
# Create .rpm packages for multiple architectures
rpm-pkg: vendor build-rpm-linux-amd64-x86_64 build-rpm-linux-arm64-arm64
dmg-pkg: install
ifneq ($(OS),darwin)
echo 'you must be on MacOS and set OS=darwin on the make command line to build an OSX package'
else
rm -rf tmppkg
mkdir -p tmppkg/usr/local/bin
cp $$GOPATH/bin/sops tmppkg/usr/local/bin/
fpm -C tmppkg -n sops --license MPL2.0 --vendor mozilla \
--description "Sops is an editor of encrypted files that supports YAML, JSON and BINARY formats and encrypts with AWS KMS and PGP." \
-m "Mozilla Security <security@mozilla.org>" \
--url https://go.mozilla.org/sops \
--architecture x86_64 \
-v "$$(grep '^const Version' version/version.go |cut -d \" -f 2)" \
-s dir -t osxpkg \
--osxpkg-identifier-prefix org.mozilla.sops \
-p tmppkg/sops-$$(git describe --abbrev=0 --tags).pkg .
hdiutil makehybrid -hfs -hfs-volume-name "Mozilla Sops" \
-o tmppkg/sops-$$(git describe --abbrev=0 --tags).dmg tmpdmg
endif
download-index:
bash make_download_page.sh
mock:
go install github.com/vektra/mockery/.../
mockery -dir vendor/github.com/aws/aws-sdk-go/service/kms/kmsiface/ -name KMSAPI -output kms/mocks
.PHONY: all test generate clean vendor functional-tests mock
07070100000010000081A400000000000000000000000162794F930000FB57000000000000000000000000000000000000001600000000sops-3.7.3/README.rstSOPS: Secrets OPerationS
========================
**sops** is an editor of encrypted files that supports YAML, JSON, ENV, INI and BINARY
formats and encrypts with AWS KMS, GCP KMS, Azure Key Vault, age, and PGP.
(`demo <https://www.youtube.com/watch?v=YTEVyLXFiq0>`_)
.. image:: https://i.imgur.com/X0TM5NI.gif
------------
.. image:: https://pkg.go.dev/badge/go.mozilla.org/sops/v3.svg
:target: https://pkg.go.dev/go.mozilla.org/sops/v3
Download
--------
Stable release
~~~~~~~~~~~~~~
Binaries and packages of the latest stable release are available at `https://github.com/mozilla/sops/releases <https://github.com/mozilla/sops/releases>`_.
Development branch
~~~~~~~~~~~~~~~~~~
For the adventurous, unstable features are available in the `develop` branch, which you can install from source:
.. code:: bash
$ mkdir -p $GOPATH/src/go.mozilla.org/sops/
$ git clone https://github.com/mozilla/sops.git $GOPATH/src/go.mozilla.org/sops/
$ cd $GOPATH/src/go.mozilla.org/sops/
$ git checkout develop
$ make install
(requires Go >= 1.17)
If you don't have Go installed, set it up with:
.. code:: bash
$ {apt,yum,brew} install golang
$ echo 'export GOPATH=~/go' >> ~/.bashrc
$ source ~/.bashrc
$ mkdir $GOPATH
Or whatever variation of the above fits your system and shell.
To use **sops** as a library, take a look at the `decrypt package <https://pkg.go.dev/go.mozilla.org/sops/v3/decrypt>`_.
.. sectnum::
.. contents:: Table of Contents
Usage
-----
For a quick presentation of Sops, check out this Youtube tutorial:
.. image:: https://img.youtube.com/vi/V2PRhxphH2w/0.jpg
:target: https://www.youtube.com/watch?v=V2PRhxphH2w
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.
.. code:: bash
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"
Your AWS credentials must be present in ``~/.aws/credentials``. sops uses aws-sdk-go.
.. code::
$ cat ~/.aws/credentials
[default]
aws_access_key_id = AKI.....
aws_secret_access_key = mw......
If you want to use PGP, export the fingerprints of the public keys, comma
separated, in the **SOPS_PGP_FP** env variable.
.. code:: bash
export SOPS_PGP_FP="85D77543B3D624B63CEA9E6DBC17301B491B3F21,E60892BB9BD89A69F759A1A0A3D652173B763E8F"
Note: you can use both PGP and KMS simultaneously.
Then simply call ``sops`` with a file path as argument. It will handle the
encryption/decryption transparently and open the cleartext file in an editor
.. code:: shell
$ sops 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.yaml
Editing will happen in whatever ``$EDITOR`` is set to, or, if it's not set, in vim.
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 <https://github.com/mozilla/sops/issues/127>`_ for
more information.
The resulting encrypted file looks like this:
.. code:: yaml
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:
.. code:: bash
$ sops -d 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
permissions on KMS keys.
Given that, the only command a ``sops`` user needs is:
.. code:: bash
$ sops <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.
Test with the dev PGP key
~~~~~~~~~~~~~~~~~~~~~~~~~
If you want to test **sops** without having to do a bunch of setup, you can use
the example files and pgp key provided with the repository::
$ git clone https://github.com/mozilla/sops.git
$ cd sops
$ gpg --import pgp/sops_functional_tests_key.asc
$ sops example.yaml
This last step will decrypt ``example.yaml`` using the test private key.
Encrypting using age
~~~~~~~~~~~~~~~~~~~~
`age <https://age-encryption.org/>`_ 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:
.. code:: bash
$ 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
configuration directory. On Linux, this would be ``$XDG_CONFIG_HOME/sops/age/keys.txt``.
On macOS, this would be ``$HOME/Library/Application Support/sops/age/keys.txt``. On
Windows, this would be ``%AppData%\sops\age\keys.txt``. You can specify the location
of this file manually by setting the environment variable **SOPS_AGE_KEY_FILE**.
Alternatively you can provide the the key(s) directly by setting the **SOPS_AGE_KEY**
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 not yet supported by sops.
Encrypting using GCP KMS
~~~~~~~~~~~~~~~~~~~~~~~~
GCP KMS uses `Application Default Credentials
<https://developers.google.com/identity/protocols/application-default-credentials>`_.
If you already logged in using
.. code:: bash
$ gcloud auth login
you can enable application default credentials using the sdk::
$ gcloud auth application-default login
Encrypting/decrypting with GCP KMS requires a KMS ResourceID. You can use the
cloud console the get the ResourceID or you can create one using the gcloud
sdk:
.. code:: bash
$ gcloud kms keyrings create sops --location global
$ gcloud kms keys create sops-key --location global --keyring sops --purpose encryption
$ gcloud kms keys list --location global --keyring sops
# you should see
NAME PURPOSE PRIMARY_STATE
projects/my-project/locations/global/keyRings/sops/cryptoKeys/sops-key ENCRYPT_DECRYPT ENABLED
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
And decrypt it using::
$ sops --decrypt test.enc.yaml
Encrypting using Azure Key Vault
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Azure Key Vault integration tries several authentication methods, in
this order:
1. Client credentials
2. Client Certificate
3. Username Password
4. MSI
5. Azure CLI auth
You can force a specific authentication method through the AZURE_AUTH_METHOD
environment variable, which may be one of: clientcredentials, clientcertificate,
usernamepassword, msi, or cli (default).
For example, you can use service principals with the following environment variables:
.. code:: bash
AZURE_TENANT_ID
AZURE_CLIENT_ID
AZURE_CLIENT_SECRET
You can create a service principal using the cli like this:
.. code:: bash
$ az ad sp create-for-rbac -n my-keyvault-sp
{
"appId": "<some-uuid>",
"displayName": "my-keyvault-sp",
"name": "http://my-keyvault-sp",
"password": "<some-uuid>",
"tenant": "<tenant-id>"
}
The appId is the client id, and the password is the client secret.
Encrypting/decrypting with Azure Key Vault requires the resource identifier for
a key. This has the following form::
https://${VAULT_URL}/keys/${KEY_NAME}/${KEY_VERSION}
To create a Key Vault and assign your service principal permissions on it
from the commandline:
.. code:: bash
# Create a resource group if you do not have one:
$ az group create --name sops-rg --location westeurope
# Key Vault names are globally unique, so generate one:
$ keyvault_name=sops-$(uuidgen | tr -d - | head -c 16)
# Create a Vault, a key, and give the service principal access:
$ az keyvault create --name $keyvault_name --resource-group sops-rg --location westeurope
$ az keyvault key create --name sops-key --vault-name $keyvault_name --protection software --ops encrypt decrypt
$ az keyvault set-policy --name $keyvault_name --resource-group sops-rg --spn $AZURE_CLIENT_ID \
--key-permissions encrypt decrypt
# Read the key id:
$ az keyvault key show --name sops-key --vault-name $keyvault_name --query key.kid
https://sops.vault.azure.net/keys/sops-key/some-string
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
And decrypt it using::
$ sops --decrypt test.enc.yaml
Encrypting using Hashicorp Vault
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We assume you have an instance (or more) of Vault running and you have privileged access to it. For instructions on how to deploy a secure instance of Vault, refer to Hashicorp's official documentation.
To easily deploy Vault locally: (DO NOT DO THIS FOR PRODUCTION!!!)
.. code:: bash
$ docker run -d -p8200:8200 vault:1.2.0 server -dev -dev-root-token-id=toor
.. code:: bash
$ # Substitute this with the address Vault is running on
$ export VAULT_ADDR=http://127.0.0.1:8200
$ # this may not be necessary in case you previously used `vault login` for production use
$ export VAULT_TOKEN=toor
$ # to check if Vault started and is configured correctly
$ vault status
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 1
Threshold 1
Version 1.2.0
Cluster Name vault-cluster-618cc902
Cluster ID e532e461-e8f0-1352-8a41-fc7c11096908
HA Enabled false
$ # It is required to enable a transit engine if not already done (It is suggested to create a transit engine specifically for sops, in which it is possible to have multiple keys with various permission levels)
$ vault secrets enable -path=sops transit
Success! Enabled the transit secrets engine at: sops/
$ # Then create one or more keys
$ vault write sops/keys/firstkey type=rsa-4096
Success! Data written to: sops/keys/firstkey
$ vault write sops/keys/secondkey type=rsa-2048
Success! Data written to: sops/keys/secondkey
$ vault write sops/keys/thirdkey type=chacha20-poly1305
Success! Data written to: sops/keys/thirdkey
$ sops --hc-vault-transit $VAULT_ADDR/v1/sops/keys/firstkey vault_example.yml
$ cat <<EOF > .sops.yaml
creation_rules:
- path_regex: \.dev\.yaml$
hc_vault_transit_uri: "$VAULT_ADDR/v1/sops/keys/secondkey"
- path_regex: \.prod\.yaml$
hc_vault_transit_uri: "$VAULT_ADDR/v1/sops/keys/thirdkey"
EOF
$ sops --verbose -e prod/raw.yaml > prod/encrypted.yaml
Adding and removing keys
~~~~~~~~~~~~~~~~~~~~~~~~
When creating new files, ``sops`` uses the PGP, KMS and GCP KMS defined in the
command line arguments ``--kms``, ``--pgp``, ``--gcp-kms`` or ``--azure-kv``, or from
the environment variables ``SOPS_KMS_ARN``, ``SOPS_PGP_FP``, ``SOPS_GCP_KMS_IDS``,
``SOPS_AZURE_KEYVAULT_URLS``. That information is stored in the file under the
``sops`` section, such that decrypting files does not require providing those
parameters again.
Master PGP and KMS keys can be added and removed from a ``sops`` file in one of
three ways::
1. By using a .sops.yaml file and the ``updatekeys`` command.
2. By using command line flags.
3. By editing the file directly.
The sops team recommends the ``updatekeys`` approach.
``updatekeys`` command
**********************
The ``updatekeys`` command uses the `.sops.yaml <#using-sops-yaml-conf-to-select-kms-pgp-for-new-files>`_
configuration file to update (add or remove) the corresponding secrets in the
encrypted file. Note that the example below uses the
`Block Scalar yaml construct <https://yaml-multiline.info/>`_ to build a space
separated list.
.. code:: yaml
creation_rules:
- pgp: >-
85D77543B3D624B63CEA9E6DBC17301B491B3F21,
FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
.. code:: bash
$ sops updatekeys test.enc.yaml
Sops will prompt you with the changes to be made. This interactivity can be
disabled by supplying the ``-y`` flag.
Command Line
************
Command line flag ``--add-kms``, ``--add-pgp``, ``--add-gcp-kms``, ``--add-azure-kv``,
``--rm-kms``, ``--rm-pgp``, ``--rm-gcp-kms`` and ``--rm-azure-kv`` can be used to add
and remove keys from a file.
These flags use the comma separated syntax as the ``--kms``, ``--pgp``, ``--gcp-kms``
and ``--azure-kv`` arguments when creating new files.
Note that ``-r`` or ``--rotate`` is mandatory in this mode. Not specifying
rotate will ignore the ``--add-*`` options. Use ``updatekeys`` if you want to
add a key without rotating the data key.
.. code:: bash
# add a new pgp key to the file and rotate the data key
$ sops -r -i --add-pgp 85D77543B3D624B63CEA9E6DBC17301B491B3F21 example.yaml
# remove a pgp key from the file and rotate the data key
$ sops -r -i --rm-pgp 85D77543B3D624B63CEA9E6DBC17301B491B3F21 example.yaml
Direct Editing
**************
Alternatively, invoking ``sops`` with the flag **-s** will display the master keys
while editing. This method can be used to add or remove kms or pgp keys under the
sops section. Invoking ``sops`` with the **-i** flag will perform an in-place edit
instead of redirecting output to ``stdout``.
For example, to add a KMS master key to a file, add the following entry while
editing:
.. code:: yaml
sops:
kms:
- arn: arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e
And, similarly, to add a PGP master key, we add its fingerprint:
.. code:: yaml
sops:
pgp:
- fp: 85D77543B3D624B63CEA9E6DBC17301B491B3F21
When the file is saved, ``sops`` will update its metadata and encrypt the data key
with the freshly added master keys. The removed entries are simply deleted from
the file.
When removing keys, it is recommended to rotate the data key using ``-r``,
otherwise owners of the removed key may have add access to the data key in the
past.
KMS AWS Profiles
~~~~~~~~~~~~~~~~
If you want to use a specific profile, you can do so with `aws_profile`:
.. code:: yaml
sops:
kms:
- arn: arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e
aws_profile: foo
If no AWS profile is set, default credentials will be used.
Similarly the `--aws-profile` flag can be set with the command line with any of the KMS commands.
Assuming roles and using KMS in various AWS accounts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SOPS has the ability to use KMS in multiple AWS accounts by assuming roles in
each account. Being able to assume roles is a nice feature of AWS that allows
administrators to establish trust relationships between accounts, typically from
the most secure account to the least secure one. In our use-case, we use roles
to indicate that a user of the Master AWS account is allowed to make use of KMS
master keys in development and staging AWS accounts. Using roles, a single file
can be encrypted with KMS keys in multiple accounts, thus increasing reliability
and ease of use.
You can use keys in various accounts by tying each KMS master key to a role that
the user is allowed to assume in each account. The `IAM roles
<http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html>`_
documentation has full details on how this needs to be configured on AWS's side.
From the point of view of ``sops``, you only need to specify the role a KMS key
must assume alongside its ARN, as follows:
.. code:: yaml
sops:
kms:
- arn: arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e
role: arn:aws:iam::927034868273:role/sops-dev-xyz
The role must have permission to call Encrypt and Decrypt using KMS. An example
policy is shown below.
.. code:: json
{
"Sid": "Allow use of the key",
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*",
"Principal": {
"AWS": [
"arn:aws:iam::927034868273:role/sops-dev-xyz"
]
}
}
You can specify a role in the ``--kms`` flag and ``SOPS_KMS_ARN`` variable by
appending it to the ARN of the master key, separated by a **+** sign::
<KMS ARN>+<ROLE ARN>
arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500+arn:aws:iam::927034868273:role/sops-dev-xyz
AWS KMS Encryption Context
~~~~~~~~~~~~~~~~~~~~~~~~~~
SOPS has the ability to use `AWS KMS key policy and encryption context
<http://docs.aws.amazon.com/kms/latest/developerguide/encryption-context.html>`_
to refine the access control of a given KMS master key.
When creating a new file, you can specify encryption context in the
``--encryption-context`` flag by comma separated list of key-value pairs:
.. code:: bash
$ sops --encryption-context Environment:production,Role:web-server test.dev.yaml
The format of the Encrypt Context string is ``<EncryptionContext Key>:<EncryptionContext Value>,<EncryptionContext Key>:<EncryptionContext Value>,...``
The encryption context will be stored in the file metadata and does
not need to be provided at decryption.
Encryption contexts can be used in conjunction with KMS Key Policies to define
roles that can only access a given context. An example policy is shown below:
.. code:: json
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/RoleForExampleApp"
},
"Action": "kms:Decrypt",
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:EncryptionContext:AppName": "ExampleApp",
"kms:EncryptionContext:FilePath": "/var/opt/secrets/"
}
}
}
Key Rotation
~~~~~~~~~~~~
It is recommended to renew the data key on a regular basis. ``sops`` supports key
rotation via the ``-r`` flag. Invoking it on an existing file causes sops to
reencrypt the file with a new data key, which is then encrypted with the various
KMS and PGP master keys defined in the file.
.. code:: bash
sops -r example.yaml
Using .sops.yaml conf to select KMS/PGP for new files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It is often tedious to specify the ``--kms`` ``--gcp-kms`` and ``--pgp`` parameters for creation
of all new files. If your secrets are stored under a specific directory, like a
``git`` repository, you can create a ``.sops.yaml`` configuration file at the root
directory to define which keys are used for which filename.
Let's take an example:
* file named **something.dev.yaml** should use one set of KMS A
* file named **something.prod.yaml** should use another set of KMS B
* other files use a third set of KMS C
* all live under **mysecretrepo/something.{dev,prod,gcp}.yaml**
Under those circumstances, a file placed at **mysecretrepo/.sops.yaml**
can manage the three sets of configurations for the three types of files:
.. code:: yaml
# creation rules are evaluated sequentially, the first match wins
creation_rules:
# upon creation of a file that matches the pattern *.dev.yaml,
# KMS set A is used
- path_regex: \.dev\.yaml$
kms: 'arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500,arn:aws:kms:us-west-2:361527076523:key/5052f06a-5d3f-489e-b86c-57201e06f31e+arn:aws:iam::361527076523:role/hiera-sops-prod'
pgp: 'FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4'
# prod files use KMS set B in the PROD IAM
- path_regex: \.prod\.yaml$
kms: 'arn:aws:kms:us-west-2:361527076523:key/5052f06a-5d3f-489e-b86c-57201e06f31e+arn:aws:iam::361527076523:role/hiera-sops-prod,arn:aws:kms:eu-central-1:361527076523:key/cb1fab90-8d17-42a1-a9d8-334968904f94+arn:aws:iam::361527076523:role/hiera-sops-prod'
pgp: 'FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4'
hc_vault_uris: "http://localhost:8200/v1/sops/keys/thirdkey"
# gcp files using GCP KMS
- path_regex: \.gcp\.yaml$
gcp_kms: projects/mygcproject/locations/global/keyRings/mykeyring/cryptoKeys/thekey
# Finally, if the rules above have not matched, this one is a
# catchall that will encrypt the file using KMS set C
# The absence of a path_regex means it will match everything
- kms: 'arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500,arn:aws:kms:us-west-2:142069644989:key/846cfb17-373d-49b9-8baf-f36b04512e47,arn:aws:kms:us-west-2:361527076523:key/5052f06a-5d3f-489e-b86c-57201e06f31e'
pgp: 'FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4'
When creating any file under **mysecretrepo**, whether at the root or under
a subdirectory, sops will recursively look for a ``.sops.yaml`` file. If one is
found, the filename of the file being created is compared with the filename
regexes of the configuration file. The first regex that matches is selected,
and its KMS and PGP keys are used to encrypt the file. It should be noted that
the looking up of ``.sops.yaml`` is from the working directory (CWD) instead of
the directory of the encrypting file (see `Issue 242 <https://github.com/mozilla/sops/issues/242>`_).
The path_regex checks the path of the encrypting file relative to the .sops.yaml config file. Here is another example:
* files located under directory **development** should use one set of KMS A
* files located under directory **production** should use another set of KMS B
* other files use a third set of KMS C
.. code:: yaml
creation_rules:
# upon creation of a file under development,
# KMS set A is used
- path_regex: .*/development/.*
kms: 'arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500,arn:aws:kms:us-west-2:361527076523:key/5052f06a-5d3f-489e-b86c-57201e06f31e+arn:aws:iam::361527076523:role/hiera-sops-prod'
pgp: 'FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4'
# prod files use KMS set B in the PROD IAM
- path_regex: .*/production/.*
kms: 'arn:aws:kms:us-west-2:361527076523:key/5052f06a-5d3f-489e-b86c-57201e06f31e+arn:aws:iam::361527076523:role/hiera-sops-prod,arn:aws:kms:eu-central-1:361527076523:key/cb1fab90-8d17-42a1-a9d8-334968904f94+arn:aws:iam::361527076523:role/hiera-sops-prod'
pgp: 'FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4'
# other files use KMS set C
- kms: 'arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500,arn:aws:kms:us-west-2:142069644989:key/846cfb17-373d-49b9-8baf-f36b04512e47,arn:aws:kms:us-west-2:361527076523:key/5052f06a-5d3f-489e-b86c-57201e06f31e'
pgp: 'FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4'
Creating a new file with the right keys is now as simple as
.. code:: bash
$ sops <newfile>.prod.yaml
Note that the configuration file is ignored when KMS or PGP parameters are
passed on the sops command line or in environment variables.
Specify a different GPG executable
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``sops`` checks for the ``SOPS_GPG_EXEC`` environment variable. If specified,
it will attempt to use the executable set there instead of the default
of ``gpg``.
Example: place the following in your ``~/.bashrc``
.. code:: bash
SOPS_GPG_EXEC = 'your_gpg_client_wrapper'
Specify a different GPG key server
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
By default, ``sops`` uses the key server ``keys.openpgp.org`` to retrieve the GPG
keys that are not present in the local keyring.
This is no longer configurable. You can learn more about why from this write-up: `SKS Keyserver Network Under Attack <https://gist.github.com/rjhansen/67ab921ffb4084c865b3618d6955275f>`_.
Key groups
~~~~~~~~~~
By default, ``sops`` encrypts the data key for a file with each of the master keys,
such that if any of the master keys is available, the file can be decrypted.
However, it is sometimes desirable to require access to multiple master keys
in order to decrypt files. This can be achieved with key groups.
When using key groups in sops, data keys are split into parts such that keys from
multiple groups are required to decrypt a file. ``sops`` uses Shamir's Secret Sharing
to split the data key such that each key group has a fragment, each key in the
key group can decrypt that fragment, and a configurable number of fragments (threshold)
are needed to decrypt and piece together the complete data key. When decrypting a
file using multiple key groups, ``sops`` goes through key groups in order, and in
each group, tries to recover the fragment of the data key using a master key from
that group. Once the fragment is recovered, ``sops`` moves on to the next group,
until enough fragments have been recovered to obtain the complete data key.
By default, the threshold is set to the number of key groups. For example, if
you have three key groups configured in your SOPS file and you don't override
the default threshold, then one master key from each of the three groups will
be required to decrypt the file.
Management of key groups is done with the ``sops groups`` command.
For example, you can add a new key group with 3 PGP keys and 3 KMS keys to the
file ``my_file.yaml``:
.. code:: bash
$ sops groups add --file my_file.yaml --pgp fingerprint1 --pgp fingerprint2 --pgp fingerprint3 --kms arn1 --kms arn2 --kms arn3
Or you can delete the 1st group (group number 0, as groups are zero-indexed)
from ``my_file.yaml``:
.. code:: bash
$ sops groups delete --file my_file.yaml 0
Key groups can also be specified in the ``.sops.yaml`` config file,
like so:
.. code:: yaml
creation_rules:
- path_regex: .*keygroups.*
key_groups:
# First key group
- pgp:
- fingerprint1
- fingerprint2
kms:
- arn: arn1
role: role1
context:
foo: bar
- arn: arn2
# Second key group
- pgp:
- fingerprint3
- fingerprint4
kms:
- arn: arn3
- arn: arn4
# Third key group
- pgp:
- fingerprint5
Given this configuration, we can create a new encrypted file like we normally
would, and optionally provide the ``--shamir-secret-sharing-threshold`` command line
flag if we want to override the default threshold. ``sops`` will then split the data
key into three parts (from the number of key groups) and encrypt each fragment with
the master keys found in each group.
For example:
.. code:: bash
$ sops --shamir-secret-sharing-threshold 2 example.json
Alternatively, you can configure the Shamir threshold for each creation rule in the ``.sops.yaml`` config
with ``shamir_threshold``:
.. code:: yaml
creation_rules:
- path_regex: .*keygroups.*
shamir_threshold: 2
key_groups:
# First key group
- pgp:
- fingerprint1
- fingerprint2
kms:
- arn: arn1
role: role1
context:
foo: bar
- arn: arn2
# Second key group
- pgp:
- fingerprint3
- fingerprint4
kms:
- arn: arn3
- arn: arn4
# Third key group
- pgp:
- fingerprint5
And then run ``sops example.json``.
The threshold (``shamir_threshold``) is set to 2, so this configuration will require
master keys from two of the three different key groups in order to decrypt the file.
You can then decrypt the file the same way as with any other SOPS file:
.. code:: bash
$ sops -d example.json
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. The ``sops`` 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 ``--keyservice``.
This flag can be specified more than once, so you can use multiple key
services. The local key service can be disabled with
``enable-local-keyservice=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:
.. code:: bash
$ sops --keyservice unix:///tmp/sops.sock -d 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:: bash
$ sops --enable-local-keyservice=false --keyservice unix:///tmp/sops.sock -d file.yaml
Auditing
~~~~~~~~
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:
.. code:: yaml
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 <https://www.postgresql.org/docs/current/static/libpq-connect.html#libpq-connstring>`_.
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:
.. code:: yaml
backends:
postgres:
- connection_string: "postgres://sops:sops@localhost/sops?sslmode=verify-full"
- connection_string: "postgres://sops:sops@remotehost/sops?sslmode=verify-full"
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.
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.
.. code:: bash
# print secrets to stdout to confirm values
$ sops -d 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 use ``exec-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 to
``find(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).
.. code:: bash
# 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 directory
Additionally, on unix-like platforms, both ``exec-env`` and ``exec-file``
support dropping privileges before executing the new program via the
``--user <username>`` 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.
.. code:: bash
# 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 denied
Using the publish command
~~~~~~~~~~~~~~~~~~~~~~~~~
``sops publish $file`` publishes a file to a pre-configured destination (this lives in the sops
config file). Additionally, support re-encryption rules that work just like the creation rules.
This command requires a ``.sops.yaml`` configuration file. Below is an example:
.. code:: yaml
destination_rules:
- s3_bucket: "sops-secrets"
path_regex: s3/*
recreation_rule:
pgp: F69E4901EDBAD2D1753F8C67A64535C4163FB307
- gcs_bucket: "sops-secrets"
path_regex: gcs/*
recreation_rule:
pgp: F69E4901EDBAD2D1753F8C67A64535C4163FB307
- vault_path: "sops/"
vault_kv_mount_name: "secret/" # default
vault_kv_version: 2 # default
path_regex: vault/*
omit_extensions: true
The above configuration will place all files under ``s3/*`` into the S3 bucket ``sops-secrets``,
all files under ``gcs/*`` into the GCS bucket ``sops-secrets``, and the contents of all files under
``vault/*`` into Vault's KV store under the path ``secrets/sops/``. For the files that will be
published to S3 and GCS, it will decrypt them and re-encrypt them using the
``F69E4901EDBAD2D1753F8C67A64535C4163FB307`` pgp key.
You would deploy a file to S3 with a command like: ``sops publish s3/app.yaml``
To publish all files in selected directory recursively, you need to specify ``--recursive`` flag.
If you don't want file extension to appear in destination secret path, use ``--omit-extensions``
flag or ``omit_extensions: true`` in the destination rule in ``.sops.yaml``.
Publishing to Vault
*******************
There are a few settings for Vault that you can place in your destination rules. The first
is ``vault_path``, which is required. The others are optional, and they are
``vault_address``, ``vault_kv_mount_name``, ``vault_kv_version``.
``sops`` uses the official Vault API provided by Hashicorp, which makes use of `environment
variables <https://www.vaultproject.io/docs/commands/#environment-variables>`_ for
configuring the client.
``vault_kv_mount_name`` is used if your Vault KV is mounted somewhere other than ``secret/``.
``vault_kv_version`` supports ``1`` and ``2``, with ``2`` being the default.
If destination secret path already exists in Vault and contains same data as the source file, it
will be skipped.
Below is an example of publishing to Vault (using token auth with a local dev instance of Vault).
.. code:: bash
$ export VAULT_TOKEN=...
$ export VAULT_ADDR='http://127.0.0.1:8200'
$ sops -d vault/test.yaml
example_string: bar
example_number: 42
example_map:
key: value
$ sops publish vault/test.yaml
uploading /home/user/sops_directory/vault/test.yaml to http://127.0.0.1:8200/v1/secret/data/sops/test.yaml ? (y/n): y
$ vault kv get secret/sops/test.yaml
====== Metadata ======
Key Value
--- -----
created_time 2019-07-11T03:32:17.074792017Z
deletion_time n/a
destroyed false
version 3
========= Data =========
Key Value
--- -----
example_map map[key:value]
example_number 42
example_string bar
Important information on types
------------------------------
YAML and JSON 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 need 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:
.. code:: bash
$ sops -e -i myfile.json
$ sops -d 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:: bash
$ sops -e myfile.json > myfile.json.enc
$ sops -d --input-type json myfile.json.enc
When operating on stdin, use the ``--input-type`` and ``--output-type`` flags as follows:
.. code:: bash
$ cat myfile.json | sops --input-type json --output-type json -d /dev/stdin
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``:
.. code:: yaml
bill-to: &id001
street: |
123 Tornado Alley
Suite 16
city: East Centerville
state: KS
ship-to: *id001
``sops`` 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:
.. code:: yaml
---
data: foo
---
data: bar
Note 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:
.. code:: yaml
---
- some
- array
- elements
But this one will work because the ``sops`` key can be added at the same level as the
``data`` key.
.. code:: yaml
data:
- some
- array
- elements
Similarly, with ``JSON`` arrays, this document will not work:
.. code:: json
[
"some",
"array",
"elements"
]
But this one will work just fine:
.. code:: json
{
"data": [
"some",
"array",
"elements"
]
}
Examples
--------
Take a look into the `examples <https://github.com/mozilla/sops/tree/master/examples>`_ folder for detailed use cases of sops in a CI environment. The section below describes specific tips for common use cases.
Creating a new file
~~~~~~~~~~~~~~~~~~~
The command below creates a new file with a data key encrypted by KMS and PGP.
.. code:: bash
$ sops --kms "arn:aws:kms:us-west-2:927034868273:key/fe86dd69-4132-404c-ab86-4269956b4500" --pgp C9CAB0AF1165060DB58D6D6B2653B624D620786D /path/to/new/file.yaml
Encrypting 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 flag ``-e`` to
encrypt the file, and redirect the output to a destination file.
.. code:: bash
$ 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
Decrypt the file with ``-d``.
.. code:: bash
$ sops -d /path/to/new/encrypted/file.yaml
Encrypt 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.
.. code:: bash
# file.yaml is in cleartext
$ sops -e -i /path/to/existing/file.yaml
# file.yaml is now encrypted
$ sops -d -i /path/to/existing/file.yaml
# file.yaml is back in cleartext
Encrypting binary files
~~~~~~~~~~~~~~~~~~~~~~~
``sops`` primary use case is encrypting YAML and JSON 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.
.. code::
$ 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 -e -i /tmp/somerandom
please wait while a data encryption key is being generated and stored securely
$ sops -d -i /tmp/somerandom
$ sha512sum /tmp/somerandom
9589bb20280e9d381f7a192000498c994e921b3cdb11d2ef5a986578dc2239a340b25ef30691bac72bdb14028270828dad7e8bd31e274af9828c40d216e60cbe /tmp/somerandom
Extract 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.
.. code:: bash
$ sops -d --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.
.. code:: bash
$ sops -d --extract '["an_array"][1]' ~/git/svc/sops/example.yaml
secretuser2
Set 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 line flag. This is useful to
set specific values, like keys, without needing an editor.
.. code:: bash
$ sops --set '["app2"]["key"] "app2keystringvalue"' ~/git/svc/sops/example.yaml
The tree path syntax uses regular python dictionary syntax, without the
variable name. Set to keys by naming them, and array elements by
numbering them.
.. code:: bash
$ sops --set '["an_array"][1] "secretuser2"' ~/git/svc/sops/example.yaml
The value must be formatted as json.
.. code:: bash
$ sops --set '["an_array"][1] {"uid1":null,"uid2":1000,"uid3":["bob"]}' ~/git/svc/sops/example.yaml
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.
.. code::
*.yaml diff=sopsdiffer
Here 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.
.. code:: bash
$ git config diff.sopsdiffer.textconv "sops -d"
$ grep -A 1 sopsdiffer .git/config
[diff "sopsdiffer"]
textconv = "sops -d"
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 and JSON files, not on BINARY files.
By default, ``sops`` encrypts all the values of a YAML or JSON 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.
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:
.. code:: bash
$ 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
navigate the file, like ``metadata`` which contains the secrets' names.
Conversely, you can opt in to only left 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:
.. code:: bash
$ 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.
You can also specify these options in the ``.sops.yaml`` config file.
Note: these four options ``--unencrypted-suffix``, ``--encrypted-suffix``, ``--encrypted-regex`` and ``--unencrypted-regex`` are
mutually exclusive and cannot all be used in the same file.
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 under ``sops.kms`` and ``sops.pgp``.
For KMS:
.. code:: yaml
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-047fa387b27e
For PGP:
.. code:: yaml
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`.
Motivation
----------
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:
1. Secrets must be stored in YAML files for easy integration into hiera
2. 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).
3. 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.
4. 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 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 and JSON 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:
.. code:: bash
$ sops --pgp "E60892BB9BD89A69F759A1A0A3D652173B763E8F,84050F1D61AF7C230A12217687DF65059EF093D3,85D77543B3D624B63CEA9E6DBC17301B491B3F21" mynewfile.yaml
Threat 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.
Backward compatibility
----------------------
``sops`` will remain backward compatible on the major version, meaning that all
improvements brought to the 1.X and 2.X branches (current) will maintain the
file format introduced in **1.0**.
Security
--------
Please report security issues to security at mozilla dot org, or by using one
of the contact method available here: `https://www.mozilla.org/en-US/security/#For_Developers <https://www.mozilla.org/en-US/security/#For_Developers>`_
License
-------
Mozilla Public License Version 2.0
Authors
-------
The core team is composed of:
* AJ Banhken @ajvb
The original authors were:
* Adrian Utrilla @autrilla
* Julien Vehent @jvehent
And a whole bunch of `contributors <https://github.com/mozilla/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...
07070100000011000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000000F00000000sops-3.7.3/aes07070100000012000081A400000000000000000000000162794F9300001562000000000000000000000000000000000000001900000000sops-3.7.3/aes/cipher.go/*
Package aes defines a Cipher that uses 256-bit AES-GCM authenticated encryption to encrypt values the SOPS tree.
*/
package aes //import "go.mozilla.org/sops/v3/aes"
import (
cryptoaes "crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/sirupsen/logrus"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/logging"
)
var log *logrus.Logger
func init() {
log = logging.NewLogger("AES")
}
type encryptedValue struct {
data []byte
iv []byte
tag []byte
datatype string
}
const nonceSize int = 32
type stashKey struct {
additionalData string
plaintext interface{}
}
// Cipher encrypts and decrypts data keys with AES GCM 256
type Cipher struct {
// stash is a map that stores IVs for reuse, so that the ciphertext doesn't change when decrypting and reencrypting
// the same values.
stash map[stashKey][]byte
}
// NewCipher is the constructor for a new Cipher object
func NewCipher() Cipher {
return Cipher{
stash: make(map[stashKey][]byte),
}
}
var encre = regexp.MustCompile(`^ENC\[AES256_GCM,data:(.+),iv:(.+),tag:(.+),type:(.+)\]`)
func parse(value string) (*encryptedValue, error) {
matches := encre.FindStringSubmatch(value)
if matches == nil {
return nil, fmt.Errorf("Input string %s does not match sops' data format", value)
}
data, err := base64.StdEncoding.DecodeString(matches[1])
if err != nil {
return nil, fmt.Errorf("Error base64-decoding data: %s", err)
}
iv, err := base64.StdEncoding.DecodeString(matches[2])
if err != nil {
return nil, fmt.Errorf("Error base64-decoding iv: %s", err)
}
tag, err := base64.StdEncoding.DecodeString(matches[3])
if err != nil {
return nil, fmt.Errorf("Error base64-decoding tag: %s", err)
}
datatype := string(matches[4])
return &encryptedValue{data, iv, tag, datatype}, nil
}
// Decrypt takes a sops-format value string and a key and returns the decrypted value and a stash value
func (c Cipher) Decrypt(ciphertext string, key []byte, additionalData string) (plaintext interface{}, err error) {
if isEmpty(ciphertext) {
return "", nil
}
encryptedValue, err := parse(ciphertext)
if err != nil {
return nil, err
}
aescipher, err := cryptoaes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCMWithNonceSize(aescipher, len(encryptedValue.iv))
if err != nil {
return nil, err
}
data := append(encryptedValue.data, encryptedValue.tag...)
decryptedBytes, err := gcm.Open(nil, encryptedValue.iv, data, []byte(additionalData))
if err != nil {
return nil, fmt.Errorf("Could not decrypt with AES_GCM: %s", err)
}
decryptedValue := string(decryptedBytes)
switch encryptedValue.datatype {
case "str":
plaintext = decryptedValue
case "int":
plaintext, err = strconv.Atoi(decryptedValue)
case "float":
plaintext, err = strconv.ParseFloat(decryptedValue, 64)
case "bytes":
plaintext = decryptedBytes
case "bool":
plaintext, err = strconv.ParseBool(decryptedValue)
case "comment":
plaintext = sops.Comment{Value: decryptedValue}
default:
return nil, fmt.Errorf("Unknown datatype: %s", encryptedValue.datatype)
}
c.stash[stashKey{plaintext: plaintext, additionalData: additionalData}] = encryptedValue.iv
return plaintext, err
}
func isEmpty(value interface{}) bool {
switch value := value.(type) {
case string:
return value == ""
case []byte:
return len(value) == 0
case sops.Comment:
return isEmpty(value.Value)
default:
return false
}
}
// Encrypt takes one of (string, int, float, bool) and encrypts it with the provided key and additional auth data, returning a sops-format encrypted string.
func (c Cipher) Encrypt(plaintext interface{}, key []byte, additionalData string) (ciphertext string, err error) {
if isEmpty(plaintext) {
return "", nil
}
aescipher, err := cryptoaes.NewCipher(key)
if err != nil {
return "", fmt.Errorf("Could not initialize AES GCM encryption cipher: %s", err)
}
var iv []byte
if stash, ok := c.stash[stashKey{plaintext: plaintext, additionalData: additionalData}]; !ok {
iv = make([]byte, nonceSize)
_, err = rand.Read(iv)
if err != nil {
return "", fmt.Errorf("Could not generate random bytes for IV: %s", err)
}
} else {
iv = stash
}
gcm, err := cipher.NewGCMWithNonceSize(aescipher, nonceSize)
if err != nil {
return "", fmt.Errorf("Could not create GCM: %s", err)
}
var plainBytes []byte
var encryptedType string
switch value := plaintext.(type) {
case string:
encryptedType = "str"
plainBytes = []byte(value)
case int:
encryptedType = "int"
plainBytes = []byte(strconv.Itoa(value))
case float64:
encryptedType = "float"
// The Python version encodes floats without padding 0s after the decimal point.
plainBytes = []byte(strconv.FormatFloat(value, 'f', -1, 64))
case bool:
encryptedType = "bool"
// The Python version encodes booleans with Titlecase
plainBytes = []byte(strings.Title(strconv.FormatBool(value)))
case sops.Comment:
encryptedType = "comment"
plainBytes = []byte(value.Value)
default:
return "", fmt.Errorf("Value to encrypt has unsupported type %T", value)
}
out := gcm.Seal(nil, iv, plainBytes, []byte(additionalData))
return fmt.Sprintf("ENC[AES256_GCM,data:%s,iv:%s,tag:%s,type:%s]",
base64.StdEncoding.EncodeToString(out[:len(out)-cryptoaes.BlockSize]),
base64.StdEncoding.EncodeToString(iv),
base64.StdEncoding.EncodeToString(out[len(out)-cryptoaes.BlockSize:]),
encryptedType), nil
}
07070100000013000081A400000000000000000000000162794F9300000AED000000000000000000000000000000000000001E00000000sops-3.7.3/aes/cipher_test.gopackage aes
import (
"crypto/rand"
"strings"
"testing"
"testing/quick"
"github.com/stretchr/testify/assert"
"go.mozilla.org/sops/v3"
)
func TestDecrypt(t *testing.T) {
expected := "foo"
key := []byte(strings.Repeat("f", 32))
message := `ENC[AES256_GCM,data:oYyi,iv:MyIDYbT718JRr11QtBkcj3Dwm4k1aCGZBVeZf0EyV8o=,tag:t5z2Z023Up0kxwCgw1gNxg==,type:str]`
decryption, err := NewCipher().Decrypt(message, key, "bar:")
if err != nil {
t.Errorf("%s", err)
}
if decryption != expected {
t.Errorf("Decrypt(\"%s\", \"%s\") == \"%s\", expected %s", message, key, decryption, expected)
}
}
func TestDecryptInvalidAad(t *testing.T) {
message := `ENC[AES256_GCM,data:oYyi,iv:MyIDYbT718JRr11QtBkcj3Dwm4k1aCGZBVeZf0EyV8o=,tag:t5z2Z023Up0kxwCgw1gNxg==,type:str]`
_, err := NewCipher().Decrypt(message, []byte(strings.Repeat("f", 32)), "")
if err == nil {
t.Errorf("Decrypting with an invalid AAC should fail")
}
}
func TestRoundtripString(t *testing.T) {
f := func(x, aad string) bool {
key := make([]byte, 32)
rand.Read(key)
s, err := NewCipher().Encrypt(x, key, aad)
if err != nil {
log.Println(err)
return false
}
d, err := NewCipher().Decrypt(s, key, aad)
if err != nil {
return false
}
return x == d
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
func TestRoundtripFloat(t *testing.T) {
key := []byte(strings.Repeat("f", 32))
f := func(x float64) bool {
s, err := NewCipher().Encrypt(x, key, "")
if err != nil {
log.Println(err)
return false
}
d, err := NewCipher().Decrypt(s, key, "")
if err != nil {
return false
}
return x == d
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
func TestRoundtripInt(t *testing.T) {
key := []byte(strings.Repeat("f", 32))
f := func(x int) bool {
s, err := NewCipher().Encrypt(x, key, "")
if err != nil {
log.Println(err)
return false
}
d, err := NewCipher().Decrypt(s, key, "")
if err != nil {
return false
}
return x == d
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
func TestRoundtripBool(t *testing.T) {
key := []byte(strings.Repeat("f", 32))
f := func(x bool) bool {
s, err := NewCipher().Encrypt(x, key, "")
if err != nil {
log.Println(err)
return false
}
d, err := NewCipher().Decrypt(s, key, "")
if err != nil {
return false
}
return x == d
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
func TestEncryptEmptyComment(t *testing.T) {
key := []byte(strings.Repeat("f", 32))
s, err := NewCipher().Encrypt(sops.Comment{}, key, "")
assert.Nil(t, err)
assert.Equal(t, "", s)
}
func TestDecryptEmptyValue(t *testing.T) {
key := []byte(strings.Repeat("f", 32))
s, err := NewCipher().Decrypt("", key, "")
assert.Nil(t, err)
assert.Equal(t, "", s)
}
07070100000014000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000000F00000000sops-3.7.3/age07070100000015000081A400000000000000000000000162794F9300000179000000000000000000000000000000000000001800000000sops-3.7.3/age/keys.txt# created: 2020-07-18T03:16:47-07:00
# public key: age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw
AGE-SECRET-KEY-1NJT5YCS2LWU4V4QAJQ6R4JNU7LXPDX602DZ9NUFANVU5GDTGUWCQ5T59M6
# created: 2021-12-12T01:39:30+01:00
# public key: age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep
AGE-SECRET-KEY-1T0Z66WSXS6RMNCPSL7P2E8N4Q7SUD8VMG9ND27S08JL7Y2XAU9EQECHDS707070100000016000081A400000000000000000000000162794F93000017E3000000000000000000000000000000000000001C00000000sops-3.7.3/age/keysource.gopackage age
import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"filippo.io/age"
"filippo.io/age/armor"
"github.com/sirupsen/logrus"
"go.mozilla.org/sops/v3/logging"
)
var log *logrus.Logger
func init() {
log = logging.NewLogger("AGE")
}
const SopsAgeKeyEnv = "SOPS_AGE_KEY"
const SopsAgeKeyFileEnv = "SOPS_AGE_KEY_FILE"
// MasterKey is an age key used to encrypt and decrypt sops' data key.
type MasterKey struct {
Identity string // a Bech32-encoded private key
Recipient string // a Bech32-encoded public key
EncryptedKey string // a sops data key encrypted with age
parsedRecipient *age.X25519Recipient // a parsed age public key
}
// Encrypt takes a sops data key, encrypts it with age and stores the result in the EncryptedKey field.
func (key *MasterKey) Encrypt(datakey []byte) error {
buffer := &bytes.Buffer{}
if key.parsedRecipient == nil {
parsedRecipient, err := parseRecipient(key.Recipient)
if err != nil {
log.WithField("recipient", key.parsedRecipient).Error("Encryption failed")
return err
}
key.parsedRecipient = parsedRecipient
}
aw := armor.NewWriter(buffer)
w, err := age.Encrypt(aw, key.parsedRecipient)
if err != nil {
return fmt.Errorf("failed to open file for encrypting sops data key with age: %w", err)
}
if _, err := w.Write(datakey); err != nil {
log.WithField("recipient", key.parsedRecipient).Error("Encryption failed")
return fmt.Errorf("failed to encrypt sops data key with age: %w", err)
}
if err := w.Close(); err != nil {
log.WithField("recipient", key.parsedRecipient).Error("Encryption failed")
return fmt.Errorf("failed to close file for encrypting sops data key with age: %w", err)
}
if err := aw.Close(); err != nil {
log.WithField("recipient", key.parsedRecipient).Error("Encryption failed")
return fmt.Errorf("failed to close armored writer: %w", err)
}
key.EncryptedKey = buffer.String()
log.WithField("recipient", key.parsedRecipient).Info("Encryption succeeded")
return nil
}
// EncryptIfNeeded encrypts the provided sops' data key and encrypts it if it hasn't been encrypted yet.
func (key *MasterKey) EncryptIfNeeded(datakey []byte) error {
if key.EncryptedKey == "" {
return key.Encrypt(datakey)
}
return nil
}
// EncryptedDataKey returns the encrypted data key this master key holds.
func (key *MasterKey) EncryptedDataKey() []byte {
return []byte(key.EncryptedKey)
}
// SetEncryptedDataKey sets the encrypted data key for this master key.
func (key *MasterKey) SetEncryptedDataKey(enc []byte) {
key.EncryptedKey = string(enc)
}
// Decrypt decrypts the EncryptedKey field with the age identity and returns the result.
func (key *MasterKey) Decrypt() ([]byte, error) {
var ageKeyReader io.Reader
var ageKeyReaderName string
if ageKeyReader == nil {
ageKey, ok := os.LookupEnv(SopsAgeKeyEnv)
if ok {
ageKeyReader = strings.NewReader(ageKey)
ageKeyReaderName = "environment variable"
}
}
if ageKeyReader == nil {
ageKeyFilePath, ok := os.LookupEnv(SopsAgeKeyFileEnv)
if ok {
ageKeyFile, err := os.Open(ageKeyFilePath)
if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
}
defer ageKeyFile.Close()
ageKeyReader = ageKeyFile
ageKeyReaderName = ageKeyFilePath
}
}
if ageKeyReader == nil {
userConfigDir, err := os.UserConfigDir()
if err != nil {
return nil, fmt.Errorf("user config directory could not be determined: %w", err)
}
ageKeyFilePath := filepath.Join(userConfigDir, "sops", "age", "keys.txt")
ageKeyFile, err := os.Open(ageKeyFilePath)
if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
}
defer ageKeyFile.Close()
ageKeyReader = ageKeyFile
ageKeyReaderName = ageKeyFilePath
}
identities, err := age.ParseIdentities(ageKeyReader)
if err != nil {
return nil, err
}
src := bytes.NewReader([]byte(key.EncryptedKey))
ar := armor.NewReader(src)
r, err := age.Decrypt(ar, identities...)
if err != nil {
return nil, fmt.Errorf("no age identity found in %q that could decrypt the data", ageKeyReaderName)
}
var b bytes.Buffer
if _, err := io.Copy(&b, r); err != nil {
return nil, fmt.Errorf("failed to copy decrypted data into bytes.Buffer: %w", err)
}
return b.Bytes(), nil
}
// NeedsRotation returns whether the data key needs to be rotated or not.
func (key *MasterKey) NeedsRotation() bool {
return false
}
// ToString converts the key to a string representation.
func (key *MasterKey) ToString() string {
return key.Recipient
}
// ToMap converts the MasterKey to a map for serialization purposes.
func (key *MasterKey) ToMap() map[string]interface{} {
return map[string]interface{}{"recipient": key.Recipient, "enc": key.EncryptedKey}
}
// MasterKeysFromRecipients takes a comma-separated list of Bech32-encoded public keys and returns a
// slice of new MasterKeys.
func MasterKeysFromRecipients(commaSeparatedRecipients string) ([]*MasterKey, error) {
if commaSeparatedRecipients == "" {
// otherwise Split returns [""] and MasterKeyFromRecipient is unhappy
return make([]*MasterKey, 0), nil
}
recipients := strings.Split(commaSeparatedRecipients, ",")
var keys []*MasterKey
for _, recipient := range recipients {
key, err := masterKeyFromRecipient(recipient)
if err != nil {
return nil, err
}
keys = append(keys, key)
}
return keys, nil
}
// masterKeyFromRecipient takes a Bech32-encoded public key and returns a new MasterKey.
func masterKeyFromRecipient(recipient string) (*MasterKey, error) {
recipient = strings.TrimSpace(recipient)
parsedRecipient, err := parseRecipient(recipient)
if err != nil {
return nil, err
}
return &MasterKey{
Recipient: recipient,
parsedRecipient: parsedRecipient,
}, nil
}
// parseRecipient attempts to parse a string containing an encoded age public key
func parseRecipient(recipient string) (*age.X25519Recipient, error) {
parsedRecipient, err := age.ParseX25519Recipient(recipient)
if err != nil {
return nil, fmt.Errorf("failed to parse input as Bech32-encoded age public key: %w", err)
}
return parsedRecipient, nil
}
07070100000017000081A400000000000000000000000162794F93000014B7000000000000000000000000000000000000002100000000sops-3.7.3/age/keysource_test.gopackage age
import (
"io/ioutil"
"os"
"path"
"runtime"
"testing"
"github.com/stretchr/testify/assert"
)
func TestMasterKeysFromRecipientsEmpty(t *testing.T) {
assert := assert.New(t)
commaSeparatedRecipients := ""
recipients, err := MasterKeysFromRecipients(commaSeparatedRecipients)
assert.NoError(err)
assert.Equal(recipients, make([]*MasterKey, 0))
}
func TestMasterKeyFromRecipientWithLeadingAndTrailingSpacesSingle(t *testing.T) {
assert := assert.New(t)
commaSeparatedRecipients := " age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw "
keys, err := MasterKeysFromRecipients(commaSeparatedRecipients)
assert.NoError(err)
assert.Equal(len(keys), 1)
assert.Equal(keys[0].Recipient, "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw")
}
func TestMasterKeyFromRecipientWithLeadingAndTrailingSpacesMultiple(t *testing.T) {
assert := assert.New(t)
commaSeparatedRecipients := " age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw , age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep "
keys, err := MasterKeysFromRecipients(commaSeparatedRecipients)
assert.NoError(err)
assert.Equal(len(keys), 2)
assert.Equal(keys[0].Recipient, "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw")
assert.Equal(keys[1].Recipient, "age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep")
}
func TestMasterKeysFromRecipientsWithSingle(t *testing.T) {
assert := assert.New(t)
commaSeparatedRecipients := "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw"
keys, err := MasterKeysFromRecipients(commaSeparatedRecipients)
assert.NoError(err)
assert.Equal(len(keys), 1)
assert.Equal(keys[0].Recipient, "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw")
}
func TestMasterKeysFromRecipientsWithMultiple(t *testing.T) {
assert := assert.New(t)
commaSeparatedRecipients := "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw,age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep"
keys, err := MasterKeysFromRecipients(commaSeparatedRecipients)
assert.NoError(err)
assert.Equal(len(keys), 2)
assert.Equal(keys[0].Recipient, "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw")
assert.Equal(keys[1].Recipient, "age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep")
}
func TestAge(t *testing.T) {
assert := assert.New(t)
commaSeparatedRecipients := "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw,age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep"
keys, err := MasterKeysFromRecipients(commaSeparatedRecipients)
assert.NoError(err)
assert.Equal(len(keys), 2)
assert.Equal(keys[0].Recipient, "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw")
assert.Equal(keys[1].Recipient, "age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep")
dataKey := []byte("abcdefghijklmnopqrstuvwxyz123456")
for _, key := range keys {
err = key.Encrypt(dataKey)
assert.NoError(err)
_, filename, _, _ := runtime.Caller(0)
err = os.Setenv("SOPS_AGE_KEY_FILE", path.Join(path.Dir(filename), "keys.txt"))
assert.NoError(err)
decryptedKey, err := key.Decrypt()
assert.NoError(err)
assert.Equal(dataKey, decryptedKey)
}
}
func TestAgeDotEnv(t *testing.T) {
assert := assert.New(t)
commaSeparatedRecipients := "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw,age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep"
keys, err := MasterKeysFromRecipients(commaSeparatedRecipients)
assert.NoError(err)
assert.Equal(len(keys), 2)
assert.Equal(keys[0].Recipient, "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw")
assert.Equal(keys[1].Recipient, "age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep")
dotenv := `IMAGE_PREFIX=repo/service-
APPLICATION_KEY=K6pfAWuUVND9Fz5SC7jmA6pfAWuUVND9Fz5SC7jmA
KEY_ID=003683d721f2ae683d721f2a1
DOMAIN=files.127.0.0.1.nip.io`
dataKey := []byte(dotenv)
err = keys[0].Encrypt(dataKey)
assert.NoError(err)
_, filename, _, _ := runtime.Caller(0)
err = os.Setenv(SopsAgeKeyFileEnv, path.Join(path.Dir(filename), "keys.txt"))
defer os.Unsetenv(SopsAgeKeyFileEnv)
assert.NoError(err)
decryptedKey, err := keys[0].Decrypt()
assert.NoError(err)
assert.Equal(dataKey, decryptedKey)
}
func TestAgeEnv(t *testing.T) {
assert := assert.New(t)
commaSeparatedRecipients := "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw,age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep"
keys, err := MasterKeysFromRecipients(commaSeparatedRecipients)
assert.NoError(err)
assert.Equal(len(keys), 2)
assert.Equal(keys[0].Recipient, "age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw")
assert.Equal(keys[1].Recipient, "age1tmaae3ld5vpevmsh5yacsauzx8jetg300mpvc4ugp5zr5l6ssq9sla97ep")
dataKey := []byte("abcdefghijklmnopqrstuvwxyz123456")
err = keys[0].Encrypt(dataKey)
assert.NoError(err)
_, filename, _, _ := runtime.Caller(0)
keysBytes, err := ioutil.ReadFile(path.Join(path.Dir(filename), "keys.txt"))
assert.NoError(err)
err = os.Setenv(SopsAgeKeyEnv, string(keysBytes))
defer os.Unsetenv(SopsAgeKeyEnv)
assert.NoError(err)
decryptedKey, err := keys[0].Decrypt()
assert.NoError(err)
assert.Equal(dataKey, decryptedKey)
}
07070100000018000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001100000000sops-3.7.3/audit07070100000019000081A400000000000000000000000162794F9300001352000000000000000000000000000000000000001A00000000sops-3.7.3/audit/audit.gopackage audit
import (
"database/sql"
"flag"
"fmt"
"io/ioutil"
"os/user"
"github.com/pkg/errors"
// empty import as per https://godoc.org/github.com/lib/pq
_ "github.com/lib/pq"
"gopkg.in/yaml.v3"
"github.com/sirupsen/logrus"
"go.mozilla.org/sops/v3/logging"
)
var log *logrus.Logger
func init() {
log = logging.NewLogger("AUDIT")
confBytes, err := ioutil.ReadFile(configFile)
if err != nil {
log.WithField("error", err).Debugf("Error reading config")
return
}
var conf config
err = yaml.Unmarshal(confBytes, &conf)
if err != nil {
log.WithField("error", err).Panicf("Error unmarshalling config")
}
// If we are running test, then don't create auditors.
// This is pretty hacky, but doing it The Right Way would require
// restructuring SOPS to use dependency injection instead of just using
// globals everywhere.
if flag.Lookup("test.v") != nil {
return
}
var auditErrors []error
for _, pgConf := range conf.Backends.Postgres {
auditDb, err := NewPostgresAuditor(pgConf.ConnStr)
if err != nil {
auditErrors = append(auditErrors, errors.Wrap(err, fmt.Sprintf("connectStr: %s, err", pgConf.ConnStr)))
}
auditors = append(auditors, auditDb)
}
if len(auditErrors) > 0 {
log.Errorf("connecting to audit database, defined in %s", configFile)
for _, err := range auditErrors {
log.Error(err)
}
log.Fatal("one or more audit backends reported errors, exiting")
}
}
// TODO: Make platform agnostic
const configFile = "/etc/sops/audit.yaml"
type config struct {
Backends struct {
Postgres []struct {
ConnStr string `yaml:"connection_string"`
} `yaml:"postgres"`
} `yaml:"backends"`
}
var auditors []Auditor
// SubmitEvent handles an event for all auditors
func SubmitEvent(event interface{}) {
for _, auditor := range auditors {
auditor.Handle(event)
}
}
// Register registers a new Auditor in the global auditor list
func Register(auditor Auditor) {
auditors = append(auditors, auditor)
}
// Auditor is notified when noteworthy events happen,
// for example when a file is encrypted or decrypted.
type Auditor interface {
// Handle() takes an audit event and attempts to persists it;
// how it is persisted and how errors are handled is up to the
// implementation of this interface.
Handle(event interface{})
}
// DecryptEvent contains fields relevant to a decryption event
type DecryptEvent struct {
File string
}
// EncryptEvent contains fields relevant to an encryption event
type EncryptEvent struct {
File string
}
// RotateEvent contains fields relevant to a key rotation event
type RotateEvent struct {
File string
}
// PostgresAuditor is a Postgres SQL DB implementation of the Auditor interface.
// It persists the audit event by writing a row to the 'audit_event' table.
// Errors with writing to the database will output a log message and the
// process will exit with status set to 1
type PostgresAuditor struct {
DB *sql.DB
}
// NewPostgresAuditor is the constructor for a new PostgresAuditor struct
// initialized with the given db connection string
func NewPostgresAuditor(connStr string) (*PostgresAuditor, error) {
db, err := sql.Open("postgres", connStr)
pg := &PostgresAuditor{DB: db}
if err != nil {
return pg, err
}
var result int
err = pg.DB.QueryRow("SELECT 1").Scan(&result)
if err != nil {
return pg, fmt.Errorf("Pinging audit database failed: %s", err)
} else if result != 1 {
return pg, fmt.Errorf("Database malfunction: SELECT 1 should return 1, but returned %d", result)
}
return pg, nil
}
// Handle persists the audit event by writing a row to the
// 'audit_event' postgres table
func (p *PostgresAuditor) Handle(event interface{}) {
u, err := user.Current()
if err != nil {
log.Fatalf("Error getting current user for auditing: %s", err)
}
switch event := event.(type) {
case DecryptEvent:
// Save the event to the database
log.WithField("file", event.File).
Debug("Saving decrypt event to database")
_, err = p.DB.Exec("INSERT INTO audit_event (action, username, file) VALUES ($1, $2, $3)", "decrypt", u.Username, event.File)
if err != nil {
log.Fatalf("Failed to insert audit record: %s", err)
}
case EncryptEvent:
// Save the event to the database
log.WithField("file", event.File).
Debug("Saving encrypt event to database")
_, err = p.DB.Exec("INSERT INTO audit_event (action, username, file) VALUES ($1, $2, $3)", "encrypt", u.Username, event.File)
if err != nil {
log.Fatalf("Failed to insert audit record: %s", err)
}
case RotateEvent:
// Save the event to the database
log.WithField("file", event.File).
Debug("Saving rotate event to database")
_, err = p.DB.Exec("INSERT INTO audit_event (action, username, file) VALUES ($1, $2, $3)", "rotate", u.Username, event.File)
if err != nil {
log.Fatalf("Failed to insert audit record: %s", err)
}
default:
log.WithField("type", fmt.Sprintf("%T", event)).
Info("Received unknown event")
}
}
0707010000001A000081A400000000000000000000000162794F930000013F000000000000000000000000000000000000001C00000000sops-3.7.3/audit/schema.sqlCREATE TABLE audit_event (
id SERIAL PRIMARY KEY,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
action TEXT,
username TEXT,
file TEXT
);
CREATE ROLE sops WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB LOGIN PASSWORD 'sops';
GRANT INSERT ON audit_event TO sops;
GRANT USAGE ON audit_event_id_seq TO sops;
0707010000001B000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001000000000sops-3.7.3/azkv0707010000001C000081A400000000000000000000000162794F93000022C7000000000000000000000000000000000000001D00000000sops-3.7.3/azkv/keysource.go/*
Package azkv contains an implementation of the go.mozilla.org/sops/v3/keys.MasterKey interface that encrypts and decrypts the
data key using Azure Key Vault with the Azure Go SDK.
*/
package azkv //import "go.mozilla.org/sops/v3/azkv"
import (
"context"
"encoding/base64"
"errors"
"fmt"
"os"
"regexp"
"strings"
"time"
"go.mozilla.org/sops/v3/logging"
"github.com/Azure/azure-sdk-for-go/services/keyvault/2016-10-01/keyvault"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/azure/auth"
"github.com/sirupsen/logrus"
)
var log *logrus.Logger
func init() {
log = logging.NewLogger("AZKV")
}
// MasterKey is a Azure Key Vault key used to encrypt and decrypt sops' data key.
type MasterKey struct {
VaultURL string
Name string
Version string
EncryptedKey string
CreationDate time.Time
}
func newKeyVaultClient() (keyvault.BaseClient, error) {
var err error
c := keyvault.New()
c.Authorizer, err = newAuthorizer()
if err != nil {
log.WithError(err).Error("Failed to create Azure authorizer")
return c, err
}
return c, nil
}
// newAuthorizer returns the correct authorizer for the given settings and/or based on the value
// of the AZURE_AUTH_METHOD environment variable, which may be one of:
// clientcredentials, clientcertificate, usernamepassword, msi, or cli (default).
func newAuthorizer() (autorest.Authorizer, error) {
settings := struct {
authMethod string
tenantID string
clientID string
clientSecret string
certificatePath string
certificatePassword string
username string
password string
envName string
resource string
environment azure.Environment
}{
authMethod: os.Getenv("AZURE_AUTH_METHOD"),
tenantID: os.Getenv("AZURE_TENANT_ID"),
clientID: os.Getenv("AZURE_CLIENT_ID"),
clientSecret: os.Getenv("AZURE_CLIENT_SECRET"),
certificatePath: os.Getenv("AZURE_CERTIFICATE_PATH"),
certificatePassword: os.Getenv("AZURE_CERTIFICATE_PASSWORD"),
username: os.Getenv("AZURE_USERNAME"),
password: os.Getenv("AZURE_PASSWORD"),
envName: os.Getenv("AZURE_ENVIRONMENT"),
resource: os.Getenv("AZURE_AD_RESOURCE"),
}
settings.environment = azure.PublicCloud
if settings.envName != "" {
val, err := azure.EnvironmentFromName(settings.envName)
if err != nil {
return nil, err
}
settings.environment = val
}
if settings.resource == "" {
settings.resource = strings.TrimSuffix(settings.environment.KeyVaultEndpoint, "/")
}
if os.Getenv("MSI_ENDPOINT") != "" {
settings.authMethod = "msi"
}
// 1. Client credentials
if (settings.clientSecret != "") || settings.authMethod == "clientcredentials" {
config := auth.NewClientCredentialsConfig(settings.clientID, settings.clientSecret, settings.tenantID)
config.AADEndpoint = settings.environment.ActiveDirectoryEndpoint
config.Resource = settings.resource
return config.Authorizer()
}
// 2. Client Certificate
if (settings.certificatePath != "") || settings.authMethod == "clientcertificate" {
config := auth.NewClientCertificateConfig(settings.certificatePath, settings.certificatePassword, settings.clientID, settings.tenantID)
config.AADEndpoint = settings.environment.ActiveDirectoryEndpoint
config.Resource = settings.resource
return config.Authorizer()
}
// 3. Username Password
if (settings.username != "" && settings.password != "") || settings.authMethod == "usernamepassword" {
config := auth.NewUsernamePasswordConfig(settings.username, settings.password, settings.clientID, settings.tenantID)
config.AADEndpoint = settings.environment.ActiveDirectoryEndpoint
config.Resource = settings.resource
return config.Authorizer()
}
// 4. MSI
if settings.authMethod == "msi" {
config := auth.NewMSIConfig()
config.Resource = settings.resource
config.ClientID = settings.clientID
return config.Authorizer()
}
// 5. Device Code
if settings.authMethod == "devicecode" {
// TODO: Removed until we decide how to handle prompt on stdout, etc.
//// TODO: This will be required on every execution. Consider caching.
//config := auth.NewDeviceFlowConfig(settings.clientID, settings.tenantID)
//return config.Authorizer()
return nil, errors.New("device code flow not implemented")
}
// 6. CLI
return auth.NewAuthorizerFromCLIWithResource(settings.resource)
}
// NewMasterKey creates a new MasterKey from an URL, key name and version, setting the creation date to the current date
func NewMasterKey(vaultURL string, keyName string, keyVersion string) *MasterKey {
return &MasterKey{
VaultURL: vaultURL,
Name: keyName,
Version: keyVersion,
CreationDate: time.Now().UTC(),
}
}
// MasterKeysFromURLs takes a comma separated list of Azure Key Vault URLs and returns a slice of new MasterKeys for them
func MasterKeysFromURLs(urls string) ([]*MasterKey, error) {
var keys []*MasterKey
if urls == "" {
return keys, nil
}
for _, s := range strings.Split(urls, ",") {
k, err := NewMasterKeyFromURL(s)
if err != nil {
return nil, err
}
keys = append(keys, k)
}
return keys, nil
}
// NewMasterKeyFromURL takes an Azure Key Vault key URL and returns a new MasterKey
// URL format is {vaultUrl}/keys/{key-name}/{key-version}
func NewMasterKeyFromURL(url string) (*MasterKey, error) {
k := &MasterKey{}
re := regexp.MustCompile("^(https://[^/]+)/keys/([^/]+)/([^/]+)$")
parts := re.FindStringSubmatch(url)
if parts == nil || len(parts) < 2 {
return nil, fmt.Errorf("Could not parse valid key from %q", url)
}
k.VaultURL = parts[1]
k.Name = parts[2]
k.Version = parts[3]
k.CreationDate = time.Now().UTC()
return k, nil
}
// EncryptedDataKey returns the encrypted data key this master key holds
func (key *MasterKey) EncryptedDataKey() []byte {
return []byte(key.EncryptedKey)
}
// SetEncryptedDataKey sets the encrypted data key for this master key
func (key *MasterKey) SetEncryptedDataKey(enc []byte) {
key.EncryptedKey = string(enc)
}
// Encrypt takes a sops data key, encrypts it with Key Vault and stores the result in the EncryptedKey field
func (key *MasterKey) Encrypt(dataKey []byte) error {
c, err := newKeyVaultClient()
if err != nil {
return err
}
data := base64.RawURLEncoding.EncodeToString(dataKey)
p := keyvault.KeyOperationsParameters{Value: &data, Algorithm: keyvault.RSAOAEP256}
res, err := c.Encrypt(context.Background(), key.VaultURL, key.Name, key.Version, p)
if err != nil {
log.WithError(err).WithFields(logrus.Fields{
"key": key.Name,
"version": key.Version,
}).Error("Encryption failed")
return fmt.Errorf("Failed to encrypt data: %w", err)
}
key.EncryptedKey = *res.Result
log.WithFields(logrus.Fields{
"key": key.Name,
"version": key.Version,
}).Info("Encryption succeeded")
return nil
}
// EncryptIfNeeded encrypts the provided sops' data key and encrypts it if it hasn't been encrypted yet
func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
if key.EncryptedKey == "" {
return key.Encrypt(dataKey)
}
return nil
}
// Decrypt decrypts the EncryptedKey field with Azure Key Vault and returns the result.
func (key *MasterKey) Decrypt() ([]byte, error) {
c, err := newKeyVaultClient()
if err != nil {
return nil, err
}
p := keyvault.KeyOperationsParameters{Value: &key.EncryptedKey, Algorithm: keyvault.RSAOAEP256}
res, err := c.Decrypt(context.TODO(), key.VaultURL, key.Name, key.Version, p)
if err != nil {
log.WithError(err).WithFields(logrus.Fields{
"key": key.Name,
"version": key.Version,
}).Error("Decryption failed")
return nil, fmt.Errorf("Error decrypting key: %w", err)
}
plaintext, err := base64.RawURLEncoding.DecodeString(*res.Result)
if err != nil {
log.WithError(err).WithFields(logrus.Fields{
"key": key.Name,
"version": key.Version,
}).Error("Decryption failed")
return nil, err
}
log.WithFields(logrus.Fields{
"key": key.Name,
"version": key.Version,
}).Info("Decryption succeeded")
return plaintext, nil
}
// NeedsRotation returns whether the data key needs to be rotated or not.
func (key *MasterKey) NeedsRotation() bool {
return time.Since(key.CreationDate) > (time.Hour * 24 * 30 * 6)
}
// ToString converts the key to a string representation
func (key *MasterKey) ToString() string {
return fmt.Sprintf("%s/keys/%s/%s", key.VaultURL, key.Name, key.Version)
}
// ToMap converts the MasterKey to a map for serialization purposes
func (key MasterKey) ToMap() map[string]interface{} {
out := make(map[string]interface{})
out["vaultUrl"] = key.VaultURL
out["key"] = key.Name
out["version"] = key.Version
out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339)
out["enc"] = key.EncryptedKey
return out
}
0707010000001D000081A400000000000000000000000162794F9300000C7D000000000000000000000000000000000000002200000000sops-3.7.3/azkv/keysource_test.gopackage azkv
import (
"flag"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestAzureKeySourceFromUrl(t *testing.T) {
cases := []struct {
name string
input string
expectSuccess bool
expectedFoundKeys int
expectedKeys []MasterKey
}{
{
name: "Single url",
input: "https://test.vault.azure.net/keys/test-key/a2a690a4fcc04166b739da342a912c90",
expectSuccess: true,
expectedFoundKeys: 1,
expectedKeys: []MasterKey{
{
VaultURL: "https://test.vault.azure.net",
Name: "test-key",
Version: "a2a690a4fcc04166b739da342a912c90",
},
},
},
{
name: "Multiple url",
input: "https://test.vault.azure.net/keys/test-key/a2a690a4fcc04166b739da342a912c90,https://test2.vault.azure.net/keys/another-test-key/cf0021e8b743453bae758e7fbf71b60e",
expectSuccess: true,
expectedFoundKeys: 2,
expectedKeys: []MasterKey{
{
VaultURL: "https://test.vault.azure.net",
Name: "test-key",
Version: "a2a690a4fcc04166b739da342a912c90",
},
{
VaultURL: "https://test2.vault.azure.net",
Name: "another-test-key",
Version: "cf0021e8b743453bae758e7fbf71b60e",
},
},
},
{
name: "Single malformed url",
input: "https://test.vault.azure.net/no-keys-here/test-key/a2a690a4fcc04166b739da342a912c90",
expectSuccess: false,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
keys, err := MasterKeysFromURLs(c.input)
if err != nil && c.expectSuccess {
t.Fatalf("Unexpected error %v", err)
} else if err == nil && !c.expectSuccess {
t.Fatal("Expected error, but no error was returned")
}
if c.expectedFoundKeys != len(keys) {
t.Errorf("Unexpected number of keys returned, expected %d, got %d", c.expectedFoundKeys, len(keys))
}
for idx := range keys {
assert.Equal(t, c.expectedKeys[idx].VaultURL, keys[idx].VaultURL)
assert.Equal(t, c.expectedKeys[idx].Name, keys[idx].Name)
assert.Equal(t, c.expectedKeys[idx].Version, keys[idx].Version)
}
})
}
}
func TestKeyToMap(t *testing.T) {
key := MasterKey{
CreationDate: time.Date(2016, time.October, 31, 10, 0, 0, 0, time.UTC),
VaultURL: "https://test.vault.azure.net",
Name: "test-key",
Version: "1",
EncryptedKey: "this is encrypted",
}
assert.Equal(t, map[string]interface{}{
"vaultUrl": key.VaultURL,
"key": key.Name,
"version": key.Version,
"enc": "this is encrypted",
"created_at": "2016-10-31T10:00:00Z",
}, key.ToMap())
}
var azureKeyAcceptanceTestURL = flag.String("azure-key", "", "URL to Azure Key Vault (note that this can incur real costs!)")
func TestRoundtrip(t *testing.T) {
if *azureKeyAcceptanceTestURL == "" {
t.Skip("Azure URL not provided, skipping acceptance test")
}
input := []byte("test-string")
key, err := NewMasterKeyFromURL(*azureKeyAcceptanceTestURL)
if err != nil {
t.Fatal(err)
}
err = key.Encrypt(input)
if err != nil {
t.Fatal(err)
}
output, err := key.Decrypt()
if err != nil {
t.Fatal(err)
}
assert.Equal(t, input, output)
}
0707010000001E000041ED00000000000000000000000362794F9300000000000000000000000000000000000000000000000F00000000sops-3.7.3/bin0707010000001F000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001200000000sops-3.7.3/bin/ci07070100000020000081ED00000000000000000000000162794F9300000324000000000000000000000000000000000000002600000000sops-3.7.3/bin/ci/deploy_dockerhub.sh#!/bin/bash
# THIS IS MEANT TO BE RUN BY CI
set -e
set +x
# Usage: retry MAX CMD...
# Retry CMD up to MAX times. If it fails MAX times, returns failure.
# Example: retry 3 docker push "mozilla/sops:$TAG"
function retry() {
max=$1
shift
count=1
until "$@"; do
count=$((count + 1))
if [[ $count -gt $max ]]; then
return 1
fi
echo "$count / $max"
done
return 0
}
if [[ "$DOCKER_DEPLOY" == "true" ]]; then
# configure docker creds
retry 3 docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
# docker tag and push git branch to dockerhub
if [ -n "$1" ]; then
retry 3 docker push "mozilla/sops:$1" ||
(echo "Couldn't push mozilla/sops:$1" && false)
echo "Pushed mozilla/sops:$1"
fi
fi
07070100000021000041ED00000000000000000000000362794F9300000000000000000000000000000000000000000000000F00000000sops-3.7.3/cmd07070100000022000041ED00000000000000000000000662794F9300000000000000000000000000000000000000000000001400000000sops-3.7.3/cmd/sops07070100000023000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001A00000000sops-3.7.3/cmd/sops/codes07070100000024000081A400000000000000000000000162794F9300000548000000000000000000000000000000000000002300000000sops-3.7.3/cmd/sops/codes/codes.go// Package codes the exit statuses returned by the sops binary
package codes
// Exit statuses returned by the binary
const (
ErrorGeneric int = 1
CouldNotReadInputFile int = 2
CouldNotWriteOutputFile int = 3
ErrorDumpingTree int = 4
ErrorReadingConfig int = 5
ErrorInvalidKMSEncryptionContextFormat int = 6
ErrorInvalidSetFormat int = 7
ErrorConflictingParameters int = 8
ErrorEncryptingMac int = 21
ErrorEncryptingTree int = 23
ErrorDecryptingMac int = 24
ErrorDecryptingTree int = 25
CannotChangeKeysFromNonExistentFile int = 49
MacMismatch int = 51
MacNotFound int = 52
ConfigFileNotFound int = 61
KeyboardInterrupt int = 85
InvalidTreePathFormat int = 91
NoFileSpecified int = 100
CouldNotRetrieveKey int = 128
NoEncryptionKeyFound int = 111
FileHasNotBeenModified int = 200
NoEditorFound int = 201
FailedToCompareVersions int = 202
FileAlreadyEncrypted int = 203
)
07070100000025000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001B00000000sops-3.7.3/cmd/sops/common07070100000026000081A400000000000000000000000162794F930000345C000000000000000000000000000000000000002500000000sops-3.7.3/cmd/sops/common/common.gopackage common
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"time"
"github.com/fatih/color"
wordwrap "github.com/mitchellh/go-wordwrap"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/cmd/sops/codes"
. "go.mozilla.org/sops/v3/cmd/sops/formats"
"go.mozilla.org/sops/v3/keys"
"go.mozilla.org/sops/v3/keyservice"
"go.mozilla.org/sops/v3/kms"
"go.mozilla.org/sops/v3/stores/dotenv"
"go.mozilla.org/sops/v3/stores/ini"
"go.mozilla.org/sops/v3/stores/json"
"go.mozilla.org/sops/v3/stores/yaml"
"go.mozilla.org/sops/v3/version"
"golang.org/x/crypto/ssh/terminal"
"gopkg.in/urfave/cli.v1"
)
// ExampleFileEmitter emits example files. This is used by the `sops` binary
// whenever a new file is created, in order to present the user with a non-empty file
type ExampleFileEmitter interface {
EmitExample() []byte
}
// Store handles marshaling and unmarshaling from SOPS files
type Store interface {
sops.Store
ExampleFileEmitter
}
type storeConstructor = func() Store
func newBinaryStore() Store {
return &json.BinaryStore{}
}
func newDotenvStore() Store {
return &dotenv.Store{}
}
func newIniStore() Store {
return &ini.Store{}
}
func newJsonStore() Store {
return &json.Store{}
}
func newYamlStore() Store {
return &yaml.Store{}
}
var storeConstructors = map[Format]storeConstructor{
Binary: newBinaryStore,
Dotenv: newDotenvStore,
Ini: newIniStore,
Json: newJsonStore,
Yaml: newYamlStore,
}
// DecryptTreeOpts are the options needed to decrypt a tree
type DecryptTreeOpts struct {
// Tree is the tree to be decrypted
Tree *sops.Tree
// KeyServices are the key services to be used for decryption of the data key
KeyServices []keyservice.KeyServiceClient
// IgnoreMac is whether or not to ignore the Message Authentication Code included in the SOPS tree
IgnoreMac bool
// Cipher is the cryptographic cipher to use to decrypt the values inside the tree
Cipher sops.Cipher
}
// DecryptTree decrypts the tree passed in through the DecryptTreeOpts and additionally returns the decrypted data key
func DecryptTree(opts DecryptTreeOpts) (dataKey []byte, err error) {
dataKey, err = opts.Tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices)
if err != nil {
return nil, NewExitError(err, codes.CouldNotRetrieveKey)
}
computedMac, err := opts.Tree.Decrypt(dataKey, opts.Cipher)
if err != nil {
return nil, NewExitError(fmt.Sprintf("Error decrypting tree: %s", err), codes.ErrorDecryptingTree)
}
fileMac, err := opts.Cipher.Decrypt(opts.Tree.Metadata.MessageAuthenticationCode, dataKey, opts.Tree.Metadata.LastModified.Format(time.RFC3339))
if !opts.IgnoreMac {
if fileMac != computedMac {
// If the file has an empty MAC, display "no MAC" instead of not displaying anything
if fileMac == "" {
fileMac = "no MAC"
}
return nil, NewExitError(fmt.Sprintf("MAC mismatch. File has %s, computed %s", fileMac, computedMac), codes.MacMismatch)
}
}
return dataKey, nil
}
// EncryptTreeOpts are the options needed to encrypt a tree
type EncryptTreeOpts struct {
// Tree is the tree to be encrypted
Tree *sops.Tree
// Cipher is the cryptographic cipher to use to encrypt the values inside the tree
Cipher sops.Cipher
// DataKey is the key the cipher should use to encrypt the values inside the tree
DataKey []byte
}
// EncryptTree encrypts the tree passed in through the EncryptTreeOpts
func EncryptTree(opts EncryptTreeOpts) error {
unencryptedMac, err := opts.Tree.Encrypt(opts.DataKey, opts.Cipher)
if err != nil {
return NewExitError(fmt.Sprintf("Error encrypting tree: %s", err), codes.ErrorEncryptingTree)
}
opts.Tree.Metadata.LastModified = time.Now().UTC()
opts.Tree.Metadata.MessageAuthenticationCode, err = opts.Cipher.Encrypt(unencryptedMac, opts.DataKey, opts.Tree.Metadata.LastModified.Format(time.RFC3339))
if err != nil {
return NewExitError(fmt.Sprintf("Could not encrypt MAC: %s", err), codes.ErrorEncryptingMac)
}
return nil
}
// LoadEncryptedFile loads an encrypted SOPS file, returning a SOPS tree
func LoadEncryptedFile(loader sops.EncryptedFileLoader, inputPath string) (*sops.Tree, error) {
fileBytes, err := ioutil.ReadFile(inputPath)
if err != nil {
return nil, NewExitError(fmt.Sprintf("Error reading file: %s", err), codes.CouldNotReadInputFile)
}
path, err := filepath.Abs(inputPath)
if err != nil {
return nil, err
}
tree, err := loader.LoadEncryptedFile(fileBytes)
tree.FilePath = path
return &tree, err
}
// NewExitError returns a cli.ExitError given an error (wrapped in a generic interface{})
// and an exit code to represent the failure
func NewExitError(i interface{}, exitCode int) *cli.ExitError {
if userErr, ok := i.(sops.UserError); ok {
return NewExitError(userErr.UserError(), exitCode)
}
return cli.NewExitError(i, exitCode)
}
// StoreForFormat returns the correct format-specific implementation
// of the Store interface given the format.
func StoreForFormat(format Format) Store {
storeConst, found := storeConstructors[format]
if !found {
storeConst = storeConstructors[Binary] // default
}
return storeConst()
}
// DefaultStoreForPath returns the correct format-specific implementation
// of the Store interface given the path to a file
func DefaultStoreForPath(path string) Store {
format := FormatForPath(path)
return StoreForFormat(format)
}
// DefaultStoreForPathOrFormat returns the correct format-specific implementation
// of the Store interface given the formatString if specified, or the path to a file.
// This is to support the cli, where both are provided.
func DefaultStoreForPathOrFormat(path, format string) Store {
formatFmt := FormatForPathOrString(path, format)
return StoreForFormat(formatFmt)
}
// KMS_ENC_CTX_BUG_FIXED_VERSION represents the SOPS version in which the
// encryption context bug was fixed
const KMS_ENC_CTX_BUG_FIXED_VERSION = "3.3.0"
// DetectKMSEncryptionContextBug returns true if the encryption context bug is detected
// in a given runtime sops.Tree object
func DetectKMSEncryptionContextBug(tree *sops.Tree) (bool, error) {
versionCheck, err := version.AIsNewerThanB(KMS_ENC_CTX_BUG_FIXED_VERSION, tree.Metadata.Version)
if err != nil {
return false, err
}
if versionCheck {
_, _, key := GetKMSKeyWithEncryptionCtx(tree)
if key != nil {
return true, nil
}
}
return false, nil
}
// GetKMSKeyWithEncryptionCtx returns the first KMS key affected by the encryption context bug as well as its location in the key groups.
func GetKMSKeyWithEncryptionCtx(tree *sops.Tree) (keyGroupIndex int, keyIndex int, key *kms.MasterKey) {
for i, kg := range tree.Metadata.KeyGroups {
for n, k := range kg {
kmsKey, ok := k.(*kms.MasterKey)
if ok {
if kmsKey.EncryptionContext != nil && len(kmsKey.EncryptionContext) >= 2 {
duplicateValues := map[string]int{}
for _, v := range kmsKey.EncryptionContext {
duplicateValues[*v] = duplicateValues[*v] + 1
}
if len(duplicateValues) > 1 {
return i, n, kmsKey
}
}
}
}
}
return 0, 0, nil
}
// GenericDecryptOpts represents decryption options and config
type GenericDecryptOpts struct {
Cipher sops.Cipher
InputStore sops.Store
InputPath string
IgnoreMAC bool
KeyServices []keyservice.KeyServiceClient
}
// LoadEncryptedFileWithBugFixes is a wrapper around LoadEncryptedFile which includes
// check for the issue described in https://github.com/mozilla/sops/pull/435
func LoadEncryptedFileWithBugFixes(opts GenericDecryptOpts) (*sops.Tree, error) {
tree, err := LoadEncryptedFile(opts.InputStore, opts.InputPath)
if err != nil {
return nil, err
}
encCtxBug, err := DetectKMSEncryptionContextBug(tree)
if err != nil {
return nil, err
}
if encCtxBug {
tree, err = FixAWSKMSEncryptionContextBug(opts, tree)
if err != nil {
return nil, err
}
}
return tree, nil
}
// FixAWSKMSEncryptionContextBug is used to fix the issue described in https://github.com/mozilla/sops/pull/435
func FixAWSKMSEncryptionContextBug(opts GenericDecryptOpts, tree *sops.Tree) (*sops.Tree, error) {
message := "Up until version 3.3.0 of sops there was a bug surrounding the " +
"use of encryption context with AWS KMS." +
"\nYou can read the full description of the issue here:" +
"\nhttps://github.com/mozilla/sops/pull/435" +
"\n\nIf a TTY is detected, sops will ask you if you'd like for this issue to be " +
"automatically fixed, which will require re-encrypting the data keys used by " +
"each key." +
"\n\nIf you are not using a TTY, sops will fix the issue for this run.\n\n"
fmt.Println(wordwrap.WrapString(message, 75))
persistFix := false
if terminal.IsTerminal(int(os.Stdout.Fd())) {
var response string
for response != "y" && response != "n" {
fmt.Println("Would you like sops to automatically fix this issue? (y/n): ")
_, err := fmt.Scanln(&response)
if err != nil {
return nil, err
}
}
if response == "n" {
return nil, fmt.Errorf("Exiting. User responded no")
}
persistFix = true
}
// If there is another key, then we should be able to just decrypt
// without having to try different variations of the encryption context.
dataKey, err := DecryptTree(DecryptTreeOpts{
Cipher: opts.Cipher,
IgnoreMac: opts.IgnoreMAC,
Tree: tree,
KeyServices: opts.KeyServices,
})
if err != nil {
dataKey = RecoverDataKeyFromBuggyKMS(opts, tree)
}
if dataKey == nil {
return nil, NewExitError(fmt.Sprintf("Failed to decrypt, meaning there is likely another problem from the encryption context bug: %s", err), codes.ErrorDecryptingTree)
}
errs := tree.Metadata.UpdateMasterKeysWithKeyServices(dataKey, opts.KeyServices)
if len(errs) > 0 {
err = fmt.Errorf("Could not re-encrypt data key: %s", errs)
return nil, err
}
err = EncryptTree(EncryptTreeOpts{
DataKey: dataKey,
Tree: tree,
Cipher: opts.Cipher,
})
if err != nil {
return nil, err
}
// If we are not going to persist the fix, just return the re-encrypted tree.
if !persistFix {
return tree, nil
}
encryptedFile, err := opts.InputStore.EmitEncryptedFile(*tree)
if err != nil {
return nil, NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
}
file, err := os.Create(opts.InputPath)
defer file.Close()
if err != nil {
return nil, NewExitError(fmt.Sprintf("Could not open file for writing: %s", err), codes.CouldNotWriteOutputFile)
}
_, err = file.Write(encryptedFile)
if err != nil {
return nil, err
}
newTree, err := LoadEncryptedFile(opts.InputStore, opts.InputPath)
if err != nil {
return nil, err
}
return newTree, nil
}
// RecoverDataKeyFromBuggyKMS loops through variations on Encryption Context to
// recover the datakey. This is used to fix the issue described in https://github.com/mozilla/sops/pull/435
func RecoverDataKeyFromBuggyKMS(opts GenericDecryptOpts, tree *sops.Tree) []byte {
kgndx, kndx, originalKey := GetKMSKeyWithEncryptionCtx(tree)
keyToEdit := *originalKey
encCtxVals := map[string]interface{}{}
for _, v := range keyToEdit.EncryptionContext {
encCtxVals[*v] = ""
}
encCtxVariations := []map[string]*string{}
for ctxVal := range encCtxVals {
encCtxVariation := map[string]*string{}
for key := range keyToEdit.EncryptionContext {
val := ctxVal
encCtxVariation[key] = &val
}
encCtxVariations = append(encCtxVariations, encCtxVariation)
}
for _, encCtxVar := range encCtxVariations {
keyToEdit.EncryptionContext = encCtxVar
tree.Metadata.KeyGroups[kgndx][kndx] = &keyToEdit
dataKey, err := DecryptTree(DecryptTreeOpts{
Cipher: opts.Cipher,
IgnoreMac: opts.IgnoreMAC,
Tree: tree,
KeyServices: opts.KeyServices,
})
if err == nil {
tree.Metadata.KeyGroups[kgndx][kndx] = originalKey
tree.Metadata.Version = version.Version
return dataKey
}
}
return nil
}
// Diff represents a key diff
type Diff struct {
Common []keys.MasterKey
Added []keys.MasterKey
Removed []keys.MasterKey
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
// DiffKeyGroups returns the list of diffs found in two sops.keyGroup slices
func DiffKeyGroups(ours, theirs []sops.KeyGroup) []Diff {
var diffs []Diff
for i := 0; i < max(len(ours), len(theirs)); i++ {
var diff Diff
var ourGroup, theirGroup sops.KeyGroup
if len(ours) > i {
ourGroup = ours[i]
}
if len(theirs) > i {
theirGroup = theirs[i]
}
ourKeys := make(map[string]struct{})
theirKeys := make(map[string]struct{})
for _, key := range ourGroup {
ourKeys[key.ToString()] = struct{}{}
}
for _, key := range theirGroup {
if _, ok := ourKeys[key.ToString()]; ok {
diff.Common = append(diff.Common, key)
} else {
diff.Added = append(diff.Added, key)
}
theirKeys[key.ToString()] = struct{}{}
}
for _, key := range ourGroup {
if _, ok := theirKeys[key.ToString()]; !ok {
diff.Removed = append(diff.Removed, key)
}
}
diffs = append(diffs, diff)
}
return diffs
}
// PrettyPrintDiffs prints a slice of Diff objects to stdout
func PrettyPrintDiffs(diffs []Diff) {
for i, diff := range diffs {
color.New(color.Underline).Printf("Group %d\n", i+1)
for _, c := range diff.Common {
fmt.Printf(" %s\n", c.ToString())
}
for _, c := range diff.Added {
color.New(color.FgGreen).Printf("+++ %s\n", c.ToString())
}
for _, c := range diff.Removed {
color.New(color.FgRed).Printf("--- %s\n", c.ToString())
}
}
}
07070100000027000081A400000000000000000000000162794F93000007DC000000000000000000000000000000000000001F00000000sops-3.7.3/cmd/sops/decrypt.gopackage main
import (
"fmt"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/cmd/sops/codes"
"go.mozilla.org/sops/v3/cmd/sops/common"
"go.mozilla.org/sops/v3/keyservice"
)
type decryptOpts struct {
Cipher sops.Cipher
InputStore sops.Store
OutputStore sops.Store
InputPath string
IgnoreMAC bool
Extract []interface{}
KeyServices []keyservice.KeyServiceClient
}
func decrypt(opts decryptOpts) (decryptedFile []byte, err error) {
tree, err := common.LoadEncryptedFileWithBugFixes(common.GenericDecryptOpts{
Cipher: opts.Cipher,
InputStore: opts.InputStore,
InputPath: opts.InputPath,
IgnoreMAC: opts.IgnoreMAC,
KeyServices: opts.KeyServices,
})
if err != nil {
return nil, err
}
_, err = common.DecryptTree(common.DecryptTreeOpts{
Cipher: opts.Cipher,
IgnoreMac: opts.IgnoreMAC,
Tree: tree,
KeyServices: opts.KeyServices,
})
if err != nil {
return nil, err
}
if len(opts.Extract) > 0 {
return extract(tree, opts.Extract, opts.OutputStore)
}
decryptedFile, err = opts.OutputStore.EmitPlainFile(tree.Branches)
if err != nil {
return nil, common.NewExitError(fmt.Sprintf("Error dumping file: %s", err), codes.ErrorDumpingTree)
}
return decryptedFile, err
}
func extract(tree *sops.Tree, path []interface{}, outputStore sops.Store) (output []byte, err error) {
v, err := tree.Branches[0].Truncate(path)
if err != nil {
return nil, fmt.Errorf("error truncating tree: %s", err)
}
if newBranch, ok := v.(sops.TreeBranch); ok {
tree.Branches[0] = newBranch
decrypted, err := outputStore.EmitPlainFile(tree.Branches)
if err != nil {
return nil, common.NewExitError(fmt.Sprintf("Error dumping file: %s", err), codes.ErrorDumpingTree)
}
return decrypted, err
} else if str, ok := v.(string); ok {
return []byte(str), nil
}
bytes, err := outputStore.EmitValue(v)
if err != nil {
return nil, common.NewExitError(fmt.Sprintf("Error dumping tree: %s", err), codes.ErrorDumpingTree)
}
return bytes, nil
}
07070100000028000081A400000000000000000000000162794F9300001FFF000000000000000000000000000000000000001C00000000sops-3.7.3/cmd/sops/edit.gopackage main
import (
"fmt"
"io/ioutil"
"os"
"crypto/md5"
exec "golang.org/x/sys/execabs"
"io"
"strings"
"bufio"
"bytes"
"path/filepath"
"github.com/google/shlex"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/cmd/sops/codes"
"go.mozilla.org/sops/v3/cmd/sops/common"
"go.mozilla.org/sops/v3/keyservice"
"go.mozilla.org/sops/v3/version"
)
type editOpts struct {
Cipher sops.Cipher
InputStore common.Store
OutputStore common.Store
InputPath string
IgnoreMAC bool
KeyServices []keyservice.KeyServiceClient
ShowMasterKeys bool
}
type editExampleOpts struct {
editOpts
UnencryptedSuffix string
EncryptedSuffix string
UnencryptedRegex string
EncryptedRegex string
KeyGroups []sops.KeyGroup
GroupThreshold int
}
type runEditorUntilOkOpts struct {
TmpFile *os.File
OriginalHash []byte
InputStore sops.Store
ShowMasterKeys bool
Tree *sops.Tree
}
func editExample(opts editExampleOpts) ([]byte, error) {
fileBytes := opts.InputStore.EmitExample()
branches, err := opts.InputStore.LoadPlainFile(fileBytes)
if err != nil {
return nil, common.NewExitError(fmt.Sprintf("Error unmarshalling file: %s", err), codes.CouldNotReadInputFile)
}
path, err := filepath.Abs(opts.InputPath)
if err != nil {
return nil, err
}
tree := sops.Tree{
Branches: branches,
Metadata: sops.Metadata{
KeyGroups: opts.KeyGroups,
UnencryptedSuffix: opts.UnencryptedSuffix,
EncryptedSuffix: opts.EncryptedSuffix,
UnencryptedRegex: opts.UnencryptedRegex,
EncryptedRegex: opts.EncryptedRegex,
Version: version.Version,
ShamirThreshold: opts.GroupThreshold,
},
FilePath: path,
}
// Generate a data key
dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices)
if len(errs) > 0 {
return nil, common.NewExitError(fmt.Sprintf("Error encrypting the data key with one or more master keys: %s", errs), codes.CouldNotRetrieveKey)
}
return editTree(opts.editOpts, &tree, dataKey)
}
func edit(opts editOpts) ([]byte, error) {
// Load the file
tree, err := common.LoadEncryptedFileWithBugFixes(common.GenericDecryptOpts{
Cipher: opts.Cipher,
InputStore: opts.InputStore,
InputPath: opts.InputPath,
IgnoreMAC: opts.IgnoreMAC,
KeyServices: opts.KeyServices,
})
if err != nil {
return nil, err
}
// Decrypt the file
dataKey, err := common.DecryptTree(common.DecryptTreeOpts{
Cipher: opts.Cipher, IgnoreMac: opts.IgnoreMAC, Tree: tree, KeyServices: opts.KeyServices,
})
if err != nil {
return nil, err
}
return editTree(opts, tree, dataKey)
}
func editTree(opts editOpts, tree *sops.Tree, dataKey []byte) ([]byte, error) {
// Create temporary file for editing
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
return nil, common.NewExitError(fmt.Sprintf("Could not create temporary directory: %s", err), codes.CouldNotWriteOutputFile)
}
defer os.RemoveAll(tmpdir)
tmpfile, err := os.Create(filepath.Join(tmpdir, filepath.Base(opts.InputPath)))
if err != nil {
return nil, common.NewExitError(fmt.Sprintf("Could not create temporary file: %s", err), codes.CouldNotWriteOutputFile)
}
// Write to temporary file
var out []byte
if opts.ShowMasterKeys {
out, err = opts.OutputStore.EmitEncryptedFile(*tree)
} else {
out, err = opts.OutputStore.EmitPlainFile(tree.Branches)
}
if err != nil {
return nil, common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
}
_, err = tmpfile.Write(out)
if err != nil {
return nil, common.NewExitError(fmt.Sprintf("Could not write output file: %s", err), codes.CouldNotWriteOutputFile)
}
// Close temporary file, since Windows won't delete the file unless it's closed beforehand
defer tmpfile.Close()
// Compute file hash to detect if the file has been edited
origHash, err := hashFile(tmpfile.Name())
if err != nil {
return nil, common.NewExitError(fmt.Sprintf("Could not hash file: %s", err), codes.CouldNotReadInputFile)
}
// Let the user edit the file
err = runEditorUntilOk(runEditorUntilOkOpts{
InputStore: opts.InputStore, OriginalHash: origHash, TmpFile: tmpfile,
ShowMasterKeys: opts.ShowMasterKeys, Tree: tree})
if err != nil {
return nil, err
}
// Encrypt the file
err = common.EncryptTree(common.EncryptTreeOpts{
DataKey: dataKey, Tree: tree, Cipher: opts.Cipher,
})
if err != nil {
return nil, err
}
// Output the file
encryptedFile, err := opts.OutputStore.EmitEncryptedFile(*tree)
if err != nil {
return nil, common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
}
return encryptedFile, nil
}
func runEditorUntilOk(opts runEditorUntilOkOpts) error {
for {
err := runEditor(opts.TmpFile.Name())
if err != nil {
return common.NewExitError(fmt.Sprintf("Could not run editor: %s", err), codes.NoEditorFound)
}
newHash, err := hashFile(opts.TmpFile.Name())
if err != nil {
return common.NewExitError(fmt.Sprintf("Could not hash file: %s", err), codes.CouldNotReadInputFile)
}
if bytes.Equal(newHash, opts.OriginalHash) {
return common.NewExitError("File has not changed, exiting.", codes.FileHasNotBeenModified)
}
edited, err := ioutil.ReadFile(opts.TmpFile.Name())
if err != nil {
return common.NewExitError(fmt.Sprintf("Could not read edited file: %s", err), codes.CouldNotReadInputFile)
}
newBranches, err := opts.InputStore.LoadPlainFile(edited)
if err != nil {
log.WithField(
"error",
err,
).Errorf("Could not load tree, probably due to invalid " +
"syntax. Press a key to return to the editor, or Ctrl+C to " +
"exit.")
bufio.NewReader(os.Stdin).ReadByte()
continue
}
if opts.ShowMasterKeys {
// The file is not actually encrypted, but it contains SOPS
// metadata
t, err := opts.InputStore.LoadEncryptedFile(edited)
if err != nil {
log.WithField(
"error",
err,
).Errorf("SOPS metadata is invalid. Press a key to " +
"return to the editor, or Ctrl+C to exit.")
bufio.NewReader(os.Stdin).ReadByte()
continue
}
// Replace the whole tree, because otherwise newBranches would
// contain the SOPS metadata
opts.Tree = &t
}
opts.Tree.Branches = newBranches
needVersionUpdated, err := version.AIsNewerThanB(version.Version, opts.Tree.Metadata.Version)
if err != nil {
return common.NewExitError(fmt.Sprintf("Failed to compare document version %q with program version %q: %v", opts.Tree.Metadata.Version, version.Version, err), codes.FailedToCompareVersions)
}
if needVersionUpdated {
opts.Tree.Metadata.Version = version.Version
}
if opts.Tree.Metadata.MasterKeyCount() == 0 {
log.Error("No master keys were provided, so sops can't " +
"encrypt the file. Press a key to return to the editor, or " +
"Ctrl+C to exit.")
bufio.NewReader(os.Stdin).ReadByte()
continue
}
break
}
return nil
}
func hashFile(filePath string) ([]byte, error) {
var result []byte
file, err := os.Open(filePath)
if err != nil {
return result, err
}
defer file.Close()
hash := md5.New()
if _, err := io.Copy(hash, file); err != nil {
return result, err
}
return hash.Sum(result), nil
}
func runEditor(path string) error {
editor := os.Getenv("EDITOR")
var cmd *exec.Cmd
if editor == "" {
editor, err := lookupAnyEditor("vim", "nano", "vi")
if err != nil {
return err
}
cmd = exec.Command(editor, path)
} else {
parts, err := shlex.Split(editor)
if err != nil {
return fmt.Errorf("invalid $EDITOR: %s", editor)
}
parts = append(parts, path)
cmd = exec.Command(parts[0], parts[1:]...)
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
func lookupAnyEditor(editorNames ...string) (editorPath string, err error) {
for _, editorName := range editorNames {
editorPath, err = exec.LookPath(editorName)
if err == nil {
return editorPath, nil
}
}
return "", fmt.Errorf("no editor available: sops attempts to use the editor defined in the EDITOR environment variable, and if that's not set defaults to any of %s, but none of them could be found", strings.Join(editorNames, ", "))
}
07070100000029000081A400000000000000000000000162794F9300000C76000000000000000000000000000000000000001F00000000sops-3.7.3/cmd/sops/encrypt.gopackage main
import (
"io/ioutil"
"path/filepath"
"fmt"
wordwrap "github.com/mitchellh/go-wordwrap"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/cmd/sops/codes"
"go.mozilla.org/sops/v3/cmd/sops/common"
"go.mozilla.org/sops/v3/keyservice"
"go.mozilla.org/sops/v3/version"
)
type encryptOpts struct {
Cipher sops.Cipher
InputStore sops.Store
OutputStore sops.Store
InputPath string
KeyServices []keyservice.KeyServiceClient
UnencryptedSuffix string
EncryptedSuffix string
UnencryptedRegex string
EncryptedRegex string
KeyGroups []sops.KeyGroup
GroupThreshold int
}
type fileAlreadyEncryptedError struct{}
func (err *fileAlreadyEncryptedError) Error() string {
return "File already encrypted"
}
func (err *fileAlreadyEncryptedError) UserError() string {
message := "The file you have provided contains a top-level entry called " +
"'sops'. This is generally due to the file already being encrypted. " +
"SOPS uses a top-level entry called 'sops' to store the metadata " +
"required to decrypt the file. For this reason, SOPS can not " +
"encrypt files that already contain such an entry.\n\n" +
"If this is an unencrypted file, rename the 'sops' entry.\n\n" +
"If this is an encrypted file and you want to edit it, use the " +
"editor mode, for example: `sops my_file.yaml`"
return wordwrap.WrapString(message, 75)
}
func ensureNoMetadata(opts encryptOpts, branch sops.TreeBranch) error {
for _, b := range branch {
if b.Key == "sops" {
return &fileAlreadyEncryptedError{}
}
}
return nil
}
func encrypt(opts encryptOpts) (encryptedFile []byte, err error) {
// Load the file
fileBytes, err := ioutil.ReadFile(opts.InputPath)
if err != nil {
return nil, common.NewExitError(fmt.Sprintf("Error reading file: %s", err), codes.CouldNotReadInputFile)
}
branches, err := opts.InputStore.LoadPlainFile(fileBytes)
if err != nil {
return nil, common.NewExitError(fmt.Sprintf("Error unmarshalling file: %s", err), codes.CouldNotReadInputFile)
}
if err := ensureNoMetadata(opts, branches[0]); err != nil {
return nil, common.NewExitError(err, codes.FileAlreadyEncrypted)
}
path, err := filepath.Abs(opts.InputPath)
if err != nil {
return nil, err
}
tree := sops.Tree{
Branches: branches,
Metadata: sops.Metadata{
KeyGroups: opts.KeyGroups,
UnencryptedSuffix: opts.UnencryptedSuffix,
EncryptedSuffix: opts.EncryptedSuffix,
UnencryptedRegex: opts.UnencryptedRegex,
EncryptedRegex: opts.EncryptedRegex,
Version: version.Version,
ShamirThreshold: opts.GroupThreshold,
},
FilePath: path,
}
dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices)
if len(errs) > 0 {
err = fmt.Errorf("Could not generate data key: %s", errs)
return nil, err
}
err = common.EncryptTree(common.EncryptTreeOpts{
DataKey: dataKey,
Tree: &tree,
Cipher: opts.Cipher,
})
if err != nil {
return nil, err
}
encryptedFile, err = opts.OutputStore.EmitEncryptedFile(tree)
if err != nil {
return nil, common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
}
return
}
0707010000002A000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001C00000000sops-3.7.3/cmd/sops/formats0707010000002B000081A400000000000000000000000162794F9300000764000000000000000000000000000000000000002700000000sops-3.7.3/cmd/sops/formats/formats.gopackage formats
import "strings"
// Format is an enum type
type Format int
const (
Binary Format = iota
Dotenv
Ini
Json
Yaml
)
var stringToFormat = map[string]Format{
"binary": Binary,
"dotenv": Dotenv,
"ini": Ini,
"json": Json,
"yaml": Yaml,
}
// FormatFromString returns a Format from a string.
// This is used for converting string cli options.
func FormatFromString(formatString string) Format {
format, found := stringToFormat[formatString]
if !found {
return Binary
}
return format
}
// IsYAMLFile returns true if a given file path corresponds to a YAML file
func IsYAMLFile(path string) bool {
return strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml")
}
// IsJSONFile returns true if a given file path corresponds to a JSON file
func IsJSONFile(path string) bool {
return strings.HasSuffix(path, ".json")
}
// IsEnvFile returns true if a given file path corresponds to a .env file
func IsEnvFile(path string) bool {
return strings.HasSuffix(path, ".env")
}
// IsIniFile returns true if a given file path corresponds to a INI file
func IsIniFile(path string) bool {
return strings.HasSuffix(path, ".ini")
}
// FormatForPath returns the correct format given the path to a file
func FormatForPath(path string) Format {
format := Binary // default
if IsYAMLFile(path) {
format = Yaml
} else if IsJSONFile(path) {
format = Json
} else if IsEnvFile(path) {
format = Dotenv
} else if IsIniFile(path) {
format = Ini
}
return format
}
// FormatForPathOrString returns the correct format-specific implementation
// of the Store interface given the formatString if specified, or the path to a file.
// This is to support the cli, where both are provided.
func FormatForPathOrString(path, format string) Format {
formatFmt, found := stringToFormat[format]
if !found {
formatFmt = FormatForPath(path)
}
return formatFmt
}
0707010000002C000081A400000000000000000000000162794F9300000671000000000000000000000000000000000000002C00000000sops-3.7.3/cmd/sops/formats/formats_test.gopackage formats
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestFormatFromString(t *testing.T) {
assert.Equal(t, Binary, FormatFromString("foobar"))
assert.Equal(t, Dotenv, FormatFromString("dotenv"))
assert.Equal(t, Ini, FormatFromString("ini"))
assert.Equal(t, Yaml, FormatFromString("yaml"))
assert.Equal(t, Json, FormatFromString("json"))
}
func TestFormatForPath(t *testing.T) {
assert.Equal(t, Binary, FormatForPath("/path/to/foobar"))
assert.Equal(t, Dotenv, FormatForPath("/path/to/foobar.env"))
assert.Equal(t, Ini, FormatForPath("/path/to/foobar.ini"))
assert.Equal(t, Json, FormatForPath("/path/to/foobar.json"))
assert.Equal(t, Yaml, FormatForPath("/path/to/foobar.yml"))
assert.Equal(t, Yaml, FormatForPath("/path/to/foobar.yaml"))
}
func TestFormatForPathOrString(t *testing.T) {
assert.Equal(t, Binary, FormatForPathOrString("/path/to/foobar", ""))
assert.Equal(t, Dotenv, FormatForPathOrString("/path/to/foobar", "dotenv"))
assert.Equal(t, Dotenv, FormatForPathOrString("/path/to/foobar.env", ""))
assert.Equal(t, Ini, FormatForPathOrString("/path/to/foobar", "ini"))
assert.Equal(t, Ini, FormatForPathOrString("/path/to/foobar.ini", ""))
assert.Equal(t, Json, FormatForPathOrString("/path/to/foobar", "json"))
assert.Equal(t, Json, FormatForPathOrString("/path/to/foobar.json", ""))
assert.Equal(t, Yaml, FormatForPathOrString("/path/to/foobar", "yaml"))
assert.Equal(t, Yaml, FormatForPathOrString("/path/to/foobar.yml", ""))
assert.Equal(t, Ini, FormatForPathOrString("/path/to/foobar.yml", "ini"))
assert.Equal(t, Binary, FormatForPathOrString("/path/to/foobar.yml", "binary"))
}
0707010000002D000081A400000000000000000000000162794F9300009F5A000000000000000000000000000000000000001C00000000sops-3.7.3/cmd/sops/main.gopackage main //import "go.mozilla.org/sops/v3/cmd/sops"
import (
encodingjson "encoding/json"
"fmt"
"net"
"net/url"
"os"
osExec "os/exec"
"path/filepath"
"reflect"
"strconv"
"strings"
"time"
"github.com/sirupsen/logrus"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/aes"
"go.mozilla.org/sops/v3/age"
_ "go.mozilla.org/sops/v3/audit"
"go.mozilla.org/sops/v3/azkv"
"go.mozilla.org/sops/v3/cmd/sops/codes"
"go.mozilla.org/sops/v3/cmd/sops/common"
"go.mozilla.org/sops/v3/cmd/sops/subcommand/exec"
"go.mozilla.org/sops/v3/cmd/sops/subcommand/groups"
keyservicecmd "go.mozilla.org/sops/v3/cmd/sops/subcommand/keyservice"
publishcmd "go.mozilla.org/sops/v3/cmd/sops/subcommand/publish"
"go.mozilla.org/sops/v3/cmd/sops/subcommand/updatekeys"
"go.mozilla.org/sops/v3/config"
"go.mozilla.org/sops/v3/gcpkms"
"go.mozilla.org/sops/v3/hcvault"
"go.mozilla.org/sops/v3/keys"
"go.mozilla.org/sops/v3/keyservice"
"go.mozilla.org/sops/v3/kms"
"go.mozilla.org/sops/v3/logging"
"go.mozilla.org/sops/v3/pgp"
"go.mozilla.org/sops/v3/stores/dotenv"
"go.mozilla.org/sops/v3/stores/json"
"go.mozilla.org/sops/v3/version"
"google.golang.org/grpc"
"gopkg.in/urfave/cli.v1"
)
var log *logrus.Logger
func init() {
log = logging.NewLogger("CMD")
}
func main() {
cli.VersionPrinter = version.PrintVersion
app := cli.NewApp()
keyserviceFlags := []cli.Flag{
cli.BoolTFlag{
Name: "enable-local-keyservice",
Usage: "use local key service",
},
cli.StringSliceFlag{
Name: "keyservice",
Usage: "Specify the key services to use in addition to the local one. Can be specified more than once. Syntax: protocol://address. Example: tcp://myserver.com:5000",
},
}
app.Name = "sops"
app.Usage = "sops - encrypted file editor with AWS KMS, GCP KMS, Azure Key Vault, age, and GPG support"
app.ArgsUsage = "sops [options] file"
app.Version = version.Version
app.Authors = []cli.Author{
{Name: "AJ Bahnken", Email: "ajvb@mozilla.com"},
{Name: "Adrian Utrilla", Email: "adrianutrilla@gmail.com"},
{Name: "Julien Vehent", Email: "jvehent@mozilla.com"},
}
app.UsageText = `sops is an editor of encrypted files that supports AWS KMS and PGP
To encrypt or decrypt a document with AWS KMS, specify the KMS ARN
in the -k flag or in the SOPS_KMS_ARN environment variable.
(you need valid credentials in ~/.aws/credentials or in your env)
To encrypt or decrypt a document with GCP KMS, specify the
GCP KMS resource ID in the --gcp-kms flag or in the SOPS_GCP_KMS_IDS
environment variable.
(you need to setup google application default credentials. See
https://developers.google.com/identity/protocols/application-default-credentials)
To encrypt or decrypt a document with HashiCorp Vault's Transit Secret Engine, specify the
Vault key URI name in the --hc-vault-transit flag or in the SOPS_VAULT_URIS environment variable (eg. https://vault.example.org:8200/v1/transit/keys/dev
where 'https://vault.example.org:8200' is the vault server, 'transit' the enginePath, and 'dev' is the name of the key )
environment variable.
(you need to enable the Transit Secrets Engine in Vault. See
https://www.vaultproject.io/docs/secrets/transit/index.html)
To encrypt or decrypt a document with Azure Key Vault, specify the
Azure Key Vault key URL in the --azure-kv flag or in the SOPS_AZURE_KEYVAULT_URL
environment variable.
(authentication is based on environment variables, see
https://docs.microsoft.com/en-us/go/azure/azure-sdk-go-authorization#use-environment-based-authentication.
The user/sp needs the key/encrypt and key/decrypt permissions)
To encrypt or decrypt using age, specify the recipient in the -a flag, or
in the SOPS_AGE_RECIPIENTS environment variable.
To encrypt or decrypt using PGP, specify the PGP fingerprint in the
-p flag or in the SOPS_PGP_FP environment variable.
To use multiple KMS or PGP keys, separate them by commas. For example:
$ sops -p "10F2...0A, 85D...B3F21" file.yaml
The -p, -k, --gcp-kms, --hc-vault-transit and --azure-kv flags are only used to encrypt new documents. Editing
or decrypting existing documents can be done with "sops file" or
"sops -d 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.
To use a different GPG binary than the one in your PATH, set SOPS_GPG_EXEC.
To select a different editor than the default (vim), set EDITOR.
For more information, see the README at github.com/mozilla/sops`
app.EnableBashCompletion = true
app.Commands = []cli.Command{
{
Name: "exec-env",
Usage: "execute a command with decrypted values inserted into the environment",
ArgsUsage: "[file to decrypt] [command to run]",
Flags: append([]cli.Flag{
cli.BoolFlag{
Name: "background",
Usage: "background the process and don't wait for it to complete",
},
cli.StringFlag{
Name: "user",
Usage: "the user to run the command as",
},
}, keyserviceFlags...),
Action: func(c *cli.Context) error {
if len(c.Args()) != 2 {
return common.NewExitError(fmt.Errorf("error: missing file to decrypt"), codes.ErrorGeneric)
}
fileName := c.Args()[0]
command := c.Args()[1]
inputStore := inputStore(c, fileName)
svcs := keyservices(c)
opts := decryptOpts{
OutputStore: &dotenv.Store{},
InputStore: inputStore,
InputPath: fileName,
Cipher: aes.NewCipher(),
KeyServices: svcs,
IgnoreMAC: c.Bool("ignore-mac"),
}
output, err := decrypt(opts)
if err != nil {
return toExitError(err)
}
if err := exec.ExecWithEnv(exec.ExecOpts{
Command: command,
Plaintext: output,
Background: c.Bool("background"),
User: c.String("user"),
}); err != nil {
return toExitError(err)
}
return nil
},
},
{
Name: "exec-file",
Usage: "execute a command with the decrypted contents as a temporary file",
ArgsUsage: "[file to decrypt] [command to run]",
Flags: append([]cli.Flag{
cli.BoolFlag{
Name: "background",
Usage: "background the process and don't wait for it to complete",
},
cli.BoolFlag{
Name: "no-fifo",
Usage: "use a regular file instead of a fifo to temporarily hold the decrypted contents",
},
cli.StringFlag{
Name: "user",
Usage: "the user to run the command as",
},
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: "filename",
Usage: "filename for the temporarily file (default: tmp-file)",
},
}, keyserviceFlags...),
Action: func(c *cli.Context) error {
if len(c.Args()) != 2 {
return common.NewExitError(fmt.Errorf("error: missing file to decrypt"), codes.ErrorGeneric)
}
fileName := c.Args()[0]
command := c.Args()[1]
inputStore := inputStore(c, fileName)
outputStore := outputStore(c, fileName)
svcs := keyservices(c)
opts := decryptOpts{
OutputStore: outputStore,
InputStore: inputStore,
InputPath: fileName,
Cipher: aes.NewCipher(),
KeyServices: svcs,
IgnoreMAC: c.Bool("ignore-mac"),
}
output, err := decrypt(opts)
if err != nil {
return toExitError(err)
}
filename := c.String("filename")
if filename == "" {
filename = "tmp-file"
}
if err := exec.ExecWithFile(exec.ExecOpts{
Command: command,
Plaintext: output,
Background: c.Bool("background"),
Fifo: !c.Bool("no-fifo"),
User: c.String("user"),
Filename: filename,
}); err != nil {
return toExitError(err)
}
return nil
},
},
{
Name: "publish",
Usage: "Publish sops file or directory to a configured destination",
ArgsUsage: `file`,
Flags: append([]cli.Flag{
cli.BoolFlag{
Name: "yes, y",
Usage: `pre-approve all changes and run non-interactively`,
},
cli.BoolFlag{
Name: "omit-extensions",
Usage: "Omit file extensions in destination path when publishing sops file to configured destinations",
},
cli.BoolFlag{
Name: "recursive",
Usage: "If the source path is a directory, publish all its content recursively",
},
cli.BoolFlag{
Name: "verbose",
Usage: "Enable verbose logging output",
},
}, keyserviceFlags...),
Action: func(c *cli.Context) error {
if c.Bool("verbose") || c.GlobalBool("verbose") {
logging.SetLevel(logrus.DebugLevel)
}
configPath, err := config.FindConfigFile(".")
if err != nil {
return common.NewExitError(err, codes.ErrorGeneric)
}
if c.NArg() < 1 {
return common.NewExitError("Error: no file specified", codes.NoFileSpecified)
}
path := c.Args()[0]
info, err := os.Stat(path)
if err != nil {
return err
}
if info.IsDir() && !c.Bool("recursive") {
return fmt.Errorf("can't operate on a directory without --recursive flag.")
}
err = filepath.Walk(path, func(subPath string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
err = publishcmd.Run(publishcmd.Opts{
ConfigPath: configPath,
InputPath: subPath,
Cipher: aes.NewCipher(),
KeyServices: keyservices(c),
InputStore: inputStore(c, subPath),
Interactive: !c.Bool("yes"),
OmitExtensions: c.Bool("omit-extensions"),
Recursive: c.Bool("recursive"),
RootPath: path,
})
if cliErr, ok := err.(*cli.ExitError); ok && cliErr != nil {
return cliErr
} else if err != nil {
return common.NewExitError(err, codes.ErrorGeneric)
}
}
return nil
})
if err != nil {
return err
}
return nil
},
},
{
Name: "keyservice",
Usage: "start a SOPS key service server",
Flags: []cli.Flag{
cli.StringFlag{
Name: "network, net",
Usage: "network to listen on, e.g. 'tcp' or 'unix'",
Value: "tcp",
},
cli.StringFlag{
Name: "address, addr",
Usage: "address to listen on, e.g. '127.0.0.1:5000' or '/tmp/sops.sock'",
Value: "127.0.0.1:5000",
},
cli.BoolFlag{
Name: "prompt",
Usage: "Prompt user to confirm every incoming request",
},
cli.BoolFlag{
Name: "verbose",
Usage: "Enable verbose logging output",
},
},
Action: func(c *cli.Context) error {
if c.Bool("verbose") || c.GlobalBool("verbose") {
logging.SetLevel(logrus.DebugLevel)
}
err := keyservicecmd.Run(keyservicecmd.Opts{
Network: c.String("network"),
Address: c.String("address"),
Prompt: c.Bool("prompt"),
})
if err != nil {
log.Errorf("Error running keyservice: %s", err)
return err
}
return nil
},
},
{
Name: "groups",
Usage: "modify the groups on a SOPS file",
Subcommands: []cli.Command{
{
Name: "add",
Usage: "add a new group to a SOPS file",
Flags: append([]cli.Flag{
cli.StringFlag{
Name: "file, f",
Usage: "the file to add the group to",
},
cli.StringSliceFlag{
Name: "pgp",
Usage: "the PGP fingerprints the new group should contain. Can be specified more than once",
},
cli.StringSliceFlag{
Name: "kms",
Usage: "the KMS ARNs the new group should contain. Can be specified more than once",
},
cli.StringFlag{
Name: "aws-profile",
Usage: "The AWS profile to use for requests to AWS",
},
cli.StringSliceFlag{
Name: "gcp-kms",
Usage: "the GCP KMS Resource ID the new group should contain. Can be specified more than once",
},
cli.StringSliceFlag{
Name: "azure-kv",
Usage: "the Azure Key Vault key URL the new group should contain. Can be specified more than once",
},
cli.StringSliceFlag{
Name: "hc-vault-transit",
Usage: "the full vault path to the key used to encrypt/decrypt. Make you choose and configure a key with encrption/decryption enabled (e.g. 'https://vault.example.org:8200/v1/transit/keys/dev'). Can be specified more than once",
},
cli.StringSliceFlag{
Name: "age",
Usage: "the age recipient the new group should contain. Can be specified more than once",
},
cli.BoolFlag{
Name: "in-place, i",
Usage: "write output back to the same file instead of stdout",
},
cli.IntFlag{
Name: "shamir-secret-sharing-threshold",
Usage: "the number of master keys required to retrieve the data key with shamir",
},
cli.StringFlag{
Name: "encryption-context",
Usage: "comma separated list of KMS encryption context key:value pairs",
},
}, keyserviceFlags...),
Action: func(c *cli.Context) error {
pgpFps := c.StringSlice("pgp")
kmsArns := c.StringSlice("kms")
gcpKmses := c.StringSlice("gcp-kms")
vaultURIs := c.StringSlice("hc-vault-transit")
azkvs := c.StringSlice("azure-kv")
ageRecipients := c.StringSlice("age")
var group sops.KeyGroup
for _, fp := range pgpFps {
group = append(group, pgp.NewMasterKeyFromFingerprint(fp))
}
for _, arn := range kmsArns {
group = append(group, kms.NewMasterKeyFromArn(arn, kms.ParseKMSContext(c.String("encryption-context")), c.String("aws-profile")))
}
for _, kms := range gcpKmses {
group = append(group, gcpkms.NewMasterKeyFromResourceID(kms))
}
for _, uri := range vaultURIs {
k, err := hcvault.NewMasterKeyFromURI(uri)
if err != nil {
log.WithError(err).Error("Failed to add key")
continue
}
group = append(group, k)
}
for _, url := range azkvs {
k, err := azkv.NewMasterKeyFromURL(url)
if err != nil {
log.WithError(err).Error("Failed to add key")
continue
}
group = append(group, k)
}
for _, recipient := range ageRecipients {
keys, err := age.MasterKeysFromRecipients(recipient)
if err != nil {
log.WithError(err).Error("Failed to add key")
continue
}
for _, key := range keys {
group = append(group, key)
}
}
return groups.Add(groups.AddOpts{
InputPath: c.String("file"),
InPlace: c.Bool("in-place"),
InputStore: inputStore(c, c.String("file")),
OutputStore: outputStore(c, c.String("file")),
Group: group,
GroupThreshold: c.Int("shamir-secret-sharing-threshold"),
KeyServices: keyservices(c),
})
},
},
{
Name: "delete",
Usage: "delete a key group from a SOPS file",
Flags: append([]cli.Flag{
cli.StringFlag{
Name: "file, f",
Usage: "the file to add the group to",
},
cli.BoolFlag{
Name: "in-place, i",
Usage: "write output back to the same file instead of stdout",
},
cli.IntFlag{
Name: "shamir-secret-sharing-threshold",
Usage: "the number of master keys required to retrieve the data key with shamir",
},
}, keyserviceFlags...),
ArgsUsage: `[index]`,
Action: func(c *cli.Context) error {
group, err := strconv.ParseUint(c.Args().First(), 10, 32)
if err != nil {
return fmt.Errorf("failed to parse [index] argument: %s", err)
}
return groups.Delete(groups.DeleteOpts{
InputPath: c.String("file"),
InPlace: c.Bool("in-place"),
InputStore: inputStore(c, c.String("file")),
OutputStore: outputStore(c, c.String("file")),
Group: uint(group),
GroupThreshold: c.Int("shamir-secret-sharing-threshold"),
KeyServices: keyservices(c),
})
},
},
},
},
{
Name: "updatekeys",
Usage: "update the keys of a SOPS file using the config file",
ArgsUsage: `file`,
Flags: append([]cli.Flag{
cli.BoolFlag{
Name: "yes, y",
Usage: `pre-approve all changes and run non-interactively`,
},
}, keyserviceFlags...),
Action: func(c *cli.Context) error {
var err error
var configPath string
if c.GlobalString("config") != "" {
configPath = c.GlobalString("config")
} else {
configPath, err = config.FindConfigFile(".")
if err != nil {
return common.NewExitError(err, codes.ErrorGeneric)
}
}
if c.NArg() < 1 {
return common.NewExitError("Error: no file specified", codes.NoFileSpecified)
}
err = updatekeys.UpdateKeys(updatekeys.Opts{
InputPath: c.Args()[0],
GroupQuorum: c.Int("shamir-secret-sharing-quorum"),
KeyServices: keyservices(c),
Interactive: !c.Bool("yes"),
ConfigPath: configPath,
})
if cliErr, ok := err.(*cli.ExitError); ok && cliErr != nil {
return cliErr
} else if err != nil {
return common.NewExitError(err, codes.ErrorGeneric)
}
return nil
},
},
}
app.Flags = append([]cli.Flag{
cli.BoolFlag{
Name: "decrypt, d",
Usage: "decrypt a file and output the result to stdout",
},
cli.BoolFlag{
Name: "encrypt, e",
Usage: "encrypt a file and output the result to stdout",
},
cli.BoolFlag{
Name: "rotate, r",
Usage: "generate a new data encryption key and reencrypt all values with the new key",
},
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.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. Decrypt mode only. Example: --extract '[\"somekey\"][0]'",
},
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: "show-master-keys, s",
Usage: "display master encryption keys in the file during editing",
},
cli.StringFlag{
Name: "add-gcp-kms",
Usage: "add the provided comma-separated list of GCP KMS key resource IDs to the list of master keys on the given file",
},
cli.StringFlag{
Name: "rm-gcp-kms",
Usage: "remove the provided comma-separated list of GCP KMS key resource IDs from the list of master keys on the given file",
},
cli.StringFlag{
Name: "add-azure-kv",
Usage: "add the provided comma-separated list of Azure Key Vault key URLs to the list of master keys on the given file",
},
cli.StringFlag{
Name: "rm-azure-kv",
Usage: "remove the provided comma-separated list of Azure Key Vault key URLs from the list of master keys on the given file",
},
cli.StringFlag{
Name: "add-kms",
Usage: "add the provided comma-separated list of KMS ARNs to the list of master keys on the given file",
},
cli.StringFlag{
Name: "rm-kms",
Usage: "remove the provided comma-separated list of KMS ARNs from the list of master keys on the given file",
},
cli.StringFlag{
Name: "add-hc-vault-transit",
Usage: "add the provided comma-separated list of Vault's URI key to the list of master keys on the given file ( eg. https://vault.example.org:8200/v1/transit/keys/dev)",
},
cli.StringFlag{
Name: "rm-hc-vault-transit",
Usage: "remove the provided comma-separated list of Vault's URI key from the list of master keys on the given file ( eg. https://vault.example.org:8200/v1/transit/keys/dev)",
},
cli.StringFlag{
Name: "add-age",
Usage: "add the provided comma-separated list of age recipients fingerprints to the list of master keys on the given file",
},
cli.StringFlag{
Name: "rm-age",
Usage: "remove the provided comma-separated list of age recipients from the list of master keys on the given file",
},
cli.StringFlag{
Name: "add-pgp",
Usage: "add the provided comma-separated list of PGP fingerprints to the list of master keys on the given file",
},
cli.StringFlag{
Name: "rm-pgp",
Usage: "remove the provided comma-separated list of PGP fingerprints from the list of master keys on the given file",
},
cli.BoolFlag{
Name: "ignore-mac",
Usage: "ignore Message Authentication Code during decryption",
},
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 suffix. When specified, only keys matching the regex will be left unencrypted.",
},
cli.StringFlag{
Name: "encrypted-regex",
Usage: "set the encrypted key suffix. When specified, only keys matching the regex will be encrypted.",
},
cli.StringFlag{
Name: "config",
Usage: "path to sops' config file. If set, sops will not search for the config file recursively.",
},
cli.StringFlag{
Name: "encryption-context",
Usage: "comma separated list of KMS encryption context key:value pairs",
},
cli.StringFlag{
Name: "set",
Usage: `set a specific key or branch in the input document. value must be a json encoded string. (edit mode only). eg. --set '["somekey"][0] {"somevalue":true}'`,
},
cli.IntFlag{
Name: "shamir-secret-sharing-threshold",
Usage: "the number of master keys required to retrieve the data key with shamir",
},
cli.BoolFlag{
Name: "verbose",
Usage: "Enable verbose logging output",
},
cli.StringFlag{
Name: "output",
Usage: "Save the output after encryption or decryption to the file specified",
},
}, keyserviceFlags...)
app.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)
}
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) {
if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-hc-vault-transit") != "" || c.String("add-azure-kv") != "" || c.String("add-age") != "" ||
c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-hc-vault-transit") != "" || c.String("rm-azure-kv") != "" || c.String("rm-age") != "" {
return common.NewExitError("Error: cannot add or remove keys on non-existent files, use `--kms` and `--pgp` instead.", codes.CannotChangeKeysFromNonExistentFile)
}
if c.Bool("encrypt") || c.Bool("decrypt") || c.Bool("rotate") {
return common.NewExitError("Error: cannot operate on non-existent file", codes.NoFileSpecified)
}
}
unencryptedSuffix := c.String("unencrypted-suffix")
encryptedSuffix := c.String("encrypted-suffix")
encryptedRegex := c.String("encrypted-regex")
unencryptedRegex := c.String("unencrypted-regex")
conf, err := loadConfig(c, fileName, nil)
if err != nil {
return toExitError(err)
}
if conf != nil {
// command line options have precedence
if unencryptedSuffix == "" {
unencryptedSuffix = conf.UnencryptedSuffix
}
if encryptedSuffix == "" {
encryptedSuffix = conf.EncryptedSuffix
}
if encryptedRegex == "" {
encryptedRegex = conf.EncryptedRegex
}
if unencryptedRegex == "" {
unencryptedRegex = conf.UnencryptedRegex
}
}
cryptRuleCount := 0
if unencryptedSuffix != "" {
cryptRuleCount++
}
if encryptedSuffix != "" {
cryptRuleCount++
}
if encryptedRegex != "" {
cryptRuleCount++
}
if unencryptedRegex != "" {
cryptRuleCount++
}
if cryptRuleCount > 1 {
return common.NewExitError("Error: cannot use more than one of encrypted_suffix, unencrypted_suffix, encrypted_regex or unencrypted_regex in the same file", codes.ErrorConflictingParameters)
}
// only supply the default UnencryptedSuffix when EncryptedSuffix and EncryptedRegex are not provided
if cryptRuleCount == 0 {
unencryptedSuffix = sops.DefaultUnencryptedSuffix
}
inputStore := inputStore(c, fileName)
outputStore := outputStore(c, fileName)
svcs := keyservices(c)
var output []byte
if c.Bool("encrypt") {
var groups []sops.KeyGroup
groups, err = keyGroups(c, fileName)
if err != nil {
return toExitError(err)
}
var threshold int
threshold, err = shamirThreshold(c, fileName)
if err != nil {
return toExitError(err)
}
output, err = encrypt(encryptOpts{
OutputStore: outputStore,
InputStore: inputStore,
InputPath: fileName,
Cipher: aes.NewCipher(),
UnencryptedSuffix: unencryptedSuffix,
EncryptedSuffix: encryptedSuffix,
UnencryptedRegex: unencryptedRegex,
EncryptedRegex: encryptedRegex,
KeyServices: svcs,
KeyGroups: groups,
GroupThreshold: threshold,
})
}
if c.Bool("decrypt") {
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,
IgnoreMAC: c.Bool("ignore-mac"),
})
}
if c.Bool("rotate") {
var addMasterKeys []keys.MasterKey
kmsEncryptionContext := kms.ParseKMSContext(c.String("encryption-context"))
for _, k := range kms.MasterKeysFromArnString(c.String("add-kms"), kmsEncryptionContext, c.String("aws-profile")) {
addMasterKeys = append(addMasterKeys, k)
}
for _, k := range pgp.MasterKeysFromFingerprintString(c.String("add-pgp")) {
addMasterKeys = append(addMasterKeys, k)
}
for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String("add-gcp-kms")) {
addMasterKeys = append(addMasterKeys, k)
}
azureKeys, err := azkv.MasterKeysFromURLs(c.String("add-azure-kv"))
if err != nil {
return err
}
for _, k := range azureKeys {
addMasterKeys = append(addMasterKeys, k)
}
hcVaultKeys, err := hcvault.NewMasterKeysFromURIs(c.String("add-hc-vault-transit"))
if err != nil {
return err
}
for _, k := range hcVaultKeys {
addMasterKeys = append(addMasterKeys, k)
}
ageKeys, err := age.MasterKeysFromRecipients(c.String("add-age"))
if err != nil {
return err
}
for _, k := range ageKeys {
addMasterKeys = append(addMasterKeys, k)
}
var rmMasterKeys []keys.MasterKey
for _, k := range kms.MasterKeysFromArnString(c.String("rm-kms"), kmsEncryptionContext, c.String("aws-profile")) {
rmMasterKeys = append(rmMasterKeys, k)
}
for _, k := range pgp.MasterKeysFromFingerprintString(c.String("rm-pgp")) {
rmMasterKeys = append(rmMasterKeys, k)
}
for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String("rm-gcp-kms")) {
rmMasterKeys = append(rmMasterKeys, k)
}
azureKeys, err = azkv.MasterKeysFromURLs(c.String("rm-azure-kv"))
if err != nil {
return err
}
for _, k := range azureKeys {
rmMasterKeys = append(rmMasterKeys, k)
}
hcVaultKeys, err = hcvault.NewMasterKeysFromURIs(c.String("rm-hc-vault-transit"))
if err != nil {
return err
}
for _, k := range hcVaultKeys {
rmMasterKeys = append(rmMasterKeys, k)
}
ageKeys, err = age.MasterKeysFromRecipients(c.String("rm-age"))
if err != nil {
return err
}
for _, k := range ageKeys {
rmMasterKeys = append(rmMasterKeys, k)
}
output, err = rotate(rotateOpts{
OutputStore: outputStore,
InputStore: inputStore,
InputPath: fileName,
Cipher: aes.NewCipher(),
KeyServices: svcs,
IgnoreMAC: c.Bool("ignore-mac"),
AddMasterKeys: addMasterKeys,
RemoveMasterKeys: rmMasterKeys,
})
}
if c.String("set") != "" {
var path []interface{}
var value interface{}
path, value, err = extractSetArguments(c.String("set"))
if err != nil {
return toExitError(err)
}
output, err = set(setOpts{
OutputStore: outputStore,
InputStore: inputStore,
InputPath: fileName,
Cipher: aes.NewCipher(),
KeyServices: svcs,
IgnoreMAC: c.Bool("ignore-mac"),
Value: value,
TreePath: path,
})
}
isEditMode := !c.Bool("encrypt") && !c.Bool("decrypt") && !c.Bool("rotate") && c.String("set") == ""
if isEditMode {
_, statErr := os.Stat(fileName)
fileExists := statErr == nil
opts := editOpts{
OutputStore: outputStore,
InputStore: inputStore,
InputPath: fileName,
Cipher: aes.NewCipher(),
KeyServices: svcs,
IgnoreMAC: c.Bool("ignore-mac"),
ShowMasterKeys: c.Bool("show-master-keys"),
}
if fileExists {
output, err = edit(opts)
} else {
// File doesn't exist, edit the example file instead
var groups []sops.KeyGroup
groups, err = keyGroups(c, fileName)
if err != nil {
return toExitError(err)
}
var threshold int
threshold, err = shamirThreshold(c, fileName)
if err != nil {
return toExitError(err)
}
output, err = editExample(editExampleOpts{
editOpts: opts,
UnencryptedSuffix: unencryptedSuffix,
EncryptedSuffix: encryptedSuffix,
UnencryptedRegex: unencryptedRegex,
EncryptedRegex: encryptedRegex,
KeyGroups: groups,
GroupThreshold: threshold,
})
}
}
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") || isEditMode || c.String("set") != "" {
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)
}
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}
func toExitError(err error) error {
if cliErr, ok := err.(*cli.ExitError); ok && cliErr != nil {
return cliErr
} else if execErr, ok := err.(*osExec.ExitError); ok && execErr != nil {
return cli.NewExitError(err, execErr.ExitCode())
} else if err != nil {
return cli.NewExitError(err, codes.ErrorGeneric)
}
return nil
}
func keyservices(c *cli.Context) (svcs []keyservice.KeyServiceClient) {
if c.Bool("enable-local-keyservice") {
svcs = append(svcs, keyservice.NewLocalClient())
}
uris := c.StringSlice("keyservice")
for _, uri := range uris {
url, err := url.Parse(uri)
if err != nil {
log.WithField("uri", uri).
Warnf("Error parsing URI for keyservice, skipping")
continue
}
addr := url.Host
if url.Scheme == "unix" {
addr = url.Path
}
opts := []grpc.DialOption{
grpc.WithInsecure(),
grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
return net.DialTimeout(url.Scheme, addr, timeout)
}),
}
log.WithField(
"address",
fmt.Sprintf("%s://%s", url.Scheme, addr),
).Infof("Connecting to key service")
conn, err := grpc.Dial(addr, opts...)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
svcs = append(svcs, keyservice.NewKeyServiceClient(conn))
}
return
}
func inputStore(context *cli.Context, path string) common.Store {
return common.DefaultStoreForPathOrFormat(path, context.String("input-type"))
}
func outputStore(context *cli.Context, path string) common.Store {
return common.DefaultStoreForPathOrFormat(path, context.String("output-type"))
}
func parseTreePath(arg string) ([]interface{}, error) {
var path []interface{}
components := strings.Split(arg, "[")
for _, component := range components {
if component == "" {
continue
}
if component[len(component)-1] != ']' {
return nil, fmt.Errorf("component %s doesn't end with ]", component)
}
component = component[:len(component)-1]
if component[0] == byte('"') || component[0] == byte('\'') {
// The component is a string
component = component[1 : len(component)-1]
path = append(path, component)
} else {
// The component must be a number
i, err := strconv.Atoi(component)
if err != nil {
return nil, err
}
path = append(path, i)
}
}
return path, nil
}
func keyGroups(c *cli.Context, file string) ([]sops.KeyGroup, error) {
var kmsKeys []keys.MasterKey
var pgpKeys []keys.MasterKey
var cloudKmsKeys []keys.MasterKey
var azkvKeys []keys.MasterKey
var hcVaultMkKeys []keys.MasterKey
var ageMasterKeys []keys.MasterKey
kmsEncryptionContext := kms.ParseKMSContext(c.String("encryption-context"))
if c.String("encryption-context") != "" && kmsEncryptionContext == nil {
return nil, common.NewExitError("Invalid KMS encryption context format", codes.ErrorInvalidKMSEncryptionContextFormat)
}
if c.String("kms") != "" {
for _, k := range kms.MasterKeysFromArnString(c.String("kms"), kmsEncryptionContext, c.String("aws-profile")) {
kmsKeys = append(kmsKeys, k)
}
}
if c.String("gcp-kms") != "" {
for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String("gcp-kms")) {
cloudKmsKeys = append(cloudKmsKeys, k)
}
}
if c.String("azure-kv") != "" {
azureKeys, err := azkv.MasterKeysFromURLs(c.String("azure-kv"))
if err != nil {
return nil, err
}
for _, k := range azureKeys {
azkvKeys = append(azkvKeys, k)
}
}
if c.String("hc-vault-transit") != "" {
hcVaultKeys, err := hcvault.NewMasterKeysFromURIs(c.String("hc-vault-transit"))
if err != nil {
return nil, err
}
for _, k := range hcVaultKeys {
hcVaultMkKeys = append(hcVaultMkKeys, k)
}
}
if c.String("pgp") != "" {
for _, k := range pgp.MasterKeysFromFingerprintString(c.String("pgp")) {
pgpKeys = append(pgpKeys, k)
}
}
if c.String("age") != "" {
ageKeys, err := age.MasterKeysFromRecipients(c.String("age"))
if err != nil {
return nil, err
}
for _, k := range ageKeys {
ageMasterKeys = append(ageMasterKeys, k)
}
}
if c.String("kms") == "" && c.String("pgp") == "" && c.String("gcp-kms") == "" && c.String("azure-kv") == "" && c.String("hc-vault-transit") == "" && c.String("age") == "" {
conf, err := loadConfig(c, file, kmsEncryptionContext)
// config file might just not be supplied, without any error
if conf == nil {
errMsg := "config file not found and no keys provided through command line options"
if err != nil {
errMsg = fmt.Sprintf("%s: %s", errMsg, err)
}
return nil, fmt.Errorf(errMsg)
}
return conf.KeyGroups, err
}
var group sops.KeyGroup
group = append(group, kmsKeys...)
group = append(group, cloudKmsKeys...)
group = append(group, azkvKeys...)
group = append(group, pgpKeys...)
group = append(group, hcVaultMkKeys...)
group = append(group, ageMasterKeys...)
log.Debugf("Master keys available: %+v", group)
return []sops.KeyGroup{group}, nil
}
// loadConfig will look for an existing config file, either provided through the command line, or using config.FindConfigFile.
// Since a config file is not required, this function does not error when one is not found, and instead returns a nil config pointer
func loadConfig(c *cli.Context, file string, kmsEncryptionContext map[string]*string) (*config.Config, error) {
var err error
var configPath string
if c.String("config") != "" {
configPath = c.String("config")
} else {
// Ignore config not found errors returned from FindConfigFile since the config file is not mandatory
configPath, err = config.FindConfigFile(".")
if err != nil {
// If we can't find a config file, but we were not explicitly requested to, assume it does not exist
return nil, nil
}
}
conf, err := config.LoadCreationRuleForFile(configPath, file, kmsEncryptionContext)
if err != nil {
return nil, err
}
return conf, nil
}
func shamirThreshold(c *cli.Context, file string) (int, error) {
if c.Int("shamir-secret-sharing-threshold") != 0 {
return c.Int("shamir-secret-sharing-threshold"), nil
}
conf, err := loadConfig(c, file, nil)
if conf == nil {
// This takes care of the following two case:
// 1. No config was provided. Err will be nil and ShamirThreshold will be the default value of 0.
// 2. We did find a config file, but failed to load it. In that case the calling function will print the error and exit.
return 0, err
}
return conf.ShamirThreshold, nil
}
func jsonValueToTreeInsertableValue(jsonValue string) (interface{}, error) {
var valueToInsert interface{}
err := encodingjson.Unmarshal([]byte(jsonValue), &valueToInsert)
if err != nil {
return nil, common.NewExitError("Value for --set is not valid JSON", codes.ErrorInvalidSetFormat)
}
// Check if decoding it as json we find a single value
// and not a map or slice, in which case we can't marshal
// it to a sops.TreeBranch
kind := reflect.ValueOf(valueToInsert).Kind()
if kind == reflect.Map || kind == reflect.Slice {
var err error
valueToInsert, err = (&json.Store{}).LoadPlainFile([]byte(jsonValue))
if err != nil {
return nil, common.NewExitError("Invalid --set value format", codes.ErrorInvalidSetFormat)
}
}
// Fix for #461
// Attempt conversion to TreeBranches to handle yaml multidoc. If conversion fails it's
// most likely a string value, so just return it as-is.
values, ok := valueToInsert.(sops.TreeBranches)
if !ok {
return valueToInsert, nil
}
return values[0], nil
}
func extractSetArguments(set string) (path []interface{}, valueToInsert interface{}, err error) {
// Set is a string with the format "python-dict-index json-value"
// Since python-dict-index has to end with ], we split at "] " to get the two parts
pathValuePair := strings.SplitAfterN(set, "] ", 2)
if len(pathValuePair) < 2 {
return nil, nil, common.NewExitError("Invalid --set format", codes.ErrorInvalidSetFormat)
}
fullPath := strings.TrimRight(pathValuePair[0], " ")
jsonValue := pathValuePair[1]
valueToInsert, err = jsonValueToTreeInsertableValue(jsonValue)
path, err = parseTreePath(fullPath)
if err != nil {
return nil, nil, common.NewExitError("Invalid --set format", codes.ErrorInvalidSetFormat)
}
return path, valueToInsert, nil
}
0707010000002E000081A400000000000000000000000162794F9300000897000000000000000000000000000000000000001E00000000sops-3.7.3/cmd/sops/rotate.gopackage main
import (
"fmt"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/audit"
"go.mozilla.org/sops/v3/cmd/sops/codes"
"go.mozilla.org/sops/v3/cmd/sops/common"
"go.mozilla.org/sops/v3/keys"
"go.mozilla.org/sops/v3/keyservice"
)
type rotateOpts struct {
Cipher sops.Cipher
InputStore sops.Store
OutputStore sops.Store
InputPath string
IgnoreMAC bool
AddMasterKeys []keys.MasterKey
RemoveMasterKeys []keys.MasterKey
KeyServices []keyservice.KeyServiceClient
}
func rotate(opts rotateOpts) ([]byte, error) {
tree, err := common.LoadEncryptedFileWithBugFixes(common.GenericDecryptOpts{
Cipher: opts.Cipher,
InputStore: opts.InputStore,
InputPath: opts.InputPath,
IgnoreMAC: opts.IgnoreMAC,
KeyServices: opts.KeyServices,
})
if err != nil {
return nil, err
}
audit.SubmitEvent(audit.RotateEvent{
File: tree.FilePath,
})
_, err = common.DecryptTree(common.DecryptTreeOpts{
Cipher: opts.Cipher, IgnoreMac: opts.IgnoreMAC, Tree: tree,
KeyServices: opts.KeyServices,
})
if err != nil {
return nil, err
}
// Add new master keys
for _, key := range opts.AddMasterKeys {
tree.Metadata.KeyGroups[0] = append(tree.Metadata.KeyGroups[0], key)
}
// Remove master keys
for _, rmKey := range opts.RemoveMasterKeys {
for i := range tree.Metadata.KeyGroups {
for j, groupKey := range tree.Metadata.KeyGroups[i] {
if rmKey.ToString() == groupKey.ToString() {
tree.Metadata.KeyGroups[i] = append(tree.Metadata.KeyGroups[i][:j], tree.Metadata.KeyGroups[i][j+1:]...)
}
}
}
}
// Create a new data key
dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices)
if len(errs) > 0 {
err = fmt.Errorf("Could not generate data key: %s", errs)
return nil, err
}
// Reencrypt the file with the new key
err = common.EncryptTree(common.EncryptTreeOpts{
DataKey: dataKey, Tree: tree, Cipher: opts.Cipher,
})
if err != nil {
return nil, err
}
encryptedFile, err := opts.OutputStore.EmitEncryptedFile(*tree)
if err != nil {
return nil, common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
}
return encryptedFile, nil
}
0707010000002F000081A400000000000000000000000162794F9300000602000000000000000000000000000000000000001B00000000sops-3.7.3/cmd/sops/set.gopackage main
import (
"fmt"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/cmd/sops/codes"
"go.mozilla.org/sops/v3/cmd/sops/common"
"go.mozilla.org/sops/v3/keyservice"
)
type setOpts struct {
Cipher sops.Cipher
InputStore sops.Store
OutputStore sops.Store
InputPath string
IgnoreMAC bool
TreePath []interface{}
Value interface{}
KeyServices []keyservice.KeyServiceClient
}
func set(opts setOpts) ([]byte, error) {
// Load the file
// TODO: Issue #173: if the file does not exist, create it with the contents passed in as opts.Value
tree, err := common.LoadEncryptedFileWithBugFixes(common.GenericDecryptOpts{
Cipher: opts.Cipher,
InputStore: opts.InputStore,
InputPath: opts.InputPath,
IgnoreMAC: opts.IgnoreMAC,
KeyServices: opts.KeyServices,
})
if err != nil {
return nil, err
}
// Decrypt the file
dataKey, err := common.DecryptTree(common.DecryptTreeOpts{
Cipher: opts.Cipher,
IgnoreMac: opts.IgnoreMAC,
Tree: tree,
KeyServices: opts.KeyServices,
})
if err != nil {
return nil, err
}
// Set the value
tree.Branches[0] = tree.Branches[0].Set(opts.TreePath, opts.Value)
err = common.EncryptTree(common.EncryptTreeOpts{
DataKey: dataKey, Tree: tree, Cipher: opts.Cipher,
})
if err != nil {
return nil, err
}
encryptedFile, err := opts.OutputStore.EmitEncryptedFile(*tree)
if err != nil {
return nil, common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
}
return encryptedFile, err
}
07070100000030000041ED00000000000000000000000762794F9300000000000000000000000000000000000000000000001F00000000sops-3.7.3/cmd/sops/subcommand07070100000031000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002400000000sops-3.7.3/cmd/sops/subcommand/exec07070100000032000081A400000000000000000000000162794F9300000795000000000000000000000000000000000000002C00000000sops-3.7.3/cmd/sops/subcommand/exec/exec.gopackage exec
import (
"bytes"
"io/ioutil"
"os"
"runtime"
"strings"
"go.mozilla.org/sops/v3/logging"
"github.com/sirupsen/logrus"
)
var log *logrus.Logger
func init() {
log = logging.NewLogger("EXEC")
}
type ExecOpts struct {
Command string
Plaintext []byte
Background bool
Fifo bool
User string
Filename string
}
func GetFile(dir, filename string) *os.File {
handle, err := ioutil.TempFile(dir, filename)
if err != nil {
log.Fatal(err)
}
return handle
}
func ExecWithFile(opts ExecOpts) error {
if opts.User != "" {
SwitchUser(opts.User)
}
if runtime.GOOS == "windows" && opts.Fifo {
log.Warn("no fifos on windows, use --no-fifo next time")
opts.Fifo = false
}
dir, err := ioutil.TempDir("", ".sops")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(dir)
var filename string
if opts.Fifo {
// fifo handling needs to be async, even opening to write
// will block if there is no reader present
filename = GetPipe(dir, opts.Filename)
go WritePipe(filename, opts.Plaintext)
} else {
handle := GetFile(dir, opts.Filename)
handle.Write(opts.Plaintext)
handle.Close()
filename = handle.Name()
}
placeholdered := strings.Replace(opts.Command, "{}", filename, -1)
cmd := BuildCommand(placeholdered)
cmd.Env = os.Environ()
if opts.Background {
return cmd.Start()
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
func ExecWithEnv(opts ExecOpts) error {
if opts.User != "" {
SwitchUser(opts.User)
}
env := os.Environ()
lines := bytes.Split(opts.Plaintext, []byte("\n"))
for _, line := range lines {
if len(line) == 0 {
continue
}
if line[0] == '#' {
continue
}
env = append(env, string(line))
}
cmd := BuildCommand(opts.Command)
cmd.Env = env
if opts.Background {
return cmd.Start()
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
07070100000033000081A400000000000000000000000162794F93000003EA000000000000000000000000000000000000003100000000sops-3.7.3/cmd/sops/subcommand/exec/exec_unix.go// +build !windows
package exec
import (
"os"
"os/exec"
"os/user"
"path/filepath"
"strconv"
"syscall"
)
func BuildCommand(command string) *exec.Cmd {
return exec.Command("/bin/sh", "-c", command)
}
func WritePipe(pipe string, contents []byte) {
handle, err := os.OpenFile(pipe, os.O_WRONLY, 0600)
if err != nil {
os.Remove(pipe)
log.Fatal(err)
}
handle.Write(contents)
handle.Close()
}
func GetPipe(dir, filename string) string {
tmpfn := filepath.Join(dir, filename)
err := syscall.Mkfifo(tmpfn, 0600)
if err != nil {
log.Fatal(err)
}
return tmpfn
}
func SwitchUser(username string) {
user, err := user.Lookup(username)
if err != nil {
log.Fatal(err)
}
uid, _ := strconv.Atoi(user.Uid)
err = syscall.Setgid(uid)
if err != nil {
log.Fatal(err)
}
err = syscall.Setuid(uid)
if err != nil {
log.Fatal(err)
}
err = syscall.Setreuid(uid, uid)
if err != nil {
log.Fatal(err)
}
err = syscall.Setregid(uid, uid)
if err != nil {
log.Fatal(err)
}
}
07070100000034000081A400000000000000000000000162794F93000001AE000000000000000000000000000000000000003400000000sops-3.7.3/cmd/sops/subcommand/exec/exec_windows.gopackage exec
import (
"os/exec"
)
func BuildCommand(command string) *exec.Cmd {
return exec.Command("cmd.exe", "/C", command)
}
func WritePipe(pipe string, contents []byte) {
log.Fatal("fifos are not available on windows")
}
func GetPipe(dir, filename string) string {
log.Fatal("fifos are not available on windows")
return ""
}
func SwitchUser(username string) {
log.Fatal("user switching not available on windows")
}
07070100000035000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002600000000sops-3.7.3/cmd/sops/subcommand/groups07070100000036000081A400000000000000000000000162794F93000004DF000000000000000000000000000000000000002D00000000sops-3.7.3/cmd/sops/subcommand/groups/add.gopackage groups
import (
"os"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/cmd/sops/common"
"go.mozilla.org/sops/v3/keyservice"
)
// AddOpts are the options for adding a key group to a SOPS file
type AddOpts struct {
InputPath string
InputStore sops.Store
OutputStore sops.Store
Group sops.KeyGroup
GroupThreshold int
InPlace bool
KeyServices []keyservice.KeyServiceClient
}
// Add adds a key group to a SOPS file
func Add(opts AddOpts) error {
tree, err := common.LoadEncryptedFile(opts.InputStore, opts.InputPath)
if err != nil {
return err
}
dataKey, err := tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices)
if err != nil {
return err
}
tree.Metadata.KeyGroups = append(tree.Metadata.KeyGroups, opts.Group)
if opts.GroupThreshold != 0 {
tree.Metadata.ShamirThreshold = opts.GroupThreshold
}
tree.Metadata.UpdateMasterKeysWithKeyServices(dataKey, opts.KeyServices)
output, err := opts.OutputStore.EmitEncryptedFile(*tree)
if err != nil {
return err
}
var outputFile = os.Stdout
if opts.InPlace {
var err error
outputFile, err = os.Create(opts.InputPath)
if err != nil {
return err
}
defer outputFile.Close()
}
outputFile.Write(output)
return nil
}
07070100000037000081A400000000000000000000000162794F930000064D000000000000000000000000000000000000003000000000sops-3.7.3/cmd/sops/subcommand/groups/delete.gopackage groups
import (
"os"
"fmt"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/cmd/sops/common"
"go.mozilla.org/sops/v3/keyservice"
)
// DeleteOpts are the options for deleting a key group from a SOPS file
type DeleteOpts struct {
InputPath string
InputStore sops.Store
OutputStore sops.Store
Group uint
GroupThreshold int
InPlace bool
KeyServices []keyservice.KeyServiceClient
}
// Delete deletes a key group from a SOPS file
func Delete(opts DeleteOpts) error {
tree, err := common.LoadEncryptedFile(opts.InputStore, opts.InputPath)
if err != nil {
return err
}
dataKey, err := tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices)
if err != nil {
return err
}
tree.Metadata.KeyGroups = append(tree.Metadata.KeyGroups[:opts.Group], tree.Metadata.KeyGroups[opts.Group+1:]...)
if opts.GroupThreshold != 0 {
tree.Metadata.ShamirThreshold = opts.GroupThreshold
}
if len(tree.Metadata.KeyGroups) < tree.Metadata.ShamirThreshold {
return fmt.Errorf("removing this key group will make the Shamir threshold impossible to satisfy: "+
"Shamir threshold is %d, but we only have %d key groups", tree.Metadata.ShamirThreshold,
len(tree.Metadata.KeyGroups))
}
tree.Metadata.UpdateMasterKeysWithKeyServices(dataKey, opts.KeyServices)
output, err := opts.OutputStore.EmitEncryptedFile(*tree)
if err != nil {
return err
}
var outputFile = os.Stdout
if opts.InPlace {
var err error
outputFile, err = os.Create(opts.InputPath)
if err != nil {
return err
}
defer outputFile.Close()
}
outputFile.Write(output)
return nil
}
07070100000038000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002A00000000sops-3.7.3/cmd/sops/subcommand/keyservice07070100000039000081A400000000000000000000000162794F930000041E000000000000000000000000000000000000003800000000sops-3.7.3/cmd/sops/subcommand/keyservice/keyservice.gopackage keyservice
import (
"net"
"os"
"os/signal"
"syscall"
"go.mozilla.org/sops/v3/keyservice"
"go.mozilla.org/sops/v3/logging"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
)
var log *logrus.Logger
func init() {
log = logging.NewLogger("KEYSERVICE")
}
// Opts are the options the key service server can take
type Opts struct {
Network string
Address string
Prompt bool
}
// Run runs a SOPS key service server
func Run(opts Opts) error {
lis, err := net.Listen(opts.Network, opts.Address)
if err != nil {
return err
}
defer lis.Close()
grpcServer := grpc.NewServer()
keyservice.RegisterKeyServiceServer(grpcServer, keyservice.Server{
Prompt: opts.Prompt,
})
log.Infof("Listening on %s://%s", opts.Network, opts.Address)
// Close socket if we get killed
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, os.Interrupt, os.Kill, syscall.SIGTERM)
go func(c chan os.Signal) {
sig := <-c
log.Infof("Caught signal %s: shutting down.", sig)
lis.Close()
os.Exit(0)
}(sigc)
return grpcServer.Serve(lis)
}
0707010000003A000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002700000000sops-3.7.3/cmd/sops/subcommand/publish0707010000003B000081A400000000000000000000000162794F93000011C5000000000000000000000000000000000000003200000000sops-3.7.3/cmd/sops/subcommand/publish/publish.gopackage publish
import (
"errors"
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/cmd/sops/codes"
"go.mozilla.org/sops/v3/cmd/sops/common"
"go.mozilla.org/sops/v3/config"
"go.mozilla.org/sops/v3/keyservice"
"go.mozilla.org/sops/v3/logging"
"go.mozilla.org/sops/v3/publish"
"go.mozilla.org/sops/v3/version"
"github.com/sirupsen/logrus"
)
var log *logrus.Logger
func init() {
log = logging.NewLogger("PUBLISH")
}
// Opts represents publish options and config
type Opts struct {
Interactive bool
Cipher sops.Cipher
ConfigPath string
InputPath string
KeyServices []keyservice.KeyServiceClient
InputStore sops.Store
OmitExtensions bool
Recursive bool
RootPath string
}
// Run publish operation
func Run(opts Opts) error {
var fileContents []byte
path, err := filepath.Abs(opts.InputPath)
if err != nil {
return err
}
conf, err := config.LoadDestinationRuleForFile(opts.ConfigPath, opts.InputPath, make(map[string]*string))
if err != nil {
return err
}
if conf.Destination == nil {
return errors.New("no destination configured for this file")
}
var destinationPath string
if opts.Recursive {
destinationPath, err = filepath.Rel(opts.RootPath, opts.InputPath)
if err != nil {
return err
}
} else {
_, destinationPath = filepath.Split(path)
}
if opts.OmitExtensions || conf.OmitExtensions {
destinationPath = strings.TrimSuffix(destinationPath, filepath.Ext(path))
}
// Check that this is a sops-encrypted file
tree, err := common.LoadEncryptedFile(opts.InputStore, opts.InputPath)
if err != nil {
return err
}
data := map[string]interface{}{}
switch conf.Destination.(type) {
case *publish.S3Destination, *publish.GCSDestination:
// Re-encrypt if settings exist to do so
if len(conf.KeyGroups[0]) != 0 {
log.Debug("Re-encrypting tree before publishing")
_, err = common.DecryptTree(common.DecryptTreeOpts{
Cipher: opts.Cipher,
IgnoreMac: false,
Tree: tree,
KeyServices: opts.KeyServices,
})
if err != nil {
return err
}
diffs := common.DiffKeyGroups(tree.Metadata.KeyGroups, conf.KeyGroups)
keysWillChange := false
for _, diff := range diffs {
if len(diff.Added) > 0 || len(diff.Removed) > 0 {
keysWillChange = true
}
}
if keysWillChange {
fmt.Printf("The following changes will be made to the file's key groups:\n")
common.PrettyPrintDiffs(diffs)
}
tree.Metadata = sops.Metadata{
KeyGroups: conf.KeyGroups,
UnencryptedSuffix: conf.UnencryptedSuffix,
EncryptedSuffix: conf.EncryptedSuffix,
Version: version.Version,
ShamirThreshold: conf.ShamirThreshold,
}
dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices)
if len(errs) > 0 {
err = fmt.Errorf("Could not generate data key: %s", errs)
return err
}
err = common.EncryptTree(common.EncryptTreeOpts{
DataKey: dataKey,
Tree: tree,
Cipher: opts.Cipher,
})
if err != nil {
return err
}
fileContents, err = opts.InputStore.EmitEncryptedFile(*tree)
if err != nil {
return common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
}
} else {
fileContents, err = ioutil.ReadFile(path)
if err != nil {
return fmt.Errorf("could not read file: %s", err)
}
}
case *publish.VaultDestination:
_, err = common.DecryptTree(common.DecryptTreeOpts{
Cipher: opts.Cipher,
IgnoreMac: false,
Tree: tree,
KeyServices: opts.KeyServices,
})
if err != nil {
return err
}
data, err = sops.EmitAsMap(tree.Branches)
if err != nil {
return err
}
}
if opts.Interactive {
var response string
for response != "y" && response != "n" {
fmt.Printf("uploading %s to %s ? (y/n): ", path, conf.Destination.Path(destinationPath))
_, err := fmt.Scanln(&response)
if err != nil {
return err
}
}
if response == "n" {
msg := fmt.Sprintf("Publication of %s canceled", path)
if opts.Recursive {
fmt.Println(msg)
return nil
} else {
return errors.New(msg)
}
}
}
switch dest := conf.Destination.(type) {
case *publish.S3Destination, *publish.GCSDestination:
err = dest.Upload(fileContents, destinationPath)
case *publish.VaultDestination:
err = dest.UploadUnencrypted(data, destinationPath)
}
if err != nil {
return err
}
return nil
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
0707010000003C000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002A00000000sops-3.7.3/cmd/sops/subcommand/updatekeys0707010000003D000081A400000000000000000000000162794F9300000B9C000000000000000000000000000000000000003800000000sops-3.7.3/cmd/sops/subcommand/updatekeys/updatekeys.gopackage updatekeys
import (
"fmt"
"log"
"os"
"path/filepath"
"go.mozilla.org/sops/v3/cmd/sops/codes"
"go.mozilla.org/sops/v3/cmd/sops/common"
"go.mozilla.org/sops/v3/config"
"go.mozilla.org/sops/v3/keyservice"
)
// Opts represents key operation options and config
type Opts struct {
InputPath string
GroupQuorum int
KeyServices []keyservice.KeyServiceClient
Interactive bool
ConfigPath string
}
// UpdateKeys update the keys for a given file
func UpdateKeys(opts Opts) error {
path, err := filepath.Abs(opts.InputPath)
if err != nil {
return err
}
info, err := os.Stat(path)
if err != nil {
return err
}
if info.IsDir() {
return fmt.Errorf("can't operate on a directory")
}
opts.InputPath = path
return updateFile(opts)
}
func updateFile(opts Opts) error {
store := common.DefaultStoreForPath(opts.InputPath)
log.Printf("Syncing keys for file %s", opts.InputPath)
tree, err := common.LoadEncryptedFile(store, opts.InputPath)
if err != nil {
return err
}
conf, err := config.LoadCreationRuleForFile(opts.ConfigPath, opts.InputPath, make(map[string]*string))
if err != nil {
return err
}
diffs := common.DiffKeyGroups(tree.Metadata.KeyGroups, conf.KeyGroups)
keysWillChange := false
for _, diff := range diffs {
if len(diff.Added) > 0 || len(diff.Removed) > 0 {
keysWillChange = true
}
}
if !keysWillChange {
log.Printf("File %s already up to date", opts.InputPath)
return nil
}
fmt.Printf("The following changes will be made to the file's groups:\n")
common.PrettyPrintDiffs(diffs)
if opts.Interactive {
var response string
for response != "y" && response != "n" {
fmt.Printf("Is this okay? (y/n):")
_, err = fmt.Scanln(&response)
if err != nil {
return err
}
}
if response == "n" {
log.Printf("File %s left unchanged", opts.InputPath)
return nil
}
}
key, err := tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices)
if err != nil {
return common.NewExitError(err, codes.CouldNotRetrieveKey)
}
tree.Metadata.KeyGroups = conf.KeyGroups
if opts.GroupQuorum != 0 {
tree.Metadata.ShamirThreshold = opts.GroupQuorum
}
tree.Metadata.ShamirThreshold = min(tree.Metadata.ShamirThreshold, len(tree.Metadata.KeyGroups))
errs := tree.Metadata.UpdateMasterKeysWithKeyServices(key, opts.KeyServices)
if len(errs) > 0 {
return fmt.Errorf("error updating one or more master keys: %s", errs)
}
output, err := store.EmitEncryptedFile(*tree)
if err != nil {
return common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
}
outputFile, err := os.Create(opts.InputPath)
if err != nil {
return fmt.Errorf("could not open file for writing: %s", err)
}
defer outputFile.Close()
_, err = outputFile.Write(output)
if err != nil {
return fmt.Errorf("error writing to file: %s", err)
}
log.Printf("File %s synced with new keys", opts.InputPath)
return nil
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
0707010000003E000041ED00000000000000000000000362794F9300000000000000000000000000000000000000000000001200000000sops-3.7.3/config0707010000003F000081A400000000000000000000000162794F9300002B3E000000000000000000000000000000000000001C00000000sops-3.7.3/config/config.go/*
Package config provides a way to find and load SOPS configuration files
*/
package config //import "go.mozilla.org/sops/v3/config"
import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"regexp"
"strings"
"github.com/sirupsen/logrus"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/age"
"go.mozilla.org/sops/v3/azkv"
"go.mozilla.org/sops/v3/gcpkms"
"go.mozilla.org/sops/v3/hcvault"
"go.mozilla.org/sops/v3/kms"
"go.mozilla.org/sops/v3/logging"
"go.mozilla.org/sops/v3/pgp"
"go.mozilla.org/sops/v3/publish"
"gopkg.in/yaml.v3"
)
var log *logrus.Logger
func init() {
log = logging.NewLogger("CONFIG")
}
type fileSystem interface {
Stat(name string) (os.FileInfo, error)
}
type osFS struct {
stat func(string) (os.FileInfo, error)
}
func (fs osFS) Stat(name string) (os.FileInfo, error) {
return fs.stat(name)
}
var fs fileSystem = osFS{stat: os.Stat}
const (
maxDepth = 100
configFileName = ".sops.yaml"
)
// FindConfigFile looks for a sops config file in the current working directory and on parent directories, up to the limit defined by the maxDepth constant.
func FindConfigFile(start string) (string, error) {
filepath := path.Dir(start)
for i := 0; i < maxDepth; i++ {
_, err := fs.Stat(path.Join(filepath, configFileName))
if err != nil {
filepath = path.Join(filepath, "..")
} else {
return path.Join(filepath, configFileName), nil
}
}
return "", fmt.Errorf("Config file not found")
}
type configFile struct {
CreationRules []creationRule `yaml:"creation_rules"`
DestinationRules []destinationRule `yaml:"destination_rules"`
}
type keyGroup struct {
KMS []kmsKey
GCPKMS []gcpKmsKey `yaml:"gcp_kms"`
AzureKV []azureKVKey `yaml:"azure_keyvault"`
Vault []string `yaml:"hc_vault"`
Age []string `yaml:"age"`
PGP []string
}
type gcpKmsKey struct {
ResourceID string `yaml:"resource_id"`
}
type kmsKey struct {
Arn string `yaml:"arn"`
Role string `yaml:"role,omitempty"`
Context map[string]*string `yaml:"context"`
AwsProfile string `yaml:"aws_profile"`
}
type azureKVKey struct {
VaultURL string `yaml:"vaultUrl"`
Key string `yaml:"key"`
Version string `yaml:"version"`
}
type destinationRule struct {
PathRegex string `yaml:"path_regex"`
S3Bucket string `yaml:"s3_bucket"`
S3Prefix string `yaml:"s3_prefix"`
GCSBucket string `yaml:"gcs_bucket"`
GCSPrefix string `yaml:"gcs_prefix"`
VaultPath string `yaml:"vault_path"`
VaultAddress string `yaml:"vault_address"`
VaultKVMountName string `yaml:"vault_kv_mount_name"`
VaultKVVersion int `yaml:"vault_kv_version"`
RecreationRule creationRule `yaml:"recreation_rule,omitempty"`
OmitExtensions bool `yaml:"omit_extensions"`
}
type creationRule struct {
PathRegex string `yaml:"path_regex"`
KMS string
AwsProfile string `yaml:"aws_profile"`
Age string `yaml:"age"`
PGP string
GCPKMS string `yaml:"gcp_kms"`
AzureKeyVault string `yaml:"azure_keyvault"`
VaultURI string `yaml:"hc_vault_transit_uri"`
KeyGroups []keyGroup `yaml:"key_groups"`
ShamirThreshold int `yaml:"shamir_threshold"`
UnencryptedSuffix string `yaml:"unencrypted_suffix"`
EncryptedSuffix string `yaml:"encrypted_suffix"`
UnencryptedRegex string `yaml:"unencrypted_regex"`
EncryptedRegex string `yaml:"encrypted_regex"`
}
// Load loads a sops config file into a temporary struct
func (f *configFile) load(bytes []byte) error {
err := yaml.Unmarshal(bytes, f)
if err != nil {
return fmt.Errorf("Could not unmarshal config file: %s", err)
}
return nil
}
// Config is the configuration for a given SOPS file
type Config struct {
KeyGroups []sops.KeyGroup
ShamirThreshold int
UnencryptedSuffix string
EncryptedSuffix string
UnencryptedRegex string
EncryptedRegex string
Destination publish.Destination
OmitExtensions bool
}
func getKeyGroupsFromCreationRule(cRule *creationRule, kmsEncryptionContext map[string]*string) ([]sops.KeyGroup, error) {
var groups []sops.KeyGroup
if len(cRule.KeyGroups) > 0 {
for _, group := range cRule.KeyGroups {
var keyGroup sops.KeyGroup
for _, k := range group.Age {
keys, err := age.MasterKeysFromRecipients(k)
if err != nil {
return nil, err
}
for _, key := range keys {
keyGroup = append(keyGroup, key)
}
}
for _, k := range group.PGP {
keyGroup = append(keyGroup, pgp.NewMasterKeyFromFingerprint(k))
}
for _, k := range group.KMS {
keyGroup = append(keyGroup, kms.NewMasterKey(k.Arn, k.Role, k.Context))
}
for _, k := range group.GCPKMS {
keyGroup = append(keyGroup, gcpkms.NewMasterKeyFromResourceID(k.ResourceID))
}
for _, k := range group.AzureKV {
keyGroup = append(keyGroup, azkv.NewMasterKey(k.VaultURL, k.Key, k.Version))
}
for _, k := range group.Vault {
if masterKey, err := hcvault.NewMasterKeyFromURI(k); err == nil {
keyGroup = append(keyGroup, masterKey)
} else {
return nil, err
}
}
groups = append(groups, keyGroup)
}
} else {
var keyGroup sops.KeyGroup
if cRule.Age != "" {
ageKeys, err := age.MasterKeysFromRecipients(cRule.Age)
if err != nil {
return nil, err
} else {
for _, ak := range ageKeys {
keyGroup = append(keyGroup, ak)
}
}
}
for _, k := range pgp.MasterKeysFromFingerprintString(cRule.PGP) {
keyGroup = append(keyGroup, k)
}
for _, k := range kms.MasterKeysFromArnString(cRule.KMS, kmsEncryptionContext, cRule.AwsProfile) {
keyGroup = append(keyGroup, k)
}
for _, k := range gcpkms.MasterKeysFromResourceIDString(cRule.GCPKMS) {
keyGroup = append(keyGroup, k)
}
azureKeys, err := azkv.MasterKeysFromURLs(cRule.AzureKeyVault)
if err != nil {
return nil, err
}
for _, k := range azureKeys {
keyGroup = append(keyGroup, k)
}
vaultKeys, err := hcvault.NewMasterKeysFromURIs(cRule.VaultURI)
if err != nil {
return nil, err
}
for _, k := range vaultKeys {
keyGroup = append(keyGroup, k)
}
groups = append(groups, keyGroup)
}
return groups, nil
}
func loadConfigFile(confPath string) (*configFile, error) {
confBytes, err := ioutil.ReadFile(confPath)
if err != nil {
return nil, fmt.Errorf("could not read config file: %s", err)
}
conf := &configFile{}
err = conf.load(confBytes)
if err != nil {
return nil, fmt.Errorf("error loading config: %s", err)
}
return conf, nil
}
func configFromRule(rule *creationRule, kmsEncryptionContext map[string]*string) (*Config, error) {
cryptRuleCount := 0
if rule.UnencryptedSuffix != "" {
cryptRuleCount++
}
if rule.EncryptedSuffix != "" {
cryptRuleCount++
}
if rule.EncryptedRegex != "" {
cryptRuleCount++
}
if cryptRuleCount > 1 {
return nil, fmt.Errorf("error loading config: cannot use more than one of encrypted_suffix, unencrypted_suffix, or encrypted_regex for the same rule")
}
groups, err := getKeyGroupsFromCreationRule(rule, kmsEncryptionContext)
if err != nil {
return nil, err
}
return &Config{
KeyGroups: groups,
ShamirThreshold: rule.ShamirThreshold,
UnencryptedSuffix: rule.UnencryptedSuffix,
EncryptedSuffix: rule.EncryptedSuffix,
UnencryptedRegex: rule.UnencryptedRegex,
EncryptedRegex: rule.EncryptedRegex,
}, nil
}
func parseDestinationRuleForFile(conf *configFile, filePath string, kmsEncryptionContext map[string]*string) (*Config, error) {
var rule *creationRule
var dRule *destinationRule
if len(conf.DestinationRules) > 0 {
for _, r := range conf.DestinationRules {
if r.PathRegex == "" {
dRule = &r
rule = &dRule.RecreationRule
break
}
if r.PathRegex != "" {
if match, _ := regexp.MatchString(r.PathRegex, filePath); match {
dRule = &r
rule = &dRule.RecreationRule
break
}
}
}
}
if dRule == nil {
return nil, fmt.Errorf("error loading config: no matching destination found in config")
}
var dest publish.Destination
if dRule != nil {
if dRule.S3Bucket != "" && dRule.GCSBucket != "" && dRule.VaultPath != "" {
return nil, fmt.Errorf("error loading config: more than one destinations were found in a single destination rule, you can only use one per rule")
}
if dRule.S3Bucket != "" {
dest = publish.NewS3Destination(dRule.S3Bucket, dRule.S3Prefix)
}
if dRule.GCSBucket != "" {
dest = publish.NewGCSDestination(dRule.GCSBucket, dRule.GCSPrefix)
}
if dRule.VaultPath != "" {
dest = publish.NewVaultDestination(dRule.VaultAddress, dRule.VaultPath, dRule.VaultKVMountName, dRule.VaultKVVersion)
}
}
config, err := configFromRule(rule, kmsEncryptionContext)
if err != nil {
return nil, err
}
config.Destination = dest
config.OmitExtensions = dRule.OmitExtensions
return config, nil
}
func parseCreationRuleForFile(conf *configFile, confPath, filePath string, kmsEncryptionContext map[string]*string) (*Config, error) {
// If config file doesn't contain CreationRules (it's empty or only contains DestionationRules), assume it does not exist
if conf.CreationRules == nil {
return nil, nil
}
configDir, err := filepath.Abs(filepath.Dir(confPath))
if err != nil {
return nil, err
}
// compare file path relative to path of config file
filePath = strings.TrimPrefix(filePath, configDir + string(filepath.Separator))
var rule *creationRule
for _, r := range conf.CreationRules {
if r.PathRegex == "" {
rule = &r
break
}
reg, err := regexp.Compile(r.PathRegex)
if err != nil {
return nil, fmt.Errorf("can not compile regexp: %w", err)
}
if reg.MatchString(filePath) {
rule = &r
break
}
}
if rule == nil {
return nil, fmt.Errorf("error loading config: no matching creation rules found")
}
config, err := configFromRule(rule, kmsEncryptionContext)
if err != nil {
return nil, err
}
return config, nil
}
// LoadCreationRuleForFile load the configuration for a given SOPS file from the config file at confPath. A kmsEncryptionContext
// should be provided for configurations that do not contain key groups, as there's no way to specify context inside
// a SOPS config file outside of key groups.
func LoadCreationRuleForFile(confPath string, filePath string, kmsEncryptionContext map[string]*string) (*Config, error) {
conf, err := loadConfigFile(confPath)
if err != nil {
return nil, err
}
return parseCreationRuleForFile(conf, confPath, filePath, kmsEncryptionContext)
}
// LoadDestinationRuleForFile works the same as LoadCreationRuleForFile, but gets the "creation_rule" from the matching destination_rule's
// "recreation_rule".
func LoadDestinationRuleForFile(confPath string, filePath string, kmsEncryptionContext map[string]*string) (*Config, error) {
conf, err := loadConfigFile(confPath)
if err != nil {
return nil, err
}
return parseDestinationRuleForFile(conf, filePath, kmsEncryptionContext)
}
07070100000040000081A400000000000000000000000162794F9300003113000000000000000000000000000000000000002100000000sops-3.7.3/config/config_test.gopackage config
import (
"os"
"path"
"testing"
"github.com/stretchr/testify/assert"
)
type mockFS struct {
stat func(string) (os.FileInfo, error)
}
func (fs mockFS) Stat(name string) (os.FileInfo, error) {
return fs.stat(name)
}
func TestFindConfigFileRecursive(t *testing.T) {
expectedPath := path.Clean("./../../.sops.yaml")
fs = mockFS{stat: func(name string) (os.FileInfo, error) {
if name == expectedPath {
return nil, nil
}
return nil, &os.PathError{}
}}
filepath, err := FindConfigFile(".")
assert.Nil(t, err)
assert.Equal(t, expectedPath, filepath)
}
func TestFindConfigFileCurrentDir(t *testing.T) {
expectedPath := path.Clean(".sops.yaml")
fs = mockFS{stat: func(name string) (os.FileInfo, error) {
if name == expectedPath {
return nil, nil
}
return nil, &os.PathError{}
}}
filepath, err := FindConfigFile(".")
assert.Nil(t, err)
assert.Equal(t, expectedPath, filepath)
}
var sampleConfig = []byte(`
creation_rules:
- path_regex: foobar*
kms: "1"
pgp: "2"
gcp_kms: "3"
hc_vault_transit_uri: http://4:8200/v1/4/keys/4
- path_regex: ""
kms: foo
pgp: bar
gcp_kms: baz
hc_vault_transit_uri: http://127.0.1.1/v1/baz/keys/baz
`)
var sampleConfigWithPath = []byte(`
creation_rules:
- path_regex: foo/bar*
kms: "1"
pgp: "2"
gcp_kms: "3"
hc_vault_uris: http://4:8200/v1/4/keys/4
- path_regex: somefilename.yml
kms: bilbo
pgp: baggins
gcp_kms: precious
hc_vault_uris: https://pluto/v1/pluto/keys/pluto
- path_regex: ""
kms: foo
pgp: bar
gcp_kms: baz
hc_vault_uris: https://foz:443/v1/foz/keys/foz
`)
var sampleConfigWithAmbiguousPath = []byte(`
creation_rules:
- path_regex: foo/*
kms: "1"
pgp: "2"
gcp_kms: "3"
hc_vault_uris: http://4:8200/v1/4/keys/4
`)
var sampleConfigWithGroups = []byte(`
creation_rules:
- path_regex: foobar*
kms: "1"
pgp: "2"
- path_regex: ""
key_groups:
- kms:
- arn: foo
pgp:
- bar
gcp_kms:
- resource_id: foo
azure_keyvault:
- vaultUrl: https://foo.vault.azure.net
key: foo-key
version: fooversion
hc_vault:
- 'https://foo.vault:8200/v1/foo/keys/foo-key'
- kms:
- arn: baz
pgp:
- qux
gcp_kms:
- resource_id: bar
- resource_id: baz
azure_keyvault:
- vaultUrl: https://bar.vault.azure.net
key: bar-key
version: barversion
hc_vault:
- 'https://baz.vault:8200/v1/baz/keys/baz-key'
`)
var sampleConfigWithSuffixParameters = []byte(`
creation_rules:
- path_regex: foobar*
kms: "1"
pgp: "2"
unencrypted_suffix: _unencrypted
- path_regex: bar?foo$
encrypted_suffix: _enc
key_groups:
- kms:
- arn: baz
pgp:
- qux
gcp_kms:
- resource_id: bar
- resource_id: baz
azure_keyvault:
- vaultUrl: https://foo.vault.azure.net
key: foo-key
version: fooversion
`)
var sampleConfigWithRegexParameters = []byte(`
creation_rules:
- path_regex: barbar*
kms: "1"
pgp: "2"
encrypted_regex: "^enc:"
unencrypted_regex: "^dec:"
`)
var sampleConfigWithInvalidParameters = []byte(`
creation_rules:
- path_regex: foobar*
kms: "1"
pgp: "2"
hc_vault_uris: "https://vault.com/v1/bug/keys/pr"
unencrypted_suffix: _unencrypted
encrypted_suffix: _enc
`)
var sampleConfigWithNoMatchingRules = []byte(`
creation_rules:
- path_regex: notexisting
pgp: bar
`)
var sampleEmptyConfig = []byte(``)
var sampleConfigWithEmptyCreationRules = []byte(`
creation_rules:
`)
var sampleConfigWithOnlyDestinationRules = []byte(`
destination_rules:
- path_regex: ""
s3_bucket: "foobar"
s3_prefix: "test/"
recreation_rule:
pgp: newpgp
`)
var sampleConfigWithDestinationRule = []byte(`
creation_rules:
- path_regex: foobar*
kms: "1"
pgp: "2"
gcp_kms: "3"
- path_regex: ""
kms: foo
pgp: bar
gcp_kms: baz
destination_rules:
- path_regex: ""
s3_bucket: "foobar"
s3_prefix: "test/"
recreation_rule:
pgp: newpgp
`)
var sampleConfigWithVaultDestinationRules = []byte(`
creation_rules:
- path_regex: foobar*
kms: "1"
pgp: "2"
gcp_kms: "3"
- path_regex: ""
kms: foo
pgp: bar
gcp_kms: baz
destination_rules:
- vault_path: "foobar/"
path_regex: "vault-v2/*"
- vault_path: "barfoo/"
vault_kv_mount_name: "kv/"
vault_kv_version: 1
path_regex: "vault-v1/*"
`)
var sampleConfigWithInvalidComplicatedRegexp = []byte(`
creation_rules:
- path_regex: "[ ]\\K(?<!\\d )(?="
kms: default
`)
var sampleConfigWithComplicatedRegexp = []byte(`
creation_rules:
- path_regex: "stage/dev/feature-.*"
kms: dev-feature
- path_regex: "stage/dev/.*"
kms: dev
- path_regex: "stage/staging/.*"
kms: staging
- path_regex: "stage/.*/.*"
kms: default
`)
func parseConfigFile(confBytes []byte, t *testing.T) *configFile {
conf := &configFile{}
err := conf.load(confBytes)
assert.Nil(t, err)
return conf
}
func TestLoadConfigFile(t *testing.T) {
expected := configFile{
CreationRules: []creationRule{
{
PathRegex: "foobar*",
KMS: "1",
PGP: "2",
GCPKMS: "3",
VaultURI: "http://4:8200/v1/4/keys/4",
},
{
PathRegex: "",
KMS: "foo",
PGP: "bar",
GCPKMS: "baz",
VaultURI: "http://127.0.1.1/v1/baz/keys/baz",
},
},
}
conf := configFile{}
err := conf.load(sampleConfig)
assert.Nil(t, err)
assert.Equal(t, expected, conf)
}
func TestLoadConfigFileWithGroups(t *testing.T) {
expected := configFile{
CreationRules: []creationRule{
{
PathRegex: "foobar*",
KMS: "1",
PGP: "2",
},
{
PathRegex: "",
KeyGroups: []keyGroup{
{
KMS: []kmsKey{{Arn: "foo"}},
PGP: []string{"bar"},
GCPKMS: []gcpKmsKey{{ResourceID: "foo"}},
AzureKV: []azureKVKey{{VaultURL: "https://foo.vault.azure.net", Key: "foo-key", Version: "fooversion"}},
Vault: []string{"https://foo.vault:8200/v1/foo/keys/foo-key"},
},
{
KMS: []kmsKey{{Arn: "baz"}},
PGP: []string{"qux"},
GCPKMS: []gcpKmsKey{
{ResourceID: "bar"},
{ResourceID: "baz"},
},
AzureKV: []azureKVKey{{VaultURL: "https://bar.vault.azure.net", Key: "bar-key", Version: "barversion"}},
Vault: []string{"https://baz.vault:8200/v1/baz/keys/baz-key"},
},
},
},
},
}
conf := configFile{}
err := conf.load(sampleConfigWithGroups)
assert.Nil(t, err)
assert.Equal(t, expected, conf)
}
func TestLoadConfigFileWithNoMatchingRules(t *testing.T) {
_, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithNoMatchingRules, t), "/conf/path", "foobar2000", nil)
assert.NotNil(t, err)
}
func TestLoadConfigFileWithInvalidComplicatedRegexp(t *testing.T) {
conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithInvalidComplicatedRegexp, t), "/conf/path", "stage/prod/api.yml", nil)
assert.Equal(t, "can not compile regexp: error parsing regexp: invalid escape sequence: `\\K`", err.Error())
assert.Nil(t, conf)
}
func TestLoadConfigFileWithComplicatedRegexp(t *testing.T) {
for filePath, k := range map[string]string{
"stage/prod/api.yml": "default",
"stage/dev/feature-foo.yml": "dev-feature",
"stage/dev/api.yml": "dev",
} {
conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithComplicatedRegexp, t), "/conf/path", filePath, nil)
assert.Nil(t, err)
assert.Equal(t, k, conf.KeyGroups[0][0].ToString())
}
}
func TestLoadEmptyConfigFile(t *testing.T) {
conf, err := parseCreationRuleForFile(parseConfigFile(sampleEmptyConfig, t), "/conf/path", "foobar2000", nil)
assert.Nil(t, conf)
assert.Nil(t, err)
}
func TestLoadConfigFileWithEmptyCreationRules(t *testing.T) {
conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithEmptyCreationRules, t), "/conf/path", "foobar2000", nil)
assert.Nil(t, conf)
assert.Nil(t, err)
}
func TestLoadConfigFileWithOnlyDestinationRules(t *testing.T) {
conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithOnlyDestinationRules, t), "/conf/path", "foobar2000", nil)
assert.Nil(t, conf)
assert.Nil(t, err)
}
func TestKeyGroupsForFile(t *testing.T) {
conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfig, t), "/conf/path", "foobar2000", nil)
assert.Nil(t, err)
assert.Equal(t, "2", conf.KeyGroups[0][0].ToString())
assert.Equal(t, "1", conf.KeyGroups[0][1].ToString())
conf, err = parseCreationRuleForFile(parseConfigFile(sampleConfig, t), "/conf/path", "whatever", nil)
assert.Nil(t, err)
assert.Equal(t, "bar", conf.KeyGroups[0][0].ToString())
assert.Equal(t, "foo", conf.KeyGroups[0][1].ToString())
}
func TestKeyGroupsForFileWithPath(t *testing.T) {
conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithPath, t), "/conf/path", "foo/bar2000", nil)
assert.Nil(t, err)
assert.Equal(t, "2", conf.KeyGroups[0][0].ToString())
assert.Equal(t, "1", conf.KeyGroups[0][1].ToString())
conf, err = parseCreationRuleForFile(parseConfigFile(sampleConfigWithPath, t), "/conf/path", "somefilename.yml", nil)
assert.Nil(t, err)
assert.Equal(t, "baggins", conf.KeyGroups[0][0].ToString())
assert.Equal(t, "bilbo", conf.KeyGroups[0][1].ToString())
conf, err = parseCreationRuleForFile(parseConfigFile(sampleConfig, t), "/conf/path", "whatever", nil)
assert.Nil(t, err)
assert.Equal(t, "bar", conf.KeyGroups[0][0].ToString())
assert.Equal(t, "foo", conf.KeyGroups[0][1].ToString())
}
func TestKeyGroupsForFileWithGroups(t *testing.T) {
conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithGroups, t), "/conf/path", "whatever", nil)
assert.Nil(t, err)
assert.Equal(t, "bar", conf.KeyGroups[0][0].ToString())
assert.Equal(t, "foo", conf.KeyGroups[0][1].ToString())
assert.Equal(t, "qux", conf.KeyGroups[1][0].ToString())
assert.Equal(t, "baz", conf.KeyGroups[1][1].ToString())
}
func TestLoadConfigFileWithUnencryptedSuffix(t *testing.T) {
conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithSuffixParameters, t), "/conf/path", "foobar", nil)
assert.Nil(t, err)
assert.Equal(t, "_unencrypted", conf.UnencryptedSuffix)
}
func TestLoadConfigFileWithEncryptedSuffix(t *testing.T) {
conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithSuffixParameters, t), "/conf/path", "barfoo", nil)
assert.Nil(t, err)
assert.Equal(t, "_enc", conf.EncryptedSuffix)
}
func TestLoadConfigFileWithUnencryptedRegex(t *testing.T) {
conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithRegexParameters, t), "/conf/path", "barbar", nil)
assert.Equal(t, nil, err)
assert.Equal(t, "^dec:", conf.UnencryptedRegex)
}
func TestLoadConfigFileWithEncryptedRegex(t *testing.T) {
conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithRegexParameters, t), "/conf/path", "barbar", nil)
assert.Equal(t, nil, err)
assert.Equal(t, "^enc:", conf.EncryptedRegex)
}
func TestLoadConfigFileWithInvalidParameters(t *testing.T) {
_, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithInvalidParameters, t), "/conf/path", "foobar", nil)
assert.NotNil(t, err)
}
func TestLoadConfigFileWithAmbiguousPath(t *testing.T) {
config := parseConfigFile(sampleConfigWithAmbiguousPath, t)
_, err := parseCreationRuleForFile(config, "/foo/config", "/foo/foo/bar", nil)
assert.Nil(t, err)
_, err = parseCreationRuleForFile(config, "/foo/config", "/foo/fuu/bar", nil)
assert.NotNil(t, err)
}
func TestLoadConfigFileWithDestinationRule(t *testing.T) {
conf, err := parseDestinationRuleForFile(parseConfigFile(sampleConfigWithDestinationRule, t), "barfoo", nil)
assert.Nil(t, err)
assert.Equal(t, "newpgp", conf.KeyGroups[0][0].ToString())
assert.NotNil(t, conf.Destination)
assert.Equal(t, "s3://foobar/test/barfoo", conf.Destination.Path("barfoo"))
}
func TestLoadConfigFileWithVaultDestinationRules(t *testing.T) {
conf, err := parseDestinationRuleForFile(parseConfigFile(sampleConfigWithVaultDestinationRules, t), "vault-v2/barfoo", nil)
assert.Nil(t, err)
assert.NotNil(t, conf.Destination)
assert.Contains(t, conf.Destination.Path("barfoo"), "/v1/secret/data/foobar/barfoo")
conf, err = parseDestinationRuleForFile(parseConfigFile(sampleConfigWithVaultDestinationRules, t), "vault-v1/barfoo", nil)
assert.Nil(t, err)
assert.NotNil(t, conf.Destination)
assert.Contains(t, conf.Destination.Path("barfoo"), "/v1/kv/barfoo/barfoo")
}
07070100000041000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002100000000sops-3.7.3/config/test_resources07070100000042000081A400000000000000000000000162794F9300000B59000000000000000000000000000000000000002E00000000sops-3.7.3/config/test_resources/example.yamlexample_key: ENC[AES256_GCM,data:mOtEkS9nmmncpxRBYA==,iv:ecPCSny/r62lJr2DuwvucwK/SGiAWLSbariWob7r4Ek=,tag:3m8Dul5yxqEmunMJom+Cag==,type:str]
example_array:
- ENC[AES256_GCM,data:vYoTCRm2gudQ/XFkIm8=,iv:DOM9XZRFFdty4kjMWvqT4UCiwHr5sd7Z3CiS6Xg4q8Q=,tag:07gBVF4mSeG/7Xs+qBeMew==,type:str]
- ENC[AES256_GCM,data:Wf3LVG1677UaZSzzIYc=,iv:8sQd09YT7BkJSntvKUudfXtjPyqzsPqayu0Qo8v+SHI=,tag:zVz8aWLVox40iEKOSUGW+Q==,type:str]
example_multiline: |-
ENC[AES256_GCM,data:Jw34myS8s3NPrOnPnBdJ67bVyNxKmq7pmp0=,iv:8pjw5k5XpV2x4bM2jpISMHy97b6ffkCFvbtKOiZ22VU=,tag:V3NFD/WtgBDXujNTxst1HQ==,type:str]
example_number: ENC[AES256_GCM,data:/KVkicl+xk6x,iv:M+qJNRywTlnkk28nLV3aO6pszNoLcNlrSs05aPIwgwE=,tag:Zku+tQMIeF/Sqnuy8QW4zg==,type:float]
example:
nested:
values: ENC[AES256_GCM,data:y/Gbd4dFl8X4,iv:zOKYeVFBLEoliQ/WuxgNwui+9Vy1CcR+Eg9Zrs9d2qs=,tag:UUC/23KmH4ZGPT+dhHBYGg==,type:str]
example_booleans:
- ENC[AES256_GCM,data:RYzb/A==,iv:7cQpPBZ/0Woa5X3kdlx+4pOW15EmJMXopelmOGfYd1w=,tag:RT0L/KZJTrHQ9E3fgFfqhA==,type:bool]
- ENC[AES256_GCM,data:H/wjpX4=,iv:EDRADHc4Z65yJzXHpVsWgaqcNPhwEdmSNeRtmi14p0g=,tag:ED3o+9Yv4NkoMg2BeS03Cw==,type:bool]
sops:
lastmodified: '2016-08-02T18:34:06Z'
attention: This section contains key material that should only be modified with
extra care. See `sops -h`.
unencrypted_suffix: _unencrypted
mac: ENC[AES256_GCM,data:n6bRU4NrPaMvQj0nI7j7x+Ihkdvr61SDYKw7atVeTDGj+puvd+/0DuFyeWJADNtlAZq7pasSobUMuA66MrNpavMLc5/tBX+NZbT1cbGD0nBt4y7ztog1MFSay03CDrhbepmWNSs8ihgYtHfxfGiqzTU5sRTM+iEHAiwJ1h7eU4k=,iv:oqO1KMNkWUC+aAxgNV4Kng4CBWzO7wIIswDwbRvlv8g=,tag:uUmgKKFeNGvAzZZLI0RViA==,type:str]
version: '1.13'
pgp:
- fp: E5297818703249D0C60E19E6824612478D1A4CCD
created_at: '2016-08-02T18:34:06Z'
enc: |
-----BEGIN PGP MESSAGE-----
Version: GnuPG v1
hQIMA2X8rvoeiASBAQ/+MYitjFEMfLz5N6JeZ0IoYAbpJZPWkUavhXF9H4SU9mYi
8Tp4KgJVRhv7IOsqmuMrxIWZL9++HqEpe+HWWy6luzPrWU7HIPpwGcYRCyWrdt2u
UjBVYMVgLwGMfrWe6sr0yWrVXrLQ2ltPBn8J5vyHDZUYXY33B1vj7F2BuF2oL32J
f9ZTAikiHJI/4Q62P5x7RQrqHqV/Ncczf+w/ni+ZnOwH5kay306DKNwTX64AWoc6
iW/nPCRzIfhWx89BPu+SwGaEwQbHvS7zMLZ/UtarNWAFQ5QmlBW6dyH9Edj5bA7u
BuhxjBT/6ZaXX6iSAl98021eplf6s945vB7gPuzrgW1LM4t6X8M9P0KMMrDujz/S
cm7Vrhm//syT2IJqFR9x/PylbpHhVTwTWZIFvLRRJlM66NYsvxMvJdknovN/889D
I+mQkA8oxtjyof8fgIOvU7ph6yBzRbb838a0Fyu5U5wwrNzBc5N23WNjzNcT1XRy
kl7W7MElHnjzq2jNJIB+XipaV6uoMbaoiYZNi+bpHj6pASCaBko7c8aPYVLruPbZ
G3fCMY1mxwRBYyn6d1sTqzVGR4WBJLc+4ikm+H4O4aa4sv137/cnRRbVNhpgefob
H9SSjSr2gr9YiHgMsG0kdilyTnccYaeljBqOJKUKiWOPk8OBiycNtU+rESagw5rS
XgHpMul9uyxksAQf7VpXJJQYYgk+DlZhkkd/hVu59vxJo2oxxp5aGyBijGSy7mcI
3FajmsEFKdPaHpbsSDP0/83RHIwVwbQK5N+IcNGHk/rnh7XEJ73vtrsA9muAmoo=
=OxaE
-----END PGP MESSAGE-----
07070100000043000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001300000000sops-3.7.3/decrypt07070100000044000081A400000000000000000000000162794F930000090C000000000000000000000000000000000000001E00000000sops-3.7.3/decrypt/decrypt.go/*
Package decrypt is the external API other Go programs can use to decrypt SOPS files. It is the only package in SOPS with
a stable API.
*/
package decrypt // import "go.mozilla.org/sops/v3/decrypt"
import (
"fmt"
"io/ioutil"
"time"
"go.mozilla.org/sops/v3/aes"
"go.mozilla.org/sops/v3/cmd/sops/common"
. "go.mozilla.org/sops/v3/cmd/sops/formats" // Re-export
)
// File is a wrapper around Data that reads a local encrypted
// file and returns its cleartext data in an []byte
func File(path, format string) (cleartext []byte, err error) {
// Read the file into an []byte
encryptedData, err := ioutil.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("Failed to read %q: %w", path, err)
}
// uses same logic as cli.
formatFmt := FormatForPathOrString(path, format)
return DataWithFormat(encryptedData, formatFmt)
}
// DataWithFormat is a helper that takes encrypted data, and a format enum value,
// decrypts the data and returns its cleartext in an []byte.
func DataWithFormat(data []byte, format Format) (cleartext []byte, err error) {
store := common.StoreForFormat(format)
// Load SOPS file and access the data key
tree, err := store.LoadEncryptedFile(data)
if err != nil {
return nil, err
}
key, err := tree.Metadata.GetDataKey()
if err != nil {
return nil, err
}
// Decrypt the tree
cipher := aes.NewCipher()
mac, err := tree.Decrypt(key, cipher)
if err != nil {
return nil, err
}
// Compute the hash of the cleartext tree and compare it with
// the one that was stored in the document. If they match,
// integrity was preserved
originalMac, err := cipher.Decrypt(
tree.Metadata.MessageAuthenticationCode,
key,
tree.Metadata.LastModified.Format(time.RFC3339),
)
if originalMac != mac {
return nil, fmt.Errorf("Failed to verify data integrity. expected mac %q, got %q", originalMac, mac)
}
return store.EmitPlainFile(tree.Branches)
}
// Data is a helper that takes encrypted data and a format string,
// decrypts the data and returns its cleartext in an []byte.
// The format string can be `json`, `yaml`, `ini`, `dotenv` or `binary`.
// If the format string is empty, binary format is assumed.
func Data(data []byte, format string) (cleartext []byte, err error) {
formatFmt := FormatFromString(format)
return DataWithFormat(data, formatFmt)
}
07070100000045000081A400000000000000000000000162794F9300000522000000000000000000000000000000000000002300000000sops-3.7.3/decrypt/example_test.gopackage decrypt
import (
"encoding/json"
"go.mozilla.org/sops/v3/logging"
"github.com/sirupsen/logrus"
)
var log *logrus.Logger
func init() {
log = logging.NewLogger("DECRYPT")
}
type configuration struct {
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Age float64 `json:"age"`
Address struct {
City string `json:"city"`
PostalCode string `json:"postalCode"`
State string `json:"state"`
StreetAddress string `json:"streetAddress"`
} `json:"address"`
PhoneNumbers []struct {
Number string `json:"number"`
Type string `json:"type"`
} `json:"phoneNumbers"`
AnEmptyValue string `json:"anEmptyValue"`
}
func ExampleDecryptFile() {
var (
confPath string = "./example.json"
cfg configuration
err error
)
confData, err := File(confPath, "json")
if err != nil {
log.Fatalf("cleartext configuration marshalling failed with error: %v", err)
}
err = json.Unmarshal(confData, &cfg)
if err != nil {
log.Fatalf("cleartext configuration unmarshalling failed with error: %v", err)
}
if cfg.FirstName != "John" ||
cfg.LastName != "Smith" ||
cfg.Age != 25.4 ||
cfg.PhoneNumbers[1].Number != "646 555-4567" {
log.Fatalf("configuration does not contain expected values: %+v", cfg)
}
log.Printf("%+v", cfg)
}
07070100000046000081A400000000000000000000000162794F9300000C64000000000000000000000000000000000000001700000000sops-3.7.3/example.ini; ENC[AES256_GCM,data:EexYkXJHDv1E9WwU8wUrBakxAZcnlIkAJQgRFts3alF+5w==,iv:/wd7bCxSkJ85eG69NMq2RH70Dlw5q/diL42GcdTbRYs=,tag:8W4B3JjLYAtJgG3Zi6Hu+A==,type:comment]
[name]
firstName = ENC[AES256_GCM,data:7cWtsg==,iv:rh/1l0yiOE3JmmSJB9CXp4Ctb5en0FcNUZZkLyWsX3c=,tag:d7We6RtSs3AYhA6jan/CoA==,type:str]
lastName = ENC[AES256_GCM,data:Iq50v00=,iv:o+bfCsNBsv/+XdXS4M7TCOTPzG38ft14/Q9/vZvnNvM=,tag:cRivFOZyIpMDXdKA9R+6mA==,type:str]
age = ENC[AES256_GCM,data:aSt9Fg==,iv:z1NjXdRRFs8+dAFkrngS7PyhUSCOTzAmxtzCcfafkAM=,tag:6kpo65TxMu1oKF9d7L1wJg==,type:str]
[address]
city = ENC[AES256_GCM,data:3hQ6RXAc8ik=,iv:ajAjhQIJiNWy9PGj8ZCI5k+3uw7igweqZ+eiZ9tuvG0=,tag:bGX7PK+/hTQE/1uYmXexyA==,type:str]
postalCode = ENC[AES256_GCM,data:1Ha0avZTe6IflQ==,iv:l8rbQbhSwxr3AJzqe/H8ALrHcXsntcBfkyUGt9/6k/U=,tag:f6s6ObY5QaqmG4CUpV+UKg==,type:str]
state = ENC[AES256_GCM,data:ba0=,iv:P/VsGYbhT8ihRvluWc8HcUDPPigh/IcHXBnj0Xb86AE=,tag:JN7Y77eOaF1+tJjN2cxtnQ==,type:str]
streetAddress = ENC[AES256_GCM,data:LYYluDyJzryoYT2Nkg==,iv:KTmXPVV5tSY8/piTb/uPcfJL2mUqkS1aUI9pukQm1Dc=,tag:AzdSsRjEWx8kzclSZIdIcA==,type:str]
[phoneNumbers]
home = ENC[AES256_GCM,data:wv3pG8J7wmiotVY6,iv:YdX9RKlN9t0PohEu+Dxws0POUf8hjDMi4PJre+4/lsg=,tag:2XadWng1DxnfMituylFC5g==,type:str]
office = ENC[AES256_GCM,data:Hu3X626TUgV3Ix7i,iv:P1SBjvZPogJih4KLFLcbxeWeZT2aTNXPBtwlC9yZZ70=,tag:4SOkQZw4ynKfrFwrmre6Lw==,type:str]
[not private]
notsecret_unencrypted = hi there!
[sops]
pgp__list_0__map_fp = FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
pgp__list_1__map_created_at = 2019-12-10T22:45:52Z
pgp__list_1__map_fp = D7229043384BCC60326C6FB9D8720D957C3D3074
lastmodified = 2019-12-10T22:45:52Z
pgp__list_0__map_created_at = 2019-12-10T22:45:52Z
pgp__list_0__map_enc = -----BEGIN PGP MESSAGE-----\n\nwcBMAyUpShfNkFB/AQgAGb6tDB1gDtwLcVuENgd/AU1ZjxDbUY46OJzi1fkQ4jyo\nvxfCviqfslYyncVrKab0S9U+alIu8Po8BrHCTwmDkcnPG7HWMcqRo+Nq3JShir3y\ntVMbHuRGykPmGjs7GOUn1WibOisiJjHzLdFOsyNexveBV9AVln7kZazCRlXwbLqc\nyJ9IQsWi1ET6DjtViejiBKblDzZ87WgvL2Z0E8QfYXmrB3GpFa+K4Aqgb3pHkQQo\nGQArNAe8BmrhEXotj0IS/K3v2wjX5Sqli+0dh4qhaXiIeYb16nfc4M1TUo4Vvj7Q\nyWdjufTi+2RIxxUEkIdRg6lDVabm98hLn6/jsQ5c5tLgAeT99N9te4j1eeO62xwy\nz4S+4Xok4Cng9OFHFODz4hh6ah3gYuXRf4qFh++GUaxGwG/iGNSiY7A9eWg1gM+N\nPB6MtiOnhuB15N5mbD2V1xrzlF0fu0K98Vviv75h4eGzYQA=\n=RaGg\n-----END PGP MESSAGE-----
mac = ENC[AES256_GCM,data:5NouZe0uY8581eqXLe31TWYqThPlfePdpIyYDBgj3FwGvPfhiWB+dn/diSt4NpZ9zUNI4VhF2cmNesk8NgZdUK9/N9WbGgmJjjdBXvo0xhvr8RR22AiVCB9ipDlSFqnOMyJzo+CAvpSDbwF1bMU0qxwKW+zpDZV2uLX+px7b2qM=,iv:em3/lIV7eYA46u2kM0bR1/3O6Ga2vRfS5/r2qh0jges=,tag:ow9277u7mFNcs4IdsU4YUQ==,type:str]
pgp__list_1__map_enc = -----BEGIN PGP MESSAGE-----\n\nwYwDXFUltYFwV4MBBAAQYkLcQ1MORGYYprtFwUZPO2J3CBOU4u76qJQod5JXZuAa\n3aCynkyrOHIpgh2cKoyUle4u/FK68mFl9+TxixlFDRxt1CsvMR8dHP0EFJOMSq5U\nNwpcnEh20++3DNiq7bCtS2W68FRh8bVxmhEEXPxW4HLJV4WZSE4pu9B7w9kd5NLg\nAeQyj0ugpILq3IonJdLsgLkg4RGV4GDgFOEqxOCT4uKeSX/gdOXvr5WJzlWJRLjT\nA51CtgnUVXlNO5zfCiFRMXOFyE1FCeBm5Ivkgh4KxkQvwI6jSZpU1rjiz4QYTOGO\n4gA=\n=XdTd\n-----END PGP MESSAGE-----
unencrypted_suffix = _unencrypted
version = 3.5.0
07070100000047000081A400000000000000000000000162794F9300000CBB000000000000000000000000000000000000001800000000sops-3.7.3/example.json{
"firstName": "ENC[AES256_GCM,data:f8++3g==,iv:rYuVzzb+C40QlYgO4Dl2V7atZUx0ITBcyb5fUsftKMo=,tag:krquPqa1HQltZqidzNamrA==,type:str]",
"lastName": "ENC[AES256_GCM,data:94a2Q8c=,iv:c3NC7L80UTtbz7gdvPV5oSUwg30lC3Kg82uvRVs5CZw=,tag:kUXRNerUWmSe44mwD4w5uA==,type:str]",
"age": "ENC[AES256_GCM,data:gjwWkw==,iv:XEWFpsyvEsPwr3qqsOJlfZ+vSZdiA+D6DAc6aoq/BS0=,tag:pcnUyMtYFa9v5DB6sNV15w==,type:float]",
"address": {
"city": "ENC[AES256_GCM,data:vSeyQwN1Z9k=,iv:DBmuX4w6w14Z/1b820OE3SM3MPx3oLGAeSoR4CWxdhg=,tag:ClpJZLb4ObIOdDD441clrw==,type:str]",
"postalCode": "ENC[AES256_GCM,data:SZadC4tZh106eg==,iv:z/yWCZTd19j+3cFY5mwVkxY8a7i6veTBnwh4fsw5Kbw=,tag:iel9Pqh0jS0KhjxklXeqIg==,type:str]",
"state": "ENC[AES256_GCM,data:b0Y=,iv:7Ar/Tb7XCDo5ABZNdSNBqGquaGEQF7dNxd1VvW7Nwak=,tag:uEjmOKwPlkYSq4IV1tQjwQ==,type:str]",
"streetAddress": "ENC[AES256_GCM,data:dVFPTRPKOFSJ1plV9w==,iv:08Ks4C1FzFozezKBBYSPEAIkC5DkthDFmMW0R3zVbkI=,tag:waInfXMCAcx5C7avXHahOw==,type:str]"
},
"phoneNumbers": [
{
"number": "ENC[AES256_GCM,data:lkUEC7s3qU9AY6W+,iv:KjF9i0K9u7THbb3Bn1adQrIKpv1ZqA3PiJkctgFm3Bw=,tag:LBSqqr+gn15x0Pz5JKMJJQ==,type:str]",
"type": "ENC[AES256_GCM,data:aXBHGg==,iv:ulcjNwVfGFvUtVN8q0h1LMYM5zRDmOsqtoFC1JOHREY=,tag:7vdMoCOD+7IeUHWQqQJ8XA==,type:str]"
},
{
"number": "ENC[AES256_GCM,data:z9Ujp3n2yXBqPNM1,iv:1nbZrIKozuS2p2AgD5/gHgjMN/VSd8SFeCbWdjy9Cf0=,tag:tfwaYihQDMAsmy/yt6ScLg==,type:str]",
"type": "ENC[AES256_GCM,data:ddkB7Iu6,iv:4t31C5r1zhCpLQ64idoJ8OBC7ocME15zCUXCmgf2ItY=,tag:RmBsmbhQW10oOTDuzfxEaA==,type:str]"
}
],
"anEmptyValue": "",
"sops": {
"kms": null,
"gcp_kms": null,
"azure_kv": null,
"lastmodified": "2019-12-10T22:45:55Z",
"mac": "ENC[AES256_GCM,data:VoXDgYpIYCvFSLyKGx4c8yk56Mk5GkeIwM8IyUi4RgkKBY/xEIzNUOuMqBzWEOvTTsivF/JtUOrBIsDRxGY0u0qNJK1R5WFuSYr3TA5sdu3ytMcu+mKY4THSJN8uuri/tXVcoF+ywLOS6NRFbHDJPtJGcy1XQwJJAgvdw+sIvQA=,iv:dvk3FYXHr2N6gYIw2OqbYiHn6FfXzuyHqZvJIo6IVGM=,tag:rjnNkJSGpXJ762EyjDltXg==,type:str]",
"pgp": [
{
"created_at": "2019-12-10T22:45:55Z",
"enc": "-----BEGIN PGP MESSAGE-----\n\nwcBMAyUpShfNkFB/AQgAMSeWf3F8kIm8EFiVgGVQgWGIHUVoolToi8d8lAC8/UdK\ncx9dIqlR43IFvvmCKyNZ6Q+/a1ERc07xLpVp3wmN80sE4NZCGZioThZjp2qNS42e\n/HtLfDu+Rie1eKcXEik40rMDn7d8gaFVOpD3FbzoZUFVm8hN5ChzqQqL1nLy9ZgY\nbthH3Rzt58Z1/sxARLNF2/yUqAEX/YEoL0MxM68Z55kwiMqSZ1rdmLKKfXdJbdoL\nSRrFyi+XaAwr1bTD+BnqHqgmYEEWEfHPDW7e1St/4IS4PU98kKuVLjhBKbfTRUpF\nkzxt+XQV6uDfPzdeOzf+JrFMRaoxTRMpcUi4Jn0vstLgAeSAjSzqkUr6DEVsuS8V\nAqRD4Z9I4HHgy+GYruDQ4kDtTvPgbeUWFma5yz25JOoORZIHaGiEB3T8ZhrFD8VI\ndFLxtxLtCeCg5BhRcrxFgWPQCMK/uCt/GFniNf4Y2OGOHQA=\n=r/lV\n-----END PGP MESSAGE-----",
"fp": "FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4"
},
{
"created_at": "2019-12-10T22:45:55Z",
"enc": "-----BEGIN PGP MESSAGE-----\n\nwYwDXFUltYFwV4MBBABs0ZEghsU+mdsRFROvlT8ACqk4Ru0Bssw3N+lkSrpR+QyY\nuRgAFNmZgPKz4DhZLHbhOD0zAAHuMCGriXVqtxMYMveX1nbXom+ayBmU0jja+6X2\njwk0MghHS1bgIsGrrhPoD7c3iirdaXSgHKAxwl4bpw5OxW6t91moOtJ4DAyGXdLg\nAeTkQxUyLFEeUbx4xX7kNCm+4Yho4CngDuH4oOD44gSWJsjgU+XiRBuJa50tiloC\nA10yQkWZtuyPOWppO5qOdXqV3XDIeOBl5JSNFuv5B80w8UIYhPPXWpriV1nBveE/\nwAA=\n=Vg44\n-----END PGP MESSAGE-----",
"fp": "D7229043384BCC60326C6FB9D8720D957C3D3074"
}
],
"unencrypted_suffix": "_unencrypted",
"version": "3.5.0"
}
}07070100000048000081A400000000000000000000000162794F93000007E9000000000000000000000000000000000000001700000000sops-3.7.3/example.txt{
"data": "ENC[AES256_GCM,data:p6kOd9e7KOYw47VlNlKa52wPFfbY3xaJYQrO5QDT1LyNvUIVBSRTrJxvn5MCC7vdnTOkcBzWmlr6Z/Q23/sx22++3Y7nXTSgFPQxPVIA8X33OoIsCamNHS8+8JWOReALCf2Cd3rzedu0GWR+/f2YBSHNA3C4nffEDbWbXRyAvcvCv3G4umH+Jh9auWUlfbk3Bx/8LvX6DodcxhQ=,iv:ESrDyOG6qetEWGBNHWRpT6ra1NhpaFH3SnjBSdMj2r0=,tag:aP5vOboB64cJDUls9WKsTA==,type:str]",
"sops": {
"kms": null,
"gcp_kms": null,
"azure_kv": null,
"lastmodified": "2019-12-10T22:44:49Z",
"mac": "ENC[AES256_GCM,data:wN+npCzfJVz6nwZQ40FTPD23Ly1CEiU1N6aDua+Mgj9cH7NwJOklW8QKTs3+q3f4HEkbeuFE6VQN+Jm05Zsj1inGjAdG2MfDurspJl6Jpe5DBKgk3zudAcc66gm4T4Dn3h7zFvNovOl+VEa4+ntaxIoVNugVDq3ZLTj/wMd3XwU=,iv:RadNg2jPeQEkE1F/GzrdcPIZHbxXoZpo+iOHpRGlLhc=,tag:ID8N4xhN7p3N5EYGTkYKxg==,type:str]",
"pgp": [
{
"created_at": "2019-12-10T22:44:49Z",
"enc": "-----BEGIN PGP MESSAGE-----\n\nwcBMAyUpShfNkFB/AQgANOTnicDHAmqwi76yIm2eAgzm32k34hsPS40vKeCKtbIP\niR91/hDmklYXgR9yL9xgBI0SRTMGySSk9YJ9daZd61JVh1IVuxr93Y8GSxhDldAn\n1Wc2dXJ24x7zxfUs4sfZYCtzXZBUb/eAPLDIkeKPzkVKN4kLdVdccOig/2lOuuVo\nw3Xy+m7cx0VPdsFFzVWok15oHj8n0+J8v6Vnyiyx7yI7xgsynNwpZDUN+K15NyGs\nkaO21AeQnxDWmwo4H93+r10esFYns0kyLOCNwN5/XLskT31f9MCo8H4bBDyeO1lE\nrfLKAn0mh81qKedQLTssjElCLBgY4CpcL9B688P/otLgAeSR+v/JrgslAw+QhiBC\nPxqj4ZUC4KbgFeERieC34sjLWuPgxOUoC769iqiM3ArscWLYG6jYb9Acigwtf5/r\nNkFoXHoZPOD15Ne/ElmCDPowh0aAFCwVp6/ipRc0teELTQA=\n=FyYT\n-----END PGP MESSAGE-----",
"fp": "FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4"
},
{
"created_at": "2019-12-10T22:44:49Z",
"enc": "-----BEGIN PGP MESSAGE-----\n\nwYwDXFUltYFwV4MBBACebuDSGC4caG1iSviC+IQKXw/mQXmWDDWLA1RDH7/ZEa/2\nqsA0Eb5bsd4Hf5pW6UpK/TDZpFn3eKeCn8wP2G792Ez19UmhwHB+0Zid9Zq76UAZ\n1bcwX2YJeCgd8/OgIxfh7a6MDDz6TDNGL916BIE6kFJwT3Vvm9EzF7vDglE5mtLg\nAeTK87HB76QLJNDI03Q8JYrN4e1S4KzgxOFhkeAv4lmhR+zgCeU3u0ripltx+Hys\ngtiGJSnuoJrYrwhSIO8JOoc2iR0bkuCn5Gf4VirHEUwrIUqgQVsmLQnick1iv+Hk\n3QA=\n=exjM\n-----END PGP MESSAGE-----",
"fp": "D7229043384BCC60326C6FB9D8720D957C3D3074"
}
],
"unencrypted_suffix": "_unencrypted",
"version": "3.5.0"
}
}07070100000049000081A400000000000000000000000162794F930000140F000000000000000000000000000000000000001800000000sops-3.7.3/example.yamlmyapp1: ENC[AES256_GCM,data:zlGNmhTYX5xol4ZZFsiaoGkD73nn,iv:ql9mkhoU1I64E/FJi3iA0HaAe2U3kQVFee2ZLwPnBik=,tag:SqVSfu/JkRrwqidAT/i0pg==,type:str]
app2:
db:
user: ENC[AES256_GCM,data:tQ1l,iv:o9MiMveNYO7T82yDab+4pAt17DO6B4wl8yGy3oFbDb8=,tag:0RjvUUtSQwaUc9SF3RSTZQ==,type:str]
password: ENC[AES256_GCM,data:8Ll+TDCgzQ==,iv:aao0OSVdFwaB9EGZ0O+Wn5bRJ6do7hgiQioqiwDi1w0=,tag:WKj0nuSSPkm2dBOBwinYvg==,type:str]
#ENC[AES256_GCM,data:uPuUQAahmq1xL5L2B0+a2gHSl7zXucjqa4Kr0AgNKTpgHw3IPx2lCoK+,iv:qYxjRmWMir47YKhmrrHwV0atmGjcJ3Dts+xvQ+6skHQ=,tag:dsj+0DsfiDyJNGUmvZwnHg==,type:comment]
key: ENC[AES256_GCM,data:xRjmLiX4BCoSUElToUs5twDq1WNWQNvNMi8yitXly43iGQiwltIs0FsY5u+7fzLepS66oLTGdL0NfWwCGxksYYzUKz5OXRLEatacZP40D71861zu+njmGdXepY0q0q5VOG7ObgIAMMMElVKRIFdjpVgmgUa+/h6R77mEbDztk6lqb28r15XyR6GmdubierTE7aialFzNoC+XO/yk7bnMi0XA/aomj1H5RdZ37LBR2k+rhqXmhkPdGTdJ39t4Ou1Q2Oc4RHRGvgs/EeFqQHcq0AilqXsFIv/PE0bQP564LaOcXK33B+PnoV1D+lXZ7mLOjKlq04c+UojwXjpZeXBr6Ip4H3dAGkPAoQKMtyGwHYzuLCNesxwPv2tPqbxkbVev0AKezGhnPjvCNvRN3S1Y0LPf1atfwzOCBQBhdUTpmXtxCdTNG0cUUZjeIKAyJXDWJooYHlstzDti/dSGMEedOnKq6648Yp7tLNDjAg5CGbDEWjTWehtqgvixUoRdYc2/r/ie3t09XB9h70BzLFDnbNdTNHhg0/aivHeDf8LJ2Co8YvIjLTwlm7GV0mSwzJIoY/2YxXFtw+XFqt+BCt1G8wq3R6OdXZK/+6fUKH/D4EVym5nrGkZIuOCiTB+wpzy09QmZ80Fo+ba0x/1g9ZTMKHk=,iv:ZtzvrO7QSHEOCnKCrIYcaesKnyScV8KaHZr22tUMLlU=,tag:2A3nJBIPF2Q3FlwMYLvG2w==,type:str]
number: ENC[AES256_GCM,data:DX0qiTOWhQvG/w==,iv:ouWsby8JoFwCRj/mLVCnNcYhP2sdyf4h6nwZuGksE7Q=,tag:lPU6AId2JrlquHnYRw+E8Q==,type:float]
an_array:
- ENC[AES256_GCM,data:vyczE8EQr9qHkaM=,iv:sT5jKk3LZ61Zq/neTli5tcnDFxCxY5RuGr2k5oGQWJQ=,tag:1HgaHWfyh6EJLkI1V2kOrw==,type:str]
- ENC[AES256_GCM,data:XtBinnYXR7bx1GY=,iv:KvT9smKVmgMNrab+RzfuWscyvJav2r8j1P08ucNmhgQ=,tag:IllAEvIPPeKOfqi1XbmT8w==,type:str]
- ENC[AES256_GCM,data:gpZ7nwWTGaI+Ti+lk+CPQOoM0ypwK7UMMBUiZAniQHDNJelipqc8hyhNeV+tpJLNaRt74OHs04EX8g==,iv:mKcwVelqLvwVDPjR8NeyMZ7AhsjRgmnYmyEuwPNPrQ8=,tag:vrkoccUfJs105yLCmXYYCw==,type:str]
- ENC[AES256_GCM,data:L9jPh+7+XsdqEpUnFcD4nA==,iv:xyfKjOXVrBDCIQG5786pSu5yvHdl/PK8eVxkIUWoCIw=,tag:Q0wlTV2e2vZeU6eTF5Oacg==,type:str]
somebooleans:
- ENC[AES256_GCM,data:ExiXxg==,iv:K7FUwomqdA7o9lzvNoAMH/wbXs08FextTGGeJKnaatU=,tag:A9UntgvPIcappmeM3jsbdA==,type:bool]
- ENC[AES256_GCM,data:3I0AVdM=,iv:q4YKnRIKufREPmwT4sz8plcsOD6iem/tY3NMUV0STBE=,tag:0w4OMKClWTzjKhqsJZT8JA==,type:bool]
this:
is:
a:
nested:
value: ENC[AES256_GCM,data:oFn5fJS5+slb2sCdLY5SxZ+iWeowWtf4wn9g,iv:MZ7i4tZnfCQhQRUwXV2fYQPIJ0tTUFLiD9xuB+765e8=,tag:ZEjE5jvxE5HkN3mma84pKw==,type:str]
#ENC[AES256_GCM,data:WwWiKtMsD1shPe5kPHOh2bJqQPGHwxa6GYrR1y14wiid,iv:AZPaRyVDOl100PvBPMeq0lt6/O5ZUhzWX5UmWNABWvM=,tag:PBReK6Ap/VHxncn0G4qtEA==,type:comment]
#ENC[AES256_GCM,data:eYRaxgs3vGeS96+ZDV8GYrwbvsrMtnWHOtsT2045tD2mlfOD,iv:/RVNEWuBlxhhY8OlJPbS/81QJukXZu1EWnPUQwrcin4=,tag:DybgrXKGWxoRyIQOlc+UMA==,type:comment]
#ENC[AES256_GCM,data:JXKEWGBg4eeCdeQ=,iv:K5keuEjyekf7a3q7WBOKwljsHGXRdQteJcXeeKvHo28=,tag:60VtIdsy13qSKPIEWHUUNg==,type:comment]
somelist_unencrypted:
- all elements of this list
- remain in clear text
- because of the _unencrypted suffix in the key
nested_unencrypted:
this:
is:
all: going to remain in clear text
sops:
kms: []
gcp_kms: []
azure_kv: []
lastmodified: '2019-12-10T22:45:53Z'
mac: ENC[AES256_GCM,data:WDjMv0eWcyPQzZlr3MppeAMQavN88xv5LzI/9wOlg+WPhRoTdrvgFpWowyWvTdUC/i0ybRQRg2u/Wam0kaqzMDpl/E806Gp9hgJcSneqydDJqPiMh+HpXkXWpc70xbYg8/gc1l7eIfSG7rS1dC2t2je60OAIfC/5zAXrL9KH4Ho=,iv:h0hWhb+46upix6K7hZfNNQoiX7WCapiMTv5I/keZsm4=,tag:v7mVbTN3HWmekol5iaO8FA==,type:str]
pgp:
- created_at: '2019-12-10T22:45:53Z'
enc: |-
-----BEGIN PGP MESSAGE-----
wcBMAyUpShfNkFB/AQgAE0MaWAQGbTKY7Xg3fDNtzlvnVBkkQRHsLt5kUTu2nAy4
sPX0NRXPVF/tAMxr9mI2fRjKnNBXKpOAecNis85D/QEkfflG8/syGkqiJqy9Nqon
WSm1bNriPfD4PL850688EJe49Xrsz6rVbW5FYZCHMbnPxvmoheMJRxLonW3/eWPy
IjJ9i6Z7W175mv1y7FELOimdQeelynp4r8bOuuq1BhePB4+wJXihw9n0ovuLklpl
Kr2iCmIUibSywlEO/LGQT/VXo6R7xgSN2Xg0RwWfflajYHNhVkHlnNYzkACjvdhj
ph1M5fLGDqPu+ySSe93EyahNhdwKgQ7R9yF8/13+QdLgAeRmrBgh0N54mQQAQyL7
mH/i4SWi4BjgmeHgUeBL4qcm2avg6OUasVJQHlTeA8D+c3TwKSTRVDijN4GBadYJ
Z5/vXKOseeBk5JWCnIHC/MtjOkuPt53nvGzi3lYvW+F0FQA=
=RDyn
-----END PGP MESSAGE-----
fp: FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
- created_at: '2019-12-10T22:45:53Z'
enc: |-
-----BEGIN PGP MESSAGE-----
wYwDXFUltYFwV4MBBABpm+tFhFhv3A7A/L/p6nL3HXKKhONrgguYgXA/hhSg4/bD
1Po5pQhCM4yb3gqWewxgVpGNKFr/Gl+kN9eZ3LXp5nEdhei/aQn7BbWkhph5PKt6
faiEZAL5PNHvktvEQwPsfNJvxe8QT2Z9oFmlueP0n3mCZz3UV9LZHNwOP7XfzdLg
AeRzUGrZK43KavmIjdgPXcd/4ZW14NPgNeGm8+BO4nD26Fvgh+UiCb0TpBiI7WsX
HQGUlFjuR6Vd7Q+vg8B/1Ovm3fUw7uCG5Gc2WeB0M+pXaLYS9bCDL9jisGOkD+Ef
ZAA=
=mqGc
-----END PGP MESSAGE-----
fp: D7229043384BCC60326C6FB9D8720D957C3D3074
unencrypted_suffix: _unencrypted
version: 3.5.0
0707010000004A000041ED00000000000000000000000462794F9300000000000000000000000000000000000000000000001400000000sops-3.7.3/examples0707010000004B000041ED00000000000000000000000462794F9300000000000000000000000000000000000000000000001F00000000sops-3.7.3/examples/all_in_one0707010000004C000081A400000000000000000000000162794F9300000013000000000000000000000000000000000000002A00000000sops-3.7.3/examples/all_in_one/.gitignoreconfig/secret.json
0707010000004D000081A400000000000000000000000162794F9300000C7D000000000000000000000000000000000000002A00000000sops-3.7.3/examples/all_in_one/README.rstAll-in-one example
==================
This directory is an example configuration for SOPS inside of a project. We will cover the files used and relevant scripts for developers.
This example is optimized for saving developer time by storing all secrets in a single file (e.g. ``secret.enc.json``).
One downside is any configurations which should be stored side by side might not be.
Getting started
---------------
To use this example, run the following:
.. code:: bash
# From the `sops` root directory
# Import the test key
gpg --import tests/sops_functional_tests_key.asc
# Navigate to our example directory
cd examples/all_in_one
# Decrypt our secrets
bin/decrypt-config.sh
# Optionally edit a secret
# bin/edit-secret.sh config/secret.enc.json
# Run a script that uses our decrypted secrets
python main.py
Storage
-------
In both development and production, we will be storing the secrets file unencrypted on disk. This is for a few reasons:
- Can't store file in an encrypted manner because we would need to know the secret to decode it
- Loading it into memory at boot is impractical
- Requires reimplementing SOPS' decryption logic to multiple languages which increases chance of human error which is bad for security
- If someone uses an automatic process reloader during development, then it could get expensive with AWS
- We could cache the results from AWS but those secrets would wind up being stored on disk
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.
Files
-----
- ``bin/decrypt-config.sh`` - Script to decrypt secret file
- ``bin/edit-config-file.sh`` - Script to edit a secret file and then decrypt it
- ``config/secret.enc.json`` - Catch-all file containing our secrets
- ``config/secret.json`` - Decrypted catch-all secrets file
- ``config/static.py`` - Configuration file which imports secrets
- ``.gitignore`` - Ignore file for decrypted secret file
- ``main.py`` - Example script
Usage
-----
Development
~~~~~~~~~~~
For development, each developer must have access to the PGP/KMS keys. This means:
- If we are using PGP, then each developer must have the private key installed on their local machine
- If we are using KMS, then each developer must have AWS access to the appropriate key
Testing
~~~~~~~
For testing in a public CI, we can copy ``secret.enc.json`` to ``secret.json``. This will represent the same structure as ``secret.enc.json`` with an additional ``sops`` key but not reveal any secret information.
..
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>`_.
Production
~~~~~~~~~~
For production, we have a few options:
- Build an archive (e.g. ``.tar.gz``) in a private CI which contains the secrets and deploy our service via the archive
- Install PGP private key/KMS credentials on production machine, decrypt secrets during deployment process on production machine
0707010000004E000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002300000000sops-3.7.3/examples/all_in_one/bin0707010000004F000081ED00000000000000000000000162794F9300000261000000000000000000000000000000000000003500000000sops-3.7.3/examples/all_in_one/bin/decrypt-config.sh#!/usr/bin/env bash
# Exit on first error
set -e
# Define our secret files
secret_files="secret.enc.json"
# For each of our files in our encrypted config
for file in $secret_files; do
# Determine src and target for our file
src_file="config/$file"
target_file="$(echo "config/$file" | sed -E "s/.enc.json/.json/")"
# If we only want to copy, then perform a copy
# DEV: We allow `CONFIG_COPY_ONLY` to handle tests in Travis CI
if test "$CONFIG_COPY_ONLY" = "TRUE"; then
cp "$src_file" "$target_file"
# Otherwise, decrypt it
else
sops --decrypt "$src_file" > "$target_file"
fi
done
07070100000050000081ED00000000000000000000000162794F930000033F000000000000000000000000000000000000003700000000sops-3.7.3/examples/all_in_one/bin/edit-config-file.sh#!/usr/bin/env bash
# Exit on first error
set -e
# Define our secret files
secret_files="secret.enc.json"
# Look up our file
filepath="$1"
if test "$filepath" = ""; then
echo "Expected \`filepath\` but received nothing" 1>&2
echo "Usage: $0 <filepath>" 1>&2
exit 1
fi
# If our file is a secret
filename="$(basename "$filepath")"
if echo "$secret_files" | grep "$filename"; then
# Load it into SOPS and run our sync script
sops "$filepath"
bin/decrypt-config.sh
# Otherwise (it's a normal file)
else
# Resolve our editor via `sops` logic
editor="$EDITOR"
if test "$editor" = ""; then
editor="$(which vim nano | head -n 1)"
fi
if test "$editor" = ""; then
echo "Expected \`EDITOR\` environment variable to be defined but it was not" 1>&2
exit 1
fi
# Edit our file
"$editor" "$filepath"
fi
07070100000051000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002600000000sops-3.7.3/examples/all_in_one/config07070100000052000081A400000000000000000000000162794F9300000000000000000000000000000000000000000000003200000000sops-3.7.3/examples/all_in_one/config/__init__.py07070100000053000081A400000000000000000000000162794F930000073B000000000000000000000000000000000000003600000000sops-3.7.3/examples/all_in_one/config/secret.enc.json{
"github_oauth_token": "ENC[AES256_GCM,data:lVe1fLoILE8e/jiah90=,iv:6g350p9Id8+ssrV5lVEF4TWEnR+JfY84iUsh92/9XIQ=,tag:2qojKddpx5lO1iR2dqLZJg==,type:str]",
"sops": {
"kms": null,
"gcp_kms": null,
"azure_kv": null,
"lastmodified": "2019-12-10T22:46:40Z",
"mac": "ENC[AES256_GCM,data:XUVZm8UI26QEbhlIkQDfEWcyhooh/bGQ2eHOTu/ojbE4SBLRpcLtKHt2Bn8Z/72pLV77FmmSXAIwy8+xvzeTmEAZNRMfjrm0ZulBY1VgJLTrvA4Hh9F7H+Pb95xtc8B9SbFAo65L+xBJJ/e6t8QsV0N4crOoq0Su3OuvqXIVa90=,iv:1Vnq304ic+vPZ0as0xLpaO8dMduMrF3Xh9aAHT+VLiA=,tag:37+ami7Z3tQd1EwZKC7sPg==,type:str]",
"pgp": [
{
"created_at": "2019-12-10T22:46:40Z",
"enc": "-----BEGIN PGP MESSAGE-----\n\nwcBMAyUpShfNkFB/AQgATTwZYaZdIVFnJrzujQvk5x/SqVc5VY367UtStph9pxn5\nkSh2lvPrYHtkbpPkvv5Ug5CUwUbofq4rj3YSO3kVSDu5Z3IrAT+EtnK5PECg2JDT\nIXskkjFegQYp4YtC5UUhjIVIALti/9b3g2skVES/IHHlL+BE9AWj93yF24weu54D\nbwF6PQyGRE7HhZ8uUWEKIGTbJbpDzySk06LJSF33uNbKUViRtHuLu4JNFb22sjzy\nMKzITct/4KVlHAkC4ZHpDlsXmtooBJ8Yk7DK2vEM1POk2pE6ZBQbEgtY0hAOMFGB\nfg+j6cvNsLYWyQrjaB9Vsf4+QPDeTWuv5+cJjvgildLgAeQssqFSnZXumtz/VgB1\nm7Ma4Rov4DLgAeHkV+DJ4vTaGavgHuVj+a53Oa9HuX4p1rNV3ZLUyJi6HmNOWjvw\np8QFzrcRyOCN5N3VkrUyEK9l03NczoI9nt7i6r8jBeHWTQA=\n=Uxc1\n-----END PGP MESSAGE-----",
"fp": "FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4"
},
{
"created_at": "2019-12-10T22:46:40Z",
"enc": "-----BEGIN PGP MESSAGE-----\n\nwYwDXFUltYFwV4MBBAA5g2pnnh43zql0b1bOa6zw0S6uMSLioFy5Or4Z+aEhywpe\n94unRNqABQdqCtowoUpjJ4vEHxQ+dkHPXpYJejolBKPaKpHJHLrdImkC9nb62jJ8\nthWFitj8ELQnHdmSk+t5kvaNj4nWXSuTTiXd2UodXZcHxH4UI4P7A4aZXKTYytLg\nAeRHhnSBOBYD0rMvFIWKLcR94SZE4LrgluHLYODT4l6Ln4fg5uX2CzKsP/KD4+jI\nPDvITr4Sdnh6m6XEctb4whn0OOKXo+Ca5OJ75/+Kb9SzSbA24uXJ1qriELpTt+Hk\nawA=\n=SIw4\n-----END PGP MESSAGE-----",
"fp": "D7229043384BCC60326C6FB9D8720D957C3D3074"
}
],
"unencrypted_suffix": "_unencrypted",
"version": "3.5.0"
}
}07070100000054000081A400000000000000000000000162794F9300000104000000000000000000000000000000000000003000000000sops-3.7.3/examples/all_in_one/config/static.py# Load in our dependencies
import json
# Load in our secrets
with open('config/secret.json', 'r') as file:
secret = json.loads(file.read())
# Define our configuration
common = {
'github_oauth_token': secret['github_oauth_token'],
'port': 8080,
}
07070100000055000081A400000000000000000000000162794F93000001CA000000000000000000000000000000000000002700000000sops-3.7.3/examples/all_in_one/main.py# Load in our dependencies
from __future__ import absolute_import
from config.static import common
# Define our main function
def main():
# Output our configuration
print('Configuration')
print('-------------')
for key in common:
# Example: `port: "8080"`
print('{key}: "{val}"'.format(key=key, val=common[key]))
# If this script is being invoked directly, then run our main function
if __name__ == '__main__':
main()
07070100000056000041ED00000000000000000000000462794F9300000000000000000000000000000000000000000000001D00000000sops-3.7.3/examples/per_file07070100000057000081A400000000000000000000000162794F9300000012000000000000000000000000000000000000002800000000sops-3.7.3/examples/per_file/.gitignoreconfig
config.bak
07070100000058000081A400000000000000000000000162794F9300000D83000000000000000000000000000000000000002800000000sops-3.7.3/examples/per_file/README.rstPer-file example
================
This directory is an example configuration for SOPS inside of a project. We will cover the files used and relevant scripts for developers.
This example is optimized for storing sensitive information next to related non-sensitive information (e.g. password next to username).
The downsides include:
- Slowing down developers by requiring usage of SOPS for non-sensitive information
- Losing dynamic configurations that rely on reusing variables (e.g. ``test = {'foo': {'bar': common['foo']['bar'], 'baz': false}}``)
- There might be work arounds via YAML
Getting started
---------------
To use this example, run the following
.. code:: bash
# From the `sops` root directory
# Import the test key
gpg --import pgp/sops_functional_tests_key.asc
# Navigate to our example directory
cd examples/per_file
# Decrypt our secrets
bin/decrypt-config.sh
# Optionally edit a secret
# bin/edit-secret.sh config.enc/static_github.json
# Run our script
python main.py
Storage
-------
In both development and production, we will be storing the secrets file unencrypted on disk. This is for a few reasons:
- Can't store file in an encrypted manner because we would need to know the secret to decode it
- Loading it into memory at boot is impractical
- Requires reimplementing SOPS' decryption logic to multiple languages which increases chance of human error which is bad for security
- If someone uses an automatic process reloader during development, then it could get expensive with AWS
- We could cache the results from AWS but those secrets would wind up being stored on disk
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.
Files
-----
- ``bin/decrypt-config.sh`` - Script to decrypt secret file
- ``bin/edit-config-file.sh`` - Script to edit a secret file and then decrypt it
- ``config`` - Directory containing decrypted secrets
- ``config.bak`` - Backup of ``config`` to prevent accidental data loss
- ``config.enc`` - Directory containing encrypted secrets
- ``static.py`` - Python script to merge together secrets
- ``static_github.json`` - File containing secrets
- ``.gitignore`` - Ignore file for ``config`` and ``config.bak``
- ``main.py`` - Example script
Usage
-----
Development
~~~~~~~~~~~
For development, each developer must have access to the PGP/KMS keys. This means:
- If we are using PGP, then each developer must have the private key installed on their local machine
- If we are using KMS, then each developer must have AWS access to the appropriate key
Testing
~~~~~~~
For testing in a public CI, we can copy ``config.enc`` to ``config``. The secret files will have structure with an additional ``sops`` key but not reveal any secret information.
..
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>`_.
Production
~~~~~~~~~~
For production, we have a few options:
- Build an archive (e.g. ``.tar.gz``) in a private CI which contains the secrets and deploy our service via the archive
- Install PGP private key/KMS credentials on production machine, decrypt secrets during deployment process on production machine
07070100000059000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002100000000sops-3.7.3/examples/per_file/bin0707010000005A000081ED00000000000000000000000162794F930000032A000000000000000000000000000000000000003300000000sops-3.7.3/examples/per_file/bin/decrypt-config.sh#!/usr/bin/env bash
# Exit on first error
set -e
# Define our secret extenssion
secret_ext=".json"
# If there is a config directory, then move it to a backup
if test -d config; then
if test -d config.bak; then
rm -r config.bak
fi
mv config/ config.bak/
fi
# Create our new config directory
mkdir config
# For each of our files in our encrypted config
for src_file in config.enc/*; do
# Determine target for our file
src_filename="$(basename "$src_file")"
target_file="config/$src_filename"
# 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"
# Otherwise, symlink to the original file
else
ln -s "../$src_file" "$target_file"
fi
done
0707010000005B000081ED00000000000000000000000162794F9300000139000000000000000000000000000000000000003500000000sops-3.7.3/examples/per_file/bin/edit-config-file.sh#!/usr/bin/env bash
# Exit on first error
set -e
# Localize our filepath
filepath="$1"
if test "$filepath" = ""; then
echo "Expected \`filepath\` but received nothing" 1>&2
echo "Usage: $0 <filepath>" 1>&2
exit 1
fi
# Load our file into SOPS and run our sync script
sops "$filepath"
bin/decrypt-config.sh
0707010000005C000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002800000000sops-3.7.3/examples/per_file/config.enc0707010000005D000081A400000000000000000000000162794F9300000000000000000000000000000000000000000000003400000000sops-3.7.3/examples/per_file/config.enc/__init__.py0707010000005E000081A400000000000000000000000162794F93000007DF000000000000000000000000000000000000003200000000sops-3.7.3/examples/per_file/config.enc/static.py# Load in our dependencies
import json
# Define our configuration
# DEV: THE FOLLOWING CONFIGURATIONS SHOULD NOT CONTAIN ANY SECRETS
# THIS FILE IS NOT ENCRYPTED!!
common = {
'port': 8080,
}
development = {
}
test = {
}
production = {
}
config = {
'common': common,
'development': development,
'test': test,
'production': production,
}
def walk(item, fn):
"""Traverse dicts and lists to update keys via `fn`"""
# If we are looking at a dict, then traverse each of its branches
if isinstance(item, dict):
for key in item:
# Walk our value
walk(item[key], fn)
# If we are changing our key, then update it
new_key = fn(key)
if new_key != key:
item[new_key] = item[key]
del item[key]
# Otherwise, if we are looking at a list, walk each of its items
elif isinstance(item, list):
for val in item:
walk(val, fn)
# Merge all of our static secrets onto our config
# For each of our secrets
secret_files = [
'config/static_github.json',
]
for secret_file in secret_files:
with open(secret_file, 'r') as file:
# Load and parse our JSON
data = json.loads(file.read())
# Strip off `_unencrypted` from all keys
walk(data, lambda key: key.replace('_unencrypted', ''))
# For each of the environments
for env_key in data:
# Load in the respective source and target
env_src = data[env_key]
env_target = config[env_key]
# Merge info between configs
for key in env_src:
if key in env_target:
raise AssertionError(
'Expected "{env_key}.{key}" to not be defined already '
'but it was. '
'Please verify no configs are using the same key'
.format(env_key=env_key, key=key))
env_target.update(env_src)
0707010000005F000081A400000000000000000000000162794F930000093F000000000000000000000000000000000000003B00000000sops-3.7.3/examples/per_file/config.enc/static_github.json{
"common": {
"github_username_unencrypted": "twolfson-dev",
"github_password": "ENC[AES256_GCM,data:wtyRyrcYh1AaoA==,iv:lJKpAdaRjC4jAl+wIT6ozSZanSIEJwEyJkLi0aFHlGg=,tag:NrrARDWsoWqOyQ+oyALHAA==,type:str]"
},
"development": {},
"test": {
"github_username_unencrypted": "test-user",
"github_password": "ENC[AES256_GCM,data:To6eP3uCm+IQo6KCBalF2Q+7zc4=,iv:GZz08Kz9fjCsIk1edHVZRYgSzfF9smzGbllOU6op+z8=,tag:Zrp5RNck7DXCVffX/zmWIQ==,type:str]"
},
"production": {
"github_username_unencrypted": "twolfson",
"github_password": "ENC[AES256_GCM,data:AZ+Yef+hheVBQWtQ6Ys=,iv:RMd4iwMCiNkt2zPyLBakdA9+HaJA9hh2qUbbdNcemLo=,tag:ejPV/E3dwubZl6s1NyZnSA==,type:str]"
},
"sops": {
"kms": null,
"gcp_kms": null,
"azure_kv": null,
"lastmodified": "2019-12-10T22:47:04Z",
"mac": "ENC[AES256_GCM,data:rBKvMg8FroPuKB03JEzRvlnONsca7ByBqoTB18kjXHwr7mmnBYLQP4mWiYPRRRaBDaGtDXx3FH+Igyo84vPwNO2eEN94gv1NpeGqPI1vRUwAkCB3UE3hIQN22zXBEwWO4sUPKzERuG8t5dFF7xO4EyzweZlC+nwtyQUQX78ysGo=,iv:vymJO0wSb+/W4LUiZi9+ov9YVy3la+BopXiqd2X/mbA=,tag:Pm7NolfYB7ELKjHaGdbauQ==,type:str]",
"pgp": [
{
"created_at": "2019-12-10T22:47:04Z",
"enc": "-----BEGIN PGP MESSAGE-----\n\nwcBMAyUpShfNkFB/AQgAJAeWCsS3nqQ+uDy5gj7Fm7evXn/gY5DW7AWJjkXmHqwx\n+IHVERj9JsfFApoUCPu4Om/KB67uegLWk/GFmAtHcwV2GrsGt1AAIURdNSwYf8P7\nLix9fpRwAUlamXS0wDbGQQyNX3X1rIMu7PnL3KLzvfBQUHaVs3+4GpNXjzUVRGgb\nmjtCTsH3u1VYiHrRz0uoWisPDlvn60fPv0mcYdu17sVpgPnC0qmHh6UJd85dGzfy\nSOisauKYkirOfLSJyVCItFD0GwPVd3mbEcxmIokHmaIznck/Tto3sXcQO4Amc54+\nCjXhk0f/KJbKQQg4mbZMaEMISE+/s3H4mBmk2VADJNLgAeS5csP9GjYrFOAHsgGR\nrDIC4SMd4DTgmuEYSOCh4qLAvJfgmOXA0EoByddCKyrCIHDdyq+lFOQq1Hk+kfl/\nruUZSgCy9OCo5FerieGdW/fWfWsKx2CGKczi0Q/ivuFb2QA=\n=rw7+\n-----END PGP MESSAGE-----",
"fp": "FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4"
},
{
"created_at": "2019-12-10T22:47:04Z",
"enc": "-----BEGIN PGP MESSAGE-----\n\nwYwDXFUltYFwV4MBBACeF7HYThUNm0osc9SEASAnYLB0/qp6IdA6dc0aNHBZxM3Z\nxiOmbGOfImgwWMDGFP3GYFe3NNt/imzy0V+umVWMYxLYb9Hm3yCdVSb6ks9xJwkp\nXWD7vLXXEuVRsLp+IsmMXSRRJ/shBLIORiOMIAiSv92yluNT8PZF3Nv1hA56Y9Lg\nAeR2GEqpdbw6/tIUPMJUHWQf4XZ14DHgkOF9beAN4qqnf1zgsuVA+YmakiS7L5iY\nA/4OMVnCDJBKTD+l91ID5d4k2bQoU+Dm5AmJqm+I6sX6nViQUgyEyjvi+aeOuuG0\nDQA=\n=0KIM\n-----END PGP MESSAGE-----",
"fp": "D7229043384BCC60326C6FB9D8720D957C3D3074"
}
],
"unencrypted_suffix": "_unencrypted",
"version": "3.5.0"
}
}07070100000060000081A400000000000000000000000162794F9300000377000000000000000000000000000000000000002500000000sops-3.7.3/examples/per_file/main.py# Load in our dependencies
from __future__ import absolute_import
from config.static import config
# Define our main function
def main():
# Output our configuration
# DEV: We use custom keys for a custom sort
print('Configuration')
print('=============')
for env_key in ('common', 'development', 'test', 'production'):
# Example: `Environment: common`
# Example: `-------------------`
env_str = 'Environment: {env_key}'.format(env_key=env_key)
print(env_str)
print(''.join(['-' for char in env_str]))
env_config = config[env_key]
for key in sorted(env_config.keys()):
# Example: `port: "8080"`
print('{key}: "{val}"'.format(key=key, val=env_config[key]))
print('')
# If this script is being invoked directly, then run our main function
if __name__ == '__main__':
main()
07070100000061000041ED00000000000000000000000562794F9300000000000000000000000000000000000000000000001C00000000sops-3.7.3/functional-tests07070100000062000081A400000000000000000000000162794F9300000401000000000000000000000000000000000000002700000000sops-3.7.3/functional-tests/.sops.yamlcreation_rules:
- path_regex: test_roundtrip_keygroups.yaml
key_groups:
- pgp:
- FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
- pgp:
- D7229043384BCC60326C6FB9D8720D957C3D3074
- path_regex: test_roundtrip_keygroups_missing_decryption_key.yaml
key_groups:
- pgp:
- FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
- pgp:
- B611A2F9F11D0FF82568805119F9B5DAEA91FF86
- pgp: FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
destination_rules:
- s3_bucket: "sops-publish-functional-tests"
s3_prefix: "functional-test/"
path_regex: test_encrypt_publish_s3.json
reencryption_rule:
pgp: B611A2F9F11D0FF82568805119F9B5DAEA91FF86
- vault_path: "functional-test/"
vault_kv_mount_name: "secret/"
vault_kv_version: 2
path_regex: test_encrypt_publish_vault.json
- vault_path: "functional-test-version-1/"
vault_kv_mount_name: "kv/"
vault_kv_version: 1
path_regex: test_encrypt_publish_vault_version_1.json
07070100000063000081A400000000000000000000000162794F93000000EE000000000000000000000000000000000000002700000000sops-3.7.3/functional-tests/Cargo.toml[package]
name = "functional-tests"
version = "0.1.0"
authors = ["Adrian Utrilla <adrianutrilla@gmail.com>"]
[dependencies]
tempdir = "0.3.5"
serde = "1.0"
serde_json = "0.8"
serde_yaml = "0.5"
serde_derive = "1.0"
lazy_static = "0.1.*"
07070100000064000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002000000000sops-3.7.3/functional-tests/bin07070100000065000081A400000000000000000000000162794F9300000000000000000000000000000000000000000000002A00000000sops-3.7.3/functional-tests/bin/editor.rs07070100000066000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002000000000sops-3.7.3/functional-tests/res07070100000067000081A400000000000000000000000162794F930000070B000000000000000000000000000000000000003200000000sops-3.7.3/functional-tests/res/comments.enc.yaml#ENC[AES256_GCM,data:IYA+b4ORDq8u9CBQolipWD4HRqoZyA==,iv:F8ldQqGng+WptHuBkFtjrGM+7sRZCsvd0FHq98lrpAE=,tag:ZHbLU9+CELinf5PhhuIzSQ==,type:comment]
lorem: ENC[AES256_GCM,data:PhmSdTs=,iv:J5ugEWq6RfyNx+5zDXvcTdoQ18YYZkqesDED7LNzou4=,tag:0Qrom6J6aUnZMZzGz5XCxw==,type:str]
#ENC[AES256_GCM,data:HiHCasVRzWUiFxKb3X/AcEeM,iv:bmNg+T91dqGk/CEtVH+FDC53osDCEPmWmJKpLyAU5OM=,tag:bTLDYxQSAfYDCBYccoUokQ==,type:comment]
dolor: ENC[AES256_GCM,data:IgvT,iv:wtPNYbDTARFE810PH6ldOLzCDcAjkB/dzPsZjpgHcko=,tag:zwE8P+AwO1hrHkgF6pTbZw==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age: []
lastmodified: '2020-10-07T15:49:13Z'
mac: ENC[AES256_GCM,data:2dhyKdHYSynjXPwYrn9356wA7vRKw+T5qwBenI2vZrgthpQBOCQG4M6f7eeH3VLTxB4mN4CAchb25dsNRoGr6A38VruaSSAhPco3Rh4AlvKSvXuhgRnzZvNxE/bnHX1D4K5cdTb4FsJg/Ue1l7UcWrlrv1s3H3SwLHP/nf+suD0=,iv:6xBYURjjaQzlUOKOrs2NWOChiNFZVAGPJZQZ59MwX3o=,tag:uXD5VYme+c8eHcCc5TD2YA==,type:str]
pgp:
- created_at: '2019-08-29T21:52:32Z'
enc: |
-----BEGIN PGP MESSAGE-----
hQEMAyUpShfNkFB/AQgAlvpTj0NYqF4mQyIeM7wX2SHLb4U07/flpqDpp2W/30Pz
AHA7sYrgP0l8BrjT2kwtgCN0cdfoIHJudezrNjANp2P5TbP2b9kYYNxpehzB9PFj
FixnCS7Zp8WIt1yXr1TX+ANZoXLopVcRbMaQ5OdH7CN1pNQtMR+R3FR3X/IqKxiU
Do1YLaooRJICUC8LJw2Tb4K+lYnTSqd/HalLGym++ivFvdDB1Ya1GhT1FswXidXK
IRjsOVbxV0q5VeNOR0zxsheOvuHyCje16c7NXJtATJVWtTFABJB8u7CY5HhZSgq+
rXJHyLHqVLzJ8E4WqHQkMNUlVcrqAz7glZ6xbAhfI9JeAYk5SuBOQOQ4yvASqH4K
b0N3+/abluBY7YPqKuRZBiEtmcYlZ+zIHuOTP1rD/7L5VY8CwE5U8SFlEqwM7nQJ
6/vtl6qngOFjwt34WrhZzUfLPB/wRV/m1Qv2kr0RNA==
=Ykiw
-----END PGP MESSAGE-----
fp: FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
unencrypted_suffix: _unencrypted
version: 3.6.1
07070100000068000081A400000000000000000000000162794F9300000043000000000000000000000000000000000000002E00000000sops-3.7.3/functional-tests/res/comments.yaml# first comment in file
lorem: ipsum
# this-is-a-comment
dolor: sit07070100000069000081A400000000000000000000000162794F9300000025000000000000000000000000000000000000003300000000sops-3.7.3/functional-tests/res/comments_list.yamllorem:
- foo
#this-is-a-comment
- bar0707010000006A000081A400000000000000000000000162794F9300000605000000000000000000000000000000000000004300000000sops-3.7.3/functional-tests/res/comments_unencrypted_comments.yaml# first comment in file
lorem: ENC[AES256_GCM,data:qVz4paM=,iv:0oGsaw71i3wZKmlyDl8uDhQT9XLvJt3oIyx514X44K8=,tag:acbMS613StWo1IVnKK+5uQ==,type:str]
# this-is-a-comment
dolor: ENC[AES256_GCM,data:21fI,iv:01LXdHZYwLTeyUB1YWIAM6KF8cPPVsw/RuQO+Ab4pgM=,tag:o1xnCIIoccWzdWxB2kZYKg==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
lastmodified: '2019-08-29T22:42:03Z'
mac: ENC[AES256_GCM,data:xKkcsqrAHxyqwgv+IVqx52AmrJdC607Dc/Ughna2e2UnnHteXTw7LGt4d0sSlw8LgjaXpa+T6lQ0MgnMPjgTEa20lbtVtauCDdRCnR7Z/Vdk7t7uLl94+STD7C1H6obnOe4fG6c2cUfDNHoeABLetti2ZBZOSZDkWQeCucLys/s=,iv:k9ODJLNYsBedQKMHcgn0KUXPlunOq5jDFH9BeJOyYRE=,tag:d+Ga71V+gijiWLFZ7BhQgg==,type:str]
pgp:
- created_at: '2019-08-29T22:42:03Z'
enc: |-
-----BEGIN PGP MESSAGE-----
wcBMAyUpShfNkFB/AQgAMgUtep3vurVXoOI1h4Ovr3YbYz+gRHAPGMAUdXQdX7az
fmh7Eq+6Eye2wpnAaogJ2RtIoYO1F/jkoQO74mgaLXNu/gtr8b6Ejc61sQSyjnjg
N9I50+Bh075TqzToZTo7gOwOlltMjA/UVGD0z+gPHP1MpcVpUvm7C0Ol5L0Co37O
UvLrQJjw1x6ktAawWokw910iX9usXiTj87fYvaqutNKRBfh1LI0Os2H8C9xpJu7q
I60NcqF7JQbAaaumRroQpF2K7RI1nt+qTshPqWzDLfjzlrCVPnJlxludqvHUKzR3
TnJdb4Dsx3o/XEQkZxpl7RZa9SZZuKg3EejTxOj0ZtLgAeRLzLl0gSeMGvkntASq
NQkM4e1p4AjgJOEqrOCw4h0xtdrg9+UczwVpI4rnibRqJZtEhFHRaupX6leAEi1l
FMikSjpGSOB55PVMt6+e4ruokoulnq32VRTi07691uEeowA=
=ft04
-----END PGP MESSAGE-----
fp: FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
unencrypted_suffix: _unencrypted
version: 3.3.1
0707010000006B000081A400000000000000000000000162794F93000007B5000000000000000000000000000000000000003300000000sops-3.7.3/functional-tests/res/multiple_keys.yamlmessage: ENC[AES256_GCM,data:4soxy1Q=,iv:GGOFFLu8aqMEyoOBdMGdevug7E0R8H0iDyXIKQ5ufzM=,tag:954eipkbSu2U64tx2usiBA==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
lastmodified: '2019-08-29T22:22:04Z'
mac: ENC[AES256_GCM,data:Rcyh6DKgpd8WNvLqnPhMRYDGztK/WjNJ+SisCD4qL50wxiYUgwRGs5S9I8kOFm4+8pruOcQyCVIDzyjQHZfM1bKs+CnMJTrp1HDz2vhIKkE8O6EdE5+JbNxwc6ZFQE6YThmx6rz93gMHnDOcrO0XG2guGxTumM5xPkZsEBfpIyQ=,iv:YI0Yc2khjy+q1BxiEBDejgymyAQmBkdyl3fQUm76/1Y=,tag:pf4ZbMy9TrpTA/B2qVEkmw==,type:str]
pgp:
- created_at: '2019-08-29T22:22:04Z'
enc: |-
-----BEGIN PGP MESSAGE-----
wcBMAyUpShfNkFB/AQgAQH8KFrXipAGaHsKjQecpZ4R5PsWg4nqetgsy6FTcAI3j
KwvBoXapI+2m5aXjAdO4k76ZxGyyXXdwe6ebbuikxCuDl28m86o9xNxbBv5SxbaO
gTxtUR+swcnYRyfVmkd07HhATXQw7b2q4ERNJAVH9zxJcmzNzP6q9eRjePRWXSjn
xxzpnS9kQkevuzJXg5dhVZ9HnsuVEthK88c9H3ZZQt/kg3GLaj2XDhFUt9Xy3POl
3Q2dE8xzVXzg5rF+0n8H0WpiRJ7hbvW6kvAanKNUDeNLY2shmkKiIzh4W0f2Vl5Q
W1nw23Tga6IQ6mSqLItZB5bYX/C9K33kceV1DYyW+tLgAeTofEdQuueh2myAYmmV
Wjap4eAq4F/gi+EpuOAS4jDaEhPgY+UkojNLl9ow2EkyrIMMJCKDSzuZcmZhwTYl
7/RSqGHDHuDX5GvXVgnRiqnoDiPsImCDN5/iNmoSVuG9cgA=
=wVy4
-----END PGP MESSAGE-----
fp: FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
- created_at: '2019-08-29T22:22:04Z'
enc: |
-----BEGIN PGP MESSAGE-----
hIwDXFUltYFwV4MBA/9/qqKDEbT9oZJqB7Z7R1RAz+TStVUzlE9+M0ScFuguBUpd
7doLhhgBYa9ygti5pnAN4E1SI97xPhflGZLTp4OtdTg5UGOUwh6BM0BZI1ai6dlK
u4ra0BKOmDzv9SkbTngStXfPPLC9msc6sZe9TER88puqNAZ4DBrTyTtEAmg5wtJe
AecV7hxDzAGleAdmaTC+WHkvg/EgwY/9PaU1IMf6tCx8O/bS8JQhxmptO1aAm5Vr
dO/SV2m9QStMafO6OG1aGpy3zkVytlxGavkUK+dPp5fYbRYGQVfUiGpgRRZEZg==
=NlDf
-----END PGP MESSAGE-----
fp: D7229043384BCC60326C6FB9D8720D957C3D3074
unencrypted_suffix: _unencrypted
version: 3.3.1
0707010000006C000081A400000000000000000000000162794F9300000456000000000000000000000000000000000000002C00000000sops-3.7.3/functional-tests/res/no_mac.yamlmyapp1: ENC[AES256_GCM,data:NjYyOQ1GyFUhy25XX7cz7CZo4tLt,iv:WeWmnZCG7/pm71VMd37ua3LsZ6NnqQzLAyN8ylfYHb8=,tag:zh2hOpxHZ8ON+sJgB+OnTg==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
lastmodified: '2019-08-29T22:23:01Z'
pgp:
- created_at: '2019-08-29T22:23:00Z'
enc: |-
-----BEGIN PGP MESSAGE-----
wcBMAyUpShfNkFB/AQgABuzcPMJSK+DvnPKZVyYlmDLCM9BobLz/7/F8zyX4O00T
UN7/0Lc+MwZWUOe7lmTVgorDLcfxLONbLg9nxXO+apbb2gRABRYdggosMFaRUn3+
m+maKQ4yDTIjIvblVa/olN8reD4Bt037mT52IxTZBLDAnyVw45dRTe9mzJ73eTi7
1Mk5s6s1ZH52SQHtbY4TSqCnOKodn3UkTjnFmDXMSAzCwBDmK/wv+fDufXSkLpeR
AGufxo697as2bQ8mpqgPPTvB04MgXwnTngUQ8BfagyOndgTCrU9YMA1gewJnJJj4
azRP9SOukqtn0c/LpewiOHHk/w00dMLEk0E8DpxMItLgAeQQGaX95PJfl3vuPeRT
2CA24Qbb4DbgseGimeBG4oS0oCLg4OVDd1tvVtu4+6VGKJr8YvgDk7WpDAK33LoO
QBWfQ5mPIeAN5GVWJ2Mk1qcSQTlO9LmM1vPiWojLjOEvZAA=
=5Gax
-----END PGP MESSAGE-----
fp: FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
unencrypted_suffix: _unencrypted
version: 3.3.1
0707010000006D000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002000000000sops-3.7.3/functional-tests/src0707010000006E000081A400000000000000000000000162794F9300005CEC000000000000000000000000000000000000002700000000sops-3.7.3/functional-tests/src/lib.rsextern crate tempdir;
extern crate serde;
extern crate serde_json;
extern crate serde_yaml;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate serde_derive;
#[cfg(test)]
mod tests {
extern crate serde;
extern crate serde_json;
extern crate serde_yaml;
use std::env;
use std::fs::File;
use std::io::{Write, Read};
use tempdir::TempDir;
use std::process::Command;
use serde_yaml::Value;
use std::path::Path;
const SOPS_BINARY_PATH: &'static str = "./sops";
const KMS_KEY: &'static str = "FUNCTIONAL_TEST_KMS_ARN";
macro_rules! assert_encrypted {
($object:expr, $key:expr) => {
assert!($object.get(&$key).is_some());
match *$object.get(&$key).unwrap() {
Value::String(ref s) => {
assert!(s.starts_with("ENC["), "Value is not encrypted");
}
_ => panic!("Value under key was not a string"),
}
}
}
lazy_static! {
static ref TMP_DIR: TempDir = TempDir::new("sops-functional-tests")
.expect("Unable to create temporary directory");
}
fn prepare_temp_file(name: &str, contents: &[u8]) -> String {
let file_path = TMP_DIR.path().join(name);
let mut tmp_file = File::create(file_path.clone()).expect("Unable to create temporary file");
tmp_file.write_all(&contents)
.expect("Error writing to temporary file");
file_path.to_string_lossy().into_owned()
}
#[test]
fn encrypt_json_file() {
let file_path = prepare_temp_file("test_encrypt.json",
b"{
\"foo\": 2,
\"bar\": \"baz\"
}");
let output = Command::new(SOPS_BINARY_PATH)
.arg("-e")
.arg(file_path.clone())
.output()
.expect("Error running sops");
assert!(output.status.success(), "sops didn't exit successfully");
let json = &String::from_utf8_lossy(&output.stdout);
let data: Value = serde_json::from_str(json).expect("Error parsing sops's JSON output");
match data.into() {
Value::Mapping(m) => {
assert!(m.get(&Value::String("sops".to_owned())).is_some(),
"sops metadata branch not found");
assert_encrypted!(&m, Value::String("foo".to_owned()));
assert_encrypted!(&m, Value::String("bar".to_owned()));
}
_ => panic!("sops's JSON output is not an object"),
}
}
#[test]
#[ignore]
fn publish_json_file_s3() {
let file_path = prepare_temp_file("test_encrypt_publish_s3.json",
b"{
\"foo\": 2,
\"bar\": \"baz\"
}");
assert!(Command::new(SOPS_BINARY_PATH)
.arg("-e")
.arg("-i")
.arg(file_path.clone())
.output()
.expect("Error running sops")
.status
.success(),
"SOPS failed to encrypt a file");
assert!(Command::new(SOPS_BINARY_PATH)
.arg("publish")
.arg("--yes")
.arg(file_path.clone())
.output()
.expect("Error running sops")
.status
.success(),
"sops failed to publish a file to S3");
//TODO: Check that file exists in S3 Bucket
}
#[test]
fn publish_json_file_vault() {
let file_path = prepare_temp_file("test_encrypt_publish_vault.json",
b"{
\"foo\": 2,
\"bar\": \"baz\"
}");
assert!(Command::new(SOPS_BINARY_PATH)
.arg("-e")
.arg("-i")
.arg(file_path.clone())
.output()
.expect("Error running sops")
.status
.success(),
"SOPS failed to encrypt a file");
assert!(Command::new(SOPS_BINARY_PATH)
.arg("publish")
.arg("--yes")
.arg(file_path.clone())
.output()
.expect("Error running sops")
.status
.success(),
"sops failed to publish a file to Vault");
//TODO: Check that file exists in Vault
}
#[test]
fn publish_json_file_vault_version_1() {
let file_path = prepare_temp_file("test_encrypt_publish_vault_version_1.json",
b"{
\"foo\": 2,
\"bar\": \"baz\"
}");
assert!(Command::new(SOPS_BINARY_PATH)
.arg("-e")
.arg("-i")
.arg(file_path.clone())
.output()
.expect("Error running sops")
.status
.success(),
"SOPS failed to encrypt a file");
assert!(Command::new(SOPS_BINARY_PATH)
.arg("publish")
.arg("--yes")
.arg(file_path.clone())
.output()
.expect("Error running sops")
.status
.success(),
"sops failed to publish a file to Vault");
//TODO: Check that file exists in Vault
}
#[test]
#[ignore]
fn encrypt_json_file_kms() {
let kms_arn = env::var(KMS_KEY).expect("Expected $FUNCTIONAL_TEST_KMS_ARN env var to be set");
let file_path = prepare_temp_file("test_encrypt_kms.json",
b"{
\"foo\": 2,
\"bar\": \"baz\"
}");
let output = Command::new(SOPS_BINARY_PATH)
.arg("--kms")
.arg(kms_arn)
.arg("-e")
.arg(file_path.clone())
.output()
.expect("Error running sops");
assert!(output.status.success(), "sops didn't exit successfully");
let json = &String::from_utf8_lossy(&output.stdout);
let data: Value = serde_json::from_str(json).expect("Error parsing sops's JSON output");
match data.into() {
Value::Mapping(m) => {
assert!(m.get(&Value::String("sops".to_owned())).is_some(),
"sops metadata branch not found");
assert_encrypted!(&m, Value::String("foo".to_owned()));
assert_encrypted!(&m, Value::String("bar".to_owned()));
}
_ => panic!("sops's JSON output is not an object"),
}
}
#[test]
fn encrypt_yaml_file() {
let file_path = prepare_temp_file("test_encrypt.yaml",
b"foo: 2
bar: baz");
let output = Command::new(SOPS_BINARY_PATH)
.arg("-e")
.arg(file_path.clone())
.output()
.expect("Error running sops");
assert!(output.status.success(), "sops didn't exit successfully");
let json = &String::from_utf8_lossy(&output.stdout);
let data: Value = serde_yaml::from_str(&json).expect("Error parsing sops's JSON output");
match data.into() {
Value::Mapping(m) => {
assert!(m.get(&Value::String("sops".to_owned())).is_some(),
"sops metadata branch not found");
assert_encrypted!(&m, Value::String("foo".to_owned()));
assert_encrypted!(&m, Value::String("bar".to_owned()));
}
_ => panic!("sops's YAML output is not a mapping"),
}
}
#[test]
fn set_json_file_update() {
let file_path = prepare_temp_file("test_set_update.json", r#"{"a": 2, "b": "ba"}"#.as_bytes());
Command::new(SOPS_BINARY_PATH)
.arg("-e")
.arg("-i")
.arg(file_path.clone())
.output()
.expect("Error running sops");
let output = Command::new(SOPS_BINARY_PATH)
.arg("--set")
.arg(r#"["a"] {"aa": "aaa"}"#)
.arg(file_path.clone())
.output()
.expect("Error running sops");
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_json::from_str(&s).expect("Error parsing sops's JSON output");
if let Value::Mapping(data) = data {
let a = data.get(&Value::String("a".to_owned())).unwrap();
if let &Value::Mapping(ref a) = a {
assert_encrypted!(&a, Value::String("aa".to_owned()));
return;
}
}
panic!("Output JSON does not have the expected structure");
}
#[test]
fn set_json_file_insert() {
let file_path = prepare_temp_file("test_set_insert.json", r#"{"a": 2, "b": "ba"}"#.as_bytes());
Command::new(SOPS_BINARY_PATH)
.arg("-e")
.arg("-i")
.arg(file_path.clone())
.output()
.expect("Error running sops");
let output = Command::new(SOPS_BINARY_PATH)
.arg("--set")
.arg(r#"["c"] {"cc": "ccc"}"#)
.arg(file_path.clone())
.output()
.expect("Error running sops");
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_json::from_str(&s).expect("Error parsing sops's JSON output");
if let Value::Mapping(data) = data {
let a = data.get(&Value::String("c".to_owned())).unwrap();
if let &Value::Mapping(ref a) = a {
assert_encrypted!(&a, Value::String("cc".to_owned()));
return;
}
}
panic!("Output JSON does not have the expected structure");
}
#[test]
fn set_yaml_file_update() {
let file_path = prepare_temp_file("test_set_update.yaml",
r#"a: 2
b: ba"#
.as_bytes());
Command::new(SOPS_BINARY_PATH)
.arg("-e")
.arg("-i")
.arg(file_path.clone())
.output()
.expect("Error running sops");
let output = Command::new(SOPS_BINARY_PATH)
.arg("--set")
.arg(r#"["a"] {"aa": "aaa"}"#)
.arg(file_path.clone())
.output()
.expect("Error running sops");
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 JSON output");
if let Value::Mapping(data) = data {
let a = data.get(&Value::String("a".to_owned())).unwrap();
if let &Value::Mapping(ref a) = a {
assert_encrypted!(&a, Value::String("aa".to_owned()));
return;
}
}
panic!("Output JSON does not have the expected structure");
}
#[test]
fn set_yaml_file_insert() {
let file_path = prepare_temp_file("test_set_insert.yaml",
r#"a: 2
b: ba"#
.as_bytes());
Command::new(SOPS_BINARY_PATH)
.arg("-e")
.arg("-i")
.arg(file_path.clone())
.output()
.expect("Error running sops");
let output = Command::new(SOPS_BINARY_PATH)
.arg("--set")
.arg(r#"["c"] {"cc": "ccc"}"#)
.arg(file_path.clone())
.output()
.expect("Error running sops");
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 JSON output");
if let Value::Mapping(data) = data {
let a = data.get(&Value::String("c".to_owned())).unwrap();
if let &Value::Mapping(ref a) = a {
assert_encrypted!(&a, Value::String("cc".to_owned()));
return;
}
}
panic!("Output YAML does not have the expected structure");
}
#[test]
fn set_yaml_file_string() {
let file_path = prepare_temp_file("test_set_string.yaml",
r#"a: 2
b: ba"#
.as_bytes());
Command::new(SOPS_BINARY_PATH)
.arg("-e")
.arg("-i")
.arg(file_path.clone())
.output()
.expect("Error running sops");
Command::new(SOPS_BINARY_PATH)
.arg("-e")
.arg("-i")
.arg("--set")
.arg(r#"["a"] "aaa""#)
.arg(file_path.clone())
.output()
.expect("Error running sops");
let output = Command::new(SOPS_BINARY_PATH)
.arg("-d")
.arg("-i")
.arg(file_path.clone())
.output()
.expect("Error running sops");
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 decrypt_file_no_mac() {
let file_path = prepare_temp_file("test_decrypt_no_mac.yaml", include_bytes!("../res/no_mac.yaml"));
assert!(!Command::new(SOPS_BINARY_PATH)
.arg("-d")
.arg(file_path.clone())
.output()
.expect("Error running sops")
.status
.success(),
"SOPS allowed decrypting a file with no MAC without --ignore-mac");
assert!(Command::new(SOPS_BINARY_PATH)
.arg("-d")
.arg("--ignore-mac")
.arg(file_path.clone())
.output()
.expect("Error running sops")
.status
.success(),
"SOPS failed to decrypt a file with no MAC with --ignore-mac passed in");
}
#[test]
fn encrypt_comments() {
let file_path = "res/comments.yaml";
let output = Command::new(SOPS_BINARY_PATH)
.arg("-e")
.arg(file_path.clone())
.output()
.expect("Error running sops");
assert!(output.status.success(), "SOPS didn't return successfully");
assert!(!String::from_utf8_lossy(&output.stdout).contains("first comment in file"), "Comment was not encrypted");
assert!(!String::from_utf8_lossy(&output.stdout).contains("this-is-a-comment"), "Comment was not encrypted");
}
#[test]
fn encrypt_comments_list() {
let file_path = "res/comments_list.yaml";
let output = Command::new(SOPS_BINARY_PATH)
.arg("-e")
.arg(file_path.clone())
.output()
.expect("Error running sops");
assert!(output.status.success(), "SOPS didn't return successfully");
assert!(!String::from_utf8_lossy(&output.stdout).contains("this-is-a-comment"), "Comment was not encrypted");
assert!(!String::from_utf8_lossy(&output.stdout).contains("this-is-a-comment"), "Comment was not encrypted");
}
#[test]
fn decrypt_comments() {
let file_path = "res/comments.enc.yaml";
let output = Command::new(SOPS_BINARY_PATH)
.arg("-d")
.arg(file_path.clone())
.output()
.expect("Error running sops");
assert!(output.status.success(), "SOPS didn't return successfully");
assert!(String::from_utf8_lossy(&output.stdout).contains("first comment in file"), "Comment was not decrypted");
assert!(String::from_utf8_lossy(&output.stdout).contains("this-is-a-comment"), "Comment was not decrypted");
}
#[test]
fn decrypt_comments_unencrypted_comments() {
let file_path = "res/comments_unencrypted_comments.yaml";
let output = Command::new(SOPS_BINARY_PATH)
.arg("-d")
.arg(file_path.clone())
.output()
.expect("Error running sops");
assert!(output.status.success(), "SOPS didn't return successfully");
assert!(String::from_utf8_lossy(&output.stdout).contains("first comment in file"), "Comment was not decrypted");
assert!(String::from_utf8_lossy(&output.stdout).contains("this-is-a-comment"), "Comment was not decrypted");
}
#[test]
fn roundtrip_shamir() {
// 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("-i")
.arg("-e")
.arg(file_path.clone())
.output()
.expect("Error running sops");
assert!(output.status.success(),
"SOPS failed to encrypt a file with Shamir Secret Sharing");
let output = Command::new(SOPS_BINARY_PATH)
.arg("-d")
.arg(file_path.clone())
.output()
.expect("Error running sops");
assert!(output.status
.success(),
"SOPS failed to decrypt a file with Shamir Secret Sharing");
assert!(String::from_utf8_lossy(&output.stdout).contains("secret"));
}
#[test]
fn roundtrip_shamir_missing_decryption_key() {
// The .sops.yaml file ensures this file is encrypted with two key groups, each with one GPG key,
// but we don't have one of the private keys
let file_path = prepare_temp_file("test_roundtrip_keygroups_missing_decryption_key.yaml",
"a: secret".as_bytes());
let output = Command::new(SOPS_BINARY_PATH)
.arg("-i")
.arg("-e")
.arg(file_path.clone())
.output()
.expect("Error running sops");
assert!(output.status.success(),
"SOPS failed to encrypt a file with Shamir Secret Sharing");
let output = Command::new(SOPS_BINARY_PATH)
.arg("-d")
.arg(file_path.clone())
.output()
.expect("Error running sops");
assert!(!output.status
.success(),
"SOPS succeeded decrypting a file with a missing decrytion key");
}
#[test]
fn test_decrypt_file_multiple_keys() {
let file_path = prepare_temp_file("test_decrypt_file_multiple_keys.yaml",
include_bytes!("../res/multiple_keys.yaml"));
let output = Command::new(SOPS_BINARY_PATH)
.arg("-d")
.arg(file_path.clone())
.output()
.expect("Error running sops");
assert!(output.status
.success(),
"SOPS failed to decrypt a file that uses multiple keys");
}
#[test]
fn extract_string() {
let file_path = prepare_temp_file("test_extract_string.yaml",
"multiline: |\n multi\n line".as_bytes());
let output = Command::new(SOPS_BINARY_PATH)
.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("--extract")
.arg("[\"multiline\"]")
.arg("-d")
.arg(file_path.clone())
.output()
.expect("Error running sops");
assert!(output.status
.success(),
"SOPS failed to extract");
assert_eq!(output.stdout, b"multi\nline");
}
#[test]
fn roundtrip_binary() {
let data = b"\"\"{}this_is_binary_data";
let file_path = prepare_temp_file("test.binary", data);
let output = Command::new(SOPS_BINARY_PATH)
.arg("-i")
.arg("-e")
.arg(file_path.clone())
.output()
.expect("Error running sops");
assert!(output.status.success(),
"SOPS failed to encrypt a binary file");
let output = Command::new(SOPS_BINARY_PATH)
.arg("-d")
.arg(file_path.clone())
.output()
.expect("Error running sops");
assert!(output.status
.success(),
"SOPS failed to decrypt a binary file");
assert_eq!(output.stdout, data);
}
#[test]
#[ignore]
fn roundtrip_kms_encryption_context() {
let kms_arn = env::var(KMS_KEY).expect("Expected $FUNCTIONAL_TEST_KMS_ARN env var to be set");
let file_path = prepare_temp_file("test_roundtrip_kms_encryption_context.json",
b"{
\"foo\": 2,
\"bar\": \"baz\"
}");
let output = Command::new(SOPS_BINARY_PATH)
.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(file_path.clone())
.output()
.expect("Error running sops");
assert!(output.status
.success(),
"SOPS failed to decrypt a file with KMS Encryption Context");
assert!(String::from_utf8_lossy(&output.stdout).contains("foo"));
assert!(String::from_utf8_lossy(&output.stdout).contains("baz"));
}
#[test]
fn output_flag() {
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("--output")
.arg(&output_path)
.arg("-e")
.arg(input_path.clone())
.output()
.expect("Error running sops");
assert!(output.status
.success(),
"SOPS failed to decrypt a binary file");
assert_eq!(output.stdout, &[]);
let mut f = File::open(&output_path).expect("output file not found");
let mut contents = String::new();
f.read_to_string(&mut contents)
.expect("couldn't read output file contents");
assert_ne!(contents, "", "Output file is empty");
}
}
0707010000006F000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001200000000sops-3.7.3/gcpkms07070100000070000081A400000000000000000000000162794F9300001551000000000000000000000000000000000000001F00000000sops-3.7.3/gcpkms/keysource.gopackage gcpkms //import "go.mozilla.org/sops/v3/gcpkms"
import (
"encoding/base64"
"fmt"
"google.golang.org/api/option"
"os"
"regexp"
"strings"
"time"
"go.mozilla.org/sops/v3/logging"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
cloudkms "google.golang.org/api/cloudkms/v1"
)
var log *logrus.Logger
func init() {
log = logging.NewLogger("GCPKMS")
}
// MasterKey is a GCP KMS key used to encrypt and decrypt sops' data key.
type MasterKey struct {
ResourceID string
EncryptedKey string
CreationDate time.Time
}
// EncryptedDataKey returns the encrypted data key this master key holds
func (key *MasterKey) EncryptedDataKey() []byte {
return []byte(key.EncryptedKey)
}
// SetEncryptedDataKey sets the encrypted data key for this master key
func (key *MasterKey) SetEncryptedDataKey(enc []byte) {
key.EncryptedKey = string(enc)
}
// Encrypt takes a sops data key, encrypts it with GCP KMS and stores the result in the EncryptedKey field
func (key *MasterKey) Encrypt(dataKey []byte) error {
cloudkmsService, err := key.createCloudKMSService()
if err != nil {
log.WithField("resourceID", key.ResourceID).Info("Encryption failed")
return fmt.Errorf("Cannot create GCP KMS service: %w", err)
}
req := &cloudkms.EncryptRequest{
Plaintext: base64.StdEncoding.EncodeToString(dataKey),
}
resp, err := cloudkmsService.Projects.Locations.KeyRings.CryptoKeys.Encrypt(key.ResourceID, req).Do()
if err != nil {
log.WithField("resourceID", key.ResourceID).Info("Encryption failed")
return fmt.Errorf("Failed to call GCP KMS encryption service: %w", err)
}
log.WithField("resourceID", key.ResourceID).Info("Encryption succeeded")
key.EncryptedKey = resp.Ciphertext
return nil
}
// EncryptIfNeeded encrypts the provided sops' data key and encrypts it if it hasn't been encrypted yet
func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
if key.EncryptedKey == "" {
return key.Encrypt(dataKey)
}
return nil
}
// Decrypt decrypts the EncryptedKey field with CGP KMS and returns the result.
func (key *MasterKey) Decrypt() ([]byte, error) {
cloudkmsService, err := key.createCloudKMSService()
if err != nil {
log.WithField("resourceID", key.ResourceID).Info("Decryption failed")
return nil, fmt.Errorf("Cannot create GCP KMS service: %w", err)
}
req := &cloudkms.DecryptRequest{
Ciphertext: key.EncryptedKey,
}
resp, err := cloudkmsService.Projects.Locations.KeyRings.CryptoKeys.Decrypt(key.ResourceID, req).Do()
if err != nil {
log.WithField("resourceID", key.ResourceID).Info("Decryption failed")
return nil, fmt.Errorf("Error decrypting key: %w", err)
}
encryptedKey, err := base64.StdEncoding.DecodeString(resp.Plaintext)
if err != nil {
log.WithField("resourceID", key.ResourceID).Info("Decryption failed")
return nil, err
}
log.WithField("resourceID", key.ResourceID).Info("Decryption succeeded")
return encryptedKey, nil
}
// NeedsRotation returns whether the data key needs to be rotated or not.
func (key *MasterKey) NeedsRotation() bool {
return time.Since(key.CreationDate) > (time.Hour * 24 * 30 * 6)
}
// ToString converts the key to a string representation
func (key *MasterKey) ToString() string {
return key.ResourceID
}
// NewMasterKeyFromResourceID takes a GCP KMS resource ID string and returns a new MasterKey for that
func NewMasterKeyFromResourceID(resourceID string) *MasterKey {
k := &MasterKey{}
resourceID = strings.Replace(resourceID, " ", "", -1)
k.ResourceID = resourceID
k.CreationDate = time.Now().UTC()
return k
}
// MasterKeysFromResourceIDString takes a comma separated list of GCP KMS resource IDs and returns a slice of new MasterKeys for them
func MasterKeysFromResourceIDString(resourceID string) []*MasterKey {
var keys []*MasterKey
if resourceID == "" {
return keys
}
for _, s := range strings.Split(resourceID, ",") {
keys = append(keys, NewMasterKeyFromResourceID(s))
}
return keys
}
func (key MasterKey) createCloudKMSService() (*cloudkms.Service, error) {
re := regexp.MustCompile(`^projects/[^/]+/locations/[^/]+/keyRings/[^/]+/cryptoKeys/[^/]+$`)
matches := re.FindStringSubmatch(key.ResourceID)
if matches == nil {
return nil, fmt.Errorf("No valid resourceId found in %q", key.ResourceID)
}
ctx := context.Background()
var options []option.ClientOption
if credentials, err := getGoogleCredentials(); err != nil {
return nil, err
} else if len(credentials) > 0 {
options = append(options, option.WithCredentialsJSON(credentials))
}
cloudkmsService, err := cloudkms.NewService(ctx, options...)
if err != nil {
return nil, err
}
return cloudkmsService, nil
}
// ToMap converts the MasterKey to a map for serialization purposes
func (key MasterKey) ToMap() map[string]interface{} {
out := make(map[string]interface{})
out["resource_id"] = key.ResourceID
out["enc"] = key.EncryptedKey
out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339)
return out
}
// getGoogleCredentials looks for a GCP Service Account in the environment
// variable: GOOGLE_CREDENTIALS, set as either a path to a credentials file or directly as the
// variable's value in JSON format.
//
// If not set, will default to use GOOGLE_APPLICATION_CREDENTIALS
func getGoogleCredentials() ([]byte, error) {
defaultCredentials := os.Getenv("GOOGLE_CREDENTIALS")
if _, err := os.Stat(defaultCredentials); err == nil {
return os.ReadFile(defaultCredentials)
}
return []byte(defaultCredentials), nil
}
07070100000071000081A400000000000000000000000162794F930000049F000000000000000000000000000000000000002400000000sops-3.7.3/gcpkms/keysource_test.gopackage gcpkms
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestGCPKMSKeySourceFromString(t *testing.T) {
s := "projects/sops-testing1/locations/global/keyRings/creds/cryptoKeys/key1, projects/sops-testing2/locations/global/keyRings/creds/cryptoKeys/key2"
ks := MasterKeysFromResourceIDString(s)
k1 := ks[0]
k2 := ks[1]
expectedResourceID1 := "projects/sops-testing1/locations/global/keyRings/creds/cryptoKeys/key1"
expectedResourceID2 := "projects/sops-testing2/locations/global/keyRings/creds/cryptoKeys/key2"
if k1.ResourceID != expectedResourceID1 {
t.Errorf("ResourceID mismatch. Expected %s, found %s", expectedResourceID1, k1.ResourceID)
}
if k2.ResourceID != expectedResourceID2 {
t.Errorf("ResourceID mismatch. Expected %s, found %s", expectedResourceID2, k2.ResourceID)
}
}
func TestKeyToMap(t *testing.T) {
key := MasterKey{
CreationDate: time.Date(2016, time.October, 31, 10, 0, 0, 0, time.UTC),
ResourceID: "foo",
EncryptedKey: "this is encrypted",
}
assert.Equal(t, map[string]interface{}{
"resource_id": "foo",
"enc": "this is encrypted",
"created_at": "2016-10-31T10:00:00Z",
}, key.ToMap())
}
07070100000072000081A400000000000000000000000162794F930000158C000000000000000000000000000000000000001200000000sops-3.7.3/go.modmodule go.mozilla.org/sops/v3
go 1.17
require (
cloud.google.com/go/storage v1.22.0
filippo.io/age v1.0.0
github.com/Azure/azure-sdk-for-go v63.3.0+incompatible
github.com/Azure/go-autorest/autorest v0.11.26
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11
github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5
github.com/aws/aws-sdk-go v1.43.43
github.com/blang/semver v3.5.1+incompatible
github.com/fatih/color v1.13.0
github.com/golang/protobuf v1.5.2
github.com/google/go-cmp v0.5.7
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/goware/prefixer v0.0.0-20160118172347-395022866408
github.com/hashicorp/vault/api v1.5.0
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef
github.com/lib/pq v1.10.5
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/go-wordwrap v1.0.1
github.com/ory/dockertest v3.3.5+incompatible
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.1
go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
golang.org/x/net v0.0.0-20220420153159-1850ba15e1be
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
google.golang.org/api v0.74.0
google.golang.org/grpc v1.45.0
google.golang.org/protobuf v1.28.0
gopkg.in/ini.v1 v1.66.4
gopkg.in/urfave/cli.v1 v1.20.0
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
)
require (
cloud.google.com/go v0.100.2 // indirect
cloud.google.com/go/compute v1.5.0 // indirect
cloud.google.com/go/iam v0.3.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/armon/go-metrics v0.3.10 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
github.com/containerd/continuity v0.2.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/golang-jwt/jwt/v4 v4.3.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/googleapis/gax-go/v2 v2.2.0 // indirect
github.com/googleapis/go-type-adapters v1.0.0 // indirect
github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.2.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-plugin v1.4.3 // indirect
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 // indirect
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.3 // indirect
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/hashicorp/go-version v1.4.0 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/vault/sdk v0.4.1 // indirect
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/opencontainers/runc v1.1.0 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/stretchr/objx v0.3.0 // indirect
go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gotest.tools v2.2.0+incompatible // indirect
)
07070100000073000081A400000000000000000000000162794F9300018C60000000000000000000000000000000000000001200000000sops-3.7.3/go.sumbazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y=
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
cloud.google.com/go/compute v1.5.0 h1:b1zWmYuuHz7gO9kDcM/EpHGr06UgsYNRpNJzI2kFiLM=
cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/iam v0.3.0 h1:exkAomrVUuzx9kWFI1wm3KI0uoDeUFPB4kKGzx6x+Gc=
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.22.0 h1:NUV0NNp9nkBuW66BFRLuMgldN60C57ET3dhbwLIYio8=
cloud.google.com/go/storage v1.22.0/go.mod h1:GbaLEoMqbVm6sx3Z0R++gSiBlgMv6yUi2q1DeGFKQgE=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/age v1.0.0 h1:V6q14n0mqYU3qKFkZ6oOaF9oXneOviS3ubXsSVBRSzc=
filippo.io/age v1.0.0/go.mod h1:PaX+Si/Sd5G8LgfCwldsSba3H1DDQZhIhFGkhbHaBq8=
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
github.com/Azure/azure-sdk-for-go v63.3.0+incompatible h1:INepVujzUrmArRZjDLHbtER+FkvCoEwyRCXGqOlmDII=
github.com/Azure/azure-sdk-for-go v63.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest v0.11.26 h1:W/MzvoAiFfL5h4nq81wm7axvITgbnOoifXXGkFrgF1g=
github.com/Azure/go-autorest/autorest v0.11.26/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U=
github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA=
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw=
github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=
github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac=
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5 h1:cSHEbLj0GZeHM1mWG84qEnGFojNEQ83W7cwaPRjcwXU=
github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo=
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aws/aws-sdk-go v1.43.43 h1:1L06qzQvl4aC3Skfh5rV7xVhGHjIZoHcqy16NoyQ1o4=
github.com/aws/aws-sdk-go v1.43.43/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/continuity v0.2.2 h1:QSqfxcn8c+12slxwu00AtzXrsami0MJb/MQs9lOLHLA=
github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk=
github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog=
github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ=
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
github.com/googleapis/gax-go/v2 v2.2.0 h1:s7jOdKSaksJVOxE0Y/S32otcfiP+UQ0cL8/GTKaONwE=
github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA=
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
github.com/goware/prefixer v0.0.0-20160118172347-395022866408 h1:Y9iQJfEqnN3/Nce9cOegemcy/9Ai5k3huT6E80F3zaw=
github.com/goware/prefixer v0.0.0-20160118172347-395022866408/go.mod h1:PE1ycukgRPJ7bJ9a1fdfQ9j8i/cEcRAoLZzbxYpNB/s=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM=
github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4=
github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=
github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I=
github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 h1:p4AKXPPS24tO8Wc8i1gLvSKdmkiSY5xuju57czJ/IJQ=
github.com/hashicorp/go-secure-stdlib/mlock v0.1.2/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.3 h1:geBw3SBrxQq+buvbf4K+Qltv1gjaXJxy8asD4CjGYow=
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.3/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
github.com/hashicorp/go-secure-stdlib/password v0.1.1/go.mod h1:9hH302QllNwu1o2TGYtSk8I8kTAN0ca1EHpwhm5Mmzo=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs=
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4=
github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/vault/api v1.5.0 h1:Bp6yc2bn7CWkOrVIzFT/Qurzx528bdavF3nz590eu28=
github.com/hashicorp/vault/api v1.5.0/go.mod h1:LkMdrZnWNrFaQyYYazWVn7KshilfDidgVBq6YiTq/bM=
github.com/hashicorp/vault/sdk v0.4.1 h1:3SaHOJY687jY1fnB61PtL0cOkKItphrbLmux7T92HBo=
github.com/hashicorp/vault/sdk v0.4.1/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I=
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM=
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.10.5 h1:J+gdV2cUmX7ZqL2B0lFcW0m+egaHC2V3lpO8nWxyYiQ=
github.com/lib/pq v1.10.5/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v1.1.0 h1:O9+X96OcDjkmmZyfaG996kV7yq8HsoU2h1XRRQcefG8=
github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA=
github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a h1:N7VD+PwpJME2ZfQT8+ejxwA4Ow10IkGbU0MGf94ll8k=
go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a/go.mod h1:YDKUvO0b//78PaaEro6CAPH6NqohCmL2Cwju5XI2HoE=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220420153159-1850ba15e1be h1:yx80W7nvY5ySWpaU8UWaj5o9e23YgO9BRhQol7Lc+JI=
golang.org/x/net v0.0.0-20220420153159-1850ba15e1be/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE=
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs=
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
google.golang.org/api v0.74.0 h1:ExR2D+5TYIrMphWgs5JCgwRhEDlPDXXrLwHHMgPHTXE=
google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf h1:JTjwKJX9erVpsw17w+OIPP7iAgEkN/r8urhWSunEDTs=
google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
07070100000074000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001300000000sops-3.7.3/hcvault07070100000075000081A400000000000000000000000162794F9300001F32000000000000000000000000000000000000002000000000sops-3.7.3/hcvault/keysource.gopackage hcvault
import (
"bytes"
"encoding/base64"
"fmt"
"io"
"net/url"
"os"
"path"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/hashicorp/vault/api"
homedir "github.com/mitchellh/go-homedir"
"github.com/sirupsen/logrus"
"go.mozilla.org/sops/v3/logging"
)
var log *logrus.Logger
func init() {
log = logging.NewLogger("VAULT_TRANSIT")
}
// MasterKey is a Vault Transit backend path used to encrypt and decrypt sops' data key.
type MasterKey struct {
EncryptedKey string
KeyName string
EnginePath string
VaultAddress string
CreationDate time.Time
}
// NewMasterKeysFromURIs gets lots of keys from lots of URIs
func NewMasterKeysFromURIs(uris string) ([]*MasterKey, error) {
var keys []*MasterKey
if uris == "" {
return keys, nil
}
uriList := strings.Split(uris, ",")
for _, uri := range uriList {
if uri == "" {
continue
}
key, err := NewMasterKeyFromURI(uri)
if err != nil {
return nil, err
}
keys = append(keys, key)
}
return keys, nil
}
// NewMasterKeyFromURI obtains the vaultAddress the transit backend path and the key name from the full URI of the key
func NewMasterKeyFromURI(uri string) (*MasterKey, error) {
log.Debugln("Called NewMasterKeyFromURI with uri: ", uri)
var key *MasterKey
if uri == "" {
return key, nil
}
u, err := url.Parse(uri)
if err != nil {
return nil, err
}
if u.Scheme == "" {
return nil, fmt.Errorf("missing scheme in vault URL (should be like this: https://vault.example.com:8200/v1/transit/keys/keyName), got: %v", uri)
}
enginePath, keyName, err := getBackendAndKeyFromPath(u.RequestURI())
if err != nil {
return nil, err
}
u.Path = ""
return NewMasterKey(u.String(), enginePath, keyName), nil
}
func getBackendAndKeyFromPath(fullPath string) (enginePath, keyName string, err error) {
// Running vault behind a reverse proxy with longer urls seems not to be supported
// by the vault client api so we have a separate Error for that here.
if re := regexp.MustCompile(`/[^/]+/v[\d]+/[^/]+/[^/]+/[^/]+`); re.Match([]byte(fullPath)) {
return "", "", fmt.Errorf("running Vault with a prefixed url is not supported! (Format has to be like https://vault.example.com:8200/v1/transit/keys/keyName)")
} else if re := regexp.MustCompile(`/v[\d]+/[^/]+/[^/]+/[^/]+`); re.Match([]byte(fullPath)) == false {
return "", "", fmt.Errorf("vault path does not seem to be formatted correctly: (eg. https://vault.example.com:8200/v1/transit/keys/keyName)")
}
fullPath = strings.TrimPrefix(fullPath, "/")
fullPath = strings.TrimSuffix(fullPath, "/")
dirs := strings.Split(fullPath, "/")
keyName = dirs[len(dirs)-1]
enginePath = path.Join(dirs[1 : len(dirs)-2]...)
err = nil
return
}
// NewMasterKey creates a new MasterKey from a vault address, transit backend path and a key name and setting the creation date to the current date
func NewMasterKey(addess, enginePath, keyName string) *MasterKey {
mk := &MasterKey{
VaultAddress: addess,
EnginePath: enginePath,
KeyName: keyName,
CreationDate: time.Now().UTC(),
}
log.Debugln("Created Vault Master Key: ", mk)
return mk
}
// EncryptedDataKey returns the encrypted data key this master key holds
func (key *MasterKey) EncryptedDataKey() []byte {
return []byte(key.EncryptedKey)
}
// SetEncryptedDataKey sets the encrypted data key for this master key
func (key *MasterKey) SetEncryptedDataKey(enc []byte) {
key.EncryptedKey = string(enc)
}
func vaultClient(address string) (*api.Client, error) {
cfg := api.DefaultConfig()
cfg.Address = address
cli, err := api.NewClient(cfg)
if err != nil {
return nil, fmt.Errorf("Cannot create Vault Client: %w", err)
}
if cli.Token() != "" {
return cli, nil
}
homePath, err := homedir.Dir()
if err != nil {
panic(fmt.Sprintf("error getting user's home directory: %v", err))
}
tokenPath := filepath.Join(homePath, ".vault-token")
f, err := os.Open(tokenPath)
if os.IsNotExist(err) {
return cli, nil
}
if err != nil {
return nil, err
}
defer f.Close()
buf := bytes.NewBuffer(nil)
if _, err := io.Copy(buf, f); err != nil {
return nil, err
}
cli.SetToken(strings.TrimSpace(buf.String()))
return cli, nil
}
// Encrypt takes a sops data key, encrypts it with Vault Transit and stores the result in the EncryptedKey field
func (key *MasterKey) Encrypt(dataKey []byte) error {
fullPath := path.Join(key.EnginePath, "encrypt", key.KeyName)
cli, err := vaultClient(key.VaultAddress)
if err != nil {
return err
}
encoded := base64.StdEncoding.EncodeToString(dataKey)
payload := make(map[string]interface{})
payload["plaintext"] = encoded
raw, err := cli.Logical().Write(fullPath, payload)
if err != nil {
log.WithField("Path", fullPath).Info("Encryption failed")
return err
}
if raw == nil || raw.Data == nil {
return fmt.Errorf("The transit backend %s is empty", fullPath)
}
encrypted, ok := raw.Data["ciphertext"]
if !ok {
return fmt.Errorf("there's not encrypted data")
}
encryptedKey, ok := encrypted.(string)
if !ok {
return fmt.Errorf("the ciphertext cannot be casted to string")
}
key.EncryptedKey = encryptedKey
return nil
}
// EncryptIfNeeded encrypts the provided sops' data key and encrypts it if it hasn't been encrypted yet
func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
if key.EncryptedKey == "" {
return key.Encrypt(dataKey)
}
return nil
}
// Decrypt decrypts the EncryptedKey field with Vault Transit and returns the result.
func (key *MasterKey) Decrypt() ([]byte, error) {
fullPath := path.Join(key.EnginePath, "decrypt", key.KeyName)
cli, err := vaultClient(key.VaultAddress)
if err != nil {
return nil, err
}
payload := make(map[string]interface{})
payload["ciphertext"] = key.EncryptedKey
raw, err := cli.Logical().Write(fullPath, payload)
if err != nil {
log.WithField("Path", fullPath).Info("Encryption failed")
return nil, err
}
if raw == nil || raw.Data == nil {
return nil, fmt.Errorf("The transit backend %s is empty", fullPath)
}
decrypted, ok := raw.Data["plaintext"]
if ok != true {
return nil, fmt.Errorf("there's no decrypted data")
}
dataKey, ok := decrypted.(string)
if ok != true {
return nil, fmt.Errorf("the plaintest cannot be casted to string")
}
result, err := base64.StdEncoding.DecodeString(dataKey)
if err != nil {
return nil, fmt.Errorf("Couldn't decode base64 plaintext")
}
return result, nil
}
// NeedsRotation returns whether the data key needs to be rotated or not.
// This is simply copied from GCPKMS
// TODO: handle key rotation on vault side
func (key *MasterKey) NeedsRotation() bool {
//TODO: manage rewrapping https://www.vaultproject.io/api/secret/transit/index.html#rewrap-data
return time.Since(key.CreationDate) > (time.Hour * 24 * 30 * 6)
}
// ToString converts the key to a string representation
func (key *MasterKey) ToString() string {
return fmt.Sprintf("%s/v1/%s/keys/%s", key.VaultAddress, key.EnginePath, key.KeyName)
}
func (key *MasterKey) createVaultTransitAndKey() error {
cli, err := vaultClient(key.VaultAddress)
if err != nil {
return err
}
if err != nil {
return fmt.Errorf("Cannot create Vault Client: %w", err)
}
err = cli.Sys().Mount(key.EnginePath, &api.MountInput{
Type: "transit",
Description: "backend transit used by SOPS",
})
if err != nil {
return err
}
path := path.Join(key.EnginePath, "keys", key.KeyName)
payload := make(map[string]interface{})
payload["type"] = "rsa-4096"
_, err = cli.Logical().Write(path, payload)
if err != nil {
return err
}
_, err = cli.Logical().Read(path)
if err != nil {
return err
}
return nil
}
// ToMap converts the MasterKey to a map for serialization purposes
func (key MasterKey) ToMap() map[string]interface{} {
out := make(map[string]interface{})
out["vault_address"] = key.VaultAddress
out["key_name"] = key.KeyName
out["engine_path"] = key.EnginePath
out["enc"] = key.EncryptedKey
out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339)
return out
}
07070100000076000081A400000000000000000000000162794F9300000FD2000000000000000000000000000000000000002500000000sops-3.7.3/hcvault/keysource_test.gopackage hcvault
import (
"fmt"
logger "log"
"os"
"testing"
"time"
"github.com/hashicorp/vault/api"
"github.com/ory/dockertest"
"github.com/stretchr/testify/assert"
)
func TestMain(m *testing.M) {
// uses a sensible default on windows (tcp/http) and linux/osx (socket)
pool, err := dockertest.NewPool("")
if err != nil {
logger.Fatalf("Could not connect to docker: %s", err)
}
// pulls an image, creates a container based on it and runs it
resource, err := pool.Run("vault", "1.2.2", []string{"VAULT_DEV_ROOT_TOKEN_ID=secret"})
if err != nil {
logger.Fatalf("Could not start resource: %s", err)
}
vaultAddr := fmt.Sprintf("http://%s", resource.GetHostPort("8200/tcp"))
os.Setenv("VAULT_ADDR", vaultAddr)
os.Setenv("VAULT_TOKEN", "secret")
// exponential backoff-retry, because the application in the container might not be ready to accept connections yet
if err := pool.Retry(func() error {
cli, err := api.NewClient(api.DefaultConfig())
if err != nil {
return fmt.Errorf("Cannot create Vault Client: %w", err)
}
status, err := cli.Sys().InitStatus()
if err != nil {
return err
}
if status != true {
return fmt.Errorf("Vault not ready yet")
}
return nil
}); err != nil {
logger.Fatalf("Could not connect to docker: %s", err)
}
key := NewMasterKey(vaultAddr, "sops", "main")
err = key.createVaultTransitAndKey()
if err != nil {
logger.Fatal(err)
}
code := 0
if err == nil {
code = m.Run()
}
// You can't defer this because os.Exit doesn't care for defer
if err := pool.Purge(resource); err != nil {
logger.Fatalf("Could not purge resource: %s", err)
}
os.Exit(code)
}
func TestKeyToMap(t *testing.T) {
key := MasterKey{
CreationDate: time.Date(2016, time.October, 31, 10, 0, 0, 0, time.UTC),
VaultAddress: "http://127.0.0.1:8200",
EnginePath: "foo",
KeyName: "bar",
EncryptedKey: "this is encrypted",
}
assert.Equal(t, map[string]interface{}{
"vault_address": "http://127.0.0.1:8200",
"engine_path": "foo",
"key_name": "bar",
"enc": "this is encrypted",
"created_at": "2016-10-31T10:00:00Z",
}, key.ToMap())
}
func TestEncryptionDecryption(t *testing.T) {
dataKey := []byte("super very Secret Key!!!")
key := MasterKey{
VaultAddress: os.Getenv("VAULT_ADDR"),
EnginePath: "sops",
KeyName: "main",
}
err := key.Encrypt(dataKey)
if err != nil {
fmt.Println(err)
t.Fail()
return
}
decrypted, err := key.Decrypt()
if err != nil {
fmt.Println(err)
t.Fail()
return
}
assert.Equal(t, dataKey, decrypted)
}
func TestNewMasterKeyFromURI(t *testing.T) {
uri1 := "https://vault.example.com:8200/v1/transit/keys/keyName"
uri2 := "https://vault.me.com/v1/super42/bestmarket/keys/slig"
uri3 := "http://127.0.0.1:12121/v1/transit/keys/dev"
mk1 := &MasterKey{
VaultAddress: "https://vault.example.com:8200",
EnginePath: "transit",
KeyName: "keyName",
}
mk2 := &MasterKey{
VaultAddress: "https://vault.me.com",
EnginePath: "super42/bestmarket",
KeyName: "slig",
}
mk3 := &MasterKey{
VaultAddress: "http://127.0.0.1:12121",
EnginePath: "transit",
KeyName: "dev",
}
genMk1, err := NewMasterKeyFromURI(uri1)
if err != nil {
log.Errorln(err)
t.Fail()
}
genMk2, err := NewMasterKeyFromURI(uri2)
if err != nil {
log.Errorln(err)
t.Fail()
}
genMk3, err := NewMasterKeyFromURI(uri3)
if err != nil {
log.Errorln(err)
t.Fail()
}
if assert.NotNil(t, genMk1) {
mk1.CreationDate = genMk1.CreationDate
assert.Equal(t, mk1, genMk1)
}
if assert.NotNil(t, genMk2) {
mk2.CreationDate = genMk2.CreationDate
assert.Equal(t, mk2, genMk2)
}
if assert.NotNil(t, genMk3) {
mk3.CreationDate = genMk3.CreationDate
assert.Equal(t, mk3, genMk3)
}
badURIs := []string{
"vault.me/keys/dev/mykey",
"http://127.0.0.1:12121/v1/keys/dev",
"tcp://127.0.0.1:12121/v1/keys/dev",
}
for _, uri := range badURIs {
if _, err = NewMasterKeyFromURI(uri); err == nil {
log.Errorf("Should be a invalid uri: %s", uri)
t.Fail()
}
}
}
07070100000077000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001000000000sops-3.7.3/keys07070100000078000081A400000000000000000000000162794F930000017D000000000000000000000000000000000000001800000000sops-3.7.3/keys/keys.gopackage keys
// MasterKey provides a way of securing the key used to encrypt the Tree by encrypting and decrypting said key.
type MasterKey interface {
Encrypt(dataKey []byte) error
EncryptIfNeeded(dataKey []byte) error
EncryptedDataKey() []byte
SetEncryptedDataKey([]byte)
Decrypt() ([]byte, error)
NeedsRotation() bool
ToString() string
ToMap() map[string]interface{}
}
07070100000079000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001600000000sops-3.7.3/keyservice0707010000007A000081A400000000000000000000000162794F9300000422000000000000000000000000000000000000002000000000sops-3.7.3/keyservice/client.gopackage keyservice
import (
"golang.org/x/net/context"
"google.golang.org/grpc"
)
// LocalClient is a key service client that performs all operations locally
type LocalClient struct {
Server KeyServiceServer
}
// NewLocalClient creates a new local client
func NewLocalClient() LocalClient {
return LocalClient{Server{}}
}
// NewCustomLocalClient creates a new local client with a non-default backing
// KeyServiceServer implementation
func NewCustomLocalClient(server KeyServiceServer) LocalClient {
return LocalClient{Server: server}
}
// Decrypt processes a decrypt request locally
// See keyservice/server.go for more details
func (c LocalClient) Decrypt(ctx context.Context,
req *DecryptRequest, opts ...grpc.CallOption) (*DecryptResponse, error) {
return c.Server.Decrypt(ctx, req)
}
// Encrypt processes an encrypt request locally
// See keyservice/server.go for more details
func (c LocalClient) Encrypt(ctx context.Context,
req *EncryptRequest, opts ...grpc.CallOption) (*EncryptResponse, error) {
return c.Server.Encrypt(ctx, req)
}
0707010000007B000081A400000000000000000000000162794F930000070F000000000000000000000000000000000000002400000000sops-3.7.3/keyservice/keyservice.go/*
Package keyservice implements a gRPC API that can be used by SOPS to encrypt and decrypt the data key using remote
master keys.
*/
package keyservice
import (
"fmt"
"go.mozilla.org/sops/v3/age"
"go.mozilla.org/sops/v3/azkv"
"go.mozilla.org/sops/v3/gcpkms"
"go.mozilla.org/sops/v3/hcvault"
"go.mozilla.org/sops/v3/keys"
"go.mozilla.org/sops/v3/kms"
"go.mozilla.org/sops/v3/pgp"
)
// KeyFromMasterKey converts a SOPS internal MasterKey to an RPC Key that can be serialized with Protocol Buffers
func KeyFromMasterKey(mk keys.MasterKey) Key {
switch mk := mk.(type) {
case *pgp.MasterKey:
return Key{
KeyType: &Key_PgpKey{
PgpKey: &PgpKey{
Fingerprint: mk.Fingerprint,
},
},
}
case *gcpkms.MasterKey:
return Key{
KeyType: &Key_GcpKmsKey{
GcpKmsKey: &GcpKmsKey{
ResourceId: mk.ResourceID,
},
},
}
case *hcvault.MasterKey:
return Key{
KeyType: &Key_VaultKey{
VaultKey: &VaultKey{
VaultAddress: mk.VaultAddress,
EnginePath: mk.EnginePath,
KeyName: mk.KeyName,
},
},
}
case *kms.MasterKey:
ctx := make(map[string]string)
for k, v := range mk.EncryptionContext {
ctx[k] = *v
}
return Key{
KeyType: &Key_KmsKey{
KmsKey: &KmsKey{
Arn: mk.Arn,
Role: mk.Role,
Context: ctx,
AwsProfile: mk.AwsProfile,
},
},
}
case *azkv.MasterKey:
return Key{
KeyType: &Key_AzureKeyvaultKey{
AzureKeyvaultKey: &AzureKeyVaultKey{
VaultUrl: mk.VaultURL,
Name: mk.Name,
Version: mk.Version,
},
},
}
case *age.MasterKey:
return Key{
KeyType: &Key_AgeKey{
AgeKey: &AgeKey{
Recipient: mk.Recipient,
},
},
}
default:
panic(fmt.Sprintf("Tried to convert unknown MasterKey type %T to keyservice.Key", mk))
}
}
0707010000007C000081A400000000000000000000000162794F93000089E3000000000000000000000000000000000000002700000000sops-3.7.3/keyservice/keyservice.pb.go// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.23.0
// protoc v3.13.0
// source: keyservice/keyservice.proto
package keyservice
import (
context "context"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Key struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Types that are assignable to KeyType:
// *Key_KmsKey
// *Key_PgpKey
// *Key_GcpKmsKey
// *Key_AzureKeyvaultKey
// *Key_VaultKey
// *Key_AgeKey
KeyType isKey_KeyType `protobuf_oneof:"key_type"`
}
func (x *Key) Reset() {
*x = Key{}
if protoimpl.UnsafeEnabled {
mi := &file_keyservice_keyservice_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Key) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Key) ProtoMessage() {}
func (x *Key) ProtoReflect() protoreflect.Message {
mi := &file_keyservice_keyservice_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Key.ProtoReflect.Descriptor instead.
func (*Key) Descriptor() ([]byte, []int) {
return file_keyservice_keyservice_proto_rawDescGZIP(), []int{0}
}
func (m *Key) GetKeyType() isKey_KeyType {
if m != nil {
return m.KeyType
}
return nil
}
func (x *Key) GetKmsKey() *KmsKey {
if x, ok := x.GetKeyType().(*Key_KmsKey); ok {
return x.KmsKey
}
return nil
}
func (x *Key) GetPgpKey() *PgpKey {
if x, ok := x.GetKeyType().(*Key_PgpKey); ok {
return x.PgpKey
}
return nil
}
func (x *Key) GetGcpKmsKey() *GcpKmsKey {
if x, ok := x.GetKeyType().(*Key_GcpKmsKey); ok {
return x.GcpKmsKey
}
return nil
}
func (x *Key) GetAzureKeyvaultKey() *AzureKeyVaultKey {
if x, ok := x.GetKeyType().(*Key_AzureKeyvaultKey); ok {
return x.AzureKeyvaultKey
}
return nil
}
func (x *Key) GetVaultKey() *VaultKey {
if x, ok := x.GetKeyType().(*Key_VaultKey); ok {
return x.VaultKey
}
return nil
}
func (x *Key) GetAgeKey() *AgeKey {
if x, ok := x.GetKeyType().(*Key_AgeKey); ok {
return x.AgeKey
}
return nil
}
type isKey_KeyType interface {
isKey_KeyType()
}
type Key_KmsKey struct {
KmsKey *KmsKey `protobuf:"bytes,1,opt,name=kms_key,json=kmsKey,proto3,oneof"`
}
type Key_PgpKey struct {
PgpKey *PgpKey `protobuf:"bytes,2,opt,name=pgp_key,json=pgpKey,proto3,oneof"`
}
type Key_GcpKmsKey struct {
GcpKmsKey *GcpKmsKey `protobuf:"bytes,3,opt,name=gcp_kms_key,json=gcpKmsKey,proto3,oneof"`
}
type Key_AzureKeyvaultKey struct {
AzureKeyvaultKey *AzureKeyVaultKey `protobuf:"bytes,4,opt,name=azure_keyvault_key,json=azureKeyvaultKey,proto3,oneof"`
}
type Key_VaultKey struct {
VaultKey *VaultKey `protobuf:"bytes,5,opt,name=vault_key,json=vaultKey,proto3,oneof"`
}
type Key_AgeKey struct {
AgeKey *AgeKey `protobuf:"bytes,6,opt,name=age_key,json=ageKey,proto3,oneof"`
}
func (*Key_KmsKey) isKey_KeyType() {}
func (*Key_PgpKey) isKey_KeyType() {}
func (*Key_GcpKmsKey) isKey_KeyType() {}
func (*Key_AzureKeyvaultKey) isKey_KeyType() {}
func (*Key_VaultKey) isKey_KeyType() {}
func (*Key_AgeKey) isKey_KeyType() {}
type PgpKey struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Fingerprint string `protobuf:"bytes,1,opt,name=fingerprint,proto3" json:"fingerprint,omitempty"`
}
func (x *PgpKey) Reset() {
*x = PgpKey{}
if protoimpl.UnsafeEnabled {
mi := &file_keyservice_keyservice_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PgpKey) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PgpKey) ProtoMessage() {}
func (x *PgpKey) ProtoReflect() protoreflect.Message {
mi := &file_keyservice_keyservice_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PgpKey.ProtoReflect.Descriptor instead.
func (*PgpKey) Descriptor() ([]byte, []int) {
return file_keyservice_keyservice_proto_rawDescGZIP(), []int{1}
}
func (x *PgpKey) GetFingerprint() string {
if x != nil {
return x.Fingerprint
}
return ""
}
type KmsKey struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Arn string `protobuf:"bytes,1,opt,name=arn,proto3" json:"arn,omitempty"`
Role string `protobuf:"bytes,2,opt,name=role,proto3" json:"role,omitempty"`
Context map[string]string `protobuf:"bytes,3,rep,name=context,proto3" json:"context,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
AwsProfile string `protobuf:"bytes,4,opt,name=aws_profile,json=awsProfile,proto3" json:"aws_profile,omitempty"`
}
func (x *KmsKey) Reset() {
*x = KmsKey{}
if protoimpl.UnsafeEnabled {
mi := &file_keyservice_keyservice_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *KmsKey) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*KmsKey) ProtoMessage() {}
func (x *KmsKey) ProtoReflect() protoreflect.Message {
mi := &file_keyservice_keyservice_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use KmsKey.ProtoReflect.Descriptor instead.
func (*KmsKey) Descriptor() ([]byte, []int) {
return file_keyservice_keyservice_proto_rawDescGZIP(), []int{2}
}
func (x *KmsKey) GetArn() string {
if x != nil {
return x.Arn
}
return ""
}
func (x *KmsKey) GetRole() string {
if x != nil {
return x.Role
}
return ""
}
func (x *KmsKey) GetContext() map[string]string {
if x != nil {
return x.Context
}
return nil
}
func (x *KmsKey) GetAwsProfile() string {
if x != nil {
return x.AwsProfile
}
return ""
}
type GcpKmsKey struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ResourceId string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"`
}
func (x *GcpKmsKey) Reset() {
*x = GcpKmsKey{}
if protoimpl.UnsafeEnabled {
mi := &file_keyservice_keyservice_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GcpKmsKey) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GcpKmsKey) ProtoMessage() {}
func (x *GcpKmsKey) ProtoReflect() protoreflect.Message {
mi := &file_keyservice_keyservice_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GcpKmsKey.ProtoReflect.Descriptor instead.
func (*GcpKmsKey) Descriptor() ([]byte, []int) {
return file_keyservice_keyservice_proto_rawDescGZIP(), []int{3}
}
func (x *GcpKmsKey) GetResourceId() string {
if x != nil {
return x.ResourceId
}
return ""
}
type VaultKey struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
VaultAddress string `protobuf:"bytes,1,opt,name=vault_address,json=vaultAddress,proto3" json:"vault_address,omitempty"`
EnginePath string `protobuf:"bytes,2,opt,name=engine_path,json=enginePath,proto3" json:"engine_path,omitempty"`
KeyName string `protobuf:"bytes,3,opt,name=key_name,json=keyName,proto3" json:"key_name,omitempty"`
}
func (x *VaultKey) Reset() {
*x = VaultKey{}
if protoimpl.UnsafeEnabled {
mi := &file_keyservice_keyservice_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *VaultKey) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*VaultKey) ProtoMessage() {}
func (x *VaultKey) ProtoReflect() protoreflect.Message {
mi := &file_keyservice_keyservice_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use VaultKey.ProtoReflect.Descriptor instead.
func (*VaultKey) Descriptor() ([]byte, []int) {
return file_keyservice_keyservice_proto_rawDescGZIP(), []int{4}
}
func (x *VaultKey) GetVaultAddress() string {
if x != nil {
return x.VaultAddress
}
return ""
}
func (x *VaultKey) GetEnginePath() string {
if x != nil {
return x.EnginePath
}
return ""
}
func (x *VaultKey) GetKeyName() string {
if x != nil {
return x.KeyName
}
return ""
}
type AzureKeyVaultKey struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
VaultUrl string `protobuf:"bytes,1,opt,name=vault_url,json=vaultUrl,proto3" json:"vault_url,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"`
}
func (x *AzureKeyVaultKey) Reset() {
*x = AzureKeyVaultKey{}
if protoimpl.UnsafeEnabled {
mi := &file_keyservice_keyservice_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AzureKeyVaultKey) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AzureKeyVaultKey) ProtoMessage() {}
func (x *AzureKeyVaultKey) ProtoReflect() protoreflect.Message {
mi := &file_keyservice_keyservice_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AzureKeyVaultKey.ProtoReflect.Descriptor instead.
func (*AzureKeyVaultKey) Descriptor() ([]byte, []int) {
return file_keyservice_keyservice_proto_rawDescGZIP(), []int{5}
}
func (x *AzureKeyVaultKey) GetVaultUrl() string {
if x != nil {
return x.VaultUrl
}
return ""
}
func (x *AzureKeyVaultKey) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *AzureKeyVaultKey) GetVersion() string {
if x != nil {
return x.Version
}
return ""
}
type AgeKey struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Recipient string `protobuf:"bytes,1,opt,name=recipient,proto3" json:"recipient,omitempty"`
}
func (x *AgeKey) Reset() {
*x = AgeKey{}
if protoimpl.UnsafeEnabled {
mi := &file_keyservice_keyservice_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AgeKey) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AgeKey) ProtoMessage() {}
func (x *AgeKey) ProtoReflect() protoreflect.Message {
mi := &file_keyservice_keyservice_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AgeKey.ProtoReflect.Descriptor instead.
func (*AgeKey) Descriptor() ([]byte, []int) {
return file_keyservice_keyservice_proto_rawDescGZIP(), []int{6}
}
func (x *AgeKey) GetRecipient() string {
if x != nil {
return x.Recipient
}
return ""
}
type EncryptRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Key *Key `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Plaintext []byte `protobuf:"bytes,2,opt,name=plaintext,proto3" json:"plaintext,omitempty"`
}
func (x *EncryptRequest) Reset() {
*x = EncryptRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_keyservice_keyservice_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *EncryptRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*EncryptRequest) ProtoMessage() {}
func (x *EncryptRequest) ProtoReflect() protoreflect.Message {
mi := &file_keyservice_keyservice_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use EncryptRequest.ProtoReflect.Descriptor instead.
func (*EncryptRequest) Descriptor() ([]byte, []int) {
return file_keyservice_keyservice_proto_rawDescGZIP(), []int{7}
}
func (x *EncryptRequest) GetKey() *Key {
if x != nil {
return x.Key
}
return nil
}
func (x *EncryptRequest) GetPlaintext() []byte {
if x != nil {
return x.Plaintext
}
return nil
}
type EncryptResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Ciphertext []byte `protobuf:"bytes,1,opt,name=ciphertext,proto3" json:"ciphertext,omitempty"`
}
func (x *EncryptResponse) Reset() {
*x = EncryptResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_keyservice_keyservice_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *EncryptResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*EncryptResponse) ProtoMessage() {}
func (x *EncryptResponse) ProtoReflect() protoreflect.Message {
mi := &file_keyservice_keyservice_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use EncryptResponse.ProtoReflect.Descriptor instead.
func (*EncryptResponse) Descriptor() ([]byte, []int) {
return file_keyservice_keyservice_proto_rawDescGZIP(), []int{8}
}
func (x *EncryptResponse) GetCiphertext() []byte {
if x != nil {
return x.Ciphertext
}
return nil
}
type DecryptRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Key *Key `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Ciphertext []byte `protobuf:"bytes,2,opt,name=ciphertext,proto3" json:"ciphertext,omitempty"`
}
func (x *DecryptRequest) Reset() {
*x = DecryptRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_keyservice_keyservice_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DecryptRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DecryptRequest) ProtoMessage() {}
func (x *DecryptRequest) ProtoReflect() protoreflect.Message {
mi := &file_keyservice_keyservice_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DecryptRequest.ProtoReflect.Descriptor instead.
func (*DecryptRequest) Descriptor() ([]byte, []int) {
return file_keyservice_keyservice_proto_rawDescGZIP(), []int{9}
}
func (x *DecryptRequest) GetKey() *Key {
if x != nil {
return x.Key
}
return nil
}
func (x *DecryptRequest) GetCiphertext() []byte {
if x != nil {
return x.Ciphertext
}
return nil
}
type DecryptResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Plaintext []byte `protobuf:"bytes,1,opt,name=plaintext,proto3" json:"plaintext,omitempty"`
}
func (x *DecryptResponse) Reset() {
*x = DecryptResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_keyservice_keyservice_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DecryptResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DecryptResponse) ProtoMessage() {}
func (x *DecryptResponse) ProtoReflect() protoreflect.Message {
mi := &file_keyservice_keyservice_proto_msgTypes[10]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DecryptResponse.ProtoReflect.Descriptor instead.
func (*DecryptResponse) Descriptor() ([]byte, []int) {
return file_keyservice_keyservice_proto_rawDescGZIP(), []int{10}
}
func (x *DecryptResponse) GetPlaintext() []byte {
if x != nil {
return x.Plaintext
}
return nil
}
var File_keyservice_keyservice_proto protoreflect.FileDescriptor
var file_keyservice_keyservice_proto_rawDesc = []byte{
0x0a, 0x1b, 0x6b, 0x65, 0x79, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x6b, 0x65, 0x79,
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x98, 0x02,
0x0a, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x07, 0x6b, 0x6d, 0x73, 0x5f, 0x6b, 0x65, 0x79,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x48,
0x00, 0x52, 0x06, 0x6b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x07, 0x70, 0x67, 0x70,
0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x50, 0x67, 0x70,
0x4b, 0x65, 0x79, 0x48, 0x00, 0x52, 0x06, 0x70, 0x67, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x2c, 0x0a,
0x0b, 0x67, 0x63, 0x70, 0x5f, 0x6b, 0x6d, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x47, 0x63, 0x70, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x48, 0x00,
0x52, 0x09, 0x67, 0x63, 0x70, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x41, 0x0a, 0x12, 0x61,
0x7a, 0x75, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x6b, 0x65,
0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x4b,
0x65, 0x79, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x48, 0x00, 0x52, 0x10, 0x61, 0x7a,
0x75, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x28,
0x0a, 0x09, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x09, 0x2e, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x48, 0x00, 0x52, 0x08,
0x76, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x07, 0x61, 0x67, 0x65, 0x5f,
0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x41, 0x67, 0x65, 0x4b,
0x65, 0x79, 0x48, 0x00, 0x52, 0x06, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x0a, 0x0a, 0x08,
0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2a, 0x0a, 0x06, 0x50, 0x67, 0x70, 0x4b,
0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e,
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70,
0x72, 0x69, 0x6e, 0x74, 0x22, 0xbb, 0x01, 0x0a, 0x06, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12,
0x10, 0x0a, 0x03, 0x61, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x72,
0x6e, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x2e,
0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x63, 0x6f,
0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x77, 0x73, 0x5f, 0x70, 0x72, 0x6f,
0x66, 0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x77, 0x73, 0x50,
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x1a, 0x3a, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78,
0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
0x38, 0x01, 0x22, 0x2c, 0x0a, 0x09, 0x47, 0x63, 0x70, 0x4b, 0x6d, 0x73, 0x4b, 0x65, 0x79, 0x12,
0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64,
0x22, 0x6b, 0x0a, 0x08, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d,
0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0c, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x50, 0x61,
0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03,
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x0a,
0x10, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x75, 0x6c, 0x74, 0x4b, 0x65,
0x79, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x12,
0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x26, 0x0a, 0x06,
0x41, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69,
0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70,
0x69, 0x65, 0x6e, 0x74, 0x22, 0x46, 0x0a, 0x0e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x04, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1c,
0x0a, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x31, 0x0a, 0x0f,
0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22,
0x48, 0x0a, 0x0e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x12, 0x16, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x04,
0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70,
0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63,
0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, 0x2f, 0x0a, 0x0f, 0x44, 0x65, 0x63,
0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09,
0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x32, 0x6c, 0x0a, 0x0a, 0x4b, 0x65,
0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x72,
0x79, 0x70, 0x74, 0x12, 0x0f, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x07, 0x44, 0x65, 0x63, 0x72,
0x79, 0x70, 0x74, 0x12, 0x0f, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_keyservice_keyservice_proto_rawDescOnce sync.Once
file_keyservice_keyservice_proto_rawDescData = file_keyservice_keyservice_proto_rawDesc
)
func file_keyservice_keyservice_proto_rawDescGZIP() []byte {
file_keyservice_keyservice_proto_rawDescOnce.Do(func() {
file_keyservice_keyservice_proto_rawDescData = protoimpl.X.CompressGZIP(file_keyservice_keyservice_proto_rawDescData)
})
return file_keyservice_keyservice_proto_rawDescData
}
var file_keyservice_keyservice_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
var file_keyservice_keyservice_proto_goTypes = []interface{}{
(*Key)(nil), // 0: Key
(*PgpKey)(nil), // 1: PgpKey
(*KmsKey)(nil), // 2: KmsKey
(*GcpKmsKey)(nil), // 3: GcpKmsKey
(*VaultKey)(nil), // 4: VaultKey
(*AzureKeyVaultKey)(nil), // 5: AzureKeyVaultKey
(*AgeKey)(nil), // 6: AgeKey
(*EncryptRequest)(nil), // 7: EncryptRequest
(*EncryptResponse)(nil), // 8: EncryptResponse
(*DecryptRequest)(nil), // 9: DecryptRequest
(*DecryptResponse)(nil), // 10: DecryptResponse
nil, // 11: KmsKey.ContextEntry
}
var file_keyservice_keyservice_proto_depIdxs = []int32{
2, // 0: Key.kms_key:type_name -> KmsKey
1, // 1: Key.pgp_key:type_name -> PgpKey
3, // 2: Key.gcp_kms_key:type_name -> GcpKmsKey
5, // 3: Key.azure_keyvault_key:type_name -> AzureKeyVaultKey
4, // 4: Key.vault_key:type_name -> VaultKey
6, // 5: Key.age_key:type_name -> AgeKey
11, // 6: KmsKey.context:type_name -> KmsKey.ContextEntry
0, // 7: EncryptRequest.key:type_name -> Key
0, // 8: DecryptRequest.key:type_name -> Key
7, // 9: KeyService.Encrypt:input_type -> EncryptRequest
9, // 10: KeyService.Decrypt:input_type -> DecryptRequest
8, // 11: KeyService.Encrypt:output_type -> EncryptResponse
10, // 12: KeyService.Decrypt:output_type -> DecryptResponse
11, // [11:13] is the sub-list for method output_type
9, // [9:11] is the sub-list for method input_type
9, // [9:9] is the sub-list for extension type_name
9, // [9:9] is the sub-list for extension extendee
0, // [0:9] is the sub-list for field type_name
}
func init() { file_keyservice_keyservice_proto_init() }
func file_keyservice_keyservice_proto_init() {
if File_keyservice_keyservice_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_keyservice_keyservice_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Key); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_keyservice_keyservice_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PgpKey); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_keyservice_keyservice_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*KmsKey); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_keyservice_keyservice_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GcpKmsKey); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_keyservice_keyservice_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*VaultKey); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_keyservice_keyservice_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AzureKeyVaultKey); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_keyservice_keyservice_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AgeKey); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_keyservice_keyservice_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*EncryptRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_keyservice_keyservice_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*EncryptResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_keyservice_keyservice_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DecryptRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_keyservice_keyservice_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DecryptResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_keyservice_keyservice_proto_msgTypes[0].OneofWrappers = []interface{}{
(*Key_KmsKey)(nil),
(*Key_PgpKey)(nil),
(*Key_GcpKmsKey)(nil),
(*Key_AzureKeyvaultKey)(nil),
(*Key_VaultKey)(nil),
(*Key_AgeKey)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_keyservice_keyservice_proto_rawDesc,
NumEnums: 0,
NumMessages: 12,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_keyservice_keyservice_proto_goTypes,
DependencyIndexes: file_keyservice_keyservice_proto_depIdxs,
MessageInfos: file_keyservice_keyservice_proto_msgTypes,
}.Build()
File_keyservice_keyservice_proto = out.File
file_keyservice_keyservice_proto_rawDesc = nil
file_keyservice_keyservice_proto_goTypes = nil
file_keyservice_keyservice_proto_depIdxs = nil
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConnInterface
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion6
// KeyServiceClient is the client API for KeyService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type KeyServiceClient interface {
Encrypt(ctx context.Context, in *EncryptRequest, opts ...grpc.CallOption) (*EncryptResponse, error)
Decrypt(ctx context.Context, in *DecryptRequest, opts ...grpc.CallOption) (*DecryptResponse, error)
}
type keyServiceClient struct {
cc grpc.ClientConnInterface
}
func NewKeyServiceClient(cc grpc.ClientConnInterface) KeyServiceClient {
return &keyServiceClient{cc}
}
func (c *keyServiceClient) Encrypt(ctx context.Context, in *EncryptRequest, opts ...grpc.CallOption) (*EncryptResponse, error) {
out := new(EncryptResponse)
err := c.cc.Invoke(ctx, "/KeyService/Encrypt", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *keyServiceClient) Decrypt(ctx context.Context, in *DecryptRequest, opts ...grpc.CallOption) (*DecryptResponse, error) {
out := new(DecryptResponse)
err := c.cc.Invoke(ctx, "/KeyService/Decrypt", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// KeyServiceServer is the server API for KeyService service.
type KeyServiceServer interface {
Encrypt(context.Context, *EncryptRequest) (*EncryptResponse, error)
Decrypt(context.Context, *DecryptRequest) (*DecryptResponse, error)
}
// UnimplementedKeyServiceServer can be embedded to have forward compatible implementations.
type UnimplementedKeyServiceServer struct {
}
func (*UnimplementedKeyServiceServer) Encrypt(context.Context, *EncryptRequest) (*EncryptResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Encrypt not implemented")
}
func (*UnimplementedKeyServiceServer) Decrypt(context.Context, *DecryptRequest) (*DecryptResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Decrypt not implemented")
}
func RegisterKeyServiceServer(s *grpc.Server, srv KeyServiceServer) {
s.RegisterService(&_KeyService_serviceDesc, srv)
}
func _KeyService_Encrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(EncryptRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(KeyServiceServer).Encrypt(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/KeyService/Encrypt",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(KeyServiceServer).Encrypt(ctx, req.(*EncryptRequest))
}
return interceptor(ctx, in, info, handler)
}
func _KeyService_Decrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DecryptRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(KeyServiceServer).Decrypt(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/KeyService/Decrypt",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(KeyServiceServer).Decrypt(ctx, req.(*DecryptRequest))
}
return interceptor(ctx, in, info, handler)
}
var _KeyService_serviceDesc = grpc.ServiceDesc{
ServiceName: "KeyService",
HandlerType: (*KeyServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Encrypt",
Handler: _KeyService_Encrypt_Handler,
},
{
MethodName: "Decrypt",
Handler: _KeyService_Decrypt_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "keyservice/keyservice.proto",
}
0707010000007D000081A400000000000000000000000162794F9300000409000000000000000000000000000000000000002700000000sops-3.7.3/keyservice/keyservice.protosyntax = "proto3";
message Key {
oneof key_type {
KmsKey kms_key = 1;
PgpKey pgp_key = 2;
GcpKmsKey gcp_kms_key = 3;
AzureKeyVaultKey azure_keyvault_key = 4;
VaultKey vault_key = 5;
AgeKey age_key = 6;
}
}
message PgpKey {
string fingerprint = 1;
}
message KmsKey {
string arn = 1;
string role = 2;
map<string, string> context = 3;
string aws_profile = 4;
}
message GcpKmsKey {
string resource_id = 1;
}
message VaultKey {
string vault_address = 1;
string engine_path = 2;
string key_name = 3;
}
message AzureKeyVaultKey {
string vault_url = 1;
string name = 2;
string version = 3;
}
message AgeKey {
string recipient = 1;
}
message EncryptRequest {
Key key = 1;
bytes plaintext = 2;
}
message EncryptResponse {
bytes ciphertext = 1;
}
message DecryptRequest {
Key key = 1;
bytes ciphertext = 2;
}
message DecryptResponse {
bytes plaintext = 1;
}
service KeyService {
rpc Encrypt (EncryptRequest) returns (EncryptResponse) {}
rpc Decrypt (DecryptRequest) returns (DecryptResponse) {}
}
0707010000007E000081A400000000000000000000000162794F93000022F9000000000000000000000000000000000000002000000000sops-3.7.3/keyservice/server.gopackage keyservice
import (
"fmt"
"go.mozilla.org/sops/v3/age"
"go.mozilla.org/sops/v3/azkv"
"go.mozilla.org/sops/v3/gcpkms"
"go.mozilla.org/sops/v3/hcvault"
"go.mozilla.org/sops/v3/kms"
"go.mozilla.org/sops/v3/pgp"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Server is a key service server that uses SOPS MasterKeys to fulfill requests
type Server struct {
// Prompt indicates whether the server should prompt before decrypting or encrypting data
Prompt bool
}
func (ks *Server) encryptWithPgp(key *PgpKey, plaintext []byte) ([]byte, error) {
pgpKey := pgp.NewMasterKeyFromFingerprint(key.Fingerprint)
err := pgpKey.Encrypt(plaintext)
if err != nil {
return nil, err
}
return []byte(pgpKey.EncryptedKey), nil
}
func (ks *Server) encryptWithKms(key *KmsKey, plaintext []byte) ([]byte, error) {
kmsKey := kmsKeyToMasterKey(key)
err := kmsKey.Encrypt(plaintext)
if err != nil {
return nil, err
}
return []byte(kmsKey.EncryptedKey), nil
}
func (ks *Server) encryptWithGcpKms(key *GcpKmsKey, plaintext []byte) ([]byte, error) {
gcpKmsKey := gcpkms.MasterKey{
ResourceID: key.ResourceId,
}
err := gcpKmsKey.Encrypt(plaintext)
if err != nil {
return nil, err
}
return []byte(gcpKmsKey.EncryptedKey), nil
}
func (ks *Server) encryptWithAzureKeyVault(key *AzureKeyVaultKey, plaintext []byte) ([]byte, error) {
azkvKey := azkv.MasterKey{
VaultURL: key.VaultUrl,
Name: key.Name,
Version: key.Version,
}
err := azkvKey.Encrypt(plaintext)
if err != nil {
return nil, err
}
return []byte(azkvKey.EncryptedKey), nil
}
func (ks *Server) encryptWithVault(key *VaultKey, plaintext []byte) ([]byte, error) {
vaultKey := hcvault.MasterKey{
VaultAddress: key.VaultAddress,
EnginePath: key.EnginePath,
KeyName: key.KeyName,
}
err := vaultKey.Encrypt(plaintext)
if err != nil {
return nil, err
}
return []byte(vaultKey.EncryptedKey), nil
}
func (ks *Server) encryptWithAge(key *AgeKey, plaintext []byte) ([]byte, error) {
ageKey := age.MasterKey{
Recipient: key.Recipient,
}
if err := ageKey.Encrypt(plaintext); err != nil {
return nil, err
}
return []byte(ageKey.EncryptedKey), nil
}
func (ks *Server) decryptWithPgp(key *PgpKey, ciphertext []byte) ([]byte, error) {
pgpKey := pgp.NewMasterKeyFromFingerprint(key.Fingerprint)
pgpKey.EncryptedKey = string(ciphertext)
plaintext, err := pgpKey.Decrypt()
return []byte(plaintext), err
}
func (ks *Server) decryptWithKms(key *KmsKey, ciphertext []byte) ([]byte, error) {
kmsKey := kmsKeyToMasterKey(key)
kmsKey.EncryptedKey = string(ciphertext)
plaintext, err := kmsKey.Decrypt()
return []byte(plaintext), err
}
func (ks *Server) decryptWithGcpKms(key *GcpKmsKey, ciphertext []byte) ([]byte, error) {
gcpKmsKey := gcpkms.MasterKey{
ResourceID: key.ResourceId,
}
gcpKmsKey.EncryptedKey = string(ciphertext)
plaintext, err := gcpKmsKey.Decrypt()
return []byte(plaintext), err
}
func (ks *Server) decryptWithAzureKeyVault(key *AzureKeyVaultKey, ciphertext []byte) ([]byte, error) {
azkvKey := azkv.MasterKey{
VaultURL: key.VaultUrl,
Name: key.Name,
Version: key.Version,
}
azkvKey.EncryptedKey = string(ciphertext)
plaintext, err := azkvKey.Decrypt()
return []byte(plaintext), err
}
func (ks *Server) decryptWithVault(key *VaultKey, ciphertext []byte) ([]byte, error) {
vaultKey := hcvault.MasterKey{
VaultAddress: key.VaultAddress,
EnginePath: key.EnginePath,
KeyName: key.KeyName,
}
vaultKey.EncryptedKey = string(ciphertext)
plaintext, err := vaultKey.Decrypt()
return []byte(plaintext), err
}
func (ks *Server) decryptWithAge(key *AgeKey, ciphertext []byte) ([]byte, error) {
ageKey := age.MasterKey{
Recipient: key.Recipient,
}
ageKey.EncryptedKey = string(ciphertext)
plaintext, err := ageKey.Decrypt()
return []byte(plaintext), err
}
// Encrypt takes an encrypt request and encrypts the provided plaintext with the provided key, returning the encrypted
// result
func (ks Server) Encrypt(ctx context.Context,
req *EncryptRequest) (*EncryptResponse, error) {
key := *req.Key
var response *EncryptResponse
switch k := key.KeyType.(type) {
case *Key_PgpKey:
ciphertext, err := ks.encryptWithPgp(k.PgpKey, req.Plaintext)
if err != nil {
return nil, err
}
response = &EncryptResponse{
Ciphertext: ciphertext,
}
case *Key_KmsKey:
ciphertext, err := ks.encryptWithKms(k.KmsKey, req.Plaintext)
if err != nil {
return nil, err
}
response = &EncryptResponse{
Ciphertext: ciphertext,
}
case *Key_GcpKmsKey:
ciphertext, err := ks.encryptWithGcpKms(k.GcpKmsKey, req.Plaintext)
if err != nil {
return nil, err
}
response = &EncryptResponse{
Ciphertext: ciphertext,
}
case *Key_AzureKeyvaultKey:
ciphertext, err := ks.encryptWithAzureKeyVault(k.AzureKeyvaultKey, req.Plaintext)
if err != nil {
return nil, err
}
response = &EncryptResponse{
Ciphertext: ciphertext,
}
case *Key_VaultKey:
ciphertext, err := ks.encryptWithVault(k.VaultKey, req.Plaintext)
if err != nil {
return nil, err
}
response = &EncryptResponse{
Ciphertext: ciphertext,
}
case *Key_AgeKey:
ciphertext, err := ks.encryptWithAge(k.AgeKey, req.Plaintext)
if err != nil {
return nil, err
}
response = &EncryptResponse{
Ciphertext: ciphertext,
}
case nil:
return nil, status.Errorf(codes.NotFound, "Must provide a key")
default:
return nil, status.Errorf(codes.NotFound, "Unknown key type")
}
if ks.Prompt {
err := ks.prompt(key, "encrypt")
if err != nil {
return nil, err
}
}
return response, nil
}
func keyToString(key Key) string {
switch k := key.KeyType.(type) {
case *Key_PgpKey:
return fmt.Sprintf("PGP key with fingerprint %s", k.PgpKey.Fingerprint)
case *Key_KmsKey:
return fmt.Sprintf("AWS KMS key with ARN %s", k.KmsKey.Arn)
case *Key_GcpKmsKey:
return fmt.Sprintf("GCP KMS key with resource ID %s", k.GcpKmsKey.ResourceId)
case *Key_AzureKeyvaultKey:
return fmt.Sprintf("Azure Key Vault key with URL %s/keys/%s/%s", k.AzureKeyvaultKey.VaultUrl, k.AzureKeyvaultKey.Name, k.AzureKeyvaultKey.Version)
case *Key_VaultKey:
return fmt.Sprintf("Hashicorp Vault key with URI %s/v1/%s/keys/%s", k.VaultKey.VaultAddress, k.VaultKey.EnginePath, k.VaultKey.KeyName)
default:
return fmt.Sprintf("Unknown key type")
}
}
func (ks Server) prompt(key Key, requestType string) error {
keyString := keyToString(key)
var response string
for response != "y" && response != "n" {
fmt.Printf("\nReceived %s request using %s. Respond to request? (y/n): ", requestType, keyString)
_, err := fmt.Scanln(&response)
if err != nil {
return err
}
}
if response == "n" {
return grpc.Errorf(codes.PermissionDenied, "Request rejected by user")
}
return nil
}
// Decrypt takes a decrypt request and decrypts the provided ciphertext with the provided key, returning the decrypted
// result
func (ks Server) Decrypt(ctx context.Context,
req *DecryptRequest) (*DecryptResponse, error) {
key := *req.Key
var response *DecryptResponse
switch k := key.KeyType.(type) {
case *Key_PgpKey:
plaintext, err := ks.decryptWithPgp(k.PgpKey, req.Ciphertext)
if err != nil {
return nil, err
}
response = &DecryptResponse{
Plaintext: plaintext,
}
case *Key_KmsKey:
plaintext, err := ks.decryptWithKms(k.KmsKey, req.Ciphertext)
if err != nil {
return nil, err
}
response = &DecryptResponse{
Plaintext: plaintext,
}
case *Key_GcpKmsKey:
plaintext, err := ks.decryptWithGcpKms(k.GcpKmsKey, req.Ciphertext)
if err != nil {
return nil, err
}
response = &DecryptResponse{
Plaintext: plaintext,
}
case *Key_AzureKeyvaultKey:
plaintext, err := ks.decryptWithAzureKeyVault(k.AzureKeyvaultKey, req.Ciphertext)
if err != nil {
return nil, err
}
response = &DecryptResponse{
Plaintext: plaintext,
}
case *Key_VaultKey:
plaintext, err := ks.decryptWithVault(k.VaultKey, req.Ciphertext)
if err != nil {
return nil, err
}
response = &DecryptResponse{
Plaintext: plaintext,
}
case *Key_AgeKey:
plaintext, err := ks.decryptWithAge(k.AgeKey, req.Ciphertext)
if err != nil {
return nil, err
}
response = &DecryptResponse{
Plaintext: plaintext,
}
case nil:
return nil, grpc.Errorf(codes.NotFound, "Must provide a key")
default:
return nil, grpc.Errorf(codes.NotFound, "Unknown key type")
}
if ks.Prompt {
err := ks.prompt(key, "decrypt")
if err != nil {
return nil, err
}
}
return response, nil
}
func kmsKeyToMasterKey(key *KmsKey) kms.MasterKey {
ctx := make(map[string]*string)
for k, v := range key.Context {
value := v // Allocate a new string to prevent the pointer below from referring to only the last iteration value
ctx[k] = &value
}
return kms.MasterKey{
Arn: key.Arn,
Role: key.Role,
EncryptionContext: ctx,
AwsProfile: key.AwsProfile,
}
}
0707010000007F000081A400000000000000000000000162794F9300000A08000000000000000000000000000000000000002500000000sops-3.7.3/keyservice/server_test.gopackage keyservice
import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)
func TestKmsKeyToMasterKey(t *testing.T) {
cases := []struct {
description string
expectedArn string
expectedRole string
expectedCtx map[string]string
expectedAwsProfile string
}{
{
description: "empty context",
expectedArn: "arn:aws:kms:eu-west-1:123456789012:key/d5c90a06-f824-4628-922b-12424571ed4d",
expectedRole: "ExampleRole",
expectedCtx: map[string]string{},
expectedAwsProfile: "",
},
{
description: "context with one key-value pair",
expectedArn: "arn:aws:kms:eu-west-1:123456789012:key/d5c90a06-f824-4628-922b-12424571ed4d",
expectedRole: "",
expectedCtx: map[string]string{
"firstKey": "first value",
},
expectedAwsProfile: "ExampleProfile",
},
{
description: "context with three key-value pairs",
expectedArn: "arn:aws:kms:eu-west-1:123456789012:key/d5c90a06-f824-4628-922b-12424571ed4d",
expectedRole: "",
expectedCtx: map[string]string{
"firstKey": "first value",
"secondKey": "second value",
"thirdKey": "third value",
},
expectedAwsProfile: "",
},
}
for _, c := range cases {
t.Run(c.description, func(t *testing.T) {
inputCtx := make(map[string]string)
for k, v := range c.expectedCtx {
inputCtx[k] = v
}
key := &KmsKey{
Arn: c.expectedArn,
Role: c.expectedRole,
Context: inputCtx,
AwsProfile: c.expectedAwsProfile,
}
masterKey := kmsKeyToMasterKey(key)
foundCtx := masterKey.EncryptionContext
for k := range c.expectedCtx {
require.Containsf(t, foundCtx, k, "Context does not contain expected key '%s'", k)
}
for k := range foundCtx {
require.Containsf(t, c.expectedCtx, k, "Context contains an unexpected key '%s' which cannot be found from expected map", k)
}
for k, expected := range c.expectedCtx {
foundVal := *foundCtx[k]
assert.Equalf(t, expected, foundVal, "Context key '%s' value '%s' does not match expected value '%s'", k, foundVal, expected)
}
assert.Equalf(t, c.expectedArn, masterKey.Arn, "Expected ARN to be '%s', but found '%s'", c.expectedArn, masterKey.Arn)
assert.Equalf(t, c.expectedRole, masterKey.Role, "Expected Role to be '%s', but found '%s'", c.expectedRole, masterKey.Role)
assert.Equalf(t, c.expectedAwsProfile, masterKey.AwsProfile, "Expected AWS profile to be '%s', but found '%s'", c.expectedAwsProfile, masterKey.AwsProfile)
})
}
}
07070100000080000041ED00000000000000000000000362794F9300000000000000000000000000000000000000000000000F00000000sops-3.7.3/kms07070100000081000081A400000000000000000000000162794F930000229C000000000000000000000000000000000000001C00000000sops-3.7.3/kms/keysource.go/*
Package kms contains an implementation of the go.mozilla.org/sops/v3.MasterKey interface that encrypts and decrypts the
data key using AWS KMS with the AWS Go SDK.
*/
package kms //import "go.mozilla.org/sops/v3/kms"
import (
"encoding/base64"
"fmt"
"os"
"regexp"
"strings"
"time"
"go.mozilla.org/sops/v3/logging"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/kms"
"github.com/aws/aws-sdk-go/service/kms/kmsiface"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/sirupsen/logrus"
)
var log *logrus.Logger
func init() {
log = logging.NewLogger("AWSKMS")
}
// this needs to be a global var for unit tests to work (mockKMS redefines
// it in keysource_test.go)
var kmsSvc kmsiface.KMSAPI
var isMocked bool
// MasterKey is a AWS KMS key used to encrypt and decrypt sops' data key.
type MasterKey struct {
Arn string
Role string
EncryptedKey string
CreationDate time.Time
EncryptionContext map[string]*string
AwsProfile string
}
// EncryptedDataKey returns the encrypted data key this master key holds
func (key *MasterKey) EncryptedDataKey() []byte {
return []byte(key.EncryptedKey)
}
// SetEncryptedDataKey sets the encrypted data key for this master key
func (key *MasterKey) SetEncryptedDataKey(enc []byte) {
key.EncryptedKey = string(enc)
}
// Encrypt takes a sops data key, encrypts it with KMS and stores the result in the EncryptedKey field
func (key *MasterKey) Encrypt(dataKey []byte) error {
// isMocked is set by unit test to indicate that the KMS service
// has already been initialized. it's ugly, but it works.
if kmsSvc == nil || !isMocked {
sess, err := key.createSession()
if err != nil {
log.WithField("arn", key.Arn).Info("Encryption failed")
return fmt.Errorf("Failed to create session: %w", err)
}
kmsSvc = kms.New(sess)
}
out, err := kmsSvc.Encrypt(&kms.EncryptInput{Plaintext: dataKey, KeyId: &key.Arn, EncryptionContext: key.EncryptionContext})
if err != nil {
log.WithField("arn", key.Arn).Info("Encryption failed")
return fmt.Errorf("Failed to call KMS encryption service: %w", err)
}
key.EncryptedKey = base64.StdEncoding.EncodeToString(out.CiphertextBlob)
log.WithField("arn", key.Arn).Info("Encryption succeeded")
return nil
}
// EncryptIfNeeded encrypts the provided sops' data key and encrypts it if it hasn't been encrypted yet
func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
if key.EncryptedKey == "" {
return key.Encrypt(dataKey)
}
return nil
}
// Decrypt decrypts the EncryptedKey field with AWS KMS and returns the result.
func (key *MasterKey) Decrypt() ([]byte, error) {
k, err := base64.StdEncoding.DecodeString(key.EncryptedKey)
if err != nil {
log.WithField("arn", key.Arn).Info("Decryption failed")
return nil, fmt.Errorf("Error base64-decoding encrypted data key: %s", err)
}
// isMocked is set by unit test to indicate that the KMS service
// has already been initialized. it's ugly, but it works.
if kmsSvc == nil || !isMocked {
sess, err := key.createSession()
if err != nil {
log.WithField("arn", key.Arn).Info("Decryption failed")
return nil, fmt.Errorf("Error creating AWS session: %w", err)
}
kmsSvc = kms.New(sess)
}
decrypted, err := kmsSvc.Decrypt(&kms.DecryptInput{CiphertextBlob: k, EncryptionContext: key.EncryptionContext})
if err != nil {
log.WithField("arn", key.Arn).Info("Decryption failed")
return nil, fmt.Errorf("Error decrypting key: %w", err)
}
log.WithField("arn", key.Arn).Info("Decryption succeeded")
return decrypted.Plaintext, nil
}
// NeedsRotation returns whether the data key needs to be rotated or not.
func (key *MasterKey) NeedsRotation() bool {
return time.Since(key.CreationDate) > (time.Hour * 24 * 30 * 6)
}
// ToString converts the key to a string representation
func (key *MasterKey) ToString() string {
return key.Arn
}
// NewMasterKey creates a new MasterKey from an ARN, role and context, setting the creation date to the current date
func NewMasterKey(arn string, role string, context map[string]*string) *MasterKey {
return &MasterKey{
Arn: arn,
Role: role,
EncryptionContext: context,
CreationDate: time.Now().UTC(),
}
}
// NewMasterKeyFromArn takes an ARN string and returns a new MasterKey for that ARN
func NewMasterKeyFromArn(arn string, context map[string]*string, awsProfile string) *MasterKey {
k := &MasterKey{}
arn = strings.Replace(arn, " ", "", -1)
roleIndex := strings.Index(arn, "+arn:aws:iam::")
if roleIndex > 0 {
k.Arn = arn[:roleIndex]
k.Role = arn[roleIndex+1:]
} else {
k.Arn = arn
}
k.EncryptionContext = context
k.CreationDate = time.Now().UTC()
k.AwsProfile = awsProfile
return k
}
// MasterKeysFromArnString takes a comma separated list of AWS KMS ARNs and returns a slice of new MasterKeys for those ARNs
func MasterKeysFromArnString(arn string, context map[string]*string, awsProfile string) []*MasterKey {
var keys []*MasterKey
if arn == "" {
return keys
}
for _, s := range strings.Split(arn, ",") {
keys = append(keys, NewMasterKeyFromArn(s, context, awsProfile))
}
return keys
}
func (key MasterKey) createStsSession(config aws.Config, sess *session.Session) (*session.Session, error) {
hostname, err := os.Hostname()
if err != nil {
return nil, err
}
stsRoleSessionNameRe, err := regexp.Compile("[^a-zA-Z0-9=,.@-]+")
if err != nil {
return nil, fmt.Errorf("Failed to compile STS role session name regex: %w", err)
}
sanitizedHostname := stsRoleSessionNameRe.ReplaceAllString(hostname, "")
stsService := sts.New(sess)
name := "sops@" + sanitizedHostname
// Make sure the name is no longer than 64 characters (role session name length limit from AWS)
roleSessionNameLengthLimit := 64
if len(name) >= roleSessionNameLengthLimit {
name = name[:roleSessionNameLengthLimit]
}
out, err := stsService.AssumeRole(&sts.AssumeRoleInput{
RoleArn: &key.Role, RoleSessionName: &name})
if err != nil {
return nil, fmt.Errorf("Failed to assume role %q: %w", key.Role, err)
}
config.Credentials = credentials.NewStaticCredentials(*out.Credentials.AccessKeyId,
*out.Credentials.SecretAccessKey, *out.Credentials.SessionToken)
sess, err = session.NewSession(&config)
if err != nil {
return nil, fmt.Errorf("Failed to create new aws session: %w", err)
}
return sess, nil
}
func (key MasterKey) createSession() (*session.Session, error) {
re := regexp.MustCompile(`^arn:aws[\w-]*:kms:(.+):[0-9]+:(key|alias)/.+$`)
matches := re.FindStringSubmatch(key.Arn)
if matches == nil {
return nil, fmt.Errorf("No valid ARN found in %q", key.Arn)
}
config := aws.Config{Region: aws.String(matches[1])}
opts := session.Options{
Profile: key.AwsProfile,
Config: config,
AssumeRoleTokenProvider: stscreds.StdinTokenProvider,
SharedConfigState: session.SharedConfigEnable,
}
sess, err := session.NewSessionWithOptions(opts)
if err != nil {
return nil, err
}
if key.Role != "" {
return key.createStsSession(config, sess)
}
return sess, nil
}
// ToMap converts the MasterKey to a map for serialization purposes
func (key MasterKey) ToMap() map[string]interface{} {
out := make(map[string]interface{})
out["arn"] = key.Arn
if key.Role != "" {
out["role"] = key.Role
}
out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339)
out["enc"] = key.EncryptedKey
if key.EncryptionContext != nil {
outcontext := make(map[string]string)
for k, v := range key.EncryptionContext {
outcontext[k] = *v
}
out["context"] = outcontext
}
return out
}
// ParseKMSContext takes either a KMS context map or a comma-separated list of KMS context key:value pairs and returns a map
func ParseKMSContext(in interface{}) map[string]*string {
nonStringValueWarning := "Encryption context contains a non-string value, context will not be used"
out := make(map[string]*string)
switch in := in.(type) {
case map[string]interface{}:
if len(in) == 0 {
return nil
}
for k, v := range in {
value, ok := v.(string)
if !ok {
log.Warn(nonStringValueWarning)
return nil
}
out[k] = &value
}
case map[interface{}]interface{}:
if len(in) == 0 {
return nil
}
for k, v := range in {
key, ok := k.(string)
if !ok {
log.Warn(nonStringValueWarning)
return nil
}
value, ok := v.(string)
if !ok {
log.Warn(nonStringValueWarning)
return nil
}
out[key] = &value
}
case string:
if in == "" {
return nil
}
for _, kv := range strings.Split(in, ",") {
kv := strings.Split(kv, ":")
if len(kv) != 2 {
log.Warn(nonStringValueWarning)
return nil
}
out[kv[0]] = &kv[1]
}
}
return out
}
07070100000082000081A400000000000000000000000162794F9300001042000000000000000000000000000000000000002100000000sops-3.7.3/kms/keysource_test.gopackage kms
import (
"bytes"
"testing"
"testing/quick"
"time"
"github.com/aws/aws-sdk-go/service/kms"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"go.mozilla.org/sops/v3/kms/mocks"
)
func TestKMS(t *testing.T) {
mockKMS := &mocks.KMSAPI{}
defer mockKMS.AssertExpectations(t)
kmsSvc = mockKMS
isMocked = true
encryptOutput := &kms.EncryptOutput{}
decryptOutput := &kms.DecryptOutput{}
mockKMS.On("Encrypt", mock.AnythingOfType("*kms.EncryptInput")).Return(encryptOutput, nil).Run(func(args mock.Arguments) {
encryptOutput.CiphertextBlob = args.Get(0).(*kms.EncryptInput).Plaintext
})
mockKMS.On("Decrypt", mock.AnythingOfType("*kms.DecryptInput")).Return(decryptOutput, nil).Run(func(args mock.Arguments) {
decryptOutput.Plaintext = args.Get(0).(*kms.DecryptInput).CiphertextBlob
})
k := MasterKey{Arn: "arn:aws:kms:us-east-1:927034868273:key/e9fc75db-05e9-44c1-9c35-633922bac347", Role: "", EncryptedKey: ""}
f := func(x []byte) bool {
err := k.Encrypt(x)
if err != nil {
log.Println(err)
}
v, err := k.Decrypt()
if err != nil {
log.Println(err)
}
return bytes.Equal(v, x)
}
config := quick.Config{}
if testing.Short() {
config.MaxCount = 10
}
if err := quick.Check(f, &config); err != nil {
t.Error(err)
}
}
func TestKMSKeySourceFromString(t *testing.T) {
s := "arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e+arn:aws:iam::927034868273:role/sops-dev, arn:aws:kms:ap-southeast-1:656532927350:key/9006a8aa-0fa6-4c14-930e-a2dfb916de1d"
ks := MasterKeysFromArnString(s, nil, "foo")
k1 := ks[0]
k2 := ks[1]
expectedArn1 := "arn:aws:kms:us-east-1:656532927350:key/920aff2e-c5f1-4040-943a-047fa387b27e"
expectedRole1 := "arn:aws:iam::927034868273:role/sops-dev"
if k1.Arn != expectedArn1 {
t.Errorf("ARN mismatch. Expected %s, found %s", expectedArn1, k1.Arn)
}
if k1.Role != expectedRole1 {
t.Errorf("Role mismatch. Expected %s, found %s", expectedRole1, k1.Role)
}
expectedArn2 := "arn:aws:kms:ap-southeast-1:656532927350:key/9006a8aa-0fa6-4c14-930e-a2dfb916de1d"
expectedRole2 := ""
if k2.Arn != expectedArn2 {
t.Errorf("ARN mismatch. Expected %s, found %s", expectedArn2, k2.Arn)
}
if k2.Role != expectedRole2 {
t.Errorf("Role mismatch. Expected empty role, found %s.", k2.Role)
}
}
func TestParseEncryptionContext(t *testing.T) {
value1 := "value1"
value2 := "value2"
// map from YAML
var yamlmap = map[interface{}]interface{}{
"key1": value1,
"key2": value2,
}
assert.Equal(t, ParseKMSContext(yamlmap), map[string]*string{
"key1": &value1,
"key2": &value2,
})
assert.Nil(t, ParseKMSContext(map[interface{}]interface{}{}))
assert.Nil(t, ParseKMSContext(map[interface{}]interface{}{
"key1": 1,
}))
assert.Nil(t, ParseKMSContext(map[interface{}]interface{}{
1: "value",
}))
// map from JSON
var jsonmap = map[string]interface{}{
"key1": value1,
"key2": value2,
}
assert.Equal(t, ParseKMSContext(jsonmap), map[string]*string{
"key1": &value1,
"key2": &value2,
})
assert.Nil(t, ParseKMSContext(map[string]interface{}{}))
assert.Nil(t, ParseKMSContext(map[string]interface{}{
"key1": 1,
}))
// sops 2.0.x formatted encryption context as a comma-separated list of key:value pairs
assert.Equal(t, ParseKMSContext("key1:value1,key2:value2"), map[string]*string{
"key1": &value1,
"key2": &value2,
})
assert.Equal(t, ParseKMSContext("key1:value1"), map[string]*string{
"key1": &value1,
})
assert.Nil(t, ParseKMSContext("key1,key2:value2"))
assert.Nil(t, ParseKMSContext("key1"))
}
func TestKeyToMap(t *testing.T) {
value1 := "value1"
value2 := "value2"
key := MasterKey{
CreationDate: time.Date(2016, time.October, 31, 10, 0, 0, 0, time.UTC),
Arn: "foo",
Role: "bar",
EncryptedKey: "this is encrypted",
EncryptionContext: map[string]*string{
"key1": &value1,
"key2": &value2,
},
}
assert.Equal(t, map[string]interface{}{
"arn": "foo",
"role": "bar",
"enc": "this is encrypted",
"created_at": "2016-10-31T10:00:00Z",
"context": map[string]string{
"key1": value1,
"key2": value2,
},
}, key.ToMap())
}
07070100000083000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001500000000sops-3.7.3/kms/mocks07070100000084000081A400000000000000000000000162794F930001B9D8000000000000000000000000000000000000001F00000000sops-3.7.3/kms/mocks/KMSAPI.go// Code generated by mockery v1.0.0. DO NOT EDIT.
package mocks
import (
context "context"
kms "github.com/aws/aws-sdk-go/service/kms"
mock "github.com/stretchr/testify/mock"
request "github.com/aws/aws-sdk-go/aws/request"
)
// KMSAPI is an autogenerated mock type for the KMSAPI type
type KMSAPI struct {
mock.Mock
}
// CancelKeyDeletion provides a mock function with given fields: _a0
func (_m *KMSAPI) CancelKeyDeletion(_a0 *kms.CancelKeyDeletionInput) (*kms.CancelKeyDeletionOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.CancelKeyDeletionOutput
if rf, ok := ret.Get(0).(func(*kms.CancelKeyDeletionInput) *kms.CancelKeyDeletionOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.CancelKeyDeletionOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.CancelKeyDeletionInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CancelKeyDeletionRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) CancelKeyDeletionRequest(_a0 *kms.CancelKeyDeletionInput) (*request.Request, *kms.CancelKeyDeletionOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.CancelKeyDeletionInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.CancelKeyDeletionOutput
if rf, ok := ret.Get(1).(func(*kms.CancelKeyDeletionInput) *kms.CancelKeyDeletionOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.CancelKeyDeletionOutput)
}
}
return r0, r1
}
// CancelKeyDeletionWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) CancelKeyDeletionWithContext(_a0 context.Context, _a1 *kms.CancelKeyDeletionInput, _a2 ...request.Option) (*kms.CancelKeyDeletionOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.CancelKeyDeletionOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.CancelKeyDeletionInput, ...request.Option) *kms.CancelKeyDeletionOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.CancelKeyDeletionOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.CancelKeyDeletionInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ConnectCustomKeyStore provides a mock function with given fields: _a0
func (_m *KMSAPI) ConnectCustomKeyStore(_a0 *kms.ConnectCustomKeyStoreInput) (*kms.ConnectCustomKeyStoreOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.ConnectCustomKeyStoreOutput
if rf, ok := ret.Get(0).(func(*kms.ConnectCustomKeyStoreInput) *kms.ConnectCustomKeyStoreOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ConnectCustomKeyStoreOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.ConnectCustomKeyStoreInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ConnectCustomKeyStoreRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ConnectCustomKeyStoreRequest(_a0 *kms.ConnectCustomKeyStoreInput) (*request.Request, *kms.ConnectCustomKeyStoreOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.ConnectCustomKeyStoreInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.ConnectCustomKeyStoreOutput
if rf, ok := ret.Get(1).(func(*kms.ConnectCustomKeyStoreInput) *kms.ConnectCustomKeyStoreOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.ConnectCustomKeyStoreOutput)
}
}
return r0, r1
}
// ConnectCustomKeyStoreWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ConnectCustomKeyStoreWithContext(_a0 context.Context, _a1 *kms.ConnectCustomKeyStoreInput, _a2 ...request.Option) (*kms.ConnectCustomKeyStoreOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.ConnectCustomKeyStoreOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.ConnectCustomKeyStoreInput, ...request.Option) *kms.ConnectCustomKeyStoreOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ConnectCustomKeyStoreOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.ConnectCustomKeyStoreInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CreateAlias provides a mock function with given fields: _a0
func (_m *KMSAPI) CreateAlias(_a0 *kms.CreateAliasInput) (*kms.CreateAliasOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.CreateAliasOutput
if rf, ok := ret.Get(0).(func(*kms.CreateAliasInput) *kms.CreateAliasOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.CreateAliasOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.CreateAliasInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CreateAliasRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) CreateAliasRequest(_a0 *kms.CreateAliasInput) (*request.Request, *kms.CreateAliasOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.CreateAliasInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.CreateAliasOutput
if rf, ok := ret.Get(1).(func(*kms.CreateAliasInput) *kms.CreateAliasOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.CreateAliasOutput)
}
}
return r0, r1
}
// CreateAliasWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) CreateAliasWithContext(_a0 context.Context, _a1 *kms.CreateAliasInput, _a2 ...request.Option) (*kms.CreateAliasOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.CreateAliasOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.CreateAliasInput, ...request.Option) *kms.CreateAliasOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.CreateAliasOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.CreateAliasInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CreateCustomKeyStore provides a mock function with given fields: _a0
func (_m *KMSAPI) CreateCustomKeyStore(_a0 *kms.CreateCustomKeyStoreInput) (*kms.CreateCustomKeyStoreOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.CreateCustomKeyStoreOutput
if rf, ok := ret.Get(0).(func(*kms.CreateCustomKeyStoreInput) *kms.CreateCustomKeyStoreOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.CreateCustomKeyStoreOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.CreateCustomKeyStoreInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CreateCustomKeyStoreRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) CreateCustomKeyStoreRequest(_a0 *kms.CreateCustomKeyStoreInput) (*request.Request, *kms.CreateCustomKeyStoreOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.CreateCustomKeyStoreInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.CreateCustomKeyStoreOutput
if rf, ok := ret.Get(1).(func(*kms.CreateCustomKeyStoreInput) *kms.CreateCustomKeyStoreOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.CreateCustomKeyStoreOutput)
}
}
return r0, r1
}
// CreateCustomKeyStoreWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) CreateCustomKeyStoreWithContext(_a0 context.Context, _a1 *kms.CreateCustomKeyStoreInput, _a2 ...request.Option) (*kms.CreateCustomKeyStoreOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.CreateCustomKeyStoreOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.CreateCustomKeyStoreInput, ...request.Option) *kms.CreateCustomKeyStoreOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.CreateCustomKeyStoreOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.CreateCustomKeyStoreInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CreateGrant provides a mock function with given fields: _a0
func (_m *KMSAPI) CreateGrant(_a0 *kms.CreateGrantInput) (*kms.CreateGrantOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.CreateGrantOutput
if rf, ok := ret.Get(0).(func(*kms.CreateGrantInput) *kms.CreateGrantOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.CreateGrantOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.CreateGrantInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CreateGrantRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) CreateGrantRequest(_a0 *kms.CreateGrantInput) (*request.Request, *kms.CreateGrantOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.CreateGrantInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.CreateGrantOutput
if rf, ok := ret.Get(1).(func(*kms.CreateGrantInput) *kms.CreateGrantOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.CreateGrantOutput)
}
}
return r0, r1
}
// CreateGrantWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) CreateGrantWithContext(_a0 context.Context, _a1 *kms.CreateGrantInput, _a2 ...request.Option) (*kms.CreateGrantOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.CreateGrantOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.CreateGrantInput, ...request.Option) *kms.CreateGrantOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.CreateGrantOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.CreateGrantInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CreateKey provides a mock function with given fields: _a0
func (_m *KMSAPI) CreateKey(_a0 *kms.CreateKeyInput) (*kms.CreateKeyOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.CreateKeyOutput
if rf, ok := ret.Get(0).(func(*kms.CreateKeyInput) *kms.CreateKeyOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.CreateKeyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.CreateKeyInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CreateKeyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) CreateKeyRequest(_a0 *kms.CreateKeyInput) (*request.Request, *kms.CreateKeyOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.CreateKeyInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.CreateKeyOutput
if rf, ok := ret.Get(1).(func(*kms.CreateKeyInput) *kms.CreateKeyOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.CreateKeyOutput)
}
}
return r0, r1
}
// CreateKeyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) CreateKeyWithContext(_a0 context.Context, _a1 *kms.CreateKeyInput, _a2 ...request.Option) (*kms.CreateKeyOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.CreateKeyOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.CreateKeyInput, ...request.Option) *kms.CreateKeyOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.CreateKeyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.CreateKeyInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Decrypt provides a mock function with given fields: _a0
func (_m *KMSAPI) Decrypt(_a0 *kms.DecryptInput) (*kms.DecryptOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.DecryptOutput
if rf, ok := ret.Get(0).(func(*kms.DecryptInput) *kms.DecryptOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DecryptOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.DecryptInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DecryptRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DecryptRequest(_a0 *kms.DecryptInput) (*request.Request, *kms.DecryptOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.DecryptInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.DecryptOutput
if rf, ok := ret.Get(1).(func(*kms.DecryptInput) *kms.DecryptOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.DecryptOutput)
}
}
return r0, r1
}
// DecryptWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DecryptWithContext(_a0 context.Context, _a1 *kms.DecryptInput, _a2 ...request.Option) (*kms.DecryptOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.DecryptOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.DecryptInput, ...request.Option) *kms.DecryptOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DecryptOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.DecryptInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DeleteAlias provides a mock function with given fields: _a0
func (_m *KMSAPI) DeleteAlias(_a0 *kms.DeleteAliasInput) (*kms.DeleteAliasOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.DeleteAliasOutput
if rf, ok := ret.Get(0).(func(*kms.DeleteAliasInput) *kms.DeleteAliasOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DeleteAliasOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.DeleteAliasInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DeleteAliasRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DeleteAliasRequest(_a0 *kms.DeleteAliasInput) (*request.Request, *kms.DeleteAliasOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.DeleteAliasInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.DeleteAliasOutput
if rf, ok := ret.Get(1).(func(*kms.DeleteAliasInput) *kms.DeleteAliasOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.DeleteAliasOutput)
}
}
return r0, r1
}
// DeleteAliasWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DeleteAliasWithContext(_a0 context.Context, _a1 *kms.DeleteAliasInput, _a2 ...request.Option) (*kms.DeleteAliasOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.DeleteAliasOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.DeleteAliasInput, ...request.Option) *kms.DeleteAliasOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DeleteAliasOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.DeleteAliasInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DeleteCustomKeyStore provides a mock function with given fields: _a0
func (_m *KMSAPI) DeleteCustomKeyStore(_a0 *kms.DeleteCustomKeyStoreInput) (*kms.DeleteCustomKeyStoreOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.DeleteCustomKeyStoreOutput
if rf, ok := ret.Get(0).(func(*kms.DeleteCustomKeyStoreInput) *kms.DeleteCustomKeyStoreOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DeleteCustomKeyStoreOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.DeleteCustomKeyStoreInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DeleteCustomKeyStoreRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DeleteCustomKeyStoreRequest(_a0 *kms.DeleteCustomKeyStoreInput) (*request.Request, *kms.DeleteCustomKeyStoreOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.DeleteCustomKeyStoreInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.DeleteCustomKeyStoreOutput
if rf, ok := ret.Get(1).(func(*kms.DeleteCustomKeyStoreInput) *kms.DeleteCustomKeyStoreOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.DeleteCustomKeyStoreOutput)
}
}
return r0, r1
}
// DeleteCustomKeyStoreWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DeleteCustomKeyStoreWithContext(_a0 context.Context, _a1 *kms.DeleteCustomKeyStoreInput, _a2 ...request.Option) (*kms.DeleteCustomKeyStoreOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.DeleteCustomKeyStoreOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.DeleteCustomKeyStoreInput, ...request.Option) *kms.DeleteCustomKeyStoreOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DeleteCustomKeyStoreOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.DeleteCustomKeyStoreInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DeleteImportedKeyMaterial provides a mock function with given fields: _a0
func (_m *KMSAPI) DeleteImportedKeyMaterial(_a0 *kms.DeleteImportedKeyMaterialInput) (*kms.DeleteImportedKeyMaterialOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.DeleteImportedKeyMaterialOutput
if rf, ok := ret.Get(0).(func(*kms.DeleteImportedKeyMaterialInput) *kms.DeleteImportedKeyMaterialOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DeleteImportedKeyMaterialOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.DeleteImportedKeyMaterialInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DeleteImportedKeyMaterialRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DeleteImportedKeyMaterialRequest(_a0 *kms.DeleteImportedKeyMaterialInput) (*request.Request, *kms.DeleteImportedKeyMaterialOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.DeleteImportedKeyMaterialInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.DeleteImportedKeyMaterialOutput
if rf, ok := ret.Get(1).(func(*kms.DeleteImportedKeyMaterialInput) *kms.DeleteImportedKeyMaterialOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.DeleteImportedKeyMaterialOutput)
}
}
return r0, r1
}
// DeleteImportedKeyMaterialWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DeleteImportedKeyMaterialWithContext(_a0 context.Context, _a1 *kms.DeleteImportedKeyMaterialInput, _a2 ...request.Option) (*kms.DeleteImportedKeyMaterialOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.DeleteImportedKeyMaterialOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.DeleteImportedKeyMaterialInput, ...request.Option) *kms.DeleteImportedKeyMaterialOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DeleteImportedKeyMaterialOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.DeleteImportedKeyMaterialInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DescribeCustomKeyStores provides a mock function with given fields: _a0
func (_m *KMSAPI) DescribeCustomKeyStores(_a0 *kms.DescribeCustomKeyStoresInput) (*kms.DescribeCustomKeyStoresOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.DescribeCustomKeyStoresOutput
if rf, ok := ret.Get(0).(func(*kms.DescribeCustomKeyStoresInput) *kms.DescribeCustomKeyStoresOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DescribeCustomKeyStoresOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.DescribeCustomKeyStoresInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DescribeCustomKeyStoresRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DescribeCustomKeyStoresRequest(_a0 *kms.DescribeCustomKeyStoresInput) (*request.Request, *kms.DescribeCustomKeyStoresOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.DescribeCustomKeyStoresInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.DescribeCustomKeyStoresOutput
if rf, ok := ret.Get(1).(func(*kms.DescribeCustomKeyStoresInput) *kms.DescribeCustomKeyStoresOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.DescribeCustomKeyStoresOutput)
}
}
return r0, r1
}
// DescribeCustomKeyStoresWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DescribeCustomKeyStoresWithContext(_a0 context.Context, _a1 *kms.DescribeCustomKeyStoresInput, _a2 ...request.Option) (*kms.DescribeCustomKeyStoresOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.DescribeCustomKeyStoresOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.DescribeCustomKeyStoresInput, ...request.Option) *kms.DescribeCustomKeyStoresOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DescribeCustomKeyStoresOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.DescribeCustomKeyStoresInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DescribeKey provides a mock function with given fields: _a0
func (_m *KMSAPI) DescribeKey(_a0 *kms.DescribeKeyInput) (*kms.DescribeKeyOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.DescribeKeyOutput
if rf, ok := ret.Get(0).(func(*kms.DescribeKeyInput) *kms.DescribeKeyOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DescribeKeyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.DescribeKeyInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DescribeKeyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DescribeKeyRequest(_a0 *kms.DescribeKeyInput) (*request.Request, *kms.DescribeKeyOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.DescribeKeyInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.DescribeKeyOutput
if rf, ok := ret.Get(1).(func(*kms.DescribeKeyInput) *kms.DescribeKeyOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.DescribeKeyOutput)
}
}
return r0, r1
}
// DescribeKeyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DescribeKeyWithContext(_a0 context.Context, _a1 *kms.DescribeKeyInput, _a2 ...request.Option) (*kms.DescribeKeyOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.DescribeKeyOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.DescribeKeyInput, ...request.Option) *kms.DescribeKeyOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DescribeKeyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.DescribeKeyInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DisableKey provides a mock function with given fields: _a0
func (_m *KMSAPI) DisableKey(_a0 *kms.DisableKeyInput) (*kms.DisableKeyOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.DisableKeyOutput
if rf, ok := ret.Get(0).(func(*kms.DisableKeyInput) *kms.DisableKeyOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DisableKeyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.DisableKeyInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DisableKeyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DisableKeyRequest(_a0 *kms.DisableKeyInput) (*request.Request, *kms.DisableKeyOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.DisableKeyInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.DisableKeyOutput
if rf, ok := ret.Get(1).(func(*kms.DisableKeyInput) *kms.DisableKeyOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.DisableKeyOutput)
}
}
return r0, r1
}
// DisableKeyRotation provides a mock function with given fields: _a0
func (_m *KMSAPI) DisableKeyRotation(_a0 *kms.DisableKeyRotationInput) (*kms.DisableKeyRotationOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.DisableKeyRotationOutput
if rf, ok := ret.Get(0).(func(*kms.DisableKeyRotationInput) *kms.DisableKeyRotationOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DisableKeyRotationOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.DisableKeyRotationInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DisableKeyRotationRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DisableKeyRotationRequest(_a0 *kms.DisableKeyRotationInput) (*request.Request, *kms.DisableKeyRotationOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.DisableKeyRotationInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.DisableKeyRotationOutput
if rf, ok := ret.Get(1).(func(*kms.DisableKeyRotationInput) *kms.DisableKeyRotationOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.DisableKeyRotationOutput)
}
}
return r0, r1
}
// DisableKeyRotationWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DisableKeyRotationWithContext(_a0 context.Context, _a1 *kms.DisableKeyRotationInput, _a2 ...request.Option) (*kms.DisableKeyRotationOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.DisableKeyRotationOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.DisableKeyRotationInput, ...request.Option) *kms.DisableKeyRotationOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DisableKeyRotationOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.DisableKeyRotationInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DisableKeyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DisableKeyWithContext(_a0 context.Context, _a1 *kms.DisableKeyInput, _a2 ...request.Option) (*kms.DisableKeyOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.DisableKeyOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.DisableKeyInput, ...request.Option) *kms.DisableKeyOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DisableKeyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.DisableKeyInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DisconnectCustomKeyStore provides a mock function with given fields: _a0
func (_m *KMSAPI) DisconnectCustomKeyStore(_a0 *kms.DisconnectCustomKeyStoreInput) (*kms.DisconnectCustomKeyStoreOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.DisconnectCustomKeyStoreOutput
if rf, ok := ret.Get(0).(func(*kms.DisconnectCustomKeyStoreInput) *kms.DisconnectCustomKeyStoreOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DisconnectCustomKeyStoreOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.DisconnectCustomKeyStoreInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DisconnectCustomKeyStoreRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) DisconnectCustomKeyStoreRequest(_a0 *kms.DisconnectCustomKeyStoreInput) (*request.Request, *kms.DisconnectCustomKeyStoreOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.DisconnectCustomKeyStoreInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.DisconnectCustomKeyStoreOutput
if rf, ok := ret.Get(1).(func(*kms.DisconnectCustomKeyStoreInput) *kms.DisconnectCustomKeyStoreOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.DisconnectCustomKeyStoreOutput)
}
}
return r0, r1
}
// DisconnectCustomKeyStoreWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) DisconnectCustomKeyStoreWithContext(_a0 context.Context, _a1 *kms.DisconnectCustomKeyStoreInput, _a2 ...request.Option) (*kms.DisconnectCustomKeyStoreOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.DisconnectCustomKeyStoreOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.DisconnectCustomKeyStoreInput, ...request.Option) *kms.DisconnectCustomKeyStoreOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.DisconnectCustomKeyStoreOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.DisconnectCustomKeyStoreInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// EnableKey provides a mock function with given fields: _a0
func (_m *KMSAPI) EnableKey(_a0 *kms.EnableKeyInput) (*kms.EnableKeyOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.EnableKeyOutput
if rf, ok := ret.Get(0).(func(*kms.EnableKeyInput) *kms.EnableKeyOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.EnableKeyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.EnableKeyInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// EnableKeyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) EnableKeyRequest(_a0 *kms.EnableKeyInput) (*request.Request, *kms.EnableKeyOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.EnableKeyInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.EnableKeyOutput
if rf, ok := ret.Get(1).(func(*kms.EnableKeyInput) *kms.EnableKeyOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.EnableKeyOutput)
}
}
return r0, r1
}
// EnableKeyRotation provides a mock function with given fields: _a0
func (_m *KMSAPI) EnableKeyRotation(_a0 *kms.EnableKeyRotationInput) (*kms.EnableKeyRotationOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.EnableKeyRotationOutput
if rf, ok := ret.Get(0).(func(*kms.EnableKeyRotationInput) *kms.EnableKeyRotationOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.EnableKeyRotationOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.EnableKeyRotationInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// EnableKeyRotationRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) EnableKeyRotationRequest(_a0 *kms.EnableKeyRotationInput) (*request.Request, *kms.EnableKeyRotationOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.EnableKeyRotationInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.EnableKeyRotationOutput
if rf, ok := ret.Get(1).(func(*kms.EnableKeyRotationInput) *kms.EnableKeyRotationOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.EnableKeyRotationOutput)
}
}
return r0, r1
}
// EnableKeyRotationWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) EnableKeyRotationWithContext(_a0 context.Context, _a1 *kms.EnableKeyRotationInput, _a2 ...request.Option) (*kms.EnableKeyRotationOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.EnableKeyRotationOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.EnableKeyRotationInput, ...request.Option) *kms.EnableKeyRotationOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.EnableKeyRotationOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.EnableKeyRotationInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// EnableKeyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) EnableKeyWithContext(_a0 context.Context, _a1 *kms.EnableKeyInput, _a2 ...request.Option) (*kms.EnableKeyOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.EnableKeyOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.EnableKeyInput, ...request.Option) *kms.EnableKeyOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.EnableKeyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.EnableKeyInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Encrypt provides a mock function with given fields: _a0
func (_m *KMSAPI) Encrypt(_a0 *kms.EncryptInput) (*kms.EncryptOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.EncryptOutput
if rf, ok := ret.Get(0).(func(*kms.EncryptInput) *kms.EncryptOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.EncryptOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.EncryptInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// EncryptRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) EncryptRequest(_a0 *kms.EncryptInput) (*request.Request, *kms.EncryptOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.EncryptInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.EncryptOutput
if rf, ok := ret.Get(1).(func(*kms.EncryptInput) *kms.EncryptOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.EncryptOutput)
}
}
return r0, r1
}
// EncryptWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) EncryptWithContext(_a0 context.Context, _a1 *kms.EncryptInput, _a2 ...request.Option) (*kms.EncryptOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.EncryptOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.EncryptInput, ...request.Option) *kms.EncryptOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.EncryptOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.EncryptInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GenerateDataKey provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateDataKey(_a0 *kms.GenerateDataKeyInput) (*kms.GenerateDataKeyOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.GenerateDataKeyOutput
if rf, ok := ret.Get(0).(func(*kms.GenerateDataKeyInput) *kms.GenerateDataKeyOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GenerateDataKeyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.GenerateDataKeyInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GenerateDataKeyPair provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateDataKeyPair(_a0 *kms.GenerateDataKeyPairInput) (*kms.GenerateDataKeyPairOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.GenerateDataKeyPairOutput
if rf, ok := ret.Get(0).(func(*kms.GenerateDataKeyPairInput) *kms.GenerateDataKeyPairOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GenerateDataKeyPairOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.GenerateDataKeyPairInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GenerateDataKeyPairRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateDataKeyPairRequest(_a0 *kms.GenerateDataKeyPairInput) (*request.Request, *kms.GenerateDataKeyPairOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.GenerateDataKeyPairInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.GenerateDataKeyPairOutput
if rf, ok := ret.Get(1).(func(*kms.GenerateDataKeyPairInput) *kms.GenerateDataKeyPairOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.GenerateDataKeyPairOutput)
}
}
return r0, r1
}
// GenerateDataKeyPairWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GenerateDataKeyPairWithContext(_a0 context.Context, _a1 *kms.GenerateDataKeyPairInput, _a2 ...request.Option) (*kms.GenerateDataKeyPairOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.GenerateDataKeyPairOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.GenerateDataKeyPairInput, ...request.Option) *kms.GenerateDataKeyPairOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GenerateDataKeyPairOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.GenerateDataKeyPairInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GenerateDataKeyPairWithoutPlaintext provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateDataKeyPairWithoutPlaintext(_a0 *kms.GenerateDataKeyPairWithoutPlaintextInput) (*kms.GenerateDataKeyPairWithoutPlaintextOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.GenerateDataKeyPairWithoutPlaintextOutput
if rf, ok := ret.Get(0).(func(*kms.GenerateDataKeyPairWithoutPlaintextInput) *kms.GenerateDataKeyPairWithoutPlaintextOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GenerateDataKeyPairWithoutPlaintextOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.GenerateDataKeyPairWithoutPlaintextInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GenerateDataKeyPairWithoutPlaintextRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateDataKeyPairWithoutPlaintextRequest(_a0 *kms.GenerateDataKeyPairWithoutPlaintextInput) (*request.Request, *kms.GenerateDataKeyPairWithoutPlaintextOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.GenerateDataKeyPairWithoutPlaintextInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.GenerateDataKeyPairWithoutPlaintextOutput
if rf, ok := ret.Get(1).(func(*kms.GenerateDataKeyPairWithoutPlaintextInput) *kms.GenerateDataKeyPairWithoutPlaintextOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.GenerateDataKeyPairWithoutPlaintextOutput)
}
}
return r0, r1
}
// GenerateDataKeyPairWithoutPlaintextWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GenerateDataKeyPairWithoutPlaintextWithContext(_a0 context.Context, _a1 *kms.GenerateDataKeyPairWithoutPlaintextInput, _a2 ...request.Option) (*kms.GenerateDataKeyPairWithoutPlaintextOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.GenerateDataKeyPairWithoutPlaintextOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.GenerateDataKeyPairWithoutPlaintextInput, ...request.Option) *kms.GenerateDataKeyPairWithoutPlaintextOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GenerateDataKeyPairWithoutPlaintextOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.GenerateDataKeyPairWithoutPlaintextInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GenerateDataKeyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateDataKeyRequest(_a0 *kms.GenerateDataKeyInput) (*request.Request, *kms.GenerateDataKeyOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.GenerateDataKeyInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.GenerateDataKeyOutput
if rf, ok := ret.Get(1).(func(*kms.GenerateDataKeyInput) *kms.GenerateDataKeyOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.GenerateDataKeyOutput)
}
}
return r0, r1
}
// GenerateDataKeyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GenerateDataKeyWithContext(_a0 context.Context, _a1 *kms.GenerateDataKeyInput, _a2 ...request.Option) (*kms.GenerateDataKeyOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.GenerateDataKeyOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.GenerateDataKeyInput, ...request.Option) *kms.GenerateDataKeyOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GenerateDataKeyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.GenerateDataKeyInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GenerateDataKeyWithoutPlaintext provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateDataKeyWithoutPlaintext(_a0 *kms.GenerateDataKeyWithoutPlaintextInput) (*kms.GenerateDataKeyWithoutPlaintextOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.GenerateDataKeyWithoutPlaintextOutput
if rf, ok := ret.Get(0).(func(*kms.GenerateDataKeyWithoutPlaintextInput) *kms.GenerateDataKeyWithoutPlaintextOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GenerateDataKeyWithoutPlaintextOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.GenerateDataKeyWithoutPlaintextInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GenerateDataKeyWithoutPlaintextRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateDataKeyWithoutPlaintextRequest(_a0 *kms.GenerateDataKeyWithoutPlaintextInput) (*request.Request, *kms.GenerateDataKeyWithoutPlaintextOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.GenerateDataKeyWithoutPlaintextInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.GenerateDataKeyWithoutPlaintextOutput
if rf, ok := ret.Get(1).(func(*kms.GenerateDataKeyWithoutPlaintextInput) *kms.GenerateDataKeyWithoutPlaintextOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.GenerateDataKeyWithoutPlaintextOutput)
}
}
return r0, r1
}
// GenerateDataKeyWithoutPlaintextWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GenerateDataKeyWithoutPlaintextWithContext(_a0 context.Context, _a1 *kms.GenerateDataKeyWithoutPlaintextInput, _a2 ...request.Option) (*kms.GenerateDataKeyWithoutPlaintextOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.GenerateDataKeyWithoutPlaintextOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.GenerateDataKeyWithoutPlaintextInput, ...request.Option) *kms.GenerateDataKeyWithoutPlaintextOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GenerateDataKeyWithoutPlaintextOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.GenerateDataKeyWithoutPlaintextInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GenerateMac provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateMac(_a0 *kms.GenerateMacInput) (*kms.GenerateMacOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.GenerateMacOutput
if rf, ok := ret.Get(0).(func(*kms.GenerateMacInput) *kms.GenerateMacOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GenerateMacOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.GenerateMacInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GenerateMacRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateMacRequest(_a0 *kms.GenerateMacInput) (*request.Request, *kms.GenerateMacOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.GenerateMacInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.GenerateMacOutput
if rf, ok := ret.Get(1).(func(*kms.GenerateMacInput) *kms.GenerateMacOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.GenerateMacOutput)
}
}
return r0, r1
}
// GenerateMacWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GenerateMacWithContext(_a0 context.Context, _a1 *kms.GenerateMacInput, _a2 ...request.Option) (*kms.GenerateMacOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.GenerateMacOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.GenerateMacInput, ...request.Option) *kms.GenerateMacOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GenerateMacOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.GenerateMacInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GenerateRandom provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateRandom(_a0 *kms.GenerateRandomInput) (*kms.GenerateRandomOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.GenerateRandomOutput
if rf, ok := ret.Get(0).(func(*kms.GenerateRandomInput) *kms.GenerateRandomOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GenerateRandomOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.GenerateRandomInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GenerateRandomRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GenerateRandomRequest(_a0 *kms.GenerateRandomInput) (*request.Request, *kms.GenerateRandomOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.GenerateRandomInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.GenerateRandomOutput
if rf, ok := ret.Get(1).(func(*kms.GenerateRandomInput) *kms.GenerateRandomOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.GenerateRandomOutput)
}
}
return r0, r1
}
// GenerateRandomWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GenerateRandomWithContext(_a0 context.Context, _a1 *kms.GenerateRandomInput, _a2 ...request.Option) (*kms.GenerateRandomOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.GenerateRandomOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.GenerateRandomInput, ...request.Option) *kms.GenerateRandomOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GenerateRandomOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.GenerateRandomInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetKeyPolicy provides a mock function with given fields: _a0
func (_m *KMSAPI) GetKeyPolicy(_a0 *kms.GetKeyPolicyInput) (*kms.GetKeyPolicyOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.GetKeyPolicyOutput
if rf, ok := ret.Get(0).(func(*kms.GetKeyPolicyInput) *kms.GetKeyPolicyOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GetKeyPolicyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.GetKeyPolicyInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetKeyPolicyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GetKeyPolicyRequest(_a0 *kms.GetKeyPolicyInput) (*request.Request, *kms.GetKeyPolicyOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.GetKeyPolicyInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.GetKeyPolicyOutput
if rf, ok := ret.Get(1).(func(*kms.GetKeyPolicyInput) *kms.GetKeyPolicyOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.GetKeyPolicyOutput)
}
}
return r0, r1
}
// GetKeyPolicyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GetKeyPolicyWithContext(_a0 context.Context, _a1 *kms.GetKeyPolicyInput, _a2 ...request.Option) (*kms.GetKeyPolicyOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.GetKeyPolicyOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.GetKeyPolicyInput, ...request.Option) *kms.GetKeyPolicyOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GetKeyPolicyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.GetKeyPolicyInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetKeyRotationStatus provides a mock function with given fields: _a0
func (_m *KMSAPI) GetKeyRotationStatus(_a0 *kms.GetKeyRotationStatusInput) (*kms.GetKeyRotationStatusOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.GetKeyRotationStatusOutput
if rf, ok := ret.Get(0).(func(*kms.GetKeyRotationStatusInput) *kms.GetKeyRotationStatusOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GetKeyRotationStatusOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.GetKeyRotationStatusInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetKeyRotationStatusRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GetKeyRotationStatusRequest(_a0 *kms.GetKeyRotationStatusInput) (*request.Request, *kms.GetKeyRotationStatusOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.GetKeyRotationStatusInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.GetKeyRotationStatusOutput
if rf, ok := ret.Get(1).(func(*kms.GetKeyRotationStatusInput) *kms.GetKeyRotationStatusOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.GetKeyRotationStatusOutput)
}
}
return r0, r1
}
// GetKeyRotationStatusWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GetKeyRotationStatusWithContext(_a0 context.Context, _a1 *kms.GetKeyRotationStatusInput, _a2 ...request.Option) (*kms.GetKeyRotationStatusOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.GetKeyRotationStatusOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.GetKeyRotationStatusInput, ...request.Option) *kms.GetKeyRotationStatusOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GetKeyRotationStatusOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.GetKeyRotationStatusInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetParametersForImport provides a mock function with given fields: _a0
func (_m *KMSAPI) GetParametersForImport(_a0 *kms.GetParametersForImportInput) (*kms.GetParametersForImportOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.GetParametersForImportOutput
if rf, ok := ret.Get(0).(func(*kms.GetParametersForImportInput) *kms.GetParametersForImportOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GetParametersForImportOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.GetParametersForImportInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetParametersForImportRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GetParametersForImportRequest(_a0 *kms.GetParametersForImportInput) (*request.Request, *kms.GetParametersForImportOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.GetParametersForImportInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.GetParametersForImportOutput
if rf, ok := ret.Get(1).(func(*kms.GetParametersForImportInput) *kms.GetParametersForImportOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.GetParametersForImportOutput)
}
}
return r0, r1
}
// GetParametersForImportWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GetParametersForImportWithContext(_a0 context.Context, _a1 *kms.GetParametersForImportInput, _a2 ...request.Option) (*kms.GetParametersForImportOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.GetParametersForImportOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.GetParametersForImportInput, ...request.Option) *kms.GetParametersForImportOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GetParametersForImportOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.GetParametersForImportInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetPublicKey provides a mock function with given fields: _a0
func (_m *KMSAPI) GetPublicKey(_a0 *kms.GetPublicKeyInput) (*kms.GetPublicKeyOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.GetPublicKeyOutput
if rf, ok := ret.Get(0).(func(*kms.GetPublicKeyInput) *kms.GetPublicKeyOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GetPublicKeyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.GetPublicKeyInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetPublicKeyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) GetPublicKeyRequest(_a0 *kms.GetPublicKeyInput) (*request.Request, *kms.GetPublicKeyOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.GetPublicKeyInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.GetPublicKeyOutput
if rf, ok := ret.Get(1).(func(*kms.GetPublicKeyInput) *kms.GetPublicKeyOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.GetPublicKeyOutput)
}
}
return r0, r1
}
// GetPublicKeyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) GetPublicKeyWithContext(_a0 context.Context, _a1 *kms.GetPublicKeyInput, _a2 ...request.Option) (*kms.GetPublicKeyOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.GetPublicKeyOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.GetPublicKeyInput, ...request.Option) *kms.GetPublicKeyOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.GetPublicKeyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.GetPublicKeyInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ImportKeyMaterial provides a mock function with given fields: _a0
func (_m *KMSAPI) ImportKeyMaterial(_a0 *kms.ImportKeyMaterialInput) (*kms.ImportKeyMaterialOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.ImportKeyMaterialOutput
if rf, ok := ret.Get(0).(func(*kms.ImportKeyMaterialInput) *kms.ImportKeyMaterialOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ImportKeyMaterialOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.ImportKeyMaterialInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ImportKeyMaterialRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ImportKeyMaterialRequest(_a0 *kms.ImportKeyMaterialInput) (*request.Request, *kms.ImportKeyMaterialOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.ImportKeyMaterialInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.ImportKeyMaterialOutput
if rf, ok := ret.Get(1).(func(*kms.ImportKeyMaterialInput) *kms.ImportKeyMaterialOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.ImportKeyMaterialOutput)
}
}
return r0, r1
}
// ImportKeyMaterialWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ImportKeyMaterialWithContext(_a0 context.Context, _a1 *kms.ImportKeyMaterialInput, _a2 ...request.Option) (*kms.ImportKeyMaterialOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.ImportKeyMaterialOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.ImportKeyMaterialInput, ...request.Option) *kms.ImportKeyMaterialOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ImportKeyMaterialOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.ImportKeyMaterialInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListAliases provides a mock function with given fields: _a0
func (_m *KMSAPI) ListAliases(_a0 *kms.ListAliasesInput) (*kms.ListAliasesOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.ListAliasesOutput
if rf, ok := ret.Get(0).(func(*kms.ListAliasesInput) *kms.ListAliasesOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ListAliasesOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.ListAliasesInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListAliasesPages provides a mock function with given fields: _a0, _a1
func (_m *KMSAPI) ListAliasesPages(_a0 *kms.ListAliasesInput, _a1 func(*kms.ListAliasesOutput, bool) bool) error {
ret := _m.Called(_a0, _a1)
var r0 error
if rf, ok := ret.Get(0).(func(*kms.ListAliasesInput, func(*kms.ListAliasesOutput, bool) bool) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// ListAliasesPagesWithContext provides a mock function with given fields: _a0, _a1, _a2, _a3
func (_m *KMSAPI) ListAliasesPagesWithContext(_a0 context.Context, _a1 *kms.ListAliasesInput, _a2 func(*kms.ListAliasesOutput, bool) bool, _a3 ...request.Option) error {
_va := make([]interface{}, len(_a3))
for _i := range _a3 {
_va[_i] = _a3[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1, _a2)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *kms.ListAliasesInput, func(*kms.ListAliasesOutput, bool) bool, ...request.Option) error); ok {
r0 = rf(_a0, _a1, _a2, _a3...)
} else {
r0 = ret.Error(0)
}
return r0
}
// ListAliasesRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ListAliasesRequest(_a0 *kms.ListAliasesInput) (*request.Request, *kms.ListAliasesOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.ListAliasesInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.ListAliasesOutput
if rf, ok := ret.Get(1).(func(*kms.ListAliasesInput) *kms.ListAliasesOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.ListAliasesOutput)
}
}
return r0, r1
}
// ListAliasesWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ListAliasesWithContext(_a0 context.Context, _a1 *kms.ListAliasesInput, _a2 ...request.Option) (*kms.ListAliasesOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.ListAliasesOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.ListAliasesInput, ...request.Option) *kms.ListAliasesOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ListAliasesOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.ListAliasesInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListGrants provides a mock function with given fields: _a0
func (_m *KMSAPI) ListGrants(_a0 *kms.ListGrantsInput) (*kms.ListGrantsResponse, error) {
ret := _m.Called(_a0)
var r0 *kms.ListGrantsResponse
if rf, ok := ret.Get(0).(func(*kms.ListGrantsInput) *kms.ListGrantsResponse); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ListGrantsResponse)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.ListGrantsInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListGrantsPages provides a mock function with given fields: _a0, _a1
func (_m *KMSAPI) ListGrantsPages(_a0 *kms.ListGrantsInput, _a1 func(*kms.ListGrantsResponse, bool) bool) error {
ret := _m.Called(_a0, _a1)
var r0 error
if rf, ok := ret.Get(0).(func(*kms.ListGrantsInput, func(*kms.ListGrantsResponse, bool) bool) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// ListGrantsPagesWithContext provides a mock function with given fields: _a0, _a1, _a2, _a3
func (_m *KMSAPI) ListGrantsPagesWithContext(_a0 context.Context, _a1 *kms.ListGrantsInput, _a2 func(*kms.ListGrantsResponse, bool) bool, _a3 ...request.Option) error {
_va := make([]interface{}, len(_a3))
for _i := range _a3 {
_va[_i] = _a3[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1, _a2)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *kms.ListGrantsInput, func(*kms.ListGrantsResponse, bool) bool, ...request.Option) error); ok {
r0 = rf(_a0, _a1, _a2, _a3...)
} else {
r0 = ret.Error(0)
}
return r0
}
// ListGrantsRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ListGrantsRequest(_a0 *kms.ListGrantsInput) (*request.Request, *kms.ListGrantsResponse) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.ListGrantsInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.ListGrantsResponse
if rf, ok := ret.Get(1).(func(*kms.ListGrantsInput) *kms.ListGrantsResponse); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.ListGrantsResponse)
}
}
return r0, r1
}
// ListGrantsWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ListGrantsWithContext(_a0 context.Context, _a1 *kms.ListGrantsInput, _a2 ...request.Option) (*kms.ListGrantsResponse, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.ListGrantsResponse
if rf, ok := ret.Get(0).(func(context.Context, *kms.ListGrantsInput, ...request.Option) *kms.ListGrantsResponse); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ListGrantsResponse)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.ListGrantsInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListKeyPolicies provides a mock function with given fields: _a0
func (_m *KMSAPI) ListKeyPolicies(_a0 *kms.ListKeyPoliciesInput) (*kms.ListKeyPoliciesOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.ListKeyPoliciesOutput
if rf, ok := ret.Get(0).(func(*kms.ListKeyPoliciesInput) *kms.ListKeyPoliciesOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ListKeyPoliciesOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.ListKeyPoliciesInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListKeyPoliciesPages provides a mock function with given fields: _a0, _a1
func (_m *KMSAPI) ListKeyPoliciesPages(_a0 *kms.ListKeyPoliciesInput, _a1 func(*kms.ListKeyPoliciesOutput, bool) bool) error {
ret := _m.Called(_a0, _a1)
var r0 error
if rf, ok := ret.Get(0).(func(*kms.ListKeyPoliciesInput, func(*kms.ListKeyPoliciesOutput, bool) bool) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// ListKeyPoliciesPagesWithContext provides a mock function with given fields: _a0, _a1, _a2, _a3
func (_m *KMSAPI) ListKeyPoliciesPagesWithContext(_a0 context.Context, _a1 *kms.ListKeyPoliciesInput, _a2 func(*kms.ListKeyPoliciesOutput, bool) bool, _a3 ...request.Option) error {
_va := make([]interface{}, len(_a3))
for _i := range _a3 {
_va[_i] = _a3[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1, _a2)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *kms.ListKeyPoliciesInput, func(*kms.ListKeyPoliciesOutput, bool) bool, ...request.Option) error); ok {
r0 = rf(_a0, _a1, _a2, _a3...)
} else {
r0 = ret.Error(0)
}
return r0
}
// ListKeyPoliciesRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ListKeyPoliciesRequest(_a0 *kms.ListKeyPoliciesInput) (*request.Request, *kms.ListKeyPoliciesOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.ListKeyPoliciesInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.ListKeyPoliciesOutput
if rf, ok := ret.Get(1).(func(*kms.ListKeyPoliciesInput) *kms.ListKeyPoliciesOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.ListKeyPoliciesOutput)
}
}
return r0, r1
}
// ListKeyPoliciesWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ListKeyPoliciesWithContext(_a0 context.Context, _a1 *kms.ListKeyPoliciesInput, _a2 ...request.Option) (*kms.ListKeyPoliciesOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.ListKeyPoliciesOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.ListKeyPoliciesInput, ...request.Option) *kms.ListKeyPoliciesOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ListKeyPoliciesOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.ListKeyPoliciesInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListKeys provides a mock function with given fields: _a0
func (_m *KMSAPI) ListKeys(_a0 *kms.ListKeysInput) (*kms.ListKeysOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.ListKeysOutput
if rf, ok := ret.Get(0).(func(*kms.ListKeysInput) *kms.ListKeysOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ListKeysOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.ListKeysInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListKeysPages provides a mock function with given fields: _a0, _a1
func (_m *KMSAPI) ListKeysPages(_a0 *kms.ListKeysInput, _a1 func(*kms.ListKeysOutput, bool) bool) error {
ret := _m.Called(_a0, _a1)
var r0 error
if rf, ok := ret.Get(0).(func(*kms.ListKeysInput, func(*kms.ListKeysOutput, bool) bool) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// ListKeysPagesWithContext provides a mock function with given fields: _a0, _a1, _a2, _a3
func (_m *KMSAPI) ListKeysPagesWithContext(_a0 context.Context, _a1 *kms.ListKeysInput, _a2 func(*kms.ListKeysOutput, bool) bool, _a3 ...request.Option) error {
_va := make([]interface{}, len(_a3))
for _i := range _a3 {
_va[_i] = _a3[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1, _a2)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *kms.ListKeysInput, func(*kms.ListKeysOutput, bool) bool, ...request.Option) error); ok {
r0 = rf(_a0, _a1, _a2, _a3...)
} else {
r0 = ret.Error(0)
}
return r0
}
// ListKeysRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ListKeysRequest(_a0 *kms.ListKeysInput) (*request.Request, *kms.ListKeysOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.ListKeysInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.ListKeysOutput
if rf, ok := ret.Get(1).(func(*kms.ListKeysInput) *kms.ListKeysOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.ListKeysOutput)
}
}
return r0, r1
}
// ListKeysWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ListKeysWithContext(_a0 context.Context, _a1 *kms.ListKeysInput, _a2 ...request.Option) (*kms.ListKeysOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.ListKeysOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.ListKeysInput, ...request.Option) *kms.ListKeysOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ListKeysOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.ListKeysInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListResourceTags provides a mock function with given fields: _a0
func (_m *KMSAPI) ListResourceTags(_a0 *kms.ListResourceTagsInput) (*kms.ListResourceTagsOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.ListResourceTagsOutput
if rf, ok := ret.Get(0).(func(*kms.ListResourceTagsInput) *kms.ListResourceTagsOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ListResourceTagsOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.ListResourceTagsInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListResourceTagsRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ListResourceTagsRequest(_a0 *kms.ListResourceTagsInput) (*request.Request, *kms.ListResourceTagsOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.ListResourceTagsInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.ListResourceTagsOutput
if rf, ok := ret.Get(1).(func(*kms.ListResourceTagsInput) *kms.ListResourceTagsOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.ListResourceTagsOutput)
}
}
return r0, r1
}
// ListResourceTagsWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ListResourceTagsWithContext(_a0 context.Context, _a1 *kms.ListResourceTagsInput, _a2 ...request.Option) (*kms.ListResourceTagsOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.ListResourceTagsOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.ListResourceTagsInput, ...request.Option) *kms.ListResourceTagsOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ListResourceTagsOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.ListResourceTagsInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListRetirableGrants provides a mock function with given fields: _a0
func (_m *KMSAPI) ListRetirableGrants(_a0 *kms.ListRetirableGrantsInput) (*kms.ListGrantsResponse, error) {
ret := _m.Called(_a0)
var r0 *kms.ListGrantsResponse
if rf, ok := ret.Get(0).(func(*kms.ListRetirableGrantsInput) *kms.ListGrantsResponse); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ListGrantsResponse)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.ListRetirableGrantsInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListRetirableGrantsRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ListRetirableGrantsRequest(_a0 *kms.ListRetirableGrantsInput) (*request.Request, *kms.ListGrantsResponse) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.ListRetirableGrantsInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.ListGrantsResponse
if rf, ok := ret.Get(1).(func(*kms.ListRetirableGrantsInput) *kms.ListGrantsResponse); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.ListGrantsResponse)
}
}
return r0, r1
}
// ListRetirableGrantsWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ListRetirableGrantsWithContext(_a0 context.Context, _a1 *kms.ListRetirableGrantsInput, _a2 ...request.Option) (*kms.ListGrantsResponse, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.ListGrantsResponse
if rf, ok := ret.Get(0).(func(context.Context, *kms.ListRetirableGrantsInput, ...request.Option) *kms.ListGrantsResponse); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ListGrantsResponse)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.ListRetirableGrantsInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// PutKeyPolicy provides a mock function with given fields: _a0
func (_m *KMSAPI) PutKeyPolicy(_a0 *kms.PutKeyPolicyInput) (*kms.PutKeyPolicyOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.PutKeyPolicyOutput
if rf, ok := ret.Get(0).(func(*kms.PutKeyPolicyInput) *kms.PutKeyPolicyOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.PutKeyPolicyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.PutKeyPolicyInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// PutKeyPolicyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) PutKeyPolicyRequest(_a0 *kms.PutKeyPolicyInput) (*request.Request, *kms.PutKeyPolicyOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.PutKeyPolicyInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.PutKeyPolicyOutput
if rf, ok := ret.Get(1).(func(*kms.PutKeyPolicyInput) *kms.PutKeyPolicyOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.PutKeyPolicyOutput)
}
}
return r0, r1
}
// PutKeyPolicyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) PutKeyPolicyWithContext(_a0 context.Context, _a1 *kms.PutKeyPolicyInput, _a2 ...request.Option) (*kms.PutKeyPolicyOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.PutKeyPolicyOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.PutKeyPolicyInput, ...request.Option) *kms.PutKeyPolicyOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.PutKeyPolicyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.PutKeyPolicyInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ReEncrypt provides a mock function with given fields: _a0
func (_m *KMSAPI) ReEncrypt(_a0 *kms.ReEncryptInput) (*kms.ReEncryptOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.ReEncryptOutput
if rf, ok := ret.Get(0).(func(*kms.ReEncryptInput) *kms.ReEncryptOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ReEncryptOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.ReEncryptInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ReEncryptRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ReEncryptRequest(_a0 *kms.ReEncryptInput) (*request.Request, *kms.ReEncryptOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.ReEncryptInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.ReEncryptOutput
if rf, ok := ret.Get(1).(func(*kms.ReEncryptInput) *kms.ReEncryptOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.ReEncryptOutput)
}
}
return r0, r1
}
// ReEncryptWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ReEncryptWithContext(_a0 context.Context, _a1 *kms.ReEncryptInput, _a2 ...request.Option) (*kms.ReEncryptOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.ReEncryptOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.ReEncryptInput, ...request.Option) *kms.ReEncryptOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ReEncryptOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.ReEncryptInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ReplicateKey provides a mock function with given fields: _a0
func (_m *KMSAPI) ReplicateKey(_a0 *kms.ReplicateKeyInput) (*kms.ReplicateKeyOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.ReplicateKeyOutput
if rf, ok := ret.Get(0).(func(*kms.ReplicateKeyInput) *kms.ReplicateKeyOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ReplicateKeyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.ReplicateKeyInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ReplicateKeyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ReplicateKeyRequest(_a0 *kms.ReplicateKeyInput) (*request.Request, *kms.ReplicateKeyOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.ReplicateKeyInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.ReplicateKeyOutput
if rf, ok := ret.Get(1).(func(*kms.ReplicateKeyInput) *kms.ReplicateKeyOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.ReplicateKeyOutput)
}
}
return r0, r1
}
// ReplicateKeyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ReplicateKeyWithContext(_a0 context.Context, _a1 *kms.ReplicateKeyInput, _a2 ...request.Option) (*kms.ReplicateKeyOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.ReplicateKeyOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.ReplicateKeyInput, ...request.Option) *kms.ReplicateKeyOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ReplicateKeyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.ReplicateKeyInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RetireGrant provides a mock function with given fields: _a0
func (_m *KMSAPI) RetireGrant(_a0 *kms.RetireGrantInput) (*kms.RetireGrantOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.RetireGrantOutput
if rf, ok := ret.Get(0).(func(*kms.RetireGrantInput) *kms.RetireGrantOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.RetireGrantOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.RetireGrantInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RetireGrantRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) RetireGrantRequest(_a0 *kms.RetireGrantInput) (*request.Request, *kms.RetireGrantOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.RetireGrantInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.RetireGrantOutput
if rf, ok := ret.Get(1).(func(*kms.RetireGrantInput) *kms.RetireGrantOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.RetireGrantOutput)
}
}
return r0, r1
}
// RetireGrantWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) RetireGrantWithContext(_a0 context.Context, _a1 *kms.RetireGrantInput, _a2 ...request.Option) (*kms.RetireGrantOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.RetireGrantOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.RetireGrantInput, ...request.Option) *kms.RetireGrantOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.RetireGrantOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.RetireGrantInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RevokeGrant provides a mock function with given fields: _a0
func (_m *KMSAPI) RevokeGrant(_a0 *kms.RevokeGrantInput) (*kms.RevokeGrantOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.RevokeGrantOutput
if rf, ok := ret.Get(0).(func(*kms.RevokeGrantInput) *kms.RevokeGrantOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.RevokeGrantOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.RevokeGrantInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RevokeGrantRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) RevokeGrantRequest(_a0 *kms.RevokeGrantInput) (*request.Request, *kms.RevokeGrantOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.RevokeGrantInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.RevokeGrantOutput
if rf, ok := ret.Get(1).(func(*kms.RevokeGrantInput) *kms.RevokeGrantOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.RevokeGrantOutput)
}
}
return r0, r1
}
// RevokeGrantWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) RevokeGrantWithContext(_a0 context.Context, _a1 *kms.RevokeGrantInput, _a2 ...request.Option) (*kms.RevokeGrantOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.RevokeGrantOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.RevokeGrantInput, ...request.Option) *kms.RevokeGrantOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.RevokeGrantOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.RevokeGrantInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ScheduleKeyDeletion provides a mock function with given fields: _a0
func (_m *KMSAPI) ScheduleKeyDeletion(_a0 *kms.ScheduleKeyDeletionInput) (*kms.ScheduleKeyDeletionOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.ScheduleKeyDeletionOutput
if rf, ok := ret.Get(0).(func(*kms.ScheduleKeyDeletionInput) *kms.ScheduleKeyDeletionOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ScheduleKeyDeletionOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.ScheduleKeyDeletionInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ScheduleKeyDeletionRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) ScheduleKeyDeletionRequest(_a0 *kms.ScheduleKeyDeletionInput) (*request.Request, *kms.ScheduleKeyDeletionOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.ScheduleKeyDeletionInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.ScheduleKeyDeletionOutput
if rf, ok := ret.Get(1).(func(*kms.ScheduleKeyDeletionInput) *kms.ScheduleKeyDeletionOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.ScheduleKeyDeletionOutput)
}
}
return r0, r1
}
// ScheduleKeyDeletionWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) ScheduleKeyDeletionWithContext(_a0 context.Context, _a1 *kms.ScheduleKeyDeletionInput, _a2 ...request.Option) (*kms.ScheduleKeyDeletionOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.ScheduleKeyDeletionOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.ScheduleKeyDeletionInput, ...request.Option) *kms.ScheduleKeyDeletionOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.ScheduleKeyDeletionOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.ScheduleKeyDeletionInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Sign provides a mock function with given fields: _a0
func (_m *KMSAPI) Sign(_a0 *kms.SignInput) (*kms.SignOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.SignOutput
if rf, ok := ret.Get(0).(func(*kms.SignInput) *kms.SignOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.SignOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.SignInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SignRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) SignRequest(_a0 *kms.SignInput) (*request.Request, *kms.SignOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.SignInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.SignOutput
if rf, ok := ret.Get(1).(func(*kms.SignInput) *kms.SignOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.SignOutput)
}
}
return r0, r1
}
// SignWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) SignWithContext(_a0 context.Context, _a1 *kms.SignInput, _a2 ...request.Option) (*kms.SignOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.SignOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.SignInput, ...request.Option) *kms.SignOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.SignOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.SignInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// TagResource provides a mock function with given fields: _a0
func (_m *KMSAPI) TagResource(_a0 *kms.TagResourceInput) (*kms.TagResourceOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.TagResourceOutput
if rf, ok := ret.Get(0).(func(*kms.TagResourceInput) *kms.TagResourceOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.TagResourceOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.TagResourceInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// TagResourceRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) TagResourceRequest(_a0 *kms.TagResourceInput) (*request.Request, *kms.TagResourceOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.TagResourceInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.TagResourceOutput
if rf, ok := ret.Get(1).(func(*kms.TagResourceInput) *kms.TagResourceOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.TagResourceOutput)
}
}
return r0, r1
}
// TagResourceWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) TagResourceWithContext(_a0 context.Context, _a1 *kms.TagResourceInput, _a2 ...request.Option) (*kms.TagResourceOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.TagResourceOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.TagResourceInput, ...request.Option) *kms.TagResourceOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.TagResourceOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.TagResourceInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UntagResource provides a mock function with given fields: _a0
func (_m *KMSAPI) UntagResource(_a0 *kms.UntagResourceInput) (*kms.UntagResourceOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.UntagResourceOutput
if rf, ok := ret.Get(0).(func(*kms.UntagResourceInput) *kms.UntagResourceOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.UntagResourceOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.UntagResourceInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UntagResourceRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) UntagResourceRequest(_a0 *kms.UntagResourceInput) (*request.Request, *kms.UntagResourceOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.UntagResourceInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.UntagResourceOutput
if rf, ok := ret.Get(1).(func(*kms.UntagResourceInput) *kms.UntagResourceOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.UntagResourceOutput)
}
}
return r0, r1
}
// UntagResourceWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) UntagResourceWithContext(_a0 context.Context, _a1 *kms.UntagResourceInput, _a2 ...request.Option) (*kms.UntagResourceOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.UntagResourceOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.UntagResourceInput, ...request.Option) *kms.UntagResourceOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.UntagResourceOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.UntagResourceInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UpdateAlias provides a mock function with given fields: _a0
func (_m *KMSAPI) UpdateAlias(_a0 *kms.UpdateAliasInput) (*kms.UpdateAliasOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.UpdateAliasOutput
if rf, ok := ret.Get(0).(func(*kms.UpdateAliasInput) *kms.UpdateAliasOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.UpdateAliasOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.UpdateAliasInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UpdateAliasRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) UpdateAliasRequest(_a0 *kms.UpdateAliasInput) (*request.Request, *kms.UpdateAliasOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.UpdateAliasInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.UpdateAliasOutput
if rf, ok := ret.Get(1).(func(*kms.UpdateAliasInput) *kms.UpdateAliasOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.UpdateAliasOutput)
}
}
return r0, r1
}
// UpdateAliasWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) UpdateAliasWithContext(_a0 context.Context, _a1 *kms.UpdateAliasInput, _a2 ...request.Option) (*kms.UpdateAliasOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.UpdateAliasOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.UpdateAliasInput, ...request.Option) *kms.UpdateAliasOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.UpdateAliasOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.UpdateAliasInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UpdateCustomKeyStore provides a mock function with given fields: _a0
func (_m *KMSAPI) UpdateCustomKeyStore(_a0 *kms.UpdateCustomKeyStoreInput) (*kms.UpdateCustomKeyStoreOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.UpdateCustomKeyStoreOutput
if rf, ok := ret.Get(0).(func(*kms.UpdateCustomKeyStoreInput) *kms.UpdateCustomKeyStoreOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.UpdateCustomKeyStoreOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.UpdateCustomKeyStoreInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UpdateCustomKeyStoreRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) UpdateCustomKeyStoreRequest(_a0 *kms.UpdateCustomKeyStoreInput) (*request.Request, *kms.UpdateCustomKeyStoreOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.UpdateCustomKeyStoreInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.UpdateCustomKeyStoreOutput
if rf, ok := ret.Get(1).(func(*kms.UpdateCustomKeyStoreInput) *kms.UpdateCustomKeyStoreOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.UpdateCustomKeyStoreOutput)
}
}
return r0, r1
}
// UpdateCustomKeyStoreWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) UpdateCustomKeyStoreWithContext(_a0 context.Context, _a1 *kms.UpdateCustomKeyStoreInput, _a2 ...request.Option) (*kms.UpdateCustomKeyStoreOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.UpdateCustomKeyStoreOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.UpdateCustomKeyStoreInput, ...request.Option) *kms.UpdateCustomKeyStoreOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.UpdateCustomKeyStoreOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.UpdateCustomKeyStoreInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UpdateKeyDescription provides a mock function with given fields: _a0
func (_m *KMSAPI) UpdateKeyDescription(_a0 *kms.UpdateKeyDescriptionInput) (*kms.UpdateKeyDescriptionOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.UpdateKeyDescriptionOutput
if rf, ok := ret.Get(0).(func(*kms.UpdateKeyDescriptionInput) *kms.UpdateKeyDescriptionOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.UpdateKeyDescriptionOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.UpdateKeyDescriptionInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UpdateKeyDescriptionRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) UpdateKeyDescriptionRequest(_a0 *kms.UpdateKeyDescriptionInput) (*request.Request, *kms.UpdateKeyDescriptionOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.UpdateKeyDescriptionInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.UpdateKeyDescriptionOutput
if rf, ok := ret.Get(1).(func(*kms.UpdateKeyDescriptionInput) *kms.UpdateKeyDescriptionOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.UpdateKeyDescriptionOutput)
}
}
return r0, r1
}
// UpdateKeyDescriptionWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) UpdateKeyDescriptionWithContext(_a0 context.Context, _a1 *kms.UpdateKeyDescriptionInput, _a2 ...request.Option) (*kms.UpdateKeyDescriptionOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.UpdateKeyDescriptionOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.UpdateKeyDescriptionInput, ...request.Option) *kms.UpdateKeyDescriptionOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.UpdateKeyDescriptionOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.UpdateKeyDescriptionInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UpdatePrimaryRegion provides a mock function with given fields: _a0
func (_m *KMSAPI) UpdatePrimaryRegion(_a0 *kms.UpdatePrimaryRegionInput) (*kms.UpdatePrimaryRegionOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.UpdatePrimaryRegionOutput
if rf, ok := ret.Get(0).(func(*kms.UpdatePrimaryRegionInput) *kms.UpdatePrimaryRegionOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.UpdatePrimaryRegionOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.UpdatePrimaryRegionInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UpdatePrimaryRegionRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) UpdatePrimaryRegionRequest(_a0 *kms.UpdatePrimaryRegionInput) (*request.Request, *kms.UpdatePrimaryRegionOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.UpdatePrimaryRegionInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.UpdatePrimaryRegionOutput
if rf, ok := ret.Get(1).(func(*kms.UpdatePrimaryRegionInput) *kms.UpdatePrimaryRegionOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.UpdatePrimaryRegionOutput)
}
}
return r0, r1
}
// UpdatePrimaryRegionWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) UpdatePrimaryRegionWithContext(_a0 context.Context, _a1 *kms.UpdatePrimaryRegionInput, _a2 ...request.Option) (*kms.UpdatePrimaryRegionOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.UpdatePrimaryRegionOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.UpdatePrimaryRegionInput, ...request.Option) *kms.UpdatePrimaryRegionOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.UpdatePrimaryRegionOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.UpdatePrimaryRegionInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Verify provides a mock function with given fields: _a0
func (_m *KMSAPI) Verify(_a0 *kms.VerifyInput) (*kms.VerifyOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.VerifyOutput
if rf, ok := ret.Get(0).(func(*kms.VerifyInput) *kms.VerifyOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.VerifyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.VerifyInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// VerifyMac provides a mock function with given fields: _a0
func (_m *KMSAPI) VerifyMac(_a0 *kms.VerifyMacInput) (*kms.VerifyMacOutput, error) {
ret := _m.Called(_a0)
var r0 *kms.VerifyMacOutput
if rf, ok := ret.Get(0).(func(*kms.VerifyMacInput) *kms.VerifyMacOutput); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.VerifyMacOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*kms.VerifyMacInput) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// VerifyMacRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) VerifyMacRequest(_a0 *kms.VerifyMacInput) (*request.Request, *kms.VerifyMacOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.VerifyMacInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.VerifyMacOutput
if rf, ok := ret.Get(1).(func(*kms.VerifyMacInput) *kms.VerifyMacOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.VerifyMacOutput)
}
}
return r0, r1
}
// VerifyMacWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) VerifyMacWithContext(_a0 context.Context, _a1 *kms.VerifyMacInput, _a2 ...request.Option) (*kms.VerifyMacOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.VerifyMacOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.VerifyMacInput, ...request.Option) *kms.VerifyMacOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.VerifyMacOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.VerifyMacInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// VerifyRequest provides a mock function with given fields: _a0
func (_m *KMSAPI) VerifyRequest(_a0 *kms.VerifyInput) (*request.Request, *kms.VerifyOutput) {
ret := _m.Called(_a0)
var r0 *request.Request
if rf, ok := ret.Get(0).(func(*kms.VerifyInput) *request.Request); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*request.Request)
}
}
var r1 *kms.VerifyOutput
if rf, ok := ret.Get(1).(func(*kms.VerifyInput) *kms.VerifyOutput); ok {
r1 = rf(_a0)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*kms.VerifyOutput)
}
}
return r0, r1
}
// VerifyWithContext provides a mock function with given fields: _a0, _a1, _a2
func (_m *KMSAPI) VerifyWithContext(_a0 context.Context, _a1 *kms.VerifyInput, _a2 ...request.Option) (*kms.VerifyOutput, error) {
_va := make([]interface{}, len(_a2))
for _i := range _a2 {
_va[_i] = _a2[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0, _a1)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *kms.VerifyOutput
if rf, ok := ret.Get(0).(func(context.Context, *kms.VerifyInput, ...request.Option) *kms.VerifyOutput); ok {
r0 = rf(_a0, _a1, _a2...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kms.VerifyOutput)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *kms.VerifyInput, ...request.Option) error); ok {
r1 = rf(_a0, _a1, _a2...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
07070100000085000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001300000000sops-3.7.3/logging07070100000086000081A400000000000000000000000162794F9300000467000000000000000000000000000000000000001E00000000sops-3.7.3/logging/logging.gopackage logging
import (
"fmt"
"github.com/fatih/color"
"github.com/sirupsen/logrus"
)
func init() {
Loggers = make(map[string]*logrus.Logger)
}
// TextFormatter extends the standard logrus TextFormatter and adds a field to specify the logger's name
type TextFormatter struct {
LoggerName string
logrus.TextFormatter
}
// Format formats a log entry onto bytes
func (f *TextFormatter) Format(entry *logrus.Entry) ([]byte, error) {
bytes, err := f.TextFormatter.Format(entry)
name := color.New(color.Bold).Sprintf("[%s]", f.LoggerName)
return []byte(fmt.Sprintf("%s\t %s", name, bytes)), err
}
// NewLogger is the constructor for a new Logger object with the given name
func NewLogger(name string) *logrus.Logger {
log := logrus.New()
log.SetLevel(logrus.WarnLevel)
log.Formatter = &TextFormatter{
LoggerName: name,
}
Loggers[name] = log
return log
}
// SetLevel sets the given level for all current Loggers
func SetLevel(level logrus.Level) {
for k := range Loggers {
Loggers[k].SetLevel(level)
}
}
// Loggers is the runtime map of logger name to logger object
var Loggers map[string]*logrus.Logger
07070100000087000081A400000000000000000000000162794F9300000270000000000000000000000000000000000000002100000000sops-3.7.3/make_download_page.sh#!/usr/bin/env bash
[ ! -d dist ] && mkdir dist
echo -e "<html>\n<head><title>Sops download page></title>\n<body>\n<h1>Sops download page</h1>\n<h2><a href="https://go.mozilla.org/sops/">go.mozilla.org/sops</a></h2>\n<table>" > index.html
IFS=$'\n'
for dist in $(aws s3 ls s3://go.mozilla.org/sops/dist/ | grep -P "deb|rpm"); do
ts=$(echo $dist|awk '{print $1,$2}')
size=$(echo $dist|awk '{print $3}')
pkg=$(echo $dist|awk '{print $4}')
echo -e "<tr><td>$ts</td><td>$size</td><td><a href=\"https://go.mozilla.org/sops/dist/$pkg\">$pkg</a></td></tr>" >> index.html
done
echo -e "</table>\n</body>\n</html>" >> index.html
07070100000088000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000000F00000000sops-3.7.3/pgp07070100000089000081A400000000000000000000000162794F930000296E000000000000000000000000000000000000001C00000000sops-3.7.3/pgp/keysource.go/*
Package pgp contains an implementation of the go.mozilla.org/sops/v3.MasterKey interface that encrypts and decrypts the
data key by first trying with the github.com/ProtonMail/go-crypto/openpgp package and if that fails, by calling the "gpg" binary.
*/
package pgp //import "go.mozilla.org/sops/v3/pgp"
import (
"bytes"
"encoding/hex"
"fmt"
"io/ioutil"
"net/http"
"os"
"os/exec"
"os/user"
"path"
"strings"
"time"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/armor"
"github.com/howeyc/gopass"
"github.com/sirupsen/logrus"
gpgagent "go.mozilla.org/gopgagent"
"go.mozilla.org/sops/v3/logging"
)
var log *logrus.Logger
func init() {
log = logging.NewLogger("PGP")
}
// MasterKey is a PGP key used to securely store sops' data key by encrypting it and decrypting it
type MasterKey struct {
Fingerprint string
EncryptedKey string
CreationDate time.Time
}
// EncryptedDataKey returns the encrypted data key this master key holds
func (key *MasterKey) EncryptedDataKey() []byte {
return []byte(key.EncryptedKey)
}
// SetEncryptedDataKey sets the encrypted data key for this master key
func (key *MasterKey) SetEncryptedDataKey(enc []byte) {
key.EncryptedKey = string(enc)
}
func gpgBinary() string {
binary := "gpg"
if envBinary := os.Getenv("SOPS_GPG_EXEC"); envBinary != "" {
binary = envBinary
}
return binary
}
func (key *MasterKey) encryptWithGPGBinary(dataKey []byte) error {
fingerprint := key.Fingerprint
if offset := len(fingerprint) - 16; offset > 0 {
fingerprint = fingerprint[offset:]
}
args := []string{
"--no-default-recipient",
"--yes",
"--encrypt",
"-a",
"-r",
key.Fingerprint,
"--trusted-key",
fingerprint,
"--no-encrypt-to",
}
cmd := exec.Command(gpgBinary(), args...)
var stdout, stderr bytes.Buffer
cmd.Stdin = bytes.NewReader(dataKey)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
return fmt.Errorf("gpg binary failed with error: %s, %s", err, stderr.String())
}
key.EncryptedKey = stdout.String()
return nil
}
func getKeyFromKeyServer(fingerprint string) (openpgp.Entity, error) {
log.Warn("Deprecation Warning: GPG key fetching from a keyserver within sops will be removed in a future version of sops. See https://github.com/mozilla/sops/issues/727 for more information.")
url := fmt.Sprintf("https://keys.openpgp.org/vks/v1/by-fingerprint/%s", fingerprint)
resp, err := http.Get(url)
if err != nil {
return openpgp.Entity{}, fmt.Errorf("error getting key from keyserver: %s", err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return openpgp.Entity{}, fmt.Errorf("keyserver returned non-200 status code %s", resp.Status)
}
ents, err := openpgp.ReadArmoredKeyRing(resp.Body)
if err != nil {
return openpgp.Entity{}, fmt.Errorf("could not read entities: %s", err)
}
return *ents[0], nil
}
func (key *MasterKey) getPubKey() (openpgp.Entity, error) {
ring, err := key.pubRing()
if err == nil {
fingerprints := key.fingerprintMap(ring)
entity, ok := fingerprints[key.Fingerprint]
if ok {
return entity, nil
}
}
entity, err := getKeyFromKeyServer(key.Fingerprint)
if err != nil {
return openpgp.Entity{},
fmt.Errorf("key with fingerprint %s is not available "+
"in keyring and could not be retrieved from keyserver", key.Fingerprint)
}
return entity, nil
}
func (key *MasterKey) encryptWithCryptoOpenPGP(dataKey []byte) error {
entity, err := key.getPubKey()
if err != nil {
return err
}
encbuf := new(bytes.Buffer)
armorbuf, err := armor.Encode(encbuf, "PGP MESSAGE", nil)
if err != nil {
return err
}
plaintextbuf, err := openpgp.Encrypt(armorbuf, []*openpgp.Entity{&entity}, nil, &openpgp.FileHints{IsBinary: true}, nil)
if err != nil {
return err
}
_, err = plaintextbuf.Write(dataKey)
if err != nil {
return err
}
err = plaintextbuf.Close()
if err != nil {
return err
}
err = armorbuf.Close()
if err != nil {
return err
}
bytes, err := ioutil.ReadAll(encbuf)
if err != nil {
return err
}
key.EncryptedKey = string(bytes)
return nil
}
// Encrypt encrypts the data key with the PGP key with the same fingerprint as the MasterKey. It looks for PGP public keys in $PGPHOME/pubring.gpg.
func (key *MasterKey) Encrypt(dataKey []byte) error {
openpgpErr := key.encryptWithCryptoOpenPGP(dataKey)
if openpgpErr == nil {
log.WithField("fingerprint", key.Fingerprint).Info("Encryption succeeded")
return nil
}
binaryErr := key.encryptWithGPGBinary(dataKey)
if binaryErr == nil {
log.WithField("fingerprint", key.Fingerprint).Info("Encryption succeeded")
return nil
}
log.WithField("fingerprint", key.Fingerprint).Info("Encryption failed")
return fmt.Errorf(
`could not encrypt data key with PGP key: github.com/ProtonMail/go-crypto/openpgp error: %v; GPG binary error: %v`,
openpgpErr, binaryErr)
}
// EncryptIfNeeded encrypts the data key with PGP only if it's needed, that is, if it hasn't been encrypted already
func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
if key.EncryptedKey == "" {
return key.Encrypt(dataKey)
}
return nil
}
func (key *MasterKey) decryptWithGPGBinary() ([]byte, error) {
args := []string{
"--use-agent",
"-d",
}
cmd := exec.Command(gpgBinary(), args...)
var stdout, stderr bytes.Buffer
cmd.Stdin = strings.NewReader(key.EncryptedKey)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
return nil, err
}
return stdout.Bytes(), nil
}
func (key *MasterKey) decryptWithCryptoOpenpgp() ([]byte, error) {
ring, err := key.secRing()
if err != nil {
return nil, fmt.Errorf("Could not load secring: %s", err)
}
block, err := armor.Decode(strings.NewReader(key.EncryptedKey))
if err != nil {
return nil, fmt.Errorf("Armor decoding failed: %s", err)
}
md, err := openpgp.ReadMessage(block.Body, ring, key.passphrasePrompt(), nil)
if err != nil {
return nil, fmt.Errorf("Reading PGP message failed: %s", err)
}
if b, err := ioutil.ReadAll(md.UnverifiedBody); err == nil {
return b, nil
}
return nil, fmt.Errorf("The key could not be decrypted with any of the PGP entries")
}
// Decrypt uses PGP to obtain the data key from the EncryptedKey store in the MasterKey and returns it
func (key *MasterKey) Decrypt() ([]byte, error) {
dataKey, openpgpErr := key.decryptWithCryptoOpenpgp()
if openpgpErr == nil {
log.WithField("fingerprint", key.Fingerprint).Info("Decryption succeeded")
return dataKey, nil
}
dataKey, binaryErr := key.decryptWithGPGBinary()
if binaryErr == nil {
log.WithField("fingerprint", key.Fingerprint).Info("Decryption succeeded")
return dataKey, nil
}
log.WithField("fingerprint", key.Fingerprint).Info("Decryption failed")
return nil, fmt.Errorf(
`could not decrypt data key with PGP key: github.com/ProtonMail/go-crypto/openpgp error: %v; GPG binary error: %v`,
openpgpErr, binaryErr)
}
// NeedsRotation returns whether the data key needs to be rotated or not
func (key *MasterKey) NeedsRotation() bool {
return time.Since(key.CreationDate).Hours() > 24*30*6
}
// ToString returns the string representation of the key, i.e. its fingerprint
func (key *MasterKey) ToString() string {
return key.Fingerprint
}
func (key *MasterKey) gpgHome() string {
dir := os.Getenv("GNUPGHOME")
if dir == "" {
usr, err := user.Current()
if err != nil {
return path.Join(os.Getenv("HOME"), "/.gnupg")
}
return path.Join(usr.HomeDir, ".gnupg")
}
return dir
}
// NewMasterKeyFromFingerprint takes a PGP fingerprint and returns a new MasterKey with that fingerprint
func NewMasterKeyFromFingerprint(fingerprint string) *MasterKey {
return &MasterKey{
Fingerprint: strings.Replace(fingerprint, " ", "", -1),
CreationDate: time.Now().UTC(),
}
}
// MasterKeysFromFingerprintString takes a comma separated list of PGP fingerprints and returns a slice of new MasterKeys with those fingerprints
func MasterKeysFromFingerprintString(fingerprint string) []*MasterKey {
var keys []*MasterKey
if fingerprint == "" {
return keys
}
for _, s := range strings.Split(fingerprint, ",") {
keys = append(keys, NewMasterKeyFromFingerprint(s))
}
return keys
}
func (key *MasterKey) loadRing(path string) (openpgp.EntityList, error) {
f, err := os.Open(path)
if err != nil {
return openpgp.EntityList{}, err
}
defer f.Close()
keyring, err := openpgp.ReadKeyRing(f)
if err != nil {
return keyring, err
}
return keyring, nil
}
func (key *MasterKey) secRing() (openpgp.EntityList, error) {
return key.loadRing(key.gpgHome() + "/secring.gpg")
}
func (key *MasterKey) pubRing() (openpgp.EntityList, error) {
return key.loadRing(key.gpgHome() + "/pubring.gpg")
}
func (key *MasterKey) fingerprintMap(ring openpgp.EntityList) map[string]openpgp.Entity {
fps := make(map[string]openpgp.Entity)
for _, entity := range ring {
fp := strings.ToUpper(hex.EncodeToString(entity.PrimaryKey.Fingerprint[:]))
if entity != nil {
fps[fp] = *entity
}
}
return fps
}
func (key *MasterKey) passphrasePrompt() func(keys []openpgp.Key, symmetric bool) ([]byte, error) {
callCounter := 0
maxCalls := 3
return func(keys []openpgp.Key, symmetric bool) ([]byte, error) {
if callCounter >= maxCalls {
return nil, fmt.Errorf("function passphrasePrompt called too many times")
}
callCounter++
conn, err := gpgagent.NewConn()
if err == gpgagent.ErrNoAgent {
log.Infof("gpg-agent not found, continuing with manual passphrase " +
"input...")
fmt.Print("Enter PGP key passphrase: ")
pass, err := gopass.GetPasswd()
if err != nil {
return nil, err
}
for _, k := range keys {
k.PrivateKey.Decrypt(pass)
}
return pass, err
}
if err != nil {
return nil, fmt.Errorf("Could not establish connection with gpg-agent: %s", err)
}
defer conn.Close()
for _, k := range keys {
req := gpgagent.PassphraseRequest{
CacheKey: k.PublicKey.KeyIdShortString(),
Prompt: "Passphrase",
Desc: fmt.Sprintf("Unlock key %s to decrypt sops's key", k.PublicKey.KeyIdShortString()),
}
pass, err := conn.GetPassphrase(&req)
if err != nil {
return nil, fmt.Errorf("gpg-agent passphrase request errored: %s", err)
}
k.PrivateKey.Decrypt([]byte(pass))
return []byte(pass), nil
}
return nil, fmt.Errorf("No key to unlock")
}
}
// ToMap converts the MasterKey into a map for serialization purposes
func (key MasterKey) ToMap() map[string]interface{} {
out := make(map[string]interface{})
out["fp"] = key.Fingerprint
out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339)
out["enc"] = key.EncryptedKey
return out
}
0707010000008A000081A400000000000000000000000162794F93000005F2000000000000000000000000000000000000002100000000sops-3.7.3/pgp/keysource_test.gopackage pgp
import (
"bytes"
"testing"
"testing/quick"
"github.com/stretchr/testify/assert"
)
func TestPGP(t *testing.T) {
key := NewMasterKeyFromFingerprint("FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4")
f := func(x []byte) bool {
if x == nil || len(x) == 0 {
return true
}
if err := key.Encrypt(x); err != nil {
t.Errorf("Failed to encrypt: %#v err: %w", x, err)
return false
}
k, err := key.Decrypt()
if err != nil {
t.Errorf("Failed to decrypt: %#v err: %w", x, err)
return false
}
return bytes.Equal(x, k)
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
func TestPGPKeySourceFromString(t *testing.T) {
s := "C8C5 2C0A B2A4 8174 01E8 12C8 F3CC 3233 3FAD 9F1E, C8C5 2C0A B2A4 8174 01E8 12C8 F3CC 3233 3FAD 9F1E"
ks := MasterKeysFromFingerprintString(s)
expected := "C8C52C0AB2A4817401E812C8F3CC32333FAD9F1E"
if ks[0].Fingerprint != expected {
t.Errorf("Fingerprint does not match. Got %s, expected %s", ks[0].Fingerprint, expected)
}
if ks[1].Fingerprint != expected {
t.Error("Fingerprint does not match")
}
}
func TestRetrievePGPKey(t *testing.T) {
// Requires a key available in https://keys.openpgp.org/ *with identity information* (that is, an email address).
// See https://keys.openpgp.org/about/faq#verify-multiple for details about identity information.
// We use the key of release@mozilla.com for here.
fingerprint := "14F26682D0916CDD81E37B6D61B7B526D98F0353"
_, err := getKeyFromKeyServer(fingerprint)
assert.NoError(t, err)
}
0707010000008B000081A400000000000000000000000162794F9300002861000000000000000000000000000000000000002D00000000sops-3.7.3/pgp/sops_functional_tests_key.asc-----BEGIN PGP PRIVATE KEY BLOCK-----
lQOYBF1oQV0BCAC1iFfE7H3uu0hbWbRYVMoz5zZ91ACHETCOMVxN8GOG4SV0l8aQ
wmK9QWkYxhi52LnicVD3D7Uy75+J3zkvEDQ15C0AZ8UHXp4JlSQuXpFhrOhfYUF/
6pr/QexT+hQjOacvY4qfnj4xKa/AGdv5vPIygtQumE6r3GhEVAxQ1GSwtCWSU3Zl
3Uqf7S8kDvJTemtR2UkVfpXcMd4AmMKgt7fVhPO8eFotqTLPvz/iClzER+q61fLA
d1rP9YlmY46MJp/PffPicWdJiKv2i6ynKcIwkrQyP6V2ZzYi/gAhNJst3ZlMfsiN
ekCtcow9Bn44uxW3U8W02FNQSNyn6V6QPDIXABEBAAEAB/0Z8kQSlkzE97QhXm0g
/PQuaVCdY9UJeSMBXTvDZhBhAcLf6yZLStq1uz4sIiWm6+ZcX8mXQ9b90fMceoaK
sVxiYYaEcCXgu5zcuMTu8xRWK30bzjkARrDjEByZFNLrr/yzO3KKWvdVAToou77N
xLxct4df+46vEMs/DOulDUkxBOjlkprlq8xSG/6vuo7rJKUylsS4s5+y+EJCfm0m
8C94IIOt42ANObDUziUHCFNhCKSUs92rL7HXfcMG6L16UrSpJ3yLNvTI34PgRydv
ppu6DAFNeqsJ6oINSWXEqjfMHK7Ly9oyF2bkB2VKoapAdz6YGJydrODhFrThcuJk
+pY9BADKnXtYvDRPoTsfRYgZewtBxf3ccGUjoS9YCC3salWuPEWnal2yI0YRwZNE
iirOFGKH6jh/fxtFZNPXuYb7MJzFqVOcARz6USCvR1va2kMZzQEOKwxOXqIYYMVh
Uwz9++QugqcBLHw9YUFmH/DsRaL4zP4H8cX5O1TALFo3aC/EHQQA5VzUDupcpRLP
gF6dCgT2GyajgRoUFU7Brq82+HJDBDhHMB+3VWJhsC9DkTMh/RtPOuLb41K0OZ//
acoXo0QjsLsBx+hNqWC0oosqaoXiUyhbmEukvlURm5uHThX9n5BZIKhiCft/NYNO
yb+OBgYFHN11BMUVyhMR7be2mlJ4EMMD/jd9WQIoHQQ6BKMNOlc6BGu4KsMv/+fF
KV4xnJKrWjJxwri0FsOYLS2qkgbSAXjxLqZWx4UylmJh1HSAyjTghY0zQEf2oDKd
0DKN8Y42aawh1AolIfDbYOampw5tBzI2/WYOksGRFCwjCidL3pNd03W9dBmNbBRc
tVKLG/kt4JwCL0y0U1NPUFMgRnVuY3Rpb25hbCBUZXN0cyBLZXkgMSAoaHR0cHM6
Ly9naXRodWIuY29tL21vemlsbGEvc29wcy8pIDxzZWNvcHNAbW96aWxsYS5jb20+
iQFOBBMBCAA4FiEE+8e54qT5KJrAwdSEPRbO5KJzgbQFAl1oQV0CGwMFCwkIBwIG
FQoJCAsCBBYCAwECHgECF4AACgkQPRbO5KJzgbTDcQf7Bp7e2zY9pBBXTgDASQl3
1SSHp9WkRUV5iqPVC9iPCELggteBGMwIpbDlobc6O8/06foxWctTUaaciPBo2+je
WFTO+DNvB7oXIArqr5673QHLh6jEABBjyt91rvta2wYF1XJBgxpui9aLICsCptFN
IRvHeKUrXBI4fG5z3CDs/EOoY8K/AAYJUF+ERtmvmisiE/m20UpbYRmkBJy25c89
Wcn12I1SUJA3H3hGwvZCYp8hY1HPxxQUtU+DZBIpryi0xQqExGAlYqck7G03F+AD
7/csaT1LEdCtWRLNwE8UkvfUF6liF0SgzxFo1pp3gBU4swds9yO9wNe12JY/M5A/
BJ0DmARdaEFdAQgAtun8JhSpNAKvOXwWX2nFhnMXTJp4viMhlAZEdmMXEi27B2DM
/nRzldjxGZoNUBSVbJNj2kx5ZUDl0o6eOpChvRaGuCOpYqOuSQvD8FnX0NgQULwu
TZ+MawsaezktJEjDSBM1R6uASeJwDZj4hcUnPgyAIESajPdowEkEjdYt261fGOLL
cVoVdtqzOMBkLVdrK/FD1kGR9jnSlKEYDV9DveBUBQGdqkgWXjS5BKcae07viC6x
Ma9AJS4pizyDALB2k0HQOelZNihOGXYUuvkcs2Fivl0Tk3OCfH9XDvFehbYRHmkR
DoMuKUDSzdy6tFBAkL0CPlXAWI6kQklaBEp19QARAQABAAf7BX7YLYi3YLGn9BEv
VuSFo7l3fLyzXfsOOjVJ/0iQ2+H12Y3l+ssi4eCntb40IjDMIHv5JwjfKNSfUwkn
5diMk3LGz2d64lTKmrU4yNLaMhMbwmE0/u4JOPoXbJZWLd3lyBeTpTiY3R9pgG8V
IGfA+xNDEjUdc5jHU+edtGk37X6l6uL3OANS/MyTRdVNr28Gv/upXmJs/NbvTost
1hsU89gaDjkfsWhdhiuCHR9bqoyot/Vgvpt1NxzfV4SQGVFeph8yCGvSRBS8zXuZ
FtmzACs0j2aOMSucAGogEoD158OpXSNfdmZ1nCswlo1yqP6+ir8mr2DTRgMtxPQa
N49b4QQAxVTwRZ6+qiSCz/GJPq7qASGG4RIr87gPzxaHmznQhKIx6LEMjX/+NU6c
94A8aZY/oN7f3rr8apIA+cAHbAwFGpbc7ke1Cgy/m/eJZNUxWPT/YBjZ4V+41Uat
viGrbmS9B4QulOpF2Ng6LcOc4dggxTPAW/CYd5T2FImr1qYjjWkEAO1Lss00LY5o
5I4QqgM0OeeBEOO8LiSDmjKgOvtsmJ6+dA4x3rYgI8smFMsvtyrcb75k6EdZazgN
YSI4sU3WceWbrtdVr1glP38CBMupnFvg8KwbjSFV8vNqVBHCXShUxnHmlOW+UVqy
CxjJf0RTOhLEY5DIRwQB0H8P30dYOfatBADaGIbs/6+1RulKpHwW/c3+XOlaTZrT
UhNjuccj7Y9IspYD+6crNkQvAri60AoDfIiO5aTk8rSYpGwB1vEmnUVmNPvRF958
GV3pyCOv/pkmnpS+4w+akiJsSHX3jqqp5fb/xd6ukUX95VgSymuJ+ya49G8B0jj6
bw7B4S2M39+Xdkg2iQE2BBgBCAAgFiEE+8e54qT5KJrAwdSEPRbO5KJzgbQFAl1o
QV0CGwwACgkQPRbO5KJzgbS7zwgAndbf532OXo9HwPH+yQQmzQCLDFL6P4V7LcFr
rydYItTEhxqI3tbb96MKXRAt+G5Mw6JjRkWhwzbU3jE7D7XBMHw7GriTTU9QltNH
g7VUpSSaiTfVcSNErzsaqbjbA7jMs7VWzOq4LZo6Efy8UDKg5qcqLFaTQrzQZYNH
NfM+kLAiUPU8m7vwmz6oJWsjHkQKUhKhHptlpwMwdHkoacqDO0x2H6H91l/PnDm4
ZG6FybJtcjr98i+p52/XOo81nLgX7tcFS3nrN9HNdgKg1ZW3yrzg8NOaFCVA8qLD
gLk//M3qDixOxiurECkFrMvt/bDxEGpN5GVy550MmyUZQrkuqg==
=VjGL
-----END PGP PRIVATE KEY BLOCK-----
-----BEGIN PGP PRIVATE KEY BLOCK-----
lQHYBF1oQYgBBADPuVP6Jdk/J/TbNa9dXirp/zzwK18ZqNudNqQGN3H+2aSgxXwL
wlRfzy7rB3CU6Ewjzk9EVYeYztTIkGHL0JZ1CCTiBJArlHO0bHQQ7CPeKPkhIhkj
eA8yu9dcU77oYC2xbwgf43KYzfMKSGEybg+sBO+bH+Y6paJK54V2cuS3GwARAQAB
AAP+Jjf5BXtVP1OAr5xvCYS77JWzhpTUSIpS7dgR0br91GAC9DmhmyBEGeSqwz95
LUyYRbY9y1rZOfpEGCrIc5GLPOQytO9XMIzaS3dpzfGhla/spaKN4vJDvIOl+ruT
bInDdCRSmqXCfm2478OhOquc0H0a46eSmoaYeKdE3E8QZiECANxUL/dFk5j8NyPo
ZcwXw9Mv0A8UrynRcqht3Scti9k7dbsHylcObM305LFdcoNnSfNAIJhxfjbiXyGW
vwT2/qMCAPFatq3gvVjy6wKKylioi5cVwbLv9L+OaRXdR/Dy2bh/t3ujnsliV4+R
f7k3rHOQeaMLTnyfcz8AenL5IOe8RSkCANFpBgyzxCcV48Mm+FWDxjrSJ4/msRnN
gxqAPRrdpm7e1uebtBkPh4ch4oCW5/lLsRN23LUVIXYJRwyFfRjehCio0rRTU09Q
UyBGdW5jdGlvbmFsIFRlc3RzIEtleSAyIChodHRwczovL2dpdGh1Yi5jb20vbW96
aWxsYS9zb3BzLykgPHNlY29wc0Btb3ppbGxhLmNvbT6IzgQTAQgAOBYhBNcikEM4
S8xgMmxvudhyDZV8PTB0BQJdaEGIAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheA
AAoJENhyDZV8PTB0R2cD/2YwaJ43iGueaAzByFnl+mUEBQJ4HhH4p7BIdx6B9AjE
3yLe8I4dqqYXxyZzaJ9d+KiqxJBT0l1GXt3H5M32yDJZqzXB9PTWP3yx8+Q1CuCs
7EL/bhJD1/sLdumVc77bmQtcI9NSiYyPzN/2ZqtV5RU14Loh24VFEjuHGvO0jI3+
nQHYBF1oQYgBBAChXi00fmpEs0Jiq0zOyYm9i749VoOsNReoB/5ix1QCimwVZKe1
D37IP5Qqysxy+LIQc4lJ+Q8foNOx1Aev5+TDyv+iU82D9xr9uPLLbA82k3AZ04Or
BjrZ/Yt1NZhuaHzciZCPpmqzF9kqVqAZc+vMiKZL1WZjS7O1FwaidY1vXwARAQAB
AAP+L0wUQeOfsD0+gv8khyPJTJZOD1pxQ6NYKLcXF8rG0+vQnECha098YKNKAXTp
kfVU8795iQYIKcQQ6Hl2O1fj1AxJE/iZYrqfm7UZz3bQ7ROSsAEPZ5GDOjKfbwsz
E6bWVH+PhS1azlvtTs9JezUtK0Wl9s+81FOrZtnUUskmWtECAMNNs9ujUt6GHv/J
NXVaSmk1z8QXitPHbAJLDMj4xVDysJWZV95eplC+RUSiLz5HeP2AQgh1D9Rv2bA5
c7OcJ3kCANOEkA0hVpXCI0FKrsihOf0NUOaAtS6CQNFlaIkrLwssJQY8pGYbRfRa
3krNJPyOlXmezV2/CsX3EqA9KXXen5cB/iSmMJO4WndGJTe7YzUEnnY/P2TKg1fN
s6v5Lf39j5Ll8V5rVDT7ApAw0IKS8fzpbdHP0HcizutlF6l44YaAXMGfhoi2BBgB
CAAgFiEE1yKQQzhLzGAybG+52HINlXw9MHQFAl1oQYgCGwwACgkQ2HINlXw9MHTD
HwQAv+ui718AT2hw2pK9JaNuTxjllrH+KPMlrov0P8oXHPCohC5cxM5sJ6tCQ0qH
XyeWoDE8V31btqFVAQyrr0wy0gntl1L/trnwMHoP8a/xa0RHNk5C7hmcuhTHbQey
JNbiRJZpCIZ1OyrF17+q6u9YBPjwqp8KrJ/0ryy2kyb7ZRM=
=+tJ6
-----END PGP PRIVATE KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQENBF1oQV0BCAC1iFfE7H3uu0hbWbRYVMoz5zZ91ACHETCOMVxN8GOG4SV0l8aQ
wmK9QWkYxhi52LnicVD3D7Uy75+J3zkvEDQ15C0AZ8UHXp4JlSQuXpFhrOhfYUF/
6pr/QexT+hQjOacvY4qfnj4xKa/AGdv5vPIygtQumE6r3GhEVAxQ1GSwtCWSU3Zl
3Uqf7S8kDvJTemtR2UkVfpXcMd4AmMKgt7fVhPO8eFotqTLPvz/iClzER+q61fLA
d1rP9YlmY46MJp/PffPicWdJiKv2i6ynKcIwkrQyP6V2ZzYi/gAhNJst3ZlMfsiN
ekCtcow9Bn44uxW3U8W02FNQSNyn6V6QPDIXABEBAAG0U1NPUFMgRnVuY3Rpb25h
bCBUZXN0cyBLZXkgMSAoaHR0cHM6Ly9naXRodWIuY29tL21vemlsbGEvc29wcy8p
IDxzZWNvcHNAbW96aWxsYS5jb20+iQFOBBMBCAA4FiEE+8e54qT5KJrAwdSEPRbO
5KJzgbQFAl1oQV0CGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQPRbO5KJz
gbTDcQf7Bp7e2zY9pBBXTgDASQl31SSHp9WkRUV5iqPVC9iPCELggteBGMwIpbDl
obc6O8/06foxWctTUaaciPBo2+jeWFTO+DNvB7oXIArqr5673QHLh6jEABBjyt91
rvta2wYF1XJBgxpui9aLICsCptFNIRvHeKUrXBI4fG5z3CDs/EOoY8K/AAYJUF+E
RtmvmisiE/m20UpbYRmkBJy25c89Wcn12I1SUJA3H3hGwvZCYp8hY1HPxxQUtU+D
ZBIpryi0xQqExGAlYqck7G03F+AD7/csaT1LEdCtWRLNwE8UkvfUF6liF0SgzxFo
1pp3gBU4swds9yO9wNe12JY/M5A/BLkBDQRdaEFdAQgAtun8JhSpNAKvOXwWX2nF
hnMXTJp4viMhlAZEdmMXEi27B2DM/nRzldjxGZoNUBSVbJNj2kx5ZUDl0o6eOpCh
vRaGuCOpYqOuSQvD8FnX0NgQULwuTZ+MawsaezktJEjDSBM1R6uASeJwDZj4hcUn
PgyAIESajPdowEkEjdYt261fGOLLcVoVdtqzOMBkLVdrK/FD1kGR9jnSlKEYDV9D
veBUBQGdqkgWXjS5BKcae07viC6xMa9AJS4pizyDALB2k0HQOelZNihOGXYUuvkc
s2Fivl0Tk3OCfH9XDvFehbYRHmkRDoMuKUDSzdy6tFBAkL0CPlXAWI6kQklaBEp1
9QARAQABiQE2BBgBCAAgFiEE+8e54qT5KJrAwdSEPRbO5KJzgbQFAl1oQV0CGwwA
CgkQPRbO5KJzgbS7zwgAndbf532OXo9HwPH+yQQmzQCLDFL6P4V7LcFrrydYItTE
hxqI3tbb96MKXRAt+G5Mw6JjRkWhwzbU3jE7D7XBMHw7GriTTU9QltNHg7VUpSSa
iTfVcSNErzsaqbjbA7jMs7VWzOq4LZo6Efy8UDKg5qcqLFaTQrzQZYNHNfM+kLAi
UPU8m7vwmz6oJWsjHkQKUhKhHptlpwMwdHkoacqDO0x2H6H91l/PnDm4ZG6FybJt
cjr98i+p52/XOo81nLgX7tcFS3nrN9HNdgKg1ZW3yrzg8NOaFCVA8qLDgLk//M3q
DixOxiurECkFrMvt/bDxEGpN5GVy550MmyUZQrkuqg==
=Zs2s
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----
mI0EXWhBiAEEAM+5U/ol2T8n9Ns1r11eKun/PPArXxmo2502pAY3cf7ZpKDFfAvC
VF/PLusHcJToTCPOT0RVh5jO1MiQYcvQlnUIJOIEkCuUc7RsdBDsI94o+SEiGSN4
DzK711xTvuhgLbFvCB/jcpjN8wpIYTJuD6wE75sf5jqlokrnhXZy5LcbABEBAAG0
U1NPUFMgRnVuY3Rpb25hbCBUZXN0cyBLZXkgMiAoaHR0cHM6Ly9naXRodWIuY29t
L21vemlsbGEvc29wcy8pIDxzZWNvcHNAbW96aWxsYS5jb20+iM4EEwEIADgWIQTX
IpBDOEvMYDJsb7nYcg2VfD0wdAUCXWhBiAIbAwULCQgHAgYVCgkICwIEFgIDAQIe
AQIXgAAKCRDYcg2VfD0wdEdnA/9mMGieN4hrnmgMwchZ5fplBAUCeB4R+KewSHce
gfQIxN8i3vCOHaqmF8cmc2ifXfioqsSQU9JdRl7dx+TN9sgyWas1wfT01j98sfPk
NQrgrOxC/24SQ9f7C3bplXO+25kLXCPTUomMj8zf9marVeUVNeC6IduFRRI7hxrz
tIyN/riNBF1oQYgBBAChXi00fmpEs0Jiq0zOyYm9i749VoOsNReoB/5ix1QCimwV
ZKe1D37IP5Qqysxy+LIQc4lJ+Q8foNOx1Aev5+TDyv+iU82D9xr9uPLLbA82k3AZ
04OrBjrZ/Yt1NZhuaHzciZCPpmqzF9kqVqAZc+vMiKZL1WZjS7O1FwaidY1vXwAR
AQABiLYEGAEIACAWIQTXIpBDOEvMYDJsb7nYcg2VfD0wdAUCXWhBiAIbDAAKCRDY
cg2VfD0wdMMfBAC/66LvXwBPaHDakr0lo25PGOWWsf4o8yWui/Q/yhcc8KiELlzE
zmwnq0JDSodfJ5agMTxXfVu2oVUBDKuvTDLSCe2XUv+2ufAweg/xr/FrREc2TkLu
GZy6FMdtB7Ik1uJElmkIhnU7KsXXv6rq71gE+PCqnwqsn/SvLLaTJvtlEw==
=PafV
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQENBF1oQaYBCADsCw223WDj2ISnkZQJ07NS7ER/ft2Tz6FPzsMqz5JmGlwQantH
MzjqNGE5du1TBK+yCIuzn+P/iokmOdFjkH20OHHCEmgBQPQ2WxpR4Bc/jDvswoL2
2amknIYStf+SgCwtNPT51RS7/5brN5pVn4GjDYg+IRLk33bxz1kN3933olkMFHJF
hrr1rcE1uxt3j2CEPzfKAUyviKXkSl41IAopE2e69zsVg0YaensTnE/hY45r1q0W
ABXoJLg4H6UtHZWWGg7vmtQYCpiiUIaWZ3YB449Ur4jxXeIZPUqhliqOoUk8a7En
1ZC1hGLw0jaENeaHywVGi9ZqABmOVYmvItI3ABEBAAG0U1NPUFMgRnVuY3Rpb25h
bCBUZXN0cyBLZXkgMyAoaHR0cHM6Ly9naXRodWIuY29tL21vemlsbGEvc29wcy8p
IDxzZWNvcHNAbW96aWxsYS5jb20+iQFOBBMBCAA4FiEEthGi+fEdD/glaIBRGfm1
2uqR/4YFAl1oQaYCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQGfm12uqR
/4Yc3QgA2WcMlCCUB4uyyvKq1a8ZTBli0YzIxT+KslL+PbOxfhoxglxPRClB0/l6
stG3RD0UXKq9cTe9f5nI96XJRqUzePfKtixSieirNTZswiHIYifYfg460gXYAIsV
deztRGxEaMUVq6vajkA69yGbi2nExaQrPQAgcOoyEcGhsQVi5M3rbIGIJtK/K777
lAvWskMWjEL/VIQ3qHJapBwhtyK8pKiQ1U2ssvXSP15RouUU/8PdD6d82dLlLfBm
eiyQCeJYQyR/geJAvhyBO+Jy+SS2RNCE6FGirACzRh3SSLpKAvieoMHqYzav8kMh
zSMa5230SKpartbsgFu7t39zsc3t6LkBDQRdaEGmAQgA60kJghTuxvZaNjXCZBnj
0+z+NSnfhdPaGgdUStXDCbzsO2l/6kvlWMwwS3VuDwGjx1vLWt9KWxmL85+qLE2H
FuRpH4sJFa+0HraY9SP1UxgO5ydommRKdLDw5iQIaJACMSsMAHHOQCJ9VuNE1gt8
gOb5J902gRahgIKpGrrmtI6x3Zmu1ryKrBH9Ln60kYvrU14ANPYtIvOwqCNxeU4C
9q1q4/b73RXg2Gri8alGb2HMIaeK6u7i5MlEOqkQof2FkmGg3nEw/6Cvcnf4jesJ
ibKa69vC6n39ZJ0eRhmhr3tw3MP76Laayhqq8T+Ffog68A3Oe+i6+f7PyLe0oaHA
uQARAQABiQE2BBgBCAAgFiEEthGi+fEdD/glaIBRGfm12uqR/4YFAl1oQaYCGwwA
CgkQGfm12uqR/4b5lwgAmVCa0XYeco6Ec+Iz0CLBvNXDFH/KsP/ypWK5duzZRKeb
D30cDwQHUFWH5WivGZ5nJ+Rs9zkD7a07omMTRmCsrjD4I3xDGMTU23l+gBSC5+9R
B6bOi81ngH3OLaSbeh2t21PDEf57M94WFNlw2LVgMvZ6S4rs7I4FZgm75h4EGGuY
It1l8SqNWcKDm9Kz/qG0lqeSGGFnQqmSBFH0Vb0hus/XErU2r3fQr1lDj0VKpOIO
J0Ys9rmI6yEPTi+GhFr1bHKwZMinz5lcHnOl8xye48tsrOtHMGN17/B6hUUGzd+W
TphrOfnfTO1YCkg1nEB5E2Raj/KV+ohqPvjE+KhE7Q==
=gqhE
-----END PGP PUBLIC KEY BLOCK-----
0707010000008C000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001300000000sops-3.7.3/publish0707010000008D000081A400000000000000000000000162794F93000004EC000000000000000000000000000000000000001A00000000sops-3.7.3/publish/gcs.gopackage publish
import (
"context"
"fmt"
"cloud.google.com/go/storage"
)
// GCSDestination represents the Google Cloud Storage destination
type GCSDestination struct {
gcsBucket string
gcsPrefix string
}
// NewGCSDestination is the constructor for a Google Cloud Storage destination
func NewGCSDestination(gcsBucket, gcsPrefix string) *GCSDestination {
return &GCSDestination{gcsBucket, gcsPrefix}
}
// Path returns a the GCS path for a file within this GCS Destination
func (gcsd *GCSDestination) Path(fileName string) string {
return fmt.Sprintf("gcs://%s/%s%s", gcsd.gcsBucket, gcsd.gcsPrefix, fileName)
}
// Upload uploads contents to a file in GCS
func (gcsd *GCSDestination) Upload(fileContents []byte, fileName string) error {
ctx := context.Background()
client, err := storage.NewClient(ctx)
if err != nil {
return err
}
wc := client.Bucket(gcsd.gcsBucket).Object(gcsd.gcsPrefix + fileName).NewWriter(ctx)
defer wc.Close()
_, err = wc.Write(fileContents)
if err != nil {
return err
}
return nil
}
// Returns NotImplementedError
func (gcsd *GCSDestination) UploadUnencrypted(data map[string]interface{}, fileName string) error {
return &NotImplementedError{"GCS does not support uploading the unencrypted file contents."}
}
0707010000008E000081A400000000000000000000000162794F93000001E4000000000000000000000000000000000000001E00000000sops-3.7.3/publish/publish.gopackage publish
import "fmt"
// Destination represents actions which all destination types
// must implement in order to be used by SOPS
type Destination interface {
Upload(fileContents []byte, fileName string) error
UploadUnencrypted(data map[string]interface{}, fileName string) error
Path(fileName string) string
}
type NotImplementedError struct {
message string
}
func (e *NotImplementedError) Error() string {
return fmt.Sprintf("NotImplementedError: %s", e.message)
}
0707010000008F000081A400000000000000000000000162794F9300000557000000000000000000000000000000000000001900000000sops-3.7.3/publish/s3.gopackage publish
import (
"bytes"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
)
// S3Destination is the AWS S3 implementation of the Destination interface
type S3Destination struct {
s3Bucket string
s3Prefix string
}
// NewS3Destination is the constructor for an S3 Destination
func NewS3Destination(s3Bucket, s3Prefix string) *S3Destination {
return &S3Destination{s3Bucket, s3Prefix}
}
// Path returns the S3 path of a file in an S3 Destination (bucket)
func (s3d *S3Destination) Path(fileName string) string {
return fmt.Sprintf("s3://%s/%s%s", s3d.s3Bucket, s3d.s3Prefix, fileName)
}
// Upload uploads contents to a file in an S3 Destination (bucket)
func (s3d *S3Destination) Upload(fileContents []byte, fileName string) error {
sess := session.Must(session.NewSession())
svc := s3.New(sess)
input := &s3.PutObjectInput{
Body: aws.ReadSeekCloser(bytes.NewReader(fileContents)),
Bucket: aws.String(s3d.s3Bucket),
Key: aws.String(s3d.s3Prefix + fileName),
}
_, err := svc.PutObject(input)
if err != nil {
return err
}
return nil
}
// Returns NotImplementedError
func (s3d *S3Destination) UploadUnencrypted(data map[string]interface{}, fileName string) error {
return &NotImplementedError{"S3 does not support uploading the unencrypted file contents."}
}
07070100000090000081A400000000000000000000000162794F9300000A34000000000000000000000000000000000000001C00000000sops-3.7.3/publish/vault.gopackage publish
import (
"fmt"
"strings"
"github.com/google/go-cmp/cmp"
vault "github.com/hashicorp/vault/api"
"go.mozilla.org/sops/v3/logging"
"github.com/sirupsen/logrus"
)
var log *logrus.Logger
func init() {
log = logging.NewLogger("PUBLISH")
}
type VaultDestination struct {
vaultAddress string
vaultPath string
kvMountName string
kvVersion int
}
func NewVaultDestination(vaultAddress, vaultPath, kvMountName string, kvVersion int) *VaultDestination {
if !strings.HasSuffix(vaultPath, "/") {
vaultPath = vaultPath + "/"
}
if kvMountName == "" {
kvMountName = "secret/"
}
if !strings.HasSuffix(kvMountName, "/") {
kvMountName = kvMountName + "/"
}
if kvVersion != 1 && kvVersion != 2 {
kvVersion = 2
}
return &VaultDestination{vaultAddress, vaultPath, kvMountName, kvVersion}
}
func (vaultd *VaultDestination) getAddress() string {
if vaultd.vaultAddress != "" {
return vaultd.vaultAddress
}
return vault.DefaultConfig().Address
}
func (vaultd *VaultDestination) Path(fileName string) string {
return fmt.Sprintf("%s/v1/%s", vaultd.getAddress(), vaultd.secretsPath(fileName))
}
func (vaultd *VaultDestination) secretsPath(fileName string) string {
if vaultd.kvVersion == 1 {
return fmt.Sprintf("%s%s%s", vaultd.kvMountName, vaultd.vaultPath, fileName)
}
return fmt.Sprintf("%sdata/%s%s", vaultd.kvMountName, vaultd.vaultPath, fileName)
}
// Returns NotImplementedError
func (vaultd *VaultDestination) Upload(fileContents []byte, fileName string) error {
return &NotImplementedError{"Vault does not support uploading encrypted sops files directly."}
}
func (vaultd *VaultDestination) UploadUnencrypted(data map[string]interface{}, fileName string) error {
client, err := vault.NewClient(nil)
if err != nil {
return err
}
if vaultd.vaultAddress != "" {
err = client.SetAddress(vaultd.vaultAddress)
if err != nil {
return err
}
}
secretsPath := vaultd.secretsPath(fileName)
existingSecret, err := client.Logical().Read(secretsPath)
if err != nil {
log.Warnf("Cannot check if destination secret already exists in %s. New version will be created even if the data has not been changed.", secretsPath)
}
if existingSecret != nil && cmp.Equal(data, existingSecret.Data["data"]) {
log.Infof("Secret in %s is already up-to-date.\n", secretsPath)
return nil
}
secretsData := make(map[string]interface{})
if vaultd.kvVersion == 1 {
secretsData = data
} else if vaultd.kvVersion == 2 {
secretsData["data"] = data
}
_, err = client.Logical().Write(secretsPath, secretsData)
if err != nil {
return err
}
return nil
}
07070100000091000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001200000000sops-3.7.3/shamir07070100000092000081A400000000000000000000000162794F9300003E31000000000000000000000000000000000000001A00000000sops-3.7.3/shamir/LICENSEMozilla Public License, version 2.0
1. Definitions
1.1. "Contributor"
means each individual or legal entity that creates, contributes to the
creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached the
notice in Exhibit A, the Executable Form of such Source Code Form, and
Modifications of such Source Code Form, in each case including portions
thereof.
1.5. "Incompatible With Secondary Licenses"
means
a. that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
b. that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the terms of
a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in a
separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible, whether
at the time of the initial grant or subsequently, any and all of the
rights conveyed by this License.
1.10. "Modifications"
means any of the following:
a. any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered Software; or
b. any new file in Source Code Form that contains any Covered Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the License,
by the making, using, selling, offering for sale, having made, import,
or transfer of either its Contributions or its Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public
License, Version 3.0, or any later versions of those licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that controls, is
controlled by, or is under common control with You. For purposes of this
definition, "control" means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by contract or
otherwise, or (b) ownership of more than fifty percent (50%) of the
outstanding shares or beneficial ownership of such entity.
2. License Grants and Conditions
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
a. under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
b. under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
a. for any code that a Contributor has removed from Covered Software; or
b. for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
c. under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights to
grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
3. Responsibilities
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
a. such Covered Software must also be made available in Source Code Form,
as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost
of distribution to the recipient; and
b. You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter the
recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty, or
limitations of liability) contained within the Source Code Form of the
Covered Software, except that You may alter any license notices to the
extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute,
judicial order, or regulation then You must: (a) comply with the terms of
this License to the maximum extent possible; and (b) describe the
limitations and the code they affect. Such description must be placed in a
text file included with all distributions of the Covered Software under
this License. Except to the extent prohibited by statute or regulation,
such description must be sufficiently detailed for a recipient of ordinary
skill to be able to understand it.
5. Termination
5.1. The rights granted under this License will terminate automatically if You
fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor
are reinstated (a) provisionally, unless and until such Contributor
explicitly and finally terminates Your grants, and (b) on an ongoing
basis, if such Contributor fails to notify You of the non-compliance by
some reasonable means prior to 60 days after You have come back into
compliance. Moreover, Your grants from a particular Contributor are
reinstated on an ongoing basis if such Contributor notifies You of the
non-compliance by some reasonable means, this is the first time You have
received notice of non-compliance with this License from such
Contributor, and You become compliant prior to 30 days after Your receipt
of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
6. Disclaimer of Warranty
Covered Software is provided under this License on an "as is" basis,
without warranty of any kind, either expressed, implied, or statutory,
including, without limitation, warranties that the Covered Software is free
of defects, merchantable, fit for a particular purpose or non-infringing.
The entire risk as to the quality and performance of the Covered Software
is with You. Should any Covered Software prove defective in any respect,
You (not any Contributor) assume the cost of any necessary servicing,
repair, or correction. This disclaimer of warranty constitutes an essential
part of this License. No use of any Covered Software is authorized under
this License except under this disclaimer.
7. Limitation of Liability
Under no circumstances and under no legal theory, whether tort (including
negligence), contract, or otherwise, shall any Contributor, or anyone who
distributes Covered Software as permitted above, be liable to You for any
direct, indirect, special, incidental, or consequential damages of any
character including, without limitation, damages for lost profits, loss of
goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses, even if such party shall have been
informed of the possibility of such damages. This limitation of liability
shall not apply to liability for death or personal injury resulting from
such party's negligence to the extent applicable law prohibits such
limitation. Some jurisdictions do not allow the exclusion or limitation of
incidental or consequential damages, so this exclusion and limitation may
not apply to You.
8. Litigation
Any litigation relating to this License may be brought only in the courts
of a jurisdiction where the defendant maintains its principal place of
business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions. Nothing
in this Section shall prevent a party's ability to bring cross-claims or
counter-claims.
9. Miscellaneous
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides that
the language of a contract shall be construed against the drafter shall not
be used to construe this License against a Contributor.
10. Versions of the License
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses If You choose to distribute Source Code Form that is
Incompatible With Secondary Licenses under the terms of this version of
the License, the notice described in Exhibit B of this License must be
attached.
Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file,
then You may include the notice in a location (such as a LICENSE file in a
relevant directory) where a recipient would be likely to look for such a
notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
This Source Code Form is "Incompatible
With Secondary Licenses", as defined by
the Mozilla Public License, v. 2.0.
07070100000093000081A400000000000000000000000162794F9300001685000000000000000000000000000000000000001C00000000sops-3.7.3/shamir/README.mdForked from [Vault](https://github.com/hashicorp/vault/tree/master/shamir)
## 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 set
`x = 1`, `x = 2` and `x = 3`. From this follows that `y = 3`, `y = 5` and
`y = 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 be
`1`.
## 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)}`
and `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 = 9
```
Let'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 + 1
```
```
L(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^2
```
So 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 = 303
```
And 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.
07070100000094000081A400000000000000000000000162794F93000020C6000000000000000000000000000000000000001C00000000sops-3.7.3/shamir/shamir.gopackage shamir
// Some comments in this file were written by @autrilla
// The code was written by HashiCorp as part of Vault.
// This implementation of Shamir's Secret Sharing matches the definition
// of the scheme. Other tools used, such as GF(2^8) arithmetic, Lagrange
// interpolation and Horner's method also match their definitions and should
// therefore be correct.
// More information about Shamir's Secret Sharing and Lagrange interpolation
// can be found in README.md
import (
"crypto/rand"
"crypto/subtle"
"fmt"
mathrand "math/rand"
"time"
)
const (
// ShareOverhead is the byte size overhead of each share
// when using Split on a secret. This is caused by appending
// a one byte tag to the share.
ShareOverhead = 1
)
// polynomial represents a polynomial of arbitrary degree
type polynomial struct {
coefficients []uint8
}
// makePolynomial constructs a random polynomial of the given
// degree but with the provided intercept value.
func makePolynomial(intercept, degree uint8) (polynomial, error) {
// Create a wrapper
p := polynomial{
coefficients: make([]byte, degree+1),
}
// Ensure the intercept is set
p.coefficients[0] = intercept
// Assign random co-efficients to the polynomial
if _, err := rand.Read(p.coefficients[1:]); err != nil {
return p, err
}
return p, nil
}
// evaluate returns the value of the polynomial for the given x
// Uses Horner's method <https://en.wikipedia.org/wiki/Horner%27s_method> to
// evaluate the polynomial at point x
func (p *polynomial) evaluate(x uint8) uint8 {
// Special case the origin
if x == 0 {
return p.coefficients[0]
}
// Compute the polynomial value using Horner's method.
degree := len(p.coefficients) - 1
out := p.coefficients[degree]
for i := degree - 1; i >= 0; i-- {
coeff := p.coefficients[i]
out = add(mult(out, x), coeff)
}
return out
}
// interpolatePolynomial takes N sample points and returns
// the value at a given x using a lagrange interpolation.
// An implementation of Lagrange interpolation
// <https://en.wikipedia.org/wiki/Lagrange_polynomial>
// For this particular implementation, x is always 0
func interpolatePolynomial(xSamples, ySamples []uint8, x uint8) uint8 {
limit := len(xSamples)
var result, basis uint8
for i := 0; i < limit; i++ {
basis = 1
for j := 0; j < limit; j++ {
if i == j {
continue
}
num := add(x, xSamples[j])
denom := add(xSamples[i], xSamples[j])
term := div(num, denom)
basis = mult(basis, term)
}
group := mult(ySamples[i], basis)
result = add(result, group)
}
return result
}
// div divides two numbers in GF(2^8)
// GF(2^8) division using log/exp tables
func div(a, b uint8) uint8 {
if b == 0 {
// leaks some timing information but we don't care anyways as this
// should never happen, hence the panic
panic("divide by zero")
}
var goodVal, zero uint8
logA := logTable[a]
logB := logTable[b]
diff := (int(logA) - int(logB)) % 255
if diff < 0 {
diff += 255
}
ret := expTable[diff]
// Ensure we return zero if a is zero but aren't subject to timing attacks
goodVal = ret
if subtle.ConstantTimeByteEq(a, 0) == 1 {
ret = zero
} else {
ret = goodVal
}
return ret
}
// mult multiplies two numbers in GF(2^8)
// GF(2^8) multiplication using log/exp tables
func mult(a, b uint8) (out uint8) {
var goodVal, zero uint8
log_a := logTable[a]
log_b := logTable[b]
sum := (int(log_a) + int(log_b)) % 255
ret := expTable[sum]
// Ensure we return zero if either a or b are zero but aren't subject to
// timing attacks
goodVal = ret
if subtle.ConstantTimeByteEq(a, 0) == 1 {
ret = zero
} else {
ret = goodVal
}
if subtle.ConstantTimeByteEq(b, 0) == 1 {
ret = zero
} else {
// This operation does not do anything logically useful. It
// only ensures a constant number of assignments to thwart
// timing attacks.
goodVal = zero
}
return ret
}
// add combines two numbers in GF(2^8)
// This can also be used for subtraction since it is symmetric.
func add(a, b uint8) uint8 {
return a ^ b
}
// Split takes an arbitrarily long secret and generates a `parts`
// number of shares, `threshold` of which are required to reconstruct
// the secret. The parts and threshold must be at least 2, and less
// than 256. The returned shares are each one byte longer than the secret
// as they attach a tag used to reconstruct the secret.
func Split(secret []byte, parts, threshold int) ([][]byte, error) {
// Sanity check the input
if parts < threshold {
return nil, fmt.Errorf("parts cannot be less than threshold")
}
if parts > 255 {
return nil, fmt.Errorf("parts cannot exceed 255")
}
if threshold < 2 {
return nil, fmt.Errorf("threshold must be at least 2")
}
if threshold > 255 {
return nil, fmt.Errorf("threshold cannot exceed 255")
}
if len(secret) == 0 {
return nil, fmt.Errorf("cannot split an empty secret")
}
// Generate random x coordinates for computing points. I don't know
// why random x coordinates are used, and I also don't know why
// a non-cryptographically secure source of randomness is used.
// As far as I know the x coordinates do not need to be random.
mathrand.Seed(time.Now().UnixNano())
xCoordinates := mathrand.Perm(255)
// Allocate the output array, initialize the final byte
// of the output with the offset. The representation of each
// output is {y1, y2, .., yN, x}.
out := make([][]byte, parts)
for idx := range out {
// Store the x coordinate for each part as its last byte
// Add 1 to the xCoordinate because if the x coordinate is 0,
// then the result of evaluating the polynomial at that point
// will be our secret
out[idx] = make([]byte, len(secret)+1)
out[idx][len(secret)] = uint8(xCoordinates[idx]) + 1
}
// Construct a random polynomial for each byte of the secret.
// Because we are using a field of size 256, we can only represent
// a single byte as the intercept of the polynomial, so we must
// use a new polynomial for each byte.
for idx, val := range secret {
// Create a random polynomial for each point.
// This polynomial crosses the y axis at `val`.
p, err := makePolynomial(val, uint8(threshold-1))
if err != nil {
return nil, fmt.Errorf("failed to generate polynomial: %w", err)
}
// Generate a `parts` number of (x,y) pairs
// We cheat by encoding the x value once as the final index,
// so that it only needs to be stored once.
for i := 0; i < parts; i++ {
// Add 1 to the xCoordinate because if it's 0,
// then the result of p.evaluate(x) will be our secret
x := uint8(xCoordinates[i]) + 1
// Evaluate the polynomial at x
y := p.evaluate(x)
out[i][idx] = y
}
}
// Return the encoded secrets
return out, nil
}
// Combine is used to reverse a Split and reconstruct a secret
// once a `threshold` number of parts are available.
func Combine(parts [][]byte) ([]byte, error) {
// Verify enough parts provided
if len(parts) < 2 {
return nil, fmt.Errorf("less than two parts cannot be used to reconstruct the secret")
}
// Verify the parts are all the same length
firstPartLen := len(parts[0])
if firstPartLen < 2 {
return nil, fmt.Errorf("parts must be at least two bytes")
}
for i := 1; i < len(parts); i++ {
if len(parts[i]) != firstPartLen {
return nil, fmt.Errorf("all parts must be the same length")
}
}
// Create a buffer to store the reconstructed secret
secret := make([]byte, firstPartLen-1)
// Buffer to store the samples
xSamples := make([]uint8, len(parts))
ySamples := make([]uint8, len(parts))
// Set the x value for each sample and ensure no x_sample values are the same,
// otherwise div() can be unhappy
// Check that we don't have any duplicate parts, that is, two or
// more parts with the same x coordinate.
checkMap := map[byte]bool{}
for i, part := range parts {
samp := part[firstPartLen-1]
if exists := checkMap[samp]; exists {
return nil, fmt.Errorf("duplicate part detected")
}
checkMap[samp] = true
xSamples[i] = samp
}
// Reconstruct each byte
for idx := range secret {
// Set the y value for each sample
for i, part := range parts {
ySamples[i] = part[idx]
}
// Use Lagrange interpolation to retrieve the free term
// of the original polynomial
val := interpolatePolynomial(xSamples, ySamples, 0)
// Evaluate the 0th value to get the intercept
secret[idx] = val
}
return secret, nil
}
07070100000095000081A400000000000000000000000162794F9300000E01000000000000000000000000000000000000002100000000sops-3.7.3/shamir/shamir_test.gopackage shamir
import (
"bytes"
"testing"
)
func TestSplit_invalid(t *testing.T) {
secret := []byte("test")
if _, err := Split(secret, 0, 0); err == nil {
t.Fatalf("expect error")
}
if _, err := Split(secret, 2, 3); err == nil {
t.Fatalf("expect error")
}
if _, err := Split(secret, 1000, 3); err == nil {
t.Fatalf("expect error")
}
if _, err := Split(secret, 10, 1); err == nil {
t.Fatalf("expect error")
}
if _, err := Split(nil, 3, 2); err == nil {
t.Fatalf("expect error")
}
}
func TestSplit(t *testing.T) {
secret := []byte("test")
out, err := Split(secret, 5, 3)
if err != nil {
t.Fatalf("err: %v", err)
}
if len(out) != 5 {
t.Fatalf("bad: %v", out)
}
for _, share := range out {
if len(share) != len(secret)+1 {
t.Fatalf("bad: %v", out)
}
}
}
func TestCombine_invalid(t *testing.T) {
// Not enough parts
if _, err := Combine(nil); err == nil {
t.Fatalf("should err")
}
// Mis-match in length
parts := [][]byte{
[]byte("foo"),
[]byte("ba"),
}
if _, err := Combine(parts); err == nil {
t.Fatalf("should err")
}
//Too short
parts = [][]byte{
[]byte("f"),
[]byte("b"),
}
if _, err := Combine(parts); err == nil {
t.Fatalf("should err")
}
parts = [][]byte{
[]byte("foo"),
[]byte("foo"),
}
if _, err := Combine(parts); err == nil {
t.Fatalf("should err")
}
}
func TestCombine(t *testing.T) {
secret := []byte("test")
out, err := Split(secret, 5, 3)
if err != nil {
t.Fatalf("err: %v", err)
}
// There is 5*4*3 possible choices,
// we will just brute force try them all
for i := 0; i < 5; i++ {
for j := 0; j < 5; j++ {
if j == i {
continue
}
for k := 0; k < 5; k++ {
if k == i || k == j {
continue
}
parts := [][]byte{out[i], out[j], out[k]}
recomb, err := Combine(parts)
if err != nil {
t.Fatalf("err: %v", err)
}
if !bytes.Equal(recomb, secret) {
t.Errorf("parts: (i:%d, j:%d, k:%d) %v", i, j, k, parts)
t.Fatalf("bad: %v %v", recomb, secret)
}
}
}
}
}
func TestField_Add(t *testing.T) {
if out := add(16, 16); out != 0 {
t.Fatalf("Bad: %v 16", out)
}
if out := add(3, 4); out != 7 {
t.Fatalf("Bad: %v 7", out)
}
}
func TestField_Mult(t *testing.T) {
if out := mult(3, 7); out != 9 {
t.Fatalf("Bad: %v 9", out)
}
if out := mult(3, 0); out != 0 {
t.Fatalf("Bad: %v 0", out)
}
if out := mult(0, 3); out != 0 {
t.Fatalf("Bad: %v 0", out)
}
}
func TestField_Divide(t *testing.T) {
if out := div(0, 7); out != 0 {
t.Fatalf("Bad: %v 0", out)
}
if out := div(3, 3); out != 1 {
t.Fatalf("Bad: %v 1", out)
}
if out := div(6, 3); out != 2 {
t.Fatalf("Bad: %v 2", out)
}
}
func TestPolynomial_Random(t *testing.T) {
p, err := makePolynomial(42, 2)
if err != nil {
t.Fatalf("err: %v", err)
}
if p.coefficients[0] != 42 {
t.Fatalf("bad: %v", p.coefficients)
}
}
func TestPolynomial_Eval(t *testing.T) {
p, err := makePolynomial(42, 1)
if err != nil {
t.Fatalf("err: %v", err)
}
if out := p.evaluate(0); out != 42 {
t.Fatalf("bad: %v", out)
}
out := p.evaluate(1)
exp := add(42, mult(1, p.coefficients[1]))
if out != exp {
t.Fatalf("bad: %v %v %v", out, exp, p.coefficients)
}
}
func TestInterpolate_Rand(t *testing.T) {
for i := 0; i < 256; i++ {
p, err := makePolynomial(uint8(i), 2)
if err != nil {
t.Fatalf("err: %v", err)
}
xVals := []uint8{1, 2, 3}
yVals := []uint8{p.evaluate(1), p.evaluate(2), p.evaluate(3)}
out := interpolatePolynomial(xVals, yVals, 0)
if out != uint8(i) {
t.Fatalf("Bad: %v %d", out, i)
}
}
}
07070100000096000081A400000000000000000000000162794F9300000DB9000000000000000000000000000000000000001C00000000sops-3.7.3/shamir/tables.gopackage shamir
// Tables taken from http://www.samiam.org/galois.html
// They use 0xe5 (229) as the generator
var (
// logTable provides the log(X)/log(g) at each index X
logTable = [256]uint8{
0x00, 0xff, 0xc8, 0x08, 0x91, 0x10, 0xd0, 0x36,
0x5a, 0x3e, 0xd8, 0x43, 0x99, 0x77, 0xfe, 0x18,
0x23, 0x20, 0x07, 0x70, 0xa1, 0x6c, 0x0c, 0x7f,
0x62, 0x8b, 0x40, 0x46, 0xc7, 0x4b, 0xe0, 0x0e,
0xeb, 0x16, 0xe8, 0xad, 0xcf, 0xcd, 0x39, 0x53,
0x6a, 0x27, 0x35, 0x93, 0xd4, 0x4e, 0x48, 0xc3,
0x2b, 0x79, 0x54, 0x28, 0x09, 0x78, 0x0f, 0x21,
0x90, 0x87, 0x14, 0x2a, 0xa9, 0x9c, 0xd6, 0x74,
0xb4, 0x7c, 0xde, 0xed, 0xb1, 0x86, 0x76, 0xa4,
0x98, 0xe2, 0x96, 0x8f, 0x02, 0x32, 0x1c, 0xc1,
0x33, 0xee, 0xef, 0x81, 0xfd, 0x30, 0x5c, 0x13,
0x9d, 0x29, 0x17, 0xc4, 0x11, 0x44, 0x8c, 0x80,
0xf3, 0x73, 0x42, 0x1e, 0x1d, 0xb5, 0xf0, 0x12,
0xd1, 0x5b, 0x41, 0xa2, 0xd7, 0x2c, 0xe9, 0xd5,
0x59, 0xcb, 0x50, 0xa8, 0xdc, 0xfc, 0xf2, 0x56,
0x72, 0xa6, 0x65, 0x2f, 0x9f, 0x9b, 0x3d, 0xba,
0x7d, 0xc2, 0x45, 0x82, 0xa7, 0x57, 0xb6, 0xa3,
0x7a, 0x75, 0x4f, 0xae, 0x3f, 0x37, 0x6d, 0x47,
0x61, 0xbe, 0xab, 0xd3, 0x5f, 0xb0, 0x58, 0xaf,
0xca, 0x5e, 0xfa, 0x85, 0xe4, 0x4d, 0x8a, 0x05,
0xfb, 0x60, 0xb7, 0x7b, 0xb8, 0x26, 0x4a, 0x67,
0xc6, 0x1a, 0xf8, 0x69, 0x25, 0xb3, 0xdb, 0xbd,
0x66, 0xdd, 0xf1, 0xd2, 0xdf, 0x03, 0x8d, 0x34,
0xd9, 0x92, 0x0d, 0x63, 0x55, 0xaa, 0x49, 0xec,
0xbc, 0x95, 0x3c, 0x84, 0x0b, 0xf5, 0xe6, 0xe7,
0xe5, 0xac, 0x7e, 0x6e, 0xb9, 0xf9, 0xda, 0x8e,
0x9a, 0xc9, 0x24, 0xe1, 0x0a, 0x15, 0x6b, 0x3a,
0xa0, 0x51, 0xf4, 0xea, 0xb2, 0x97, 0x9e, 0x5d,
0x22, 0x88, 0x94, 0xce, 0x19, 0x01, 0x71, 0x4c,
0xa5, 0xe3, 0xc5, 0x31, 0xbb, 0xcc, 0x1f, 0x2d,
0x3b, 0x52, 0x6f, 0xf6, 0x2e, 0x89, 0xf7, 0xc0,
0x68, 0x1b, 0x64, 0x04, 0x06, 0xbf, 0x83, 0x38}
// expTable provides the anti-log or exponentiation value
// for the equivalent index
expTable = [256]uint8{
0x01, 0xe5, 0x4c, 0xb5, 0xfb, 0x9f, 0xfc, 0x12,
0x03, 0x34, 0xd4, 0xc4, 0x16, 0xba, 0x1f, 0x36,
0x05, 0x5c, 0x67, 0x57, 0x3a, 0xd5, 0x21, 0x5a,
0x0f, 0xe4, 0xa9, 0xf9, 0x4e, 0x64, 0x63, 0xee,
0x11, 0x37, 0xe0, 0x10, 0xd2, 0xac, 0xa5, 0x29,
0x33, 0x59, 0x3b, 0x30, 0x6d, 0xef, 0xf4, 0x7b,
0x55, 0xeb, 0x4d, 0x50, 0xb7, 0x2a, 0x07, 0x8d,
0xff, 0x26, 0xd7, 0xf0, 0xc2, 0x7e, 0x09, 0x8c,
0x1a, 0x6a, 0x62, 0x0b, 0x5d, 0x82, 0x1b, 0x8f,
0x2e, 0xbe, 0xa6, 0x1d, 0xe7, 0x9d, 0x2d, 0x8a,
0x72, 0xd9, 0xf1, 0x27, 0x32, 0xbc, 0x77, 0x85,
0x96, 0x70, 0x08, 0x69, 0x56, 0xdf, 0x99, 0x94,
0xa1, 0x90, 0x18, 0xbb, 0xfa, 0x7a, 0xb0, 0xa7,
0xf8, 0xab, 0x28, 0xd6, 0x15, 0x8e, 0xcb, 0xf2,
0x13, 0xe6, 0x78, 0x61, 0x3f, 0x89, 0x46, 0x0d,
0x35, 0x31, 0x88, 0xa3, 0x41, 0x80, 0xca, 0x17,
0x5f, 0x53, 0x83, 0xfe, 0xc3, 0x9b, 0x45, 0x39,
0xe1, 0xf5, 0x9e, 0x19, 0x5e, 0xb6, 0xcf, 0x4b,
0x38, 0x04, 0xb9, 0x2b, 0xe2, 0xc1, 0x4a, 0xdd,
0x48, 0x0c, 0xd0, 0x7d, 0x3d, 0x58, 0xde, 0x7c,
0xd8, 0x14, 0x6b, 0x87, 0x47, 0xe8, 0x79, 0x84,
0x73, 0x3c, 0xbd, 0x92, 0xc9, 0x23, 0x8b, 0x97,
0x95, 0x44, 0xdc, 0xad, 0x40, 0x65, 0x86, 0xa2,
0xa4, 0xcc, 0x7f, 0xec, 0xc0, 0xaf, 0x91, 0xfd,
0xf7, 0x4f, 0x81, 0x2f, 0x5b, 0xea, 0xa8, 0x1c,
0x02, 0xd1, 0x98, 0x71, 0xed, 0x25, 0xe3, 0x24,
0x06, 0x68, 0xb3, 0x93, 0x2c, 0x6f, 0x3e, 0x6c,
0x0a, 0xb8, 0xce, 0xae, 0x74, 0xb1, 0x42, 0xb4,
0x1e, 0xd3, 0x49, 0xe9, 0x9c, 0xc8, 0xc6, 0xc7,
0x22, 0x6e, 0xdb, 0x20, 0xbf, 0x43, 0x51, 0x52,
0x66, 0xb2, 0x76, 0x60, 0xda, 0xc5, 0xf3, 0xf6,
0xaa, 0xcd, 0x9a, 0xa0, 0x75, 0x54, 0x0e, 0x01}
)
07070100000097000081A400000000000000000000000162794F93000000E4000000000000000000000000000000000000002100000000sops-3.7.3/shamir/tables_test.gopackage shamir
import "testing"
func TestTables(t *testing.T) {
for i := 1; i < 256; i++ {
logV := logTable[i]
expV := expTable[logV]
if expV != uint8(i) {
t.Fatalf("bad: %d log: %d exp: %d", i, logV, expV)
}
}
}
07070100000098000081A400000000000000000000000162794F9300005E78000000000000000000000000000000000000001300000000sops-3.7.3/sops.go/*
Package sops manages JSON, YAML and BINARY documents to be encrypted or decrypted.
This package should not be used directly. Instead, Sops users should install the
command line client via `go get -u go.mozilla.org/sops/v3/cmd/sops`, or use the
decryption helper provided at `go.mozilla.org/sops/v3/decrypt`.
We do not guarantee API stability for any package other than `go.mozilla.org/sops/v3/decrypt`.
A Sops document is a Tree composed of a data branch with arbitrary key/value pairs
and a metadata branch with encryption and integrity information.
In JSON and YAML formats, the structure of the cleartext tree is preserved, keys are
stored in cleartext and only values are encrypted. Keeping the values in cleartext
provides better readability when storing Sops documents in version controls, and allows
for merging competing changes on documents. This is a major difference between Sops
and other encryption tools that store documents as encrypted blobs.
In BINARY format, the cleartext data is treated as a single blob and the encrypted
document is in JSON format with a single `data` key and a single encrypted value.
Sops allows operators to encrypt their documents with multiple master keys. Each of
the master key defined in the document is able to decrypt it, allowing users to
share documents amongst themselves without sharing keys, or using a PGP key as a
backup for KMS.
In practice, this is achieved by generating a data key for each document that is used
to encrypt all values, and encrypting the data with each master key defined. Being
able to decrypt the data key gives access to the document.
The integrity of each document is guaranteed by calculating a Message Authentication Code
(MAC) that is stored encrypted by the data key. When decrypting a document, the MAC should
be recalculated and compared with the MAC stored in the document to verify that no
fraudulent changes have been applied. The MAC covers keys and values as well as their
ordering.
*/
package sops //import "go.mozilla.org/sops/v3"
import (
"crypto/rand"
"crypto/sha512"
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
"time"
"github.com/sirupsen/logrus"
"go.mozilla.org/sops/v3/audit"
"go.mozilla.org/sops/v3/keys"
"go.mozilla.org/sops/v3/keyservice"
"go.mozilla.org/sops/v3/logging"
"go.mozilla.org/sops/v3/shamir"
"golang.org/x/net/context"
)
// DefaultUnencryptedSuffix is the default suffix a TreeItem key has to end with for sops to leave its Value unencrypted
const DefaultUnencryptedSuffix = "_unencrypted"
type sopsError string
func (e sopsError) Error() string {
return string(e)
}
// MacMismatch occurs when the computed MAC does not match the expected ones
const MacMismatch = sopsError("MAC mismatch")
// MetadataNotFound occurs when the input file is malformed and doesn't have sops metadata in it
const MetadataNotFound = sopsError("sops metadata not found")
var log *logrus.Logger
func init() {
log = logging.NewLogger("SOPS")
}
// Cipher provides a way to encrypt and decrypt the data key used to encrypt and decrypt sops files, so that the
// data key can be stored alongside the encrypted content. A Cipher must be able to decrypt the values it encrypts.
type Cipher interface {
// Encrypt takes a plaintext, a key and additional data and returns the plaintext encrypted with the key, using the
// additional data for authentication
Encrypt(plaintext interface{}, key []byte, additionalData string) (ciphertext string, err error)
// Encrypt takes a ciphertext, a key and additional data and returns the ciphertext encrypted with the key, using
// the additional data for authentication
Decrypt(ciphertext string, key []byte, additionalData string) (plaintext interface{}, err error)
}
// Comment represents a comment in the sops tree for the file formats that actually support them.
type Comment struct {
Value string
}
// TreeItem is an item inside sops's tree
type TreeItem struct {
Key interface{}
Value interface{}
}
// TreeBranch is a branch inside sops's tree. It is a slice of TreeItems and is therefore ordered
type TreeBranch []TreeItem
// TreeBranches is a collection of TreeBranch
// Trees usually have more than one branch
type TreeBranches []TreeBranch
func valueFromPathAndLeaf(path []interface{}, leaf interface{}) interface{} {
switch component := path[0].(type) {
case int:
if len(path) == 1 {
return []interface{}{
leaf,
}
}
return []interface{}{
valueFromPathAndLeaf(path[1:], leaf),
}
default:
if len(path) == 1 {
return TreeBranch{
TreeItem{
Key: component,
Value: leaf,
},
}
}
return TreeBranch{
TreeItem{
Key: component,
Value: valueFromPathAndLeaf(path[1:], leaf),
},
}
}
}
func set(branch interface{}, path []interface{}, value interface{}) interface{} {
switch branch := branch.(type) {
case TreeBranch:
for i, item := range branch {
if item.Key == path[0] {
if len(path) == 1 {
branch[i].Value = value
} else {
branch[i].Value = set(item.Value, path[1:], value)
}
return branch
}
}
// Not found, need to add the next path entry to the branch
if len(path) == 1 {
return append(branch, TreeItem{Key: path[0], Value: value})
}
return valueFromPathAndLeaf(path, value)
case []interface{}:
position := path[0].(int)
if len(path) == 1 {
if position >= len(branch) {
return append(branch, value)
}
branch[position] = value
} else {
if position >= len(branch) {
branch = append(branch, valueFromPathAndLeaf(path[1:], value))
}
branch[position] = set(branch[position], path[1:], value)
}
return branch
default:
return valueFromPathAndLeaf(path, value)
}
}
// Set sets a value on a given tree for the specified path
func (branch TreeBranch) Set(path []interface{}, value interface{}) TreeBranch {
return set(branch, path, value).(TreeBranch)
}
// Tree is the data structure used by sops to represent documents internally
type Tree struct {
Metadata Metadata
Branches TreeBranches
// FilePath is the path of the file this struct represents
FilePath string
}
// Truncate truncates the tree to the path specified
func (branch TreeBranch) Truncate(path []interface{}) (interface{}, error) {
log.WithField("path", path).Info("Truncating tree")
var current interface{} = branch
for _, component := range path {
switch component := component.(type) {
case string:
found := false
for _, item := range current.(TreeBranch) {
if item.Key == component {
current = item.Value
found = true
break
}
}
if !found {
return nil, fmt.Errorf("component ['%s'] not found", component)
}
case int:
if reflect.ValueOf(current).Kind() != reflect.Slice {
return nil, fmt.Errorf("component [%d] is integer, but tree part is not a slice", component)
}
if reflect.ValueOf(current).Len() <= component {
return nil, fmt.Errorf("component [%d] accesses out of bounds", component)
}
current = reflect.ValueOf(current).Index(component).Interface()
}
}
return current, nil
}
func (branch TreeBranch) walkValue(in interface{}, path []string, onLeaves func(in interface{}, path []string) (interface{}, error)) (interface{}, error) {
switch in := in.(type) {
case string:
return onLeaves(in, path)
case []byte:
return onLeaves(string(in), path)
case int:
return onLeaves(in, path)
case bool:
return onLeaves(in, path)
case float64:
return onLeaves(in, path)
case Comment:
return onLeaves(in, path)
case TreeBranch:
return branch.walkBranch(in, path, onLeaves)
case []interface{}:
return branch.walkSlice(in, path, onLeaves)
case nil:
// the value returned remains the same since it doesn't make
// sense to encrypt or decrypt a nil value
return nil, nil
default:
return nil, fmt.Errorf("Cannot walk value, unknown type: %T", in)
}
}
func (branch TreeBranch) walkSlice(in []interface{}, path []string, onLeaves func(in interface{}, path []string) (interface{}, error)) ([]interface{}, error) {
for i, v := range in {
newV, err := branch.walkValue(v, path, onLeaves)
if err != nil {
return nil, err
}
in[i] = newV
}
return in, nil
}
func (branch TreeBranch) walkBranch(in TreeBranch, path []string, onLeaves func(in interface{}, path []string) (interface{}, error)) (TreeBranch, error) {
for i, item := range in {
if _, ok := item.Key.(Comment); ok {
enc, err := branch.walkValue(item.Key, path, onLeaves)
if err != nil {
return nil, err
}
if encComment, ok := enc.(Comment); ok {
in[i].Key = encComment
continue
} else if comment, ok := enc.(string); ok {
in[i].Key = Comment{Value: comment}
continue
} else {
return nil, fmt.Errorf("walkValue of Comment should be either Comment or string, was %T", enc)
}
}
key, ok := item.Key.(string)
if !ok {
return nil, fmt.Errorf("Tree contains a non-string key (type %T): %s. Only string keys are"+
"supported", item.Key, item.Key)
}
newV, err := branch.walkValue(item.Value, append(path, key), onLeaves)
if err != nil {
return nil, err
}
in[i].Value = newV
}
return in, nil
}
// Encrypt walks over the tree and encrypts all values with the provided cipher,
// except those whose key ends with the UnencryptedSuffix specified on the
// Metadata struct, those not ending with EncryptedSuffix, if EncryptedSuffix
// is provided (by default it is not), those not matching EncryptedRegex,
// if EncryptedRegex is provided (by default it is not) or those matching
// UnencryptedRegex, if UnencryptedRegex is provided (by default it is not).
// If encryption is successful, it returns the MAC for the encrypted tree.
func (tree Tree) Encrypt(key []byte, cipher Cipher) (string, error) {
audit.SubmitEvent(audit.EncryptEvent{
File: tree.FilePath,
})
hash := sha512.New()
walk := func(branch TreeBranch) error {
_, err := branch.walkBranch(branch, make([]string, 0), func(in interface{}, path []string) (interface{}, error) {
// Only add to MAC if not a comment
if _, ok := in.(Comment); !ok {
bytes, err := ToBytes(in)
if err != nil {
return nil, fmt.Errorf("Could not convert %s to bytes: %s", in, err)
}
hash.Write(bytes)
}
encrypted := true
if tree.Metadata.UnencryptedSuffix != "" {
for _, v := range path {
if strings.HasSuffix(v, tree.Metadata.UnencryptedSuffix) {
encrypted = false
break
}
}
}
if tree.Metadata.EncryptedSuffix != "" {
encrypted = false
for _, v := range path {
if strings.HasSuffix(v, tree.Metadata.EncryptedSuffix) {
encrypted = true
break
}
}
}
if tree.Metadata.UnencryptedRegex != "" {
for _, p := range path {
matched, _ := regexp.Match(tree.Metadata.UnencryptedRegex, []byte(p))
if matched {
encrypted = false
break
}
}
}
if tree.Metadata.EncryptedRegex != "" {
encrypted = false
for _, p := range path {
matched, _ := regexp.Match(tree.Metadata.EncryptedRegex, []byte(p))
if matched {
encrypted = true
break
}
}
}
if encrypted {
var err error
pathString := strings.Join(path, ":") + ":"
in, err = cipher.Encrypt(in, key, pathString)
if err != nil {
return nil, fmt.Errorf("Could not encrypt value: %s", err)
}
}
return in, nil
})
return err
}
for _, branch := range tree.Branches {
err := walk(branch)
if err != nil {
return "", fmt.Errorf("Error walking tree: %s", err)
}
}
return fmt.Sprintf("%X", hash.Sum(nil)), nil
}
// Decrypt walks over the tree and decrypts all values with the provided cipher,
// except those whose key ends with the UnencryptedSuffix specified on the Metadata struct,
// those not ending with EncryptedSuffix, if EncryptedSuffix is provided (by default it is not),
// those not matching EncryptedRegex, if EncryptedRegex is provided (by default it is not),
// or those matching UnencryptedRegex, if UnencryptedRegex is provided (by default it is not).
// If decryption is successful, it returns the MAC for the decrypted tree.
func (tree Tree) Decrypt(key []byte, cipher Cipher) (string, error) {
log.Debug("Decrypting tree")
audit.SubmitEvent(audit.DecryptEvent{
File: tree.FilePath,
})
hash := sha512.New()
walk := func(branch TreeBranch) error {
_, err := branch.walkBranch(branch, make([]string, 0), func(in interface{}, path []string) (interface{}, error) {
encrypted := true
if tree.Metadata.UnencryptedSuffix != "" {
for _, p := range path {
if strings.HasSuffix(p, tree.Metadata.UnencryptedSuffix) {
encrypted = false
break
}
}
}
if tree.Metadata.EncryptedSuffix != "" {
encrypted = false
for _, p := range path {
if strings.HasSuffix(p, tree.Metadata.EncryptedSuffix) {
encrypted = true
break
}
}
}
if tree.Metadata.UnencryptedRegex != "" {
for _, p := range path {
matched, _ := regexp.Match(tree.Metadata.UnencryptedRegex, []byte(p))
if matched {
encrypted = false
break
}
}
}
if tree.Metadata.EncryptedRegex != "" {
encrypted = false
for _, p := range path {
matched, _ := regexp.Match(tree.Metadata.EncryptedRegex, []byte(p))
if matched {
encrypted = true
break
}
}
}
var v interface{}
if encrypted {
var err error
pathString := strings.Join(path, ":") + ":"
if c, ok := in.(Comment); ok {
v, err = cipher.Decrypt(c.Value, key, pathString)
if err != nil {
// Assume the comment was not encrypted in the first place
log.WithField("comment", c.Value).
Warn("Found possibly unencrypted comment in file. " +
"This is to be expected if the file being " +
"decrypted was created with an older version of " +
"SOPS.")
v = c
}
} else {
v, err = cipher.Decrypt(in.(string), key, pathString)
if err != nil {
return nil, fmt.Errorf("Could not decrypt value: %s", err)
}
}
} else {
v = in
}
// Only add to MAC if not a comment
if _, ok := v.(Comment); !ok {
bytes, err := ToBytes(v)
if err != nil {
return nil, fmt.Errorf("Could not convert %s to bytes: %s", in, err)
}
hash.Write(bytes)
}
return v, nil
})
return err
}
for _, branch := range tree.Branches {
err := walk(branch)
if err != nil {
return "", fmt.Errorf("Error walking tree: %s", err)
}
}
return fmt.Sprintf("%X", hash.Sum(nil)), nil
}
// GenerateDataKey generates a new random data key and encrypts it with all MasterKeys.
func (tree Tree) GenerateDataKey() ([]byte, []error) {
newKey := make([]byte, 32)
_, err := rand.Read(newKey)
if err != nil {
return nil, []error{fmt.Errorf("Could not generate random key: %s", err)}
}
return newKey, tree.Metadata.UpdateMasterKeys(newKey)
}
// GenerateDataKeyWithKeyServices generates a new random data key and encrypts it with all MasterKeys.
func (tree *Tree) GenerateDataKeyWithKeyServices(svcs []keyservice.KeyServiceClient) ([]byte, []error) {
newKey := make([]byte, 32)
_, err := rand.Read(newKey)
if err != nil {
return nil, []error{fmt.Errorf("Could not generate random key: %s", err)}
}
return newKey, tree.Metadata.UpdateMasterKeysWithKeyServices(newKey, svcs)
}
// Metadata holds information about a file encrypted by sops
type Metadata struct {
LastModified time.Time
UnencryptedSuffix string
EncryptedSuffix string
UnencryptedRegex string
EncryptedRegex string
MessageAuthenticationCode string
Version string
KeyGroups []KeyGroup
// ShamirThreshold is the number of key groups required to recover the
// original data key
ShamirThreshold int
// DataKey caches the decrypted data key so it doesn't have to be decrypted with a master key every time it's needed
DataKey []byte
}
// KeyGroup is a slice of SOPS MasterKeys that all encrypt the same part of the data key
type KeyGroup []keys.MasterKey
// EncryptedFileLoader is the interface for loading of encrypted files. It provides a
// way to load encrypted SOPS files into the internal SOPS representation. Because it
// loads encrypted files, the returned data structure already contains all SOPS
// metadata.
type EncryptedFileLoader interface {
LoadEncryptedFile(in []byte) (Tree, error)
}
// PlainFileLoader is the interface for loading of plain text files. It provides a
// way to load unencrypted files into SOPS. Because the files it loads are
// unencrypted, the returned data structure does not contain any metadata.
type PlainFileLoader interface {
LoadPlainFile(in []byte) (TreeBranches, error)
}
// EncryptedFileEmitter is the interface for emitting encrypting files. It provides a
// way to emit encrypted files from the internal SOPS representation.
type EncryptedFileEmitter interface {
EmitEncryptedFile(Tree) ([]byte, error)
}
// PlainFileEmitter is the interface for emitting plain text files. It provides a way
// to emit plain text files from the internal SOPS representation so that they can be
// shown
type PlainFileEmitter interface {
EmitPlainFile(TreeBranches) ([]byte, error)
}
// ValueEmitter is the interface for emitting a value. It provides a way to emit
// values from the internal SOPS representation so that they can be shown
type ValueEmitter interface {
EmitValue(interface{}) ([]byte, error)
}
// Store is used to interact with files, both encrypted and unencrypted.
type Store interface {
EncryptedFileLoader
PlainFileLoader
EncryptedFileEmitter
PlainFileEmitter
ValueEmitter
}
// MasterKeyCount returns the number of master keys available
func (m *Metadata) MasterKeyCount() int {
count := 0
for _, group := range m.KeyGroups {
count += len(group)
}
return count
}
// UpdateMasterKeysWithKeyServices encrypts the data key with all master keys using the provided key services
func (m *Metadata) UpdateMasterKeysWithKeyServices(dataKey []byte, svcs []keyservice.KeyServiceClient) (errs []error) {
if len(svcs) == 0 {
return []error{
fmt.Errorf("no key services provided, cannot update master keys"),
}
}
var parts [][]byte
if len(m.KeyGroups) == 1 {
// If there's only one key group, we can't do Shamir. All keys
// in the group encrypt the whole data key.
parts = append(parts, dataKey)
} else {
var err error
if m.ShamirThreshold == 0 {
m.ShamirThreshold = len(m.KeyGroups)
}
log.WithFields(logrus.Fields{
"quorum": m.ShamirThreshold,
"parts": len(m.KeyGroups),
}).Info("Splitting data key with Shamir Secret Sharing")
parts, err = shamir.Split(dataKey, len(m.KeyGroups), int(m.ShamirThreshold))
if err != nil {
errs = append(errs, fmt.Errorf("could not split data key into parts for Shamir: %s", err))
return
}
if len(parts) != len(m.KeyGroups) {
errs = append(errs, fmt.Errorf("not enough parts obtained from Shamir: need %d, got %d", len(m.KeyGroups), len(parts)))
return
}
}
for i, group := range m.KeyGroups {
part := parts[i]
for _, key := range group {
svcKey := keyservice.KeyFromMasterKey(key)
var keyErrs []error
encrypted := false
for _, svc := range svcs {
rsp, err := svc.Encrypt(context.Background(), &keyservice.EncryptRequest{
Key: &svcKey,
Plaintext: part,
})
if err != nil {
keyErrs = append(keyErrs, fmt.Errorf("failed to encrypt new data key with master key %q: %w", key.ToString(), err))
continue
}
key.SetEncryptedDataKey(rsp.Ciphertext)
encrypted = true
// Only need to encrypt the key successfully with one service
break
}
if !encrypted {
errs = append(errs, keyErrs...)
}
}
}
m.DataKey = dataKey
return
}
// UpdateMasterKeys encrypts the data key with all master keys
func (m *Metadata) UpdateMasterKeys(dataKey []byte) (errs []error) {
return m.UpdateMasterKeysWithKeyServices(dataKey, []keyservice.KeyServiceClient{
keyservice.NewLocalClient(),
})
}
// GetDataKeyWithKeyServices retrieves the data key, asking KeyServices to decrypt it with each
// MasterKey in the Metadata's KeySources until one of them succeeds.
func (m Metadata) GetDataKeyWithKeyServices(svcs []keyservice.KeyServiceClient) ([]byte, error) {
if m.DataKey != nil {
return m.DataKey, nil
}
getDataKeyErr := getDataKeyError{
RequiredSuccessfulKeyGroups: m.ShamirThreshold,
GroupResults: make([]error, len(m.KeyGroups)),
}
var parts [][]byte
for i, group := range m.KeyGroups {
part, err := decryptKeyGroup(group, svcs)
if err == nil {
parts = append(parts, part)
}
getDataKeyErr.GroupResults[i] = err
}
var dataKey []byte
if len(m.KeyGroups) > 1 {
if len(parts) < m.ShamirThreshold {
return nil, &getDataKeyErr
}
var err error
dataKey, err = shamir.Combine(parts)
if err != nil {
return nil, fmt.Errorf("could not get data key from shamir parts: %s", err)
}
} else {
if len(parts) != 1 {
return nil, &getDataKeyErr
}
dataKey = parts[0]
}
log.Info("Data key recovered successfully")
m.DataKey = dataKey
return dataKey, nil
}
// decryptKeyGroup tries to decrypt the contents of the provided KeyGroup with
// any of the MasterKeys in the KeyGroup with any of the provided key services,
// returning as soon as one key service succeeds.
func decryptKeyGroup(group KeyGroup, svcs []keyservice.KeyServiceClient) ([]byte, error) {
var keyErrs []error
for _, key := range group {
part, err := decryptKey(key, svcs)
if err != nil {
keyErrs = append(keyErrs, err)
} else {
return part, nil
}
}
return nil, decryptKeyErrors(keyErrs)
}
// decryptKey tries to decrypt the contents of the provided MasterKey with any
// of the key services, returning as soon as one key service succeeds.
func decryptKey(key keys.MasterKey, svcs []keyservice.KeyServiceClient) ([]byte, error) {
svcKey := keyservice.KeyFromMasterKey(key)
var part []byte
decryptErr := decryptKeyError{
keyName: key.ToString(),
}
for _, svc := range svcs {
// All keys in a key group encrypt the same part, so as soon
// as we decrypt it successfully with one key, we need to
// proceed with the next group
var err error
if part == nil {
var rsp *keyservice.DecryptResponse
rsp, err = svc.Decrypt(
context.Background(),
&keyservice.DecryptRequest{
Ciphertext: key.EncryptedDataKey(),
Key: &svcKey,
})
if err == nil {
part = rsp.Plaintext
}
}
decryptErr.errs = append(decryptErr.errs, err)
}
if part != nil {
return part, nil
}
return nil, &decryptErr
}
// GetDataKey retrieves the data key from the first MasterKey in the Metadata's KeySources that's able to return it,
// using the local KeyService
func (m Metadata) GetDataKey() ([]byte, error) {
return m.GetDataKeyWithKeyServices([]keyservice.KeyServiceClient{
keyservice.NewLocalClient(),
})
}
// ToBytes converts a string, int, float or bool to a byte representation.
func ToBytes(in interface{}) ([]byte, error) {
switch in := in.(type) {
case string:
return []byte(in), nil
case int:
return []byte(strconv.Itoa(in)), nil
case float64:
return []byte(strconv.FormatFloat(in, 'f', -1, 64)), nil
case bool:
return []byte(strings.Title(strconv.FormatBool(in))), nil
case []byte:
return in, nil
case Comment:
return ToBytes(in.Value)
default:
return nil, fmt.Errorf("Could not convert unknown type %T to bytes", in)
}
}
// EmitAsMap will emit the tree branches as a map. This is used by the publish
// command for writing decrypted trees to various destinations. Should only be
// used for outputting to data structures in code.
func EmitAsMap(in TreeBranches) (map[string]interface{}, error) {
data := map[string]interface{}{}
for _, branch := range in {
for _, item := range branch {
if _, ok := item.Key.(Comment); ok {
continue
}
val, err := encodeValueForMap(item.Value)
if err != nil {
return nil, err
}
data[item.Key.(string)] = val
}
}
return data, nil
}
func encodeValueForMap(v interface{}) (interface{}, error) {
switch v := v.(type) {
case TreeBranch:
return EmitAsMap([]TreeBranch{v})
default:
return v, nil
}
}
07070100000099000081A400000000000000000000000162794F930000377F000000000000000000000000000000000000001800000000sops-3.7.3/sops_test.gopackage sops
import (
"bytes"
"fmt"
"reflect"
"testing"
"github.com/stretchr/testify/assert"
)
type reverseCipher struct{}
// reverse returns its argument string reversed rune-wise left to right.
func reverse(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
func (c reverseCipher) Encrypt(value interface{}, key []byte, path string) (string, error) {
b, err := ToBytes(value)
if err != nil {
return "", err
}
return reverse(string(b)), nil
}
func (c reverseCipher) Decrypt(value string, key []byte, path string) (plaintext interface{}, err error) {
if value == "error" {
return nil, fmt.Errorf("Error")
}
return reverse(value), nil
}
func TestUnencryptedSuffix(t *testing.T) {
branches := TreeBranches{
TreeBranch{
TreeItem{
Key: "foo_unencrypted",
Value: "bar",
},
TreeItem{
Key: "bar_unencrypted",
Value: TreeBranch{
TreeItem{
Key: "foo",
Value: "bar",
},
},
},
},
}
tree := Tree{Branches: branches, Metadata: Metadata{UnencryptedSuffix: "_unencrypted"}}
expected := TreeBranch{
TreeItem{
Key: "foo_unencrypted",
Value: "bar",
},
TreeItem{
Key: "bar_unencrypted",
Value: TreeBranch{
TreeItem{
Key: "foo",
Value: "bar",
},
},
},
}
cipher := reverseCipher{}
_, err := tree.Encrypt(bytes.Repeat([]byte("f"), 32), cipher)
if err != nil {
t.Errorf("Encrypting the tree failed: %s", err)
}
if !reflect.DeepEqual(tree.Branches[0], expected) {
t.Errorf("Trees don't match: \ngot \t\t%+v,\n expected \t\t%+v", tree.Branches[0], expected)
}
_, err = tree.Decrypt(bytes.Repeat([]byte("f"), 32), cipher)
if err != nil {
t.Errorf("Decrypting the tree failed: %s", err)
}
if !reflect.DeepEqual(tree.Branches[0], expected) {
t.Errorf("Trees don't match: \ngot\t\t\t%+v,\nexpected\t\t%+v", tree.Branches[0], expected)
}
}
func TestEncryptedSuffix(t *testing.T) {
branches := TreeBranches{
TreeBranch{
TreeItem{
Key: "foo_encrypted",
Value: "bar",
},
TreeItem{
Key: "bar",
Value: TreeBranch{
TreeItem{
Key: "foo",
Value: "bar",
},
},
},
},
}
tree := Tree{Branches: branches, Metadata: Metadata{EncryptedSuffix: "_encrypted"}}
expected := TreeBranch{
TreeItem{
Key: "foo_encrypted",
Value: "rab",
},
TreeItem{
Key: "bar",
Value: TreeBranch{
TreeItem{
Key: "foo",
Value: "bar",
},
},
},
}
cipher := reverseCipher{}
_, err := tree.Encrypt(bytes.Repeat([]byte("f"), 32), cipher)
if err != nil {
t.Errorf("Encrypting the tree failed: %s", err)
}
if !reflect.DeepEqual(tree.Branches[0], expected) {
t.Errorf("Trees don't match: \ngot \t\t%+v,\n expected \t\t%+v", tree.Branches[0], expected)
}
_, err = tree.Decrypt(bytes.Repeat([]byte("f"), 32), cipher)
if err != nil {
t.Errorf("Decrypting the tree failed: %s", err)
}
expected[0].Value = "bar"
if !reflect.DeepEqual(tree.Branches[0], expected) {
t.Errorf("Trees don't match: \ngot\t\t\t%+v,\nexpected\t\t%+v", tree.Branches[0], expected)
}
}
func TestEncryptedRegex(t *testing.T) {
branches := TreeBranches{
TreeBranch{
TreeItem{
Key: "enc:foo",
Value: "bar",
},
TreeItem{
Key: "bar",
Value: TreeBranch{
TreeItem{
Key: "foo",
Value: "bar",
},
},
},
},
}
tree := Tree{Branches: branches, Metadata: Metadata{EncryptedRegex: "^enc:"}}
expected := TreeBranch{
TreeItem{
Key: "enc:foo",
Value: "rab",
},
TreeItem{
Key: "bar",
Value: TreeBranch{
TreeItem{
Key: "foo",
Value: "bar",
},
},
},
}
cipher := reverseCipher{}
_, err := tree.Encrypt(bytes.Repeat([]byte("f"), 32), cipher)
if err != nil {
t.Errorf("Encrypting the tree failed: %s", err)
}
if !reflect.DeepEqual(tree.Branches[0], expected) {
t.Errorf("Trees don't match: \ngot \t\t%+v,\n expected \t\t%+v", tree.Branches[0], expected)
}
_, err = tree.Decrypt(bytes.Repeat([]byte("f"), 32), cipher)
if err != nil {
t.Errorf("Decrypting the tree failed: %s", err)
}
expected[0].Value = "bar"
if !reflect.DeepEqual(tree.Branches[0], expected) {
t.Errorf("Trees don't match: \ngot\t\t\t%+v,\nexpected\t\t%+v", tree.Branches[0], expected)
}
}
func TestUnencryptedRegex(t *testing.T) {
branches := TreeBranches{
TreeBranch{
TreeItem{
Key: "dec:foo",
Value: "bar",
},
TreeItem{
Key: "dec:bar",
Value: TreeBranch{
TreeItem{
Key: "foo",
Value: "bar",
},
},
},
},
}
tree := Tree{Branches: branches, Metadata: Metadata{UnencryptedRegex: "^dec:"}}
expected := TreeBranch{
TreeItem{
Key: "dec:foo",
Value: "bar",
},
TreeItem{
Key: "dec:bar",
Value: TreeBranch{
TreeItem{
Key: "foo",
Value: "bar",
},
},
},
}
cipher := reverseCipher{}
_, err := tree.Encrypt(bytes.Repeat([]byte("f"), 32), cipher)
if err != nil {
t.Errorf("Encrypting the tree failed: %s", err)
}
// expected[1].Value[] = "bar"
if !reflect.DeepEqual(tree.Branches[0], expected) {
t.Errorf("Trees don't match: \ngot \t\t%+v,\n expected \t\t%+v", tree.Branches[0], expected)
}
_, err = tree.Decrypt(bytes.Repeat([]byte("f"), 32), cipher)
if err != nil {
t.Errorf("Decrypting the tree failed: %s", err)
}
if !reflect.DeepEqual(tree.Branches[0], expected) {
t.Errorf("Trees don't match: \ngot\t\t\t%+v,\nexpected\t\t%+v", tree.Branches[0], expected)
}
}
type MockCipher struct{}
func (m MockCipher) Encrypt(value interface{}, key []byte, path string) (string, error) {
return "a", nil
}
func (m MockCipher) Decrypt(value string, key []byte, path string) (interface{}, error) {
return "a", nil
}
func TestEncrypt(t *testing.T) {
branches := TreeBranches{
TreeBranch{
TreeItem{
Key: "foo",
Value: "bar",
},
TreeItem{
Key: "baz",
Value: TreeBranch{
TreeItem{
Key: "bar",
Value: 5,
},
},
},
TreeItem{
Key: "bar",
Value: false,
},
TreeItem{
Key: "foobar",
Value: 2.12,
},
TreeItem{
Key: "barfoo",
Value: nil,
},
},
TreeBranch{
TreeItem{
Key: "foo2",
Value: "bar",
},
},
TreeBranch{
TreeItem{
Key: "foo3",
Value: "bar",
},
},
}
expected := TreeBranches{
TreeBranch{
TreeItem{
Key: "foo",
Value: "a",
},
TreeItem{
Key: "baz",
Value: TreeBranch{
TreeItem{
Key: "bar",
Value: "a",
},
},
},
TreeItem{
Key: "bar",
Value: "a",
},
TreeItem{
Key: "foobar",
Value: "a",
},
TreeItem{
Key: "barfoo",
Value: nil,
},
},
TreeBranch{
TreeItem{
Key: "foo2",
Value: "a",
},
},
TreeBranch{
TreeItem{
Key: "foo3",
Value: "a",
},
},
}
tree := Tree{Branches: branches, Metadata: Metadata{UnencryptedSuffix: DefaultUnencryptedSuffix}}
tree.Encrypt(bytes.Repeat([]byte{'f'}, 32), MockCipher{})
if !reflect.DeepEqual(tree.Branches, expected) {
t.Errorf("%s does not equal expected tree: %s", tree.Branches, expected)
}
}
func TestDecrypt(t *testing.T) {
branches := TreeBranches{
TreeBranch{
TreeItem{
Key: "foo",
Value: "bar",
},
TreeItem{
Key: "baz",
Value: TreeBranch{
TreeItem{
Key: "bar",
Value: "5",
},
},
},
TreeItem{
Key: "bar",
Value: "false",
},
TreeItem{
Key: "foobar",
Value: "2.12",
},
TreeItem{
Key: "barfoo",
Value: nil,
},
},
TreeBranch{
TreeItem{
Key: "foo",
Value: "bar",
},
TreeItem{
Key: "baz",
Value: TreeBranch{
TreeItem{
Key: "bar",
Value: "6",
},
},
},
},
TreeBranch{
TreeItem{
Key: "foo3",
Value: "bar",
},
},
}
expected := TreeBranches{
TreeBranch{
TreeItem{
Key: "foo",
Value: "a",
},
TreeItem{
Key: "baz",
Value: TreeBranch{
TreeItem{
Key: "bar",
Value: "a",
},
},
},
TreeItem{
Key: "bar",
Value: "a",
},
TreeItem{
Key: "foobar",
Value: "a",
},
TreeItem{
Key: "barfoo",
Value: nil,
},
},
TreeBranch{
TreeItem{
Key: "foo",
Value: "a",
},
TreeItem{
Key: "baz",
Value: TreeBranch{
TreeItem{
Key: "bar",
Value: "a",
},
},
},
},
TreeBranch{
TreeItem{
Key: "foo3",
Value: "a",
},
},
}
tree := Tree{Branches: branches, Metadata: Metadata{UnencryptedSuffix: DefaultUnencryptedSuffix}}
tree.Decrypt(bytes.Repeat([]byte{'f'}, 32), MockCipher{})
if !reflect.DeepEqual(tree.Branches, expected) {
t.Errorf("%s does not equal expected tree: %s", tree.Branches[0], expected)
}
}
func TestTruncateTree(t *testing.T) {
tree := TreeBranch{
TreeItem{
Key: "foo",
Value: 2,
},
TreeItem{
Key: "bar",
Value: TreeBranch{
TreeItem{
Key: "foobar",
Value: []int{
1,
2,
3,
4,
},
},
},
},
}
expected := 3
result, err := tree.Truncate([]interface{}{
"bar",
"foobar",
2,
})
assert.Equal(t, nil, err)
assert.Equal(t, expected, result)
}
func TestEncryptComments(t *testing.T) {
tree := Tree{
Branches: TreeBranches{
TreeBranch{
TreeItem{
Key: Comment{"foo"},
Value: nil,
},
TreeItem{
Key: "list",
Value: []interface{}{
"1",
Comment{"bar"},
"2",
},
},
},
},
Metadata: Metadata{
UnencryptedSuffix: DefaultUnencryptedSuffix,
},
}
tree.Encrypt(bytes.Repeat([]byte{'f'}, 32), reverseCipher{})
assert.Equal(t, "oof", tree.Branches[0][0].Key.(Comment).Value)
assert.Equal(t, "rab", tree.Branches[0][1].Value.([]interface{})[1])
}
func TestDecryptComments(t *testing.T) {
tree := Tree{
Branches: TreeBranches{
TreeBranch{
TreeItem{
Key: Comment{"oof"},
Value: nil,
},
TreeItem{
Key: "list",
Value: []interface{}{
"1",
Comment{"rab"},
"2",
},
},
TreeItem{
Key: "list",
Value: nil,
},
},
},
Metadata: Metadata{
UnencryptedSuffix: DefaultUnencryptedSuffix,
},
}
tree.Decrypt(bytes.Repeat([]byte{'f'}, 32), reverseCipher{})
assert.Equal(t, "foo", tree.Branches[0][0].Key.(Comment).Value)
assert.Equal(t, "bar", tree.Branches[0][1].Value.([]interface{})[1])
}
func TestDecryptUnencryptedComments(t *testing.T) {
tree := Tree{
Branches: TreeBranches{
TreeBranch{
TreeItem{
// We use `error` to simulate an error decrypting, the fake cipher will error in this case
Key: Comment{"error"},
Value: nil,
},
},
},
Metadata: Metadata{},
}
tree.Decrypt(bytes.Repeat([]byte{'f'}, 32), reverseCipher{})
assert.Equal(t, "error", tree.Branches[0][0].Key.(Comment).Value)
}
func TestSetNewKey(t *testing.T) {
branch := TreeBranch{
TreeItem{
Key: "foo",
Value: TreeBranch{
TreeItem{
Key: "bar",
Value: TreeBranch{
TreeItem{
Key: "baz",
Value: "foobar",
},
},
},
},
},
}
set := branch.Set([]interface{}{"foo", "bar", "foo"}, "hello")
assert.Equal(t, "hello", set[0].Value.(TreeBranch)[0].Value.(TreeBranch)[1].Value)
}
func TestSetArrayDeepNew(t *testing.T) {
branch := TreeBranch{
TreeItem{
Key: "foo",
Value: []interface{}{
"one",
"two",
},
},
}
set := branch.Set([]interface{}{"foo", 2, "bar"}, "hello")
assert.Equal(t, "hello", set[0].Value.([]interface{})[2].(TreeBranch)[0].Value)
}
func TestSetNewKeyDeep(t *testing.T) {
branch := TreeBranch{
TreeItem{
Key: "foo",
Value: "bar",
},
}
set := branch.Set([]interface{}{"foo", "bar", "baz"}, "hello")
assert.Equal(t, "hello", set[0].Value.(TreeBranch)[0].Value.(TreeBranch)[0].Value)
}
func TestSetNewKeyOnEmptyBranch(t *testing.T) {
branch := TreeBranch{}
set := branch.Set([]interface{}{"foo", "bar", "baz"}, "hello")
assert.Equal(t, "hello", set[0].Value.(TreeBranch)[0].Value.(TreeBranch)[0].Value)
}
func TestSetArray(t *testing.T) {
branch := TreeBranch{
TreeItem{
Key: "foo",
Value: []interface{}{
"one",
"two",
"three",
},
},
}
set := branch.Set([]interface{}{"foo", 0}, "uno")
assert.Equal(t, "uno", set[0].Value.([]interface{})[0])
}
func TestSetArrayNew(t *testing.T) {
branch := TreeBranch{}
set := branch.Set([]interface{}{"foo", 0, 0}, "uno")
assert.Equal(t, "uno", set[0].Value.([]interface{})[0].([]interface{})[0])
}
func TestSetExisting(t *testing.T) {
branch := TreeBranch{
TreeItem{
Key: "foo",
Value: "foobar",
},
}
set := branch.Set([]interface{}{"foo"}, "bar")
assert.Equal(t, "bar", set[0].Value)
}
func TestSetArrayLeafNewItem(t *testing.T) {
branch := TreeBranch{
TreeItem{
Key: "array",
Value: []interface{}{},
},
}
set := branch.Set([]interface{}{"array", 2}, "hello")
assert.Equal(t, TreeBranch{
TreeItem{
Key: "array",
Value: []interface{}{
"hello",
},
},
}, set)
}
func TestSetArrayNonLeaf(t *testing.T) {
branch := TreeBranch{
TreeItem{
Key: "array",
Value: []interface{}{
1,
},
},
}
set := branch.Set([]interface{}{"array", 0, "hello"}, "hello")
assert.Equal(t, TreeBranch{
TreeItem{
Key: "array",
Value: []interface{}{
TreeBranch{
TreeItem{
Key: "hello",
Value: "hello",
},
},
},
},
}, set)
}
func TestEmitAsMap(t *testing.T) {
expected := map[string]interface{}{
"foobar": "barfoo",
"number": 42,
"foo": map[string]interface{}{
"bar": map[string]interface{}{
"baz": "foobar",
},
},
}
branches := TreeBranches{
TreeBranch{
TreeItem{
Key: "foobar",
Value: "barfoo",
},
TreeItem{
Key: "number",
Value: 42,
},
},
TreeBranch{
TreeItem{
Key: "foo",
Value: TreeBranch{
TreeItem{
Key: "bar",
Value: TreeBranch{
TreeItem{
Key: "baz",
Value: "foobar",
},
},
},
},
},
},
}
data, err := EmitAsMap(branches)
if assert.NoError(t, err) {
assert.Equal(t, expected, data)
}
}
0707010000009A000041ED00000000000000000000000662794F9300000000000000000000000000000000000000000000001200000000sops-3.7.3/stores0707010000009B000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001900000000sops-3.7.3/stores/dotenv0707010000009C000081A400000000000000000000000162794F9300001278000000000000000000000000000000000000002200000000sops-3.7.3/stores/dotenv/store.gopackage dotenv //import "go.mozilla.org/sops/v3/stores/dotenv"
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/stores"
)
// SopsPrefix is the prefix for all metadatada entry keys
const SopsPrefix = "sops_"
// Store handles storage of dotenv data
type Store struct {
}
// LoadEncryptedFile loads an encrypted file's bytes onto a sops.Tree runtime object
func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
branches, err := store.LoadPlainFile(in)
if err != nil {
return sops.Tree{}, err
}
var resultBranch sops.TreeBranch
mdMap := make(map[string]interface{})
for _, item := range branches[0] {
switch key := item.Key.(type) {
case string:
if strings.HasPrefix(key, SopsPrefix) {
key = key[len(SopsPrefix):]
mdMap[key] = item.Value
} else {
resultBranch = append(resultBranch, item)
}
case sops.Comment:
resultBranch = append(resultBranch, item)
default:
panic(fmt.Sprintf("Unexpected type: %T (value %#v)", key, key))
}
}
metadata, err := mapToMetadata(mdMap)
if err != nil {
return sops.Tree{}, err
}
internalMetadata, err := metadata.ToInternal()
if err != nil {
return sops.Tree{}, err
}
return sops.Tree{
Branches: sops.TreeBranches{
resultBranch,
},
Metadata: internalMetadata,
}, nil
}
// LoadPlainFile returns the contents of a plaintext file loaded onto a
// sops runtime object
func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
var branches sops.TreeBranches
var branch sops.TreeBranch
for _, line := range bytes.Split(in, []byte("\n")) {
if len(line) == 0 {
continue
}
if line[0] == '#' {
branch = append(branch, sops.TreeItem{
Key: sops.Comment{string(line[1:])},
Value: nil,
})
} else {
pos := bytes.Index(line, []byte("="))
if pos == -1 {
return nil, fmt.Errorf("invalid dotenv input line: %s", line)
}
branch = append(branch, sops.TreeItem{
Key: string(line[:pos]),
Value: strings.Replace(string(line[pos+1:]), "\\n", "\n", -1),
})
}
}
branches = append(branches, branch)
return branches, nil
}
// EmitEncryptedFile returns the encrypted file's bytes corresponding to a sops
// runtime object
func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
metadata := stores.MetadataFromInternal(in.Metadata)
mdItems, err := metadataToMap(metadata)
if err != nil {
return nil, err
}
for key, value := range mdItems {
if value == nil {
continue
}
in.Branches[0] = append(in.Branches[0], sops.TreeItem{Key: SopsPrefix + key, Value: value})
}
return store.EmitPlainFile(in.Branches)
}
// EmitPlainFile returns the plaintext file's bytes corresponding to a sops
// runtime object
func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
buffer := bytes.Buffer{}
for _, item := range in[0] {
if isComplexValue(item.Value) {
return nil, fmt.Errorf("cannot use complex value in dotenv file: %s", item.Value)
}
var line string
if comment, ok := item.Key.(sops.Comment); ok {
line = fmt.Sprintf("#%s\n", comment.Value)
} else {
value := strings.Replace(item.Value.(string), "\n", "\\n", -1)
line = fmt.Sprintf("%s=%s\n", item.Key, value)
}
buffer.WriteString(line)
}
return buffer.Bytes(), nil
}
// EmitValue returns a single value as bytes
func (Store) EmitValue(v interface{}) ([]byte, error) {
if s, ok := v.(string); ok {
return []byte(s), nil
}
return nil, fmt.Errorf("the dotenv store only supports emitting strings, got %T", v)
}
// EmitExample returns the bytes corresponding to an example Flat Tree runtime object
func (store *Store) EmitExample() []byte {
bytes, err := store.EmitPlainFile(stores.ExampleFlatTree.Branches)
if err != nil {
panic(err)
}
return bytes
}
func metadataToMap(md stores.Metadata) (map[string]interface{}, error) {
var mdMap map[string]interface{}
inrec, err := json.Marshal(md)
if err != nil {
return nil, err
}
err = json.Unmarshal(inrec, &mdMap)
if err != nil {
return nil, err
}
flat := stores.Flatten(mdMap)
for k, v := range flat {
if s, ok := v.(string); ok {
flat[k] = strings.Replace(s, "\n", "\\n", -1)
}
}
return flat, nil
}
func mapToMetadata(m map[string]interface{}) (stores.Metadata, error) {
for k, v := range m {
if s, ok := v.(string); ok {
m[k] = strings.Replace(s, "\\n", "\n", -1)
}
}
m = stores.Unflatten(m)
var md stores.Metadata
inrec, err := json.Marshal(m)
if err != nil {
return md, err
}
err = json.Unmarshal(inrec, &md)
return md, err
}
func isComplexValue(v interface{}) bool {
switch v.(type) {
case []interface{}:
return true
case sops.TreeBranch:
return true
}
return false
}
0707010000009D000081A400000000000000000000000162794F930000048A000000000000000000000000000000000000002700000000sops-3.7.3/stores/dotenv/store_test.gopackage dotenv
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"go.mozilla.org/sops/v3"
)
var PLAIN = []byte(strings.TrimLeft(`
VAR1=val1
VAR2=val2
#comment
VAR3_unencrypted=val3
VAR4=val4\nval4
`, "\n"))
var BRANCH = sops.TreeBranch{
sops.TreeItem{
Key: "VAR1",
Value: "val1",
},
sops.TreeItem{
Key: "VAR2",
Value: "val2",
},
sops.TreeItem{
Key: sops.Comment{"comment"},
Value: nil,
},
sops.TreeItem{
Key: "VAR3_unencrypted",
Value: "val3",
},
sops.TreeItem{
Key: "VAR4",
Value: "val4\nval4",
},
}
func TestLoadPlainFile(t *testing.T) {
branches, err := (&Store{}).LoadPlainFile(PLAIN)
assert.Nil(t, err)
assert.Equal(t, BRANCH, branches[0])
}
func TestEmitPlainFile(t *testing.T) {
branches := sops.TreeBranches{
BRANCH,
}
bytes, err := (&Store{}).EmitPlainFile(branches)
assert.Nil(t, err)
assert.Equal(t, PLAIN, bytes)
}
func TestEmitValueString(t *testing.T) {
bytes, err := (&Store{}).EmitValue("hello")
assert.Nil(t, err)
assert.Equal(t, []byte("hello"), bytes)
}
func TestEmitValueNonstring(t *testing.T) {
_, err := (&Store{}).EmitValue(BRANCH)
assert.NotNil(t, err)
}
0707010000009E000081A400000000000000000000000162794F93000013C4000000000000000000000000000000000000001D00000000sops-3.7.3/stores/flatten.gopackage stores
import (
"fmt"
"strconv"
"strings"
)
const mapSeparator = "__map_"
const listSeparator = "__list_"
// flattenAndMerge flattens the provided value and merges into the
// into map using prefix
func flattenAndMerge(into map[string]interface{}, prefix string, value interface{}) {
flattenedValue := flattenValue(value)
if flattenedValue, ok := flattenedValue.(map[string]interface{}); ok {
for flatK, flatV := range flattenedValue {
into[prefix+flatK] = flatV
}
} else {
into[prefix] = value
}
}
func flattenValue(value interface{}) interface{} {
var output interface{}
switch value := value.(type) {
case map[string]interface{}:
newMap := make(map[string]interface{})
for k, v := range value {
flattenAndMerge(newMap, mapSeparator+k, v)
}
output = newMap
case []interface{}:
newMap := make(map[string]interface{})
for i, v := range value {
flattenAndMerge(newMap, listSeparator+fmt.Sprintf("%d", i), v)
}
output = newMap
default:
output = value
}
return output
}
// Flatten flattens a map with potentially nested maps into a flat
// map. Only string keys are allowed on both the top-level map and
// child maps.
func Flatten(in map[string]interface{}) map[string]interface{} {
newMap := make(map[string]interface{})
for k, v := range in {
if flat, ok := flattenValue(v).(map[string]interface{}); ok {
for flatK, flatV := range flat {
newMap[k+flatK] = flatV
}
} else {
newMap[k] = v
}
}
return newMap
}
type token interface{}
type mapToken struct {
key string
}
type listToken struct {
position int
}
// tokenize converts a path generated by Flatten to be used as a key
// in the flattened map, and converts it to a slice of tokens
func tokenize(path string) []token {
const (
StateNormal = 0
StateMap = iota
StateList = iota
)
var tokens []token
state := StateNormal
lastTokenEnd := 0
i := 0
finishPrevToken := func() {
var t token
switch state {
case StateNormal:
t = mapToken{path[lastTokenEnd:i]}
case StateMap:
t = mapToken{path[lastTokenEnd+len(mapSeparator) : i]}
case StateList:
pos, _ := strconv.Atoi(path[lastTokenEnd+len(listSeparator) : i])
t = listToken{pos}
}
lastTokenEnd = i
tokens = append(tokens, t)
}
for i < len(path) {
if strings.HasPrefix(path[i:], mapSeparator) {
finishPrevToken()
state = StateMap
i += len(mapSeparator)
} else if strings.HasPrefix(path[i:], listSeparator) {
finishPrevToken()
state = StateList
i += len(listSeparator)
} else {
i++
}
}
finishPrevToken()
return tokens
}
// unflatten takes the currentNode, currentToken, nextToken and value
// and populates currentNode such that currentToken can be considered
// processed. It inspects nextToken to decide what type to allocate
// and assign under currentNode.
func unflatten(currentNode interface{}, currentToken, nextToken token, value interface{}) interface{} {
switch currentToken := currentToken.(type) {
case mapToken:
currentNode := currentNode.(map[string]interface{})
switch nextToken := nextToken.(type) {
case mapToken:
if _, ok := currentNode[currentToken.key]; !ok {
currentNode[currentToken.key] = make(map[string]interface{})
}
next := currentNode[currentToken.key].(map[string]interface{})
return next
case listToken:
if _, ok := currentNode[currentToken.key]; !ok {
currentNode[currentToken.key] = make([]interface{}, nextToken.position+1)
}
next := currentNode[currentToken.key].([]interface{})
if nextToken.position >= len(next) {
// Grow the slice and reassign it
newNext := make([]interface{}, nextToken.position+1)
copy(newNext, next)
next = newNext
currentNode[currentToken.key] = next
}
return next
default:
currentNode[currentToken.key] = value
}
case listToken:
currentNode := currentNode.([]interface{})
switch nextToken := nextToken.(type) {
case mapToken:
if currentNode[currentToken.position] == nil {
currentNode[currentToken.position] = make(map[string]interface{})
}
next := currentNode[currentToken.position].(map[string]interface{})
return next
case listToken:
if currentNode[currentToken.position] == nil {
currentNode[currentToken.position] = make([]interface{}, nextToken.position+1)
}
next := currentNode[currentToken.position].([]interface{})
if nextToken.position >= len(next) {
// Grow the slice and reassign it
newNext := make([]interface{}, nextToken.position+1)
copy(newNext, next)
next = newNext
currentNode[currentToken.position] = next
}
return next
default:
currentNode[currentToken.position] = value
}
}
return nil
}
// Unflatten unflattens a map flattened by Flatten
func Unflatten(in map[string]interface{}) map[string]interface{} {
newMap := make(map[string]interface{})
for k, v := range in {
var current interface{} = newMap
tokens := append(tokenize(k), nil)
for i := 0; i < len(tokens)-1; i++ {
current = unflatten(current, tokens[i], tokens[i+1], v)
}
}
return newMap
}
0707010000009F000081A400000000000000000000000162794F9300000D66000000000000000000000000000000000000002200000000sops-3.7.3/stores/flatten_test.gopackage stores
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestFlat(t *testing.T) {
input := map[string]interface{}{
"foo": "bar",
}
expected := map[string]interface{}{
"foo": "bar",
}
flattened := Flatten(input)
assert.Equal(t, expected, flattened)
unflattened := Unflatten(flattened)
assert.Equal(t, input, unflattened)
}
func TestMap(t *testing.T) {
input := map[string]interface{}{
"foo": map[string]interface{}{
"bar": 0,
"baz": 0,
},
}
expected := map[string]interface{}{
"foo" + mapSeparator + "bar": 0,
"foo" + mapSeparator + "baz": 0,
}
flattened := Flatten(input)
assert.Equal(t, expected, flattened)
unflattened := Unflatten(flattened)
assert.Equal(t, input, unflattened)
}
func TestFlattenMapMoreNesting(t *testing.T) {
input := map[string]interface{}{
"foo": map[string]interface{}{
"bar": map[string]interface{}{
"baz": 0,
},
},
}
expected := map[string]interface{}{
"foo" + mapSeparator + "bar" + mapSeparator + "baz": 0,
}
flattened := Flatten(input)
assert.Equal(t, expected, flattened)
unflattened := Unflatten(flattened)
assert.Equal(t, input, unflattened)
}
func TestFlattenList(t *testing.T) {
input := map[string]interface{}{
"foo": []interface{}{
0,
},
}
expected := map[string]interface{}{
"foo" + listSeparator + "0": 0,
}
flattened := Flatten(input)
assert.Equal(t, expected, flattened)
unflattened := Unflatten(flattened)
assert.Equal(t, input, unflattened)
}
func TestFlattenListWithMap(t *testing.T) {
input := map[string]interface{}{
"foo": []interface{}{
map[string]interface{}{
"bar": 0,
},
},
}
expected := map[string]interface{}{
"foo" + listSeparator + "0" + mapSeparator + "bar": 0,
}
flattened := Flatten(input)
assert.Equal(t, expected, flattened)
unflattened := Unflatten(flattened)
assert.Equal(t, input, unflattened)
}
func TestFlatten(t *testing.T) {
input := map[string]interface{}{
"foo": "bar",
"baz": map[string]interface{}{
"foo": 2,
"bar": map[string]interface{}{
"foo": 2,
},
},
"qux": []interface{}{
"hello", 1, 2,
},
}
expected := map[string]interface{}{
"foo": "bar",
"baz" + mapSeparator + "foo": 2,
"baz" + mapSeparator + "bar" + mapSeparator + "foo": 2,
"qux" + listSeparator + "0": "hello",
"qux" + listSeparator + "1": 1,
"qux" + listSeparator + "2": 2,
}
flattened := Flatten(input)
assert.Equal(t, expected, flattened)
unflattened := Unflatten(flattened)
assert.Equal(t, input, unflattened)
}
func TestTokenizeFlat(t *testing.T) {
input := "bar"
expected := []token{mapToken{"bar"}}
tokenized := tokenize(input)
assert.Equal(t, expected, tokenized)
}
func TestTokenizeMap(t *testing.T) {
input := "bar" + mapSeparator + "foo"
expected := []token{mapToken{"bar"}, mapToken{"foo"}}
tokenized := tokenize(input)
assert.Equal(t, expected, tokenized)
}
func TestTokenizeList(t *testing.T) {
input := "bar" + listSeparator + "10"
expected := []token{mapToken{"bar"}, listToken{10}}
tokenized := tokenize(input)
assert.Equal(t, expected, tokenized)
}
func TestTokenizeNested(t *testing.T) {
input := "bar" + listSeparator + "10" + mapSeparator + "baz"
expected := []token{mapToken{"bar"}, listToken{10}, mapToken{"baz"}}
tokenized := tokenize(input)
assert.Equal(t, expected, tokenized)
}
070701000000A0000041ED00000000000000000000000362794F9300000000000000000000000000000000000000000000001600000000sops-3.7.3/stores/ini070701000000A1000081A400000000000000000000000162794F9300001C7E000000000000000000000000000000000000001F00000000sops-3.7.3/stores/ini/store.gopackage ini //import "go.mozilla.org/sops/v3/stores/ini"
import (
"bytes"
"encoding/json"
"fmt"
"strconv"
"strings"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/stores"
"gopkg.in/ini.v1"
)
// Store handles storage of ini data.
type Store struct {
}
func (store Store) encodeTree(branches sops.TreeBranches) ([]byte, error) {
iniFile := ini.Empty()
for _, branch := range branches {
for _, item := range branch {
if _, ok := item.Key.(sops.Comment); ok {
continue
}
section, err := iniFile.NewSection(item.Key.(string))
if err != nil {
return nil, fmt.Errorf("Error encoding section %s: %s", item.Key, err)
}
itemTree, ok := item.Value.(sops.TreeBranch)
if !ok {
return nil, fmt.Errorf("Error encoding section: Section values should always be TreeBranches")
}
first := 0
if len(itemTree) > 0 {
if sectionComment, ok := itemTree[0].Key.(sops.Comment); ok {
section.Comment = sectionComment.Value
first = 1
}
}
var lastItem *ini.Key
for i := first; i < len(itemTree); i++ {
keyVal := itemTree[i]
if comment, ok := keyVal.Key.(sops.Comment); ok {
if lastItem != nil {
lastItem.Comment = comment.Value
}
} else {
lastItem, err = section.NewKey(keyVal.Key.(string), store.valToString(keyVal.Value))
if err != nil {
return nil, fmt.Errorf("Error encoding key: %s", err)
}
}
}
}
}
var buffer bytes.Buffer
iniFile.WriteTo(&buffer)
return buffer.Bytes(), nil
}
func (store Store) stripCommentChar(comment string) string {
if strings.HasPrefix(comment, ";") {
comment = strings.TrimLeft(comment, "; ")
} else if strings.HasPrefix(comment, "#") {
comment = strings.TrimLeft(comment, "# ")
}
return comment
}
func (store Store) valToString(v interface{}) string {
switch v := v.(type) {
case fmt.Stringer:
return v.String()
case float64:
return strconv.FormatFloat(v, 'f', 6, 64)
case bool:
return strconv.FormatBool(v)
default:
return fmt.Sprintf("%s", v)
}
}
func (store Store) iniFromTreeBranches(branches sops.TreeBranches) ([]byte, error) {
return store.encodeTree(branches)
}
func (store Store) treeBranchesFromIni(in []byte) (sops.TreeBranches, error) {
iniFile, err := ini.Load(in)
if err != nil {
return nil, err
}
var branch sops.TreeBranch
for _, section := range iniFile.Sections() {
item, err := store.treeItemFromSection(section)
if err != nil {
return sops.TreeBranches{branch}, err
}
branch = append(branch, item)
}
return sops.TreeBranches{branch}, nil
}
func (store Store) treeItemFromSection(section *ini.Section) (sops.TreeItem, error) {
var sectionItem sops.TreeItem
sectionItem.Key = section.Name()
var items sops.TreeBranch
if section.Comment != "" {
items = append(items, sops.TreeItem{
Key: sops.Comment{
Value: store.stripCommentChar(section.Comment),
},
Value: nil,
})
}
for _, key := range section.Keys() {
item := sops.TreeItem{Key: key.Name(), Value: key.Value()}
items = append(items, item)
if key.Comment != "" {
items = append(items, sops.TreeItem{
Key: sops.Comment{
Value: store.stripCommentChar(key.Comment),
},
Value: nil,
})
}
}
sectionItem.Value = items
return sectionItem, nil
}
// LoadEncryptedFile loads encrypted INI file's bytes onto a sops.Tree runtime object
func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
iniFileOuter, err := ini.Load(in)
if err != nil {
return sops.Tree{}, err
}
sopsSection, err := iniFileOuter.GetSection("sops")
if err != nil {
return sops.Tree{}, sops.MetadataNotFound
}
metadataHolder, err := store.iniSectionToMetadata(sopsSection)
if err != nil {
return sops.Tree{}, err
}
metadata, err := metadataHolder.ToInternal()
if err != nil {
return sops.Tree{}, err
}
// After that, we load the whole file into a map.
branches, err := store.treeBranchesFromIni(in)
if err != nil {
return sops.Tree{}, fmt.Errorf("Could not unmarshal input data: %s", err)
}
// Discard metadata, as we already loaded it.
for bi, branch := range branches {
for s, sectionBranch := range branch {
if sectionBranch.Key == "sops" {
branch = append(branch[:s], branch[s+1:]...)
branches[bi] = branch
}
}
}
return sops.Tree{
Branches: branches,
Metadata: metadata,
}, nil
}
func (store *Store) iniSectionToMetadata(sopsSection *ini.Section) (stores.Metadata, error) {
metadataHash := make(map[string]interface{})
for k, v := range sopsSection.KeysHash() {
metadataHash[k] = strings.Replace(v, "\\n", "\n", -1)
}
m := stores.Unflatten(metadataHash)
var md stores.Metadata
inrec, err := json.Marshal(m)
if err != nil {
return md, err
}
err = json.Unmarshal(inrec, &md)
return md, err
}
// LoadPlainFile loads a plaintext INI file's bytes onto a sops.TreeBranches runtime object
func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
branches, err := store.treeBranchesFromIni(in)
if err != nil {
return branches, fmt.Errorf("Could not unmarshal input data: %s", err)
}
return branches, nil
}
// EmitEncryptedFile returns encrypted INI file bytes corresponding to a sops.Tree
// runtime object
func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
metadata := stores.MetadataFromInternal(in.Metadata)
newBranch, err := store.encodeMetadataToIniBranch(metadata)
if err != nil {
return nil, err
}
sectionItem := sops.TreeItem{Key: "sops", Value: newBranch}
branch := sops.TreeBranch{sectionItem}
in.Branches = append(in.Branches, branch)
out, err := store.iniFromTreeBranches(in.Branches)
if err != nil {
return nil, fmt.Errorf("Error marshaling to ini: %s", err)
}
return out, nil
}
func (store *Store) encodeMetadataToIniBranch(md stores.Metadata) (sops.TreeBranch, error) {
var mdMap map[string]interface{}
inrec, err := json.Marshal(md)
if err != nil {
return nil, err
}
err = json.Unmarshal(inrec, &mdMap)
if err != nil {
return nil, err
}
flat := stores.Flatten(mdMap)
for k, v := range flat {
if s, ok := v.(string); ok {
flat[k] = strings.Replace(s, "\n", "\\n", -1)
}
}
if err != nil {
return nil, err
}
branch := sops.TreeBranch{}
for key, value := range flat {
if value == nil {
continue
}
branch = append(branch, sops.TreeItem{Key: key, Value: value})
}
return branch, nil
}
// EmitPlainFile returns the plaintext INI file bytes corresponding to a sops.TreeBranches object
func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
out, err := store.iniFromTreeBranches(in)
if err != nil {
return nil, fmt.Errorf("Error marshaling to ini: %s", err)
}
return out, nil
}
func (store Store) encodeValue(v interface{}) ([]byte, error) {
switch v := v.(type) {
case sops.TreeBranches:
return store.encodeTree(v)
default:
return json.Marshal(v)
}
}
// EmitValue returns a single value encoded in a generic interface{} as bytes
func (store *Store) EmitValue(v interface{}) ([]byte, error) {
return store.encodeValue(v)
}
// EmitExample returns the plaintext INI file bytes corresponding to the SimpleTree example
func (store *Store) EmitExample() []byte {
bytes, err := store.EmitPlainFile(stores.ExampleSimpleTree.Branches)
if err != nil {
panic(err)
}
return bytes
}
070701000000A2000081A400000000000000000000000162794F9300000ABD000000000000000000000000000000000000002400000000sops-3.7.3/stores/ini/store_test.gopackage ini
import (
"testing"
"github.com/stretchr/testify/assert"
"go.mozilla.org/sops/v3"
)
func TestDecodeIni(t *testing.T) {
in := `
; last modified 1 April 2001 by John Doe
[owner]
name=John Doe
organization=Acme Widgets Inc.
[database]
; use IP address in case network name resolution is not working
server=192.0.2.62
port=143
file="payroll.dat"
`
expected := sops.TreeBranches{
sops.TreeBranch{
sops.TreeItem{
Key: "DEFAULT",
Value: sops.TreeBranch(nil),
},
sops.TreeItem{
Key: "owner",
Value: sops.TreeBranch{
sops.TreeItem{
Key: sops.Comment{Value: "last modified 1 April 2001 by John Doe"},
Value: nil,
},
sops.TreeItem{
Key: "name",
Value: "John Doe",
},
sops.TreeItem{
Key: "organization",
Value: "Acme Widgets Inc.",
},
},
},
sops.TreeItem{
Key: "database",
Value: sops.TreeBranch{
sops.TreeItem{
Key: "server",
Value: "192.0.2.62",
},
sops.TreeItem{
Key: sops.Comment{Value: "use IP address in case network name resolution is not working"},
Value: nil,
},
sops.TreeItem{
Key: "port",
Value: "143",
},
sops.TreeItem{
Key: "file",
Value: "payroll.dat",
},
},
},
},
}
branch, err := Store{}.treeBranchesFromIni([]byte(in))
assert.Nil(t, err)
assert.Equal(t, expected, branch)
}
func TestEncodeSimpleIni(t *testing.T) {
branches := sops.TreeBranches{
sops.TreeBranch{
sops.TreeItem{
Key: "DEFAULT",
Value: sops.TreeBranch{
sops.TreeItem{
Key: "foo",
Value: "bar",
},
sops.TreeItem{
Key: "baz",
Value: "3.0",
},
sops.TreeItem{
Key: "qux",
Value: "false",
},
},
},
},
}
out, err := Store{}.iniFromTreeBranches(branches)
assert.Nil(t, err)
expected, _ := Store{}.treeBranchesFromIni(out)
assert.Equal(t, expected, branches)
}
func TestEncodeIniWithEscaping(t *testing.T) {
branches := sops.TreeBranches{
sops.TreeBranch{
sops.TreeItem{
Key: "DEFAULT",
Value: sops.TreeBranch{
sops.TreeItem{
Key: "foo\\bar",
Value: "value",
},
sops.TreeItem{
Key: "a_key_with\"quotes\"",
Value: "4.0",
},
sops.TreeItem{
Key: "baz\\\\foo",
Value: "2.0",
},
},
},
},
}
out, err := Store{}.iniFromTreeBranches(branches)
assert.Nil(t, err)
expected, _ := Store{}.treeBranchesFromIni(out)
assert.Equal(t, expected, branches)
}
func TestUnmarshalMetadataFromNonSOPSFile(t *testing.T) {
data := []byte(`hello=2`)
store := Store{}
_, err := store.LoadEncryptedFile(data)
assert.Equal(t, sops.MetadataNotFound, err)
}
070701000000A3000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002500000000sops-3.7.3/stores/ini/test_resources070701000000A4000081A400000000000000000000000162794F9300000C03000000000000000000000000000000000000003200000000sops-3.7.3/stores/ini/test_resources/example.json{
"example_key": "ENC[AES256_GCM,data:Xjen3YMQYCBfTU8VjA==,iv:1NUKversqeQiuTmAkZuyd6UY2AWiBS4owa4QHnwKOBM=,tag:ZO+Aln5DQ5Qm/QV2uBdahA==,type:str]",
"example_array": [
"ENC[AES256_GCM,data:XJR0qvZuifm8j1TIQB8=,iv:QUkwy1dp0RU0PKEAw/VxVe1ZsQ972c8gPMJoVKgMfuw=,tag:LGdwC5nTua4rSe0dLbbA1Q==,type:str]",
"ENC[AES256_GCM,data:7bhghzi5GN/mMqh1vHU=,iv:X5vrd9X7ItIG/RVCn0T7RFhUrTb2YItr3i97EVk9nOY=,tag:vrM058PPOGmWOGPThOP5Ew==,type:str]"
],
"example_number": "ENC[AES256_GCM,data:w9etQN5r8iCz,iv:YF+1uUlMa4I1C7A0ELpVuMa1yK042uEMhp8y6HiCTDE=,tag:Dmh+AV9sh+ir0M1Txe+v2A==,type:float]",
"example_booleans": [
"ENC[AES256_GCM,data:/hltsg==,iv:pbAtZ9i8rxFpaFlbwE1KOA+k/TVx5dm0tDtH94GCEVc=,tag:yz3d1pQu9zy5Ra9z2kDcoA==,type:bool]",
"ENC[AES256_GCM,data:HEei0+s=,iv:hgKT5eiYdHn5AqWdNji7vRKfabln90VbLmJqt7A480E=,tag:6pfezzTX/eULebF+32Z2+w==,type:bool]"
],
"sops": {
"lastmodified": "2016-08-04T23:30:35Z",
"attention": "This section contains key material that should only be modified with extra care. See `sops -h`.",
"unencrypted_suffix": "_unencrypted",
"mac": "ENC[AES256_GCM,data:EK1LkVgW5CBEsGgGc7RkfZlzqWrP2fZe3kG7HbkJ5JFd591oUkbQ6I2uPImkcxf7HjiEHzKPPF5QvNg3+rUxgw6S8pQtumhDbFrfDi8GDS2VVvPR+0fnc2fR5PMGm36bOaQFDNSmgyJzKhMmNL+MtRhH+fMUnHhrnxuN3wfLr4w=,iv:xbNK6wRDVT4xhrP+vP2RIy+uNjZSSzqEJZPOdShn96o=,tag:vT4akR5X6qx5/wJ4dncxtg==,type:str]",
"version": "1.13",
"kms": [
{
"created_at": "2016-08-04T23:30:35Z",
"enc": "AQECAHgFEiO2dNygC3Rz8PhERCc8Sfhak4g81FUPqQJ0OBcAKgAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDPKe5R67LMN3+xAkygIBEIA7F8noZukawV3VLQ/yH3Ep7Ptx8weLFUgVf/ZI6xqSMNvEHIr4+vf2xjBiAyrEF8u/n9nm9PWAdKHszFM=",
"arn": "arn:aws:kms:us-east-1:927034868273:key/e9fc75db-05e9-44c1-9c35-633922bac347"
}
],
"pgp": [
{
"fp": "E5297818703249D0C60E19E6824612478D1A4CCD",
"created_at": "2016-08-04T23:30:35Z",
"enc": "-----BEGIN PGP MESSAGE-----\nVersion: GnuPG v1\n\nhQIMA2X8rvoeiASBARAAurTEVS82kqadk68f5ZlwR176S148WYTYxFp5oMC7cVD7\n42+Eo9RzaxbHeO5n7XKX0SDOUUeCucFl8fwuDUV1iDIx4/u5HgWXxDuvWoNe5cAL\n4LBS1Er2ZBVAdU0WHZ/8USuZLhSu7ucAHOvqNpHzPT6gkuBUYLQKOu0c+onWHqVO\n1DhfkTtvphotZ0ZBBR099t5N8ofD0W2+SM268A9/bB5yQcK9Ig/KxZBrfmMQm7zx\n9hLVQhcBmj0OQG37K4/SXGwjrQFarh6lm+FuZM0Q+GI+OARoAKdpZOnPEhXKE6un\nSEa69rh5FKVM/XRp2/QVZEakzRtq3gi9CtYL2sNr7KEnCvxt/v2pEc6evfIvxWTc\nT8MWdk48FkVjdsJ34sNiIM8msstnYorse8RZny9gcLE+A5lsRavo2QPL4GADyHF8\n7kwijSVDd08nByTBMMEPpMozUFhzF8QuVZPD+siuUvi+Bned9MmqgGMfvhS0Kf38\nMZFy5C6e38VGEX3IrWChvzbBm/M3fjs1fPVDShHfk1MYsCU9sXNQMQVewWE0s/em\nklycIL3hywd4N9z1MVW2hBpRrC247PtGQRKGoB9qbKtSgjTtgM7bo1vYekeY1tjr\nBGTHNFV+FBqFih16u/rGVzIaBsf5lLL/RtpaFZx1OWHMd9XjQpRrHhjOMpQ8tjvS\nXgGH59vv/9GNZ+Rix1QF+iMD84sfkyyguGKwg+TC3m275v+HIO1NvNdU6oS3O/Xq\nBCBV3yYAUwcrWUPWCuSUHJbuHKJEI1ymXUu8+RUElPyi/5JEhW+J1WlVPvnG1Xk=\n=Q/XA\n-----END PGP MESSAGE-----\n"
}
]
}
}070701000000A5000041ED00000000000000000000000362794F9300000000000000000000000000000000000000000000001700000000sops-3.7.3/stores/json070701000000A6000081A400000000000000000000000162794F93000022AF000000000000000000000000000000000000002000000000sops-3.7.3/stores/json/store.gopackage json //import "go.mozilla.org/sops/v3/stores/json"
import (
"bytes"
"encoding/json"
"fmt"
"io"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/stores"
)
// Store handles storage of JSON data.
type Store struct {
}
// BinaryStore handles storage of binary data in a JSON envelope.
type BinaryStore struct {
store Store
}
// LoadEncryptedFile loads an encrypted json file onto a sops.Tree object
func (store BinaryStore) LoadEncryptedFile(in []byte) (sops.Tree, error) {
return store.store.LoadEncryptedFile(in)
}
// LoadPlainFile loads a plaintext json file onto a sops.Tree encapsulated
// within a sops.TreeBranches object
func (store BinaryStore) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
return sops.TreeBranches{
sops.TreeBranch{
sops.TreeItem{
Key: "data",
Value: string(in),
},
},
}, nil
}
// EmitEncryptedFile produces an encrypted json file's bytes from its corresponding sops.Tree object
func (store BinaryStore) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
return store.store.EmitEncryptedFile(in)
}
// EmitPlainFile produces plaintext json file's bytes from its corresponding sops.TreeBranches object
func (store BinaryStore) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
// JSON stores a single object per file
for _, item := range in[0] {
if item.Key == "data" {
return []byte(item.Value.(string)), nil
}
}
return nil, fmt.Errorf("No binary data found in tree")
}
// EmitValue extracts a value from a generic interface{} object representing a structured set
// of binary files
func (store BinaryStore) EmitValue(v interface{}) ([]byte, error) {
return nil, fmt.Errorf("Binary files are not structured and extracting a single value is not possible")
}
// EmitExample returns the example's plaintext json file bytes
func (store BinaryStore) EmitExample() []byte {
return []byte("Welcome to SOPS! Edit this file as you please!")
}
func (store Store) sliceFromJSONDecoder(dec *json.Decoder) ([]interface{}, error) {
var slice []interface{}
for {
t, err := dec.Token()
if err != nil {
return slice, err
}
if delim, ok := t.(json.Delim); ok && delim.String() == "]" {
return slice, nil
} else if ok && delim.String() == "{" {
item, err := store.treeBranchFromJSONDecoder(dec)
if err != nil {
return slice, err
}
slice = append(slice, item)
} else if ok && delim.String() == "[" {
item, err := store.sliceFromJSONDecoder(dec)
if err != nil {
return slice, err
}
slice = append(slice, item)
} else {
slice = append(slice, t)
}
}
}
var errEndOfObject = fmt.Errorf("End of object")
func (store Store) treeItemFromJSONDecoder(dec *json.Decoder) (sops.TreeItem, error) {
var item sops.TreeItem
key, err := dec.Token()
if err != nil && err != io.EOF {
return item, err
}
if k, ok := key.(string); ok {
item.Key = k
} else if d, ok := key.(json.Delim); ok && d.String() == "}" {
return item, errEndOfObject
} else {
return item, fmt.Errorf("Expected JSON object key, got %s of type %T instead", key, key)
}
value, err := dec.Token()
if err != nil {
return item, err
}
if delim, ok := value.(json.Delim); ok {
if delim.String() == "[" {
v, err := store.sliceFromJSONDecoder(dec)
if err != nil {
return item, err
}
item.Value = v
}
if delim.String() == "{" {
v, err := store.treeBranchFromJSONDecoder(dec)
if err != nil {
return item, err
}
item.Value = v
}
} else {
item.Value = value
}
return item, nil
}
func (store Store) treeBranchFromJSONDecoder(dec *json.Decoder) (sops.TreeBranch, error) {
var tree sops.TreeBranch
for {
item, err := store.treeItemFromJSONDecoder(dec)
if err == io.EOF {
return tree, nil
}
if err == errEndOfObject {
return tree, nil
}
if err != nil {
return tree, err
}
tree = append(tree, item)
}
}
func (store Store) encodeValue(v interface{}) ([]byte, error) {
switch v := v.(type) {
case sops.TreeBranch:
return store.encodeTree(v)
case []interface{}:
return store.encodeArray(v)
default:
return json.Marshal(v)
}
}
func (store Store) encodeArray(array []interface{}) ([]byte, error) {
out := "["
for i, item := range array {
if _, ok := item.(sops.Comment); ok {
continue
}
v, err := store.encodeValue(item)
if err != nil {
return nil, err
}
out += string(v)
if i != len(array)-1 {
out += ","
}
}
out += "]"
return []byte(out), nil
}
func (store Store) encodeTree(tree sops.TreeBranch) ([]byte, error) {
out := "{"
for i, item := range tree {
if _, ok := item.Key.(sops.Comment); ok {
continue
}
v, err := store.encodeValue(item.Value)
if err != nil {
return nil, fmt.Errorf("Error encoding value %s: %s", v, err)
}
k, err := json.Marshal(item.Key.(string))
if err != nil {
return nil, fmt.Errorf("Error encoding key %s: %s", k, err)
}
out += string(k) + `: ` + string(v)
if i != len(tree)-1 {
out += ","
}
}
return []byte(out + "}"), nil
}
func (store Store) jsonFromTreeBranch(branch sops.TreeBranch) ([]byte, error) {
out, err := store.encodeTree(branch)
if err != nil {
return nil, err
}
return store.reindentJSON(out)
}
func (store Store) treeBranchFromJSON(in []byte) (sops.TreeBranch, error) {
dec := json.NewDecoder(bytes.NewReader(in))
dec.Token()
return store.treeBranchFromJSONDecoder(dec)
}
func (store Store) reindentJSON(in []byte) ([]byte, error) {
var out bytes.Buffer
err := json.Indent(&out, in, "", "\t")
return out.Bytes(), err
}
// LoadEncryptedFile loads an encrypted secrets file onto a sops.Tree object
func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
// Because we don't know what fields the input file will have, we have to
// load the file in two steps.
// First, we load the file's metadata, the structure of which is known.
metadataHolder := stores.SopsFile{}
err := json.Unmarshal(in, &metadataHolder)
if err != nil {
if err, ok := err.(*json.UnmarshalTypeError); ok {
if err.Value == "number" && err.Struct == "Metadata" && err.Field == "version" {
return sops.Tree{},
fmt.Errorf("SOPS versions higher than 2.0.10 can not automatically decrypt JSON files " +
"created with SOPS 1.x. In order to be able to decrypt this file, you can either edit it " +
"manually and make sure the JSON value under `sops -> version` is a string and not a " +
"number, or you can rotate the file's key with any version of SOPS between 2.0 and 2.0.10 " +
"using `sops -r your_file.json`")
}
}
return sops.Tree{}, fmt.Errorf("Error unmarshalling input json: %s", err)
}
if metadataHolder.Metadata == nil {
return sops.Tree{}, sops.MetadataNotFound
}
metadata, err := metadataHolder.Metadata.ToInternal()
if err != nil {
return sops.Tree{}, err
}
// After that, we load the whole file into a map.
branch, err := store.treeBranchFromJSON(in)
if err != nil {
return sops.Tree{}, fmt.Errorf("Could not unmarshal input data: %s", err)
}
// Discard metadata, as we already loaded it.
for i, item := range branch {
if item.Key == "sops" {
branch = append(branch[:i], branch[i+1:]...)
}
}
return sops.Tree{
Branches: sops.TreeBranches{
branch,
},
Metadata: metadata,
}, nil
}
// LoadPlainFile loads plaintext json file bytes onto a sops.TreeBranches object
func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
branch, err := store.treeBranchFromJSON(in)
if err != nil {
return nil, fmt.Errorf("Could not unmarshal input data: %s", err)
}
return sops.TreeBranches{
branch,
}, nil
}
// EmitEncryptedFile returns the encrypted bytes of the json file corresponding to a
// sops.Tree runtime object
func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
tree := append(in.Branches[0], sops.TreeItem{Key: "sops", Value: stores.MetadataFromInternal(in.Metadata)})
out, err := store.jsonFromTreeBranch(tree)
if err != nil {
return nil, fmt.Errorf("Error marshaling to json: %s", err)
}
return out, nil
}
// EmitPlainFile returns the plaintext bytes of the json file corresponding to a
// sops.TreeBranches runtime object
func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
out, err := store.jsonFromTreeBranch(in[0])
if err != nil {
return nil, fmt.Errorf("Error marshaling to json: %s", err)
}
return out, nil
}
// EmitValue returns bytes corresponding to a single encoded value
// in a generic interface{} object
func (store *Store) EmitValue(v interface{}) ([]byte, error) {
s, err := store.encodeValue(v)
if err != nil {
return nil, err
}
return store.reindentJSON(s)
}
// EmitExample returns the bytes corresponding to an example complex tree
func (store *Store) EmitExample() []byte {
bytes, err := store.EmitPlainFile(stores.ExampleComplexTree.Branches)
if err != nil {
panic(err)
}
return bytes
}
070701000000A7000081A400000000000000000000000162794F9300001B0E000000000000000000000000000000000000002500000000sops-3.7.3/stores/json/store_test.gopackage json
import (
"testing"
"github.com/stretchr/testify/assert"
"go.mozilla.org/sops/v3"
)
func TestDecodeJSON(t *testing.T) {
in := `
{
"glossary":{
"title":"example glossary",
"GlossDiv":{
"title":"S",
"GlossList":{
"GlossEntry":{
"ID":"SGML",
"SortAs":"SGML",
"GlossTerm":"Standard Generalized Markup Language",
"Acronym":"SGML",
"Abbrev":"ISO 8879:1986",
"GlossDef":{
"para":"A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso":[
"GML",
"XML"
]
},
"GlossSee":"markup"
}
}
}
}
}`
expected := sops.TreeBranch{
sops.TreeItem{
Key: "glossary",
Value: sops.TreeBranch{
sops.TreeItem{
Key: "title",
Value: "example glossary",
},
sops.TreeItem{
Key: "GlossDiv",
Value: sops.TreeBranch{
sops.TreeItem{
Key: "title",
Value: "S",
},
sops.TreeItem{
Key: "GlossList",
Value: sops.TreeBranch{
sops.TreeItem{
Key: "GlossEntry",
Value: sops.TreeBranch{
sops.TreeItem{
Key: "ID",
Value: "SGML",
},
sops.TreeItem{
Key: "SortAs",
Value: "SGML",
},
sops.TreeItem{
Key: "GlossTerm",
Value: "Standard Generalized Markup Language",
},
sops.TreeItem{
Key: "Acronym",
Value: "SGML",
},
sops.TreeItem{
Key: "Abbrev",
Value: "ISO 8879:1986",
},
sops.TreeItem{
Key: "GlossDef",
Value: sops.TreeBranch{
sops.TreeItem{
Key: "para",
Value: "A meta-markup language, used to create markup languages such as DocBook.",
},
sops.TreeItem{
Key: "GlossSeeAlso",
Value: []interface{}{
"GML",
"XML",
},
},
},
},
sops.TreeItem{
Key: "GlossSee",
Value: "markup",
},
},
},
},
},
},
},
},
},
}
branch, err := Store{}.treeBranchFromJSON([]byte(in))
assert.Nil(t, err)
assert.Equal(t, expected, branch)
}
func TestDecodeSimpleJSONObject(t *testing.T) {
in := `{"foo": "bar", "baz": 2}`
expected := sops.TreeBranch{
sops.TreeItem{
Key: "foo",
Value: "bar",
},
sops.TreeItem{
Key: "baz",
Value: 2.0,
},
}
branch, err := Store{}.treeBranchFromJSON([]byte(in))
assert.Nil(t, err)
assert.Equal(t, expected, branch)
}
func TestDecodeNumber(t *testing.T) {
in := `42`
_, err := Store{}.treeBranchFromJSON([]byte(in))
assert.NotNil(t, err)
}
func TestDecodeNestedJSONObject(t *testing.T) {
in := `{"foo": {"foo": "bar"}}`
expected := sops.TreeBranch{
sops.TreeItem{
Key: "foo",
Value: sops.TreeBranch{
sops.TreeItem{
Key: "foo",
Value: "bar",
},
},
},
}
branch, err := Store{}.treeBranchFromJSON([]byte(in))
assert.Nil(t, err)
assert.Equal(t, expected, branch)
}
func TestDecodeJSONWithArray(t *testing.T) {
in := `{"foo": {"foo": [1, 2, 3]}, "bar": "baz"}`
expected := sops.TreeBranch{
sops.TreeItem{
Key: "foo",
Value: sops.TreeBranch{
sops.TreeItem{
Key: "foo",
Value: []interface{}{1.0, 2.0, 3.0},
},
},
},
sops.TreeItem{
Key: "bar",
Value: "baz",
},
}
branch, err := Store{}.treeBranchFromJSON([]byte(in))
assert.Nil(t, err)
assert.Equal(t, expected, branch)
}
func TestDecodeJSONArrayOfObjects(t *testing.T) {
in := `{"foo": [{"bar": "foo"}, {"foo": "bar"}]}`
expected := sops.TreeBranch{
sops.TreeItem{
Key: "foo",
Value: []interface{}{
sops.TreeBranch{
sops.TreeItem{
Key: "bar",
Value: "foo",
},
},
sops.TreeBranch{
sops.TreeItem{
Key: "foo",
Value: "bar",
},
},
},
},
}
branch, err := Store{}.treeBranchFromJSON([]byte(in))
assert.Nil(t, err)
assert.Equal(t, expected, branch)
}
func TestDecodeJSONArrayOfArrays(t *testing.T) {
in := `{"foo": [[["foo", {"bar": "foo"}]]]}`
expected := sops.TreeBranch{
sops.TreeItem{
Key: "foo",
Value: []interface{}{
[]interface{}{
[]interface{}{
"foo",
sops.TreeBranch{
sops.TreeItem{
Key: "bar",
Value: "foo",
},
},
},
},
},
},
}
branch, err := Store{}.treeBranchFromJSON([]byte(in))
assert.Nil(t, err)
assert.Equal(t, expected, branch)
}
func TestEncodeSimpleJSON(t *testing.T) {
branch := sops.TreeBranch{
sops.TreeItem{
Key: "foo",
Value: "bar",
},
sops.TreeItem{
Key: "foo",
Value: 3.0,
},
sops.TreeItem{
Key: "bar",
Value: false,
},
}
out, err := Store{}.jsonFromTreeBranch(branch)
assert.Nil(t, err)
expected, _ := Store{}.treeBranchFromJSON(out)
assert.Equal(t, expected, branch)
}
func TestEncodeJSONWithEscaping(t *testing.T) {
branch := sops.TreeBranch{
sops.TreeItem{
Key: "foo\\bar",
Value: "value",
},
sops.TreeItem{
Key: "a_key_with\"quotes\"",
Value: 4.0,
},
sops.TreeItem{
Key: "baz\\\\foo",
Value: 2.0,
},
}
out, err := Store{}.jsonFromTreeBranch(branch)
assert.Nil(t, err)
expected, _ := Store{}.treeBranchFromJSON(out)
assert.Equal(t, expected, branch)
}
func TestEncodeJSONArrayOfObjects(t *testing.T) {
tree := sops.Tree{
Branches: sops.TreeBranches{
sops.TreeBranch{
sops.TreeItem{
Key: "foo",
Value: []interface{}{
sops.TreeBranch{
sops.TreeItem{
Key: "foo",
Value: 3,
},
sops.TreeItem{
Key: "bar",
Value: false,
},
},
2,
},
},
},
},
}
expected := `{
"foo": [
{
"foo": 3,
"bar": false
},
2
]
}`
store := Store{}
out, err := store.EmitPlainFile(tree.Branches)
assert.Nil(t, err)
assert.Equal(t, expected, string(out))
}
func TestUnmarshalMetadataFromNonSOPSFile(t *testing.T) {
data := []byte(`{"hello": 2}`)
store := Store{}
_, err := store.LoadEncryptedFile(data)
assert.Equal(t, sops.MetadataNotFound, err)
}
func TestLoadJSONFormattedBinaryFile(t *testing.T) {
// This is JSON data, but we want SOPS to interpret it as binary,
// e.g. because the --input-type binary flag was provided.
data := []byte(`{"hello": 2}`)
store := BinaryStore{}
branches, err := store.LoadPlainFile(data)
assert.Nil(t, err)
assert.Equal(t, "data", branches[0][0].Key)
}
func TestEmitValueString(t *testing.T) {
bytes, err := (&Store{}).EmitValue("hello")
assert.Nil(t, err)
assert.Equal(t, []byte("\"hello\""), bytes)
}
070701000000A8000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000002600000000sops-3.7.3/stores/json/test_resources070701000000A9000081A400000000000000000000000162794F9300000C03000000000000000000000000000000000000003300000000sops-3.7.3/stores/json/test_resources/example.json{
"example_key": "ENC[AES256_GCM,data:Xjen3YMQYCBfTU8VjA==,iv:1NUKversqeQiuTmAkZuyd6UY2AWiBS4owa4QHnwKOBM=,tag:ZO+Aln5DQ5Qm/QV2uBdahA==,type:str]",
"example_array": [
"ENC[AES256_GCM,data:XJR0qvZuifm8j1TIQB8=,iv:QUkwy1dp0RU0PKEAw/VxVe1ZsQ972c8gPMJoVKgMfuw=,tag:LGdwC5nTua4rSe0dLbbA1Q==,type:str]",
"ENC[AES256_GCM,data:7bhghzi5GN/mMqh1vHU=,iv:X5vrd9X7ItIG/RVCn0T7RFhUrTb2YItr3i97EVk9nOY=,tag:vrM058PPOGmWOGPThOP5Ew==,type:str]"
],
"example_number": "ENC[AES256_GCM,data:w9etQN5r8iCz,iv:YF+1uUlMa4I1C7A0ELpVuMa1yK042uEMhp8y6HiCTDE=,tag:Dmh+AV9sh+ir0M1Txe+v2A==,type:float]",
"example_booleans": [
"ENC[AES256_GCM,data:/hltsg==,iv:pbAtZ9i8rxFpaFlbwE1KOA+k/TVx5dm0tDtH94GCEVc=,tag:yz3d1pQu9zy5Ra9z2kDcoA==,type:bool]",
"ENC[AES256_GCM,data:HEei0+s=,iv:hgKT5eiYdHn5AqWdNji7vRKfabln90VbLmJqt7A480E=,tag:6pfezzTX/eULebF+32Z2+w==,type:bool]"
],
"sops": {
"lastmodified": "2016-08-04T23:30:35Z",
"attention": "This section contains key material that should only be modified with extra care. See `sops -h`.",
"unencrypted_suffix": "_unencrypted",
"mac": "ENC[AES256_GCM,data:EK1LkVgW5CBEsGgGc7RkfZlzqWrP2fZe3kG7HbkJ5JFd591oUkbQ6I2uPImkcxf7HjiEHzKPPF5QvNg3+rUxgw6S8pQtumhDbFrfDi8GDS2VVvPR+0fnc2fR5PMGm36bOaQFDNSmgyJzKhMmNL+MtRhH+fMUnHhrnxuN3wfLr4w=,iv:xbNK6wRDVT4xhrP+vP2RIy+uNjZSSzqEJZPOdShn96o=,tag:vT4akR5X6qx5/wJ4dncxtg==,type:str]",
"version": "1.13",
"kms": [
{
"created_at": "2016-08-04T23:30:35Z",
"enc": "AQECAHgFEiO2dNygC3Rz8PhERCc8Sfhak4g81FUPqQJ0OBcAKgAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDPKe5R67LMN3+xAkygIBEIA7F8noZukawV3VLQ/yH3Ep7Ptx8weLFUgVf/ZI6xqSMNvEHIr4+vf2xjBiAyrEF8u/n9nm9PWAdKHszFM=",
"arn": "arn:aws:kms:us-east-1:927034868273:key/e9fc75db-05e9-44c1-9c35-633922bac347"
}
],
"pgp": [
{
"fp": "E5297818703249D0C60E19E6824612478D1A4CCD",
"created_at": "2016-08-04T23:30:35Z",
"enc": "-----BEGIN PGP MESSAGE-----\nVersion: GnuPG v1\n\nhQIMA2X8rvoeiASBARAAurTEVS82kqadk68f5ZlwR176S148WYTYxFp5oMC7cVD7\n42+Eo9RzaxbHeO5n7XKX0SDOUUeCucFl8fwuDUV1iDIx4/u5HgWXxDuvWoNe5cAL\n4LBS1Er2ZBVAdU0WHZ/8USuZLhSu7ucAHOvqNpHzPT6gkuBUYLQKOu0c+onWHqVO\n1DhfkTtvphotZ0ZBBR099t5N8ofD0W2+SM268A9/bB5yQcK9Ig/KxZBrfmMQm7zx\n9hLVQhcBmj0OQG37K4/SXGwjrQFarh6lm+FuZM0Q+GI+OARoAKdpZOnPEhXKE6un\nSEa69rh5FKVM/XRp2/QVZEakzRtq3gi9CtYL2sNr7KEnCvxt/v2pEc6evfIvxWTc\nT8MWdk48FkVjdsJ34sNiIM8msstnYorse8RZny9gcLE+A5lsRavo2QPL4GADyHF8\n7kwijSVDd08nByTBMMEPpMozUFhzF8QuVZPD+siuUvi+Bned9MmqgGMfvhS0Kf38\nMZFy5C6e38VGEX3IrWChvzbBm/M3fjs1fPVDShHfk1MYsCU9sXNQMQVewWE0s/em\nklycIL3hywd4N9z1MVW2hBpRrC247PtGQRKGoB9qbKtSgjTtgM7bo1vYekeY1tjr\nBGTHNFV+FBqFih16u/rGVzIaBsf5lLL/RtpaFZx1OWHMd9XjQpRrHhjOMpQ8tjvS\nXgGH59vv/9GNZ+Rix1QF+iMD84sfkyyguGKwg+TC3m275v+HIO1NvNdU6oS3O/Xq\nBCBV3yYAUwcrWUPWCuSUHJbuHKJEI1ymXUu8+RUElPyi/5JEhW+J1WlVPvnG1Xk=\n=Q/XA\n-----END PGP MESSAGE-----\n"
}
]
}
}070701000000AA000081A400000000000000000000000162794F9300003E2A000000000000000000000000000000000000001C00000000sops-3.7.3/stores/stores.go/*
Package stores acts as a layer between the internal representation of encrypted files and the encrypted files
themselves.
Subpackages implement serialization and deserialization to multiple formats.
This package defines the structure SOPS files should have and conversions to and from the internal representation. Part
of the purpose of this package is to make it easy to change the SOPS file format while remaining backwards-compatible.
*/
package stores
import (
"time"
"fmt"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/age"
"go.mozilla.org/sops/v3/azkv"
"go.mozilla.org/sops/v3/gcpkms"
"go.mozilla.org/sops/v3/hcvault"
"go.mozilla.org/sops/v3/kms"
"go.mozilla.org/sops/v3/pgp"
)
// SopsFile is a struct used by the stores as a helper to unmarshal the SOPS metadata
type SopsFile struct {
// Metadata is a pointer so we can easily tell when the field is not present
// in the SOPS file by checking for nil. This way we can show the user a
// helpful error message indicating that the metadata wasn't found, instead
// of showing a cryptic parsing error
Metadata *Metadata `yaml:"sops" json:"sops" ini:"sops"`
}
// Metadata is stored in SOPS encrypted files, and it contains the information necessary to decrypt the file.
// This struct is just used for serialization, and SOPS uses another struct internally, sops.Metadata. It exists
// in order to allow the binary format to stay backwards compatible over time, but at the same time allow the internal
// representation SOPS uses to change over time.
type Metadata struct {
ShamirThreshold int `yaml:"shamir_threshold,omitempty" json:"shamir_threshold,omitempty"`
KeyGroups []keygroup `yaml:"key_groups,omitempty" json:"key_groups,omitempty"`
KMSKeys []kmskey `yaml:"kms" json:"kms"`
GCPKMSKeys []gcpkmskey `yaml:"gcp_kms" json:"gcp_kms"`
AzureKeyVaultKeys []azkvkey `yaml:"azure_kv" json:"azure_kv"`
VaultKeys []vaultkey `yaml:"hc_vault" json:"hc_vault"`
AgeKeys []agekey `yaml:"age" json:"age"`
LastModified string `yaml:"lastmodified" json:"lastmodified"`
MessageAuthenticationCode string `yaml:"mac" json:"mac"`
PGPKeys []pgpkey `yaml:"pgp" json:"pgp"`
UnencryptedSuffix string `yaml:"unencrypted_suffix,omitempty" json:"unencrypted_suffix,omitempty"`
EncryptedSuffix string `yaml:"encrypted_suffix,omitempty" json:"encrypted_suffix,omitempty"`
UnencryptedRegex string `yaml:"unencrypted_regex,omitempty" json:"unencrypted_regex,omitempty"`
EncryptedRegex string `yaml:"encrypted_regex,omitempty" json:"encrypted_regex,omitempty"`
Version string `yaml:"version" json:"version"`
}
type keygroup struct {
PGPKeys []pgpkey `yaml:"pgp,omitempty" json:"pgp,omitempty"`
KMSKeys []kmskey `yaml:"kms,omitempty" json:"kms,omitempty"`
GCPKMSKeys []gcpkmskey `yaml:"gcp_kms,omitempty" json:"gcp_kms,omitempty"`
AzureKeyVaultKeys []azkvkey `yaml:"azure_kv,omitempty" json:"azure_kv,omitempty"`
VaultKeys []vaultkey `yaml:"hc_vault" json:"hc_vault"`
AgeKeys []agekey `yaml:"age" json:"age"`
}
type pgpkey struct {
CreatedAt string `yaml:"created_at" json:"created_at"`
EncryptedDataKey string `yaml:"enc" json:"enc"`
Fingerprint string `yaml:"fp" json:"fp"`
}
type kmskey struct {
Arn string `yaml:"arn" json:"arn"`
Role string `yaml:"role,omitempty" json:"role,omitempty"`
Context map[string]*string `yaml:"context,omitempty" json:"context,omitempty"`
CreatedAt string `yaml:"created_at" json:"created_at"`
EncryptedDataKey string `yaml:"enc" json:"enc"`
AwsProfile string `yaml:"aws_profile" json:"aws_profile"`
}
type gcpkmskey struct {
ResourceID string `yaml:"resource_id" json:"resource_id"`
CreatedAt string `yaml:"created_at" json:"created_at"`
EncryptedDataKey string `yaml:"enc" json:"enc"`
}
type vaultkey struct {
VaultAddress string `yaml:"vault_address" json:"vault_address"`
EnginePath string `yaml:"engine_path" json:"engine_path"`
KeyName string `yaml:"key_name" json:"key_name"`
CreatedAt string `yaml:"created_at" json:"created_at"`
EncryptedDataKey string `yaml:"enc" json:"enc"`
}
type azkvkey struct {
VaultURL string `yaml:"vault_url" json:"vault_url"`
Name string `yaml:"name" json:"name"`
Version string `yaml:"version" json:"version"`
CreatedAt string `yaml:"created_at" json:"created_at"`
EncryptedDataKey string `yaml:"enc" json:"enc"`
}
type agekey struct {
Recipient string `yaml:"recipient" json:"recipient"`
EncryptedDataKey string `yaml:"enc" json:"enc"`
}
// MetadataFromInternal converts an internal SOPS metadata representation to a representation appropriate for storage
func MetadataFromInternal(sopsMetadata sops.Metadata) Metadata {
var m Metadata
m.LastModified = sopsMetadata.LastModified.Format(time.RFC3339)
m.UnencryptedSuffix = sopsMetadata.UnencryptedSuffix
m.EncryptedSuffix = sopsMetadata.EncryptedSuffix
m.UnencryptedRegex = sopsMetadata.UnencryptedRegex
m.EncryptedRegex = sopsMetadata.EncryptedRegex
m.MessageAuthenticationCode = sopsMetadata.MessageAuthenticationCode
m.Version = sopsMetadata.Version
m.ShamirThreshold = sopsMetadata.ShamirThreshold
if len(sopsMetadata.KeyGroups) == 1 {
group := sopsMetadata.KeyGroups[0]
m.PGPKeys = pgpKeysFromGroup(group)
m.KMSKeys = kmsKeysFromGroup(group)
m.GCPKMSKeys = gcpkmsKeysFromGroup(group)
m.VaultKeys = vaultKeysFromGroup(group)
m.AzureKeyVaultKeys = azkvKeysFromGroup(group)
m.AgeKeys = ageKeysFromGroup(group)
} else {
for _, group := range sopsMetadata.KeyGroups {
m.KeyGroups = append(m.KeyGroups, keygroup{
KMSKeys: kmsKeysFromGroup(group),
PGPKeys: pgpKeysFromGroup(group),
GCPKMSKeys: gcpkmsKeysFromGroup(group),
VaultKeys: vaultKeysFromGroup(group),
AzureKeyVaultKeys: azkvKeysFromGroup(group),
AgeKeys: ageKeysFromGroup(group),
})
}
}
return m
}
func pgpKeysFromGroup(group sops.KeyGroup) (keys []pgpkey) {
for _, key := range group {
switch key := key.(type) {
case *pgp.MasterKey:
keys = append(keys, pgpkey{
Fingerprint: key.Fingerprint,
EncryptedDataKey: key.EncryptedKey,
CreatedAt: key.CreationDate.Format(time.RFC3339),
})
}
}
return
}
func kmsKeysFromGroup(group sops.KeyGroup) (keys []kmskey) {
for _, key := range group {
switch key := key.(type) {
case *kms.MasterKey:
keys = append(keys, kmskey{
Arn: key.Arn,
CreatedAt: key.CreationDate.Format(time.RFC3339),
EncryptedDataKey: key.EncryptedKey,
Context: key.EncryptionContext,
Role: key.Role,
AwsProfile: key.AwsProfile,
})
}
}
return
}
func gcpkmsKeysFromGroup(group sops.KeyGroup) (keys []gcpkmskey) {
for _, key := range group {
switch key := key.(type) {
case *gcpkms.MasterKey:
keys = append(keys, gcpkmskey{
ResourceID: key.ResourceID,
CreatedAt: key.CreationDate.Format(time.RFC3339),
EncryptedDataKey: key.EncryptedKey,
})
}
}
return
}
func vaultKeysFromGroup(group sops.KeyGroup) (keys []vaultkey) {
for _, key := range group {
switch key := key.(type) {
case *hcvault.MasterKey:
keys = append(keys, vaultkey{
VaultAddress: key.VaultAddress,
EnginePath: key.EnginePath,
KeyName: key.KeyName,
CreatedAt: key.CreationDate.Format(time.RFC3339),
EncryptedDataKey: key.EncryptedKey,
})
}
}
return
}
func azkvKeysFromGroup(group sops.KeyGroup) (keys []azkvkey) {
for _, key := range group {
switch key := key.(type) {
case *azkv.MasterKey:
keys = append(keys, azkvkey{
VaultURL: key.VaultURL,
Name: key.Name,
Version: key.Version,
CreatedAt: key.CreationDate.Format(time.RFC3339),
EncryptedDataKey: key.EncryptedKey,
})
}
}
return
}
func ageKeysFromGroup(group sops.KeyGroup) (keys []agekey) {
for _, key := range group {
switch key := key.(type) {
case *age.MasterKey:
keys = append(keys, agekey{
Recipient: key.Recipient,
EncryptedDataKey: key.EncryptedKey,
})
}
}
return
}
// ToInternal converts a storage-appropriate Metadata struct to a SOPS internal representation
func (m *Metadata) ToInternal() (sops.Metadata, error) {
lastModified, err := time.Parse(time.RFC3339, m.LastModified)
if err != nil {
return sops.Metadata{}, err
}
groups, err := m.internalKeygroups()
if err != nil {
return sops.Metadata{}, err
}
cryptRuleCount := 0
if m.UnencryptedSuffix != "" {
cryptRuleCount++
}
if m.EncryptedSuffix != "" {
cryptRuleCount++
}
if m.UnencryptedRegex != "" {
cryptRuleCount++
}
if m.EncryptedRegex != "" {
cryptRuleCount++
}
if cryptRuleCount > 1 {
return sops.Metadata{}, fmt.Errorf("Cannot use more than one of encrypted_suffix, unencrypted_suffix, encrypted_regex or unencrypted_regex in the same file")
}
if cryptRuleCount == 0 {
m.UnencryptedSuffix = sops.DefaultUnencryptedSuffix
}
return sops.Metadata{
KeyGroups: groups,
ShamirThreshold: m.ShamirThreshold,
Version: m.Version,
MessageAuthenticationCode: m.MessageAuthenticationCode,
UnencryptedSuffix: m.UnencryptedSuffix,
EncryptedSuffix: m.EncryptedSuffix,
UnencryptedRegex: m.UnencryptedRegex,
EncryptedRegex: m.EncryptedRegex,
LastModified: lastModified,
}, nil
}
func internalGroupFrom(kmsKeys []kmskey, pgpKeys []pgpkey, gcpKmsKeys []gcpkmskey, azkvKeys []azkvkey, vaultKeys []vaultkey, ageKeys []agekey) (sops.KeyGroup, error) {
var internalGroup sops.KeyGroup
for _, kmsKey := range kmsKeys {
k, err := kmsKey.toInternal()
if err != nil {
return nil, err
}
internalGroup = append(internalGroup, k)
}
for _, gcpKmsKey := range gcpKmsKeys {
k, err := gcpKmsKey.toInternal()
if err != nil {
return nil, err
}
internalGroup = append(internalGroup, k)
}
for _, azkvKey := range azkvKeys {
k, err := azkvKey.toInternal()
if err != nil {
return nil, err
}
internalGroup = append(internalGroup, k)
}
for _, vaultKey := range vaultKeys {
k, err := vaultKey.toInternal()
if err != nil {
return nil, err
}
internalGroup = append(internalGroup, k)
}
for _, pgpKey := range pgpKeys {
k, err := pgpKey.toInternal()
if err != nil {
return nil, err
}
internalGroup = append(internalGroup, k)
}
for _, ageKey := range ageKeys {
k, err := ageKey.toInternal()
if err != nil {
return nil, err
}
internalGroup = append(internalGroup, k)
}
return internalGroup, nil
}
func (m *Metadata) internalKeygroups() ([]sops.KeyGroup, error) {
var internalGroups []sops.KeyGroup
if len(m.PGPKeys) > 0 || len(m.KMSKeys) > 0 || len(m.GCPKMSKeys) > 0 || len(m.AzureKeyVaultKeys) > 0 || len(m.VaultKeys) > 0 || len(m.AgeKeys) > 0 {
internalGroup, err := internalGroupFrom(m.KMSKeys, m.PGPKeys, m.GCPKMSKeys, m.AzureKeyVaultKeys, m.VaultKeys, m.AgeKeys)
if err != nil {
return nil, err
}
internalGroups = append(internalGroups, internalGroup)
return internalGroups, nil
} else if len(m.KeyGroups) > 0 {
for _, group := range m.KeyGroups {
internalGroup, err := internalGroupFrom(group.KMSKeys, group.PGPKeys, group.GCPKMSKeys, group.AzureKeyVaultKeys, group.VaultKeys, group.AgeKeys)
if err != nil {
return nil, err
}
internalGroups = append(internalGroups, internalGroup)
}
return internalGroups, nil
} else {
return nil, fmt.Errorf("No keys found in file")
}
}
func (kmsKey *kmskey) toInternal() (*kms.MasterKey, error) {
creationDate, err := time.Parse(time.RFC3339, kmsKey.CreatedAt)
if err != nil {
return nil, err
}
return &kms.MasterKey{
Role: kmsKey.Role,
EncryptionContext: kmsKey.Context,
EncryptedKey: kmsKey.EncryptedDataKey,
CreationDate: creationDate,
Arn: kmsKey.Arn,
AwsProfile: kmsKey.AwsProfile,
}, nil
}
func (gcpKmsKey *gcpkmskey) toInternal() (*gcpkms.MasterKey, error) {
creationDate, err := time.Parse(time.RFC3339, gcpKmsKey.CreatedAt)
if err != nil {
return nil, err
}
return &gcpkms.MasterKey{
ResourceID: gcpKmsKey.ResourceID,
EncryptedKey: gcpKmsKey.EncryptedDataKey,
CreationDate: creationDate,
}, nil
}
func (azkvKey *azkvkey) toInternal() (*azkv.MasterKey, error) {
creationDate, err := time.Parse(time.RFC3339, azkvKey.CreatedAt)
if err != nil {
return nil, err
}
return &azkv.MasterKey{
VaultURL: azkvKey.VaultURL,
Name: azkvKey.Name,
Version: azkvKey.Version,
EncryptedKey: azkvKey.EncryptedDataKey,
CreationDate: creationDate,
}, nil
}
func (vaultKey *vaultkey) toInternal() (*hcvault.MasterKey, error) {
creationDate, err := time.Parse(time.RFC3339, vaultKey.CreatedAt)
if err != nil {
return nil, err
}
return &hcvault.MasterKey{
VaultAddress: vaultKey.VaultAddress,
EnginePath: vaultKey.EnginePath,
KeyName: vaultKey.KeyName,
CreationDate: creationDate,
EncryptedKey: vaultKey.EncryptedDataKey,
}, nil
}
func (pgpKey *pgpkey) toInternal() (*pgp.MasterKey, error) {
creationDate, err := time.Parse(time.RFC3339, pgpKey.CreatedAt)
if err != nil {
return nil, err
}
return &pgp.MasterKey{
EncryptedKey: pgpKey.EncryptedDataKey,
CreationDate: creationDate,
Fingerprint: pgpKey.Fingerprint,
}, nil
}
func (ageKey *agekey) toInternal() (*age.MasterKey, error) {
return &age.MasterKey{
EncryptedKey: ageKey.EncryptedDataKey,
Recipient: ageKey.Recipient,
}, nil
}
// ExampleComplexTree is an example sops.Tree object exhibiting complex relationships
var ExampleComplexTree = sops.Tree{
Branches: sops.TreeBranches{
sops.TreeBranch{
sops.TreeItem{
Key: "hello",
Value: `Welcome to SOPS! Edit this file as you please!`,
},
sops.TreeItem{
Key: "example_key",
Value: "example_value",
},
sops.TreeItem{
Key: sops.Comment{Value: " Example comment"},
Value: nil,
},
sops.TreeItem{
Key: "example_array",
Value: []interface{}{
"example_value1",
"example_value2",
},
},
sops.TreeItem{
Key: "example_number",
Value: 1234.56789,
},
sops.TreeItem{
Key: "example_booleans",
Value: []interface{}{true, false},
},
},
},
}
// ExampleSimpleTree is an example sops.Tree object exhibiting only simple relationships
// with only one nested branch and only simple string values
var ExampleSimpleTree = sops.Tree{
Branches: sops.TreeBranches{
sops.TreeBranch{
sops.TreeItem{
Key: "Welcome!",
Value: sops.TreeBranch{
sops.TreeItem{
Key: sops.Comment{Value: " This is an example file."},
Value: nil,
},
sops.TreeItem{
Key: "hello",
Value: "Welcome to SOPS! Edit this file as you please!",
},
sops.TreeItem{
Key: "example_key",
Value: "example_value",
},
},
},
},
},
}
// ExampleFlatTree is an example sops.Tree object exhibiting only simple relationships
// with no nested branches and only simple string values
var ExampleFlatTree = sops.Tree{
Branches: sops.TreeBranches{
sops.TreeBranch{
sops.TreeItem{
Key: sops.Comment{Value: " This is an example file."},
Value: nil,
},
sops.TreeItem{
Key: "hello",
Value: "Welcome to SOPS! Edit this file as you please!",
},
sops.TreeItem{
Key: "example_key",
Value: "example_value",
},
sops.TreeItem{
Key: "example_multiline",
Value: "foo\nbar\nbaz",
},
},
},
}
070701000000AB000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001700000000sops-3.7.3/stores/yaml070701000000AC000081A400000000000000000000000162794F9300002C07000000000000000000000000000000000000002000000000sops-3.7.3/stores/yaml/store.gopackage yaml //import "go.mozilla.org/sops/v3/stores/yaml"
import (
"bytes"
"fmt"
"io"
"strings"
"gopkg.in/yaml.v3"
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/stores"
)
// Store handles storage of YAML data
type Store struct {
}
func (store Store) appendCommentToList(comment string, list []interface{}) []interface{} {
if comment != "" {
for _, commentLine := range strings.Split(comment, "\n") {
if commentLine != "" {
list = append(list, sops.Comment{
Value: commentLine[1:],
})
}
}
}
return list
}
func (store Store) appendCommentToMap(comment string, branch sops.TreeBranch) sops.TreeBranch {
if comment != "" {
for _, commentLine := range strings.Split(comment, "\n") {
if commentLine != "" {
branch = append(branch, sops.TreeItem{
Key: sops.Comment{
Value: commentLine[1:],
},
Value: nil,
})
}
}
}
return branch
}
func (store Store) nodeToTreeValue(node *yaml.Node, commentsWereHandled bool) (interface{}, error) {
switch node.Kind {
case yaml.DocumentNode:
panic("documents should never be passed here")
case yaml.SequenceNode:
var result []interface{}
if !commentsWereHandled {
result = store.appendCommentToList(node.HeadComment, result)
result = store.appendCommentToList(node.LineComment, result)
}
for _, item := range node.Content {
result = store.appendCommentToList(item.HeadComment, result)
result = store.appendCommentToList(item.LineComment, result)
val, err := store.nodeToTreeValue(item, true)
if err != nil {
return nil, err
}
result = append(result, val)
result = store.appendCommentToList(item.FootComment, result)
}
if !commentsWereHandled {
result = store.appendCommentToList(node.FootComment, result)
}
return result, nil
case yaml.MappingNode:
branch := make(sops.TreeBranch, 0)
return store.appendYamlNodeToTreeBranch(node, branch, commentsWereHandled)
case yaml.ScalarNode:
var result interface{}
node.Decode(&result)
return result, nil
case yaml.AliasNode:
return store.nodeToTreeValue(node.Alias, false);
}
return nil, nil
}
func (store Store) appendYamlNodeToTreeBranch(node *yaml.Node, branch sops.TreeBranch, commentsWereHandled bool) (sops.TreeBranch, error) {
var err error
if !commentsWereHandled {
branch = store.appendCommentToMap(node.HeadComment, branch)
branch = store.appendCommentToMap(node.LineComment, branch)
}
switch node.Kind {
case yaml.DocumentNode:
for _, item := range node.Content {
branch, err = store.appendYamlNodeToTreeBranch(item, branch, false)
if err != nil {
return nil, err
}
}
case yaml.SequenceNode:
return nil, fmt.Errorf("YAML documents that are sequences are not supported")
case yaml.MappingNode:
for i := 0; i < len(node.Content); i += 2 {
key := node.Content[i]
value := node.Content[i + 1]
branch = store.appendCommentToMap(key.HeadComment, branch)
branch = store.appendCommentToMap(key.LineComment, branch)
handleValueComments := value.Kind == yaml.ScalarNode || value.Kind == yaml.AliasNode
if handleValueComments {
branch = store.appendCommentToMap(value.HeadComment, branch)
branch = store.appendCommentToMap(value.LineComment, branch)
}
var keyValue interface{}
key.Decode(&keyValue)
valueTV, err := store.nodeToTreeValue(value, handleValueComments)
if err != nil {
return nil, err
}
branch = append(branch, sops.TreeItem{
Key: keyValue,
Value: valueTV,
})
if handleValueComments {
branch = store.appendCommentToMap(value.FootComment, branch)
}
branch = store.appendCommentToMap(key.FootComment, branch)
}
case yaml.ScalarNode:
// A empty document with a document start marker without comments results in null
if node.ShortTag() == "!!null" {
return branch, nil
}
return nil, fmt.Errorf("YAML documents that are values are not supported")
case yaml.AliasNode:
branch, err = store.appendYamlNodeToTreeBranch(node.Alias, branch, false)
}
if !commentsWereHandled {
branch = store.appendCommentToMap(node.FootComment, branch)
}
return branch, nil
}
func (store Store) yamlDocumentNodeToTreeBranch(in yaml.Node) (sops.TreeBranch, error) {
branch := make(sops.TreeBranch, 0)
return store.appendYamlNodeToTreeBranch(&in, branch, false)
}
func (store *Store) addCommentsHead(node *yaml.Node, comments []string) []string {
if len(comments) > 0 {
comment := "#" + strings.Join(comments, "\n#")
if len(node.HeadComment) > 0 {
node.HeadComment = comment + "\n" + node.HeadComment
} else {
node.HeadComment = comment
}
}
return nil
}
func (store *Store) addCommentsFoot(node *yaml.Node, comments []string) []string {
if len(comments) > 0 {
comment := "#" + strings.Join(comments, "\n#")
if len(node.FootComment) > 0 {
node.FootComment += "\n" + comment
} else {
node.FootComment = comment
}
}
return nil
}
func (store *Store) treeValueToNode(in interface{}) *yaml.Node {
switch in := in.(type) {
case sops.TreeBranch:
var mapping = &yaml.Node{}
mapping.Kind = yaml.MappingNode
store.appendTreeBranch(in, mapping)
return mapping
case []interface{}:
var sequence = &yaml.Node{}
sequence.Kind = yaml.SequenceNode
store.appendSequence(in, sequence)
return sequence
default:
var valueNode = &yaml.Node{}
valueNode.Encode(in)
return valueNode
}
}
func (store *Store) appendSequence(in []interface{}, sequence *yaml.Node) {
var comments []string
var beginning bool = true
for _, item := range in {
if comment, ok := item.(sops.Comment); ok {
comments = append(comments, comment.Value)
} else {
if beginning {
comments = store.addCommentsHead(sequence, comments)
beginning = false
}
itemNode := store.treeValueToNode(item)
comments = store.addCommentsHead(itemNode, comments)
sequence.Content = append(sequence.Content, itemNode)
}
}
if len(comments) > 0 {
if beginning {
comments = store.addCommentsHead(sequence, comments)
} else {
comments = store.addCommentsFoot(sequence.Content[len(sequence.Content) - 1], comments)
}
}
}
func (store *Store) appendTreeBranch(branch sops.TreeBranch, mapping *yaml.Node) {
var comments []string
var beginning bool = true
for _, item := range branch {
if comment, ok := item.Key.(sops.Comment); ok {
comments = append(comments, comment.Value)
} else {
if beginning {
comments = store.addCommentsHead(mapping, comments)
beginning = false
}
var keyNode = &yaml.Node{}
keyNode.Encode(item.Key)
comments = store.addCommentsHead(keyNode, comments)
valueNode := store.treeValueToNode(item.Value)
mapping.Content = append(mapping.Content, keyNode, valueNode)
}
}
if len(comments) > 0 {
if beginning {
comments = store.addCommentsHead(mapping, comments)
} else {
comments = store.addCommentsFoot(mapping.Content[len(mapping.Content) - 1], comments)
}
}
}
// LoadEncryptedFile loads the contents of an encrypted yaml file onto a
// sops.Tree runtime object
func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
// Because we don't know what fields the input file will have, we have to
// load the file in two steps.
// First, we load the file's metadata, the structure of which is known.
metadataHolder := stores.SopsFile{}
err := yaml.Unmarshal(in, &metadataHolder)
if err != nil {
return sops.Tree{}, fmt.Errorf("Error unmarshalling input yaml: %s", err)
}
if metadataHolder.Metadata == nil {
return sops.Tree{}, sops.MetadataNotFound
}
metadata, err := metadataHolder.Metadata.ToInternal()
if err != nil {
return sops.Tree{}, err
}
var data yaml.Node
if err := yaml.Unmarshal(in, &data); err != nil {
return sops.Tree{}, fmt.Errorf("Error unmarshaling input YAML: %s", err)
}
var branches sops.TreeBranches
d := yaml.NewDecoder(bytes.NewReader(in))
for true {
var data yaml.Node
err := d.Decode(&data)
if err == io.EOF {
break
}
if err != nil {
return sops.Tree{}, fmt.Errorf("Error unmarshaling input YAML: %s", err)
}
branch, err := store.yamlDocumentNodeToTreeBranch(data)
if err != nil {
return sops.Tree{}, fmt.Errorf("Error unmarshaling input YAML: %s", err)
}
for i, elt := range branch {
if elt.Key == "sops" { // Erase
branch = append(branch[:i], branch[i+1:]...)
}
}
branches = append(branches, branch)
}
return sops.Tree{
Branches: branches,
Metadata: metadata,
}, nil
}
// LoadPlainFile loads the contents of a plaintext yaml file onto a
// sops.Tree runtime object
func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
var branches sops.TreeBranches
d := yaml.NewDecoder(bytes.NewReader(in))
for true {
var data yaml.Node
err := d.Decode(&data)
if err == io.EOF {
break
}
if err != nil {
return nil, fmt.Errorf("Error unmarshaling input YAML: %s", err)
}
branch, err := store.yamlDocumentNodeToTreeBranch(data)
if err != nil {
return nil, fmt.Errorf("Error unmarshaling input YAML: %s", err)
}
branches = append(branches, branch)
}
return branches, nil
}
// EmitEncryptedFile returns the encrypted bytes of the yaml file corresponding to a
// sops.Tree runtime object
func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
var b bytes.Buffer
e := yaml.NewEncoder(io.Writer(&b))
e.SetIndent(4)
for _, branch := range in.Branches {
// Document root
var doc = yaml.Node{}
doc.Kind = yaml.DocumentNode
// Add global mapping
var mapping = yaml.Node{}
mapping.Kind = yaml.MappingNode
doc.Content = append(doc.Content, &mapping)
// Create copy of branch with metadata appended
branch = append(sops.TreeBranch(nil), branch...)
branch = append(branch, sops.TreeItem{
Key: "sops",
Value: stores.MetadataFromInternal(in.Metadata),
})
// Marshal branch to global mapping node
store.appendTreeBranch(branch, &mapping)
// Encode YAML
err := e.Encode(&doc)
if err != nil {
return nil, fmt.Errorf("Error marshaling to yaml: %s", err)
}
}
e.Close()
return b.Bytes(), nil
}
// EmitPlainFile returns the plaintext bytes of the yaml file corresponding to a
// sops.TreeBranches runtime object
func (store *Store) EmitPlainFile(branches sops.TreeBranches) ([]byte, error) {
var b bytes.Buffer
e := yaml.NewEncoder(io.Writer(&b))
e.SetIndent(4)
for _, branch := range branches {
// Document root
var doc = yaml.Node{}
doc.Kind = yaml.DocumentNode
// Add global mapping
var mapping = yaml.Node{}
mapping.Kind = yaml.MappingNode
// Marshal branch to global mapping node
store.appendTreeBranch(branch, &mapping)
doc.Content = append(doc.Content, &mapping)
// Encode YAML
err := e.Encode(&doc)
if err != nil {
return nil, fmt.Errorf("Error marshaling to yaml: %s", err)
}
}
e.Close()
return b.Bytes(), nil
}
// EmitValue returns bytes corresponding to a single encoded value
// in a generic interface{} object
func (store *Store) EmitValue(v interface{}) ([]byte, error) {
n := store.treeValueToNode(v)
return yaml.Marshal(n)
}
// EmitExample returns the bytes corresponding to an example complex tree
func (store *Store) EmitExample() []byte {
bytes, err := store.EmitPlainFile(stores.ExampleComplexTree.Branches)
if err != nil {
panic(err)
}
return bytes
}
070701000000AD000081A400000000000000000000000162794F930000170C000000000000000000000000000000000000002500000000sops-3.7.3/stores/yaml/store_test.gopackage yaml
import (
"testing"
"github.com/stretchr/testify/assert"
"go.mozilla.org/sops/v3"
)
var PLAIN = []byte(`---
# comment 0
key1: value
key1_a: value
# ^ comment 1
---
key2: value2`)
var PLAIN_0 = []byte(`# comment 0
key1: value
key1_a: value
# ^ comment 1
`)
var BRANCHES = sops.TreeBranches{
sops.TreeBranch{
sops.TreeItem{
Key: sops.Comment{" comment 0"},
Value: nil,
},
sops.TreeItem{
Key: "key1",
Value: "value",
},
sops.TreeItem{
Key: "key1_a",
Value: "value",
},
sops.TreeItem{
Key: sops.Comment{" ^ comment 1"},
Value: nil,
},
},
sops.TreeBranch{
sops.TreeItem{
Key: "key2",
Value: "value2",
},
},
}
var COMMENT_1 = []byte(`# test
a:
b: null
# foo
`)
var COMMENT_2 = []byte(`a:
# foo
b: null
`)
var COMMENT_3_IN = []byte(`## Configuration for prometheus-node-exporter subchart
##
prometheus-node-exporter:
podLabels:
## Add the 'node-exporter' label to be used by serviceMonitor to match standard common usage in rules and grafana dashboards
##
jobLabel: node-exporter
extraArgs:
- --collector.filesystem.ignored-mount-points=^/(dev|proc|sys|var/lib/docker/.+)($|/)
- --collector.filesystem.ignored-fs-types=^(autofs|binfmt_misc|cgroup|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|sysfs|tracefs)$
`)
var COMMENT_3_OUT = []byte(`## Configuration for prometheus-node-exporter subchart
##
prometheus-node-exporter:
podLabels:
## Add the 'node-exporter' label to be used by serviceMonitor to match standard common usage in rules and grafana dashboards
##
jobLabel: node-exporter
extraArgs:
- --collector.filesystem.ignored-mount-points=^/(dev|proc|sys|var/lib/docker/.+)($|/)
- --collector.filesystem.ignored-fs-types=^(autofs|binfmt_misc|cgroup|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|sysfs|tracefs)$
`)
var COMMENT_4 = []byte(`# foo
`)
var COMMENT_5 = []byte(`# foo
---
key: value
`)
// The following is a regression test for https://github.com/mozilla/sops/issues/865
var COMMENT_6 = []byte(`a:
- a
# I no longer get duplicated
- {}
`)
var COMMENT_6_BRANCHES = sops.TreeBranches{
sops.TreeBranch{
sops.TreeItem{
Key: "a",
Value: []interface{}{
"a",
sops.Comment{" I no longer get duplicated"},
sops.TreeBranch{},
},
},
},
}
func TestUnmarshalMetadataFromNonSOPSFile(t *testing.T) {
data := []byte(`hello: 2`)
_, err := (&Store{}).LoadEncryptedFile(data)
assert.Equal(t, sops.MetadataNotFound, err)
}
func TestLoadPlainFile(t *testing.T) {
branches, err := (&Store{}).LoadPlainFile(PLAIN)
assert.Nil(t, err)
assert.Equal(t, BRANCHES, branches)
}
func TestComment1(t *testing.T) {
// First iteration: load and store
branches, err := (&Store{}).LoadPlainFile(COMMENT_1)
assert.Nil(t, err)
bytes, err := (&Store{}).EmitPlainFile(branches)
assert.Nil(t, err)
assert.Equal(t, string(COMMENT_1), string(bytes))
assert.Equal(t, COMMENT_1, bytes)
}
func TestComment2(t *testing.T) {
// First iteration: load and store
branches, err := (&Store{}).LoadPlainFile(COMMENT_2)
assert.Nil(t, err)
bytes, err := (&Store{}).EmitPlainFile(branches)
assert.Nil(t, err)
assert.Equal(t, string(COMMENT_2), string(bytes))
assert.Equal(t, COMMENT_2, bytes)
}
func TestComment3(t *testing.T) {
// First iteration: load and store
branches, err := (&Store{}).LoadPlainFile(COMMENT_3_IN)
assert.Nil(t, err)
bytes, err := (&Store{}).EmitPlainFile(branches)
assert.Nil(t, err)
assert.Equal(t, string(COMMENT_3_OUT), string(bytes))
assert.Equal(t, COMMENT_3_OUT, bytes)
}
/* TODO: re-enable once https://github.com/go-yaml/yaml/pull/690 is merged
func TestComment4(t *testing.T) {
// First iteration: load and store
branches, err := (&Store{}).LoadPlainFile(COMMENT_4)
assert.Nil(t, err)
bytes, err := (&Store{}).EmitPlainFile(branches)
assert.Nil(t, err)
assert.Equal(t, string(COMMENT_4), string(bytes))
assert.Equal(t, COMMENT_4, bytes)
}
func TestComment5(t *testing.T) {
// First iteration: load and store
branches, err := (&Store{}).LoadPlainFile(COMMENT_5)
assert.Nil(t, err)
bytes, err := (&Store{}).EmitPlainFile(branches)
assert.Nil(t, err)
assert.Equal(t, string(COMMENT_5), string(bytes))
assert.Equal(t, COMMENT_5, bytes)
}
*/
func TestEmpty(t *testing.T) {
// First iteration: load and store
branches, err := (&Store{}).LoadPlainFile([]byte(``))
assert.Nil(t, err)
assert.Equal(t, len(branches), 0)
bytes, err := (&Store{}).EmitPlainFile(branches)
assert.Nil(t, err)
assert.Equal(t, ``, string(bytes))
}
/* TODO: re-enable once https://github.com/go-yaml/yaml/pull/690 is merged
func TestEmpty2(t *testing.T) {
// First iteration: load and store
branches, err := (&Store{}).LoadPlainFile([]byte(`---`))
assert.Nil(t, err)
assert.Equal(t, len(branches), 1)
assert.Equal(t, len(branches[0]), 0)
bytes, err := (&Store{}).EmitPlainFile(branches)
assert.Nil(t, err)
assert.Equal(t, ``, string(bytes))
}
*/
func TestEmpty3(t *testing.T) {
branches, err := (&Store{}).LoadPlainFile([]byte("{}\n"))
assert.Nil(t, err)
assert.Equal(t, len(branches), 1)
bytes, err := (&Store{}).EmitPlainFile(branches)
assert.Nil(t, err)
assert.Equal(t, "{}\n", string(bytes))
}
func TestComment6(t *testing.T) {
branches, err := (&Store{}).LoadPlainFile(COMMENT_6)
assert.Nil(t, err)
assert.Equal(t, COMMENT_6_BRANCHES, branches)
bytes, err := (&Store{}).EmitPlainFile(branches)
assert.Nil(t, err)
assert.Equal(t, string(COMMENT_6), string(bytes))
assert.Equal(t, COMMENT_6, bytes)
}
func TestEmitValue(t *testing.T) {
// First iteration: load and store
bytes, err := (&Store{}).EmitValue(BRANCHES[0])
assert.Nil(t, err)
assert.Equal(t, string(PLAIN_0), string(bytes))
assert.Equal(t, PLAIN_0, bytes)
}
070701000000AE000081ED00000000000000000000000162794F930000016D000000000000000000000000000000000000001300000000sops-3.7.3/test.sh#!/usr/bin/env bash
set -e
echo "" > coverage.txt
failed=0
for d in $(go list ./... | grep -v vendor); do
go test -race -coverprofile=profile.out -covermode=atomic $d && true
rc=$?
if [ $rc != 0 ]; then
failed=$rc
fi
if [ -f profile.out ]; then
cat profile.out >> coverage.txt
rm profile.out
fi
done
exit ${failed}
070701000000AF000081A400000000000000000000000162794F930000115E000000000000000000000000000000000000001900000000sops-3.7.3/usererrors.gopackage sops
import (
"fmt"
"io/ioutil"
"strings"
"github.com/fatih/color"
"github.com/goware/prefixer"
wordwrap "github.com/mitchellh/go-wordwrap"
)
// UserError is a well-formatted error for the purpose of being displayed to
// the end user.
type UserError interface {
error
UserError() string
}
var statusSuccess = color.New(color.FgGreen).Sprint("SUCCESS")
var statusFailed = color.New(color.FgRed).Sprint("FAILED")
type getDataKeyError struct {
RequiredSuccessfulKeyGroups int
GroupResults []error
}
func (err *getDataKeyError) successfulKeyGroups() int {
n := 0
for _, r := range err.GroupResults {
if r == nil {
n++
}
}
return n
}
func (err *getDataKeyError) Error() string {
return fmt.Sprintf("Error getting data key: %d successful groups "+
"required, got %d", err.RequiredSuccessfulKeyGroups,
err.successfulKeyGroups())
}
func (err *getDataKeyError) UserError() string {
var groupErrs []string
for i, res := range err.GroupResults {
groupErr := decryptGroupError{
err: res,
groupName: fmt.Sprintf("%d", i),
}
groupErrs = append(groupErrs, groupErr.UserError())
}
var trailer string
if err.RequiredSuccessfulKeyGroups == 0 {
trailer = "Recovery failed because no master key was able to decrypt " +
"the file. In order for SOPS to recover the file, at least one key " +
"has to be successful, but none were."
} else {
trailer = fmt.Sprintf("Recovery failed because the file was "+
"encrypted with a Shamir threshold of %d, but only %d part(s) "+
"were successfully recovered, one for each successful key group. "+
"In order for SOPS to recover the file, at least %d groups have "+
"to be successful. In order for a group to be successful, "+
"decryption has to succeed with any of the keys in that key group.",
err.RequiredSuccessfulKeyGroups, err.successfulKeyGroups(),
err.RequiredSuccessfulKeyGroups)
}
trailer = wordwrap.WrapString(trailer, 75)
return fmt.Sprintf("Failed to get the data key required to "+
"decrypt the SOPS file.\n\n%s\n\n%s",
strings.Join(groupErrs, "\n\n"), trailer)
}
type decryptGroupError struct {
groupName string
err error
}
func (r *decryptGroupError) Error() string {
return fmt.Sprintf("could not decrypt group %s: %s", r.groupName, r.err)
}
func (r *decryptGroupError) UserError() string {
var status string
if r.err == nil {
status = statusSuccess
} else {
status = statusFailed
}
header := fmt.Sprintf(`Group %s: %s`, r.groupName, status)
if r.err == nil {
return header
}
message := r.err.Error()
if userError, ok := r.err.(UserError); ok {
message = userError.UserError()
}
reader := prefixer.New(strings.NewReader(message), " ")
// Safe to ignore this error, as reading from a strings.Reader can't fail
errMsg, _ := ioutil.ReadAll(reader)
return fmt.Sprintf("%s\n%s", header, string(errMsg))
}
type decryptKeyErrors []error
func (e decryptKeyErrors) Error() string {
return fmt.Sprintf("error decrypting key: %s", []error(e))
}
func (e decryptKeyErrors) UserError() string {
var errStrs []string
for _, err := range []error(e) {
if userErr, ok := err.(UserError); ok {
errStrs = append(errStrs, userErr.UserError())
} else {
errStrs = append(errStrs, err.Error())
}
}
return strings.Join(errStrs, "\n\n")
}
type decryptKeyError struct {
keyName string
errs []error
}
func (e *decryptKeyError) isSuccessful() bool {
for _, err := range e.errs {
if err == nil {
return true
}
}
return false
}
func (e *decryptKeyError) Error() string {
return fmt.Sprintf("error decrypting key %s: %s", e.keyName, e.errs)
}
func (e *decryptKeyError) UserError() string {
var status string
if e.isSuccessful() {
status = statusSuccess
} else {
status = statusFailed
}
header := fmt.Sprintf("%s: %s", e.keyName, status)
if e.isSuccessful() {
return header
}
var errMessages []string
for _, err := range e.errs {
wrappedErr := wordwrap.WrapString(err.Error(), 60)
reader := prefixer.New(strings.NewReader(wrappedErr), " | ")
// Safe to ignore this error, as reading from a strings.Reader can't fail
errMsg, _ := ioutil.ReadAll(reader)
errMsg[0] = '-'
errMessages = append(errMessages, string(errMsg))
}
joinedMsgs := strings.Join(errMessages, "\n\n")
reader := prefixer.New(strings.NewReader(joinedMsgs), " ")
errMsg, _ := ioutil.ReadAll(reader)
return fmt.Sprintf("%s\n%s", header, string(errMsg))
}
070701000000B0000041ED00000000000000000000000362794F9300000000000000000000000000000000000000000000001600000000sops-3.7.3/validation070701000000B1000081A400000000000000000000000162794F93000000A0000000000000000000000000000000000000002100000000sops-3.7.3/validation/Cargo.toml[package]
name = "validation"
version = "0.1.0"
authors = ["Adrian Utrilla <adrianutrilla@gmail.com>"]
[dependencies]
serde_json = "0.8.0"
yaml-rust = "0.3.3"
070701000000B2000081A400000000000000000000000162794F9300000314000000000000000000000000000000000000002300000000sops-3.7.3/validation/example.json{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": [
"GML",
"XML"
]
},
"GlossSee": "markup"
}
}
}
}
}
070701000000B3000081A400000000000000000000000162794F93000000B7000000000000000000000000000000000000002300000000sops-3.7.3/validation/example.yamlname: Martin
job: Developer
skill: Elite
foods:
- Apple
- Orange
- Strawberry
- Mango
languages:
perl: Elite
python: Elite
pascal: Lame
foo: |
bar
baz
070701000000B4000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001A00000000sops-3.7.3/validation/src070701000000B5000081A400000000000000000000000162794F9300000E76000000000000000000000000000000000000002200000000sops-3.7.3/validation/src/main.rsuse std::rc::Rc;
use std::process::{Stdio, Command};
use std::io::{Write, Read};
use std::fs::{OpenOptions, File};
use std::str;
use std::env;
extern crate serde_json;
use serde_json::Value;
extern crate yaml_rust;
use yaml_rust::{YamlLoader, YamlEmitter};
fn main() {}
fn run_sops_and_return_output(command: &mut Command, filename: &str) -> String {
let mut child = command.stdout(Stdio::piped())
.arg(filename)
.spawn()
.expect("Could not start sops python process");
let output = child.wait_with_output().expect("Could not retrieve sops's output");
if !output.status.success() {
panic!("sops did not exit successfully!");
}
return String::from_utf8(output.stdout).expect("Could not decode sops's output as utf-8");
}
fn get_sops_python() -> Command {
let sops_python_path = env::var("SOPS_PYTHON_PATH")
.expect("SOPS_PYTHON_PATH environment variable missing");
let mut cmd = Command::new("python");
cmd.arg(sops_python_path);
cmd
}
fn encrypt_with_sops_python(plaintext: &str) -> String {
let mut child = get_sops_python();
let child = child.arg("-e");
return run_sops_and_return_output(child, plaintext);
}
fn decrypt_with_sops_python(ciphertext: &str) -> String {
let mut child = get_sops_python();
let child = child.arg("-d");
return run_sops_and_return_output(child, ciphertext);
}
fn validate_json_file(input_file_name: &str,
encrypt: fn(&str) -> String,
decrypt: fn(&str) -> String) {
let output_file_name = "temp.json";
let mut input = String::new();
File::open(input_file_name).unwrap().read_to_string(&mut input);
let input_value: Value = serde_json::from_str(&input).expect("Could not decode input json");
let encrypted_output = encrypt(input_file_name);
let mut output_file = OpenOptions::new()
.write(true)
.create(true)
.open(output_file_name)
.expect("Could not open output file");
output_file.write_all(encrypted_output.as_bytes()).expect("Could not write to output file");
let decryption = decrypt(output_file_name);
let output_value: Value = serde_json::from_str(&decryption).unwrap();
std::fs::remove_file(output_file_name).expect("Could not remove output file");
assert_eq!(input_value, output_value);
}
fn validate_yaml_file(input_file_name: &str,
encrypt: fn(&str) -> String,
decrypt: fn(&str) -> String) {
let output_file_name = "temp.yaml";
let mut input = String::new();
File::open(input_file_name).unwrap().read_to_string(&mut input);
let input_value = YamlLoader::load_from_str(&input).expect("Could not decode input yaml");
let encrypted_output = encrypt(input_file_name);
let mut output_file = OpenOptions::new()
.write(true)
.create(true)
.open(output_file_name)
.expect("Could not open output file");
output_file.write_all(encrypted_output.as_bytes()).expect("Could not write to output file");
let decryption = decrypt(output_file_name);
let output_value = YamlLoader::load_from_str(&decryption)
.expect("Could not decode output yaml");
std::fs::remove_file(output_file_name).expect("Could not remove output file");
assert_eq!(input_value, output_value);
}
#[test]
fn validate_python_json() {
validate_json_file("example.json",
encrypt_with_sops_python,
decrypt_with_sops_python);
}
#[test]
fn validate_python_yaml() {
validate_yaml_file("example.yaml",
encrypt_with_sops_python,
decrypt_with_sops_python);
}
070701000000B6000041ED00000000000000000000000262794F9300000000000000000000000000000000000000000000001300000000sops-3.7.3/version070701000000B7000081A400000000000000000000000162794F93000009AF000000000000000000000000000000000000001E00000000sops-3.7.3/version/version.gopackage version
import (
"bufio"
"fmt"
"net/http"
"strings"
"github.com/blang/semver"
"gopkg.in/urfave/cli.v1"
)
// Version represents the value of the current semantic version
const Version = "3.7.3"
// PrintVersion handles the version command for sops
func PrintVersion(c *cli.Context) {
out := fmt.Sprintf("%s %s", c.App.Name, c.App.Version)
upstreamVersion, err := RetrieveLatestVersionFromUpstream()
if err != nil {
out += fmt.Sprintf("\n[warning] failed to retrieve latest version from upstream: %v\n", err)
}
outdated, err := AIsNewerThanB(upstreamVersion, Version)
if err != nil {
out += fmt.Sprintf("\n[warning] failed to compare current version with latest: %v\n", err)
}
if outdated {
out += fmt.Sprintf("\n[info] sops %s is available, update with `go get -u go.mozilla.org/sops/v3/cmd/sops`\n", upstreamVersion)
} else {
out += " (latest)\n"
}
fmt.Fprintf(c.App.Writer, "%s", out)
}
// AIsNewerThanB takes 2 semver strings are returns true
// is the A is newer than B, false otherwise
func AIsNewerThanB(A, B string) (bool, error) {
if strings.HasPrefix(B, "1.") {
// sops 1.0 doesn't use the semver format, which will
// fail the call to `make` below. Since we now we're
// more recent than 1.X anyway, return true right away
return true, nil
}
vA, err := semver.Make(A)
if err != nil {
return false, err
}
vB, err := semver.Make(B)
if err != nil {
return false, err
}
if vA.Compare(vB) > 0 {
// vA is newer than vB
return true, nil
}
return false, nil
}
// RetrieveLatestVersionFromUpstream gets the latest version from the source code at Github
func RetrieveLatestVersionFromUpstream() (string, error) {
resp, err := http.Get("https://raw.githubusercontent.com/mozilla/sops/master/version/version.go")
if err != nil {
return "", err
}
defer resp.Body.Close()
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, `const Version = "`) {
comps := strings.Split(line, `"`)
if len(comps) < 2 {
return "", fmt.Errorf("Failed to parse version from upstream source")
}
// try to parse the version as semver
_, err := semver.Make(comps[1])
if err != nil {
return "", fmt.Errorf("Retrieved version %q does not match semver format: %w", comps[1], err)
}
return comps[1], nil
}
}
if err := scanner.Err(); err != nil {
return "", err
}
return "", fmt.Errorf("Version information not found in upstream file")
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!1674 blocks