mirror of
https://github.com/getsops/sops.git
synced 2026-02-05 12:45:21 +01:00
Merge branch 'main' into cg/minimum-sops-config
This commit is contained in:
15
.github/dependabot.yaml
vendored
15
.github/dependabot.yaml
vendored
@@ -2,7 +2,7 @@ version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
directory: "/.release"
|
||||
labels: ["dependencies"]
|
||||
schedule:
|
||||
# By default, this will be on a Monday.
|
||||
@@ -39,3 +39,16 @@ updates:
|
||||
go:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/functional-tests"
|
||||
labels: ["area/CI", "dependencies"]
|
||||
schedule:
|
||||
# By default, this will be on a Monday.
|
||||
interval: "weekly"
|
||||
groups:
|
||||
# Group all updates together, so that they are all applied in a single PR.
|
||||
# xref: https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#groups
|
||||
rust:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
77
.github/utils/patch-go.mod.py
vendored
Normal file
77
.github/utils/patch-go.mod.py
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
"""
|
||||
Patch go.mod so that the lines 'go xxx' to 'toolchain xxx' are as in git's
|
||||
HEAD.
|
||||
|
||||
This is necessary since newer 'go mod tidy' versions tend to modify these
|
||||
lines. Since we check in CI that 'go mod tidy' does not change go.mod, this
|
||||
causes CI to fail.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
|
||||
|
||||
def split_go_mod(contents: str) -> tuple[list[str], list[str], list[str]]:
|
||||
"""
|
||||
Given the contents of go.mod, splits it into three lists of lines
|
||||
(with endings):
|
||||
1. The lines before 'go';
|
||||
2. The lines starting with 'go' and ending with 'toolchain';
|
||||
3. The lines after 'toolchain'.
|
||||
"""
|
||||
parts: tuple[list[str], list[str], list[str]] = ([], [], [])
|
||||
index = 0
|
||||
for line in contents.splitlines(keepends=True):
|
||||
next_index = index
|
||||
if line.startswith('go '):
|
||||
index = next_index = 1
|
||||
if line.startswith('toolchain '):
|
||||
next_index = 2
|
||||
parts[index].append(line)
|
||||
index = next_index
|
||||
return parts
|
||||
|
||||
|
||||
def get_file_contents_from_git_revision(filename: str, revision: str) -> str:
|
||||
"""
|
||||
Get the file contents of ``filename`` from Git revision ``revision``.
|
||||
"""
|
||||
p = subprocess.run(
|
||||
['git', 'show', f'{revision}:{filename}'],
|
||||
stdout=subprocess.PIPE,
|
||||
check=True,
|
||||
encoding='utf-8',
|
||||
)
|
||||
return p.stdout
|
||||
|
||||
|
||||
def read_file(filename: str) -> str:
|
||||
"""
|
||||
Read the file's contents.
|
||||
"""
|
||||
with open(filename, 'r', encoding='utf-8') as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def write_file(filename: str, contents: str) -> None:
|
||||
"""
|
||||
Write the file's contents.
|
||||
"""
|
||||
with open(filename, 'w', encoding='utf-8') as f:
|
||||
f.write(contents)
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Patches go.mod.
|
||||
"""
|
||||
filename = 'go.mod'
|
||||
_, go_versions, __ = split_go_mod(
|
||||
get_file_contents_from_git_revision(filename, 'HEAD')
|
||||
)
|
||||
head, _, tail = split_go_mod(read_file(filename))
|
||||
lines = head + go_versions + tail
|
||||
write_file(filename, ''.join(lines))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
92
.github/workflows/cli.yml
vendored
92
.github/workflows/cli.yml
vendored
@@ -13,30 +13,33 @@ permissions:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and test ${{ matrix.os }} ${{ matrix.arch }}
|
||||
name: Build and test ${{ matrix.os }} ${{ matrix.arch }} ${{ matrix.go-version }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
os: [linux, darwin, windows]
|
||||
arch: [amd64, arm64]
|
||||
go-version: ['1.22', '1.23']
|
||||
exclude:
|
||||
- os: windows
|
||||
arch: arm64
|
||||
env:
|
||||
VAULT_VERSION: "1.1.3"
|
||||
VAULT_VERSION: "1.14.0"
|
||||
VAULT_TOKEN: "root"
|
||||
VAULT_ADDR: "http://127.0.0.1:8200"
|
||||
steps:
|
||||
- name: Set up Go 1.21
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
- name: Set up Go ${{ matrix.go-version }}
|
||||
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: ${{ matrix.go-version }}
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
@@ -46,16 +49,19 @@ jobs:
|
||||
- name: Vendor Go Modules
|
||||
run: make vendor
|
||||
|
||||
- name: Restore go/toolchain lines of go.mod
|
||||
run: python3 .github/utils/patch-go.mod.py
|
||||
|
||||
- name: Ensure clean working tree
|
||||
run: git diff --exit-code
|
||||
|
||||
- name: Build Linux and Darwin
|
||||
- name: Build ${{ matrix.os }}
|
||||
if: matrix.os != 'windows'
|
||||
run: GOOS=${{ matrix.os }} GOARCH=${{ matrix.arch }} go build -o sops-${{ matrix.os }}-${{ matrix.arch }}-${{ github.sha }} -v ./cmd/sops
|
||||
run: GOOS=${{ matrix.os }} GOARCH=${{ matrix.arch }} go build -o sops-${{ matrix.go-version }}-${{ matrix.os }}-${{ matrix.arch }}-${{ github.sha }} -v ./cmd/sops
|
||||
|
||||
- name: Build Windows
|
||||
- name: Build ${{ matrix.os }}
|
||||
if: matrix.os == 'windows'
|
||||
run: GOOS=${{ matrix.os }} go build -o sops-${{ matrix.os }}-${{ github.sha }} -v ./cmd/sops
|
||||
run: GOOS=${{ matrix.os }} go build -o sops-${{ matrix.go-version }}-${{ 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
|
||||
@@ -63,40 +69,51 @@ jobs:
|
||||
- name: Test
|
||||
run: make test
|
||||
|
||||
- name: Upload artifact for Linux and Darwin
|
||||
- name: Upload artifact for ${{ matrix.os }}
|
||||
if: matrix.os != 'windows'
|
||||
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: sops-${{ matrix.os }}-${{ matrix.arch }}-${{ github.sha }}
|
||||
path: sops-${{ matrix.os }}-${{ matrix.arch }}-${{ github.sha }}
|
||||
name: sops-${{ matrix.go-version }}-${{ matrix.os }}-${{ matrix.arch }}-${{ github.sha }}
|
||||
path: sops-${{ matrix.go-version }}-${{ matrix.os }}-${{ matrix.arch }}-${{ github.sha }}
|
||||
|
||||
- name: Upload artifact for Windows
|
||||
- name: Upload artifact for ${{ matrix.os }}
|
||||
if: matrix.os == 'windows'
|
||||
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: sops-${{ matrix.os }}-${{ github.sha }}
|
||||
path: sops-${{ matrix.os }}-${{ github.sha }}
|
||||
name: sops-${{ matrix.go-version }}-${{ matrix.os }}-${{ github.sha }}
|
||||
path: sops-${{ matrix.go-version }}-${{ matrix.os }}-${{ github.sha }}
|
||||
test:
|
||||
name: Functional tests
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: ['1.22']
|
||||
env:
|
||||
VAULT_VERSION: "1.1.3"
|
||||
VAULT_VERSION: "1.14.0"
|
||||
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.70.0
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
name: sops-linux-amd64-${{ github.sha }}
|
||||
persist-credentials: false
|
||||
|
||||
# Rustup will detect toolchain version and profile from rust-toolchain.toml
|
||||
# It will download and install the toolchain and components automatically
|
||||
# and make them available for subsequent commands
|
||||
- name: Install Rust toolchain
|
||||
run: rustup show
|
||||
|
||||
- name: Show Rust version
|
||||
run: cargo --version
|
||||
|
||||
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
|
||||
with:
|
||||
name: sops-${{ matrix.go-version }}-linux-amd64-${{ github.sha }}
|
||||
|
||||
- name: Move SOPS binary
|
||||
run: mv sops-linux-amd64-${{ github.sha }} ./functional-tests/sops
|
||||
run: mv sops-${{ matrix.go-version }}-linux-amd64-${{ github.sha }} ./functional-tests/sops
|
||||
|
||||
- name: Make SOPS binary executable
|
||||
run: chmod +x ./functional-tests/sops
|
||||
@@ -116,3 +133,22 @@ jobs:
|
||||
- name: Run tests
|
||||
run: cargo test
|
||||
working-directory: ./functional-tests
|
||||
|
||||
# The 'check' job should depend on all other jobs so it's possible to configure branch protection only for 'check'
|
||||
# instead of having to explicitly list all individual jobs that need to pass.
|
||||
check:
|
||||
if: always()
|
||||
|
||||
needs:
|
||||
- build
|
||||
- test
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Decide whether the needed jobs succeeded or failed
|
||||
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
|
||||
with:
|
||||
allowed-failures: docs, linters
|
||||
allowed-skips: non-voting-flaky-job
|
||||
jobs: ${{ toJSON(needs) }}
|
||||
|
||||
12
.github/workflows/codeql.yml
vendored
12
.github/workflows/codeql.yml
vendored
@@ -29,11 +29,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15
|
||||
uses: github/codeql-action/init@5f8171a638ada777af81d42b55959a643bb29017 # v3.28.12
|
||||
with:
|
||||
languages: go
|
||||
# xref: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
@@ -45,9 +47,11 @@ jobs:
|
||||
# target, which includes a lot more than just the Go files we want to
|
||||
# scan.
|
||||
- name: Build
|
||||
run: make install
|
||||
run: |
|
||||
make vendor
|
||||
make install
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15
|
||||
uses: github/codeql-action/analyze@5f8171a638ada777af81d42b55959a643bb29017 # v3.28.12
|
||||
with:
|
||||
category: "/language:go"
|
||||
|
||||
4
.github/workflows/docs.yml
vendored
4
.github/workflows/docs.yml
vendored
@@ -23,7 +23,9 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.0.0
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install rstcheck and markdownlint
|
||||
run: |
|
||||
|
||||
39
.github/workflows/linters.yml
vendored
Normal file
39
.github/workflows/linters.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Linters
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
# Only run when Rust version or linted files change
|
||||
paths:
|
||||
- 'rust-toolchain.toml'
|
||||
- 'functional-tests/**/*.rs'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint Rust source files
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Rustup will detect toolchain version and profile from rust-toolchain.toml
|
||||
# It will download and install the toolchain and components automatically
|
||||
# and make them available for subsequent commands
|
||||
- name: Install Rust toolchain and additional components
|
||||
run: rustup component add rustfmt
|
||||
|
||||
- name: Show Rust version
|
||||
run: cargo --version
|
||||
|
||||
- name: Run Formatting Check
|
||||
run: cargo fmt --check
|
||||
working-directory: ./functional-tests
|
||||
27
.github/workflows/release.yml
vendored
27
.github/workflows/release.yml
vendored
@@ -25,37 +25,38 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v4.0.1
|
||||
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v4.0.1
|
||||
with:
|
||||
go-version: 1.21.x
|
||||
go-version-file: go.mod
|
||||
cache: false
|
||||
|
||||
- name: Setup Syft
|
||||
uses: anchore/sbom-action/download-syft@d94f46e13c6c62f59525ac9a1e147a99dc0b9bf5 # v0.17.0
|
||||
uses: anchore/sbom-action/download-syft@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 # v0.18.0
|
||||
|
||||
- name: Setup Cosign
|
||||
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0
|
||||
uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1
|
||||
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0
|
||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27 # v3.5.0
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to Quay.io
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
registry: quay.io
|
||||
username: ${{ secrets.QUAY_BOT_USERNAME }}
|
||||
@@ -63,7 +64,7 @@ jobs:
|
||||
|
||||
- name: Run GoReleaser
|
||||
id: goreleaser
|
||||
uses: goreleaser/goreleaser-action@286f3b13b1b49da4ac219696163fb8c1c93e1200 # v6.0.0
|
||||
uses: goreleaser/goreleaser-action@90a3faa9d0182683851fbfa97ca1a2cb983bfca3 # v6.2.1
|
||||
with:
|
||||
# Note that the following is the version of goreleaser, and NOT a Go version!
|
||||
# When bumping it, make sure to check out goreleaser's changelog first!
|
||||
@@ -168,7 +169,7 @@ jobs:
|
||||
id-token: write # For creating OIDC tokens for signing.
|
||||
contents: write # For adding assets to a release.
|
||||
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
|
||||
with:
|
||||
base64-subjects: "${{ needs.combine-subjects.outputs.all-subjects }}"
|
||||
upload-assets: true
|
||||
@@ -185,7 +186,7 @@ jobs:
|
||||
strategy:
|
||||
matrix: ${{ fromJSON(needs.release.outputs.container-subjects) }}
|
||||
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
|
||||
with:
|
||||
image: ghcr.io/${{ matrix.image }}
|
||||
digest: ${{ matrix.digest }}
|
||||
@@ -204,7 +205,7 @@ jobs:
|
||||
strategy:
|
||||
matrix: ${{ fromJSON(needs.release.outputs.container-subjects) }}
|
||||
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
|
||||
with:
|
||||
image: quay.io/${{ matrix.image }}
|
||||
digest: ${{ matrix.digest }}
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,5 @@
|
||||
bin/
|
||||
dist/
|
||||
functional-tests/sops
|
||||
Cargo.lock
|
||||
vendor/
|
||||
profile.out
|
||||
|
||||
@@ -69,6 +69,7 @@ builds:
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
# Modified timestamp on the binary, set to ensure reproducible builds.
|
||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.18
|
||||
FROM alpine:3.21
|
||||
|
||||
RUN apk --no-cache add \
|
||||
ca-certificates \
|
||||
|
||||
620
CHANGELOG.md
Normal file
620
CHANGELOG.md
Normal file
@@ -0,0 +1,620 @@
|
||||
# Changelog
|
||||
|
||||
## 3.9.4
|
||||
|
||||
Improvements:
|
||||
|
||||
* Dependency updates ([#1727](https://github.com/getsops/sops/pull/1727), [#1732](https://github.com/getsops/sops/pull/1732),
|
||||
[#1734](https://github.com/getsops/sops/pull/1734), [#1739](https://github.com/getsops/sops/pull/1739)).
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* Prevent key deduplication to identify different AWS KMS keys that only differ by
|
||||
role, context, or profile ([#1733](https://github.com/getsops/sops/pull/1733)).
|
||||
* Update part of Azure SDK which prevented decryption in some cases ([#1695](https://github.com/getsops/sops/issue/1695),
|
||||
[#1734](https://github.com/getsops/sops/pull/1734)).
|
||||
|
||||
Project changes:
|
||||
|
||||
* CI dependency updates ([#1730](https://github.com/getsops/sops/pull/1730), [#1738](https://github.com/getsops/sops/pull/1738)).
|
||||
* Rust dependency updates ([#1728](https://github.com/getsops/sops/pull/1728), [#1731](https://github.com/getsops/sops/pull/1731),
|
||||
[#1735](https://github.com/getsops/sops/pull/1735)).
|
||||
|
||||
## 3.9.3
|
||||
|
||||
Improvements:
|
||||
|
||||
* Dependency updates ([#1699](https://github.com/getsops/sops/pull/1699), [#1703](https://github.com/getsops/sops/pull/1703),
|
||||
[#1710](https://github.com/getsops/sops/pull/1710), [#1714](https://github.com/getsops/sops/pull/1714),
|
||||
[#1715](https://github.com/getsops/sops/pull/1715), [#1723](https://github.com/getsops/sops/pull/1723)).
|
||||
* Add `persist-credentials: false` to checkouts in GitHub workflows ([#1704](https://github.com/getsops/sops/pull/1704)).
|
||||
* Tests: use container images from
|
||||
[https://github.com/getsops/ci-container-images](https://github.com/getsops/ci-container-images)
|
||||
([#1722](https://github.com/getsops/sops/pull/1722)).
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* GnuPG: do not incorrectly trim fingerprint in presence of exclamation
|
||||
marks for specfic subkey selection ([#1720](https://github.com/getsops/sops/pull/1720)).
|
||||
* `updatekeys` subcommand: fix `--input-type` CLI flag being ignored ([#1721](https://github.com/getsops/sops/pull/1721)).
|
||||
|
||||
Project changes:
|
||||
|
||||
* CI dependency updates ([#1698](https://github.com/getsops/sops/pull/1698), [#1708](https://github.com/getsops/sops/pull/1708),
|
||||
[#1717](https://github.com/getsops/sops/pull/1717)).
|
||||
* Rust dependency updates ([#1707](https://github.com/getsops/sops/pull/1707), [#1716](https://github.com/getsops/sops/pull/1716),
|
||||
[#1725](https://github.com/getsops/sops/pull/1725)).
|
||||
|
||||
## 3.9.2
|
||||
|
||||
Improvements:
|
||||
|
||||
* Dependency updates ([#1645](https://github.com/getsops/sops/pull/1645), [#1649](https://github.com/getsops/sops/pull/1649),
|
||||
[#1653](https://github.com/getsops/sops/pull/1653), [#1662](https://github.com/getsops/sops/pull/1662),
|
||||
[#1686](https://github.com/getsops/sops/pull/1686), [#1693](https://github.com/getsops/sops/pull/1693)).
|
||||
* Update compiled Protobuf definitions ([#1688](https://github.com/getsops/sops/pull/1688)).
|
||||
* Remove unused variables and simplify conditional (##1687).
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* Handle whitespace in Azure Key Vault URLs ([#1652](https://github.com/getsops/sops/pull/1652)).
|
||||
* Correctly handle comments during JSON serialization ([#1647](https://github.com/getsops/sops/pull/1647)).
|
||||
|
||||
Project changes:
|
||||
|
||||
* CI dependency updates ([#1644](https://github.com/getsops/sops/pull/1644), [#1648](https://github.com/getsops/sops/pull/1648),
|
||||
[#1654](https://github.com/getsops/sops/pull/1654), [#1664](https://github.com/getsops/sops/pull/1664),
|
||||
[#1673](https://github.com/getsops/sops/pull/1673), [#1677](https://github.com/getsops/sops/pull/1677),
|
||||
[#1685](https://github.com/getsops/sops/pull/1685)).
|
||||
* Rust dependency updates ([#1655](https://github.com/getsops/sops/pull/1655), [#1663](https://github.com/getsops/sops/pull/1663),
|
||||
[#1670](https://github.com/getsops/sops/pull/1670), [#1676](https://github.com/getsops/sops/pull/1676),
|
||||
[#1689](https://github.com/getsops/sops/pull/1689)).
|
||||
* Update and improve Protobuf code generation ([#1688](https://github.com/getsops/sops/pull/1688)).
|
||||
|
||||
## 3.9.1
|
||||
|
||||
Improvements:
|
||||
|
||||
* Dependency updates ([#1550](https://github.com/getsops/sops/pull/1550), [#1554](https://github.com/getsops/sops/pull/1554),
|
||||
[#1558](https://github.com/getsops/sops/pull/1558), [#1562](https://github.com/getsops/sops/pull/1562),
|
||||
[#1565](https://github.com/getsops/sops/pull/1565), [#1568](https://github.com/getsops/sops/pull/1568),
|
||||
[#1575](https://github.com/getsops/sops/pull/1575), [#1581](https://github.com/getsops/sops/pull/1581),
|
||||
[#1589](https://github.com/getsops/sops/pull/1589), [#1593](https://github.com/getsops/sops/pull/1593),
|
||||
[#1602](https://github.com/getsops/sops/pull/1602), [#1603](https://github.com/getsops/sops/pull/1603),
|
||||
[#1618](https://github.com/getsops/sops/pull/1618), [#1629](https://github.com/getsops/sops/pull/1629),
|
||||
[#1635](https://github.com/getsops/sops/pull/1635), [#1639](https://github.com/getsops/sops/pull/1639),
|
||||
[#1640](https://github.com/getsops/sops/pull/1640)).
|
||||
* Clarify naming of the configuration file in the documentation ([#1569](https://github.com/getsops/sops/pull/1569)).
|
||||
* Build with Go 1.22 ([#1589](https://github.com/getsops/sops/pull/1589)).
|
||||
* Specify filename of missing file in error messages ([#1625](https://github.com/getsops/sops/pull/1625)).
|
||||
* `updatekeys` subcommand: show changes in `shamir_threshold` ([#1609](https://github.com/getsops/sops/pull/1609)).
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* Fix the URL used for determining the latest SOPS version ([#1553](https://github.com/getsops/sops/pull/1553)).
|
||||
* `updatekeys` subcommand: actually use option
|
||||
`--shamir-secret-sharing-threshold` ([#1608](https://github.com/getsops/sops/pull/1608)).
|
||||
* Fix `--config` being ignored in subcommands by `loadConfig` ([#1613](https://github.com/getsops/sops/pull/1613)).
|
||||
* Allow `edit` subcommand to create files ([#1596](https://github.com/getsops/sops/pull/1596)).
|
||||
* Do not encrypt if a key group is empty, or there are no key groups ([#1600](https://github.com/getsops/sops/pull/1600)).
|
||||
* Do not ignore config errors when trying to parse a config file ([#1614](https://github.com/getsops/sops/pull/1614)).
|
||||
|
||||
Project changes:
|
||||
|
||||
* CI dependency updates ([#1551](https://github.com/getsops/sops/pull/1551), [#1555](https://github.com/getsops/sops/pull/1555),
|
||||
[#1559](https://github.com/getsops/sops/pull/1559), [#1564](https://github.com/getsops/sops/pull/1564),
|
||||
[#1566](https://github.com/getsops/sops/pull/1566), [#1574](https://github.com/getsops/sops/pull/1574),
|
||||
[#1584](https://github.com/getsops/sops/pull/1584), [#1586](https://github.com/getsops/sops/pull/1586),
|
||||
[#1590](https://github.com/getsops/sops/pull/1590), [#1592](https://github.com/getsops/sops/pull/1592),
|
||||
[#1619](https://github.com/getsops/sops/pull/1619), [#1628](https://github.com/getsops/sops/pull/1628),
|
||||
[#1634](https://github.com/getsops/sops/pull/1634)).
|
||||
* Improve CI workflows ([#1548](https://github.com/getsops/sops/pull/1548), [#1630](https://github.com/getsops/sops/pull/1630)).
|
||||
* Ignore user-set environment variable `SOPS_AGE_KEY_FILE` in tests ([#1595](https://github.com/getsops/sops/pull/1595)).
|
||||
* Add example of using Age recipients in `.sops.yaml` ([#1607](https://github.com/getsops/sops/pull/1607)).
|
||||
* Add linting check for Rust code formatting ([#1604](https://github.com/getsops/sops/pull/1604)).
|
||||
* Set Rust version globally via `rust-toolchain.toml` for functional tests ([#1612](https://github.com/getsops/sops/pull/1612)).
|
||||
* Improve test coverage ([#1617](https://github.com/getsops/sops/pull/1617)).
|
||||
* Improve tests ([#1622](https://github.com/getsops/sops/pull/1622), [#1624](https://github.com/getsops/sops/pull/1624)).
|
||||
* Simplify branch rules to check DCO and `check` task instead of an explicit
|
||||
list of tasks in the CLI workflow ([#1621](https://github.com/getsops/sops/pull/1621)).
|
||||
* Build with Go 1.22 and 1.23 in CI and update Vault to 1.14 ([#1531](https://github.com/getsops/sops/pull/1531)).
|
||||
* Build release with Go 1.22 ([#1615](https://github.com/getsops/sops/pull/1615)).
|
||||
* Fix Dependabot config for Docker; add Dependabot config for Rust ([#1632](https://github.com/getsops/sops/pull/1632)).
|
||||
* Lock Rust package versions for functional tests for improved
|
||||
reproducibility ([#1637](https://github.com/getsops/sops/pull/1637)).
|
||||
* Rust dependency updates ([#1638](https://github.com/getsops/sops/pull/1638)).
|
||||
|
||||
## 3.9.0
|
||||
|
||||
Features:
|
||||
|
||||
* Add `--mac-only-encrypted` to compute MAC only over values which
|
||||
end up encrypted ([#973](https://github.com/getsops/sops/pull/973))
|
||||
* Allow configuration of indentation for YAML and JSON stores ([#1273](https://github.com/getsops/sops/pull/1273),
|
||||
[#1372](https://github.com/getsops/sops/pull/1372))
|
||||
* Introduce a `--pristine` flag to `sops exec-env` ([#912](https://github.com/getsops/sops/pull/912))
|
||||
* Allow to pass multiple paths to `sops updatekeys` ([#1274](https://github.com/getsops/sops/pull/1274))
|
||||
* Allow to override `fileName` with different value ([#1332](https://github.com/getsops/sops/pull/1332))
|
||||
* Sort masterkeys according to `--decryption-order` ([#1345](https://github.com/getsops/sops/pull/1345))
|
||||
* Add separate subcommands for encryption, decryption, rotating, editing,
|
||||
and setting values ([#1391](https://github.com/getsops/sops/pull/1391))
|
||||
* Add `filestatus` command ([#545](https://github.com/getsops/sops/pull/545))
|
||||
* Add command `unset` ([#1475](https://github.com/getsops/sops/pull/1475))
|
||||
* Merge key for key groups and make keys unique ([#1493](https://github.com/getsops/sops/pull/1493))
|
||||
* Support using comments to select parts to encrypt ([#974](https://github.com/getsops/sops/pull/974),
|
||||
[#1392](https://github.com/getsops/sops/pull/1392))
|
||||
|
||||
Deprecations:
|
||||
|
||||
* Deprecate the `--background` option to `exec-env` and `exec-file` ([#1379](https://github.com/getsops/sops/pull/1379))
|
||||
|
||||
Improvements:
|
||||
|
||||
* Warn/fail if the wrong number of arguments is provided ([#1342](https://github.com/getsops/sops/pull/1342))
|
||||
* Warn if more than one command is used ([#1388](https://github.com/getsops/sops/pull/1388))
|
||||
* Dependency updates ([#1327](https://github.com/getsops/sops/pull/1327),
|
||||
[#1328](https://github.com/getsops/sops/pull/1328), [#1330](https://github.com/getsops/sops/pull/1330),
|
||||
[#1336](https://github.com/getsops/sops/pull/1336), [#1334](https://github.com/getsops/sops/pull/1334),
|
||||
[#1344](https://github.com/getsops/sops/pull/1344), [#1348](https://github.com/getsops/sops/pull/1348),
|
||||
[#1354](https://github.com/getsops/sops/pull/1354), [#1357](https://github.com/getsops/sops/pull/1357),
|
||||
[#1360](https://github.com/getsops/sops/pull/1360), [#1373](https://github.com/getsops/sops/pull/1373),
|
||||
[#1381](https://github.com/getsops/sops/pull/1381), [#1383](https://github.com/getsops/sops/pull/1383),
|
||||
[#1385](https://github.com/getsops/sops/pull/1385), [#1408](https://github.com/getsops/sops/pull/1408),
|
||||
[#1428](https://github.com/getsops/sops/pull/1428), [#1429](https://github.com/getsops/sops/pull/1429),
|
||||
[#1427](https://github.com/getsops/sops/pull/1427), [#1439](https://github.com/getsops/sops/pull/1439),
|
||||
[#1454](https://github.com/getsops/sops/pull/1454), [#1460](https://github.com/getsops/sops/pull/1460),
|
||||
[#1466](https://github.com/getsops/sops/pull/1466), [#1489](https://github.com/getsops/sops/pull/1489),
|
||||
[#1519](https://github.com/getsops/sops/pull/1519), [#1525](https://github.com/getsops/sops/pull/1525),
|
||||
[#1528](https://github.com/getsops/sops/pull/1528), [#1540](https://github.com/getsops/sops/pull/1540),
|
||||
[#1543](https://github.com/getsops/sops/pull/1543), [#1545](https://github.com/getsops/sops/pull/1545))
|
||||
* Build with Go 1.21 ([#1427](https://github.com/getsops/sops/pull/1427))
|
||||
* Improve README.rst ([#1339](https://github.com/getsops/sops/pull/1339),
|
||||
[#1399](https://github.com/getsops/sops/pull/1399), [#1350](https://github.com/getsops/sops/pull/1350))
|
||||
* Fix typos ([#1337](https://github.com/getsops/sops/pull/1337), [#1477](https://github.com/getsops/sops/pull/1477),
|
||||
[#1484](https://github.com/getsops/sops/pull/1484))
|
||||
* Polish the `sops help` output a bit ([#1341](https://github.com/getsops/sops/pull/1341),
|
||||
[#1544](https://github.com/getsops/sops/pull/1544))
|
||||
* Improve and fix tests ([#1346](https://github.com/getsops/sops/pull/1346),
|
||||
[#1349](https://github.com/getsops/sops/pull/1349), [#1370](https://github.com/getsops/sops/pull/1370),
|
||||
[#1390](https://github.com/getsops/sops/pull/1390), [#1396](https://github.com/getsops/sops/pull/1396),
|
||||
[#1492](https://github.com/getsops/sops/pull/1492))
|
||||
* Create a constant for the `sops` metadata key ([#1398](https://github.com/getsops/sops/pull/1398))
|
||||
* Refactoring: move extraction of encryption and rotation options to
|
||||
separate functions ([#1389](https://github.com/getsops/sops/pull/1389))
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Respect `aws_profile` from keygroup config ([#1049](https://github.com/getsops/sops/pull/1049))
|
||||
* Fix a bug where not having a config results in a panic ([#1371](https://github.com/getsops/sops/pull/1371))
|
||||
* Consolidate Flatten/Unflatten pre/post processing ([#1356](https://github.com/getsops/sops/pull/1356))
|
||||
* INI and DotEnv stores: `shamir_threshold` is an integer ([#1394](https://github.com/getsops/sops/pull/1394))
|
||||
* Make check whether file contains invalid keys for encryption dependent
|
||||
on output store ([#1393](https://github.com/getsops/sops/pull/1393))
|
||||
* Do not panic if `updatekeys` is used with a config that has no creation
|
||||
rules defined ([#1506](https://github.com/getsops/sops/pull/1506))
|
||||
* `exec-file`: if `--filename` is used, use the provided filename without
|
||||
random suffix ([#1474](https://github.com/getsops/sops/pull/1474))
|
||||
* Do not use DotEnv store for `exec-env`, but specialized environment
|
||||
serializing code ([#1436](https://github.com/getsops/sops/pull/1436))
|
||||
* Decryption: do not fail if no matching `creation_rule` is present in
|
||||
config file ([#1434](https://github.com/getsops/sops/pull/1434))
|
||||
|
||||
Project changes:
|
||||
|
||||
* CI dependency updates ([#1347](https://github.com/getsops/sops/pull/1347),
|
||||
[#1359](https://github.com/getsops/sops/pull/1359), [#1376](https://github.com/getsops/sops/pull/1376),
|
||||
[#1382](https://github.com/getsops/sops/pull/1382), [#1386](https://github.com/getsops/sops/pull/1386),
|
||||
[#1425](https://github.com/getsops/sops/pull/1425), [#1432](https://github.com/getsops/sops/pull/1432),
|
||||
[#1498](https://github.com/getsops/sops/pull/1498), [#1503](https://github.com/getsops/sops/pull/1503),
|
||||
[#1508](https://github.com/getsops/sops/pull/1508), [#1510](https://github.com/getsops/sops/pull/1510),
|
||||
[#1516](https://github.com/getsops/sops/pull/1516), [#1521](https://github.com/getsops/sops/pull/1521),
|
||||
[#1492](https://github.com/getsops/sops/pull/1492), [#1534](https://github.com/getsops/sops/pull/1534))
|
||||
* Adjust Makefile to new goreleaser 6.0.0 release ([#1526](https://github.com/getsops/sops/pull/1526))
|
||||
|
||||
## 3.8.1
|
||||
|
||||
Improvements:
|
||||
|
||||
* Improve handling of errors when binary store handles bad data ([#1289](https://github.com/getsops/sops/pull/1289))
|
||||
* On macOS, prefer `XDG_CONFIG_HOME` over os.UserConfigDir() ([#1291](https://github.com/getsops/sops/pull/1291))
|
||||
* Dependency updates ([#1306](https://github.com/getsops/sops/pull/1306),
|
||||
[#1319](https://github.com/getsops/sops/pull/1319), [#1325](https://github.com/getsops/sops/pull/1325))
|
||||
* pgp: better error reporting for missing GPG binary during import of keys ([#1286](https://github.com/getsops/sops/pull/1286))
|
||||
* Fix descriptions of `unencrypted-regex` and `encrypted-regex` flags, and
|
||||
ensure `unencrypted_regex` is considered in config validation ([#1300](https://github.com/getsops/sops/pull/1300))
|
||||
* stores/json: improve error messages when parsing invalid JSON ([#1307](https://github.com/getsops/sops/pull/1307))
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* pgp: improve handling of GnuPG home dir ([#1298](https://github.com/getsops/sops/pull/1298))
|
||||
* Do not crash if an empty YAML file is encrypted ([#1290](https://github.com/getsops/sops/pull/1290))
|
||||
* Handling of various ignored errors ([#1304](https://github.com/getsops/sops/pull/1304),
|
||||
[#1311](https://github.com/getsops/sops/pull/1311))
|
||||
* pgp: do not require abs path for `SOPS_GPG_EXEC` ([#1309](https://github.com/getsops/sops/pull/1309))
|
||||
* Report key rotation errors ([#1317](https://github.com/getsops/sops/pull/1317))
|
||||
* Ensure wrapping of errors in main package ([#1318](https://github.com/getsops/sops/pull/1318))
|
||||
|
||||
Project changes:
|
||||
|
||||
* Enrich AWS authentication documentation ([#1272](https://github.com/getsops/sops/pull/1272))
|
||||
* Add linting for RST and MD files ([#1287](https://github.com/getsops/sops/pull/1287))
|
||||
* Delete SOPS encrypted file we don't have keys for ([#1288](https://github.com/getsops/sops/pull/1288))
|
||||
* CI dependency updates ([#1295](https://github.com/getsops/sops/pull/1295), [#1301](https://github.com/getsops/sops/pull/1301))
|
||||
* pgp: make error the last return value ([#1310](https://github.com/getsops/sops/pull/1310))
|
||||
* Improve documentation files ([#1320](https://github.com/getsops/sops/pull/1320))
|
||||
|
||||
## 3.8.0
|
||||
|
||||
Features:
|
||||
|
||||
* Support `--version` without network requests using `--disable-version-check` ([#1115](https://github.com/getsops/sops/pull/1115))
|
||||
* Support `--input-type` for updatekeys command ([#1116](https://github.com/getsops/sops/pull/1116))
|
||||
|
||||
Improvements:
|
||||
|
||||
* pgp: modernize and improve, and add tests ([#1054](https://github.com/getsops/sops/pull/1054),
|
||||
[#1282](https://github.com/getsops/sops/pull/1282))
|
||||
* azkv: update SDK to latest, add tests, tidy ([#1067](https://github.com/getsops/sops/pull/1067),
|
||||
[#1092](https://github.com/getsops/sops/pull/1092), [#1256](https://github.com/getsops/sops/pull/1256))
|
||||
* age: improve identity loading, add tests, tidy ([#1064](https://github.com/getsops/sops/pull/1064))
|
||||
* kms: AWS SDK V2, allow creds config, add tests ([#1065](https://github.com/getsops/sops/pull/1065),
|
||||
[#1257](https://github.com/getsops/sops/pull/1257))
|
||||
* gcpkms: update SDK to latest, add tests, tidy ([#1072](https://github.com/getsops/sops/pull/1072),
|
||||
[#1255](https://github.com/getsops/sops/pull/1255))
|
||||
* hcvault: update API, add tests, tidy ([#1085](https://github.com/getsops/sops/pull/1085))
|
||||
* Do not report version when upstream `--version` check fails ([#1124](https://github.com/getsops/sops/pull/1124))
|
||||
* Use GitHub endpoints in `--version` command ([#1261](https://github.com/getsops/sops/pull/1261))
|
||||
* Close temporary file before invoking editor to widen support on Windows ([#1265](https://github.com/getsops/sops/pull/1265))
|
||||
* Update dependencies ([#1063](https://github.com/getsops/sops/pull/1063),
|
||||
[#1091](https://github.com/getsops/sops/pull/1091), [#1147](https://github.com/getsops/sops/pull/1147),
|
||||
[#1242](https://github.com/getsops/sops/pull/1242), [#1260](https://github.com/getsops/sops/pull/1260),
|
||||
[#1264](https://github.com/getsops/sops/pull/1264), [#1275](https://github.com/getsops/sops/pull/1275),
|
||||
[#1280](https://github.com/getsops/sops/pull/1280), [#1283](https://github.com/getsops/sops/pull/1283))
|
||||
* Deal with various deprecations of dependencies ([#1113](https://github.com/getsops/sops/pull/1113),
|
||||
[#1262](https://github.com/getsops/sops/pull/1262))
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Ensure YAML comments are not displaced ([#1069](https://github.com/getsops/sops/pull/1069))
|
||||
* Ensure default Google credentials can be used again after introduction
|
||||
of `GOOGLE_CREDENTIALS` ([#1249](https://github.com/getsops/sops/pull/1249))
|
||||
* Avoid duplicate logging of errors in some key sources ([#1146](https://github.com/getsops/sops/pull/1146),
|
||||
[#1281](https://github.com/getsops/sops/pull/1281))
|
||||
* Using `--set` on a root level key does no longer truncate existing values ([#899](https://github.com/getsops/sops/pull/899))
|
||||
* Ensure stable order of SOPS parameters in dotenv file ([#1101](https://github.com/getsops/sops/pull/1101))
|
||||
|
||||
Project changes:
|
||||
|
||||
* Update Go to 1.20 ([#1148](https://github.com/getsops/sops/pull/1148))
|
||||
* Update rustc functional tests to v1.70.0 ([#1234](https://github.com/getsops/sops/pull/1234))
|
||||
* Remove remaining CircleCI workflow ([#1237](https://github.com/getsops/sops/pull/1237))
|
||||
* Run CLI workflow on main ([#1243](https://github.com/getsops/sops/pull/1243))
|
||||
* Delete obsolete `validation/` artifact ([#1248](https://github.com/getsops/sops/pull/1248))
|
||||
* Rename Go module to `github.com/getsops/sops/v3` ([#1247](https://github.com/getsops/sops/pull/1247))
|
||||
* Revamp release automation, including (Cosign) signed container images
|
||||
and checksums file, SLSA3 provenance and SBOMs ([#1250](https://github.com/getsops/sops/pull/1250))
|
||||
* Update various bits of documentation ([#1244](https://github.com/getsops/sops/pull/1244))
|
||||
* Add missing `--encrypt` flag from Vault example ([#1060](https://github.com/getsops/sops/pull/1060))
|
||||
* Add documentation on how to use age in `.sops.yaml` ([#1192](https://github.com/getsops/sops/pull/1192))
|
||||
* Improve Make targets and address various issues ([#1258](https://github.com/getsops/sops/pull/1258))
|
||||
* Ensure clean working tree in CI ([#1267](https://github.com/getsops/sops/pull/1267))
|
||||
* Fix CHANGELOG.rst formatting ([#1269](https://github.com/getsops/sops/pull/1269))
|
||||
* Pin GitHub Actions to full length commit SHA and add CodeQL ([#1276](https://github.com/getsops/sops/pull/1276))
|
||||
* Enable Dependabot for Docker, GitHub Actions and Go Mod ([#1277](https://github.com/getsops/sops/pull/1277))
|
||||
* Generate versioned `.intoto.jsonl` ([#1278](https://github.com/getsops/sops/pull/1278))
|
||||
* Update CI dependencies ([#1279](https://github.com/getsops/sops/pull/1279))
|
||||
|
||||
## 3.7.3
|
||||
|
||||
Changes:
|
||||
|
||||
* Upgrade dependencies ([#1024](https://github.com/getsops/sops/pull/1024), [#1045](https://github.com/getsops/sops/pull/1045))
|
||||
* Build alpine container in CI ([#1018](https://github.com/getsops/sops/pull/1018),
|
||||
[#1032](https://github.com/getsops/sops/pull/1032), [#1025](https://github.com/getsops/sops/pull/1025))
|
||||
* keyservice: accept KeyServiceServer in LocalClient ([#1035](https://github.com/getsops/sops/pull/1035))
|
||||
* Add support for GCP Service Account within `GOOGLE_CREDENTIALS` ([#953](https://github.com/getsops/sops/pull/953))
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Upload the correct binary for the linux amd64 build ([#1026](https://github.com/getsops/sops/pull/1026))
|
||||
* Fix bug when specifying multiple age recipients ([#966](https://github.com/getsops/sops/pull/966))
|
||||
* Allow for empty yaml maps ([#908](https://github.com/getsops/sops/pull/908))
|
||||
* Limit AWS role names to 64 characters ([#1037](https://github.com/getsops/sops/pull/1037))
|
||||
|
||||
## 3.7.2
|
||||
|
||||
Changes:
|
||||
|
||||
* README updates ([#861](https://github.com/getsops/sops/pull/861), [#860](https://github.com/getsops/sops/pull/860))
|
||||
* Various test fixes ([#909](https://github.com/getsops/sops/pull/909),
|
||||
[#906](https://github.com/getsops/sops/pull/906), [#1008](https://github.com/getsops/sops/pull/1008))
|
||||
* Added Linux and Darwin arm64 releases ([#911](https://github.com/getsops/sops/pull/911),
|
||||
[#891](https://github.com/getsops/sops/pull/891))
|
||||
* Upgrade to go v1.17 ([#1012](https://github.com/getsops/sops/pull/1012))
|
||||
* Support SOPS_AGE_KEY environment variable ([#1006](https://github.com/getsops/sops/pull/1006))
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Make sure comments in yaml files are not duplicated ([#866](https://github.com/getsops/sops/pull/866))
|
||||
* Make sure configuration file paths work correctly relative to the
|
||||
config file in us ([#853](https://github.com/getsops/sops/pull/853))
|
||||
|
||||
## 3.7.1
|
||||
|
||||
Changes:
|
||||
|
||||
* Security fix
|
||||
* Add release workflow ([#843](https://github.com/getsops/sops/pull/843))
|
||||
* Fix issue where CI wouldn't run against master ([#848](https://github.com/getsops/sops/pull/848))
|
||||
* Trim extra whitespace around age keys ([#846](https://github.com/getsops/sops/pull/846))
|
||||
|
||||
## 3.7.0
|
||||
|
||||
Features:
|
||||
|
||||
* Add support for age ([#688](https://github.com/getsops/sops/pull/688))
|
||||
* Add filename to exec-file ([#761](https://github.com/getsops/sops/pull/761))
|
||||
|
||||
Changes:
|
||||
|
||||
* On failed decryption with GPG, return the error returned by GPG to the
|
||||
sops user ([#762](https://github.com/getsops/sops/pull/762))
|
||||
* Use yaml.v3 instead of modified yaml.v2 for handling YAML files ([#791](https://github.com/getsops/sops/pull/791))
|
||||
* Update aws-sdk-go to version v1.37.18 ([#823](https://github.com/getsops/sops/pull/823))
|
||||
|
||||
Project Changes:
|
||||
|
||||
* Switch from TravisCI to Github Actions ([#792](https://github.com/getsops/sops/pull/792))
|
||||
|
||||
## 3.6.1
|
||||
|
||||
Features:
|
||||
|
||||
* Add support for --unencrypted-regex ([#715](https://github.com/getsops/sops/pull/715))
|
||||
|
||||
Changes:
|
||||
|
||||
* Use keys.openpgp.org instead of gpg.mozilla.org ([#732](https://github.com/getsops/sops/pull/732))
|
||||
* Upgrade AWS SDK version ([#714](https://github.com/getsops/sops/pull/714))
|
||||
* Support --input-type for exec-file ([#699](https://github.com/getsops/sops/pull/699))
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Fixes broken Vault tests ([#731](https://github.com/getsops/sops/pull/731))
|
||||
* Revert "Add standard newline/quoting behavior to dotenv store" ([#706](https://github.com/getsops/sops/pull/706))
|
||||
|
||||
## 3.6.0
|
||||
|
||||
Features:
|
||||
|
||||
* Support for encrypting data through the use of Hashicorp Vault ([#655](https://github.com/getsops/sops/pull/655))
|
||||
* `sops publish` now supports `--recursive` flag for publishing all files
|
||||
in a directory ([#602](https://github.com/getsops/sops/pull/602))
|
||||
* `sops publish` now supports `--omit-extensions` flag for omitting the
|
||||
extension in the destination path ([#602](https://github.com/getsops/sops/pull/602))
|
||||
* sops now supports JSON arrays of arrays ([#642](https://github.com/getsops/sops/pull/642))
|
||||
|
||||
Improvements:
|
||||
|
||||
* Updates and standardization for the dotenv store ([#612](https://github.com/getsops/sops/pull/612),
|
||||
[#622](https://github.com/getsops/sops/pull/622))
|
||||
* Close temp files after using them for edit command ([#685](https://github.com/getsops/sops/pull/685))
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* AWS SDK usage now correctly resolves the `~/.aws/config` file ([#680](https://github.com/getsops/sops/pull/680))
|
||||
* `sops updatekeys` now correctly matches config rules ([#682](https://github.com/getsops/sops/pull/682))
|
||||
* `sops updatekeys` now correctly uses the config path cli flag ([#672](https://github.com/getsops/sops/pull/672))
|
||||
* Partially empty sops config files don't break the use of sops anymore ([#662](https://github.com/getsops/sops/pull/662))
|
||||
* Fix possible infinite loop in PGP's passphrase prompt call ([#690](https://github.com/getsops/sops/pull/690))
|
||||
|
||||
Project changes:
|
||||
|
||||
* Dockerfile now based off of golang version 1.14 ([#649](https://github.com/getsops/sops/pull/649))
|
||||
* Push alpine version of docker image to Dockerhub ([#609](https://github.com/getsops/sops/pull/609))
|
||||
* Push major, major.minor, and major.minor.patch tagged docker images to
|
||||
Dockerhub ([#607](https://github.com/getsops/sops/pull/607))
|
||||
* Removed out of date contact information ([#668](https://github.com/getsops/sops/pull/668))
|
||||
* Update authors in the cli help text ([#645](https://github.com/getsops/sops/pull/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](https://github.com/getsops/sops/pull/463))
|
||||
* Allow for `--set` value to be a string. ([#461](https://github.com/getsops/sops/pull/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](https://github.com/getsops/sops/pull/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
|
||||
447
CHANGELOG.rst
447
CHANGELOG.rst
@@ -1,449 +1,4 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
3.9.0
|
||||
-----
|
||||
Features:
|
||||
|
||||
* Add ``--mac-only-encrypted`` to compute MAC only over values which end up encrypted (#973)
|
||||
* Allow configuration of indentation for YAML and JSON stores (#1273, #1372)
|
||||
* Introduce a ``--pristine`` flag to ``sops exec-env`` (#912)
|
||||
* Allow to pass multiple paths to ``sops updatekeys`` (#1274)
|
||||
* Allow to override ``fileName`` with different value (#1332)
|
||||
* Sort masterkeys according to ``--decryption-order`` (#1345)
|
||||
* Add separate subcommands for encryption, decryption, rotating, editing, and setting values (#1391)
|
||||
* Add ``filestatus`` command (#545)
|
||||
* Add command ``unset`` (#1475)
|
||||
* Merge key for key groups and make keys unique (#1493)
|
||||
* Support using comments to select parts to encrypt (#974, #1392)
|
||||
|
||||
Deprecations:
|
||||
|
||||
* Deprecate the ``--background`` option to ``exec-env`` and ``exec-file`` (#1379)
|
||||
|
||||
Improvements:
|
||||
|
||||
* Warn/fail if the wrong number of arguments is provided (#1342)
|
||||
* Warn if more than one command is used (#1388)
|
||||
* Dependency updates (#1327, #1328, #1330, #1336, #1334, #1344, #1348, #1354, #1357, #1360, #1373, #1381, #1383, #1385, #1408, #1428, #1429, #1427, #1439, #1454, #1460, #1466, #1489, #1519, #1525, #1528, #1540, #1543, #1545)
|
||||
* Build with Go 1.21 (#1427)
|
||||
* Improve README.rst (#1339, #1399, #1350)
|
||||
* Fix typos (#1337, #1477, #1484)
|
||||
* Polish the ``sops help`` output a bit (#1341, #1544)
|
||||
* Improve and fix tests (#1346, #1349, #1370, #1390, #1396, #1492)
|
||||
* Create a constant for the ``sops`` metadata key (#1398)
|
||||
* Refactoring: move extraction of encryption and rotation options to separate functions (#1389)
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Respect ``aws_profile`` from keygroup config (#1049)
|
||||
* Fix a bug where not having a config results in a panic (#1371)
|
||||
* Consolidate Flatten/Unflatten pre/post processing (#1356)
|
||||
* INI and DotEnv stores: ``shamir_threshold`` is an integer (#1394)
|
||||
* Make check whether file contains invalid keys for encryption dependent on output store (#1393)
|
||||
* Do not panic if ``updatekeys`` is used with a config that has no creation rules defined (#1506)
|
||||
* ``exec-file``: if ``--filename`` is used, use the provided filename without random suffix (#1474)
|
||||
* Do not use DotEnv store for ``exec-env``, but specialized environment serializing code (#1436)
|
||||
* Decryption: do not fail if no matching ``creation_rule`` is present in config file (#1434)
|
||||
|
||||
Project changes:
|
||||
|
||||
* CI dependency updates (#1347, #1359, #1376, #1382, #1386, #1425, #1432, #1498, #1503, #1508, #1510, #1516, #1521, #1492, #1534)
|
||||
* Adjust Makefile to new goreleaser 6.0.0 release (#1526)
|
||||
|
||||
3.8.1
|
||||
-----
|
||||
Improvements:
|
||||
|
||||
* Improve handling of errors when binary store handles bad data (#1289)
|
||||
* On macOS, prefer ``XDG_CONFIG_HOME`` over os.UserConfigDir() (#1291)
|
||||
* Dependency updates (#1306, #1319, #1325)
|
||||
* pgp: better error reporting for missing GPG binary during import of keys (#1286)
|
||||
* Fix descriptions of unencrypted-regex and encrypted-regex flags, and ensure unencrypted_regex is considered in config validation (#1300)
|
||||
* stores/json: improve error messages when parsing invalid JSON (#1307)
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* pgp: improve handling of GnuPG home dir (#1298)
|
||||
* Do not crash if an empty YAML file is encrypted (#1290)
|
||||
* Handling of various ignored errors (#1304, #1311)
|
||||
* pgp: do not require abs path for ``SOPS_GPG_EXEC`` (#1309)
|
||||
* Report key rotation errors (#1317)
|
||||
* Ensure wrapping of errors in main package (#1318)
|
||||
|
||||
Project changes:
|
||||
|
||||
* Enrich AWS authentication documentation (#1272)
|
||||
* Add linting for RST and MD files (#1287)
|
||||
* Delete SOPS encrypted file we don't have keys for (#1288)
|
||||
* CI dependency updates (#1295, #1301)
|
||||
* pgp: make error the last return value (#1310)
|
||||
* Improve documentation files (#1320)
|
||||
|
||||
3.8.0
|
||||
-----
|
||||
Features:
|
||||
|
||||
* Support ``--version`` without network requests using ``--disable-version-check`` (#1115)
|
||||
* Support ``--input-type`` for updatekeys command (#1116)
|
||||
|
||||
Improvements:
|
||||
|
||||
* pgp: modernize and improve, and add tests (#1054, #1282)
|
||||
* azkv: update SDK to latest, add tests, tidy (#1067, #1092, #1256)
|
||||
* age: improve identity loading, add tests, tidy (#1064)
|
||||
* kms: AWS SDK V2, allow creds config, add tests (#1065, #1257)
|
||||
* gcpkms: update SDK to latest, add tests, tidy (#1072, #1255)
|
||||
* hcvault: update API, add tests, tidy (#1085)
|
||||
* Do not report version when upstream ``--version`` check fails (#1124)
|
||||
* Use GitHub endpoints in ``--version`` command (#1261)
|
||||
* Close temporary file before invoking editor to widen support on Windows (#1265)
|
||||
* Update dependencies (#1063, #1091, #1147, #1242, #1260, #1264, #1275, #1280, #1283)
|
||||
* Deal with various deprecations of dependencies (#1113, #1262)
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Ensure YAML comments are not displaced (#1069)
|
||||
* Ensure default Google credentials can be used again after introduction of ``GOOGLE_CREDENTIALS`` (#1249)
|
||||
* Avoid duplicate logging of errors in some key sources (#1146, #1281)
|
||||
* Using ``--set`` on a root level key does no longer truncate existing values (#899)
|
||||
* Ensure stable order of SOPS parameters in dotenv file (#1101)
|
||||
|
||||
Project changes:
|
||||
|
||||
* Update Go to 1.20 (#1148)
|
||||
* Update rustc functional tests to v1.70.0 (#1234)
|
||||
* Remove remaining CircleCI workflow (#1237)
|
||||
* Run CLI workflow on main (#1243)
|
||||
* Delete obsolete ``validation/`` artifact (#1248)
|
||||
* Rename Go module to ``github.com/getsops/sops/v3`` (#1247)
|
||||
* Revamp release automation, including (Cosign) signed container images and checksums file, SLSA3 provenance and SBOMs (#1250)
|
||||
* Update various bits of documentation (#1244)
|
||||
* Add missing ``--encrypt`` flag from Vault example (#1060)
|
||||
* Add documentation on how to use age in ``.sops.yaml`` (#1192)
|
||||
* Improve Make targets and address various issues (#1258)
|
||||
* Ensure clean working tree in CI (#1267)
|
||||
* Fix CHANGELOG.rst formatting (#1269)
|
||||
* Pin GitHub Actions to full length commit SHA and add CodeQL (#1276)
|
||||
* Enable Dependabot for Docker, GitHub Actions and Go Mod (#1277)
|
||||
* Generate versioned ``.intoto.jsonl`` (#1278)
|
||||
* Update CI dependencies (#1279)
|
||||
|
||||
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
|
||||
The changelog can be found in `CHANGELOG.md <CHANGELOG.md>`_.
|
||||
|
||||
20
Makefile
20
Makefile
@@ -20,6 +20,12 @@ SYFT_VERSION ?= v0.87.0
|
||||
GORELEASER := $(BIN_DIR)/goreleaser
|
||||
GORELEASER_VERSION ?= v1.20.0
|
||||
|
||||
PROTOC_GO := $(BIN_DIR)/protoc-gen-go
|
||||
PROTOC_GO_VERSION ?= v1.35.2
|
||||
|
||||
PROTOC_GO_GRPC := $(BIN_DIR)/protoc-gen-go-grpc
|
||||
PROTOC_GO_GRPC_VERSION ?= v1.5.1
|
||||
|
||||
RSTCHECK := $(shell command -v rstcheck)
|
||||
MARKDOWNLINT := $(shell command -v mdl)
|
||||
|
||||
@@ -67,18 +73,18 @@ checkmd: $(MD_FILES)
|
||||
.PHONY: test
|
||||
test: vendor
|
||||
gpg --import pgp/sops_functional_tests_key.asc 2>&1 1>/dev/null || exit 0
|
||||
LANG=en_US.UTF-8 $(GO) test $(GO_TEST_FLAGS) ./...
|
||||
unset SOPS_AGE_KEY_FILE; unset SOPS_AGE_KEY_CMD; LANG=en_US.UTF-8 $(GO) test $(GO_TEST_FLAGS) ./...
|
||||
|
||||
.PHONY: showcoverage
|
||||
showcoverage: test
|
||||
$(GO) tool cover -html=profile.out
|
||||
|
||||
.PHONY: generate
|
||||
generate: keyservice/keyservice.pb.go
|
||||
generate: install-protoc-go install-protoc-go-grpc keyservice/keyservice.pb.go
|
||||
$(GO) generate
|
||||
|
||||
%.pb.go: %.proto
|
||||
protoc --go_out=plugins=grpc:. $<
|
||||
protoc --plugin gen-go=$(PROTOC_GO) --plugin gen-go-grpc=$(PLUGIN_GO_GRPC) --go-grpc_opt=require_unimplemented_servers=false --go-grpc_out=. --go_out=. $<
|
||||
|
||||
.PHONY: functional-tests
|
||||
functional-tests:
|
||||
@@ -112,6 +118,14 @@ install-goreleaser:
|
||||
install-syft:
|
||||
$(call go-install-tool,$(SYFT),github.com/anchore/syft/cmd/syft@$(SYFT_VERSION),$(SYFT_VERSION))
|
||||
|
||||
.PHONY: install-protoc-go
|
||||
install-protoc-go:
|
||||
$(call go-install-tool,$(PROTOC_GO),google.golang.org/protobuf/cmd/protoc-gen-go@$(PROTOC_GO_VERSION),$(PROTOC_GO_VERSION))
|
||||
|
||||
.PHONY: install-protoc-go-grpc
|
||||
install-protoc-go-grpc:
|
||||
$(call go-install-tool,$(PROTOC_GO_GRPC),google.golang.org/grpc/cmd/protoc-gen-go-grpc@$(PROTOC_GO_GRPC_VERSION),$(PROTOC_GO_GRPC_VERSION))
|
||||
|
||||
# go-install-tool will 'go install' any package $2 and install it to $1.
|
||||
define go-install-tool
|
||||
@[ -f $(1)-$(3) ] || { \
|
||||
|
||||
98
README.rst
98
README.rst
@@ -106,7 +106,8 @@ encryption/decryption transparently and open the cleartext file in an editor
|
||||
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.
|
||||
Editing will happen in whatever ``$SOPS_EDITOR`` or ``$EDITOR`` is set to, or, if it's
|
||||
not set, in vim, nano, or vi.
|
||||
Keep in mind that SOPS will wait for the editor to exit, and then try to reencrypt
|
||||
the file. Some GUI editors (atom, sublime) spawn a child process and then exit
|
||||
immediately. They usually have an option to wait for the main editor window to be
|
||||
@@ -188,6 +189,22 @@ the example files and pgp key provided with the repository::
|
||||
|
||||
This last step will decrypt ``example.yaml`` using the test private key.
|
||||
|
||||
Encrypting with GnuPG subkeys
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you want to encrypt with specific GnuPG subkeys, it does not suffice to provide the
|
||||
exact key ID of the subkey to SOPS, since GnuPG might use *another* subkey instead
|
||||
to encrypt the file key with. To force GnuPG to use a specific subkey, you need to
|
||||
append ``!`` to the key's fingerprint.
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
creation_rules:
|
||||
- pgp: >-
|
||||
85D77543B3D624B63CEA9E6DBC17301B491B3F21!,
|
||||
E60892BB9BD89A69F759A1A0A3D652173B763E8F!
|
||||
|
||||
Please note that this is only passed on correctly to GnuPG since SOPS 3.9.3.
|
||||
|
||||
Encrypting using age
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -210,15 +227,43 @@ On macOS, this would be ``$HOME/Library/Application Support/sops/age/keys.txt``.
|
||||
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 key(s) directly by setting the **SOPS_AGE_KEY**
|
||||
environment variable.
|
||||
environment variable. Alternatively, you can provide a command to output the age keys
|
||||
by setting the **SOPS_AGE_KEY_CMD** 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 with SSH keys via age is also supported by SOPS. You can use SSH public keys
|
||||
("ssh-ed25519 AAAA...", "ssh-rsa AAAA...") as age recipients when encrypting a file.
|
||||
When decrypting a file, SOPS will look for ``~/.ssh/id_ed25519`` and falls back to
|
||||
``~/.ssh/id_rsa``. You can specify the location of the private key manually by setting
|
||||
the environment variableuse **SOPS_AGE_SSH_PRIVATE_KEY_FILE**.
|
||||
|
||||
Note that only ``ssh-rsa`` and ``ssh-ed25519`` are supported.
|
||||
|
||||
A list of age recipients can be added to the ``.sops.yaml``:
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
creation_rules:
|
||||
- age: >-
|
||||
age1s3cqcks5genc6ru8chl0hkkd04zmxvczsvdxq99ekffe4gmvjpzsedk23c,
|
||||
age1qe5lxzzeppw5k79vxn3872272sgy224g2nzqlzy3uljs84say3yqgvd0sw
|
||||
|
||||
It is also possible to use ``updatekeys``, when adding or removing age recipients. For example:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ sops updatekeys secret.enc.yaml
|
||||
2022/02/09 16:32:02 Syncing keys for file /iac/solution1/secret.enc.yaml
|
||||
The following changes will be made to the file's groups:
|
||||
Group 1
|
||||
age1s3cqcks5genc6ru8chl0hkkd04zmxvczsvdxq99ekffe4gmvjpzsedk23c
|
||||
+++ age1qe5lxzzeppw5k79vxn3872272sgy224g2nzqlzy3uljs84say3yqgvd0sw
|
||||
Is this okay? (y/n):y
|
||||
2022/02/09 16:32:04 File /iac/solution1/secret.enc.yaml synced with new keys
|
||||
|
||||
Encrypting using GCP KMS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
GCP KMS uses `Application Default Credentials
|
||||
@@ -338,33 +383,38 @@ Encrypting and decrypting from other programs
|
||||
When using ``sops`` in scripts or from other programs, there are often situations where you do not want to write
|
||||
encrypted or decrypted data to disk. The best way to avoid this is to pass data to SOPS via stdin, and to let
|
||||
SOPS write data to stdout. By default, the encrypt and decrypt operations write data to stdout already. To pass
|
||||
data via stdin, you need to pass ``/dev/stdin`` as the input filename. Please note that this only works on
|
||||
Unix-like operating systems such as macOS and Linux. On Windows, you have to use named pipes.
|
||||
data via stdin, you need to not provide an input filename. For encryption, you also must provide the
|
||||
``--filename-override`` option with the file's filename. The filename will be used to determine the input and output
|
||||
types, and to select the correct creation rule.
|
||||
|
||||
To decrypt data, you can simply do:
|
||||
The simplest way to decrypt data from stdin is as follows:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ cat encrypted-data | sops decrypt /dev/stdin > decrypted-data
|
||||
$ cat encrypted-data | sops decrypt > decrypted-data
|
||||
|
||||
To control the input and output format, pass ``--input-type`` and ``--output-type`` as appropriate. By default,
|
||||
``sops`` determines the input and output format from the provided filename, which is ``/dev/stdin`` here, and
|
||||
thus will use the binary store which expects JSON input and outputs binary data on decryption.
|
||||
By default, ``sops`` determines the input and output format from the provided filename. Since in this case,
|
||||
no filename is provided, ``sops`` will use the binary store which expects JSON input and outputs binary data
|
||||
on decryption. This is often not what you want.
|
||||
|
||||
For example, to decrypt YAML data and obtain the decrypted result as YAML, use:
|
||||
To avoid this, you can either provide a filename with ``--filename-override``, or explicitly control
|
||||
the input and output formats by passing ``--input-type`` and ``--output-type`` as appropriate:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ cat encrypted-data | sops decrypt --input-type yaml --output-type yaml /dev/stdin > decrypted-data
|
||||
$ cat encrypted-data | sops decrypt --filename-override filename.yaml > decrypted-data
|
||||
$ cat encrypted-data | sops decrypt --input-type yaml --output-type yaml > decrypted-data
|
||||
|
||||
In both cases, ``sops`` will assume that the data you provide is in YAML format, and will encode the decrypted
|
||||
data in YAML as well. The second form allows to use different formats for input and output.
|
||||
|
||||
To encrypt, it is important to note that SOPS also uses the filename to look up the correct creation rule from
|
||||
``.sops.yaml``. Likely ``/dev/stdin`` will not match a creation rule, or only match the fallback rule without
|
||||
``path_regex``, which is usually not what you want. For that, ``sops`` provides the ``--filename-override``
|
||||
parameter which allows you to tell SOPS which filename to use to match creation rules:
|
||||
``.sops.yaml``. Therefore, you must provide the ``--filename-override`` parameter which allows you to tell
|
||||
SOPS which filename to use to match creation rules:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ echo 'foo: bar' | sops encrypt --filename-override path/filename.sops.yaml /dev/stdin > encrypted-data
|
||||
$ echo 'foo: bar' | sops encrypt --filename-override path/filename.sops.yaml > encrypted-data
|
||||
|
||||
SOPS will find a matching creation rule for ``path/filename.sops.yaml`` in ``.sops.yaml`` and use that one to
|
||||
encrypt the data from stdin. This filename will also be used to determine the input and output store. As always,
|
||||
@@ -373,7 +423,7 @@ the input store type can be adjusted by passing ``--input-type``, and the output
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ echo foo=bar | sops encrypt --filename-override path/filename.sops.yaml --input-type dotenv /dev/stdin > encrypted-data
|
||||
$ echo foo=bar | sops encrypt --filename-override path/filename.sops.yaml --input-type dotenv > encrypted-data
|
||||
|
||||
|
||||
Encrypting using Hashicorp Vault
|
||||
@@ -485,7 +535,7 @@ disabled by supplying the ``-y`` flag.
|
||||
******************
|
||||
|
||||
The ``rotate`` command generates a new data encryption key and reencrypt all values
|
||||
with the new key. At te same time, the command line flag ``--add-kms``, ``--add-pgp``,
|
||||
with the new key. At the same time, the 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``
|
||||
@@ -669,6 +719,11 @@ of all new files. If your secrets are stored under a specific directory, like a
|
||||
``git`` repository, you can create a ``.sops.yaml`` configuration file at the root
|
||||
directory to define which keys are used for which filename.
|
||||
|
||||
.. note::
|
||||
|
||||
The file needs to be named ``.sops.yaml``. Other names (i.e. ``.sops.yml``) won't be automatically
|
||||
discovered by SOPS. You'll need to pass the ``--config .sops.yml`` option for it to be picked up.
|
||||
|
||||
Let's take an example:
|
||||
|
||||
* file named **something.dev.yaml** should use one set of KMS A, PGP and age
|
||||
@@ -1026,6 +1081,11 @@ written to disk.
|
||||
$ echo your password: $database_password
|
||||
your password:
|
||||
|
||||
If you want process signals to be sent to the command, for example if you are
|
||||
running ``exec-env`` to launch a server and your server handles SIGTERM, then the
|
||||
``--same-process`` flag can be used to instruct ``sops`` to start your command in
|
||||
the same process instead of a child process. This uses the ``execve`` system call
|
||||
and is supported on Unix-like systems.
|
||||
|
||||
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
|
||||
@@ -1211,7 +1271,7 @@ When operating on stdin, use the ``--input-type`` and ``--output-type`` flags as
|
||||
|
||||
.. code:: sh
|
||||
|
||||
$ cat myfile.json | sops decrypt --input-type json --output-type json /dev/stdin
|
||||
$ cat myfile.json | sops decrypt --input-type json --output-type json
|
||||
|
||||
JSON and JSON_binary indentation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/getsops/sops/v3"
|
||||
"github.com/getsops/sops/v3/logging"
|
||||
@@ -110,6 +111,10 @@ func (c Cipher) Decrypt(ciphertext string, key []byte, additionalData string) (p
|
||||
plaintext = decryptedBytes
|
||||
case "bool":
|
||||
plaintext, err = strconv.ParseBool(decryptedValue)
|
||||
case "time":
|
||||
var value time.Time
|
||||
err = value.UnmarshalText(decryptedBytes)
|
||||
plaintext = value
|
||||
case "comment":
|
||||
plaintext = sops.Comment{Value: decryptedValue}
|
||||
default:
|
||||
@@ -176,6 +181,12 @@ func (c Cipher) Encrypt(plaintext interface{}, key []byte, additionalData string
|
||||
} else {
|
||||
plainBytes = []byte("False")
|
||||
}
|
||||
case time.Time:
|
||||
encryptedType = "time"
|
||||
plainBytes, err = value.MarshalText()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error marshaling timestamp %q: %w", value, err)
|
||||
}
|
||||
case sops.Comment:
|
||||
encryptedType = "comment"
|
||||
plainBytes = []byte(value.Value)
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package aes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/getsops/sops/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDecrypt(t *testing.T) {
|
||||
@@ -108,6 +111,36 @@ func TestRoundtripBool(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundtripTime(t *testing.T) {
|
||||
key := []byte(strings.Repeat("f", 32))
|
||||
parsedTime, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05+07:00")
|
||||
assert.Nil(t, err)
|
||||
loc := time.FixedZone("", 12300) // offset must be divisible by 60, otherwise won't survive a round-trip
|
||||
values := []time.Time{
|
||||
time.UnixMilli(0).In(time.UTC),
|
||||
time.UnixMilli(123456).In(time.UTC),
|
||||
time.UnixMilli(123456).In(loc),
|
||||
time.UnixMilli(123456789).In(time.UTC),
|
||||
time.UnixMilli(123456789).In(loc),
|
||||
time.UnixMilli(1234567890).In(time.UTC),
|
||||
time.UnixMilli(1234567890).In(loc),
|
||||
parsedTime,
|
||||
}
|
||||
for _, value := range values {
|
||||
s, err := NewCipher().Encrypt(value, key, "foo")
|
||||
assert.Nil(t, err)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
d, err := NewCipher().Decrypt(s, key, "foo")
|
||||
assert.Nil(t, err)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
assert.Equal(t, value, d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncryptEmptyComment(t *testing.T) {
|
||||
key := []byte(strings.Repeat("f", 32))
|
||||
s, err := NewCipher().Encrypt(sops.Comment{}, key, "")
|
||||
@@ -121,3 +154,61 @@ func TestDecryptEmptyValue(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "", s)
|
||||
}
|
||||
|
||||
// This test would belong more in sops_test.go, but from there we cannot access
|
||||
// the aes package to get a cipher which can actually handle time.Time objects.
|
||||
func TestTimestamps(t *testing.T) {
|
||||
unixTime := time.UnixMilli(123456789).In(time.UTC)
|
||||
parsedTime, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05+07:00")
|
||||
assert.Nil(t, err)
|
||||
branches := sops.TreeBranches{
|
||||
sops.TreeBranch{
|
||||
sops.TreeItem{
|
||||
Key: "foo",
|
||||
Value: unixTime,
|
||||
},
|
||||
sops.TreeItem{
|
||||
Key: "bar",
|
||||
Value: sops.TreeBranch{
|
||||
sops.TreeItem{
|
||||
Key: "foo",
|
||||
Value: parsedTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
tree := sops.Tree{Branches: branches, Metadata: sops.Metadata{UnencryptedSuffix: "_unencrypted"}}
|
||||
expected := sops.TreeBranch{
|
||||
sops.TreeItem{
|
||||
Key: "foo",
|
||||
Value: unixTime,
|
||||
},
|
||||
sops.TreeItem{
|
||||
Key: "bar",
|
||||
Value: sops.TreeBranch{
|
||||
sops.TreeItem{
|
||||
Key: "foo",
|
||||
Value: parsedTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
cipher := NewCipher()
|
||||
_, 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 do match: \ngot \t\t%+v,\n not 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)
|
||||
}
|
||||
assert.Equal(t, tree.Branches[0][0].Value, unixTime)
|
||||
assert.Equal(t, tree.Branches[0], expected)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
190
age/encrypted_keys.go
Normal file
190
age/encrypted_keys.go
Normal file
@@ -0,0 +1,190 @@
|
||||
// These functions have been copied from the age project
|
||||
// https://github.com/FiloSottile/age/blob/101cc8676386b0503571a929a88618cae2f0b1cd/cmd/age/encrypted_keys.go
|
||||
// https://github.com/FiloSottile/age/blob/101cc8676386b0503571a929a88618cae2f0b1cd/cmd/age/parse.go
|
||||
//
|
||||
// Copyright 2021 The age Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in age's LICENSE file at
|
||||
// https://github.com/FiloSottile/age/blob/v1.0.0/LICENSE
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package age
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"filippo.io/age"
|
||||
"filippo.io/age/armor"
|
||||
|
||||
gpgagent "github.com/getsops/gopgagent"
|
||||
)
|
||||
|
||||
type EncryptedIdentity struct {
|
||||
Contents []byte
|
||||
Passphrase func() (string, error)
|
||||
NoMatchWarning func()
|
||||
IncorrectPassphrase func()
|
||||
|
||||
identities []age.Identity
|
||||
}
|
||||
|
||||
var _ age.Identity = &EncryptedIdentity{}
|
||||
|
||||
func (i *EncryptedIdentity) Unwrap(stanzas []*age.Stanza) (fileKey []byte, err error) {
|
||||
if i.identities == nil {
|
||||
if err := i.decrypt(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, id := range i.identities {
|
||||
fileKey, err = id.Unwrap(stanzas)
|
||||
if errors.Is(err, age.ErrIncorrectIdentity) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fileKey, nil
|
||||
}
|
||||
i.NoMatchWarning()
|
||||
return nil, age.ErrIncorrectIdentity
|
||||
}
|
||||
|
||||
func (i *EncryptedIdentity) decrypt() error {
|
||||
d, err := age.Decrypt(bytes.NewReader(i.Contents), &LazyScryptIdentity{i.Passphrase})
|
||||
if e := new(age.NoIdentityMatchError); errors.As(err, &e) {
|
||||
// ScryptIdentity returns ErrIncorrectIdentity for an incorrect
|
||||
// passphrase, which would lead Decrypt to returning "no identity
|
||||
// matched any recipient". That makes sense in the API, where there
|
||||
// might be multiple configured ScryptIdentity. Since in cmd/age there
|
||||
// can be only one, return a better error message.
|
||||
i.IncorrectPassphrase()
|
||||
return fmt.Errorf("incorrect passphrase")
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decrypt identity file: %v", err)
|
||||
}
|
||||
i.identities, err = age.ParseIdentities(d)
|
||||
return err
|
||||
}
|
||||
|
||||
// LazyScryptIdentity is an age.Identity that requests a passphrase only if it
|
||||
// encounters an scrypt stanza. After obtaining a passphrase, it delegates to
|
||||
// ScryptIdentity.
|
||||
type LazyScryptIdentity struct {
|
||||
Passphrase func() (string, error)
|
||||
}
|
||||
|
||||
var _ age.Identity = &LazyScryptIdentity{}
|
||||
|
||||
func (i *LazyScryptIdentity) Unwrap(stanzas []*age.Stanza) (fileKey []byte, err error) {
|
||||
for _, s := range stanzas {
|
||||
if s.Type == "scrypt" && len(stanzas) != 1 {
|
||||
return nil, errors.New("an scrypt recipient must be the only one")
|
||||
}
|
||||
}
|
||||
if len(stanzas) != 1 || stanzas[0].Type != "scrypt" {
|
||||
return nil, age.ErrIncorrectIdentity
|
||||
}
|
||||
pass, err := i.Passphrase()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read passphrase: %v", err)
|
||||
}
|
||||
ii, err := age.NewScryptIdentity(pass)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fileKey, err = ii.Unwrap(stanzas)
|
||||
return fileKey, err
|
||||
}
|
||||
|
||||
func unwrapIdentities(key string, reader io.Reader) (ParsedIdentities, error) {
|
||||
b := bufio.NewReader(reader)
|
||||
p, _ := b.Peek(14) // length of "age-encryption" and "-----BEGIN AGE"
|
||||
peeked := string(p)
|
||||
|
||||
switch {
|
||||
// An age encrypted file, plain or armored.
|
||||
case peeked == "age-encryption" || peeked == "-----BEGIN AGE":
|
||||
var r io.Reader = b
|
||||
if peeked == "-----BEGIN AGE" {
|
||||
r = armor.NewReader(r)
|
||||
}
|
||||
const privateKeySizeLimit = 1 << 24 // 16 MiB
|
||||
contents, err := io.ReadAll(io.LimitReader(r, privateKeySizeLimit))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read '%s': %w", key, err)
|
||||
}
|
||||
if len(contents) == privateKeySizeLimit {
|
||||
return nil, fmt.Errorf("failed to read '%s': file too long", key)
|
||||
}
|
||||
IncorrectPassphrase := func() {
|
||||
conn, err := gpgagent.NewConn()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func(conn *gpgagent.Conn) {
|
||||
if err := conn.Close(); err != nil {
|
||||
log.Errorf("failed to close connection with gpg-agent: %s", err)
|
||||
}
|
||||
}(conn)
|
||||
err = conn.RemoveFromCache(key)
|
||||
if err != nil {
|
||||
log.Warnf("gpg-agent remove cache request errored: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
ids := []age.Identity{&EncryptedIdentity{
|
||||
Contents: contents,
|
||||
Passphrase: func() (string, error) {
|
||||
conn, err := gpgagent.NewConn()
|
||||
if err != nil {
|
||||
passphrase, err := readSecret("Enter passphrase for identity " + key + ":")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(passphrase), nil
|
||||
}
|
||||
defer func(conn *gpgagent.Conn) {
|
||||
if err := conn.Close(); err != nil {
|
||||
log.Errorf("failed to close connection with gpg-agent: %s", err)
|
||||
}
|
||||
}(conn)
|
||||
|
||||
req := gpgagent.PassphraseRequest{
|
||||
// TODO is the cachekey good enough?
|
||||
CacheKey: key,
|
||||
Prompt: "Passphrase",
|
||||
Desc: fmt.Sprintf("Enter passphrase for identity '%s':", key),
|
||||
}
|
||||
pass, err := conn.GetPassphrase(&req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("gpg-agent passphrase request errored: %s", err)
|
||||
}
|
||||
//make sure that we won't store empty pass
|
||||
if len(pass) == 0 {
|
||||
IncorrectPassphrase()
|
||||
}
|
||||
return pass, nil
|
||||
},
|
||||
IncorrectPassphrase: IncorrectPassphrase,
|
||||
NoMatchWarning: func() {
|
||||
log.Warnf("encrypted identity '%s' didn't match file's recipients", key)
|
||||
},
|
||||
}}
|
||||
return ids, nil
|
||||
// An unencrypted age identity file.
|
||||
default:
|
||||
ids, err := parseIdentities(b)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse '%s' age identities: %w", key, err)
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
}
|
||||
150
age/keysource.go
150
age/keysource.go
@@ -1,20 +1,25 @@
|
||||
package age
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"filippo.io/age"
|
||||
"filippo.io/age/agessh"
|
||||
"filippo.io/age/armor"
|
||||
"filippo.io/age/plugin"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/getsops/sops/v3/logging"
|
||||
"github.com/google/shlex"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,6 +29,12 @@ const (
|
||||
// SopsAgeKeyFileEnv can be set as an environment variable pointing to an
|
||||
// age keys file.
|
||||
SopsAgeKeyFileEnv = "SOPS_AGE_KEY_FILE"
|
||||
// SopsAgeKeyCmdEnv can be set as an environment variable with a command
|
||||
// to execute that returns the age keys.
|
||||
SopsAgeKeyCmdEnv = "SOPS_AGE_KEY_CMD"
|
||||
// SopsAgeSshPrivateKeyFileEnv can be set as an environment variable pointing to
|
||||
// a private SSH key file.
|
||||
SopsAgeSshPrivateKeyFileEnv = "SOPS_AGE_SSH_PRIVATE_KEY_FILE"
|
||||
// SopsAgeKeyUserConfigPath is the default age keys file path in
|
||||
// getUserConfigDir().
|
||||
SopsAgeKeyUserConfigPath = "sops/age/keys.txt"
|
||||
@@ -60,7 +71,7 @@ type MasterKey struct {
|
||||
parsedIdentities []age.Identity
|
||||
// parsedRecipient contains a parsed age public key.
|
||||
// It is used to lazy-load the Recipient at-most once.
|
||||
parsedRecipient *age.X25519Recipient
|
||||
parsedRecipient age.Recipient
|
||||
}
|
||||
|
||||
// MasterKeysFromRecipients takes a comma-separated list of Bech32-encoded
|
||||
@@ -111,7 +122,10 @@ type ParsedIdentities []age.Identity
|
||||
// parsing (using age.ParseIdentities) and appending to the slice yourself, in
|
||||
// combination with e.g. a sync.Mutex.
|
||||
func (i *ParsedIdentities) Import(identity ...string) error {
|
||||
identities, err := parseIdentities(identity...)
|
||||
// one identity per line
|
||||
r := strings.NewReader(strings.Join(identity, "\n"))
|
||||
|
||||
identities, err := parseIdentities(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse and add to age identities: %w", err)
|
||||
}
|
||||
@@ -233,6 +247,35 @@ func (key *MasterKey) TypeToIdentifier() string {
|
||||
return KeyTypeIdentifier
|
||||
}
|
||||
|
||||
// loadAgeSSHIdentity attempts to load the age SSH identity based on an SSH
|
||||
// private key from the SopsAgeSshPrivateKeyFileEnv environment variable. If the
|
||||
// environment variable is not present, it will fall back to `~/.ssh/id_ed25519`
|
||||
// or `~/.ssh/id_rsa`. If no age SSH identity is found, it will return nil.
|
||||
func loadAgeSSHIdentity() (age.Identity, error) {
|
||||
sshKeyFilePath, ok := os.LookupEnv(SopsAgeSshPrivateKeyFileEnv)
|
||||
if ok {
|
||||
return parseSSHIdentityFromPrivateKeyFile(sshKeyFilePath)
|
||||
}
|
||||
|
||||
userHomeDir, err := os.UserHomeDir()
|
||||
if err != nil || userHomeDir == "" {
|
||||
log.Warnf("could not determine the user home directory: %v", err)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sshEd25519PrivateKeyPath := filepath.Join(userHomeDir, ".ssh", "id_ed25519")
|
||||
if _, err := os.Stat(sshEd25519PrivateKeyPath); err == nil {
|
||||
return parseSSHIdentityFromPrivateKeyFile(sshEd25519PrivateKeyPath)
|
||||
}
|
||||
|
||||
sshRsaPrivateKeyPath := filepath.Join(userHomeDir, ".ssh", "id_rsa")
|
||||
if _, err := os.Stat(sshRsaPrivateKeyPath); err == nil {
|
||||
return parseSSHIdentityFromPrivateKeyFile(sshRsaPrivateKeyPath)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func getUserConfigDir() (string, error) {
|
||||
if runtime.GOOS == "darwin" {
|
||||
if userConfigDir, ok := os.LookupEnv(xdgConfigHome); ok && userConfigDir != "" {
|
||||
@@ -244,9 +287,19 @@ func getUserConfigDir() (string, error) {
|
||||
|
||||
// loadIdentities attempts to load the age identities based on runtime
|
||||
// environment configurations (e.g. SopsAgeKeyEnv, SopsAgeKeyFileEnv,
|
||||
// SopsAgeKeyUserConfigPath). It will load all found references, and expects
|
||||
// at least one configuration to be present.
|
||||
// SopsAgeSshPrivateKeyFileEnv, SopsAgeKeyUserConfigPath). It will load all
|
||||
// found references, and expects at least one configuration to be present.
|
||||
func (key *MasterKey) loadIdentities() (ParsedIdentities, error) {
|
||||
var identities ParsedIdentities
|
||||
|
||||
sshIdentity, err := loadAgeSSHIdentity()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get SSH identity: %w", err)
|
||||
}
|
||||
if sshIdentity != nil {
|
||||
identities = append(identities, sshIdentity)
|
||||
}
|
||||
|
||||
var readers = make(map[string]io.Reader, 0)
|
||||
|
||||
if ageKey, ok := os.LookupEnv(SopsAgeKeyEnv); ok {
|
||||
@@ -262,8 +315,20 @@ func (key *MasterKey) loadIdentities() (ParsedIdentities, error) {
|
||||
readers[SopsAgeKeyFileEnv] = f
|
||||
}
|
||||
|
||||
if ageKeyCmd, ok := os.LookupEnv(SopsAgeKeyCmdEnv); ok {
|
||||
args, err := shlex.Split(ageKeyCmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse command %s from %s: %w", ageKeyCmd, SopsAgeKeyCmdEnv, err)
|
||||
}
|
||||
out, err := exec.Command(args[0], args[1:]...).Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute command %s from %s: %w", ageKeyCmd, SopsAgeKeyCmdEnv, err)
|
||||
}
|
||||
readers[SopsAgeKeyCmdEnv] = bytes.NewReader(out)
|
||||
}
|
||||
|
||||
userConfigDir, err := getUserConfigDir()
|
||||
if err != nil && len(readers) == 0 {
|
||||
if err != nil && len(readers) == 0 && len(identities) == 0 {
|
||||
return nil, fmt.Errorf("user config directory could not be determined: %w", err)
|
||||
}
|
||||
if userConfigDir != "" {
|
||||
@@ -272,7 +337,7 @@ func (key *MasterKey) loadIdentities() (ParsedIdentities, error) {
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return nil, fmt.Errorf("failed to open file: %w", err)
|
||||
}
|
||||
if errors.Is(err, os.ErrNotExist) && len(readers) == 0 {
|
||||
if errors.Is(err, os.ErrNotExist) && len(readers) == 0 && len(identities) == 0 {
|
||||
// If we have no other readers, presence of the file is required.
|
||||
return nil, fmt.Errorf("failed to open file: %w", err)
|
||||
}
|
||||
@@ -282,11 +347,10 @@ func (key *MasterKey) loadIdentities() (ParsedIdentities, error) {
|
||||
}
|
||||
}
|
||||
|
||||
var identities ParsedIdentities
|
||||
for n, r := range readers {
|
||||
ids, err := age.ParseIdentities(r)
|
||||
ids, err := unwrapIdentities(n, r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse '%s' age identities: %w", n, err)
|
||||
return nil, err
|
||||
}
|
||||
identities = append(identities, ids...)
|
||||
}
|
||||
@@ -294,26 +358,66 @@ func (key *MasterKey) loadIdentities() (ParsedIdentities, error) {
|
||||
}
|
||||
|
||||
// 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)
|
||||
// key or a public ssh key.
|
||||
func parseRecipient(recipient string) (age.Recipient, error) {
|
||||
switch {
|
||||
case strings.HasPrefix(recipient, "age1") && strings.Count(recipient, "1") > 1:
|
||||
parsedRecipient, err := plugin.NewRecipient(recipient, pluginTerminalUI)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse input as age key from age plugin: %w", err)
|
||||
}
|
||||
return parsedRecipient, nil
|
||||
case strings.HasPrefix(recipient, "age1"):
|
||||
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
|
||||
case strings.HasPrefix(recipient, "ssh-"):
|
||||
parsedRecipient, err := agessh.ParseRecipient(recipient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse input as age-ssh public key: %w", err)
|
||||
}
|
||||
return parsedRecipient, nil
|
||||
}
|
||||
return parsedRecipient, nil
|
||||
|
||||
return nil, fmt.Errorf("failed to parse input, unknown recipient type: %q", recipient)
|
||||
}
|
||||
|
||||
// parseIdentities attempts to parse the string set of encoded age identities.
|
||||
// A single identity argument is allowed to be a multiline string containing
|
||||
// multiple identities. Empty lines and lines starting with "#" are ignored.
|
||||
func parseIdentities(identity ...string) (ParsedIdentities, error) {
|
||||
var identities []age.Identity
|
||||
for _, i := range identity {
|
||||
parsed, err := age.ParseIdentities(strings.NewReader(i))
|
||||
// parseIdentities attempts to parse one or more age identities from the provided reader.
|
||||
// One identity per line.
|
||||
// Empty lines and lines starting with "#" are ignored.
|
||||
func parseIdentities(r io.Reader) (ParsedIdentities, error) {
|
||||
var identities ParsedIdentities
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
parsed, err := parseIdentity(line)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
identities = append(identities, parsed...)
|
||||
|
||||
identities = append(identities, parsed)
|
||||
}
|
||||
|
||||
return identities, nil
|
||||
}
|
||||
|
||||
func parseIdentity(s string) (age.Identity, error) {
|
||||
switch {
|
||||
case strings.HasPrefix(s, "AGE-PLUGIN-"):
|
||||
return plugin.NewIdentity(s, pluginTerminalUI)
|
||||
case strings.HasPrefix(s, "AGE-SECRET-KEY-1"):
|
||||
return age.ParseX25519Identity(s)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown identity type")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,35 @@ EylloI7MNGbadPGb
|
||||
-----END AGE ENCRYPTED FILE-----`
|
||||
// mockEncryptedKeyPlain is the plain value of mockEncryptedKey.
|
||||
mockEncryptedKeyPlain string = "data"
|
||||
// passphrase used to encrypt age identity.
|
||||
mockIdentityPassphrase string = "passphrase"
|
||||
mockEncryptedIdentity string = `-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCBMN2FXZW9xSFViYjdNeW5D
|
||||
dy9iSHFnIDE4Ck9zV0ZoNldmci9rL3VXd3BtZmQvK3VZWEpBQjdhZ0UrcmhqR2lF
|
||||
YThFMzAKLS0tIGVEQ0xwODI1TlNYeHNHaHZKWHoyLzYwMTMvTGhaZG1oa203cSs0
|
||||
VUpBL1kKsaTnt+H/z8mkL21UYKIt3YMpWSV/oYqTm1cSSUnF9InZEYU9HndK9rc8
|
||||
ni+MTJCmYf4mgvvGPMf7oIQvs6ijaTdlQb+zeQsL4eif20w+CWgvPNrS6iXUIs8W
|
||||
w5/fHsxwmrkG96nDkMErJKhmjmLpC+YdbiMe6P/KIpas09m08RTIqcz7ua0Xm3ey
|
||||
ndU+8ILJOhcnWV55W43nTw/UUFse7f+qY61n7kcd1sGd7ZfSEdEIqS3K2vEtA3ER
|
||||
fn0s3cyXVEBxL9OZqcAk45bCFVOl13Fp/DBfquHEjvAyeg0=
|
||||
-----END AGE ENCRYPTED FILE-----`
|
||||
// mockSshRecipient is a mock age ssh recipient, it matches mockSshIdentity
|
||||
mockSshRecipient string = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID+Wi8WZw2bXfBpcs/WECttCzP39OkenS6pHWHWGFJvN Test"
|
||||
// mockSshIdentity is a mock age identity based on an OpenSSH private key (ed25519)
|
||||
mockSshIdentity string = `-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACA/lovFmcNm13waXLP1hArbQsz9/TpHp0uqR1h1hhSbzQAAAIgCXDMIAlwz
|
||||
CAAAAAtzc2gtZWQyNTUxOQAAACA/lovFmcNm13waXLP1hArbQsz9/TpHp0uqR1h1hhSbzQ
|
||||
AAAEBJdWTJ8dC0OnMcwy4gQ96sp6KG8GE9EiyhFGhKldKiST+Wi8WZw2bXfBpcs/WECttC
|
||||
zP39OkenS6pHWHWGFJvNAAAABFRlc3QB
|
||||
-----END OPENSSH PRIVATE KEY-----`
|
||||
mockEncryptedSshKey string = `-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDJjd0R4dyB2R3Ns
|
||||
VUNHaXBiTEJaNU5BMFFQZUpCYWJqODFyTTZ4WWZoRVpUd2M2aTBFCkduUFJHb1U2
|
||||
K3RqWVQrLzE4anZKZ3h2T3c2MFpZTHlGaHprcElXenByWTAKLS0tIG56MHFSZERl
|
||||
em9PWmRMMTY4aytYTnVZN04yeER5Z2E3TWxWT3JTZWR2ekUKp/HZLy4MzQqoszGk
|
||||
+P0hSPPNhOhvFwv4AqCw1+A+WyeHGQPq
|
||||
-----END AGE ENCRYPTED FILE-----`
|
||||
)
|
||||
|
||||
func TestMasterKeysFromRecipients(t *testing.T) {
|
||||
@@ -41,22 +70,32 @@ func TestMasterKeysFromRecipients(t *testing.T) {
|
||||
assert.Equal(t, got[0].Recipient, mockRecipient)
|
||||
})
|
||||
|
||||
t.Run("recipients", func(t *testing.T) {
|
||||
got, err := MasterKeysFromRecipients(mockRecipient + "," + otherRecipient)
|
||||
t.Run("recipient-ssh", func(t *testing.T) {
|
||||
got, err := MasterKeysFromRecipients(mockSshRecipient)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Len(t, got, 2)
|
||||
assert.Len(t, got, 1)
|
||||
assert.Equal(t, got[0].Recipient, mockSshRecipient)
|
||||
})
|
||||
|
||||
t.Run("recipients", func(t *testing.T) {
|
||||
got, err := MasterKeysFromRecipients(mockRecipient + "," + otherRecipient + "," + mockSshRecipient)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Len(t, got, 3)
|
||||
assert.Equal(t, got[0].Recipient, mockRecipient)
|
||||
assert.Equal(t, got[1].Recipient, otherRecipient)
|
||||
assert.Equal(t, got[2].Recipient, mockSshRecipient)
|
||||
})
|
||||
|
||||
t.Run("leading and trailing spaces", func(t *testing.T) {
|
||||
got, err := MasterKeysFromRecipients(" " + mockRecipient + " , " + otherRecipient + " ")
|
||||
got, err := MasterKeysFromRecipients(" " + mockRecipient + " , " + otherRecipient + " , " + mockSshRecipient + " ")
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Len(t, got, 2)
|
||||
assert.Len(t, got, 3)
|
||||
assert.Equal(t, got[0].Recipient, mockRecipient)
|
||||
assert.Equal(t, got[1].Recipient, otherRecipient)
|
||||
assert.Equal(t, got[2].Recipient, mockSshRecipient)
|
||||
})
|
||||
|
||||
t.Run("empty", func(t *testing.T) {
|
||||
@@ -75,6 +114,14 @@ func TestMasterKeyFromRecipient(t *testing.T) {
|
||||
assert.Nil(t, got.parsedIdentities)
|
||||
})
|
||||
|
||||
t.Run("recipient-ssh", func(t *testing.T) {
|
||||
got, err := MasterKeyFromRecipient(mockSshRecipient)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, mockSshRecipient, got.Recipient)
|
||||
assert.NotNil(t, got.parsedRecipient)
|
||||
assert.Nil(t, got.parsedIdentities)
|
||||
})
|
||||
|
||||
t.Run("leading and trailing spaces", func(t *testing.T) {
|
||||
got, err := MasterKeyFromRecipient(" " + mockRecipient + " ")
|
||||
assert.NoError(t, err)
|
||||
@@ -83,6 +130,14 @@ func TestMasterKeyFromRecipient(t *testing.T) {
|
||||
assert.Nil(t, got.parsedIdentities)
|
||||
})
|
||||
|
||||
t.Run("leading and trailing spaces - ssh", func(t *testing.T) {
|
||||
got, err := MasterKeyFromRecipient(" " + mockSshRecipient + " ")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, mockSshRecipient, got.Recipient)
|
||||
assert.NotNil(t, got.parsedRecipient)
|
||||
assert.Nil(t, got.parsedIdentities)
|
||||
})
|
||||
|
||||
t.Run("invalid recipient", func(t *testing.T) {
|
||||
got, err := MasterKeyFromRecipient("invalid")
|
||||
assert.Error(t, err)
|
||||
@@ -111,6 +166,8 @@ func TestParsedIdentities_ApplyToMasterKey(t *testing.T) {
|
||||
func TestMasterKey_Encrypt(t *testing.T) {
|
||||
mockParsedRecipient, err := parseRecipient(mockRecipient)
|
||||
assert.NoError(t, err)
|
||||
mockSshParsedRecipient, err := parseRecipient(mockSshRecipient)
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Run("recipient", func(t *testing.T) {
|
||||
key := &MasterKey{
|
||||
@@ -120,6 +177,14 @@ func TestMasterKey_Encrypt(t *testing.T) {
|
||||
assert.NotEmpty(t, key.EncryptedKey)
|
||||
})
|
||||
|
||||
t.Run("recipient ssh", func(t *testing.T) {
|
||||
key := &MasterKey{
|
||||
Recipient: mockSshRecipient,
|
||||
}
|
||||
assert.NoError(t, key.Encrypt([]byte(mockEncryptedKeyPlain)))
|
||||
assert.NotEmpty(t, key.EncryptedKey)
|
||||
})
|
||||
|
||||
t.Run("parsed recipient", func(t *testing.T) {
|
||||
key := &MasterKey{
|
||||
parsedRecipient: mockParsedRecipient,
|
||||
@@ -128,13 +193,21 @@ func TestMasterKey_Encrypt(t *testing.T) {
|
||||
assert.NotEmpty(t, key.EncryptedKey)
|
||||
})
|
||||
|
||||
t.Run("parsed recipient ssh", func(t *testing.T) {
|
||||
key := &MasterKey{
|
||||
parsedRecipient: mockSshParsedRecipient,
|
||||
}
|
||||
assert.NoError(t, key.Encrypt([]byte(mockEncryptedKeyPlain)))
|
||||
assert.NotEmpty(t, key.EncryptedKey)
|
||||
})
|
||||
|
||||
t.Run("invalid recipient", func(t *testing.T) {
|
||||
key := &MasterKey{
|
||||
Recipient: "invalid",
|
||||
}
|
||||
err := key.Encrypt([]byte(mockEncryptedKeyPlain))
|
||||
assert.Error(t, err)
|
||||
assert.ErrorContains(t, err, "failed to parse input as Bech32-encoded age public key")
|
||||
assert.ErrorContains(t, err, "failed to parse input, unknown recipient type:")
|
||||
assert.Empty(t, key.EncryptedKey)
|
||||
})
|
||||
|
||||
@@ -180,6 +253,7 @@ func TestMasterKey_Decrypt(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("loaded identities", func(t *testing.T) {
|
||||
overwriteUserConfigDir(t, t.TempDir())
|
||||
key := &MasterKey{EncryptedKey: mockEncryptedKey}
|
||||
t.Setenv(SopsAgeKeyEnv, mockIdentity)
|
||||
|
||||
@@ -188,6 +262,25 @@ func TestMasterKey_Decrypt(t *testing.T) {
|
||||
assert.EqualValues(t, mockEncryptedKeyPlain, got)
|
||||
})
|
||||
|
||||
t.Run("loaded identities ssh", func(t *testing.T) {
|
||||
key := &MasterKey{EncryptedKey: mockEncryptedSshKey}
|
||||
tmp := t.TempDir()
|
||||
overwriteUserConfigDir(t, tmp)
|
||||
|
||||
homeDir, err := os.UserHomeDir()
|
||||
assert.NoError(t, err)
|
||||
keyPath := filepath.Join(homeDir, ".ssh/id_25519")
|
||||
assert.True(t, strings.HasPrefix(keyPath, homeDir))
|
||||
|
||||
assert.NoError(t, os.MkdirAll(filepath.Dir(keyPath), 0o700))
|
||||
assert.NoError(t, os.WriteFile(keyPath, []byte(mockSshIdentity), 0o644))
|
||||
t.Setenv(SopsAgeSshPrivateKeyFileEnv, keyPath)
|
||||
|
||||
got, err := key.Decrypt()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, mockEncryptedKeyPlain, got)
|
||||
})
|
||||
|
||||
t.Run("no identities", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
overwriteUserConfigDir(t, tmpDir)
|
||||
@@ -215,6 +308,7 @@ func TestMasterKey_Decrypt(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("invalid encrypted key", func(t *testing.T) {
|
||||
overwriteUserConfigDir(t, t.TempDir())
|
||||
key := &MasterKey{EncryptedKey: "invalid"}
|
||||
t.Setenv(SopsAgeKeyEnv, mockIdentity)
|
||||
|
||||
@@ -327,6 +421,25 @@ func TestMasterKey_loadIdentities(t *testing.T) {
|
||||
assert.Len(t, got, 1)
|
||||
})
|
||||
|
||||
t.Run(SopsAgeSshPrivateKeyFileEnv, func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
overwriteUserConfigDir(t, tmpDir)
|
||||
|
||||
homeDir, err := os.UserHomeDir()
|
||||
assert.NoError(t, err)
|
||||
keyPath := filepath.Join(homeDir, ".ssh/id_25519")
|
||||
assert.True(t, strings.HasPrefix(keyPath, homeDir))
|
||||
|
||||
assert.NoError(t, os.MkdirAll(filepath.Dir(keyPath), 0o700))
|
||||
assert.NoError(t, os.WriteFile(keyPath, []byte(mockSshIdentity), 0o644))
|
||||
t.Setenv(SopsAgeSshPrivateKeyFileEnv, keyPath)
|
||||
|
||||
key := &MasterKey{}
|
||||
got, err := key.loadIdentities()
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, got, 1)
|
||||
})
|
||||
|
||||
t.Run("no identity", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
overwriteUserConfigDir(t, tmpDir)
|
||||
@@ -372,10 +485,37 @@ func TestMasterKey_loadIdentities(t *testing.T) {
|
||||
assert.ErrorContains(t, err, fmt.Sprintf("failed to parse '%s' age identities", SopsAgeKeyEnv))
|
||||
assert.Nil(t, got)
|
||||
})
|
||||
|
||||
t.Run(SopsAgeKeyCmdEnv, func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
// Overwrite to ensure local config is not picked up by tests
|
||||
overwriteUserConfigDir(t, tmpDir)
|
||||
|
||||
t.Setenv(SopsAgeKeyCmdEnv, "echo '"+mockIdentity+"'")
|
||||
|
||||
key := &MasterKey{}
|
||||
got, err := key.loadIdentities()
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, got, 1)
|
||||
})
|
||||
|
||||
t.Run("cmd error", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
// Overwrite to ensure local config is not picked up by tests
|
||||
overwriteUserConfigDir(t, tmpDir)
|
||||
|
||||
t.Setenv(SopsAgeKeyCmdEnv, "meow")
|
||||
|
||||
key := &MasterKey{}
|
||||
got, err := key.loadIdentities()
|
||||
assert.Error(t, err)
|
||||
assert.ErrorContains(t, err, "failed to execute command meow")
|
||||
assert.Nil(t, got)
|
||||
})
|
||||
}
|
||||
|
||||
// overwriteUserConfigDir sets the user config directory based on the
|
||||
// os.UserConfigDir logic.
|
||||
// overwriteUserConfigDir sets the user config directory and the user home directory
|
||||
// based on the os.UserConfigDir logic.
|
||||
func overwriteUserConfigDir(t *testing.T, path string) {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
@@ -384,6 +524,7 @@ func overwriteUserConfigDir(t *testing.T, path string) {
|
||||
t.Setenv("home", path)
|
||||
default: // Unix
|
||||
t.Setenv("XDG_CONFIG_HOME", path)
|
||||
t.Setenv("HOME", path)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,3 +541,54 @@ func TestUserConfigDir(t *testing.T) {
|
||||
assert.Equal(t, home, dir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMasterKey_Identities_Passphrase(t *testing.T) {
|
||||
t.Run(SopsAgeKeyEnv, func(t *testing.T) {
|
||||
key := &MasterKey{EncryptedKey: mockEncryptedKey}
|
||||
t.Setenv(SopsAgeKeyEnv, mockEncryptedIdentity)
|
||||
//blocks calling gpg-agent
|
||||
os.Unsetenv("XDG_RUNTIME_DIR")
|
||||
testOnlyAgePassword = mockIdentityPassphrase
|
||||
got, err := key.Decrypt()
|
||||
testOnlyAgePassword = ""
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, mockEncryptedKeyPlain, got)
|
||||
})
|
||||
|
||||
t.Run(SopsAgeKeyFileEnv, func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
// Overwrite to ensure local config is not picked up by tests
|
||||
overwriteUserConfigDir(t, tmpDir)
|
||||
|
||||
keyPath := filepath.Join(tmpDir, "keys.txt")
|
||||
assert.NoError(t, os.WriteFile(keyPath, []byte(mockEncryptedIdentity), 0o644))
|
||||
|
||||
key := &MasterKey{EncryptedKey: mockEncryptedKey}
|
||||
t.Setenv(SopsAgeKeyFileEnv, keyPath)
|
||||
//blocks calling gpg-agent
|
||||
os.Unsetenv("XDG_RUNTIME_DIR")
|
||||
testOnlyAgePassword = mockIdentityPassphrase
|
||||
|
||||
got, err := key.Decrypt()
|
||||
testOnlyAgePassword = ""
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, mockEncryptedKeyPlain, got)
|
||||
})
|
||||
|
||||
t.Run("invalid encrypted key", func(t *testing.T) {
|
||||
key := &MasterKey{EncryptedKey: "invalid"}
|
||||
t.Setenv(SopsAgeKeyEnv, mockEncryptedIdentity)
|
||||
//blocks calling gpg-agent
|
||||
os.Unsetenv("XDG_RUNTIME_DIR")
|
||||
testOnlyAgePassword = mockIdentityPassphrase
|
||||
|
||||
got, err := key.Decrypt()
|
||||
testOnlyAgePassword = ""
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.ErrorContains(t, err, "failed to create reader for decrypting sops data key with age")
|
||||
assert.Nil(t, got)
|
||||
})
|
||||
}
|
||||
|
||||
84
age/ssh_parse.go
Normal file
84
age/ssh_parse.go
Normal file
@@ -0,0 +1,84 @@
|
||||
// These functions are similar to those in the age project
|
||||
// https://github.com/FiloSottile/age/blob/v1.0.0/cmd/age/parse.go
|
||||
//
|
||||
// Copyright 2021 The age Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in age's LICENSE file at
|
||||
// https://github.com/FiloSottile/age/blob/v1.0.0/LICENSE
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package age
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"filippo.io/age"
|
||||
"filippo.io/age/agessh"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// readPublicKeyFile attempts to read a public key based on the given private
|
||||
// key path. It assumes the public key is in the same directory, with the same
|
||||
// name, but with a ".pub" extension. If the public key cannot be read, an
|
||||
// error is returned.
|
||||
func readPublicKeyFile(privateKeyPath string) (ssh.PublicKey, error) {
|
||||
publicKeyPath := privateKeyPath + ".pub"
|
||||
f, err := os.Open(publicKeyPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to obtain public %q key for %q SSH key: %w", publicKeyPath, privateKeyPath, err)
|
||||
}
|
||||
defer f.Close()
|
||||
contents, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read %q: %w", publicKeyPath, err)
|
||||
}
|
||||
pubKey, _, _, _, err := ssh.ParseAuthorizedKey(contents)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse %q: %w", publicKeyPath, err)
|
||||
}
|
||||
return pubKey, nil
|
||||
}
|
||||
|
||||
// parseSSHIdentityFromPrivateKeyFile returns an age.Identity from the given
|
||||
// private key file. If the private key file is encrypted, it will configure
|
||||
// the identity to prompt for a passphrase.
|
||||
func parseSSHIdentityFromPrivateKeyFile(keyPath string) (age.Identity, error) {
|
||||
keyFile, err := os.Open(keyPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open file: %w", err)
|
||||
}
|
||||
defer keyFile.Close()
|
||||
contents, err := io.ReadAll(keyFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read file: %w", err)
|
||||
}
|
||||
id, err := agessh.ParseIdentity(contents)
|
||||
if sshErr, ok := err.(*ssh.PassphraseMissingError); ok {
|
||||
pubKey := sshErr.PublicKey
|
||||
if pubKey == nil {
|
||||
pubKey, err = readPublicKeyFile(keyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
passphrasePrompt := func() ([]byte, error) {
|
||||
pass, err := readSecret(fmt.Sprintf("Enter passphrase for %q:", keyPath))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read passphrase for %q: %v", keyPath, err)
|
||||
}
|
||||
return pass, nil
|
||||
}
|
||||
i, err := agessh.NewEncryptedSSHIdentity(pubKey, contents, passphrasePrompt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create encrypted SSH identity: %w", err)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("malformed SSH identity in %q: %w", keyPath, err)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
173
age/tui.go
Normal file
173
age/tui.go
Normal file
@@ -0,0 +1,173 @@
|
||||
// These functions have been copied from the age project
|
||||
// https://github.com/FiloSottile/age/blob/3d91014ea095e8d70f7c6c4833f89b53a96e0832/cmd/age/tui.go
|
||||
//
|
||||
// Copyright 2021 The age Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in age's LICENSE file at
|
||||
// https://github.com/FiloSottile/age/blob/v1.0.0/LICENSE
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package age
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"filippo.io/age/plugin"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
var testOnlyAgePassword string
|
||||
|
||||
func printf(format string, v ...interface{}) {
|
||||
log.Printf("age: "+format, v...)
|
||||
}
|
||||
|
||||
func warningf(format string, v ...interface{}) {
|
||||
log.Printf("age: warning: "+format, v...)
|
||||
}
|
||||
|
||||
// clearLine clears the current line on the terminal, or opens a new line if
|
||||
// terminal escape codes don't work.
|
||||
func clearLine(out io.Writer) {
|
||||
const (
|
||||
CUI = "\033[" // Control Sequence Introducer
|
||||
CPL = CUI + "F" // Cursor Previous Line
|
||||
EL = CUI + "K" // Erase in Line
|
||||
)
|
||||
|
||||
// First, open a new line, which is guaranteed to work everywhere. Then, try
|
||||
// to erase the line above with escape codes.
|
||||
//
|
||||
// (We use CRLF instead of LF to work around an apparent bug in WSL2's
|
||||
// handling of CONOUT$. Only when running a Windows binary from WSL2, the
|
||||
// cursor would not go back to the start of the line with a simple LF.
|
||||
// Honestly, it's impressive CONIN$ and CONOUT$ work at all inside WSL2.)
|
||||
fmt.Fprintf(out, "\r\n"+CPL+EL)
|
||||
}
|
||||
|
||||
// withTerminal runs f with the terminal input and output files, if available.
|
||||
// withTerminal does not open a non-terminal stdin, so the caller does not need
|
||||
// to check stdinInUse.
|
||||
func withTerminal(f func(in, out *os.File) error) error {
|
||||
if runtime.GOOS == "windows" {
|
||||
in, err := os.OpenFile("CONIN$", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
out, err := os.OpenFile("CONOUT$", os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
return f(in, out)
|
||||
} else if tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0); err == nil {
|
||||
defer tty.Close()
|
||||
return f(tty, tty)
|
||||
} else if term.IsTerminal(int(os.Stdin.Fd())) {
|
||||
return f(os.Stdin, os.Stdin)
|
||||
} else {
|
||||
return fmt.Errorf("standard input is not a terminal, and /dev/tty is not available: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// readSecret reads a value from the terminal with no echo. The prompt is ephemeral.
|
||||
func readSecret(prompt string) (s []byte, err error) {
|
||||
if testing.Testing() {
|
||||
if testOnlyAgePassword != "" {
|
||||
return []byte(testOnlyAgePassword), nil
|
||||
}
|
||||
}
|
||||
|
||||
err = withTerminal(func(in, out *os.File) error {
|
||||
fmt.Fprintf(out, "%s ", prompt)
|
||||
defer clearLine(out)
|
||||
s, err = term.ReadPassword(int(in.Fd()))
|
||||
return err
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// readCharacter reads a single character from the terminal with no echo. The
|
||||
// prompt is ephemeral.
|
||||
func readCharacter(prompt string) (c byte, err error) {
|
||||
err = withTerminal(func(in, out *os.File) error {
|
||||
fmt.Fprintf(out, "%s ", prompt)
|
||||
defer clearLine(out)
|
||||
|
||||
oldState, err := term.MakeRaw(int(in.Fd()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer term.Restore(int(in.Fd()), oldState)
|
||||
|
||||
b := make([]byte, 1)
|
||||
if _, err := in.Read(b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c = b[0]
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var pluginTerminalUI = &plugin.ClientUI{
|
||||
DisplayMessage: func(name, message string) error {
|
||||
printf("%s plugin: %s", name, message)
|
||||
return nil
|
||||
},
|
||||
RequestValue: func(name, message string, _ bool) (s string, err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
warningf("could not read value for age-plugin-%s: %v", name, err)
|
||||
}
|
||||
}()
|
||||
secret, err := readSecret(message)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(secret), nil
|
||||
},
|
||||
Confirm: func(name, message, yes, no string) (choseYes bool, err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
warningf("could not read value for age-plugin-%s: %v", name, err)
|
||||
}
|
||||
}()
|
||||
if no == "" {
|
||||
message += fmt.Sprintf(" (press enter for %q)", yes)
|
||||
_, err := readSecret(message)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
message += fmt.Sprintf(" (press [1] for %q or [2] for %q)", yes, no)
|
||||
for {
|
||||
selection, err := readCharacter(message)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
switch selection {
|
||||
case '1':
|
||||
return true, nil
|
||||
case '2':
|
||||
return false, nil
|
||||
case '\x03': // CTRL-C
|
||||
return false, errors.New("user cancelled prompt")
|
||||
default:
|
||||
warningf("reading value for age-plugin-%s: invalid selection %q", name, selection)
|
||||
}
|
||||
}
|
||||
},
|
||||
WaitTimer: func(name string) {
|
||||
printf("waiting on %s plugin...", name)
|
||||
},
|
||||
}
|
||||
@@ -76,9 +76,10 @@ func NewMasterKey(vaultURL string, keyName string, keyVersion string) *MasterKey
|
||||
// NewMasterKeyFromURL takes an Azure Key Vault key URL, and returns a new
|
||||
// MasterKey. The URL format is {vaultUrl}/keys/{keyName}/{keyVersion}.
|
||||
func NewMasterKeyFromURL(url string) (*MasterKey, error) {
|
||||
url = strings.TrimSpace(url)
|
||||
re := regexp.MustCompile("^(https://[^/]+)/keys/([^/]+)/([^/]+)$")
|
||||
parts := re.FindStringSubmatch(url)
|
||||
if parts == nil || len(parts) < 3 {
|
||||
if len(parts) < 3 {
|
||||
return nil, fmt.Errorf("could not parse %q into a valid Azure Key Vault MasterKey", url)
|
||||
}
|
||||
return NewMasterKey(parts[1], parts[2], parts[3]), nil
|
||||
|
||||
@@ -88,6 +88,23 @@ func TestMasterKeysFromURLs(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple URLs with leading and trailing spaces",
|
||||
urls: " https://test.vault.azure.net/keys/test-key/a2a690a4fcc04166b739da342a912c90 , https://test2.vault.azure.net/keys/another-test-key/cf0021e8b743453bae758e7fbf71b60e ",
|
||||
expectKeyCount: 2,
|
||||
expectKeys: []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: "multiple URLs, one malformed",
|
||||
urls: "https://test.vault.azure.net/keys/test-key/a2a690a4fcc04166b739da342a912c90,https://test.vault.azure.net/no-keys-here/test-key/a2a690a4fcc04166b739da342a912c90",
|
||||
|
||||
@@ -2,6 +2,7 @@ package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
@@ -130,11 +131,20 @@ func EncryptTree(opts EncryptTreeOpts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadEncryptedFile loads an encrypted SOPS file, returning a SOPS tree
|
||||
func LoadEncryptedFile(loader sops.EncryptedFileLoader, inputPath string) (*sops.Tree, error) {
|
||||
fileBytes, err := os.ReadFile(inputPath)
|
||||
if err != nil {
|
||||
return nil, NewExitError(fmt.Sprintf("Error reading file: %s", err), codes.CouldNotReadInputFile)
|
||||
// LoadEncryptedFileEx loads an encrypted SOPS file from a file or stdin, returning a SOPS tree
|
||||
func LoadEncryptedFileEx(loader sops.EncryptedFileLoader, inputPath string, readFromStdin bool) (*sops.Tree, error) {
|
||||
var fileBytes []byte
|
||||
var err error
|
||||
if readFromStdin {
|
||||
fileBytes, err = io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
return nil, NewExitError(fmt.Sprintf("Error reading from stdin: %s", err), codes.CouldNotReadInputFile)
|
||||
}
|
||||
} else {
|
||||
fileBytes, err = os.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 {
|
||||
@@ -145,6 +155,11 @@ func LoadEncryptedFile(loader sops.EncryptedFileLoader, inputPath string) (*sops
|
||||
return &tree, err
|
||||
}
|
||||
|
||||
// LoadEncryptedFile loads an encrypted SOPS file, returning a SOPS tree
|
||||
func LoadEncryptedFile(loader sops.EncryptedFileLoader, inputPath string) (*sops.Tree, error) {
|
||||
return LoadEncryptedFileEx(loader, inputPath, false)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@@ -207,7 +222,7 @@ func GetKMSKeyWithEncryptionCtx(tree *sops.Tree) (keyGroupIndex int, keyIndex in
|
||||
for n, k := range kg {
|
||||
kmsKey, ok := k.(*kms.MasterKey)
|
||||
if ok {
|
||||
if kmsKey.EncryptionContext != nil && len(kmsKey.EncryptionContext) >= 2 {
|
||||
if len(kmsKey.EncryptionContext) >= 2 {
|
||||
duplicateValues := map[string]int{}
|
||||
for _, v := range kmsKey.EncryptionContext {
|
||||
duplicateValues[*v] = duplicateValues[*v] + 1
|
||||
@@ -227,6 +242,7 @@ type GenericDecryptOpts struct {
|
||||
Cipher sops.Cipher
|
||||
InputStore sops.Store
|
||||
InputPath string
|
||||
ReadFromStdin bool
|
||||
IgnoreMAC bool
|
||||
KeyServices []keyservice.KeyServiceClient
|
||||
DecryptionOrder []string
|
||||
@@ -235,7 +251,7 @@ type GenericDecryptOpts struct {
|
||||
// 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)
|
||||
tree, err := LoadEncryptedFileEx(opts.InputStore, opts.InputPath, opts.ReadFromStdin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -447,3 +463,17 @@ func PrettyPrintDiffs(diffs []Diff) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PrettyPrintShamirDiff prints changes in shamir_threshold to stdout
|
||||
func PrettyPrintShamirDiff(oldValue, newValue int) {
|
||||
if oldValue > 0 && oldValue == newValue {
|
||||
fmt.Printf("shamir_threshold: %d\n", newValue)
|
||||
} else {
|
||||
if newValue > 0 {
|
||||
color.New(color.FgGreen).Printf("+++ shamir_threshold: %d\n", newValue)
|
||||
}
|
||||
if oldValue > 0 {
|
||||
color.New(color.FgRed).Printf("--- shamir_threshold: %d\n", oldValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ type decryptOpts struct {
|
||||
InputStore sops.Store
|
||||
OutputStore sops.Store
|
||||
InputPath string
|
||||
ReadFromStdin bool
|
||||
IgnoreMAC bool
|
||||
Extract []interface{}
|
||||
KeyServices []keyservice.KeyServiceClient
|
||||
@@ -27,11 +28,12 @@ type decryptOpts struct {
|
||||
|
||||
func decryptTree(opts decryptOpts) (tree *sops.Tree, err error) {
|
||||
tree, err = common.LoadEncryptedFileWithBugFixes(common.GenericDecryptOpts{
|
||||
Cipher: opts.Cipher,
|
||||
InputStore: opts.InputStore,
|
||||
InputPath: opts.InputPath,
|
||||
IgnoreMAC: opts.IgnoreMAC,
|
||||
KeyServices: opts.KeyServices,
|
||||
Cipher: opts.Cipher,
|
||||
InputStore: opts.InputStore,
|
||||
InputPath: opts.InputPath,
|
||||
ReadFromStdin: opts.ReadFromStdin,
|
||||
IgnoreMAC: opts.IgnoreMAC,
|
||||
KeyServices: opts.KeyServices,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -245,7 +245,12 @@ func hashFile(filePath string) ([]byte, error) {
|
||||
}
|
||||
|
||||
func runEditor(path string) error {
|
||||
editor := os.Getenv("EDITOR")
|
||||
envVar := "SOPS_EDITOR"
|
||||
editor := os.Getenv(envVar)
|
||||
if editor == "" {
|
||||
envVar = "EDITOR"
|
||||
editor = os.Getenv(envVar)
|
||||
}
|
||||
var cmd *exec.Cmd
|
||||
if editor == "" {
|
||||
editor, err := lookupAnyEditor("vim", "nano", "vi")
|
||||
@@ -256,7 +261,7 @@ func runEditor(path string) error {
|
||||
} else {
|
||||
parts, err := shlex.Split(editor)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid $EDITOR: %s", editor)
|
||||
return fmt.Errorf("invalid $%s: %s", envVar, editor)
|
||||
}
|
||||
parts = append(parts, path)
|
||||
cmd = exec.Command(parts[0], parts[1:]...)
|
||||
@@ -275,5 +280,5 @@ func lookupAnyEditor(editorNames ...string) (editorPath string, err error) {
|
||||
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, ", "))
|
||||
return "", fmt.Errorf("no editor available: sops attempts to use the editor defined in the SOPS_EDITOR or EDITOR environment variables, and if that's not set defaults to any of %s, but none of them could be found", strings.Join(editorNames, ", "))
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -27,11 +28,12 @@ type encryptConfig struct {
|
||||
}
|
||||
|
||||
type encryptOpts struct {
|
||||
Cipher sops.Cipher
|
||||
InputStore sops.Store
|
||||
OutputStore sops.Store
|
||||
InputPath string
|
||||
KeyServices []keyservice.KeyServiceClient
|
||||
Cipher sops.Cipher
|
||||
InputStore sops.Store
|
||||
OutputStore sops.Store
|
||||
InputPath string
|
||||
ReadFromStdin bool
|
||||
KeyServices []keyservice.KeyServiceClient
|
||||
encryptConfig
|
||||
}
|
||||
|
||||
@@ -78,9 +80,17 @@ func metadataFromEncryptionConfig(config encryptConfig) sops.Metadata {
|
||||
|
||||
func encrypt(opts encryptOpts) (encryptedFile []byte, err error) {
|
||||
// Load the file
|
||||
fileBytes, err := os.ReadFile(opts.InputPath)
|
||||
if err != nil {
|
||||
return nil, common.NewExitError(fmt.Sprintf("Error reading file: %s", err), codes.CouldNotReadInputFile)
|
||||
var fileBytes []byte
|
||||
if opts.ReadFromStdin {
|
||||
fileBytes, err = io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
return nil, common.NewExitError(fmt.Sprintf("Error reading from stdin: %s", err), codes.CouldNotReadInputFile)
|
||||
}
|
||||
} else {
|
||||
fileBytes, err = os.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 {
|
||||
|
||||
322
cmd/sops/main.go
322
cmd/sops/main.go
@@ -137,7 +137,8 @@ func main() {
|
||||
|
||||
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.
|
||||
To select a different editor than the default (vim), set SOPS_EDITOR or
|
||||
EDITOR.
|
||||
|
||||
Note that flags must always be provided before the filename to operate on.
|
||||
Otherwise, they will be ignored.
|
||||
@@ -162,6 +163,10 @@ func main() {
|
||||
Name: "user",
|
||||
Usage: "the user to run the command as",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "same-process",
|
||||
Usage: "run command in the current process instead of in a child process",
|
||||
},
|
||||
}, keyserviceFlags...),
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.NArg() != 2 {
|
||||
@@ -171,7 +176,10 @@ func main() {
|
||||
fileName := c.Args()[0]
|
||||
command := c.Args()[1]
|
||||
|
||||
inputStore := inputStore(c, fileName)
|
||||
inputStore, err := inputStore(c, fileName)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
|
||||
svcs := keyservices(c)
|
||||
|
||||
@@ -191,6 +199,10 @@ func main() {
|
||||
|
||||
if c.Bool("background") {
|
||||
log.Warn("exec-env's --background option is deprecated and will be removed in a future version of sops")
|
||||
|
||||
if c.Bool("same-process") {
|
||||
return common.NewExitError("Error: The --same-process flag cannot be used with --background", codes.ErrorConflictingParameters)
|
||||
}
|
||||
}
|
||||
|
||||
tree, err := decryptTree(opts)
|
||||
@@ -221,12 +233,13 @@ func main() {
|
||||
}
|
||||
|
||||
if err := exec.ExecWithEnv(exec.ExecOpts{
|
||||
Command: command,
|
||||
Plaintext: []byte{},
|
||||
Background: c.Bool("background"),
|
||||
Pristine: c.Bool("pristine"),
|
||||
User: c.String("user"),
|
||||
Env: env,
|
||||
Command: command,
|
||||
Plaintext: []byte{},
|
||||
Background: c.Bool("background"),
|
||||
Pristine: c.Bool("pristine"),
|
||||
User: c.String("user"),
|
||||
SameProcess: c.Bool("same-process"),
|
||||
Env: env,
|
||||
}); err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
@@ -272,8 +285,14 @@ func main() {
|
||||
fileName := c.Args()[0]
|
||||
command := c.Args()[1]
|
||||
|
||||
inputStore := inputStore(c, fileName)
|
||||
outputStore := outputStore(c, fileName)
|
||||
inputStore, err := inputStore(c, fileName)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
outputStore, err := outputStore(c, fileName)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
|
||||
svcs := keyservices(c)
|
||||
|
||||
@@ -340,9 +359,15 @@ func main() {
|
||||
if c.Bool("verbose") || c.GlobalBool("verbose") {
|
||||
logging.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
configPath, err := config.FindConfigFile(".")
|
||||
if err != nil {
|
||||
return common.NewExitError(err, codes.ErrorGeneric)
|
||||
var configPath string
|
||||
var err error
|
||||
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)
|
||||
@@ -365,13 +390,17 @@ func main() {
|
||||
return toExitError(err)
|
||||
}
|
||||
if !info.IsDir() {
|
||||
inputStore, err := inputStore(c, subPath)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
err = publishcmd.Run(publishcmd.Opts{
|
||||
ConfigPath: configPath,
|
||||
InputPath: subPath,
|
||||
Cipher: aes.NewCipher(),
|
||||
KeyServices: keyservices(c),
|
||||
DecryptionOrder: order,
|
||||
InputStore: inputStore(c, subPath),
|
||||
InputStore: inputStore,
|
||||
Interactive: !c.Bool("yes"),
|
||||
OmitExtensions: c.Bool("omit-extensions"),
|
||||
Recursive: c.Bool("recursive"),
|
||||
@@ -433,14 +462,22 @@ func main() {
|
||||
Name: "filestatus",
|
||||
Usage: "check the status of the file, returning encryption status",
|
||||
ArgsUsage: `file`,
|
||||
Flags: []cli.Flag{},
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "input-type",
|
||||
Usage: "currently ini, json, yaml, dotenv and binary are supported. If not set, sops will use the file's extension to determine the type",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.NArg() < 1 {
|
||||
return common.NewExitError("Error: no file specified", codes.NoFileSpecified)
|
||||
}
|
||||
|
||||
fileName := c.Args()[0]
|
||||
inputStore := inputStore(c, fileName)
|
||||
inputStore, err := inputStore(c, fileName)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
opts := filestatuscmd.Opts{
|
||||
InputStore: inputStore,
|
||||
InputPath: fileName,
|
||||
@@ -560,11 +597,19 @@ func main() {
|
||||
group = append(group, key)
|
||||
}
|
||||
}
|
||||
inputStore, err := inputStore(c, c.String("file"))
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
outputStore, err := outputStore(c, c.String("file"))
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
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")),
|
||||
InputStore: inputStore,
|
||||
OutputStore: outputStore,
|
||||
Group: group,
|
||||
GroupThreshold: c.Int("shamir-secret-sharing-threshold"),
|
||||
KeyServices: keyservices(c),
|
||||
@@ -599,11 +644,19 @@ func main() {
|
||||
return fmt.Errorf("failed to parse [index] argument: %s", err)
|
||||
}
|
||||
|
||||
inputStore, err := inputStore(c, c.String("file"))
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
outputStore, err := outputStore(c, c.String("file"))
|
||||
if err != nil {
|
||||
return toExitError(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")),
|
||||
InputStore: inputStore,
|
||||
OutputStore: outputStore,
|
||||
Group: uint(group),
|
||||
GroupThreshold: c.Int("shamir-secret-sharing-threshold"),
|
||||
KeyServices: keyservices(c),
|
||||
@@ -643,12 +696,12 @@ func main() {
|
||||
failedCounter := 0
|
||||
for _, path := range c.Args() {
|
||||
err := updatekeys.UpdateKeys(updatekeys.Opts{
|
||||
InputPath: path,
|
||||
GroupQuorum: c.Int("shamir-secret-sharing-quorum"),
|
||||
KeyServices: keyservices(c),
|
||||
Interactive: !c.Bool("yes"),
|
||||
ConfigPath: configPath,
|
||||
InputType: c.String("input-type"),
|
||||
InputPath: path,
|
||||
ShamirThreshold: c.Int("shamir-secret-sharing-threshold"),
|
||||
KeyServices: keyservices(c),
|
||||
Interactive: !c.Bool("yes"),
|
||||
ConfigPath: configPath,
|
||||
InputType: c.String("input-type"),
|
||||
})
|
||||
|
||||
if c.NArg() == 1 {
|
||||
@@ -675,8 +728,8 @@ func main() {
|
||||
},
|
||||
{
|
||||
Name: "decrypt",
|
||||
Usage: "decrypt a file, and output the results to stdout",
|
||||
ArgsUsage: `file`,
|
||||
Usage: "decrypt a file, and output the results to stdout. If no filename is provided, stdin will be used.",
|
||||
ArgsUsage: `[file]`,
|
||||
Flags: append([]cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "in-place, i",
|
||||
@@ -704,7 +757,7 @@ func main() {
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "filename-override",
|
||||
Usage: "Use this filename instead of the provided argument for loading configuration, and for determining input type and output type",
|
||||
Usage: "Use this filename instead of the provided argument for loading configuration, and for determining input type and output type. Should be provided when reading from stdin.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "decryption-order",
|
||||
@@ -716,27 +769,43 @@ func main() {
|
||||
if c.Bool("verbose") {
|
||||
logging.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
if c.NArg() < 1 {
|
||||
return common.NewExitError("Error: no file specified", codes.NoFileSpecified)
|
||||
readFromStdin := c.NArg() == 0
|
||||
if readFromStdin && c.Bool("in-place") {
|
||||
return common.NewExitError("Error: cannot use --in-place when reading from stdin", codes.ErrorConflictingParameters)
|
||||
}
|
||||
warnMoreThanOnePositionalArgument(c)
|
||||
if c.Bool("in-place") && c.String("output") != "" {
|
||||
return common.NewExitError("Error: cannot operate on both --output and --in-place", codes.ErrorConflictingParameters)
|
||||
}
|
||||
fileName, err := filepath.Abs(c.Args()[0])
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
if _, err := os.Stat(fileName); os.IsNotExist(err) {
|
||||
return common.NewExitError("Error: cannot operate on non-existent file", codes.NoFileSpecified)
|
||||
var fileName string
|
||||
var err error
|
||||
if !readFromStdin {
|
||||
fileName, err = filepath.Abs(c.Args()[0])
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
if _, err := os.Stat(fileName); os.IsNotExist(err) {
|
||||
return common.NewExitError(fmt.Sprintf("Error: cannot operate on non-existent file %q", fileName), codes.NoFileSpecified)
|
||||
}
|
||||
}
|
||||
fileNameOverride := c.String("filename-override")
|
||||
if fileNameOverride == "" {
|
||||
fileNameOverride = fileName
|
||||
} else {
|
||||
fileNameOverride, err = filepath.Abs(fileNameOverride)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
}
|
||||
|
||||
inputStore := inputStore(c, fileNameOverride)
|
||||
outputStore := outputStore(c, fileNameOverride)
|
||||
inputStore, err := inputStore(c, fileNameOverride)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
outputStore, err := outputStore(c, fileNameOverride)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
svcs := keyservices(c)
|
||||
|
||||
order, err := decryptionOrder(c.String("decryption-order"))
|
||||
@@ -753,6 +822,7 @@ func main() {
|
||||
OutputStore: outputStore,
|
||||
InputStore: inputStore,
|
||||
InputPath: fileName,
|
||||
ReadFromStdin: readFromStdin,
|
||||
Cipher: aes.NewCipher(),
|
||||
Extract: extract,
|
||||
KeyServices: svcs,
|
||||
@@ -794,8 +864,8 @@ func main() {
|
||||
},
|
||||
{
|
||||
Name: "encrypt",
|
||||
Usage: "encrypt a file, and output the results to stdout",
|
||||
ArgsUsage: `file`,
|
||||
Usage: "encrypt a file, and output the results to stdout. If no filename is provided, stdin will be used.",
|
||||
ArgsUsage: `[file]`,
|
||||
Flags: append([]cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "in-place, i",
|
||||
@@ -873,34 +943,55 @@ func main() {
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "filename-override",
|
||||
Usage: "Use this filename instead of the provided argument for loading configuration, and for determining input type and output type",
|
||||
Usage: "Use this filename instead of the provided argument for loading configuration, and for determining input type and output type. Required when reading from stdin.",
|
||||
},
|
||||
}, keyserviceFlags...),
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.Bool("verbose") {
|
||||
logging.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
if c.NArg() < 1 {
|
||||
return common.NewExitError("Error: no file specified", codes.NoFileSpecified)
|
||||
readFromStdin := c.NArg() == 0
|
||||
if readFromStdin {
|
||||
if c.Bool("in-place") {
|
||||
return common.NewExitError("Error: cannot use --in-place when reading from stdin", codes.ErrorConflictingParameters)
|
||||
}
|
||||
if c.String("filename-override") == "" {
|
||||
return common.NewExitError("Error: must specify --filename-override when reading from stdin", codes.ErrorConflictingParameters)
|
||||
}
|
||||
}
|
||||
warnMoreThanOnePositionalArgument(c)
|
||||
if c.Bool("in-place") && c.String("output") != "" {
|
||||
return common.NewExitError("Error: cannot operate on both --output and --in-place", codes.ErrorConflictingParameters)
|
||||
}
|
||||
fileName, err := filepath.Abs(c.Args()[0])
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
if _, err := os.Stat(fileName); os.IsNotExist(err) {
|
||||
return common.NewExitError("Error: cannot operate on non-existent file", codes.NoFileSpecified)
|
||||
var fileName string
|
||||
var err error
|
||||
if !readFromStdin {
|
||||
fileName, err = filepath.Abs(c.Args()[0])
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
if _, err := os.Stat(fileName); os.IsNotExist(err) {
|
||||
return common.NewExitError(fmt.Sprintf("Error: cannot operate on non-existent file %q", fileName), codes.NoFileSpecified)
|
||||
}
|
||||
}
|
||||
fileNameOverride := c.String("filename-override")
|
||||
if fileNameOverride == "" {
|
||||
fileNameOverride = fileName
|
||||
} else {
|
||||
fileNameOverride, err = filepath.Abs(fileNameOverride)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
}
|
||||
|
||||
inputStore := inputStore(c, fileNameOverride)
|
||||
outputStore := outputStore(c, fileNameOverride)
|
||||
inputStore, err := inputStore(c, fileNameOverride)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
outputStore, err := outputStore(c, fileNameOverride)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
svcs := keyservices(c)
|
||||
|
||||
encConfig, err := getEncryptConfig(c, fileNameOverride)
|
||||
@@ -911,6 +1002,7 @@ func main() {
|
||||
OutputStore: outputStore,
|
||||
InputStore: inputStore,
|
||||
InputPath: fileName,
|
||||
ReadFromStdin: readFromStdin,
|
||||
Cipher: aes.NewCipher(),
|
||||
KeyServices: svcs,
|
||||
encryptConfig: encConfig,
|
||||
@@ -1050,16 +1142,27 @@ func main() {
|
||||
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 the `edit` subcommand instead.", codes.CannotChangeKeysFromNonExistentFile)
|
||||
return common.NewExitError(fmt.Sprintf("Error: cannot add or remove keys on non-existent file %q, use the `edit` subcommand instead.", fileName), codes.CannotChangeKeysFromNonExistentFile)
|
||||
}
|
||||
}
|
||||
fileNameOverride := c.String("filename-override")
|
||||
if fileNameOverride == "" {
|
||||
fileNameOverride = fileName
|
||||
} else {
|
||||
fileNameOverride, err = filepath.Abs(fileNameOverride)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
}
|
||||
|
||||
inputStore := inputStore(c, fileNameOverride)
|
||||
outputStore := outputStore(c, fileNameOverride)
|
||||
inputStore, err := inputStore(c, fileNameOverride)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
outputStore, err := outputStore(c, fileNameOverride)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
svcs := keyservices(c)
|
||||
|
||||
order, err := decryptionOrder(c.String("decryption-order"))
|
||||
@@ -1202,12 +1305,15 @@ func main() {
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
if _, err := os.Stat(fileName); os.IsNotExist(err) {
|
||||
return common.NewExitError("Error: cannot operate on non-existent file", codes.NoFileSpecified)
|
||||
}
|
||||
|
||||
inputStore := inputStore(c, fileName)
|
||||
outputStore := outputStore(c, fileName)
|
||||
inputStore, err := inputStore(c, fileName)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
outputStore, err := outputStore(c, fileName)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
svcs := keyservices(c)
|
||||
|
||||
order, err := decryptionOrder(c.String("decryption-order"))
|
||||
@@ -1288,6 +1394,10 @@ func main() {
|
||||
Usage: "comma separated list of decryption key types",
|
||||
EnvVar: "SOPS_DECRYPTION_ORDER",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "idempotent",
|
||||
Usage: "do nothing if the given index already has the given value",
|
||||
},
|
||||
}, keyserviceFlags...),
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.Bool("verbose") {
|
||||
@@ -1301,8 +1411,14 @@ func main() {
|
||||
return toExitError(err)
|
||||
}
|
||||
|
||||
inputStore := inputStore(c, fileName)
|
||||
outputStore := outputStore(c, fileName)
|
||||
inputStore, err := inputStore(c, fileName)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
outputStore, err := outputStore(c, fileName)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
svcs := keyservices(c)
|
||||
|
||||
path, err := parseTreePath(c.Args()[1])
|
||||
@@ -1319,7 +1435,7 @@ func main() {
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
output, err := set(setOpts{
|
||||
output, changed, err := set(setOpts{
|
||||
OutputStore: outputStore,
|
||||
InputStore: inputStore,
|
||||
InputPath: fileName,
|
||||
@@ -1334,6 +1450,11 @@ func main() {
|
||||
return toExitError(err)
|
||||
}
|
||||
|
||||
if !changed && c.Bool("idempotent") {
|
||||
log.Info("File not written due to no change")
|
||||
return nil
|
||||
}
|
||||
|
||||
// We open the file *after* the operations on the tree have been
|
||||
// executed to avoid truncating it when there's errors
|
||||
file, err := os.Create(fileName)
|
||||
@@ -1376,8 +1497,8 @@ func main() {
|
||||
EnvVar: "SOPS_DECRYPTION_ORDER",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "idempotent",
|
||||
Usage: "do nothing if the given index does not exist",
|
||||
Name: "idempotent",
|
||||
Usage: "do nothing if the given index does not exist",
|
||||
},
|
||||
}, keyserviceFlags...),
|
||||
Action: func(c *cli.Context) error {
|
||||
@@ -1392,8 +1513,14 @@ func main() {
|
||||
return toExitError(err)
|
||||
}
|
||||
|
||||
inputStore := inputStore(c, fileName)
|
||||
outputStore := outputStore(c, fileName)
|
||||
inputStore, err := inputStore(c, fileName)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
outputStore, err := outputStore(c, fileName)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
svcs := keyservices(c)
|
||||
|
||||
path, err := parseTreePath(c.Args()[1])
|
||||
@@ -1452,8 +1579,9 @@ func main() {
|
||||
Usage: "generate a new data encryption key and reencrypt all values with the new key",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "disable-version-check",
|
||||
Usage: "do not check whether the current version is latest during --version",
|
||||
Name: "disable-version-check",
|
||||
Usage: "do not check whether the current version is latest during --version",
|
||||
EnvVar: "SOPS_DISABLE_VERSION_CHECK",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "kms, k",
|
||||
@@ -1592,6 +1720,7 @@ func main() {
|
||||
cli.StringFlag{
|
||||
Name: "config",
|
||||
Usage: "path to sops' config file. If set, sops will not search for the config file recursively.",
|
||||
EnvVar: "SOPS_CONFIG",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "encryption-context",
|
||||
@@ -1652,15 +1781,20 @@ func main() {
|
||||
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)
|
||||
return common.NewExitError(fmt.Sprintf("Error: cannot add or remove keys on non-existent file %q, use `--kms` and `--pgp` instead.", fileName), codes.CannotChangeKeysFromNonExistentFile)
|
||||
}
|
||||
if isEncryptMode || isDecryptMode || isRotateMode {
|
||||
return common.NewExitError("Error: cannot operate on non-existent file", codes.NoFileSpecified)
|
||||
return common.NewExitError(fmt.Sprintf("Error: cannot operate on non-existent file %q", fileName), codes.NoFileSpecified)
|
||||
}
|
||||
}
|
||||
fileNameOverride := c.String("filename-override")
|
||||
if fileNameOverride == "" {
|
||||
fileNameOverride = fileName
|
||||
} else {
|
||||
fileNameOverride, err = filepath.Abs(fileNameOverride)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
}
|
||||
|
||||
commandCount := 0
|
||||
@@ -1690,8 +1824,14 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
inputStore := inputStore(c, fileNameOverride)
|
||||
outputStore := outputStore(c, fileNameOverride)
|
||||
inputStore, err := inputStore(c, fileNameOverride)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
outputStore, err := outputStore(c, fileNameOverride)
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
svcs := keyservices(c)
|
||||
|
||||
order, err := decryptionOrder(c.String("decryption-order"))
|
||||
@@ -1758,7 +1898,7 @@ func main() {
|
||||
if err != nil {
|
||||
return toExitError(err)
|
||||
}
|
||||
output, err = set(setOpts{
|
||||
output, _, err = set(setOpts{
|
||||
OutputStore: outputStore,
|
||||
InputStore: inputStore,
|
||||
InputPath: fileName,
|
||||
@@ -2030,7 +2170,7 @@ func keyservices(c *cli.Context) (svcs []keyservice.KeyServiceClient) {
|
||||
"address",
|
||||
fmt.Sprintf("%s://%s", url.Scheme, addr),
|
||||
).Infof("Connecting to key service")
|
||||
conn, err := grpc.Dial(addr, opts...)
|
||||
conn, err := grpc.NewClient(addr, opts...)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to listen: %v", err)
|
||||
}
|
||||
@@ -2040,10 +2180,8 @@ func keyservices(c *cli.Context) (svcs []keyservice.KeyServiceClient) {
|
||||
}
|
||||
|
||||
func loadStoresConfig(context *cli.Context, path string) (*config.StoresConfig, error) {
|
||||
var configPath string
|
||||
if context.String("config") != "" {
|
||||
configPath = context.String("config")
|
||||
} else {
|
||||
configPath := context.GlobalString("config")
|
||||
if configPath == "" {
|
||||
// Ignore config not found errors returned from FindConfigFile since the config file is not mandatory
|
||||
foundPath, err := config.FindConfigFile(".")
|
||||
if err != nil {
|
||||
@@ -2054,13 +2192,19 @@ func loadStoresConfig(context *cli.Context, path string) (*config.StoresConfig,
|
||||
return config.LoadStoresConfig(configPath)
|
||||
}
|
||||
|
||||
func inputStore(context *cli.Context, path string) common.Store {
|
||||
storesConf, _ := loadStoresConfig(context, path)
|
||||
return common.DefaultStoreForPathOrFormat(storesConf, path, context.String("input-type"))
|
||||
func inputStore(context *cli.Context, path string) (common.Store, error) {
|
||||
storesConf, err := loadStoresConfig(context, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return common.DefaultStoreForPathOrFormat(storesConf, path, context.String("input-type")), nil
|
||||
}
|
||||
|
||||
func outputStore(context *cli.Context, path string) common.Store {
|
||||
storesConf, _ := loadStoresConfig(context, path)
|
||||
func outputStore(context *cli.Context, path string) (common.Store, error) {
|
||||
storesConf, err := loadStoresConfig(context, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if context.IsSet("indent") {
|
||||
indent := context.Int("indent")
|
||||
storesConf.YAML.Indent = indent
|
||||
@@ -2068,7 +2212,7 @@ func outputStore(context *cli.Context, path string) common.Store {
|
||||
storesConf.JSONBinary.Indent = indent
|
||||
}
|
||||
|
||||
return common.DefaultStoreForPathOrFormat(storesConf, path, context.String("output-type"))
|
||||
return common.DefaultStoreForPathOrFormat(storesConf, path, context.String("output-type")), nil
|
||||
}
|
||||
|
||||
func parseTreePath(arg string) ([]interface{}, error) {
|
||||
@@ -2159,7 +2303,7 @@ func keyGroups(c *cli.Context, file string) ([]sops.KeyGroup, error) {
|
||||
if err != nil {
|
||||
errMsg = fmt.Sprintf("%s: %s", errMsg, err)
|
||||
}
|
||||
return nil, fmt.Errorf(errMsg)
|
||||
return nil, fmt.Errorf("%s", errMsg)
|
||||
}
|
||||
return conf.KeyGroups, err
|
||||
}
|
||||
@@ -2178,10 +2322,8 @@ func keyGroups(c *cli.Context, file string) ([]sops.KeyGroup, error) {
|
||||
// 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 {
|
||||
configPath := c.GlobalString("config")
|
||||
if configPath == "" {
|
||||
// Ignore config not found errors returned from FindConfigFile since the config file is not mandatory
|
||||
configPath, err = config.FindConfigFile(".")
|
||||
if err != nil {
|
||||
|
||||
@@ -21,7 +21,7 @@ type setOpts struct {
|
||||
DecryptionOrder []string
|
||||
}
|
||||
|
||||
func set(opts setOpts) ([]byte, error) {
|
||||
func set(opts setOpts) ([]byte, bool, 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{
|
||||
@@ -32,7 +32,7 @@ func set(opts setOpts) ([]byte, error) {
|
||||
KeyServices: opts.KeyServices,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// Decrypt the file
|
||||
@@ -44,22 +44,23 @@ func set(opts setOpts) ([]byte, error) {
|
||||
DecryptionOrder: opts.DecryptionOrder,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// Set the value
|
||||
tree.Branches[0] = tree.Branches[0].Set(opts.TreePath, opts.Value)
|
||||
var changed bool
|
||||
tree.Branches[0], changed = 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
|
||||
return nil, false, 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 nil, false, common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree)
|
||||
}
|
||||
return encryptedFile, err
|
||||
return encryptedFile, changed, err
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package exec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -23,14 +24,15 @@ func init() {
|
||||
}
|
||||
|
||||
type ExecOpts struct {
|
||||
Command string
|
||||
Plaintext []byte
|
||||
Background bool
|
||||
Pristine bool
|
||||
Fifo bool
|
||||
User string
|
||||
Filename string
|
||||
Env []string
|
||||
Command string
|
||||
Plaintext []byte
|
||||
Background bool
|
||||
SameProcess bool
|
||||
Pristine bool
|
||||
Fifo bool
|
||||
User string
|
||||
Filename string
|
||||
Env []string
|
||||
}
|
||||
|
||||
func GetFile(dir, filename string) *os.File {
|
||||
@@ -115,6 +117,10 @@ func ExecWithEnv(opts ExecOpts) error {
|
||||
SwitchUser(opts.User)
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" && opts.SameProcess {
|
||||
return fmt.Errorf("The --same-process flag is not supported on Windows")
|
||||
}
|
||||
|
||||
var env []string
|
||||
|
||||
if !opts.Pristine {
|
||||
@@ -134,6 +140,15 @@ func ExecWithEnv(opts ExecOpts) error {
|
||||
|
||||
env = append(env, opts.Env...)
|
||||
|
||||
if opts.SameProcess {
|
||||
if opts.Background {
|
||||
log.Fatal("background is not supported for same-process")
|
||||
}
|
||||
|
||||
// Note that the call does NOT return, unless an error happens.
|
||||
return ExecSyscall(opts.Command, env)
|
||||
}
|
||||
|
||||
cmd := BuildCommand(opts.Command)
|
||||
cmd.Env = env
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func ExecSyscall(command string, env []string) error {
|
||||
return syscall.Exec("/bin/sh", []string{"/bin/sh", "-c", command}, env)
|
||||
}
|
||||
|
||||
func BuildCommand(command string) *exec.Cmd {
|
||||
return exec.Command("/bin/sh", "-c", command)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,11 @@ import (
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func ExecSyscall(command string, env []string) error {
|
||||
log.Fatal("same-process not available on windows")
|
||||
return nil
|
||||
}
|
||||
|
||||
func BuildCommand(command string) *exec.Cmd {
|
||||
return exec.Command("cmd.exe", "/C", command)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
// Opts represents key operation options and config
|
||||
type Opts struct {
|
||||
InputPath string
|
||||
GroupQuorum int
|
||||
ShamirThreshold int
|
||||
KeyServices []keyservice.KeyServiceClient
|
||||
DecryptionOrder []string
|
||||
Interactive bool
|
||||
@@ -45,7 +45,7 @@ func updateFile(opts Opts) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
store := common.DefaultStoreForPath(sc, opts.InputPath)
|
||||
store := common.DefaultStoreForPathOrFormat(sc, opts.InputPath, opts.InputType)
|
||||
log.Printf("Syncing keys for file %s", opts.InputPath)
|
||||
tree, err := common.LoadEncryptedFile(store, opts.InputPath)
|
||||
if err != nil {
|
||||
@@ -66,11 +66,22 @@ func updateFile(opts Opts) error {
|
||||
keysWillChange = true
|
||||
}
|
||||
}
|
||||
if !keysWillChange {
|
||||
|
||||
// TODO: use conf.ShamirThreshold instead of tree.Metadata.ShamirThreshold in the next line?
|
||||
// Or make this configurable?
|
||||
var shamirThreshold = tree.Metadata.ShamirThreshold
|
||||
if opts.ShamirThreshold != 0 {
|
||||
shamirThreshold = opts.ShamirThreshold
|
||||
}
|
||||
shamirThreshold = min(shamirThreshold, len(conf.KeyGroups))
|
||||
var shamirThresholdWillChange = tree.Metadata.ShamirThreshold != shamirThreshold
|
||||
|
||||
if !keysWillChange && !shamirThresholdWillChange {
|
||||
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.PrettyPrintShamirDiff(tree.Metadata.ShamirThreshold, shamirThreshold)
|
||||
common.PrettyPrintDiffs(diffs)
|
||||
|
||||
if opts.Interactive {
|
||||
@@ -92,10 +103,7 @@ func updateFile(opts Opts) error {
|
||||
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))
|
||||
tree.Metadata.ShamirThreshold = shamirThreshold
|
||||
errs := tree.Metadata.UpdateMasterKeysWithKeyServices(key, opts.KeyServices)
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("error updating one or more master keys: %s", errs)
|
||||
|
||||
@@ -17,19 +17,11 @@ import (
|
||||
"github.com/getsops/sops/v3/gcpkms"
|
||||
"github.com/getsops/sops/v3/hcvault"
|
||||
"github.com/getsops/sops/v3/kms"
|
||||
"github.com/getsops/sops/v3/logging"
|
||||
"github.com/getsops/sops/v3/pgp"
|
||||
"github.com/getsops/sops/v3/publish"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var log *logrus.Logger
|
||||
|
||||
func init() {
|
||||
log = logging.NewLogger("CONFIG")
|
||||
}
|
||||
|
||||
type fileSystem interface {
|
||||
Stat(name string) (os.FileInfo, error)
|
||||
}
|
||||
@@ -377,19 +369,17 @@ func parseDestinationRuleForFile(conf *configFile, filePath string, kmsEncryptio
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/getsops/sops/v3/keys"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -94,6 +96,20 @@ creation_rules:
|
||||
- kms:
|
||||
- arn: foo
|
||||
aws_profile: bar
|
||||
- arn: foo
|
||||
context:
|
||||
baz: bam
|
||||
- arn: foo
|
||||
aws_profile: bar
|
||||
context:
|
||||
baz: bam
|
||||
- arn: foo
|
||||
role: '123'
|
||||
- arn: foo
|
||||
aws_profile: bar
|
||||
context:
|
||||
baz: bam
|
||||
role: '123'
|
||||
pgp:
|
||||
- bar
|
||||
gcp_kms:
|
||||
@@ -129,113 +145,124 @@ creation_rules:
|
||||
- 'https://foo.vault:8200/v1/foo/keys/foo-key'
|
||||
- merge:
|
||||
- merge:
|
||||
- kms:
|
||||
- pgp:
|
||||
# key01
|
||||
- foo
|
||||
kms:
|
||||
# key02
|
||||
- arn: foo
|
||||
aws_profile: foo
|
||||
pgp:
|
||||
# key02
|
||||
- foo
|
||||
gcp_kms:
|
||||
# key03
|
||||
- arn: foo
|
||||
aws_profile: bar
|
||||
context:
|
||||
baz: bam
|
||||
role: '123'
|
||||
gcp_kms:
|
||||
# key04
|
||||
- resource_id: foo
|
||||
azure_keyvault:
|
||||
# key04
|
||||
# key05
|
||||
- vaultUrl: https://foo.vault.azure.net
|
||||
key: foo-key
|
||||
version: fooversion
|
||||
hc_vault:
|
||||
# key05
|
||||
- 'https://bar.vault:8200/v1/bar/keys/bar-key'
|
||||
- kms:
|
||||
# key06
|
||||
- arn: bar
|
||||
aws_profile: bar
|
||||
pgp:
|
||||
- 'https://bar.vault:8200/v1/bar/keys/bar-key'
|
||||
- pgp:
|
||||
# key07
|
||||
- bar
|
||||
gcp_kms:
|
||||
kms:
|
||||
# key08
|
||||
- resource_id: bar
|
||||
- arn: bar
|
||||
aws_profile: bar
|
||||
gcp_kms:
|
||||
# key09
|
||||
- resource_id: bar
|
||||
# key10
|
||||
- resource_id: baz
|
||||
azure_keyvault:
|
||||
# key10
|
||||
# key11
|
||||
- vaultUrl: https://bar.vault.azure.net
|
||||
key: bar-key
|
||||
version: barversion
|
||||
hc_vault:
|
||||
# key01 - duplicate#1
|
||||
# key12
|
||||
- 'https://baz.vault:8200/v1/baz/keys/baz-key'
|
||||
pgp:
|
||||
# key13
|
||||
- baz
|
||||
kms:
|
||||
# key11
|
||||
# key14
|
||||
- arn: baz
|
||||
aws_profile: baz
|
||||
pgp:
|
||||
# key12
|
||||
- baz
|
||||
gcp_kms:
|
||||
# key03 - duplicate#2
|
||||
# --> should be removed when loading config
|
||||
# duplicate of key09
|
||||
- resource_id: bar
|
||||
azure_keyvault:
|
||||
# key04 - duplicate#3
|
||||
# duplicate of key05
|
||||
- vaultUrl: https://foo.vault.azure.net
|
||||
key: foo-key
|
||||
version: fooversion
|
||||
hc_vault:
|
||||
# key13 - duplicate#4 - but from different key_group
|
||||
# --> should stay
|
||||
# key15 (duplicate of key00, but that's in a different key_group)
|
||||
- 'https://foo.vault:8200/v1/foo/keys/foo-key'
|
||||
- kms:
|
||||
# key14
|
||||
- pgp:
|
||||
# key16
|
||||
- qux
|
||||
kms:
|
||||
# key17
|
||||
- arn: qux
|
||||
aws_profile: qux
|
||||
# key14 - duplicate#5
|
||||
# key18
|
||||
- arn: baz
|
||||
aws_profile: bar
|
||||
pgp:
|
||||
# key15
|
||||
- qux
|
||||
# key19
|
||||
- arn: baz
|
||||
role: '123'
|
||||
gcp_kms:
|
||||
# key16
|
||||
# key20
|
||||
- resource_id: qux
|
||||
# key17
|
||||
# key21
|
||||
- resource_id: fnord
|
||||
azure_keyvault:
|
||||
# key18
|
||||
# key22
|
||||
- vaultUrl: https://baz.vault.azure.net
|
||||
key: baz-key
|
||||
version: bazversion
|
||||
hc_vault:
|
||||
# key19
|
||||
# key23
|
||||
- 'https://qux.vault:8200/v1/qux/keys/qux-key'
|
||||
# everything below this should be loaded,
|
||||
# since it is not in a merge block
|
||||
pgp:
|
||||
# duplicate of key07
|
||||
- bar
|
||||
kms:
|
||||
# duplicated key06
|
||||
# duplicate of key08
|
||||
- arn: bar
|
||||
aws_profile: bar
|
||||
# key20
|
||||
# key24
|
||||
- arn: fnord
|
||||
aws_profile: fnord
|
||||
pgp:
|
||||
# duplicated key07
|
||||
- bar
|
||||
# duplicate of key03
|
||||
- arn: foo
|
||||
aws_profile: bar
|
||||
context:
|
||||
baz: bam
|
||||
role: '123'
|
||||
gcp_kms:
|
||||
# duplicated key08
|
||||
# duplicate of key09
|
||||
- resource_id: bar
|
||||
# key21
|
||||
# duplicate of key21
|
||||
- resource_id: fnord
|
||||
azure_keyvault:
|
||||
# duplicated key10
|
||||
# duplicate of key11
|
||||
- vaultUrl: https://bar.vault.azure.net
|
||||
key: bar-key
|
||||
version: barversion
|
||||
hc_vault:
|
||||
# duplicated 'key01 - duplicate#2'
|
||||
# duplicate of key12
|
||||
- 'https://baz.vault:8200/v1/baz/keys/baz-key'
|
||||
# key22
|
||||
# key25
|
||||
- 'https://fnord.vault:8200/v1/fnord/keys/fnord-key'
|
||||
`)
|
||||
|
||||
@@ -421,6 +448,7 @@ func TestLoadConfigFile(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadConfigFileWithGroups(t *testing.T) {
|
||||
bam := "bam"
|
||||
expected := configFile{
|
||||
CreationRules: []creationRule{
|
||||
{
|
||||
@@ -432,7 +460,37 @@ func TestLoadConfigFileWithGroups(t *testing.T) {
|
||||
PathRegex: "",
|
||||
KeyGroups: []keyGroup{
|
||||
{
|
||||
KMS: []kmsKey{{Arn: "foo", AwsProfile: "bar"}},
|
||||
KMS: []kmsKey{
|
||||
{
|
||||
Arn: "foo",
|
||||
AwsProfile: "bar",
|
||||
},
|
||||
{
|
||||
Arn: "foo",
|
||||
Context: map[string]*string{
|
||||
"baz": &bam,
|
||||
},
|
||||
},
|
||||
{
|
||||
Arn: "foo",
|
||||
AwsProfile: "bar",
|
||||
Context: map[string]*string{
|
||||
"baz": &bam,
|
||||
},
|
||||
},
|
||||
{
|
||||
Arn: "foo",
|
||||
Role: "123",
|
||||
},
|
||||
{
|
||||
Arn: "foo",
|
||||
AwsProfile: "bar",
|
||||
Context: map[string]*string{
|
||||
"baz": &bam,
|
||||
},
|
||||
Role: "123",
|
||||
},
|
||||
},
|
||||
PGP: []string{"bar"},
|
||||
GCPKMS: []gcpKmsKey{{ResourceID: "foo"}},
|
||||
AzureKV: []azureKVKey{{VaultURL: "https://foo.vault.azure.net", Key: "foo-key", Version: "fooversion"}},
|
||||
@@ -459,12 +517,52 @@ func TestLoadConfigFileWithGroups(t *testing.T) {
|
||||
assert.Equal(t, expected, conf)
|
||||
}
|
||||
|
||||
func id(key keys.MasterKey) string {
|
||||
return fmt.Sprintf("%s: %s", key.TypeToIdentifier(), key.ToString())
|
||||
}
|
||||
|
||||
func ids(keys []keys.MasterKey) []string {
|
||||
result := make([]string, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
result = append(result, id(key))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func TestLoadConfigFileWithMerge(t *testing.T) {
|
||||
conf, err := parseCreationRuleForFile(parseConfigFile(sampleConfigWithMergeType, t), "/conf/path", "whatever", nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(conf.KeyGroups))
|
||||
assert.Equal(t, 1, len(conf.KeyGroups[0]))
|
||||
assert.Equal(t, 22, len(conf.KeyGroups[1]))
|
||||
assert.Equal(t, []string{
|
||||
"hc_vault: https://foo.vault:8200/v1/foo/keys/foo-key",
|
||||
}, ids(conf.KeyGroups[0]))
|
||||
assert.Equal(t, []string{
|
||||
"pgp: foo", // key01
|
||||
"kms: foo||foo", //key02
|
||||
"kms: foo+123|baz:bam|bar", //key03
|
||||
"gcp_kms: foo", //key04
|
||||
"azure_kv: https://foo.vault.azure.net/keys/foo-key/fooversion", //key05
|
||||
"hc_vault: https://bar.vault:8200/v1/bar/keys/bar-key", //key06
|
||||
"pgp: bar", //key07
|
||||
"kms: bar||bar", //key08
|
||||
"gcp_kms: bar", //key09
|
||||
"gcp_kms: baz", //key10
|
||||
"azure_kv: https://bar.vault.azure.net/keys/bar-key/barversion", //key11
|
||||
"hc_vault: https://baz.vault:8200/v1/baz/keys/baz-key", //key12
|
||||
"pgp: baz", //key13
|
||||
"kms: baz||baz", //key14
|
||||
"hc_vault: https://foo.vault:8200/v1/foo/keys/foo-key", //key15
|
||||
"pgp: qux", //key16
|
||||
"kms: qux||qux", //key17
|
||||
"kms: baz||bar", //key18
|
||||
"kms: baz+123", //key19
|
||||
"gcp_kms: qux", //key20
|
||||
"gcp_kms: fnord", //key21
|
||||
"azure_kv: https://baz.vault.azure.net/keys/baz-key/bazversion", //key22
|
||||
"hc_vault: https://qux.vault:8200/v1/qux/keys/qux-key", //key23
|
||||
"kms: fnord||fnord", //key24
|
||||
"hc_vault: https://fnord.vault:8200/v1/fnord/keys/fnord-key", //key25
|
||||
}, ids(conf.KeyGroups[1]))
|
||||
}
|
||||
|
||||
func TestLoadConfigFileWithNoMatchingRules(t *testing.T) {
|
||||
@@ -538,9 +636,13 @@ 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, "foo||bar", conf.KeyGroups[0][1].ToString())
|
||||
assert.Equal(t, "foo|baz:bam", conf.KeyGroups[0][2].ToString())
|
||||
assert.Equal(t, "foo|baz:bam|bar", conf.KeyGroups[0][3].ToString())
|
||||
assert.Equal(t, "foo+123", conf.KeyGroups[0][4].ToString())
|
||||
assert.Equal(t, "foo+123|baz:bam|bar", conf.KeyGroups[0][5].ToString())
|
||||
assert.Equal(t, "qux", conf.KeyGroups[1][0].ToString())
|
||||
assert.Equal(t, "baz", conf.KeyGroups[1][1].ToString())
|
||||
assert.Equal(t, "baz||foo", conf.KeyGroups[1][1].ToString())
|
||||
}
|
||||
|
||||
func TestLoadConfigFileWithUnencryptedSuffix(t *testing.T) {
|
||||
|
||||
@@ -37,7 +37,7 @@ This configuration is quite sophisticated, and ensures at least the following:
|
||||
`main` branch. At present, this means that all pull requests attached to the
|
||||
milestone for the release are merged. If there are any pull requests that
|
||||
should not be included in the release, move them to a different milestone.
|
||||
- [ ] Create a pull request to update the [`CHANGELOG.rst`](../CHANGELOG.rst)
|
||||
- [ ] Create a pull request to update the [`CHANGELOG.md`](../CHANGELOG.md)
|
||||
file. This should include a summary of all changes since the last release,
|
||||
including references to any relevant pull requests.
|
||||
- [ ] In this same pull request, update the version number in `version/version.go`
|
||||
|
||||
@@ -11,6 +11,12 @@ creation_rules:
|
||||
- FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
|
||||
- pgp:
|
||||
- B611A2F9F11D0FF82568805119F9B5DAEA91FF86
|
||||
- path_regex: test_no_keygroups.yaml
|
||||
- path_regex: test_zero_keygroups.yaml
|
||||
key_groups: []
|
||||
- path_regex: test_empty_keygroup.yaml
|
||||
key_groups:
|
||||
- {}
|
||||
- pgp: FBC7B9E2A4F9289AC0C1D4843D16CEE4A27381B4
|
||||
destination_rules:
|
||||
- s3_bucket: "sops-publish-functional-tests"
|
||||
|
||||
322
functional-tests/Cargo.lock
generated
Normal file
322
functional-tests/Cargo.lock
generated
Normal file
@@ -0,0 +1,322 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "functional-tests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.168"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dade4812df5c384711475be5fcd8c162555352945401aed22a35bffeab61f657"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.140"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.34+deprecated"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.13.3+wasi-0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@@ -5,9 +5,9 @@ edition = "2021"
|
||||
authors = ["Adrian Utrilla <adrianutrilla@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
tempdir = "0.3.5"
|
||||
tempfile = "3.19.1"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0.99"
|
||||
serde_yaml = "0.9.22"
|
||||
serde_json = "1.0.140"
|
||||
serde_yaml = "0.9.34"
|
||||
serde_derive = "1.0"
|
||||
lazy_static = "1.4.0"
|
||||
lazy_static = "1.5.0"
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#[cfg_attr(test, macro_use)]
|
||||
extern crate lazy_static;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate serde_yaml;
|
||||
extern crate tempdir;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate tempfile;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@@ -16,10 +14,11 @@ mod tests {
|
||||
use serde_yaml::Value;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::io::{BufWriter, Read, Write};
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use tempdir::TempDir;
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use tempfile::Builder;
|
||||
use tempfile::TempDir;
|
||||
const SOPS_BINARY_PATH: &'static str = "./sops";
|
||||
const KMS_KEY: &'static str = "FUNCTIONAL_TEST_KMS_ARN";
|
||||
|
||||
@@ -36,8 +35,10 @@ mod tests {
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref TMP_DIR: TempDir =
|
||||
TempDir::new("sops-functional-tests").expect("Unable to create temporary directory");
|
||||
static ref TMP_DIR: TempDir = Builder::new()
|
||||
.prefix("sops-functional-tests")
|
||||
.tempdir()
|
||||
.expect("Unable to create temporary directory");
|
||||
}
|
||||
|
||||
fn prepare_temp_file(name: &str, contents: &[u8]) -> String {
|
||||
@@ -80,6 +81,47 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_to_stdin(process: &Child, content: &[u8]) {
|
||||
let mut outstdin = process.stdin.as_ref().unwrap();
|
||||
let mut writer = BufWriter::new(&mut outstdin);
|
||||
writer.write_all(content).expect("Cannot write to stdin");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encrypt_from_stdin() {
|
||||
let process = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
.arg("--filename-override")
|
||||
.arg("test_encrypt.yaml")
|
||||
.arg("--output-type")
|
||||
.arg("json")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.expect("Error running sops");
|
||||
write_to_stdin(
|
||||
&process,
|
||||
b"foo: 2
|
||||
bar: baz
|
||||
",
|
||||
);
|
||||
let output = process.wait_with_output().expect("Failed to wait on 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() {
|
||||
@@ -295,6 +337,115 @@ bar: baz",
|
||||
panic!("Output JSON does not have the expected structure");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_json_file_update_idempotent_write() {
|
||||
let file_path = prepare_temp_file(
|
||||
"test_set_update_idempotent_write.json",
|
||||
r#"{"a": 2, "b": "ba"}"#.as_bytes(),
|
||||
);
|
||||
assert!(
|
||||
Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops")
|
||||
.status
|
||||
.success(),
|
||||
"sops didn't exit successfully"
|
||||
);
|
||||
let mut before = String::new();
|
||||
File::open(file_path.clone())
|
||||
.unwrap()
|
||||
.read_to_string(&mut before)
|
||||
.unwrap();
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("set")
|
||||
.arg("--output-type")
|
||||
.arg("yaml")
|
||||
.arg(file_path.clone())
|
||||
.arg(r#"["b"]"#)
|
||||
.arg(r#""ba""#)
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
println!(
|
||||
"stdout: {}, stderr: {}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
assert!(output.status.success(), "sops didn't exit successfully");
|
||||
let mut after = String::new();
|
||||
File::open(file_path.clone())
|
||||
.unwrap()
|
||||
.read_to_string(&mut after)
|
||||
.unwrap();
|
||||
assert!(before != after);
|
||||
assert!(after.starts_with("a: "));
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("decrypt")
|
||||
.arg("--input-type")
|
||||
.arg("yaml")
|
||||
.arg("--output-type")
|
||||
.arg("yaml")
|
||||
.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 data = &String::from_utf8_lossy(&output.stdout);
|
||||
assert!(data == "a: 2\nb: ba\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_json_file_update_idempotent_nowrite() {
|
||||
let file_path = prepare_temp_file(
|
||||
"test_set_update_idempotent_nowrite.json",
|
||||
r#"{"a": 2, "b": "ba"}"#.as_bytes(),
|
||||
);
|
||||
assert!(
|
||||
Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops")
|
||||
.status
|
||||
.success(),
|
||||
"sops didn't exit successfully"
|
||||
);
|
||||
let mut before = String::new();
|
||||
File::open(file_path.clone())
|
||||
.unwrap()
|
||||
.read_to_string(&mut before)
|
||||
.unwrap();
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("set")
|
||||
.arg("--output-type")
|
||||
.arg("yaml")
|
||||
.arg("--idempotent")
|
||||
.arg(file_path.clone())
|
||||
.arg(r#"["b"]"#)
|
||||
.arg(r#""ba""#)
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
println!(
|
||||
"stdout: {}, stderr: {}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
assert!(output.status.success(), "sops didn't exit successfully");
|
||||
let mut after = String::new();
|
||||
File::open(file_path.clone())
|
||||
.unwrap()
|
||||
.read_to_string(&mut after)
|
||||
.unwrap();
|
||||
println!("before: {}\nafter: {}", &before, &after,);
|
||||
assert!(before == after);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_json_file_insert() {
|
||||
let file_path =
|
||||
@@ -549,11 +700,57 @@ b: ba"#
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_yaml_time() {
|
||||
let file_path = prepare_temp_file(
|
||||
"test_time.yaml",
|
||||
r#"a: 2024-01-01
|
||||
b: 2006-01-02T15:04:05+07:06"#
|
||||
.as_bytes(),
|
||||
);
|
||||
assert!(
|
||||
Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops")
|
||||
.status
|
||||
.success(),
|
||||
"sops didn't exit successfully"
|
||||
);
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("decrypt")
|
||||
.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)
|
||||
);
|
||||
assert!(output.status.success(), "sops didn't exit successfully");
|
||||
let mut s = String::new();
|
||||
File::open(file_path)
|
||||
.unwrap()
|
||||
.read_to_string(&mut s)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
s,
|
||||
r#"a: 2024-01-01T00:00:00Z
|
||||
b: 2006-01-02T15:04:05+07:06
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unset_json_file() {
|
||||
// Test removal of tree branch
|
||||
let file_path =
|
||||
prepare_temp_file("test_unset.json", r#"{"a": 2, "b": "ba", "c": [1,2]}"#.as_bytes());
|
||||
let file_path = prepare_temp_file(
|
||||
"test_unset.json",
|
||||
r#"{"a": 2, "b": "ba", "c": [1,2]}"#.as_bytes(),
|
||||
);
|
||||
assert!(
|
||||
Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
@@ -661,8 +858,10 @@ b: ba"#
|
||||
#[test]
|
||||
fn unset_yaml_file() {
|
||||
// Test removal of tree branch
|
||||
let file_path =
|
||||
prepare_temp_file("test_unset.yaml", r#"{"a": 2, "b": "ba", "c": [1,2]}"#.as_bytes());
|
||||
let file_path = prepare_temp_file(
|
||||
"test_unset.yaml",
|
||||
r#"{"a": 2, "b": "ba", "c": [1,2]}"#.as_bytes(),
|
||||
);
|
||||
assert!(
|
||||
Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
@@ -797,12 +996,42 @@ b: ba"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decrypt_from_stdin() {
|
||||
let process = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("decrypt")
|
||||
.arg("--input-type")
|
||||
.arg("yaml")
|
||||
.arg("--output-type")
|
||||
.arg("yaml")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.expect("Error running sops");
|
||||
write_to_stdin(&process, include_bytes!("../res/comments.enc.yaml"));
|
||||
let output = process.wait_with_output().expect("Failed to wait on sops");
|
||||
assert!(output.status.success(), "sops didn't exit successfully");
|
||||
let yaml = &String::from_utf8_lossy(&output.stdout);
|
||||
let data: Value = serde_yaml::from_str(&yaml).expect("Error parsing sops's YAML output");
|
||||
match data.into() {
|
||||
Value::Mapping(m) => {
|
||||
assert!(
|
||||
m.get(&Value::String("sops".to_owned())).is_none(),
|
||||
"sops metadata branch found"
|
||||
);
|
||||
assert_eq!(m["lorem"], Value::String("ipsum".to_owned()));
|
||||
assert_eq!(m["dolor"], Value::String("sit".to_owned()));
|
||||
}
|
||||
_ => panic!("sops's JSON output is not an object"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encrypt_comments() {
|
||||
let file_path = "res/comments.yaml";
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
.arg(file_path.clone())
|
||||
.arg(file_path)
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
assert!(output.status.success(), "SOPS didn't return successfully");
|
||||
@@ -821,7 +1050,7 @@ b: ba"#
|
||||
let file_path = "res/comments_list.yaml";
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
.arg(file_path.clone())
|
||||
.arg(file_path)
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
assert!(output.status.success(), "SOPS didn't return successfully");
|
||||
@@ -840,7 +1069,7 @@ b: ba"#
|
||||
let file_path = "res/comments.enc.yaml";
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("decrypt")
|
||||
.arg(file_path.clone())
|
||||
.arg(file_path)
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
assert!(output.status.success(), "SOPS didn't return successfully");
|
||||
@@ -859,7 +1088,7 @@ b: ba"#
|
||||
let file_path = "res/comments_unencrypted_comments.yaml";
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("decrypt")
|
||||
.arg(file_path.clone())
|
||||
.arg(file_path)
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
assert!(output.status.success(), "SOPS didn't return successfully");
|
||||
@@ -945,6 +1174,66 @@ b: ba"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_keygroups() {
|
||||
// The .sops.yaml file ensures this file is encrypted by zero keygroups
|
||||
let file_path = prepare_temp_file("test_no_keygroups.yaml", "a: secret".as_bytes());
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
assert!(
|
||||
!output.status.success(),
|
||||
"SOPS succeeded encrypting a file without a key group"
|
||||
);
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&output.stderr).unwrap(),
|
||||
"Could not generate data key: [empty key group provided]\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_keygroups() {
|
||||
// The .sops.yaml file ensures this file is encrypted by zero keygroups
|
||||
let file_path = prepare_temp_file("test_zero_keygroups.yaml", "a: secret".as_bytes());
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
assert!(
|
||||
!output.status.success(),
|
||||
"SOPS succeeded encrypting a file without a key group"
|
||||
);
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&output.stderr).unwrap(),
|
||||
"Could not generate data key: [empty key group provided]\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_keygroup() {
|
||||
// The .sops.yaml file ensures this file is encrypted by zero keygroups
|
||||
let file_path = prepare_temp_file("test_empty_keygroup.yaml", "a: secret".as_bytes());
|
||||
let output = Command::new(SOPS_BINARY_PATH)
|
||||
.arg("encrypt")
|
||||
.arg("-i")
|
||||
.arg(file_path.clone())
|
||||
.output()
|
||||
.expect("Error running sops");
|
||||
assert!(
|
||||
!output.status.success(),
|
||||
"SOPS succeeded encrypting a file without a key group"
|
||||
);
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&output.stderr).unwrap(),
|
||||
"Could not generate data key: [empty key group provided]\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_string() {
|
||||
let file_path = prepare_temp_file(
|
||||
@@ -1164,7 +1453,8 @@ bar: |-
|
||||
r#"{
|
||||
"foo": "bar",
|
||||
"bar": "baz\nbam"
|
||||
}"#
|
||||
}
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
kms "cloud.google.com/go/kms/apiv1"
|
||||
"cloud.google.com/go/kms/apiv1/kmspb"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
"google.golang.org/api/option"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
@@ -50,6 +51,11 @@ type MasterKey struct {
|
||||
// for NeedsRotation.
|
||||
CreationDate time.Time
|
||||
|
||||
// tokenSource contains the oauth2.TokenSource used by the GCP client.
|
||||
// It can be injected by a (local) keyservice.KeyServiceServer using
|
||||
// TokenSource.ApplyToMasterKey.
|
||||
// If nil, the remaining authentication methods are attempted.
|
||||
tokenSource oauth2.TokenSource
|
||||
// credentialJSON is the Service Account credentials JSON used for
|
||||
// authenticating towards the GCP KMS service.
|
||||
credentialJSON []byte
|
||||
@@ -82,6 +88,22 @@ func MasterKeysFromResourceIDString(resourceID string) []*MasterKey {
|
||||
return keys
|
||||
}
|
||||
|
||||
// TokenSource is an oauth2.TokenSource used for authenticating towards the
|
||||
// GCP KMS service.
|
||||
type TokenSource struct {
|
||||
source oauth2.TokenSource
|
||||
}
|
||||
|
||||
// NewTokenSource creates a new TokenSource from the provided oauth2.TokenSource.
|
||||
func NewTokenSource(source oauth2.TokenSource) TokenSource {
|
||||
return TokenSource{source: source}
|
||||
}
|
||||
|
||||
// ApplyToMasterKey configures the TokenSource on the provided key.
|
||||
func (t TokenSource) ApplyToMasterKey(key *MasterKey) {
|
||||
key.tokenSource = t.source
|
||||
}
|
||||
|
||||
// CredentialJSON is the Service Account credentials JSON used for authenticating
|
||||
// towards the GCP KMS service.
|
||||
type CredentialJSON []byte
|
||||
@@ -203,8 +225,8 @@ func (key *MasterKey) TypeToIdentifier() string {
|
||||
return KeyTypeIdentifier
|
||||
}
|
||||
|
||||
// newKMSClient returns a GCP KMS client configured with the credentialJSON
|
||||
// and/or grpcConn, falling back to environmental defaults.
|
||||
// newKMSClient returns a GCP KMS client configured with the tokenSource
|
||||
// or credentialJSON, and/or grpcConn, falling back to environmental defaults.
|
||||
// It returns an error if the ResourceID is invalid, or if the setup of the
|
||||
// client fails.
|
||||
func (key *MasterKey) newKMSClient() (*kms.KeyManagementClient, error) {
|
||||
@@ -216,6 +238,8 @@ func (key *MasterKey) newKMSClient() (*kms.KeyManagementClient, error) {
|
||||
|
||||
var opts []option.ClientOption
|
||||
switch {
|
||||
case key.tokenSource != nil:
|
||||
opts = append(opts, option.WithTokenSource(key.tokenSource))
|
||||
case key.credentialJSON != nil:
|
||||
opts = append(opts, option.WithCredentialsJSON(key.credentialJSON))
|
||||
default:
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"cloud.google.com/go/kms/apiv1/kmspb"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/oauth2"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
@@ -38,6 +39,13 @@ func TestMasterKeysFromResourceIDString(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenSource_ApplyToMasterKey(t *testing.T) {
|
||||
src := NewTokenSource(oauth2.StaticTokenSource(&oauth2.Token{AccessToken: "some-token"}))
|
||||
key := &MasterKey{}
|
||||
src.ApplyToMasterKey(key)
|
||||
assert.Equal(t, src.source, key.tokenSource)
|
||||
}
|
||||
|
||||
func TestCredentialJSON_ApplyToMasterKey(t *testing.T) {
|
||||
key := &MasterKey{}
|
||||
credential := CredentialJSON("mock")
|
||||
@@ -159,7 +167,7 @@ func newGRPCServer(port string) *grpc.ClientConn {
|
||||
}
|
||||
go serv.Serve(lis)
|
||||
|
||||
conn, err := grpc.Dial(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
conn, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
173
go.mod
173
go.mod
@@ -1,135 +1,148 @@
|
||||
module github.com/getsops/sops/v3
|
||||
|
||||
go 1.21
|
||||
go 1.22
|
||||
toolchain go1.24.1
|
||||
|
||||
require (
|
||||
cloud.google.com/go/kms v1.18.4
|
||||
cloud.google.com/go/storage v1.43.0
|
||||
filippo.io/age v1.2.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.1.0
|
||||
github.com/ProtonMail/go-crypto v1.1.0-alpha.5-proton
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.3
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.9
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.35.3
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.2
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3
|
||||
cloud.google.com/go/kms v1.21.1
|
||||
cloud.google.com/go/storage v1.51.0
|
||||
filippo.io/age v1.2.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1
|
||||
github.com/ProtonMail/go-crypto v1.1.6
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.9
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.62
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.66
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.38.1
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.78.2
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17
|
||||
github.com/blang/semver v3.5.1+incompatible
|
||||
github.com/fatih/color v1.17.0
|
||||
github.com/getsops/gopgagent v0.0.0-20240527072608-0c14999532fe
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/goware/prefixer v0.0.0-20160118172347-395022866408
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2
|
||||
github.com/hashicorp/vault/api v1.14.0
|
||||
github.com/hashicorp/vault/api v1.16.0
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/mitchellh/go-wordwrap v1.0.1
|
||||
github.com/ory/dockertest/v3 v3.10.0
|
||||
github.com/ory/dockertest/v3 v3.11.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/urfave/cli v1.22.15
|
||||
golang.org/x/net v0.27.0
|
||||
golang.org/x/sys v0.22.0
|
||||
golang.org/x/term v0.22.0
|
||||
google.golang.org/api v0.189.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade
|
||||
google.golang.org/grpc v1.65.0
|
||||
google.golang.org/protobuf v1.34.2
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/urfave/cli v1.22.16
|
||||
golang.org/x/crypto v0.36.0
|
||||
golang.org/x/net v0.37.0
|
||||
golang.org/x/oauth2 v0.28.0
|
||||
golang.org/x/sys v0.31.0
|
||||
golang.org/x/term v0.30.0
|
||||
google.golang.org/api v0.227.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4
|
||||
google.golang.org/grpc v1.71.0
|
||||
google.golang.org/protobuf v1.36.5
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.115.0 // indirect
|
||||
cloud.google.com/go/auth v0.7.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.5.0 // indirect
|
||||
cloud.google.com/go/iam v1.1.10 // indirect
|
||||
cloud.google.com/go/longrunning v0.5.9 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
cel.dev/expr v0.19.2 // indirect
|
||||
cloud.google.com/go v0.118.3 // indirect
|
||||
cloud.google.com/go/auth v0.15.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/iam v1.4.1 // indirect
|
||||
cloud.google.com/go/longrunning v0.6.5 // indirect
|
||||
cloud.google.com/go/monitoring v1.24.0 // indirect
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
|
||||
github.com/aws/smithy-go v1.20.3 // indirect
|
||||
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 // indirect
|
||||
github.com/aws/smithy-go v1.22.2 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cloudflare/circl v1.3.9 // indirect
|
||||
github.com/containerd/continuity v0.4.3 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudflare/circl v1.5.0 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect
|
||||
github.com/containerd/continuity v0.4.5 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/cli v27.0.1+incompatible // indirect
|
||||
github.com/docker/docker v27.1.0+incompatible // indirect
|
||||
github.com/docker/cli v27.4.1+incompatible // indirect
|
||||
github.com/docker/docker v27.4.1+incompatible // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.6 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.7 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/sys/user v0.3.0 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/opencontainers/runc v1.1.13 // indirect
|
||||
github.com/opencontainers/runc v1.2.3 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 // indirect
|
||||
go.opentelemetry.io/otel v1.27.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.27.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.27.0 // indirect
|
||||
golang.org/x/crypto v0.25.0 // indirect
|
||||
golang.org/x/oauth2 v0.21.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240722135656-d784300faade // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240722135656-d784300faade // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
|
||||
go.opentelemetry.io/otel v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.34.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
444
go.sum
444
go.sum
@@ -1,180 +1,185 @@
|
||||
c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805 h1:u2qwJeEvnypw+OCPUHmoZE3IqwfuN5kgDfo5MLzpNM0=
|
||||
c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805/go.mod h1:FomMrUJ2Lxt5jCLmZkG3FHa72zUprnhd3v/Z18Snm4w=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14=
|
||||
cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU=
|
||||
cloud.google.com/go/auth v0.7.2 h1:uiha352VrCDMXg+yoBtaD0tUF4Kv9vrtrWPYXwutnDE=
|
||||
cloud.google.com/go/auth v0.7.2/go.mod h1:VEc4p5NNxycWQTMQEDQF0bd6aTMb6VgYDXEwiJJQAbs=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I=
|
||||
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
||||
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
||||
cloud.google.com/go/iam v1.1.10 h1:ZSAr64oEhQSClwBL670MsJAW5/RLiC6kfw3Bqmd5ZDI=
|
||||
cloud.google.com/go/iam v1.1.10/go.mod h1:iEgMq62sg8zx446GCaijmA2Miwg5o3UbO+nI47WHJps=
|
||||
cloud.google.com/go/kms v1.18.4 h1:dYN3OCsQ6wJLLtOnI8DGUwQ5shMusXsWCCC+s09ATsk=
|
||||
cloud.google.com/go/kms v1.18.4/go.mod h1:SG1bgQ3UWW6/KdPo9uuJnzELXY5YTTMJtDYvajiQ22g=
|
||||
cloud.google.com/go/longrunning v0.5.9 h1:haH9pAuXdPAMqHvzX0zlWQigXT7B0+CL4/2nXXdBo5k=
|
||||
cloud.google.com/go/longrunning v0.5.9/go.mod h1:HD+0l9/OOW0za6UWdKJtXoFAX/BGg/3Wj8p10NeWF7c=
|
||||
cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs=
|
||||
cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
filippo.io/age v1.2.0 h1:vRDp7pUMaAJzXNIWJVAZnEf/Dyi4Vu4wI8S1LBzufhE=
|
||||
filippo.io/age v1.2.0/go.mod h1:JL9ew2lTN+Pyft4RiNGguFfOpewKwSHm5ayKD/A4004=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0 h1:GJHeeA2N7xrG3q30L2UXDyuWRzDM900/65j70wcM4Ww=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
|
||||
cel.dev/expr v0.19.2 h1:V354PbqIXr9IQdwy4SYA4xa0HXaWq1BUPAGzugBY5V4=
|
||||
cel.dev/expr v0.19.2/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
cloud.google.com/go v0.118.3 h1:jsypSnrE/w4mJysioGdMBg4MiW/hHx/sArFpaBWHdME=
|
||||
cloud.google.com/go v0.118.3/go.mod h1:Lhs3YLnBlwJ4KA6nuObNMZ/fCbOQBPuWKPoE0Wa/9Vc=
|
||||
cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps=
|
||||
cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
|
||||
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/iam v1.4.1 h1:cFC25Nv+u5BkTR/BT1tXdoF2daiVbZ1RLx2eqfQ9RMM=
|
||||
cloud.google.com/go/iam v1.4.1/go.mod h1:2vUEJpUG3Q9p2UdsyksaKpDzlwOrnMzS30isdReIcLM=
|
||||
cloud.google.com/go/kms v1.21.1 h1:r1Auo+jlfJSf8B7mUnVw5K0fI7jWyoUy65bV53VjKyk=
|
||||
cloud.google.com/go/kms v1.21.1/go.mod h1:s0wCyByc9LjTdCjG88toVs70U9W+cc6RKFc8zAqX7nE=
|
||||
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
|
||||
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
|
||||
cloud.google.com/go/longrunning v0.6.5 h1:sD+t8DO8j4HKW4QfouCklg7ZC1qC4uzVZt8iz3uTW+Q=
|
||||
cloud.google.com/go/longrunning v0.6.5/go.mod h1:Et04XK+0TTLKa5IPYryKf5DkpwImy6TluQ1QTLwlKmY=
|
||||
cloud.google.com/go/monitoring v1.24.0 h1:csSKiCJ+WVRgNkRzzz3BPoGjFhjPY23ZTcaenToJxMM=
|
||||
cloud.google.com/go/monitoring v1.24.0/go.mod h1:Bd1PRK5bmQBQNnuGwHBfUamAV1ys9049oEPHnn4pcsc=
|
||||
cloud.google.com/go/storage v1.51.0 h1:ZVZ11zCiD7b3k+cH5lQs/qcNaoSz3U9I0jgwVzqDlCw=
|
||||
cloud.google.com/go/storage v1.51.0/go.mod h1:YEJfu/Ki3i5oHC/7jyTgsGZwdQ8P9hqMqvpi5kRKGgc=
|
||||
cloud.google.com/go/trace v1.11.3 h1:c+I4YFjxRQjvAhRmSsmjpASUKq88chOX854ied0K/pE=
|
||||
cloud.google.com/go/trace v1.11.3/go.mod h1:pt7zCYiDSQjC9Y2oqCsh9jF4GStB/hmjrYLsxRR27q8=
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
filippo.io/age v1.2.1 h1:X0TZjehAZylOIj4DubWYU1vWQxv9bJpo+Uu2/LGhi1o=
|
||||
filippo.io/age v1.2.1/go.mod h1:JL9ew2lTN+Pyft4RiNGguFfOpewKwSHm5ayKD/A4004=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 h1:DSDNVxqkoXJiko6x8a90zidoYqnYYa6c1MTzDKzKkTo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1/go.mod h1:zGqV2R4Cr/k8Uye5w+dgQ06WJtEcbQG/8J7BB6hnCr4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.1.0 h1:DRiANoJTiW6obBQe3SqZizkuV1PEgfiiGivmVocDy64=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.1.0/go.mod h1:qLIye2hwb/ZouqhpSD9Zn3SJipvpEnz1Ywl3VUk9Y0s=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.1 h1:9fXQS/0TtQmKXp8SureKouF+idbQvp7cPUxykiohnBs=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.1/go.mod h1:f+OaoSg0VQYPMqB0Jp2D54j1VHzITYcJaCNwV+k00ts=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
|
||||
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0/go.mod h1:BnBReJLvVYx2CS/UHOgVz2BXKXD9wsQPxZug20nZhd0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0 h1:OqVGm6Ei3x5+yZmSJG1Mh2NwHvpVmZ08CB5qJhT9Nuk=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||
github.com/ProtonMail/go-crypto v1.1.0-alpha.5-proton h1:KVBEgU3CJpmzLChnLiSuEyCuhGhcMt3eOST+7A+ckto=
|
||||
github.com/ProtonMail/go-crypto v1.1.0-alpha.5-proton/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.9 h1:TC2vjvaAv1VNl9A0rm+SeuBjrzXnrlwk6Yop+gKRi38=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.9/go.mod h1:WPv2FRnkIOoDv/8j2gSUsI4qDc7392w5anFB/I89GZ8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.35.3 h1:UPTdlTOwWUX49fVi7cymEN6hDqCwe3LNv1vi7TXUutk=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.35.3/go.mod h1:gjDP16zn+WWalyaUqwCCioQ8gU8lzttCCc9jYsiQI/8=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.2 h1:sZXIzO38GZOU+O0C+INqbH7C2yALwfMWpd64tONS/NE=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.2/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ=
|
||||
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
|
||||
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
|
||||
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.9 h1:Kg+fAYNaJeGXp1vmjtidss8O2uXIsXwaRqsQJKXVr+0=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.9/go.mod h1:oU3jj2O53kgOU4TXq/yipt6ryiooYjlkqqVaZk7gY/U=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.62 h1:fvtQY3zFzYJ9CfixuAQ96IxDrBajbBWGqjNTCa79ocU=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.62/go.mod h1:ElETBxIQqcxej++Cs8GyPBbgMys5DgQPTwo7cUPDKt8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.66 h1:MTLivtC3s89de7Fe3P8rzML/8XPNRfuyJhlRTsCEt0k=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.66/go.mod h1:NAuQ2s6gaFEsuTIb2+P5t6amB1w5MhvJFxppoezGWH0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 h1:lguz0bmOoGzozP9XfRJR1QIayEYo+2vP/No3OfLF0pU=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.38.1 h1:tecq7+mAav5byF+Mr+iONJnCBf4B4gon8RSp4BrweSc=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.38.1/go.mod h1:cQn6tAF77Di6m4huxovNM7NVAozWTZLsDRp9t8Z/WYk=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.78.2 h1:jIiopHEV22b4yQP2q36Y0OmwLbsxNWdWwfZRR5QRRO4=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.78.2/go.mod h1:U5SNqwhXB3Xe6F47kXvWihPl/ilGaEDe8HD/50Z9wxc=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 h1:8JdC7Gr9NROg1Rusk25IcZeTO59zLxsKgE0gkh5O6h0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.1/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 h1:KwuLovgQPcdjNMfFt9OhUd9a2OwcOKhxfvF4glTzLuA=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 h1:PZV5W8yk4OtH1JAuhV2PXwwO9v5G5Aoj+eMCn4T+1Kc=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
|
||||
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
|
||||
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
|
||||
github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE=
|
||||
github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8=
|
||||
github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
|
||||
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk=
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
|
||||
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docker/cli v27.0.1+incompatible h1:d/OrlblkOTkhJ1IaAGD1bLgUBtFQC/oP0VjkFMIN+B0=
|
||||
github.com/docker/cli v27.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
|
||||
github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/docker/cli v27.4.1+incompatible h1:VzPiUlRJ/xh+otB75gva3r05isHMo5wXDfPRi5/b4hI=
|
||||
github.com/docker/cli v27.4.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker v27.4.1+incompatible h1:ZJvcY7gfwHn1JF48PfbyXg7Jyt9ZCWDW+GGXOIxEwp4=
|
||||
github.com/docker/docker v27.4.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M=
|
||||
github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/getsops/gopgagent v0.0.0-20240527072608-0c14999532fe h1:QKe/kmAYbndxwu91TcjHERsnMh5SgOB1x/qicvOdUJ8=
|
||||
github.com/getsops/gopgagent v0.0.0-20240527072608-0c14999532fe/go.mod h1:awFzISqLJoZLm+i9QQ4SgMNHDqljH6jWV0B36V5MrUM=
|
||||
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
|
||||
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
||||
github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e h1:y/1nzrdF+RPds4lfoEpNhjfmzlgZtPqyO3jMzrqDQws=
|
||||
github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e/go.mod h1:awFzISqLJoZLm+i9QQ4SgMNHDqljH6jWV0B36V5MrUM=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
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.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
|
||||
github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=
|
||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
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/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||
github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
|
||||
github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
|
||||
github.com/goware/prefixer v0.0.0-20160118172347-395022866408 h1:Y9iQJfEqnN3/Nce9cOegemcy/9Ai5k3huT6E80F3zaw=
|
||||
github.com/goware/prefixer v0.0.0-20160118172347-395022866408/go.mod h1:PE1ycukgRPJ7bJ9a1fdfQ9j8i/cEcRAoLZzbxYpNB/s=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -194,12 +199,14 @@ github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 h1:iBt4Ew4XEGLfh6/bPk4rSY
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8/go.mod h1:aiJI+PIApBRQG7FZTEBx5GiiX+HbOHilUdNxUZi4eV0=
|
||||
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-sockaddr v1.0.6 h1:RSG8rKU28VTUTvEKghe5gIhIQpv8evvNpnDEyqO4u9I=
|
||||
github.com/hashicorp/go-sockaddr v1.0.6/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI=
|
||||
github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw=
|
||||
github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw=
|
||||
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.14.0 h1:Ah3CFLixD5jmjusOgm8grfN9M0d+Y8fVR2SW0K6pJLU=
|
||||
github.com/hashicorp/vault/api v1.14.0/go.mod h1:pV9YLxBGSz+cItFDd8Ii4G17waWOQ32zVjMWHe/cOqk=
|
||||
github.com/hashicorp/vault/api v1.16.0 h1:nbEYGJiAPGzT9U4oWgaaB0g+Rj8E59QuHKyA5LhwQN4=
|
||||
github.com/hashicorp/vault/api v1.16.0/go.mod h1:KhuUhzOD8lDSk29AtzNjgAu2kxRA9jL9NAbkFlqvkBA=
|
||||
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
|
||||
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
@@ -223,25 +230,30 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
|
||||
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/opencontainers/runc v1.1.13 h1:98S2srgG9vw0zWcDpFMn5TRrh8kLxa/5OFUstuUhmRs=
|
||||
github.com/opencontainers/runc v1.1.13/go.mod h1:R016aXacfp/gwQBYw2FDGa9m+n6atbLWrYY8hNMT/sA=
|
||||
github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4=
|
||||
github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg=
|
||||
github.com/opencontainers/runc v1.2.3 h1:fxE7amCzfZflJO2lHXf4y/y8M1BoAqp+FVmG19oYB80=
|
||||
github.com/opencontainers/runc v1.2.3/go.mod h1:nSxcWUydXrsBZVYNSkTjoQ/N6rcyTtn+1SD5D4+kRIM=
|
||||
github.com/ory/dockertest/v3 v3.11.0 h1:OiHcxKAvSDUwsEVh2BjxQQc/5EHz9n0va9awCtNGuyA=
|
||||
github.com/ory/dockertest/v3 v3.11.0/go.mod h1:VIPxS1gwT9NpPOrfD3rACs8Y9Z7yhzO4SB194iUDnUI=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
|
||||
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
@@ -256,12 +268,12 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/urfave/cli v1.22.15 h1:nuqt+pdC/KqswQKhETJjo7pvn/k4xMUxgW6liI7XpnM=
|
||||
github.com/urfave/cli v1.22.15/go.mod h1:wSan1hmo5zeyLGBjRJbzRTNk8gwoYa2B9n4q9dmRIc0=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ=
|
||||
github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
@@ -271,53 +283,46 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 h1:vS1Ao/R55RNV4O7TA2Qopok8yN+X0LIP6RVWLFkprck=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0/go.mod h1:BMsdeOxN04K0L5FNUBfjFdvwWGNe/rkmSwH4Aelu/X0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 h1:9l89oX4ba9kHbBol3Xin3leYJ+252h0zszDtBwyKe2A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0/go.mod h1:XLZfZboOJWHNKUv7eH0inh0E9VV6eWDFB/9yJyTLPp0=
|
||||
go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg=
|
||||
go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ=
|
||||
go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik=
|
||||
go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||
go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw=
|
||||
go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
|
||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I=
|
||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
|
||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
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.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
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/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
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-20190620200207-3b0461eec859/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
|
||||
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -326,21 +331,17 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
@@ -348,37 +349,18 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.189.0 h1:equMo30LypAkdkLMBqfeIqtyAnlyig1JSZArl4XPwdI=
|
||||
google.golang.org/api v0.189.0/go.mod h1:FLWGJKb0hb+pU2j+rJqwbnsF+ym+fQs73rbJ+KAUgy8=
|
||||
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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20240722135656-d784300faade h1:lKFsS7wpngDgSCeFn7MoLy+wBDQZ1UQIJD4UNM1Qvkg=
|
||||
google.golang.org/genproto v0.0.0-20240722135656-d784300faade/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240722135656-d784300faade h1:WxZOF2yayUHpHSbUE6NMzumUzBxYc3YGwo0YHnbzsJY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240722135656-d784300faade/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade h1:oCRSWfwGXQsqlVdErcyTt4A93Y8fo0/9D4b1gnI++qo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
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.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
google.golang.org/api v0.227.0 h1:QvIHF9IuyG6d6ReE+BNd11kIB8hZvjN8Z5xY5t21zYc=
|
||||
google.golang.org/api v0.227.0/go.mod h1:EIpaG6MbTgQarWF5xJvX0eOJPK9n/5D4Bynb9j2HXvQ=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
|
||||
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
||||
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
@@ -389,7 +371,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
|
||||
gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
|
||||
@@ -41,7 +41,7 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
// Pull the image, create a container based on it, and run it
|
||||
resource, err := pool.Run("vault", testVaultVersion, []string{"VAULT_DEV_ROOT_TOKEN_ID=" + testVaultToken})
|
||||
resource, err := pool.Run("ghcr.io/getsops/ci-container-images/vault", testVaultVersion, []string{"VAULT_DEV_ROOT_TOKEN_ID=" + testVaultToken})
|
||||
if err != nil {
|
||||
logger.Fatalf("could not start resource: %s", err)
|
||||
}
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.23.0
|
||||
// protoc v3.13.0
|
||||
// protoc-gen-go v1.35.2
|
||||
// protoc v5.28.3
|
||||
// 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"
|
||||
@@ -25,16 +20,13 @@ const (
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
type Key struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Types that are assignable to KeyType:
|
||||
//
|
||||
// *Key_KmsKey
|
||||
// *Key_PgpKey
|
||||
// *Key_GcpKmsKey
|
||||
@@ -46,11 +38,9 @@ type Key struct {
|
||||
|
||||
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)
|
||||
}
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Key) String() string {
|
||||
@@ -61,7 +51,7 @@ func (*Key) ProtoMessage() {}
|
||||
|
||||
func (x *Key) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
@@ -175,11 +165,9 @@ type PgpKey struct {
|
||||
|
||||
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)
|
||||
}
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *PgpKey) String() string {
|
||||
@@ -190,7 +178,7 @@ func (*PgpKey) ProtoMessage() {}
|
||||
|
||||
func (x *PgpKey) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
@@ -225,11 +213,9 @@ type KmsKey struct {
|
||||
|
||||
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)
|
||||
}
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *KmsKey) String() string {
|
||||
@@ -240,7 +226,7 @@ func (*KmsKey) ProtoMessage() {}
|
||||
|
||||
func (x *KmsKey) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
@@ -293,11 +279,9 @@ type GcpKmsKey struct {
|
||||
|
||||
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)
|
||||
}
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GcpKmsKey) String() string {
|
||||
@@ -308,7 +292,7 @@ func (*GcpKmsKey) ProtoMessage() {}
|
||||
|
||||
func (x *GcpKmsKey) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
@@ -342,11 +326,9 @@ type VaultKey struct {
|
||||
|
||||
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)
|
||||
}
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *VaultKey) String() string {
|
||||
@@ -357,7 +339,7 @@ func (*VaultKey) ProtoMessage() {}
|
||||
|
||||
func (x *VaultKey) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
@@ -405,11 +387,9 @@ type AzureKeyVaultKey struct {
|
||||
|
||||
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)
|
||||
}
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *AzureKeyVaultKey) String() string {
|
||||
@@ -420,7 +400,7 @@ func (*AzureKeyVaultKey) ProtoMessage() {}
|
||||
|
||||
func (x *AzureKeyVaultKey) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
@@ -466,11 +446,9 @@ type AgeKey struct {
|
||||
|
||||
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)
|
||||
}
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *AgeKey) String() string {
|
||||
@@ -481,7 +459,7 @@ func (*AgeKey) ProtoMessage() {}
|
||||
|
||||
func (x *AgeKey) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[6]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
@@ -514,11 +492,9 @@ type EncryptRequest struct {
|
||||
|
||||
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)
|
||||
}
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *EncryptRequest) String() string {
|
||||
@@ -529,7 +505,7 @@ func (*EncryptRequest) ProtoMessage() {}
|
||||
|
||||
func (x *EncryptRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[7]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
@@ -568,11 +544,9 @@ type EncryptResponse struct {
|
||||
|
||||
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)
|
||||
}
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *EncryptResponse) String() string {
|
||||
@@ -583,7 +557,7 @@ func (*EncryptResponse) ProtoMessage() {}
|
||||
|
||||
func (x *EncryptResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[8]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
@@ -616,11 +590,9 @@ type DecryptRequest struct {
|
||||
|
||||
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)
|
||||
}
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *DecryptRequest) String() string {
|
||||
@@ -631,7 +603,7 @@ func (*DecryptRequest) ProtoMessage() {}
|
||||
|
||||
func (x *DecryptRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[9]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
@@ -670,11 +642,9 @@ type DecryptResponse struct {
|
||||
|
||||
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)
|
||||
}
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *DecryptResponse) String() string {
|
||||
@@ -685,7 +655,7 @@ func (*DecryptResponse) ProtoMessage() {}
|
||||
|
||||
func (x *DecryptResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_keyservice_keyservice_proto_msgTypes[10]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
@@ -784,7 +754,8 @@ var file_keyservice_keyservice_proto_rawDesc = []byte{
|
||||
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,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x0e, 0x5a, 0x0c, 0x2e, 0x2f, 0x6b, 0x65,
|
||||
0x79, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -800,7 +771,7 @@ func file_keyservice_keyservice_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_keyservice_keyservice_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
|
||||
var file_keyservice_keyservice_proto_goTypes = []interface{}{
|
||||
var file_keyservice_keyservice_proto_goTypes = []any{
|
||||
(*Key)(nil), // 0: Key
|
||||
(*PgpKey)(nil), // 1: PgpKey
|
||||
(*KmsKey)(nil), // 2: KmsKey
|
||||
@@ -840,141 +811,7 @@ 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{}{
|
||||
file_keyservice_keyservice_proto_msgTypes[0].OneofWrappers = []any{
|
||||
(*Key_KmsKey)(nil),
|
||||
(*Key_PgpKey)(nil),
|
||||
(*Key_GcpKmsKey)(nil),
|
||||
@@ -1001,119 +838,3 @@ func file_keyservice_keyservice_proto_init() {
|
||||
file_keyservice_keyservice_proto_goTypes = nil
|
||||
file_keyservice_keyservice_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConnInterface
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion6
|
||||
|
||||
// KeyServiceClient is the client API for KeyService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type KeyServiceClient interface {
|
||||
Encrypt(ctx context.Context, in *EncryptRequest, opts ...grpc.CallOption) (*EncryptResponse, error)
|
||||
Decrypt(ctx context.Context, in *DecryptRequest, opts ...grpc.CallOption) (*DecryptResponse, error)
|
||||
}
|
||||
|
||||
type keyServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewKeyServiceClient(cc grpc.ClientConnInterface) KeyServiceClient {
|
||||
return &keyServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *keyServiceClient) Encrypt(ctx context.Context, in *EncryptRequest, opts ...grpc.CallOption) (*EncryptResponse, error) {
|
||||
out := new(EncryptResponse)
|
||||
err := c.cc.Invoke(ctx, "/KeyService/Encrypt", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *keyServiceClient) Decrypt(ctx context.Context, in *DecryptRequest, opts ...grpc.CallOption) (*DecryptResponse, error) {
|
||||
out := new(DecryptResponse)
|
||||
err := c.cc.Invoke(ctx, "/KeyService/Decrypt", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// KeyServiceServer is the server API for KeyService service.
|
||||
type KeyServiceServer interface {
|
||||
Encrypt(context.Context, *EncryptRequest) (*EncryptResponse, error)
|
||||
Decrypt(context.Context, *DecryptRequest) (*DecryptResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedKeyServiceServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedKeyServiceServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedKeyServiceServer) Encrypt(context.Context, *EncryptRequest) (*EncryptResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Encrypt not implemented")
|
||||
}
|
||||
func (*UnimplementedKeyServiceServer) Decrypt(context.Context, *DecryptRequest) (*DecryptResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Decrypt not implemented")
|
||||
}
|
||||
|
||||
func RegisterKeyServiceServer(s *grpc.Server, srv KeyServiceServer) {
|
||||
s.RegisterService(&_KeyService_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _KeyService_Encrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(EncryptRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(KeyServiceServer).Encrypt(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/KeyService/Encrypt",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(KeyServiceServer).Encrypt(ctx, req.(*EncryptRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _KeyService_Decrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DecryptRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(KeyServiceServer).Decrypt(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/KeyService/Decrypt",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(KeyServiceServer).Decrypt(ctx, req.(*DecryptRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _KeyService_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "KeyService",
|
||||
HandlerType: (*KeyServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Encrypt",
|
||||
Handler: _KeyService_Encrypt_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Decrypt",
|
||||
Handler: _KeyService_Decrypt_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "keyservice/keyservice.proto",
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option go_package = "./keyservice";
|
||||
|
||||
message Key {
|
||||
oneof key_type {
|
||||
KmsKey kms_key = 1;
|
||||
|
||||
157
keyservice/keyservice_grpc.pb.go
Normal file
157
keyservice/keyservice_grpc.pb.go
Normal file
@@ -0,0 +1,157 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.5.1
|
||||
// - protoc v5.28.3
|
||||
// source: keyservice/keyservice.proto
|
||||
|
||||
package keyservice
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
KeyService_Encrypt_FullMethodName = "/KeyService/Encrypt"
|
||||
KeyService_Decrypt_FullMethodName = "/KeyService/Decrypt"
|
||||
)
|
||||
|
||||
// KeyServiceClient is the client API for KeyService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type KeyServiceClient interface {
|
||||
Encrypt(ctx context.Context, in *EncryptRequest, opts ...grpc.CallOption) (*EncryptResponse, error)
|
||||
Decrypt(ctx context.Context, in *DecryptRequest, opts ...grpc.CallOption) (*DecryptResponse, error)
|
||||
}
|
||||
|
||||
type keyServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewKeyServiceClient(cc grpc.ClientConnInterface) KeyServiceClient {
|
||||
return &keyServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *keyServiceClient) Encrypt(ctx context.Context, in *EncryptRequest, opts ...grpc.CallOption) (*EncryptResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(EncryptResponse)
|
||||
err := c.cc.Invoke(ctx, KeyService_Encrypt_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *keyServiceClient) Decrypt(ctx context.Context, in *DecryptRequest, opts ...grpc.CallOption) (*DecryptResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(DecryptResponse)
|
||||
err := c.cc.Invoke(ctx, KeyService_Decrypt_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// KeyServiceServer is the server API for KeyService service.
|
||||
// All implementations should embed UnimplementedKeyServiceServer
|
||||
// for forward compatibility.
|
||||
type KeyServiceServer interface {
|
||||
Encrypt(context.Context, *EncryptRequest) (*EncryptResponse, error)
|
||||
Decrypt(context.Context, *DecryptRequest) (*DecryptResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedKeyServiceServer should be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedKeyServiceServer struct{}
|
||||
|
||||
func (UnimplementedKeyServiceServer) Encrypt(context.Context, *EncryptRequest) (*EncryptResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Encrypt not implemented")
|
||||
}
|
||||
func (UnimplementedKeyServiceServer) Decrypt(context.Context, *DecryptRequest) (*DecryptResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Decrypt not implemented")
|
||||
}
|
||||
func (UnimplementedKeyServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeKeyServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to KeyServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeKeyServiceServer interface {
|
||||
mustEmbedUnimplementedKeyServiceServer()
|
||||
}
|
||||
|
||||
func RegisterKeyServiceServer(s grpc.ServiceRegistrar, srv KeyServiceServer) {
|
||||
// If the following call pancis, it indicates UnimplementedKeyServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&KeyService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _KeyService_Encrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(EncryptRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(KeyServiceServer).Encrypt(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: KeyService_Encrypt_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(KeyServiceServer).Encrypt(ctx, req.(*EncryptRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _KeyService_Decrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DecryptRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(KeyServiceServer).Decrypt(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: KeyService_Decrypt_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(KeyServiceServer).Decrypt(ctx, req.(*DecryptRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// KeyService_ServiceDesc is the grpc.ServiceDesc for KeyService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var KeyService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "KeyService",
|
||||
HandlerType: (*KeyServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Encrypt",
|
||||
Handler: _KeyService_Encrypt_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Decrypt",
|
||||
Handler: _KeyService_Decrypt_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "keyservice/keyservice.proto",
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -181,6 +182,38 @@ func ParseKMSContext(in interface{}) map[string]*string {
|
||||
return out
|
||||
}
|
||||
|
||||
// kmsContextToString converts a dictionary into a string that can be parsed
|
||||
// again with ParseKMSContext().
|
||||
func kmsContextToString(in map[string]*string) string {
|
||||
if len(in) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Collect the keys in a slice and compute the expected length
|
||||
keys := make([]string, 0, len(in))
|
||||
length := 0
|
||||
for key := range in {
|
||||
keys = append(keys, key)
|
||||
length += len(key) + len(*in[key]) + 2
|
||||
}
|
||||
|
||||
// Sort the keys
|
||||
sort.Strings(keys)
|
||||
|
||||
// Compose a comma-separated string of key-vale pairs
|
||||
var builder strings.Builder
|
||||
builder.Grow(length)
|
||||
for index, key := range keys {
|
||||
if index > 0 {
|
||||
builder.WriteString(",")
|
||||
}
|
||||
builder.WriteString(key)
|
||||
builder.WriteByte(':')
|
||||
builder.WriteString(*in[key])
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// CredentialsProvider is a wrapper around aws.CredentialsProvider used for
|
||||
// authentication towards AWS KMS.
|
||||
type CredentialsProvider struct {
|
||||
@@ -278,7 +311,18 @@ func (key *MasterKey) NeedsRotation() bool {
|
||||
|
||||
// ToString converts the key to a string representation.
|
||||
func (key *MasterKey) ToString() string {
|
||||
return key.Arn
|
||||
arnRole := key.Arn
|
||||
if key.Role != "" {
|
||||
arnRole = fmt.Sprintf("%s+%s", key.Arn, key.Role)
|
||||
}
|
||||
context := kmsContextToString(key.EncryptionContext)
|
||||
if key.AwsProfile != "" {
|
||||
return fmt.Sprintf("%s|%s|%s", arnRole, context, key.AwsProfile)
|
||||
}
|
||||
if context != "" {
|
||||
return fmt.Sprintf("%s|%s", arnRole, context)
|
||||
}
|
||||
return arnRole
|
||||
}
|
||||
|
||||
// ToMap converts the MasterKey to a map for serialization purposes.
|
||||
|
||||
@@ -33,7 +33,7 @@ const (
|
||||
// testLocalKMSImage is a container image repository reference to a mock
|
||||
// version of AWS' Key Management Service.
|
||||
// Ref: https://github.com/nsmithuk/local-kms
|
||||
testLocalKMSImage = "docker.io/nsmithuk/local-kms"
|
||||
testLocalKMSImage = "ghcr.io/getsops/ci-container-images/local-kms"
|
||||
// testLocalKMSImage is the container image tag to use.
|
||||
testLocalKMSTag = "3.11.1"
|
||||
)
|
||||
@@ -367,8 +367,38 @@ func TestMasterKey_NeedsRotation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMasterKey_ToString(t *testing.T) {
|
||||
dummyARNWithRole := fmt.Sprintf("%s+arn:aws:iam::my-role", dummyARN)
|
||||
|
||||
bar := "bar"
|
||||
bam := "bam"
|
||||
context := map[string]*string{
|
||||
"foo": &bar,
|
||||
"baz": &bam,
|
||||
}
|
||||
|
||||
key := NewMasterKeyFromArn(dummyARN, nil, "")
|
||||
assert.Equal(t, dummyARN, key.ToString())
|
||||
|
||||
key = NewMasterKeyFromArn(dummyARNWithRole, nil, "")
|
||||
assert.Equal(t, dummyARNWithRole, key.ToString())
|
||||
|
||||
key = NewMasterKeyFromArn(dummyARN, nil, "profile")
|
||||
assert.Equal(t, fmt.Sprintf("%s||profile", dummyARN), key.ToString())
|
||||
|
||||
key = NewMasterKeyFromArn(dummyARNWithRole, nil, "profile")
|
||||
assert.Equal(t, fmt.Sprintf("%s||profile", dummyARNWithRole), key.ToString())
|
||||
|
||||
key = NewMasterKeyFromArn(dummyARN, context, "")
|
||||
assert.Equal(t, fmt.Sprintf("%s|baz:bam,foo:bar", dummyARN), key.ToString())
|
||||
|
||||
key = NewMasterKeyFromArn(dummyARNWithRole, context, "")
|
||||
assert.Equal(t, fmt.Sprintf("%s|baz:bam,foo:bar", dummyARNWithRole), key.ToString())
|
||||
|
||||
key = NewMasterKeyFromArn(dummyARN, context, "profile")
|
||||
assert.Equal(t, fmt.Sprintf("%s|baz:bam,foo:bar|profile", dummyARN), key.ToString())
|
||||
|
||||
key = NewMasterKeyFromArn(dummyARNWithRole, context, "profile")
|
||||
assert.Equal(t, fmt.Sprintf("%s|baz:bam,foo:bar|profile", dummyARNWithRole), key.ToString())
|
||||
}
|
||||
|
||||
func TestMasterKey_ToMap(t *testing.T) {
|
||||
|
||||
@@ -428,7 +428,17 @@ func (key *MasterKey) decryptWithGnuPG() ([]byte, error) {
|
||||
return nil, fmt.Errorf("failed to decrypt sops data key with pgp: %s",
|
||||
strings.TrimSpace(stderr.String()))
|
||||
}
|
||||
return stdout.Bytes(), nil
|
||||
result := stdout.Bytes()
|
||||
if len(result) == 0 {
|
||||
// This can happen if an older GnuPG version is used to decrypt a key encrypted with a
|
||||
// newer GnuPG version that used an AEAD cipher, which the old version does not support.
|
||||
// Apparently some GnuPG versions drop the unspuported packets, which results in a decrypted
|
||||
// data of 0 bytes, and returns nothing with exit code 0.
|
||||
//
|
||||
// (See https://github.com/getsops/sops/issues/896#issuecomment-2688079300 for more infos.)
|
||||
return nil, fmt.Errorf("failed to decrypt sops data key with pgp: zero bytes returned")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// NeedsRotation returns whether the data key needs to be rotated
|
||||
@@ -634,7 +644,13 @@ func gnuPGHome(customPath string) string {
|
||||
// This is mostly used for compatibility reasons, as older versions of GnuPG
|
||||
// do not always like long IDs.
|
||||
func shortenFingerprint(fingerprint string) string {
|
||||
if offset := len(fingerprint) - 16; offset > 0 {
|
||||
offset := len(fingerprint) - 16
|
||||
// If the fingerprint ends with '!', we must include '!' in the ID *and* the
|
||||
// 16 hex digits before it. See https://github.com/getsops/sops/issues/1365.
|
||||
if strings.HasSuffix(fingerprint, "!") {
|
||||
offset -= 1
|
||||
}
|
||||
if offset > 0 {
|
||||
fingerprint = fingerprint[offset:]
|
||||
}
|
||||
return fingerprint
|
||||
|
||||
@@ -697,10 +697,23 @@ func Test_gnuPGHome(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_shortenFingerprint(t *testing.T) {
|
||||
// Test with regular fingerprint
|
||||
shortId := shortenFingerprint(mockFingerprint)
|
||||
assert.Equal(t, "9732075EA221A7EA", shortId)
|
||||
|
||||
assert.Equal(t, shortId, shortenFingerprint(shortId))
|
||||
|
||||
// Test with forced subkey
|
||||
shortId = shortenFingerprint(mockFingerprint + "!")
|
||||
assert.Equal(t, "9732075EA221A7EA!", shortId)
|
||||
|
||||
assert.Equal(t, shortId, shortenFingerprint(shortId))
|
||||
|
||||
// Make sure that too short IDs are kept
|
||||
for _, tooShort := range []string{"012345679abcdef", "012345679abcdef!", "123", "123!"} {
|
||||
shortId = shortenFingerprint(tooShort)
|
||||
assert.Equal(t, tooShort, shortId)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(hidde): previous tests kept around for now.
|
||||
|
||||
3
rust-toolchain.toml
Normal file
3
rust-toolchain.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "1.85.0"
|
||||
profile = "minimal"
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
mathrand "math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -190,7 +189,6 @@ func Split(secret []byte, parts, threshold int) ([][]byte, error) {
|
||||
// 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
|
||||
|
||||
88
sops.go
88
sops.go
@@ -127,6 +127,48 @@ type TreeBranch []TreeItem
|
||||
// Trees usually have more than one branch
|
||||
type TreeBranches []TreeBranch
|
||||
|
||||
func equals(oneBranch interface{}, otherBranch interface{}) bool {
|
||||
switch oneBranch := oneBranch.(type) {
|
||||
case TreeBranch:
|
||||
otherBranch, ok := otherBranch.(TreeBranch)
|
||||
if !ok || len(oneBranch) != len(otherBranch) {
|
||||
return false
|
||||
}
|
||||
for i, item := range oneBranch {
|
||||
otherItem := otherBranch[i]
|
||||
if !equals(item.Key, otherItem.Key) || !equals(item.Value, otherItem.Value) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case []interface{}:
|
||||
otherBranch, ok := otherBranch.([]interface{})
|
||||
if !ok || len(oneBranch) != len(otherBranch) {
|
||||
return false
|
||||
}
|
||||
for i, item := range oneBranch {
|
||||
if !equals(item, otherBranch[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case Comment:
|
||||
otherBranch, ok := otherBranch.(Comment)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return oneBranch.Value == otherBranch.Value
|
||||
default:
|
||||
// Unexpected type
|
||||
return oneBranch == otherBranch
|
||||
}
|
||||
}
|
||||
|
||||
// Compare a branch with another one
|
||||
func (branch TreeBranch) Equals(other TreeBranch) bool {
|
||||
return equals(branch, other)
|
||||
}
|
||||
|
||||
func valueFromPathAndLeaf(path []interface{}, leaf interface{}) interface{} {
|
||||
switch component := path[0].(type) {
|
||||
case int:
|
||||
@@ -156,47 +198,55 @@ func valueFromPathAndLeaf(path []interface{}, leaf interface{}) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
func set(branch interface{}, path []interface{}, value interface{}) interface{} {
|
||||
func set(branch interface{}, path []interface{}, value interface{}) (interface{}, bool) {
|
||||
switch branch := branch.(type) {
|
||||
case TreeBranch:
|
||||
for i, item := range branch {
|
||||
if item.Key == path[0] {
|
||||
var changed bool
|
||||
if len(path) == 1 {
|
||||
changed = !equals(branch[i].Value, value)
|
||||
branch[i].Value = value
|
||||
} else {
|
||||
branch[i].Value = set(item.Value, path[1:], value)
|
||||
branch[i].Value, changed = set(item.Value, path[1:], value)
|
||||
}
|
||||
return branch
|
||||
return branch, changed
|
||||
}
|
||||
}
|
||||
// Not found, need to add the next path entry to the branch
|
||||
value := valueFromPathAndLeaf(path, value)
|
||||
if newBranch, ok := value.(TreeBranch); ok && len(newBranch) > 0 {
|
||||
return append(branch, newBranch[0])
|
||||
return append(branch, newBranch[0]), true
|
||||
}
|
||||
return branch
|
||||
return branch, true
|
||||
case []interface{}:
|
||||
position := path[0].(int)
|
||||
var changed bool
|
||||
if len(path) == 1 {
|
||||
if position >= len(branch) {
|
||||
return append(branch, value)
|
||||
return append(branch, value), true
|
||||
}
|
||||
changed = !equals(branch[position], value)
|
||||
branch[position] = value
|
||||
} else {
|
||||
if position >= len(branch) {
|
||||
branch = append(branch, valueFromPathAndLeaf(path[1:], value))
|
||||
changed = true
|
||||
} else {
|
||||
branch[position], changed = set(branch[position], path[1:], value)
|
||||
}
|
||||
branch[position] = set(branch[position], path[1:], value)
|
||||
}
|
||||
return branch
|
||||
return branch, changed
|
||||
default:
|
||||
return valueFromPathAndLeaf(path, value)
|
||||
newValue := valueFromPathAndLeaf(path, value)
|
||||
return newValue, !equals(branch, newValue)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
func (branch TreeBranch) Set(path []interface{}, value interface{}) (TreeBranch, bool) {
|
||||
v, changed := set(branch, path, value)
|
||||
return v.(TreeBranch), changed
|
||||
}
|
||||
|
||||
func unset(branch interface{}, path []interface{}) (interface{}, error) {
|
||||
@@ -297,6 +347,8 @@ func (branch TreeBranch) walkValue(in interface{}, path []string, commentsStack
|
||||
return onLeaves(in, path, commentsStack)
|
||||
case float64:
|
||||
return onLeaves(in, path, commentsStack)
|
||||
case time.Time:
|
||||
return onLeaves(in, path, commentsStack)
|
||||
case Comment:
|
||||
return onLeaves(in, path, commentsStack)
|
||||
case TreeBranch:
|
||||
@@ -700,6 +752,11 @@ func (m *Metadata) UpdateMasterKeysWithKeyServices(dataKey []byte, svcs []keyser
|
||||
fmt.Errorf("no key services provided, cannot update master keys"),
|
||||
}
|
||||
}
|
||||
if len(m.KeyGroups) == 0 {
|
||||
return []error{
|
||||
fmt.Errorf("no key groups provided"),
|
||||
}
|
||||
}
|
||||
var parts [][]byte
|
||||
if len(m.KeyGroups) == 1 {
|
||||
// If there's only one key group, we can't do Shamir. All keys
|
||||
@@ -726,6 +783,11 @@ func (m *Metadata) UpdateMasterKeysWithKeyServices(dataKey []byte, svcs []keyser
|
||||
}
|
||||
for i, group := range m.KeyGroups {
|
||||
part := parts[i]
|
||||
if len(group) == 0 {
|
||||
return []error{
|
||||
fmt.Errorf("empty key group provided"),
|
||||
}
|
||||
}
|
||||
for _, key := range group {
|
||||
svcKey := keyservice.KeyFromMasterKey(key)
|
||||
var keyErrs []error
|
||||
@@ -762,7 +824,7 @@ func (m *Metadata) UpdateMasterKeys(dataKey []byte) (errs []error) {
|
||||
|
||||
// GetDataKeyWithKeyServices retrieves the data key, asking KeyServices to decrypt it with each
|
||||
// MasterKey in the Metadata's KeySources until one of them succeeds.
|
||||
func (m Metadata) GetDataKeyWithKeyServices(svcs []keyservice.KeyServiceClient, decryptionOrder []string) ([]byte, error) {
|
||||
func (m *Metadata) GetDataKeyWithKeyServices(svcs []keyservice.KeyServiceClient, decryptionOrder []string) ([]byte, error) {
|
||||
if m.DataKey != nil {
|
||||
return m.DataKey, nil
|
||||
}
|
||||
@@ -908,6 +970,8 @@ func ToBytes(in interface{}) ([]byte, error) {
|
||||
return boolB, nil
|
||||
case []byte:
|
||||
return in, nil
|
||||
case time.Time:
|
||||
return in.MarshalText()
|
||||
case Comment:
|
||||
return ToBytes(in.Value)
|
||||
default:
|
||||
|
||||
431
sops_test.go
431
sops_test.go
@@ -633,8 +633,7 @@ func TestUnencryptedCommentRegexFail(t *testing.T) {
|
||||
tree := Tree{Branches: branches, Metadata: Metadata{UnencryptedCommentRegex: "ENC"}}
|
||||
cipher := encPrefixCipher{}
|
||||
_, err := tree.Encrypt(bytes.Repeat([]byte("f"), 32), cipher)
|
||||
assert.NotNil(t, err)
|
||||
assert.Contains(t, err.Error(), "Encrypted comment \"ENC:sops:noenc\" matches UnencryptedCommentRegex!")
|
||||
assert.ErrorContains(t, err, "Encrypted comment \"ENC:sops:noenc\" matches UnencryptedCommentRegex!")
|
||||
}
|
||||
|
||||
type MockCipher struct{}
|
||||
@@ -845,6 +844,23 @@ func TestDecrypt(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type WrongType struct{}
|
||||
|
||||
func TestEncryptWrongType(t *testing.T) {
|
||||
branches := TreeBranches{
|
||||
TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: WrongType{},
|
||||
},
|
||||
},
|
||||
}
|
||||
tree := Tree{Branches: branches, Metadata: Metadata{UnencryptedSuffix: DefaultUnencryptedSuffix}}
|
||||
result, err := tree.Encrypt(bytes.Repeat([]byte{'f'}, 32), MockCipher{})
|
||||
assert.Equal(t, "", result)
|
||||
assert.ErrorContains(t, err, "unknown type: sops.WrongType")
|
||||
}
|
||||
|
||||
func TestTruncateTree(t *testing.T) {
|
||||
tree := TreeBranch{
|
||||
TreeItem{
|
||||
@@ -872,10 +888,49 @@ func TestTruncateTree(t *testing.T) {
|
||||
"foobar",
|
||||
2,
|
||||
})
|
||||
assert.Equal(t, nil, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
|
||||
func TestTruncateTreeNotFound(t *testing.T) {
|
||||
tree := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: 2,
|
||||
},
|
||||
}
|
||||
result, err := tree.Truncate([]interface{}{"baz"})
|
||||
assert.ErrorContains(t, err, "baz")
|
||||
assert.Nil(t, result, "Truncate result was not nil upon %s", err)
|
||||
}
|
||||
|
||||
func TestTruncateTreeNotArray(t *testing.T) {
|
||||
tree := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: 2,
|
||||
},
|
||||
}
|
||||
result, err := tree.Truncate([]interface{}{"foo", 99})
|
||||
assert.ErrorContains(t, err, "99")
|
||||
assert.Nil(t, result, "Truncate result was not nil upon %s", err)
|
||||
}
|
||||
|
||||
func TestTruncateTreeArrayOutOfBounds(t *testing.T) {
|
||||
tree := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: []interface{}{
|
||||
"one",
|
||||
"two",
|
||||
},
|
||||
},
|
||||
}
|
||||
result, err := tree.Truncate([]interface{}{"foo", 99})
|
||||
assert.ErrorContains(t, err, "99")
|
||||
assert.Nil(t, result, "Truncate result was not nil upon %s", err)
|
||||
}
|
||||
|
||||
func TestEncryptComments(t *testing.T) {
|
||||
tree := Tree{
|
||||
Branches: TreeBranches{
|
||||
@@ -968,10 +1023,33 @@ func TestSetNewKey(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
set := branch.Set([]interface{}{"foo", "bar", "foo"}, "hello")
|
||||
set, changed := branch.Set([]interface{}{"foo", "bar", "foo"}, "hello")
|
||||
assert.Equal(t, true, changed)
|
||||
assert.Equal(t, "hello", set[0].Value.(TreeBranch)[0].Value.(TreeBranch)[1].Value)
|
||||
}
|
||||
|
||||
func TestSetNewKeyUnchanged(t *testing.T) {
|
||||
branch := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: TreeBranch{
|
||||
TreeItem{
|
||||
Key: "bar",
|
||||
Value: TreeBranch{
|
||||
TreeItem{
|
||||
Key: "baz",
|
||||
Value: "foobar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
set, changed := branch.Set([]interface{}{"foo", "bar", "baz"}, "foobar")
|
||||
assert.Equal(t, false, changed)
|
||||
assert.Equal(t, "foobar", set[0].Value.(TreeBranch)[0].Value.(TreeBranch)[0].Value)
|
||||
}
|
||||
|
||||
func TestSetNewBranch(t *testing.T) {
|
||||
branch := TreeBranch{
|
||||
TreeItem{
|
||||
@@ -979,7 +1057,8 @@ func TestSetNewBranch(t *testing.T) {
|
||||
Value: "value",
|
||||
},
|
||||
}
|
||||
set := branch.Set([]interface{}{"foo", "bar", "baz"}, "hello")
|
||||
set, changed := branch.Set([]interface{}{"foo", "bar", "baz"}, "hello")
|
||||
assert.Equal(t, true, changed)
|
||||
assert.Equal(t, TreeBranch{
|
||||
TreeItem{
|
||||
Key: "key",
|
||||
@@ -1012,7 +1091,8 @@ func TestSetArrayDeepNew(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
set := branch.Set([]interface{}{"foo", 2, "bar"}, "hello")
|
||||
set, changed := branch.Set([]interface{}{"foo", 2, "bar"}, "hello")
|
||||
assert.Equal(t, true, changed)
|
||||
assert.Equal(t, "hello", set[0].Value.([]interface{})[2].(TreeBranch)[0].Value)
|
||||
}
|
||||
|
||||
@@ -1023,13 +1103,15 @@ func TestSetNewKeyDeep(t *testing.T) {
|
||||
Value: "bar",
|
||||
},
|
||||
}
|
||||
set := branch.Set([]interface{}{"foo", "bar", "baz"}, "hello")
|
||||
set, changed := branch.Set([]interface{}{"foo", "bar", "baz"}, "hello")
|
||||
assert.Equal(t, true, changed)
|
||||
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")
|
||||
set, changed := branch.Set([]interface{}{"foo", "bar", "baz"}, "hello")
|
||||
assert.Equal(t, true, changed)
|
||||
assert.Equal(t, "hello", set[0].Value.(TreeBranch)[0].Value.(TreeBranch)[0].Value)
|
||||
}
|
||||
|
||||
@@ -1044,13 +1126,15 @@ func TestSetArray(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
set := branch.Set([]interface{}{"foo", 0}, "uno")
|
||||
set, changed := branch.Set([]interface{}{"foo", 0}, "uno")
|
||||
assert.Equal(t, true, changed)
|
||||
assert.Equal(t, "uno", set[0].Value.([]interface{})[0])
|
||||
}
|
||||
|
||||
func TestSetArrayNew(t *testing.T) {
|
||||
branch := TreeBranch{}
|
||||
set := branch.Set([]interface{}{"foo", 0, 0}, "uno")
|
||||
set, changed := branch.Set([]interface{}{"foo", 0, 0}, "uno")
|
||||
assert.Equal(t, true, changed)
|
||||
assert.Equal(t, "uno", set[0].Value.([]interface{})[0].([]interface{})[0])
|
||||
}
|
||||
|
||||
@@ -1061,7 +1145,8 @@ func TestSetExisting(t *testing.T) {
|
||||
Value: "foobar",
|
||||
},
|
||||
}
|
||||
set := branch.Set([]interface{}{"foo"}, "bar")
|
||||
set, changed := branch.Set([]interface{}{"foo"}, "bar")
|
||||
assert.Equal(t, true, changed)
|
||||
assert.Equal(t, "bar", set[0].Value)
|
||||
}
|
||||
|
||||
@@ -1072,7 +1157,8 @@ func TestSetArrayLeafNewItem(t *testing.T) {
|
||||
Value: []interface{}{},
|
||||
},
|
||||
}
|
||||
set := branch.Set([]interface{}{"array", 2}, "hello")
|
||||
set, changed := branch.Set([]interface{}{"array", 2}, "hello")
|
||||
assert.Equal(t, true, changed)
|
||||
assert.Equal(t, TreeBranch{
|
||||
TreeItem{
|
||||
Key: "array",
|
||||
@@ -1092,7 +1178,8 @@ func TestSetArrayNonLeaf(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
set := branch.Set([]interface{}{"array", 0, "hello"}, "hello")
|
||||
set, changed := branch.Set([]interface{}{"array", 0, "hello"}, "hello")
|
||||
assert.Equal(t, true, changed)
|
||||
assert.Equal(t, TreeBranch{
|
||||
TreeItem{
|
||||
Key: "array",
|
||||
@@ -1108,6 +1195,315 @@ func TestSetArrayNonLeaf(t *testing.T) {
|
||||
}, set)
|
||||
}
|
||||
|
||||
func TestUnsetKeyRootLeaf(t *testing.T) {
|
||||
branch := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: "foo",
|
||||
},
|
||||
TreeItem{
|
||||
Key: "foofoo",
|
||||
Value: "foofoo",
|
||||
},
|
||||
}
|
||||
unset, err := branch.Unset([]interface{}{"foofoo"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: "foo",
|
||||
},
|
||||
}, unset)
|
||||
}
|
||||
|
||||
func TestUnsetKeyBranchLeaf(t *testing.T) {
|
||||
branch := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: TreeBranch{
|
||||
TreeItem{
|
||||
Key: "bar",
|
||||
Value: "bar",
|
||||
},
|
||||
TreeItem{
|
||||
Key: "barbar",
|
||||
Value: "barbar",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unset, err := branch.Unset([]interface{}{"foo", "barbar"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: TreeBranch{
|
||||
TreeItem{
|
||||
Key: "bar",
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, unset)
|
||||
}
|
||||
|
||||
func TestUnsetKeyBranch(t *testing.T) {
|
||||
branch := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: "foo",
|
||||
},
|
||||
TreeItem{
|
||||
Key: "foofoo",
|
||||
Value: TreeBranch{
|
||||
TreeItem{
|
||||
Key: "bar",
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unset, err := branch.Unset([]interface{}{"foofoo"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: "foo",
|
||||
},
|
||||
}, unset)
|
||||
}
|
||||
|
||||
func TestUnsetKeyRootLastLeaf(t *testing.T) {
|
||||
branch := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: "foo",
|
||||
},
|
||||
}
|
||||
unset, err := branch.Unset([]interface{}{"foo"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, TreeBranch{
|
||||
}, unset)
|
||||
}
|
||||
|
||||
func TestUnsetKeyBranchLastLeaf(t *testing.T) {
|
||||
branch := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: TreeBranch{
|
||||
TreeItem{
|
||||
Key: "bar",
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unset, err := branch.Unset([]interface{}{"foo", "bar"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: TreeBranch{
|
||||
},
|
||||
},
|
||||
}, unset)
|
||||
}
|
||||
|
||||
func TestUnsetKeyArray(t *testing.T) {
|
||||
branch := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: TreeBranch{
|
||||
TreeItem{
|
||||
Key: "bar",
|
||||
Value: []interface{}{
|
||||
TreeBranch{
|
||||
TreeItem{
|
||||
Key: "baz",
|
||||
Value: "baz",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unset, err := branch.Unset([]interface{}{"foo", "bar"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: TreeBranch{
|
||||
},
|
||||
},
|
||||
}, unset)
|
||||
}
|
||||
|
||||
func TestUnsetArrayItem(t *testing.T) {
|
||||
branch := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: []interface{}{
|
||||
TreeBranch{
|
||||
TreeItem{
|
||||
Key: "bar",
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
TreeBranch{
|
||||
TreeItem{
|
||||
Key: "barbar",
|
||||
Value: "barbar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unset, err := branch.Unset([]interface{}{"foo", 1})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: []interface{}{
|
||||
TreeBranch{
|
||||
TreeItem{
|
||||
Key: "bar",
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, unset)
|
||||
}
|
||||
|
||||
func TestUnsetKeyInArrayItem(t *testing.T) {
|
||||
branch := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: []interface{}{
|
||||
TreeBranch{
|
||||
TreeItem{
|
||||
Key: "bar",
|
||||
Value: "bar",
|
||||
},
|
||||
TreeItem{
|
||||
Key: "barbar",
|
||||
Value: "barbar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unset, err := branch.Unset([]interface{}{"foo", 0, "barbar"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: []interface{}{
|
||||
TreeBranch{
|
||||
TreeItem{
|
||||
Key: "bar",
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, unset)
|
||||
}
|
||||
|
||||
func TestUnsetArrayLastItem(t *testing.T) {
|
||||
branch := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: []interface{}{
|
||||
TreeBranch{
|
||||
TreeItem{
|
||||
Key: "bar",
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unset, err := branch.Unset([]interface{}{"foo", 0})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: []interface{}{
|
||||
},
|
||||
},
|
||||
}, unset)
|
||||
}
|
||||
|
||||
func TestUnsetKeyNotFound(t *testing.T) {
|
||||
branch := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: TreeBranch{
|
||||
TreeItem{
|
||||
Key: "bar",
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unset, err := branch.Unset([]interface{}{"foo", "unknown-value"})
|
||||
assert.Equal(t, err.(*SopsKeyNotFound).Key, "unknown-value")
|
||||
assert.ErrorContains(t, err, "unknown-value")
|
||||
assert.Nil(t, unset, "Unset result was not nil upon %s", err)
|
||||
}
|
||||
|
||||
func TestUnsetKeyInArrayNotFound(t *testing.T) {
|
||||
branch := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: []interface{}{
|
||||
TreeBranch{
|
||||
TreeItem{
|
||||
Key: "bar",
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unset, err := branch.Unset([]interface{}{"foo", 0, "unknown"})
|
||||
assert.Equal(t, err.(*SopsKeyNotFound).Key, "unknown")
|
||||
assert.Nil(t, unset, "Unset result was not nil upon %s", err)
|
||||
}
|
||||
|
||||
func TestUnsetArrayItemOutOfBounds(t *testing.T) {
|
||||
branch := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: []interface{}{
|
||||
TreeBranch{
|
||||
TreeItem{
|
||||
Key: "bar",
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
unset, err := branch.Unset([]interface{}{"foo", 99})
|
||||
assert.Equal(t, err.(*SopsKeyNotFound).Key, 99)
|
||||
assert.Nil(t, unset, "Unset result was not nil upon %s", err)
|
||||
}
|
||||
|
||||
func TestUnsetKeyNotABranch(t *testing.T) {
|
||||
branch := TreeBranch{
|
||||
TreeItem{
|
||||
Key: "foo",
|
||||
Value: 99,
|
||||
},
|
||||
}
|
||||
unset, err := branch.Unset([]interface{}{"foo", "bar"})
|
||||
assert.ErrorContains(t, err, "Unsupported type")
|
||||
assert.Nil(t, unset, "Unset result was not nil upon %s", err)
|
||||
}
|
||||
|
||||
func TestEmitAsMap(t *testing.T) {
|
||||
expected := map[string]interface{}{
|
||||
"foobar": "barfoo",
|
||||
@@ -1128,6 +1524,10 @@ func TestEmitAsMap(t *testing.T) {
|
||||
Key: "number",
|
||||
Value: 42,
|
||||
},
|
||||
TreeItem{
|
||||
Key: Comment{"comment"},
|
||||
Value: nil,
|
||||
},
|
||||
},
|
||||
TreeBranch{
|
||||
TreeItem{
|
||||
@@ -1149,9 +1549,8 @@ func TestEmitAsMap(t *testing.T) {
|
||||
|
||||
data, err := EmitAsMap(branches)
|
||||
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, expected, data)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, data)
|
||||
}
|
||||
|
||||
func TestSortKeyGroupIndices(t *testing.T) {
|
||||
|
||||
@@ -24,7 +24,8 @@ func NewStore(c *config.INIStoreConfig) *Store {
|
||||
}
|
||||
|
||||
func (store Store) encodeTree(branches sops.TreeBranches) ([]byte, error) {
|
||||
iniFile := ini.Empty()
|
||||
iniFile := ini.Empty(ini.LoadOptions{AllowNonUniqueSections: true})
|
||||
iniFile.DeleteSection(ini.DefaultSection)
|
||||
for _, branch := range branches {
|
||||
for _, item := range branch {
|
||||
if _, ok := item.Key.(sops.Comment); ok {
|
||||
@@ -95,7 +96,7 @@ func (store Store) iniFromTreeBranches(branches sops.TreeBranches) ([]byte, erro
|
||||
}
|
||||
|
||||
func (store Store) treeBranchesFromIni(in []byte) (sops.TreeBranches, error) {
|
||||
iniFile, err := ini.Load(in)
|
||||
iniFile, err := ini.LoadSources(ini.LoadOptions{AllowNonUniqueSections: true}, in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -143,7 +144,7 @@ func (store Store) treeItemFromSection(section *ini.Section) (sops.TreeItem, err
|
||||
|
||||
// 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)
|
||||
iniFileOuter, err := ini.LoadSources(ini.LoadOptions{AllowNonUniqueSections: true}, in)
|
||||
if err != nil {
|
||||
return sops.Tree{}, err
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package ini
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/getsops/sops/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDecodeIni(t *testing.T) {
|
||||
@@ -127,6 +127,55 @@ func TestEncodeIniWithEscaping(t *testing.T) {
|
||||
assert.Equal(t, expected, branches)
|
||||
}
|
||||
|
||||
func TestEncodeIniWithDuplicateSections(t *testing.T) {
|
||||
branches := sops.TreeBranches{
|
||||
sops.TreeBranch{
|
||||
sops.TreeItem{
|
||||
Key: "DEFAULT",
|
||||
Value: interface{}(sops.TreeBranch(nil)),
|
||||
},
|
||||
sops.TreeItem{
|
||||
Key: "foo",
|
||||
Value: sops.TreeBranch{
|
||||
sops.TreeItem{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
},
|
||||
sops.TreeItem{
|
||||
Key: "baz",
|
||||
Value: "3.0",
|
||||
},
|
||||
sops.TreeItem{
|
||||
Key: "qux",
|
||||
Value: "false",
|
||||
},
|
||||
},
|
||||
},
|
||||
sops.TreeItem{
|
||||
Key: "foo",
|
||||
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 TestUnmarshalMetadataFromNonSOPSFile(t *testing.T) {
|
||||
data := []byte(`hello=2`)
|
||||
store := Store{}
|
||||
|
||||
@@ -186,18 +186,20 @@ func (store Store) encodeValue(v interface{}) ([]byte, error) {
|
||||
|
||||
func (store Store) encodeArray(array []interface{}) ([]byte, error) {
|
||||
out := "["
|
||||
for i, item := range array {
|
||||
empty := true
|
||||
for _, item := range array {
|
||||
if _, ok := item.(sops.Comment); ok {
|
||||
continue
|
||||
}
|
||||
if !empty {
|
||||
out += ","
|
||||
}
|
||||
v, err := store.encodeValue(item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out += string(v)
|
||||
if i != len(array)-1 {
|
||||
out += ","
|
||||
}
|
||||
empty = false
|
||||
}
|
||||
out += "]"
|
||||
return []byte(out), nil
|
||||
@@ -205,10 +207,14 @@ func (store Store) encodeArray(array []interface{}) ([]byte, error) {
|
||||
|
||||
func (store Store) encodeTree(tree sops.TreeBranch) ([]byte, error) {
|
||||
out := "{"
|
||||
for i, item := range tree {
|
||||
empty := true
|
||||
for _, item := range tree {
|
||||
if _, ok := item.Key.(sops.Comment); ok {
|
||||
continue
|
||||
}
|
||||
if !empty {
|
||||
out += ","
|
||||
}
|
||||
v, err := store.encodeValue(item.Value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error encoding value %s: %s", v, err)
|
||||
@@ -218,9 +224,7 @@ func (store Store) encodeTree(tree sops.TreeBranch) ([]byte, error) {
|
||||
return nil, fmt.Errorf("Error encoding key %s: %s", k, err)
|
||||
}
|
||||
out += string(k) + `: ` + string(v)
|
||||
if i != len(tree)-1 {
|
||||
out += ","
|
||||
}
|
||||
empty = false
|
||||
}
|
||||
return []byte(out + "}"), nil
|
||||
}
|
||||
@@ -326,6 +330,7 @@ func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error marshaling to json: %s", err)
|
||||
}
|
||||
out = append(out, '\n')
|
||||
return out, nil
|
||||
}
|
||||
|
||||
@@ -336,6 +341,7 @@ func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error marshaling to json: %s", err)
|
||||
}
|
||||
out = append(out, '\n')
|
||||
return out, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,8 @@ func TestDecodeJSON(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
}
|
||||
`
|
||||
expected := sops.TreeBranch{
|
||||
sops.TreeItem{
|
||||
Key: "glossary",
|
||||
@@ -312,7 +313,8 @@ func TestEncodeJSONArrayOfObjects(t *testing.T) {
|
||||
},
|
||||
2
|
||||
]
|
||||
}`
|
||||
}
|
||||
`
|
||||
store := Store{
|
||||
config: config.JSONStoreConfig{
|
||||
Indent: -1,
|
||||
@@ -446,7 +448,8 @@ func TestIndentTwoSpaces(t *testing.T) {
|
||||
},
|
||||
2
|
||||
]
|
||||
}`
|
||||
}
|
||||
`
|
||||
store := Store{
|
||||
config: config.JSONStoreConfig{
|
||||
Indent: 2,
|
||||
@@ -488,7 +491,8 @@ func TestIndentDefault(t *testing.T) {
|
||||
},
|
||||
2
|
||||
]
|
||||
}`
|
||||
}
|
||||
`
|
||||
store := Store{
|
||||
config: config.JSONStoreConfig{
|
||||
Indent: -1,
|
||||
@@ -530,7 +534,8 @@ func TestNoIndent(t *testing.T) {
|
||||
},
|
||||
2
|
||||
]
|
||||
}`
|
||||
}
|
||||
`
|
||||
store := Store{
|
||||
config: config.JSONStoreConfig{
|
||||
Indent: 0,
|
||||
@@ -539,4 +544,94 @@ func TestNoIndent(t *testing.T) {
|
||||
out, err := store.EmitPlainFile(tree.Branches)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expected, string(out))
|
||||
|
||||
}
|
||||
|
||||
func TestConflictingAttributes(t *testing.T) {
|
||||
// See https://stackoverflow.com/a/23195243
|
||||
// Duplicate keys in json is technically valid, but discouraged.
|
||||
// Implementations may handle them differently. ECMA-262 says
|
||||
//
|
||||
// > In the case where there are duplicate name Strings within an object,
|
||||
// > lexically preceding values for the same key shall be overwritten.
|
||||
|
||||
data := `
|
||||
{
|
||||
"hello": "Sops config file",
|
||||
"hello": "Doubles are ok",
|
||||
"hello": ["repeatedly"],
|
||||
"hello": 3.14
|
||||
}
|
||||
`
|
||||
s := new(Store)
|
||||
_, err := s.LoadPlainFile([]byte(data))
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestComments(t *testing.T) {
|
||||
tree := sops.Tree{
|
||||
Branches: sops.TreeBranches{
|
||||
sops.TreeBranch{
|
||||
sops.TreeItem{
|
||||
Key: "foo",
|
||||
Value: []interface{}{
|
||||
sops.Comment{Value: " comment 0"},
|
||||
sops.TreeBranch{
|
||||
sops.TreeItem{
|
||||
Key: sops.Comment{Value: " comment 1"},
|
||||
Value: nil,
|
||||
},
|
||||
sops.TreeItem{
|
||||
Key: "foo",
|
||||
Value: 3,
|
||||
},
|
||||
sops.TreeItem{
|
||||
Key: sops.Comment{Value: " comment 2"},
|
||||
Value: nil,
|
||||
},
|
||||
sops.TreeItem{
|
||||
Key: sops.Comment{Value: " comment 3"},
|
||||
Value: nil,
|
||||
},
|
||||
sops.TreeItem{
|
||||
Key: "bar",
|
||||
Value: false,
|
||||
},
|
||||
sops.TreeItem{
|
||||
Key: sops.Comment{Value: " comment 4"},
|
||||
Value: nil,
|
||||
},
|
||||
sops.TreeItem{
|
||||
Key: sops.Comment{Value: " comment 5"},
|
||||
Value: nil,
|
||||
},
|
||||
},
|
||||
sops.Comment{Value: " comment 6"},
|
||||
sops.Comment{Value: " comment 7"},
|
||||
2,
|
||||
sops.Comment{Value: " comment 8"},
|
||||
sops.Comment{Value: " comment 9"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
expected := `{
|
||||
"foo": [
|
||||
{
|
||||
"foo": 3,
|
||||
"bar": false
|
||||
},
|
||||
2
|
||||
]
|
||||
}
|
||||
`
|
||||
store := Store{
|
||||
config: config.JSONStoreConfig{
|
||||
Indent: 2,
|
||||
},
|
||||
}
|
||||
out, err := store.EmitPlainFile(tree.Branches)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expected, string(out))
|
||||
}
|
||||
|
||||
@@ -307,6 +307,13 @@ func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
|
||||
// sops.Tree runtime object
|
||||
func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
|
||||
var branches sops.TreeBranches
|
||||
if len(in) > 0 {
|
||||
// This is needed to make the yaml-decoder check for uniqueness of keys
|
||||
// Can probably be removed when https://github.com/go-yaml/yaml/issues/814 is merged.
|
||||
if err := yaml.NewDecoder(bytes.NewReader(in)).Decode(make(map[string]interface{})); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
d := yaml.NewDecoder(bytes.NewReader(in))
|
||||
for {
|
||||
var data yaml.Node
|
||||
@@ -322,6 +329,12 @@ func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error unmarshaling input YAML: %s", err)
|
||||
}
|
||||
// Prevent use of reserved keywords
|
||||
for _, item := range branch {
|
||||
if item.Key == stores.SopsMetadataKey {
|
||||
return nil, fmt.Errorf("YAML doc used reserved word '%v'", item.Key)
|
||||
}
|
||||
}
|
||||
branches = append(branches, branch)
|
||||
}
|
||||
return branches, nil
|
||||
|
||||
@@ -62,19 +62,19 @@ key4: *bar
|
||||
var ALIASES_BRANCHES = sops.TreeBranches{
|
||||
sops.TreeBranch{
|
||||
sops.TreeItem{
|
||||
Key: "key1",
|
||||
Key: "key1",
|
||||
Value: []interface{}{
|
||||
"foo",
|
||||
},
|
||||
},
|
||||
sops.TreeItem{
|
||||
Key: "key2",
|
||||
Key: "key2",
|
||||
Value: []interface{}{
|
||||
"foo",
|
||||
},
|
||||
},
|
||||
sops.TreeItem{
|
||||
Key: "key3",
|
||||
Key: "key3",
|
||||
Value: sops.TreeBranch{
|
||||
sops.TreeItem{
|
||||
Key: "foo",
|
||||
@@ -87,7 +87,7 @@ var ALIASES_BRANCHES = sops.TreeBranches{
|
||||
},
|
||||
},
|
||||
sops.TreeItem{
|
||||
Key: "key4",
|
||||
Key: "key4",
|
||||
Value: sops.TreeBranch{
|
||||
sops.TreeItem{
|
||||
Key: "foo",
|
||||
@@ -237,7 +237,6 @@ prometheus-node-exporter:
|
||||
- --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)$
|
||||
`)
|
||||
|
||||
|
||||
func TestUnmarshalMetadataFromNonSOPSFile(t *testing.T) {
|
||||
data := []byte(`hello: 2`)
|
||||
_, err := (&Store{}).LoadEncryptedFile(data)
|
||||
@@ -398,3 +397,35 @@ func TestHasSopsTopLevelKey(t *testing.T) {
|
||||
})
|
||||
assert.Equal(t, ok, false)
|
||||
}
|
||||
|
||||
func TestDuplicateAttributes(t *testing.T) {
|
||||
// Duplicate keys are _not_ valid yaml.
|
||||
//
|
||||
// See https://yaml.org/spec/1.2.2/#mapping
|
||||
// > The content of a mapping node is an unordered set of key/value node pairs,
|
||||
// > with the restriction that each of the keys is unique.
|
||||
//
|
||||
data := `
|
||||
hello: Sops config file
|
||||
hello: Duplicates are not ok
|
||||
rootunique:
|
||||
key2: "value"
|
||||
key2: "foo"
|
||||
`
|
||||
s := new(Store)
|
||||
_, err := s.LoadPlainFile([]byte(data))
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, `yaml: unmarshal errors:
|
||||
line 3: mapping key "hello" already defined at line 2`, err.Error())
|
||||
}
|
||||
|
||||
func TestReservedAttributes(t *testing.T) {
|
||||
data := `
|
||||
hello: Sops config file
|
||||
sops: The attribute 'sops' must be rejected, otherwise the file cannot be opened later on
|
||||
`
|
||||
s := new(Store)
|
||||
_, err := s.LoadPlainFile([]byte(data))
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, `YAML doc used reserved word 'sops'`, err.Error())
|
||||
}
|
||||
|
||||
@@ -12,11 +12,13 @@ import (
|
||||
)
|
||||
|
||||
// Version represents the value of the current semantic version.
|
||||
var Version = "3.9.0"
|
||||
var Version = "3.9.4"
|
||||
|
||||
// PrintVersion prints the current version of sops. If the flag
|
||||
// `--disable-version-check` is set, the function will not attempt
|
||||
// to retrieve the latest version from the GitHub API.
|
||||
// `--disable-version-check` is set or if the environment variable
|
||||
// SOPS_DISABLE_VERSION_CHECK is set to a value that is considered
|
||||
// true by https://pkg.go.dev/strconv#ParseBool, the function will
|
||||
// not attempt to retrieve the latest version from the GitHub API.
|
||||
//
|
||||
// If the flag is not set, the function will attempt to retrieve
|
||||
// the latest version from the GitHub API and compare it to the
|
||||
|
||||
Reference in New Issue
Block a user