diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 23bf099..9b1b6a1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: - id: go-imports - id: no-go-testing - id: golangci-lint - args: ["--skip-dirs=vendor -c .golang-ci.yml ."] + args: ["--exclude-dirs=vendor -c .golang-ci.yml ."] - id: go-unit-tests - id: go-build - id: go-mod-tidy \ No newline at end of file diff --git a/go.mod b/go.mod index f207a61..9aa1395 100644 --- a/go.mod +++ b/go.mod @@ -1,197 +1,121 @@ module github.com/bedag/subst -go 1.19 +go 1.22.0 + +toolchain go1.23.0 require ( - cloud.google.com/go/kms v1.15.1 - filippo.io/age v1.1.1 - github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 - github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0 - github.com/BurntSushi/toml v1.2.1 + github.com/BurntSushi/toml v1.4.0 github.com/MakeNowJust/heredoc v1.0.0 - github.com/Masterminds/sprig/v3 v3.2.3 - github.com/Shopify/ejson v1.4.1 - github.com/aws/aws-sdk-go v1.44.321 - github.com/aws/aws-sdk-go-v2 v1.18.1 - github.com/aws/aws-sdk-go-v2/config v1.18.27 - github.com/aws/aws-sdk-go-v2/credentials v1.13.26 - github.com/aws/aws-sdk-go-v2/service/kms v1.22.2 - github.com/aws/aws-sdk-go-v2/service/sts v1.19.2 - github.com/dimchansky/utfbom v1.1.1 + github.com/Masterminds/sprig/v3 v3.3.0 + github.com/Shopify/ejson v1.5.2 github.com/geofffranks/simpleyaml v0.0.0-20161109204137-c9320f076de5 - github.com/geofffranks/spruce v1.29.0 - github.com/hashicorp/vault/api v1.9.2 - github.com/onsi/gomega v1.27.8 - github.com/ory/dockertest/v3 v3.10.0 - github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.6.1 + github.com/geofffranks/spruce v1.31.1 + github.com/rs/zerolog v1.33.0 + github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.14.0 github.com/starkandwayne/goutils v0.0.0-20190115202530-896b8a6904be - github.com/stretchr/testify v1.8.4 - go.mozilla.org/sops/v3 v3.7.3 - golang.org/x/net v0.14.0 - google.golang.org/api v0.136.0 - google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 - google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 - google.golang.org/grpc v1.57.0 - google.golang.org/protobuf v1.31.0 + github.com/stretchr/testify v1.9.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/apimachinery v0.27.4 - k8s.io/client-go v0.27.4 - sigs.k8s.io/kustomize/api v0.13.2 - sigs.k8s.io/kustomize/kyaml v0.14.1 - sigs.k8s.io/yaml v1.3.0 + k8s.io/apimachinery v0.31.0 + k8s.io/client-go v0.31.0 + sigs.k8s.io/kustomize/api v0.17.3 + sigs.k8s.io/kustomize/kyaml v0.17.2 + sigs.k8s.io/yaml v1.4.0 ) require ( - cloud.google.com/go/compute v1.23.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v1.1.1 // indirect - github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0 // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.29 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect - github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect - github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect - github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect - github.com/Azure/go-autorest/logger v0.2.1 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect + dario.cat/mergo v1.0.1 // indirect + github.com/KimMachineGun/automemlimit v0.6.1 // indirect github.com/Knetic/govaluate v3.0.0+incompatible // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.2.0 // indirect - github.com/Microsoft/go-winio v0.6.0 // indirect - github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.12.12 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12 // indirect - github.com/aws/smithy-go v1.13.5 // indirect - github.com/blang/semver v3.5.1+incompatible // indirect - github.com/cenkalti/backoff/v3 v3.2.2 // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect - github.com/cloudflare/circl v1.3.3 // indirect - github.com/cloudfoundry-community/vaultkv v0.5.0 // indirect - github.com/containerd/continuity v0.3.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/cli v20.10.17+incompatible // indirect - github.com/docker/docker v20.10.7+incompatible // indirect - github.com/docker/go-connections v0.4.0 // indirect + github.com/Masterminds/semver/v3 v3.3.0 // indirect + github.com/aws/aws-sdk-go v1.55.5 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cilium/ebpf v0.9.1 // indirect + github.com/cloudfoundry-community/vaultkv v0.7.0 // indirect + github.com/containerd/cgroups/v3 v3.0.1 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/docker/go-units v0.4.0 // indirect github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad // indirect - github.com/emicklei/go-restful/v3 v3.10.2 // indirect - github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/fatih/color v1.15.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/geofffranks/yaml v0.0.0-20161117152608-9f2fe4b6f295 // indirect github.com/go-errors/errors v1.4.2 // indirect - github.com/go-jose/go-jose/v3 v3.0.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-openapi/jsonpointer v0.20.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.4 // indirect + github.com/godbus/dbus/v5 v5.0.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/gnostic v0.6.9 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/s2a-go v0.1.5 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect - github.com/googleapis/gax-go/v2 v2.12.0 // indirect - github.com/goware/prefixer v0.0.0-20160118172347-395022866408 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/cap v0.7.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-retryablehttp v0.7.4 // indirect - github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect - github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect - github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef // indirect - github.com/huandu/xstrings v1.3.3 // indirect + github.com/huandu/xstrings v1.5.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kylelemons/godebug v1.1.0 // indirect - github.com/lib/pq v1.10.5 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/onsi/ginkgo v1.16.5 // indirect - github.com/onsi/ginkgo/v2 v2.11.0 // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.2 // indirect - github.com/opencontainers/runc v1.1.5 // indirect + github.com/onsi/gomega v1.33.1 // indirect + github.com/opencontainers/runtime-spec v1.0.2 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect - github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/ryanuber/go-glob v1.0.0 // indirect - github.com/shopspring/decimal v1.2.0 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/sirupsen/logrus v1.8.1 // indirect github.com/spf13/afero v1.9.3 // indirect - github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/cast v1.7.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.4.1 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect - github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/xeipuuv/gojsonschema v1.2.0 // indirect - github.com/xlab/treeprint v1.1.0 // indirect + github.com/x448/float16 v0.8.4 // indirect + github.com/xlab/treeprint v1.2.0 // indirect github.com/ziutek/utils v0.0.0-20190626152656-eb2a3b364d6c // indirect - go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a // indirect - go.opencensus.io v0.24.0 // indirect go.starlark.net v0.0.0-20221205180719-3fd0dac74452 // indirect - golang.org/x/crypto v0.12.0 // indirect - golang.org/x/mod v0.10.0 // indirect - golang.org/x/oauth2 v0.11.0 // indirect - golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.11.0 // indirect - golang.org/x/term v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/term v0.24.0 // indirect + golang.org/x/text v0.18.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.9.3 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/urfave/cli.v1 v1.20.0 // indirect - k8s.io/api v0.27.4 // indirect - k8s.io/klog/v2 v2.100.1 // indirect - k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect - k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + k8s.io/api v0.31.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) diff --git a/go.sum b/go.sum index 678d09f..f0df140 100644 --- a/go.sum +++ b/go.sum @@ -17,23 +17,15 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= -cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= -cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= -cloud.google.com/go/kms v1.15.1 h1:HUC3fAoepH3RpcQXiJhXWWYizjQ5r7YjI7SO9ZbHf9s= -cloud.google.com/go/kms v1.15.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -44,206 +36,93 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg= -filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= -github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1 h1:SEy2xmstIphdPwNBUi7uhvjyjhVKISfwjfOJmuy7kg4= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0 h1:yfJe15aSwEQ6Oo6J+gdfdulPNoZ3TEhmbhLIoxZcA+U= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0 h1:T028gtTPiYt/RMUfs8nVsAL7FDQrfLlrm/NnRG/zcC4= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= -github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= -github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= -github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= -github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8= -github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 h1:wkAZRgT/pn8HhFyzfe9UnqOjJYqlembgCTi72Bm/xKk= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.12/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= -github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY= -github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/KimMachineGun/automemlimit v0.6.1 h1:ILa9j1onAAMadBsyyUJv5cack8Y1WT26yLj/V+ulKp8= +github.com/KimMachineGun/automemlimit v0.6.1/go.mod h1:T7xYht7B8r6AG/AqFcUdc7fzd2bIdBKmepfP2S1svPY= github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg= github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= -github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= -github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= -github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= -github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/Shopify/ejson v1.4.1 h1:zGGojGJNTdIWza/kOT8gd2HKCg3ZkSi3CZ1ZX70NHsw= -github.com/Shopify/ejson v1.4.1/go.mod h1:VZMUtDzvBW/PAXRUF5fzp1ffb1ucT8MztrZXXLYZurw= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go v1.44.321 h1:iXwFLxWjZPjYqjPq0EcCs46xX7oDLEELte1+BzgpKk8= -github.com/aws/aws-sdk-go v1.44.321/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/aws/aws-sdk-go-v2 v1.18.1 h1:+tefE750oAb7ZQGzla6bLkOwfcQCEtC5y2RqoqCeqKo= -github.com/aws/aws-sdk-go-v2 v1.18.1/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/config v1.18.27 h1:Az9uLwmssTE6OGTpsFqOnaGpLnKDqNYOJzWuC6UAYzA= -github.com/aws/aws-sdk-go-v2/config v1.18.27/go.mod h1:0My+YgmkGxeqjXZb5BYme5pc4drjTnM+x1GJ3zv42Nw= -github.com/aws/aws-sdk-go-v2/credentials v1.13.26 h1:qmU+yhKmOCyujmuPY7tf5MxR/RKyZrOPO3V4DobiTUk= -github.com/aws/aws-sdk-go-v2/credentials v1.13.26/go.mod h1:GoXt2YC8jHUBbA4jr+W3JiemnIbkXOfxSXcisUsZ3os= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 h1:LxK/bitrAr4lnh9LnIS6i7zWbCOdMsfzKFBI6LUCS0I= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4/go.mod h1:E1hLXN/BL2e6YizK1zFlYd8vsfi2GTjbjBazinMmeaM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34 h1:A5UqQEmPaCFpedKouS4v+dHCTUo2sKqhoKO9U5kxyWo= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34/go.mod h1:wZpTEecJe0Btj3IYnDx/VlUzor9wm3fJHyvLpQF0VwY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28 h1:srIVS45eQuewqz6fKKu6ZGXaq6FuFg5NzgQBAM6g8Y4= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28/go.mod h1:7VRpKQQedkfIEXb4k52I7swUnZP0wohVajJMRn3vsUw= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 h1:LWA+3kDM8ly001vJ1X1waCuLJdtTl48gwkPKWy9sosI= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35/go.mod h1:0Eg1YjxE0Bhn56lx+SHJwCzhW+2JGtizsrx+lCqrfm0= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28 h1:bkRyG4a929RCnpVSTvLM2j/T4ls015ZhhYApbmYs15s= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28/go.mod h1:jj7znCIg05jXlaGBlFMGP8+7UN3VtCkRBG2spnmRQkU= -github.com/aws/aws-sdk-go-v2/service/kms v1.22.2 h1:jwmtdM1/l1DRNy5jQrrYpsQm8zwetkgeqhAqefDr1yI= -github.com/aws/aws-sdk-go-v2/service/kms v1.22.2/go.mod h1:aNfh11Smy55o65PB3MyKbkM8BFyFUcZmj1k+4g8eNfg= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.12 h1:nneMBM2p79PGWBQovYO/6Xnc2ryRMw3InnDJq1FHkSY= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.12/go.mod h1:HuCOxYsF21eKrerARYO6HapNeh9GBNq7fius2AcwodY= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12 h1:2qTR7IFk7/0IN/adSFhYu9Xthr0zVFTgBrmPldILn80= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12/go.mod h1:E4VrHCPzmVB/KFXtqBGKb3c8zpbNBgKe3fisDNLAW5w= -github.com/aws/aws-sdk-go-v2/service/sts v1.19.2 h1:XFJ2Z6sNUUcAz9poj+245DMkrHE4h2j5I9/xD50RHfE= -github.com/aws/aws-sdk-go-v2/service/sts v1.19.2/go.mod h1:dp0yLPsLBOi++WTxzCjA/oZqi6NPIhoR+uF7GeMU9eg= -github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= -github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= -github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= -github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= +github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= +github.com/Shopify/ejson v1.5.2 h1:sXUlmNd5MFHfxIvchQqkbksYmKmHb05coSYhMpWpUNs= +github.com/Shopify/ejson v1.5.2/go.mod h1:bVvQ3MaBCfMOkIp1rWZcot3TruYXCc7qUUbI1tjs/YM= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= +github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4= +github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= -github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= -github.com/cloudfoundry-community/vaultkv v0.5.0 h1:QLwtzUe19guxDveA8xCoCt54/Nyqy6DGvcgmT3R7yD4= -github.com/cloudfoundry-community/vaultkv v0.5.0/go.mod h1:Ww4Y5blv2SP9+qk/KE7rhU/rFqIW7B9fcLT7OS6lb8c= +github.com/cloudfoundry-community/vaultkv v0.7.0 h1:VFq0TQxGIxuJuqXKDlY73XneOQyKKTEBA8EwqKI3OOU= +github.com/cloudfoundry-community/vaultkv v0.7.0/go.mod h1:D17jAL9n2GS66nbapOU7vRkGQ2D5zhsnyhCuspfNDlg= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= -github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/containerd/cgroups/v3 v3.0.1 h1:4hfGvu8rfGIwVIDd+nLzn/B9ZXx4BcCjzt5ToenJRaE= +github.com/containerd/cgroups/v3 v3.0.1/go.mod h1:/vtwk1VXrtoa5AaZLkypuOJgA/6DyPMZHJPGQNtlHnw= +github.com/coreos/go-oidc/v3 v3.5.0/go.mod h1:ecXRtV4romGPeO6ieExAsUK9cb/3fp9hXNz1tlv8PIM= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= -github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= -github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M= -github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/docker v20.10.7+incompatible h1:Z6O9Nhsjv+ayUEeI1IojKbYcsGdgYSNqxe1s2MYzUhQ= -github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad h1:Qk76DOWdOp+GlyDKBAG3Klr9cn7N+LcYc82AZ2S7+cA= github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad/go.mod h1:mPKfmRa823oBIgl2r20LeMSpTAteW5j7FLkc0vjmzyQ= -github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE= -github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= -github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/geofffranks/simpleyaml v0.0.0-20161109204137-c9320f076de5 h1:5AjbNPs5ax5Rf1/FeG8tLUYOoEbEDvcMvBgBUH4fDRM= github.com/geofffranks/simpleyaml v0.0.0-20161109204137-c9320f076de5/go.mod h1:EoVmbOOR2VpnWfvsZ1wVdjvUbitLYk1SYxGTssyjW4s= -github.com/geofffranks/spruce v1.29.0 h1:D6THmdYb5zMRT/jW8O42U85BeXkbCts9csyroMqB62A= -github.com/geofffranks/spruce v1.29.0/go.mod h1:LysbPKCU2deahHFst/kYj5WMZO/JLRbwwNMutHX9Ct0= +github.com/geofffranks/spruce v1.31.1 h1:U83FUWJBSe7zXrPt9uPLDH7pMvC4mEaieoxA7jcRqmI= +github.com/geofffranks/spruce v1.31.1/go.mod h1:48uMcwndjciQpBE00shdZ4mSGqFg2xjEL6ruQKR3HFY= github.com/geofffranks/yaml v0.0.0-20161117152608-9f2fe4b6f295 h1:CxigGHNaNtLTrnMveo9CjJjgXTuZpbLvuOvY/+c1v8g= github.com/geofffranks/yaml v0.0.0-20161117152608-9f2fe4b6f295/go.mod h1:+Qu4YOxbpR+Dn8JVzOTjJKWt3EZkEQD918wX+CkNcbE= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +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-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= @@ -252,24 +131,18 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -293,12 +166,12 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= -github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= 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= @@ -307,12 +180,12 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -329,65 +202,45 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.5 h1:8IYp3w9nysqv3JH+NJgXJzGbDHzLOTj43BmSkp+O7qg= -github.com/google/s2a-go v0.1.5/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= 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.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= -github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= +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/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= -github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gopherjs/gopherjs v0.0.0-20211002144500-51d3295ec039 h1:5lduVZdirtbIjJojKmVI/tR2gYU0nAVcmdNEHOBvIC8= -github.com/goware/prefixer v0.0.0-20160118172347-395022866408 h1:Y9iQJfEqnN3/Nce9cOegemcy/9Ai5k3huT6E80F3zaw= -github.com/goware/prefixer v0.0.0-20160118172347-395022866408/go.mod h1:PE1ycukgRPJ7bJ9a1fdfQ9j8i/cEcRAoLZzbxYpNB/s= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= +github.com/hashicorp/cap v0.5.0/go.mod h1:IAy00Er+ZFpMo+5x6B4bkO2HgpzgrkfsuDWMmHAuKUE= +github.com/hashicorp/cap v0.7.0 h1:atLIEU5lJslYXo1qsv7RtUL1HrJVVxnfkErIT3uxLp0= +github.com/hashicorp/cap v0.7.0/go.mod h1:UynhCoGX3pxL0OfVrfMzPWAyjMYp96bk11BNTf2zt8o= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= -github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= -github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= -github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 h1:ET4pqyjiGmY09R5y+rSd70J2w45CtbWDNvGqWp/R3Ng= +github.com/hashicorp/go-secure-stdlib/base62 v0.1.2/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/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.9.2 h1:YjkZLJ7K3inKgMZ0wzCU9OHqc+UqMQyXsPXnf3Cl2as= -github.com/hashicorp/vault/api v1.9.2/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8= -github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM= -github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= -github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -401,50 +254,39 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 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/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lib/pq v1.10.5 h1:J+gdV2cUmX7ZqL2B0lFcW0m+egaHC2V3lpO8nWxyYiQ= -github.com/lib/pq v1.10.5/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= -github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -452,7 +294,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -460,81 +301,69 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= -github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= +github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= -github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= -github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= -github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= -github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= +github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= +github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +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/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= 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/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= -github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v1.2.1 h1:bKNHfEv7tSIjZ8JbKaFjzFINljxG4lzZvmHUnElzOIg= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/smarty/assertions v1.16.0 h1:EvHNkdRA4QHMrn75NZSoUQ/mAUXAYWfatfB01yTCzfY= +github.com/smarty/assertions v1.16.0/go.mod h1:duaaFdCS0K9dnoM50iyek/eYINOZ64gbh1Xlf6LG7AI= +github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= +github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= github.com/starkandwayne/goutils v0.0.0-20190115202530-896b8a6904be h1:vV6o1C8iPioC0Ahi3e9Bs9vVPW9/YN3uwgA6EFahAws= github.com/starkandwayne/goutils v0.0.0-20190115202530-896b8a6904be/go.mod h1:Py4V645l0xZXsyvSR6WIcsGhNQEiIFDlmJ4Xwd6UCws= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -542,25 +371,18 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/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/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= -github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= +github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/yhat/scrape v0.0.0-20161128144610-24b7890b0945/go.mod h1:4vRFPPNYllgCacoj+0FoKOjTW68rUhEfqPLiEJaK2w8= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -568,21 +390,18 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/ziutek/utils v0.0.0-20190626152656-eb2a3b364d6c h1:PyI4qg2zvSToKuMdr0WiwbsKkKzyKQBwhELU01zOcfg= github.com/ziutek/utils v0.0.0-20190626152656-eb2a3b364d6c/go.mod h1:ACOZERHuXvWeAzjD4DvMwvxz/Q8DOF9VP8lcfwV59Oo= -go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a h1:N7VD+PwpJME2ZfQT8+ejxwA4Ow10IkGbU0MGf94ll8k= -go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a/go.mod h1:YDKUvO0b//78PaaEro6CAPH6NqohCmL2Cwju5XI2HoE= -go.mozilla.org/sops/v3 v3.7.3 h1:CYx02LnWTATWv6NqWJIt4JCKVKSnGV+MsRiDpvwWQhg= -go.mozilla.org/sops/v3 v3.7.3/go.mod h1:AutdccISG5Nt/faUigaKPU9aGmhyZuCyUiSx5YCa1O8= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.starlark.net v0.0.0-20221205180719-3fd0dac74452 h1:JZtNuL6LPB+scU5yaQ6hqRlJFRiddZm2FwRt2AQqtHA= go.starlark.net v0.0.0-20221205180719-3fd0dac74452/go.mod h1:kIVgS18CjmEC3PqMd5kaJSGEifyV/CeB9x506ZJ1Vbk= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -592,15 +411,10 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -636,7 +450,6 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -668,20 +481,18 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +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.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -691,8 +502,10 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= -golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= +golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +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/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -705,9 +518,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -716,18 +526,17 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -740,7 +549,6 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -748,55 +556,52 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -813,7 +618,6 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -855,8 +659,9 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -880,15 +685,12 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.136.0 h1:e/6enzUE1s4tGPa6Q3ZYShKTtvRc+1Jq0rrafhppmOs= -google.golang.org/api v0.136.0/go.mod h1:XtJfF+V2zgUxelOn5Zs3kECtluMxneJG8ZxUTlLNTPA= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -913,7 +715,6 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -927,13 +728,6 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= -google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 h1:nIgk/EEq3/YlnmVVXVnm14rC2oxgs1o0ong4sD/rd44= -google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 h1:wukfNtZmZUurLN/atp2hiIeTKn7QJWIQdHzqmsOnAOk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -947,15 +741,9 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= 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= @@ -968,15 +756,17 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -984,22 +774,15 @@ gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= -gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/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 v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1007,28 +790,29 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.27.4 h1:0pCo/AN9hONazBKlNUdhQymmnfLRbSZjd5H5H3f0bSs= -k8s.io/api v0.27.4/go.mod h1:O3smaaX15NfxjzILfiln1D8Z3+gEYpjEpiNA/1EVK1Y= -k8s.io/apimachinery v0.27.4 h1:CdxflD4AF61yewuid0fLl6bM4a3q04jWel0IlP+aYjs= -k8s.io/apimachinery v0.27.4/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= -k8s.io/client-go v0.27.4 h1:vj2YTtSJ6J4KxaC88P4pMPEQECWMY8gqPqsTgUKzvjk= -k8s.io/client-go v0.27.4/go.mod h1:ragcly7lUlN0SRPk5/ZkGnDjPknzb37TICq07WhI6Xc= -k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= -k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= -k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= -k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= +k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= +k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= +k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= +k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +mvdan.cc/gofumpt v0.5.0/go.mod h1:HBeVDtMKRZpXyxFciAirzdKklDlGu8aAy1wEbH5Y9js= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kustomize/api v0.13.2 h1:kejWfLeJhUsTGioDoFNJET5LQe/ajzXhJGYoU+pJsiA= -sigs.k8s.io/kustomize/api v0.13.2/go.mod h1:DUp325VVMFVcQSq+ZxyDisA8wtldwHxLZbr1g94UHsw= -sigs.k8s.io/kustomize/kyaml v0.14.1 h1:c8iibius7l24G2wVAGZn/Va2wNys03GXLjYVIcFVxKA= -sigs.k8s.io/kustomize/kyaml v0.14.1/go.mod h1:AN1/IpawKilWD7V+YvQwRGUvuUOOWpjsHu6uHwonSF4= -sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= -sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sigs.k8s.io/kustomize/api v0.17.3 h1:6GCuHSsxq7fN5yhF2XrC+AAr8gxQwhexgHflOAD/JJU= +sigs.k8s.io/kustomize/api v0.17.3/go.mod h1:TuDH4mdx7jTfK61SQ/j1QZM/QWR+5rmEiNjvYlhzFhc= +sigs.k8s.io/kustomize/kyaml v0.17.2 h1:+AzvoJUY0kq4QAhH/ydPHHMRLijtUKiyVyh7fOSshr0= +sigs.k8s.io/kustomize/kyaml v0.17.2/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/internal/decryptors/ejson/ejson.go b/internal/decryptors/ejson/ejson.go index 260e5e3..d3ace71 100644 --- a/internal/decryptors/ejson/ejson.go +++ b/internal/decryptors/ejson/ejson.go @@ -45,7 +45,10 @@ func NewEJSONDecryptor(config decryptors.DecryptorConfig, keyDirectory string, k if len(keys) > 0 { for _, key := range keys { - init.AddKey(key) + err := init.AddKey(key) + if err != nil { + return nil, err + } } } diff --git a/internal/decryptors/ejson/ejson_test.go b/internal/decryptors/ejson/ejson_test.go index 1342962..520f3ad 100644 --- a/internal/decryptors/ejson/ejson_test.go +++ b/internal/decryptors/ejson/ejson_test.go @@ -99,7 +99,10 @@ func TestMultipleKeyAddition(t *testing.T) { } keyContent := string(content) keysFromFile = append(keysFromFile, keyContent) - decryptor.AddKey(keyContent) + err = decryptor.AddKey(keyContent) + if err != nil { + t.Fatalf("Failed to add key to decryptor: %v", err) + } } } diff --git a/internal/decryptors/sops/kustomize-controller/LICENSE b/internal/decryptors/sops/kustomize-controller/LICENSE deleted file mode 100644 index a612ad9..0000000 --- a/internal/decryptors/sops/kustomize-controller/LICENSE +++ /dev/null @@ -1,373 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - controls, is controlled by, or is under common control with You. For - purposes of this definition, "control" means (a) the power, direct - or indirect, to cause the direction or management of such entity, - whether by contract or otherwise, or (b) ownership of more than - fifty percent (50%) of the outstanding shares or beneficial - ownership of such entity. - -2. License Grants and Conditions --------------------------------- - -2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -(a) under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, including, without limitation, warranties that the * -* Covered Software is free of defects, merchantable, fit for a * -* particular purpose or non-infringing. The entire risk as to the * -* quality and performance of the Covered Software is with You. * -* Should any Covered Software prove defective in any respect, You * -* (not any Contributor) assume the cost of any necessary servicing, * -* repair, or correction. This disclaimer of warranty constitutes an * -* essential part of this License. No use of any Covered Software is * -* authorized under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, indirect, * -* special, incidental, or consequential damages of any character * -* including, without limitation, damages for lost profits, loss of * -* goodwill, work stoppage, computer failure or malfunction, or any * -* and all other commercial damages or losses, even if such party * -* shall have been informed of the possibility of such damages. This * -* limitation of liability shall not apply to liability for death or * -* personal injury resulting from such party's negligence to the * -* extent applicable law prohibits such limitation. Some * -* jurisdictions do not allow the exclusion or limitation of * -* incidental or consequential damages, so this exclusion and * -* limitation may not apply to You. * -* * -************************************************************************ - -8. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice ---------------------------------------------------------- - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. diff --git a/internal/decryptors/sops/kustomize-controller/README.md b/internal/decryptors/sops/kustomize-controller/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/internal/decryptors/sops/kustomize-controller/age/keysource.go b/internal/decryptors/sops/kustomize-controller/age/keysource.go deleted file mode 100644 index c63d368..0000000 --- a/internal/decryptors/sops/kustomize-controller/age/keysource.go +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (C) 2021 The Mozilla SOPS authors -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package age - -import ( - "bytes" - "fmt" - "io" - "strings" - - "filippo.io/age" - "filippo.io/age/armor" -) - -// MasterKey is an age key used to Encrypt and Decrypt SOPS' data key. -// -// Adapted from https://github.com/mozilla/sops/blob/v3.7.2/age/keysource.go -// to be able to have fine-grain control over the used decryption keys -// without relying on the existence of file(path)s. -type MasterKey struct { - // Identities contains the set of Bench32-encoded age identities used to - // Decrypt. - // They are lazy-loaded using MasterKeyFromIdentities, or on first - // Decrypt(). - // In addition to using this field, ParsedIdentities.ApplyToMasterKey() can - // be used to parse and lazy-load identities. - Identities []string - // Recipient contains the Bench32-encoded age public key used to Encrypt. - Recipient string - // EncryptedKey contains the SOPS data key encrypted with age. - EncryptedKey string - - // parsedIdentities contains a slice of parsed age identities. - // It is used to lazy-load the Identities at-most once. - // It can also be injected by a (local) keyservice.KeyServiceServer using - // ParsedIdentities.ApplyToMasterKey(). - parsedIdentities []age.Identity - // parsedRecipient contains a parsed age public key. - // It is used to lazy-load the Recipient at-most once. - parsedRecipient *age.X25519Recipient -} - -// MasterKeyFromRecipient takes a Bech32-encoded age public key, parses it, and -// returns a new MasterKey. -func MasterKeyFromRecipient(recipient string) (*MasterKey, error) { - parsedRecipient, err := parseRecipient(recipient) - if err != nil { - return nil, err - } - return &MasterKey{ - Recipient: recipient, - parsedRecipient: parsedRecipient, - }, nil -} - -// MasterKeyFromIdentities takes a set if Bech32-encoded age identities, parses -// them, and returns a new MasterKey. -func MasterKeyFromIdentities(identities ...string) (*MasterKey, error) { - parsedIdentities, err := parseIdentities(identities...) - if err != nil { - return nil, err - } - return &MasterKey{ - Identities: identities, - parsedIdentities: parsedIdentities, - }, nil -} - -// ParsedIdentities contains a set of parsed age identities. -// It allows for creating a (local) keyservice.KeyServiceServer which parses -// identities only once, to then inject them using ApplyToMasterKey() for all -// requests. -type ParsedIdentities []age.Identity - -// Import attempts to parse the given identities, to then add them to itself. -// It returns any parsing error. -// A single identity argument is allowed to be a multiline string containing -// multiple identities. Empty lines and lines starting with "#" are ignored. -// It is not thread safe, and parallel importing would better be done by -// 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...) - if err != nil { - return fmt.Errorf("failed to parse and add to age identities: %w", err) - } - *i = append(*i, identities...) - return nil -} - -// ApplyToMasterKey configures the ParsedIdentities on the provided key. -func (i ParsedIdentities) ApplyToMasterKey(key *MasterKey) { - key.parsedIdentities = i -} - -// Encrypt takes a SOPS data key, encrypts it with the Recipient, and stores -// the result in the EncryptedKey field. -func (key *MasterKey) Encrypt(dataKey []byte) error { - if key.parsedRecipient == nil { - parsedRecipient, err := parseRecipient(key.Recipient) - if err != nil { - return err - } - key.parsedRecipient = parsedRecipient - } - - var buffer bytes.Buffer - aw := armor.NewWriter(&buffer) - w, err := age.Encrypt(aw, key.parsedRecipient) - if err != nil { - return fmt.Errorf("failed to create writer for encrypting sops data key with age: %w", err) - } - if _, err := w.Write(dataKey); err != nil { - return fmt.Errorf("failed to encrypt sops data key with age: %w", err) - } - if err := w.Close(); err != nil { - return fmt.Errorf("failed to close writer for encrypting sops data key with age: %w", err) - } - if err := aw.Close(); err != nil { - return fmt.Errorf("failed to close armored writer: %w", err) - } - - key.SetEncryptedDataKey(buffer.Bytes()) - return nil -} - -// EncryptIfNeeded encrypts the provided SOPS data key, if it has not been -// encrypted yet. -func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error { - if key.EncryptedKey == "" { - return key.Encrypt(dataKey) - } - return nil -} - -// EncryptedDataKey returns the encrypted SOPS data key this master key holds. -func (key *MasterKey) EncryptedDataKey() []byte { - return []byte(key.EncryptedKey) -} - -// SetEncryptedDataKey sets the encrypted SOPS data key for this master key. -func (key *MasterKey) SetEncryptedDataKey(enc []byte) { - key.EncryptedKey = string(enc) -} - -// Decrypt decrypts the EncryptedKey with the (parsed) Identities and returns -// the result. -func (key *MasterKey) Decrypt() ([]byte, error) { - if len(key.parsedIdentities) == 0 && len(key.Identities) > 0 { - parsedIdentities, err := parseIdentities(key.Identities...) - if err != nil { - return nil, err - } - key.parsedIdentities = parsedIdentities - } - - src := bytes.NewReader([]byte(key.EncryptedKey)) - ar := armor.NewReader(src) - r, err := age.Decrypt(ar, key.parsedIdentities...) - if err != nil { - return nil, fmt.Errorf("failed to create reader for decrypting sops data key with age: %w", err) - } - - var b bytes.Buffer - if _, err := io.Copy(&b, r); err != nil { - return nil, fmt.Errorf("failed to copy age decrypted data into bytes.Buffer: %w", err) - } - return b.Bytes(), nil -} - -// NeedsRotation returns whether the data key needs to be rotated or not. -func (key *MasterKey) NeedsRotation() bool { - return false -} - -// ToString converts the key to a string representation. -func (key *MasterKey) ToString() string { - return key.Recipient -} - -// ToMap converts the MasterKey to a map for serialization purposes. -func (key *MasterKey) ToMap() map[string]interface{} { - out := make(map[string]interface{}) - out["recipient"] = key.Recipient - out["enc"] = key.EncryptedKey - return out -} - -// parseRecipient attempts to parse a string containing an encoded age public -// key. -func parseRecipient(recipient string) (*age.X25519Recipient, error) { - parsedRecipient, err := age.ParseX25519Recipient(recipient) - if err != nil { - return nil, fmt.Errorf("failed to parse input as Bech32-encoded age public key: %w", err) - } - return parsedRecipient, nil -} - -// 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) ([]age.Identity, error) { - var identities []age.Identity - for _, i := range identity { - parsed, err := age.ParseIdentities(strings.NewReader(i)) - if err != nil { - return nil, err - } - identities = append(identities, parsed...) - } - return identities, nil -} diff --git a/internal/decryptors/sops/kustomize-controller/age/keysource_test.go b/internal/decryptors/sops/kustomize-controller/age/keysource_test.go deleted file mode 100644 index b32588c..0000000 --- a/internal/decryptors/sops/kustomize-controller/age/keysource_test.go +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package age - -import ( - "testing" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - . "github.com/onsi/gomega" - "go.mozilla.org/sops/v3/age" -) - -const ( - mockRecipient string = "age1lzd99uklcjnc0e7d860axevet2cz99ce9pq6tzuzd05l5nr28ams36nvun" - mockIdentity string = "AGE-SECRET-KEY-1G0Q5K9TV4REQ3ZSQRMTMG8NSWQGYT0T7TZ33RAZEE0GZYVZN0APSU24RK7" - - // mockUnrelatedIdentity is not actually utilized in tests, but confirms we - // iterate over all available identities. - mockUnrelatedIdentity string = "AGE-SECRET-KEY-1432K5YRNSC44GC4986NXMX6GVZ52WTMT9C79CLUVWYY4DKDHD5JSNDP4MC" - - // mockEncryptedKey equals to "data" when decrypted with mockIdentity. - mockEncryptedKey string = `-----BEGIN AGE ENCRYPTED FILE----- -YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvY2t2NkdLUGRvY3l2OGNy -MVJWcUhCOEZrUG8yeCtnRnhxL0I5NFk4YjJFCmE4SVQ3MEdyZkFqRWpSa2F0NVhF -VDUybzBxdS9nSGpHSVRVMUI0UEVqZkkKLS0tIGJjeGhNQ0Y5L2VZRVVYSm90djFF -bzdnQ3UwTGljMmtrbWNMV1MxYkFzUFUK4xjOZOTGdcbzuwUY/zeBXhcF+Md3e5PQ -EylloI7MNGbadPGb ------END AGE ENCRYPTED FILE-----` -) - -var ( - mockIdentities = []string{ - mockUnrelatedIdentity, - mockIdentity, - } -) - -func TestMasterKeyFromRecipient(t *testing.T) { - g := NewWithT(t) - - got, err := MasterKeyFromRecipient(mockRecipient) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(got).ToNot(BeNil()) - g.Expect(got.Recipient).To(Equal(mockRecipient)) - g.Expect(got.parsedRecipient).ToNot(BeNil()) - g.Expect(got.parsedIdentities).To(BeEmpty()) - - got, err = MasterKeyFromRecipient("invalid") - g.Expect(err).To(HaveOccurred()) - g.Expect(got).To(BeNil()) -} - -func TestMasterKeyFromIdentities(t *testing.T) { - g := NewWithT(t) - - got, err := MasterKeyFromIdentities(mockIdentities...) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(got).ToNot(BeNil()) - g.Expect(got.Identities).To(HaveLen(2)) - g.Expect(got.Identities).To(ContainElements(mockIdentities)) - g.Expect(got.parsedIdentities).To(HaveLen(2)) - - got, err = MasterKeyFromIdentities("invalid") - g.Expect(err).To(HaveOccurred()) - g.Expect(got).To(BeNil()) -} - -func TestParsedIdentities_Import(t *testing.T) { - g := NewWithT(t) - - i := make(ParsedIdentities, 0) - g.Expect(i.Import(mockIdentities...)).To(Succeed()) - g.Expect(i).To(HaveLen(2)) - - g.Expect(i.Import("invalid")).To(HaveOccurred()) - g.Expect(i).To(HaveLen(2)) -} - -func TestParsedIdentities_ApplyToMasterKey(t *testing.T) { - g := NewWithT(t) - - i := make(ParsedIdentities, 0) - g.Expect(i.Import(mockIdentities...)).To(Succeed()) - - key := &MasterKey{} - i.ApplyToMasterKey(key) - g.Expect(key.parsedIdentities).To(BeEquivalentTo(i)) -} - -func TestMasterKey_Encrypt(t *testing.T) { - mockParsedRecipient, _ := parseRecipient(mockRecipient) - - tests := []struct { - name string - key *MasterKey - wantErr bool - }{ - { - name: "recipient", - key: &MasterKey{ - Recipient: mockRecipient, - }, - }, - { - name: "parsed recipient", - key: &MasterKey{ - parsedRecipient: mockParsedRecipient, - }, - }, - { - name: "invalid recipient", - key: &MasterKey{ - Recipient: "invalid", - }, - wantErr: true, - }, - { - name: "parsed recipient and invalid recipient", - key: &MasterKey{ - Recipient: "invalid", - parsedRecipient: mockParsedRecipient, - }, - // This should pass, confirming parsedRecipient > Recipient - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - - err := tt.key.Encrypt([]byte("data")) - if tt.wantErr { - g.Expect(err).To(HaveOccurred()) - g.Expect(tt.key.EncryptedKey).To(BeEmpty()) - return - } - - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(tt.key.EncryptedKey).ToNot(BeEmpty()) - g.Expect(tt.key.EncryptedKey).To(ContainSubstring("AGE ENCRYPTED FILE")) - }) - } -} - -func TestMasterKey_Encrypt_SOPS_Compat(t *testing.T) { - g := NewWithT(t) - - dataKey := []byte("foo") - - encryptKey := &MasterKey{ - Recipient: mockRecipient, - } - g.Expect(encryptKey.Encrypt(dataKey)).To(Succeed()) - - t.Setenv(age.SopsAgeKeyEnv, mockIdentity) - decryptKey := &age.MasterKey{ - EncryptedKey: encryptKey.EncryptedKey, - } - dec, err := decryptKey.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(dec).To(Equal(dataKey)) -} - -func TestMasterKey_EncryptIfNeeded(t *testing.T) { - g := NewWithT(t) - - key, err := MasterKeyFromRecipient(mockRecipient) - g.Expect(err).ToNot(HaveOccurred()) - - g.Expect(key.EncryptIfNeeded([]byte("data"))).To(Succeed()) - - encryptedKey := key.EncryptedKey - g.Expect(encryptedKey).To(ContainSubstring("AGE ENCRYPTED FILE")) - - g.Expect(key.EncryptIfNeeded([]byte("some other data"))).To(Succeed()) - g.Expect(key.EncryptedKey).To(Equal(encryptedKey)) -} - -func TestMasterKey_EncryptedDataKey(t *testing.T) { - g := NewWithT(t) - - key := &MasterKey{EncryptedKey: "some key"} - g.Expect(key.EncryptedDataKey()).To(BeEquivalentTo(key.EncryptedKey)) -} - -func TestMasterKey_Decrypt(t *testing.T) { - parsedIdentities, _ := parseIdentities(mockIdentities...) - - tests := []struct { - name string - key *MasterKey - wantErr bool - }{ - { - name: "identities", - key: &MasterKey{ - Identities: mockIdentities, - EncryptedKey: mockEncryptedKey, - }, - }, - { - name: "parsed identities", - key: &MasterKey{ - EncryptedKey: mockEncryptedKey, - parsedIdentities: parsedIdentities, - }, - }, - { - name: "no identities", - key: &MasterKey{ - Identities: []string{}, - EncryptedKey: mockEncryptedKey, - }, - wantErr: true, - }, - { - name: "invalid identity", - key: &MasterKey{ - Identities: []string{"invalid"}, - EncryptedKey: mockEncryptedKey, - }, - wantErr: true, - }, - { - name: "invalid encrypted key", - key: &MasterKey{ - Identities: mockIdentities, - EncryptedKey: "invalid", - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - - data, err := tt.key.Decrypt() - if tt.wantErr { - g.Expect(err).To(HaveOccurred()) - g.Expect(data).To(BeNil()) - return - } - - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(data).To(Equal([]byte("data"))) - }) - } -} - -func TestMasterKey_Decrypt_SOPS_Compat(t *testing.T) { - g := NewWithT(t) - - dataKey := []byte("foo") - - encryptKey := &age.MasterKey{ - Recipient: mockRecipient, - } - g.Expect(encryptKey.Encrypt(dataKey)).To(Succeed()) - - decryptKey := &MasterKey{ - Identities: []string{mockIdentity}, - EncryptedKey: encryptKey.EncryptedKey, - } - dec, err := decryptKey.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(dec).To(Equal(dataKey)) -} - -func TestMasterKey_EncryptDecrypt_RoundTrip(t *testing.T) { - g := NewWithT(t) - - encryptKey, err := MasterKeyFromRecipient(mockRecipient) - g.Expect(err).ToNot(HaveOccurred()) - - data := []byte("some secret data") - g.Expect(encryptKey.Encrypt(data)).To(Succeed()) - g.Expect(encryptKey.EncryptedKey).ToNot(BeEmpty()) - - decryptKey, err := MasterKeyFromIdentities(mockIdentity) - g.Expect(err).ToNot(HaveOccurred()) - - decryptKey.EncryptedKey = encryptKey.EncryptedKey - decryptedData, err := decryptKey.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(decryptedData).To(Equal(data)) -} - -func TestMasterKey_ToString(t *testing.T) { - g := NewWithT(t) - - key := &MasterKey{Recipient: mockRecipient} - g.Expect(key.ToString()).To(Equal(key.Recipient)) -} - -func TestMasterKey_ToMap(t *testing.T) { - g := NewWithT(t) - - key := &MasterKey{ - Recipient: mockRecipient, - Identities: mockIdentities, // must never be included - EncryptedKey: "some-encrypted-key", - } - g.Expect(key.ToMap()).To(Equal(map[string]interface{}{ - "recipient": mockRecipient, - "enc": key.EncryptedKey, - })) -} - -func Fuzz_Age(f *testing.F) { - f.Fuzz(func(t *testing.T, receipt, identities string, seed, data []byte) { - fc := fuzz.NewConsumer(seed) - masterKey := MasterKey{} - - if err := fc.GenerateStruct(&masterKey); err != nil { - return - } - - _ = masterKey.Encrypt(data) - _ = masterKey.EncryptIfNeeded(data) - _, _ = MasterKeyFromRecipient(receipt) - _, _ = MasterKeyFromIdentities(identities) - }) -} diff --git a/internal/decryptors/sops/kustomize-controller/awskms/keysource.go b/internal/decryptors/sops/kustomize-controller/awskms/keysource.go deleted file mode 100644 index c287ad6..0000000 --- a/internal/decryptors/sops/kustomize-controller/awskms/keysource.go +++ /dev/null @@ -1,291 +0,0 @@ -/* -Copyright (C) 2022 The Flux authors - -This Source Code Form is subject to the terms of the Mozilla Public -License, v. 2.0. If a copy of the MPL was not distributed with this -file, You can obtain one at https://mozilla.org/MPL/2.0/. -*/ - -package awskms - -import ( - "context" - "fmt" - "os" - "regexp" - "strings" - "time" - - "encoding/base64" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/credentials" - "github.com/aws/aws-sdk-go-v2/service/kms" - "github.com/aws/aws-sdk-go-v2/service/sts" - "sigs.k8s.io/yaml" -) - -const ( - // arnRegex matches an AWS ARN. - // valid ARN example: arn:aws:kms:us-west-2:107501996527:key/612d5f0p-p1l3-45e6-aca6-a5b005693a48 - arnRegex = `^arn:aws[\w-]*:kms:(.+):[0-9]+:(key|alias)/.+$` - // stsSessionRegex matches an AWS STS session name. - // valid STS session examples: john_s, sops@42WQm042 - stsSessionRegex = "[^a-zA-Z0-9=,.@-_]+" - // kmsTTL is the duration after which a MasterKey requires rotation. - kmsTTL = time.Hour * 24 * 30 * 6 - // roleSessionNameLengthLimit is the AWS role session name length limit. - roleSessionNameLengthLimit = 64 -) - -// MasterKey is an AWS KMS key used to encrypt and decrypt sops' data key. -// Adapted from: https://github.com/mozilla/sops/blob/v3.7.2/kms/keysource.go#L39 -// Modified to accept custom static credentials as opposed to using env vars by default -// and use aws-sdk-go-v2 instead of aws-sdk-go being used in upstream. -type MasterKey struct { - // AWS Role ARN associated with the KMS key. - Arn string - // AWS Role ARN used to assume a role through AWS STS. - Role string - // EncryptedKey stores the data key in it's encrypted form. - EncryptedKey string - // CreationDate is when this MasterKey was created. - CreationDate time.Time - // EncryptionContext provides additional context about the data key. - // Ref: https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#encrypt_context - EncryptionContext map[string]string - // AWSProfile is the profile to use for loading configuration and credentials. - AwsProfile string - - // credentialsProvider is used to configure the AWS config with the - // necessary credentials. - credentialsProvider aws.CredentialsProvider - - // epResolver can be used to override the endpoint the AWS client resolves - // to by default. This is mostly used for testing purposes as it can not be - // injected using e.g. an environment variable. The field is not publicly - // exposed, nor configurable. - epResolver aws.EndpointResolverWithOptions -} - -// CredsProvider is a wrapper around aws.CredentialsProvider used for authenticating -// towards AWS KMS. -type CredsProvider struct { - credsProvider aws.CredentialsProvider -} - -// NewCredsProvider returns a CredsProvider object with the provided aws.CredentialsProvider. -func NewCredsProvider(cp aws.CredentialsProvider) *CredsProvider { - return &CredsProvider{ - credsProvider: cp, - } -} - -// ApplyToMasterKey configures the credentials the provided key. -func (c CredsProvider) ApplyToMasterKey(key *MasterKey) { - key.credentialsProvider = c.credsProvider -} - -// LoadCredsProviderFromYaml parses the given YAML returns a CredsProvider object -// which contains the credentials provider used for authenticating towards AWS KMS. -func LoadCredsProviderFromYaml(b []byte) (*CredsProvider, error) { - credInfo := struct { - AccessKeyID string `json:"aws_access_key_id"` - SecretAccessKey string `json:"aws_secret_access_key"` - SessionToken string `json:"aws_session_token"` - }{} - if err := yaml.Unmarshal(b, &credInfo); err != nil { - return nil, fmt.Errorf("failed to unmarshal AWS credentials file: %w", err) - } - return &CredsProvider{ - credsProvider: credentials.NewStaticCredentialsProvider(credInfo.AccessKeyID, - credInfo.SecretAccessKey, credInfo.SessionToken), - }, nil -} - -// EncryptedDataKey returns the encrypted data key this master key holds. -func (key *MasterKey) EncryptedDataKey() []byte { - return []byte(key.EncryptedKey) -} - -// SetEncryptedDataKey sets the encrypted data key for this master key. -func (key *MasterKey) SetEncryptedDataKey(enc []byte) { - key.EncryptedKey = string(enc) -} - -// Encrypt takes a SOPS data key, encrypts it with KMS and stores the result -// in the EncryptedKey field. -func (key *MasterKey) Encrypt(dataKey []byte) error { - cfg, err := key.createKMSConfig() - if err != nil { - return err - } - client := kms.NewFromConfig(*cfg) - input := &kms.EncryptInput{ - KeyId: &key.Arn, - Plaintext: dataKey, - EncryptionContext: key.EncryptionContext, - } - out, err := client.Encrypt(context.TODO(), input) - if err != nil { - return fmt.Errorf("failed to encrypt sops data key with AWS KMS: %w", err) - } - key.EncryptedKey = base64.StdEncoding.EncodeToString(out.CiphertextBlob) - return nil -} - -// EncryptIfNeeded encrypts the provided sops' data key and encrypts it, if it -// has not been encrypted yet. -func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error { - if key.EncryptedKey == "" { - return key.Encrypt(dataKey) - } - return nil -} - -// Decrypt decrypts the EncryptedKey field with AWS KMS and returns the result. -func (key *MasterKey) Decrypt() ([]byte, error) { - k, err := base64.StdEncoding.DecodeString(key.EncryptedKey) - if err != nil { - return nil, fmt.Errorf("error base64-decoding encrypted data key: %s", err) - } - cfg, err := key.createKMSConfig() - if err != nil { - return nil, err - } - client := kms.NewFromConfig(*cfg) - input := &kms.DecryptInput{ - KeyId: &key.Arn, - CiphertextBlob: k, - EncryptionContext: key.EncryptionContext, - } - decrypted, err := client.Decrypt(context.TODO(), input) - if err != nil { - return nil, fmt.Errorf("failed to decrypt sops data key with AWS KMS: %w", err) - } - return decrypted.Plaintext, nil -} - -// NeedsRotation returns whether the data key needs to be rotated or not. -func (key *MasterKey) NeedsRotation() bool { - return time.Since(key.CreationDate) > kmsTTL -} - -// ToString converts the key to a string representation. -func (key *MasterKey) ToString() string { - return key.Arn -} - -// ToMap converts the MasterKey to a map for serialization purposes. -func (key MasterKey) ToMap() map[string]interface{} { - out := make(map[string]interface{}) - out["arn"] = key.Arn - if key.Role != "" { - out["role"] = key.Role - } - out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339) - out["enc"] = key.EncryptedKey - if key.EncryptionContext != nil { - outcontext := make(map[string]string) - for k, v := range key.EncryptionContext { - outcontext[k] = v - } - out["context"] = outcontext - } - return out -} - -// NewMasterKey creates a new MasterKey from an ARN, role and context, setting the -// creation date to the current date. -func NewMasterKey(arn string, role string, context map[string]string) *MasterKey { - return &MasterKey{ - Arn: arn, - Role: role, - EncryptionContext: context, - CreationDate: time.Now().UTC(), - } -} - -// NewMasterKeyFromArn takes an ARN string and returns a new MasterKey for that -// ARN. -func NewMasterKeyFromArn(arn string, context map[string]string, awsProfile string) *MasterKey { - k := &MasterKey{} - arn = strings.Replace(arn, " ", "", -1) - roleIndex := strings.Index(arn, "+arn:aws:iam::") - if roleIndex > 0 { - k.Arn = arn[:roleIndex] - k.Role = arn[roleIndex+1:] - } else { - k.Arn = arn - } - k.EncryptionContext = context - k.CreationDate = time.Now().UTC() - k.AwsProfile = awsProfile - return k -} - -// createKMSConfig returns a Config configured with the appropriate credentials. -func (key MasterKey) createKMSConfig() (*aws.Config, error) { - re := regexp.MustCompile(arnRegex) - matches := re.FindStringSubmatch(key.Arn) - if matches == nil { - return nil, fmt.Errorf("no valid ARN found in '%s'", key.Arn) - } - region := matches[1] - cfg, err := config.LoadDefaultConfig(context.TODO(), func(lo *config.LoadOptions) error { - // Use the credentialsProvider if present, otherwise default to reading credentials - // from the environment. - if key.credentialsProvider != nil { - lo.Credentials = key.credentialsProvider - } - if key.AwsProfile != "" { - lo.SharedConfigProfile = key.AwsProfile - } - lo.Region = region - - // Set the epResolver, if present. Used ONLY for tests. - if key.epResolver != nil { - lo.EndpointResolverWithOptions = key.epResolver - } - return nil - }) - if err != nil { - return nil, fmt.Errorf("couldn't load AWS config: %w", err) - } - if key.Role != "" { - return key.createSTSConfig(&cfg) - } - - return &cfg, nil -} - -// createSTSConfig uses AWS STS to assume a role and returns a Config configured -// with that role's credentials. -func (key MasterKey) createSTSConfig(config *aws.Config) (*aws.Config, error) { - hostname, err := os.Hostname() - if err != nil { - return nil, err - } - stsRoleSessionNameRe := regexp.MustCompile(stsSessionRegex) - - sanitizedHostname := stsRoleSessionNameRe.ReplaceAllString(hostname, "") - name := "sops@" + sanitizedHostname - if len(name) >= roleSessionNameLengthLimit { - name = name[:roleSessionNameLengthLimit] - } - - client := sts.NewFromConfig(*config) - input := &sts.AssumeRoleInput{ - RoleArn: &key.Role, - RoleSessionName: &name, - } - out, err := client.AssumeRole(context.TODO(), input) - if err != nil { - return nil, fmt.Errorf("failed to assume role '%s': %w", key.Role, err) - } - config.Credentials = credentials.NewStaticCredentialsProvider(*out.Credentials.AccessKeyId, - *out.Credentials.SecretAccessKey, *out.Credentials.SessionToken, - ) - return config, nil -} diff --git a/internal/decryptors/sops/kustomize-controller/awskms/keysource_test.go b/internal/decryptors/sops/kustomize-controller/awskms/keysource_test.go deleted file mode 100644 index 54f5040..0000000 --- a/internal/decryptors/sops/kustomize-controller/awskms/keysource_test.go +++ /dev/null @@ -1,406 +0,0 @@ -/* -Copyright (C) 2022 The Flux authors - -This Source Code Form is subject to the terms of the Mozilla Public -License, v. 2.0. If a copy of the MPL was not distributed with this -file, You can obtain one at https://mozilla.org/MPL/2.0/. -*/ - -package awskms - -import ( - "context" - "encoding/base64" - "fmt" - logger "log" - "os" - "testing" - "time" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/credentials" - "github.com/aws/aws-sdk-go-v2/service/kms" - awsv1 "github.com/aws/aws-sdk-go/aws" - sessionv1 "github.com/aws/aws-sdk-go/aws/session" - kmsv1 "github.com/aws/aws-sdk-go/service/kms" - . "github.com/onsi/gomega" - "github.com/ory/dockertest/v3" -) - -var ( - testKMSServerURL string - testKMSARN string -) - -const ( - dummyARN = "arn:aws:kms:us-west-2:107501996527:key/612d5f0p-p1l3-45e6-aca6-a5b005693a48" - testLocalKMSTag = "3.11.1" - testLocalKMSImage = "nsmithuk/local-kms" -) - -// TestMain initializes a AWS KMS server using Docker, writes the HTTP address to -// testAWSEndpoint, tries to generate a key for encryption-decryption using a -// backoff retry approach and then sets testKMSARN to the id of the generated key. -// It then runs all the tests, which can make use of the various `test*` variables. -func TestMain(m *testing.M) { - // Uses a sensible default on Windows (TCP/HTTP) and Linux/MacOS (socket) - pool, err := dockertest.NewPool("") - if err != nil { - logger.Fatalf("could not connect to docker: %s", err) - } - - // Pull the image, create a container based on it, and run it - // resource, err := pool.Run("nsmithuk/local-kms", testLocalKMSVersion, []string{}) - resource, err := pool.RunWithOptions(&dockertest.RunOptions{ - Repository: testLocalKMSImage, - Tag: testLocalKMSTag, - ExposedPorts: []string{"8080"}, - }) - if err != nil { - logger.Fatalf("could not start resource: %s", err) - } - - purgeResource := func() { - if err := pool.Purge(resource); err != nil { - logger.Printf("could not purge resource: %s", err) - } - } - - testKMSServerURL = fmt.Sprintf("http://127.0.0.1:%v", resource.GetPort("8080/tcp")) - masterKey := createTestMasterKey(dummyARN) - - kmsClient, err := createTestKMSClient(masterKey) - if err != nil { - purgeResource() - logger.Fatalf("could not create session: %s", err) - } - - var key *kms.CreateKeyOutput - if err := pool.Retry(func() error { - key, err = kmsClient.CreateKey(context.TODO(), &kms.CreateKeyInput{}) - if err != nil { - return err - } - return nil - }); err != nil { - purgeResource() - logger.Fatalf("could not create key: %s", err) - } - - if key.KeyMetadata.Arn != nil { - testKMSARN = *key.KeyMetadata.Arn - } else { - purgeResource() - logger.Fatalf("could not set arn") - } - - // Run the tests, but only if we succeeded in setting up the AWS KMS server. - var code int - if err == nil { - code = m.Run() - } - - // This can't be deferred, as os.Exit simpy does not care - if err := pool.Purge(resource); err != nil { - logger.Fatalf("could not purge resource: %s", err) - } - - os.Exit(code) -} - -func TestMasterKey_Encrypt(t *testing.T) { - g := NewWithT(t) - key := createTestMasterKey(testKMSARN) - dataKey := []byte("thisistheway") - g.Expect(key.Encrypt(dataKey)).To(Succeed()) - g.Expect(key.EncryptedKey).ToNot(BeEmpty()) - - kmsClient, err := createTestKMSClient(key) - g.Expect(err).ToNot(HaveOccurred()) - - k, err := base64.StdEncoding.DecodeString(key.EncryptedKey) - g.Expect(err).ToNot(HaveOccurred()) - - input := &kms.DecryptInput{ - CiphertextBlob: k, - EncryptionContext: key.EncryptionContext, - } - decrypted, err := kmsClient.Decrypt(context.TODO(), input) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(decrypted.Plaintext).To(Equal(dataKey)) -} - -func TestMasterKey_Encrypt_SOPS_Compat(t *testing.T) { - g := NewWithT(t) - - encryptKey := createTestMasterKey(testKMSARN) - dataKey := []byte("encrypt-compat") - g.Expect(encryptKey.Encrypt(dataKey)).To(Succeed()) - - // This is the core decryption logic of `sopskms.MasterKey.Decrypt()`. - // We don't call `sops.MasterKey.Decrypt()` directly to avoid issues with - // session and config setup. - config := awsv1.Config{ - Region: awsv1.String("us-west-2"), - Endpoint: &testKMSServerURL, - } - t.Setenv("AWS_ACCESS_KEY_ID", "id") - t.Setenv("AWS_SECRET_ACCESS_KEY", "secret") - k, err := base64.StdEncoding.DecodeString(encryptKey.EncryptedKey) - g.Expect(err).ToNot(HaveOccurred()) - sess, err := sessionv1.NewSessionWithOptions(sessionv1.Options{ - Config: config, - }) - kmsSvc := kmsv1.New(sess) - decrypted, err := kmsSvc.Decrypt(&kmsv1.DecryptInput{CiphertextBlob: k}) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(decrypted.Plaintext).To(Equal(dataKey)) -} - -func TestMasterKey_EncryptIfNeeded(t *testing.T) { - g := NewWithT(t) - - key := createTestMasterKey(testKMSARN) - g.Expect(key.EncryptIfNeeded([]byte("data"))).To(Succeed()) - - encryptedKey := key.EncryptedKey - g.Expect(encryptedKey).ToNot(BeEmpty()) - - g.Expect(key.EncryptIfNeeded([]byte("some other data"))).To(Succeed()) - g.Expect(key.EncryptedKey).To(Equal(encryptedKey)) -} - -func TestMasterKey_EncryptedDataKey(t *testing.T) { - g := NewWithT(t) - - key := &MasterKey{EncryptedKey: "some key"} - g.Expect(key.EncryptedDataKey()).To(BeEquivalentTo(key.EncryptedKey)) -} - -func TestMasterKey_Decrypt(t *testing.T) { - g := NewWithT(t) - - key := createTestMasterKey(testKMSARN) - kmsClient, err := createTestKMSClient(key) - g.Expect(err).ToNot(HaveOccurred()) - - dataKey := []byte("itsalwaysdns") - out, err := kmsClient.Encrypt(context.TODO(), &kms.EncryptInput{ - Plaintext: dataKey, KeyId: &key.Arn, EncryptionContext: key.EncryptionContext, - }) - g.Expect(err).ToNot(HaveOccurred()) - - key.EncryptedKey = base64.StdEncoding.EncodeToString(out.CiphertextBlob) - got, err := key.Decrypt() - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(got).To(Equal(dataKey)) -} - -func TestMasterKey_Decrypt_SOPS_Compat(t *testing.T) { - g := NewWithT(t) - - // This is the core encryption logic of `sopskms.MasterKey.Encrypt()`. - // We don't call `sops.MasterKey.Encrypt()` directly to avoid issues with - // session and config setup. - dataKey := []byte("decrypt-compat") - config := awsv1.Config{ - Region: awsv1.String("us-west-2"), - Endpoint: &testKMSServerURL, - } - t.Setenv("AWS_ACCESS_KEY_ID", "id") - t.Setenv("AWS_SECRET_ACCESS_KEY", "secret") - sess, err := sessionv1.NewSessionWithOptions(sessionv1.Options{ - Config: config, - }) - kmsSvc := kmsv1.New(sess) - encrypted, err := kmsSvc.Encrypt(&kmsv1.EncryptInput{Plaintext: dataKey, KeyId: &testKMSARN}) - g.Expect(err).ToNot(HaveOccurred()) - - decryptKey := createTestMasterKey(testKMSARN) - decryptKey.EncryptedKey = base64.StdEncoding.EncodeToString(encrypted.CiphertextBlob) - dec, err := decryptKey.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(dec).To(Equal(dataKey)) -} - -func TestMasterKey_EncryptDecrypt_RoundTrip(t *testing.T) { - g := NewWithT(t) - - dataKey := []byte("thisistheway") - - encryptKey := createTestMasterKey(testKMSARN) - g.Expect(encryptKey.Encrypt(dataKey)).To(Succeed()) - g.Expect(encryptKey.EncryptedKey).ToNot(BeEmpty()) - - decryptKey := createTestMasterKey(testKMSARN) - decryptKey.EncryptedKey = encryptKey.EncryptedKey - - decryptedData, err := decryptKey.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(decryptedData).To(Equal(dataKey)) -} - -func TestMasterKey_NeedsRotation(t *testing.T) { - g := NewWithT(t) - - key := NewMasterKeyFromArn(dummyARN, nil, "") - g.Expect(key.NeedsRotation()).To(BeFalse()) - - key.CreationDate = key.CreationDate.Add(-(kmsTTL + time.Second)) - g.Expect(key.NeedsRotation()).To(BeTrue()) -} - -func TestMasterKey_ToMap(t *testing.T) { - g := NewWithT(t) - key := MasterKey{ - Arn: "test-arn", - Role: "test-role", - EncryptedKey: "enc-key", - EncryptionContext: map[string]string{ - "env": "test", - }, - } - g.Expect(key.ToMap()).To(Equal(map[string]interface{}{ - "arn": "test-arn", - "role": "test-role", - "created_at": "0001-01-01T00:00:00Z", - "enc": "enc-key", - "context": map[string]string{ - "env": "test", - }, - })) -} - -func TestCreds_ApplyToMasterKey(t *testing.T) { - g := NewWithT(t) - - creds := CredsProvider{ - credsProvider: credentials.NewStaticCredentialsProvider("", "", ""), - } - key := &MasterKey{} - creds.ApplyToMasterKey(key) - g.Expect(key.credentialsProvider).To(Equal(creds.credsProvider)) -} - -func TestLoadAwsKmsCredsFromYaml(t *testing.T) { - g := NewWithT(t) - credsYaml := []byte(` -aws_access_key_id: test-id -aws_secret_access_key: test-secret -aws_session_token: test-token -`) - credsProvider, err := LoadCredsProviderFromYaml(credsYaml) - g.Expect(err).ToNot(HaveOccurred()) - - creds, err := credsProvider.credsProvider.Retrieve(context.TODO()) - g.Expect(err).ToNot(HaveOccurred()) - - g.Expect(creds.AccessKeyID).To(Equal("test-id")) - g.Expect(creds.SecretAccessKey).To(Equal("test-secret")) - g.Expect(creds.SessionToken).To(Equal("test-token")) -} - -func Test_createKMSConfig(t *testing.T) { - tests := []struct { - name string - key MasterKey - assertFunc func(g *WithT, cfg *aws.Config, err error) - fallback bool - }{ - { - name: "master key with invalid arn fails", - key: MasterKey{ - Arn: "arn:gcp:kms:antartica-north-2::key/45e6-aca6-a5b005693a48", - }, - assertFunc: func(g *WithT, _ *aws.Config, err error) { - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring("no valid ARN found")) - }, - }, - { - name: "master key with with proper configuration passes", - key: MasterKey{ - credentialsProvider: credentials.NewStaticCredentialsProvider("test-id", "test-secret", "test-token"), - AwsProfile: "test-profile", - Arn: "arn:aws:kms:us-west-2:107501996527:key/612d5f0p-p1l3-45e6-aca6-a5b005693a48", - }, - assertFunc: func(g *WithT, cfg *aws.Config, err error) { - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(cfg.Region).To(Equal("us-west-2")) - - creds, err := cfg.Credentials.Retrieve(context.TODO()) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(creds.AccessKeyID).To(Equal("test-id")) - g.Expect(creds.SecretAccessKey).To(Equal("test-secret")) - g.Expect(creds.SessionToken).To(Equal("test-token")) - - // ConfigSources is a slice of config.Config, which in turn is an interface. - // Since we use a LoadOptions object, we assert the type of cfgSrc and then - // check if the expected profile is present. - for _, cfgSrc := range cfg.ConfigSources { - if src, ok := cfgSrc.(config.LoadOptions); ok { - g.Expect(src.SharedConfigProfile).To(Equal("test-profile")) - } - } - }, - }, - { - name: "master key without creds and profile falls back to the environment variables", - key: MasterKey{ - Arn: "arn:aws:kms:us-west-2:107501996527:key/612d5f0p-p1l3-45e6-aca6-a5b005693a48", - }, - fallback: true, - assertFunc: func(g *WithT, cfg *aws.Config, err error) { - g.Expect(err).ToNot(HaveOccurred()) - - creds, err := cfg.Credentials.Retrieve(context.TODO()) - g.Expect(creds.AccessKeyID).To(Equal("id")) - g.Expect(creds.SecretAccessKey).To(Equal("secret")) - g.Expect(creds.SessionToken).To(Equal("token")) - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - // Set the environment variables if we want to fallback - if tt.fallback { - t.Setenv("AWS_ACCESS_KEY_ID", "id") - t.Setenv("AWS_SECRET_ACCESS_KEY", "secret") - t.Setenv("AWS_SESSION_TOKEN", "token") - } - cfg, err := tt.key.createKMSConfig() - tt.assertFunc(g, cfg, err) - }) - } -} - -func createTestMasterKey(arn string) MasterKey { - return MasterKey{ - Arn: arn, - credentialsProvider: credentials.NewStaticCredentialsProvider("id", "secret", ""), - epResolver: epResolver{}, - } -} - -// epResolver is a dummy resolver that points to the local test KMS server -type epResolver struct{} - -func (e epResolver) ResolveEndpoint(service, region string, options ...interface{}) (aws.Endpoint, error) { - return aws.Endpoint{ - URL: testKMSServerURL, - }, nil -} - -func createTestKMSClient(key MasterKey) (*kms.Client, error) { - cfg, err := key.createKMSConfig() - if err != nil { - return nil, err - } - - cfg.EndpointResolverWithOptions = epResolver{} - - return kms.NewFromConfig(*cfg), nil -} diff --git a/internal/decryptors/sops/kustomize-controller/azkv/config.go b/internal/decryptors/sops/kustomize-controller/azkv/config.go deleted file mode 100644 index 4685cdd..0000000 --- a/internal/decryptors/sops/kustomize-controller/azkv/config.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package azkv - -import ( - "fmt" - - "github.com/Azure/azure-sdk-for-go/sdk/azcore" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" - "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - "sigs.k8s.io/yaml" -) - -// LoadAADConfigFromBytes attempts to load the given bytes into the given AADConfig. -// By first decoding it if UTF-16, and then unmarshalling it into the given struct. -// It returns an error for any failure. -func LoadAADConfigFromBytes(b []byte, s *AADConfig) error { - b, err := decode(b) - if err != nil { - return fmt.Errorf("failed to decode Azure authentication file bytes: %w", err) - } - if err = yaml.Unmarshal(b, s); err != nil { - err = fmt.Errorf("failed to unmarshal Azure authentication file: %w", err) - } - return err -} - -// AADConfig contains the selection of fields from an Azure authentication file -// required for Active Directory authentication. -type AADConfig struct { - AZConfig - TenantID string `json:"tenantId,omitempty"` - ClientID string `json:"clientId,omitempty"` - ClientSecret string `json:"clientSecret,omitempty"` - ClientCertificate string `json:"clientCertificate,omitempty"` - ClientCertificatePassword string `json:"clientCertificatePassword,omitempty"` - ClientCertificateSendChain bool `json:"clientCertificateSendChain,omitempty"` - AuthorityHost string `json:"authorityHost,omitempty"` -} - -// AZConfig contains the Service Principal fields as generated by `az`. -// Ref: https://docs.microsoft.com/en-us/azure/aks/kubernetes-service-principal?tabs=azure-cli#manually-create-a-service-principal -type AZConfig struct { - AppID string `json:"appId,omitempty"` - Tenant string `json:"tenant,omitempty"` - Password string `json:"password,omitempty"` -} - -// TokenFromAADConfig attempts to construct a Token using the AADConfig values. -// It detects credentials in the following order: -// -// - azidentity.ClientSecretCredential when `tenantId`, `clientId` and -// `clientSecret` fields are found. -// - azidentity.ClientCertificateCredential when `tenantId`, -// `clientCertificate` (and optionally `clientCertificatePassword`) fields -// are found. -// - azidentity.ClientSecretCredential when AZConfig fields are found. -// - azidentity.ManagedIdentityCredential for a User ID, when a `clientId` -// field but no `tenantId` is found. -// -// If no set of credentials is found or the azcore.TokenCredential can not be -// created, an error is returned. -func TokenFromAADConfig(c AADConfig) (_ *Token, err error) { - var token azcore.TokenCredential - if c.TenantID != "" && c.ClientID != "" { - if c.ClientSecret != "" { - if token, err = azidentity.NewClientSecretCredential(c.TenantID, c.ClientID, c.ClientSecret, &azidentity.ClientSecretCredentialOptions{ - ClientOptions: azcore.ClientOptions{ - Cloud: c.GetCloudConfig(), - }, - }); err != nil { - return - } - return NewToken(token), nil - } - if c.ClientCertificate != "" { - certs, pk, err := azidentity.ParseCertificates([]byte(c.ClientCertificate), []byte(c.ClientCertificatePassword)) - if err != nil { - return nil, err - } - if token, err = azidentity.NewClientCertificateCredential(c.TenantID, c.ClientID, certs, pk, &azidentity.ClientCertificateCredentialOptions{ - SendCertificateChain: c.ClientCertificateSendChain, - ClientOptions: azcore.ClientOptions{ - Cloud: c.GetCloudConfig(), - }, - }); err != nil { - return nil, err - } - return NewToken(token), nil - } - } - - switch { - case c.Tenant != "" && c.AppID != "" && c.Password != "": - if token, err = azidentity.NewClientSecretCredential(c.Tenant, c.AppID, c.Password, &azidentity.ClientSecretCredentialOptions{ - ClientOptions: azcore.ClientOptions{ - Cloud: c.GetCloudConfig(), - }, - }); err != nil { - return - } - return NewToken(token), nil - case c.ClientID != "": - if token, err = azidentity.NewManagedIdentityCredential(&azidentity.ManagedIdentityCredentialOptions{ - ID: azidentity.ClientID(c.ClientID), - }); err != nil { - return - } - return NewToken(token), nil - default: - return nil, fmt.Errorf("invalid data: requires a '%s' field, a combination of '%s', '%s' and '%s', or '%s', '%s' and '%s'", - "clientId", "tenantId", "clientId", "clientSecret", "tenantId", "clientId", "clientCertificate") - } -} - -// GetCloudConfig returns a cloud.Configuration with the AuthorityHost, or the -// Azure Public Cloud default. -func (s AADConfig) GetCloudConfig() cloud.Configuration { - if s.AuthorityHost != "" { - return cloud.Configuration{ - ActiveDirectoryAuthorityHost: s.AuthorityHost, - Services: map[cloud.ServiceName]cloud.ServiceConfiguration{}, - } - } - return cloud.AzurePublic -} diff --git a/internal/decryptors/sops/kustomize-controller/azkv/config_test.go b/internal/decryptors/sops/kustomize-controller/azkv/config_test.go deleted file mode 100644 index c30b990..0000000 --- a/internal/decryptors/sops/kustomize-controller/azkv/config_test.go +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package azkv - -import ( - "bytes" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "math/big" - "testing" - - "github.com/Azure/azure-sdk-for-go/sdk/azcore" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" - "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - . "github.com/onsi/gomega" -) - -func TestLoadAADConfigFromBytes(t *testing.T) { - tests := []struct { - name string - b []byte - want AADConfig - wantErr bool - }{ - { - name: "Service Principal with Secret", - b: []byte(`tenantId: "some-tenant-id" -clientId: "some-client-id" -clientSecret: "some-client-secret"`), - want: AADConfig{ - TenantID: "some-tenant-id", - ClientID: "some-client-id", - ClientSecret: "some-client-secret", - }, - }, - { - name: "Service Principal with Certificate", - b: []byte(`tenantId: "some-tenant-id" -clientId: "some-client-id" -clientCertificate: "some-client-certificate"`), - want: AADConfig{ - TenantID: "some-tenant-id", - ClientID: "some-client-id", - ClientCertificate: "some-client-certificate", - }, - }, - { - name: "Managed Identity with Client ID", - b: []byte(`clientId: "some-client-id"`), - want: AADConfig{ - ClientID: "some-client-id", - }, - }, - { - name: "Service Principal with Secret from az CLI", - b: []byte(`{"appId": "some-app-id", "tenant": "some-tenant", "password": "some-password"}`), - want: AADConfig{ - AZConfig: AZConfig{ - AppID: "some-app-id", - Tenant: "some-tenant", - Password: "some-password", - }, - }, - }, - { - name: "Authority host", - b: []byte(`{"authorityHost": "https://example.com"}`), - want: AADConfig{ - AuthorityHost: "https://example.com", - }, - }, - { - name: "invalid", - b: []byte("some string"), - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - - got := AADConfig{} - err := LoadAADConfigFromBytes(tt.b, &got) - if tt.wantErr { - g.Expect(err).To(HaveOccurred()) - g.Expect(got).To(Equal(tt.want)) - return - } - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(got).To(Equal(tt.want)) - }) - } -} - -func TestTokenFromAADConfig(t *testing.T) { - tlsMock := validTLS(t) - - tests := []struct { - name string - config AADConfig - want azcore.TokenCredential - wantErr bool - }{ - { - name: "Service Principal with Secret", - config: AADConfig{ - TenantID: "some-tenant-id", - ClientID: "some-client-id", - ClientSecret: "some-client-secret", - }, - want: &azidentity.ClientSecretCredential{}, - }, - { - name: "Service Principal with Certificate", - config: AADConfig{ - TenantID: "some-tenant-id", - ClientID: "some-client-id", - ClientCertificate: string(tlsMock), - }, - want: &azidentity.ClientCertificateCredential{}, - }, - { - name: "Service Principal with az CLI format", - config: AADConfig{ - AZConfig: AZConfig{ - AppID: "some-app-id", - Tenant: "some-tenant", - Password: "some-password", - }, - }, - want: &azidentity.ClientSecretCredential{}, - }, - { - name: "Managed Identity with Client ID", - config: AADConfig{ - ClientID: "some-client-id", - }, - want: &azidentity.ManagedIdentityCredential{}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - - got, err := TokenFromAADConfig(tt.config) - if tt.wantErr { - g.Expect(err).To(HaveOccurred()) - g.Expect(got.token).To(BeNil()) - return - } - - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(got.token).ToNot(BeNil()) - g.Expect(got.token).To(BeAssignableToTypeOf(tt.want)) - }) - } -} - -func TestAADConfig_GetCloudConfig(t *testing.T) { - g := NewWithT(t) - - g.Expect((AADConfig{}).GetCloudConfig()).To(Equal(cloud.AzurePublic)) - g.Expect((AADConfig{AuthorityHost: "https://example.com"}).GetCloudConfig()).To(Equal(cloud.Configuration{ - ActiveDirectoryAuthorityHost: "https://example.com", - Services: map[cloud.ServiceName]cloud.ServiceConfiguration{}, - })) -} - -func validTLS(t *testing.T) []byte { - key, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - t.Fatal("Private key cannot be created.", err.Error()) - } - - out := bytes.NewBuffer(nil) - - var privateKey = &pem.Block{ - Type: "PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(key), - } - if err = pem.Encode(out, privateKey); err != nil { - t.Fatal("Private key cannot be PEM encoded.", err.Error()) - } - - certTemplate := x509.Certificate{ - SerialNumber: big.NewInt(1337), - } - cert, err := x509.CreateCertificate(rand.Reader, &certTemplate, &certTemplate, &key.PublicKey, key) - if err != nil { - t.Fatal("Certificate cannot be created.", err.Error()) - } - var certificate = &pem.Block{ - Type: "CERTIFICATE", - Bytes: cert, - } - if err = pem.Encode(out, certificate); err != nil { - t.Fatal("Certificate cannot be PEM encoded.", err.Error()) - } - - return out.Bytes() -} diff --git a/internal/decryptors/sops/kustomize-controller/azkv/keysource.go b/internal/decryptors/sops/kustomize-controller/azkv/keysource.go deleted file mode 100644 index 4fddff9..0000000 --- a/internal/decryptors/sops/kustomize-controller/azkv/keysource.go +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package azkv - -import ( - "bytes" - "context" - "encoding/base64" - "encoding/binary" - "errors" - "fmt" - "io/ioutil" - "os" - "strings" - "time" - "unicode/utf16" - - "github.com/Azure/azure-sdk-for-go/sdk/azcore" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" - "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - "github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys" - "github.com/dimchansky/utfbom" -) - -var ( - // azkvTTL is the duration after which a MasterKey requires rotation. - azkvTTL = time.Hour * 24 * 30 * 6 -) - -// MasterKey is an Azure Key Vault Key used to Encrypt and Decrypt SOPS' -// data key. -// -// The underlying authentication token can be configured using TokenFromAADConfig -// and Token.ApplyToMasterKey(). -type MasterKey struct { - VaultURL string - Name string - Version string - - EncryptedKey string - CreationDate time.Time - - token azcore.TokenCredential -} - -// MasterKeyFromURL creates a new MasterKey from a Vault URL, key name, and key -// version. -func MasterKeyFromURL(url, name, version string) *MasterKey { - key := &MasterKey{ - VaultURL: url, - Name: name, - Version: version, - CreationDate: time.Now().UTC(), - } - return key -} - -// Token is an azcore.TokenCredential used for authenticating towards Azure Key -// Vault. -type Token struct { - token azcore.TokenCredential -} - -// NewToken creates a new Token with the provided azcore.TokenCredential. -func NewToken(token azcore.TokenCredential) *Token { - return &Token{token: token} -} - -// ApplyToMasterKey configures the Token on the provided key. -func (t Token) ApplyToMasterKey(key *MasterKey) { - key.token = t.token -} - -// Encrypt takes a SOPS data key, encrypts it with Azure Key Vault, and stores -// the result in the EncryptedKey field. -func (key *MasterKey) Encrypt(dataKey []byte) error { - creds, err := key.getTokenCredential() - if err != nil { - return fmt.Errorf("failed to get Azure token credential to encrypt: %w", err) - } - c, err := azkeys.NewClient(key.VaultURL, creds, nil) - if err != nil { - return fmt.Errorf("failed to construct Azure Key Vault crypto client to encrypt data: %w", err) - } - resp, err := c.Encrypt(context.Background(), key.Name, key.Version, azkeys.KeyOperationParameters{ - Algorithm: to.Ptr(azkeys.EncryptionAlgorithmRSAOAEP256), - Value: dataKey, - }, nil) - if err != nil { - return fmt.Errorf("failed to encrypt sops data key with Azure Key Vault key '%s': %w", key.ToString(), err) - } - // This is for compatibility between the SOPS upstream which uses - // a much older Azure SDK, and our implementation which is up-to-date - // with the latest. - encodedEncryptedKey := base64.RawURLEncoding.EncodeToString(resp.Result) - key.SetEncryptedDataKey([]byte(encodedEncryptedKey)) - return nil -} - -// EncryptedDataKey returns the encrypted data key this master key holds. -func (key *MasterKey) EncryptedDataKey() []byte { - return []byte(key.EncryptedKey) -} - -// SetEncryptedDataKey sets the encrypted data key for this master key. -func (key *MasterKey) SetEncryptedDataKey(enc []byte) { - key.EncryptedKey = string(enc) -} - -// EncryptIfNeeded encrypts the provided SOPS data key, if it has not been -// encrypted yet. -func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error { - if key.EncryptedKey == "" { - return key.Encrypt(dataKey) - } - return nil -} - -// Decrypt decrypts the EncryptedKey field with Azure Key Vault and returns -// the result. -func (key *MasterKey) Decrypt() ([]byte, error) { - creds, err := key.getTokenCredential() - if err != nil { - return nil, fmt.Errorf("failed to get Azure token credential to decrypt: %w", err) - } - c, err := azkeys.NewClient(key.VaultURL, creds, nil) - if err != nil { - return nil, fmt.Errorf("failed to construct Azure Key Vault crypto client to decrypt data: %w", err) - } - // This is for compatibility between the SOPS upstream which uses - // a much older Azure SDK, and our implementation which is up-to-date - // with the latest. - rawEncryptedKey, err := base64.RawURLEncoding.DecodeString(key.EncryptedKey) - if err != nil { - return nil, fmt.Errorf("failed to base64 decode Azure Key Vault encrypted key: %w", err) - } - resp, err := c.Decrypt(context.Background(), key.Name, key.Version, azkeys.KeyOperationParameters{ - Algorithm: to.Ptr(azkeys.EncryptionAlgorithmRSAOAEP256), - Value: rawEncryptedKey, - }, nil) - if err != nil { - return nil, fmt.Errorf("failed to decrypt sops data key with Azure Key Vault key '%s': %w", key.ToString(), err) - } - return resp.Result, nil -} - -// NeedsRotation returns whether the data key needs to be rotated or not. -func (key *MasterKey) NeedsRotation() bool { - return time.Since(key.CreationDate) > (azkvTTL) -} - -// ToString converts the key to a string representation. -func (key *MasterKey) ToString() string { - return fmt.Sprintf("%s/keys/%s/%s", key.VaultURL, key.Name, key.Version) -} - -// ToMap converts the MasterKey to a map for serialization purposes. -func (key MasterKey) ToMap() map[string]interface{} { - out := make(map[string]interface{}) - out["vaultUrl"] = key.VaultURL - out["key"] = key.Name - out["version"] = key.Version - out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339) - out["enc"] = key.EncryptedKey - return out -} - -func decode(b []byte) ([]byte, error) { - reader, enc := utfbom.Skip(bytes.NewReader(b)) - switch enc { - case utfbom.UTF16LittleEndian: - u16 := make([]uint16, (len(b)/2)-1) - err := binary.Read(reader, binary.LittleEndian, &u16) - if err != nil { - return nil, err - } - return []byte(string(utf16.Decode(u16))), nil - case utfbom.UTF16BigEndian: - u16 := make([]uint16, (len(b)/2)-1) - err := binary.Read(reader, binary.BigEndian, &u16) - if err != nil { - return nil, err - } - return []byte(string(utf16.Decode(u16))), nil - } - return ioutil.ReadAll(reader) -} - -// getTokenCredential returns the tokenCredential of the MasterKey, or -// azidentity.NewDefaultAzureCredential. -func (key *MasterKey) getTokenCredential() (azcore.TokenCredential, error) { - if key.token == nil { - return getDefaultAzureCredential() - } - return key.token, nil -} - -// getDefaultAzureCredentials is a modification of -// azidentity.NewDefaultAzureCredential, specifically adapted to not shell out -// to the Azure CLI. -// -// It attemps to return an azcore.TokenCredential based on the following order: -// -// - azidentity.NewEnvironmentCredential if environment variables AZURE_CLIENT_ID, -// AZURE_CLIENT_ID is set with either one of the following: (AZURE_CLIENT_SECRET) -// or (AZURE_CLIENT_CERTIFICATE_PATH and AZURE_CLIENT_CERTIFICATE_PATH) or -// (AZURE_USERNAME, AZURE_PASSWORD) -// - azidentity.WorkloadIdentity if environment variable configuration -// (AZURE_AUTHORITY_HOST, AZURE_CLIENT_ID, AZURE_FEDERATED_TOKEN_FILE, AZURE_TENANT_ID) -// is set by the Azure workload identity webhook. -// - azidentity.ManagedIdentity if only AZURE_CLIENT_ID env variable is set. -func getDefaultAzureCredential() (azcore.TokenCredential, error) { - var ( - azureClientID = "AZURE_CLIENT_ID" - azureFederatedTokenFile = "AZURE_FEDERATED_TOKEN_FILE" - azureAuthorityHost = "AZURE_AUTHORITY_HOST" - azureTenantID = "AZURE_TENANT_ID" - ) - - var errorMessages []string - options := &azidentity.DefaultAzureCredentialOptions{} - - envCred, err := azidentity.NewEnvironmentCredential(&azidentity.EnvironmentCredentialOptions{ - ClientOptions: options.ClientOptions, DisableInstanceDiscovery: options.DisableInstanceDiscovery}, - ) - if err == nil { - return envCred, nil - } else { - errorMessages = append(errorMessages, "EnvironmentCredential: "+err.Error()) - } - - // workload identity requires values for AZURE_AUTHORITY_HOST, AZURE_CLIENT_ID, AZURE_FEDERATED_TOKEN_FILE, AZURE_TENANT_ID - haveWorkloadConfig := false - clientID, haveClientID := os.LookupEnv(azureClientID) - if haveClientID { - if file, ok := os.LookupEnv(azureFederatedTokenFile); ok { - if _, ok := os.LookupEnv(azureAuthorityHost); ok { - if tenantID, ok := os.LookupEnv(azureTenantID); ok { - haveWorkloadConfig = true - workloadCred, err := azidentity.NewWorkloadIdentityCredential(&azidentity.WorkloadIdentityCredentialOptions{ - ClientID: clientID, - TenantID: tenantID, - TokenFilePath: file, - ClientOptions: options.ClientOptions, - DisableInstanceDiscovery: options.DisableInstanceDiscovery, - }) - if err == nil { - return workloadCred, nil - } else { - errorMessages = append(errorMessages, "Workload Identity"+": "+err.Error()) - } - } - } - } - } - if !haveWorkloadConfig { - err := errors.New("missing environment variables for workload identity. Check webhook and pod configuration") - errorMessages = append(errorMessages, fmt.Sprintf("Workload Identity: %s", err)) - } - - o := &azidentity.ManagedIdentityCredentialOptions{ClientOptions: options.ClientOptions} - if haveClientID { - o.ID = azidentity.ClientID(clientID) - } - miCred, err := azidentity.NewManagedIdentityCredential(o) - if err == nil { - return miCred, nil - } else { - errorMessages = append(errorMessages, "ManagedIdentity"+": "+err.Error()) - } - - return nil, errors.New(strings.Join(errorMessages, "\n")) -} diff --git a/internal/decryptors/sops/kustomize-controller/azkv/keysource_integration_test.go b/internal/decryptors/sops/kustomize-controller/azkv/keysource_integration_test.go deleted file mode 100644 index 4de95d4..0000000 --- a/internal/decryptors/sops/kustomize-controller/azkv/keysource_integration_test.go +++ /dev/null @@ -1,147 +0,0 @@ -//go:build integration -// +build integration - -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package azkv - -import ( - "context" - "encoding/base64" - "os" - "testing" - "time" - - "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" - "github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys" - . "github.com/onsi/gomega" - "go.mozilla.org/sops/v3/azkv" -) - -// The following values should be created based on the instructions in: -// https://github.com/mozilla/sops#encrypting-using-azure-key-vault -var ( - testVaultURL = os.Getenv("TEST_AZURE_VAULT_URL") - testVaultKeyName = os.Getenv("TEST_AZURE_VAULT_KEY_NAME") - testVaultKeyVersion = os.Getenv("TEST_AZURE_VAULT_KEY_VERSION") - testAADConfig = AADConfig{ - TenantID: os.Getenv("TEST_AZURE_TENANT_ID"), - ClientID: os.Getenv("TEST_AZURE_CLIENT_ID"), - ClientSecret: os.Getenv("TEST_AZURE_CLIENT_SECRET"), - } -) - -func TestMasterKey_Encrypt(t *testing.T) { - g := NewWithT(t) - - key := MasterKeyFromURL(testVaultURL, testVaultKeyName, testVaultKeyVersion) - token, err := TokenFromAADConfig(testAADConfig) - g.Expect(err).ToNot(HaveOccurred()) - token.ApplyToMasterKey(key) - - g.Expect(key.Encrypt([]byte("foo"))).To(Succeed()) - g.Expect(key.EncryptedDataKey()).ToNot(BeEmpty()) -} - -func TestMasterKey_Decrypt(t *testing.T) { - g := NewWithT(t) - - key := MasterKeyFromURL(testVaultURL, testVaultKeyName, testVaultKeyVersion) - token, err := TokenFromAADConfig(testAADConfig) - g.Expect(err).ToNot(HaveOccurred()) - token.ApplyToMasterKey(key) - - dataKey := []byte("this is super secret data") - c, err := azkeys.NewClient(key.VaultURL, key.token, nil) - g.Expect(err).ToNot(HaveOccurred()) - resp, err := c.Encrypt(context.Background(), key.Name, key.Version, azkeys.KeyOperationParameters{ - Algorithm: to.Ptr(azkeys.EncryptionAlgorithmRSAOAEP256), - Value: dataKey, - }, nil) - g.Expect(err).ToNot(HaveOccurred()) - key.EncryptedKey = base64.RawURLEncoding.EncodeToString(resp.Result) - g.Expect(key.EncryptedKey).ToNot(BeEmpty()) - g.Expect(key.EncryptedKey).ToNot(Equal(dataKey)) - - got, err := key.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(got).To(Equal(dataKey)) -} - -func TestMasterKey_EncryptDecrypt_RoundTrip(t *testing.T) { - g := NewWithT(t) - - key := MasterKeyFromURL(testVaultURL, testVaultKeyName, testVaultKeyVersion) - token, err := TokenFromAADConfig(testAADConfig) - g.Expect(err).ToNot(HaveOccurred()) - token.ApplyToMasterKey(key) - - dataKey := []byte("some-data-that-should-be-secret") - - g.Expect(key.Encrypt(dataKey)).To(Succeed()) - g.Expect(key.EncryptedDataKey()).ToNot(BeEmpty()) - - dec, err := key.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(dec).To(Equal(dataKey)) -} - -func TestMasterKey_Encrypt_SOPS_Compat(t *testing.T) { - g := NewWithT(t) - - encryptKey := MasterKeyFromURL(testVaultURL, testVaultKeyName, testVaultKeyVersion) - token, err := TokenFromAADConfig(testAADConfig) - g.Expect(err).ToNot(HaveOccurred()) - token.ApplyToMasterKey(encryptKey) - - dataKey := []byte("foo") - g.Expect(encryptKey.Encrypt(dataKey)).To(Succeed()) - - t.Setenv("AZURE_CLIENT_ID", testAADConfig.ClientID) - t.Setenv("AZURE_TENANT_ID", testAADConfig.TenantID) - t.Setenv("AZURE_CLIENT_SECRET", testAADConfig.ClientSecret) - - decryptKey := &azkv.MasterKey{ - VaultURL: testVaultURL, - Name: testVaultKeyName, - Version: testVaultKeyVersion, - EncryptedKey: encryptKey.EncryptedKey, - CreationDate: time.Now(), - } - - dec, err := decryptKey.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(dec).To(Equal(dataKey)) -} - -func TestMasterKey_Decrypt_SOPS_Compat(t *testing.T) { - g := NewWithT(t) - - t.Setenv("AZURE_CLIENT_ID", testAADConfig.ClientID) - t.Setenv("AZURE_TENANT_ID", testAADConfig.TenantID) - t.Setenv("AZURE_CLIENT_SECRET", testAADConfig.ClientSecret) - - dataKey := []byte("foo") - - encryptKey := &azkv.MasterKey{ - VaultURL: testVaultURL, - Name: testVaultKeyName, - Version: testVaultKeyVersion, - CreationDate: time.Now(), - } - g.Expect(encryptKey.Encrypt(dataKey)).To(Succeed()) - - decryptKey := MasterKeyFromURL(testVaultURL, testVaultKeyName, testVaultKeyVersion) - token, err := TokenFromAADConfig(testAADConfig) - g.Expect(err).ToNot(HaveOccurred()) - token.ApplyToMasterKey(decryptKey) - - decryptKey.EncryptedKey = encryptKey.EncryptedKey - dec, err := decryptKey.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(dec).To(Equal(dataKey)) -} diff --git a/internal/decryptors/sops/kustomize-controller/azkv/keysource_test.go b/internal/decryptors/sops/kustomize-controller/azkv/keysource_test.go deleted file mode 100644 index 39c19aa..0000000 --- a/internal/decryptors/sops/kustomize-controller/azkv/keysource_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package azkv - -import ( - "testing" - "time" - - . "github.com/onsi/gomega" -) - -func TestToken_ApplyToMasterKey(t *testing.T) { - g := NewWithT(t) - - token, err := TokenFromAADConfig( - AADConfig{TenantID: "tenant", ClientID: "client", ClientSecret: "secret"}, - ) - g.Expect(err).ToNot(HaveOccurred()) - - key := &MasterKey{} - token.ApplyToMasterKey(key) - g.Expect(key.token).To(Equal(token.token)) -} - -func TestMasterKey_EncryptedDataKey(t *testing.T) { - g := NewWithT(t) - - key := &MasterKey{EncryptedKey: "some key"} - g.Expect(key.EncryptedDataKey()).To(BeEquivalentTo(key.EncryptedKey)) -} - -func TestMasterKey_SetEncryptedDataKey(t *testing.T) { - g := NewWithT(t) - - encryptedKey := []byte("encrypted") - key := &MasterKey{} - key.SetEncryptedDataKey(encryptedKey) - g.Expect(key.EncryptedKey).To(BeEquivalentTo(encryptedKey)) -} - -func TestMasterKey_NeedsRotation(t *testing.T) { - g := NewWithT(t) - - key := MasterKeyFromURL("", "", "") - g.Expect(key.NeedsRotation()).To(BeFalse()) - - key.CreationDate = key.CreationDate.Add(-(azkvTTL + time.Second)) - g.Expect(key.NeedsRotation()).To(BeTrue()) -} - -func TestMasterKey_ToString(t *testing.T) { - g := NewWithT(t) - - key := MasterKeyFromURL("https://myvault.vault.azure.net", "key-name", "key-version") - g.Expect(key.ToString()).To(Equal("https://myvault.vault.azure.net/keys/key-name/key-version")) -} - -func TestMasterKey_ToMap(t *testing.T) { - g := NewWithT(t) - - key := MasterKeyFromURL("https://myvault.vault.azure.net", "key-name", "key-version") - key.EncryptedKey = "data" - g.Expect(key.ToMap()).To(Equal(map[string]interface{}{ - "vaultUrl": key.VaultURL, - "key": key.Name, - "version": key.Version, - "created_at": key.CreationDate.UTC().Format(time.RFC3339), - "enc": key.EncryptedKey, - })) -} diff --git a/internal/decryptors/sops/kustomize-controller/gcpkms/keysource.go b/internal/decryptors/sops/kustomize-controller/gcpkms/keysource.go deleted file mode 100644 index a1946a1..0000000 --- a/internal/decryptors/sops/kustomize-controller/gcpkms/keysource.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package gcpkms - -import ( - "context" - "encoding/base64" - "fmt" - "regexp" - "time" - - kms "cloud.google.com/go/kms/apiv1" - kmspb "cloud.google.com/go/kms/apiv1/kmspb" - "google.golang.org/api/option" - "google.golang.org/grpc" -) - -var ( - // gcpkmsTTL is the duration after which a MasterKey requires rotation. - gcpkmsTTL = time.Hour * 24 * 30 * 6 -) - -// CredentialJSON is the service account keys used for authentication towards -// GCP KMS. -type CredentialJSON []byte - -// ApplyToMasterKey configures the CredentialJSON on the provided key. -func (c CredentialJSON) ApplyToMasterKey(key *MasterKey) { - key.credentialJSON = c -} - -// MasterKey is a GCP KMS key used to encrypt and decrypt the SOPS -// data key. -// Adapted from https://github.com/mozilla/sops/blob/v3.7.2/gcpkms/keysource.go -// to be able to have fine-grain control over the credentials used to authenticate -// towards GCP KMS. -type MasterKey struct { - // ResourceID is the resource id used to refer to the gcp kms key. - // It can be retrieved using the `gcloud` command. - ResourceID string - // EncryptedKey is the string returned after encrypting with GCP KMS. - EncryptedKey string - // CreationDate is the creation timestamp of the MasterKey. Used - // for NeedsRotation. - CreationDate time.Time - - // credentialJSON are the service account keys used to authenticate - // towards GCP KMS. - credentialJSON []byte - // grpcConn can be used to inject a custom GCP client connection. - // Mostly useful for testing at present, to wire the client to a mock - // server. - grpcConn *grpc.ClientConn -} - -// MasterKeyFromResourceID creates a new MasterKey with the provided resource -// ID. -func MasterKeyFromResourceID(resourceID string) *MasterKey { - return &MasterKey{ - ResourceID: resourceID, - CreationDate: time.Now().UTC(), - } -} - -// Encrypt takes a SOPS data key, encrypts it with GCP KMS, and stores the -// result in the EncryptedKey field. -func (key *MasterKey) Encrypt(datakey []byte) error { - cloudkmsService, err := key.newKMSClient() - if err != nil { - return err - } - defer cloudkmsService.Close() - - req := &kmspb.EncryptRequest{ - Name: key.ResourceID, - Plaintext: datakey, - } - ctx := context.Background() - resp, err := cloudkmsService.Encrypt(ctx, req) - if err != nil { - return fmt.Errorf("failed to encrypt sops data key with GCP KMS: %w", err) - } - key.EncryptedKey = base64.StdEncoding.EncodeToString(resp.Ciphertext) - return nil -} - -// SetEncryptedDataKey sets the encrypted data key for this master key. -func (key *MasterKey) SetEncryptedDataKey(enc []byte) { - key.EncryptedKey = string(enc) -} - -// EncryptedDataKey returns the encrypted data key this master key holds. -func (key *MasterKey) EncryptedDataKey() []byte { - return []byte(key.EncryptedKey) -} - -// EncryptIfNeeded encrypts the provided SOPS data key, if it has not been -// encrypted yet. -func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error { - if key.EncryptedKey == "" { - return key.Encrypt(dataKey) - } - return nil -} - -// Decrypt decrypts the EncryptedKey field with GCP KMS and returns -// the result. -func (key *MasterKey) Decrypt() ([]byte, error) { - service, err := key.newKMSClient() - if err != nil { - return nil, err - } - defer service.Close() - - decodedCipher, err := base64.StdEncoding.DecodeString(string(key.EncryptedDataKey())) - if err != nil { - return nil, err - } - req := &kmspb.DecryptRequest{ - Name: key.ResourceID, - Ciphertext: decodedCipher, - } - ctx := context.Background() - resp, err := service.Decrypt(ctx, req) - if err != nil { - return nil, fmt.Errorf("failed to decrypt sops data key with GCP KMS Key: %w", err) - } - - return resp.Plaintext, nil -} - -// NeedsRotation returns whether the data key needs to be rotated or not. -func (key *MasterKey) NeedsRotation() bool { - return time.Since(key.CreationDate) > (gcpkmsTTL) -} - -// ToString converts the key to a string representation. -func (key *MasterKey) ToString() string { - return key.ResourceID -} - -// ToMap converts the MasterKey to a map for serialization purposes. -func (key MasterKey) ToMap() map[string]interface{} { - out := make(map[string]interface{}) - out["resource_id"] = key.ResourceID - out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339) - out["enc"] = key.EncryptedKey - return out -} - -// newKMSClient returns a GCP KMS client configured with the credentialJSON -// and/or grpcConn, falling back to environmental defaults. -// It returns an error if the ResourceID is invalid, or if the client setup -// fails. -func (key *MasterKey) newKMSClient() (*kms.KeyManagementClient, error) { - re := regexp.MustCompile(`^projects/[^/]+/locations/[^/]+/keyRings/[^/]+/cryptoKeys/[^/]+$`) - matches := re.FindStringSubmatch(key.ResourceID) - if matches == nil { - return nil, fmt.Errorf("no valid resourceId found in %q", key.ResourceID) - } - - var opts []option.ClientOption - if key.credentialJSON != nil { - opts = append(opts, option.WithCredentialsJSON(key.credentialJSON)) - } - if key.grpcConn != nil { - opts = append(opts, option.WithGRPCConn(key.grpcConn)) - } - - ctx := context.Background() - client, err := kms.NewKeyManagementClient(ctx, opts...) - if err != nil { - return nil, err - } - - return client, nil -} diff --git a/internal/decryptors/sops/kustomize-controller/gcpkms/keysource_integration_test.go b/internal/decryptors/sops/kustomize-controller/gcpkms/keysource_integration_test.go deleted file mode 100644 index e8412a4..0000000 --- a/internal/decryptors/sops/kustomize-controller/gcpkms/keysource_integration_test.go +++ /dev/null @@ -1,148 +0,0 @@ -//go:build integration && disabled -// +build integration,disabled - -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package gcpkms - -import ( - "context" - "fmt" - "io/ioutil" - "os" - "testing" - - . "github.com/onsi/gomega" - "go.mozilla.org/sops/v3/gcpkms" - - "google.golang.org/api/option" - kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - kms "cloud.google.com/go/kms/apiv1" -) - -var ( - project = os.Getenv("TEST_PROJECT") - testKeyring = os.Getenv("TEST_KEYRING") - testKey = os.Getenv("TEST_CRYPTO_KEY") - testCredsJSON = os.Getenv("TEST_CRED_JSON") - resourceID = fmt.Sprintf("projects/%s/locations/global/keyRings/%s/cryptoKeys/%s", - project, testKeyring, testKey) -) - -func TestMasterKey_Decrypt_SOPS_Compat(t *testing.T) { - g := NewWithT(t) - os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", testCredsJSON) - - g.Expect(createKMSKeyIfNotExists(resourceID)).To(Succeed()) - - dataKey := []byte("blue golden light") - encryptedKey := gcpkms.NewMasterKeyFromResourceID(resourceID) - g.Expect(encryptedKey.Encrypt(dataKey)).To(Succeed()) - - decryptionKey := MasterKeyFromResourceID(resourceID) - creds, err := ioutil.ReadFile(testCredsJSON) - g.Expect(err).ToNot(HaveOccurred()) - decryptionKey.EncryptedKey = encryptedKey.EncryptedKey - decryptionKey.credentialJSON = creds - dec, err := decryptionKey.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(dec).To(Equal(dataKey)) -} - -func TestMasterKey_Encrypt_SOPS_Compat(t *testing.T) { - os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", testCredsJSON) - g := NewWithT(t) - - g.Expect(createKMSKeyIfNotExists(resourceID)).To(Succeed()) - - dataKey := []byte("silver golden lights") - - encryptionKey := MasterKeyFromResourceID(resourceID) - creds, err := ioutil.ReadFile(testCredsJSON) - g.Expect(err).ToNot(HaveOccurred()) - encryptionKey.credentialJSON = creds - err = encryptionKey.Encrypt(dataKey) - g.Expect(err).ToNot(HaveOccurred()) - - decryptionKey := gcpkms.NewMasterKeyFromResourceID(resourceID) - decryptionKey.EncryptedKey = encryptionKey.EncryptedKey - dec, err := decryptionKey.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(dec).To(Equal(dataKey)) -} - -func TestMasterKey_EncryptDecrypt_RoundTrip(t *testing.T) { - g := NewWithT(t) - - g.Expect(createKMSKeyIfNotExists(resourceID)).To(Succeed()) - - key := MasterKeyFromResourceID(resourceID) - creds, err := ioutil.ReadFile(testCredsJSON) - g.Expect(err).ToNot(HaveOccurred()) - key.credentialJSON = creds - - datakey := []byte("a thousand splendid sons") - g.Expect(key.Encrypt(datakey)).To(Succeed()) - g.Expect(key.EncryptedKey).ToNot(BeEmpty()) - - dec, err := key.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(dec).To(Equal(datakey)) -} - -func createKMSKeyIfNotExists(resourceID string) error { - ctx := context.Background() - // check if crypto key exists if not create it - c, err := kms.NewKeyManagementClient(ctx, option.WithCredentialsFile(testCredsJSON)) - if err != nil { - return fmt.Errorf("err creating client: %q", err) - } - - getCryptoKeyReq := &kmspb.GetCryptoKeyRequest{ - Name: resourceID, - } - _, err = c.GetCryptoKey(ctx, getCryptoKeyReq) - if err == nil { - return nil - } - - e, ok := status.FromError(err) - if !ok || (ok && e.Code() != codes.NotFound) { - return fmt.Errorf("err getting crypto key: %q", err) - } - - projectID := fmt.Sprintf("projects/%s/locations/global", project) - createKeyRingReq := &kmspb.CreateKeyRingRequest{ - Parent: projectID, - KeyRingId: testKeyring, - } - - _, err = c.CreateKeyRing(ctx, createKeyRingReq) - e, ok = status.FromError(err) - if err != nil && !(ok && e.Code() == codes.AlreadyExists) { - return fmt.Errorf("err creating key ring: %q", err) - } - - keyRingName := fmt.Sprintf("%s/keyRings/%s", projectID, testKeyring) - keyReq := &kmspb.CreateCryptoKeyRequest{ - Parent: keyRingName, - CryptoKeyId: testKey, - CryptoKey: &kmspb.CryptoKey{ - Purpose: kmspb.CryptoKey_ENCRYPT_DECRYPT, - }, - } - _, err = c.CreateCryptoKey(ctx, keyReq) - e, ok = status.FromError(err) - if err != nil && !(ok && e.Code() == codes.AlreadyExists) { - return fmt.Errorf("err creating crypto key: %q", err) - } - - return nil -} diff --git a/internal/decryptors/sops/kustomize-controller/gcpkms/keysource_test.go b/internal/decryptors/sops/kustomize-controller/gcpkms/keysource_test.go deleted file mode 100644 index c22c455..0000000 --- a/internal/decryptors/sops/kustomize-controller/gcpkms/keysource_test.go +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package gcpkms - -import ( - "encoding/base64" - "fmt" - "log" - "net" - "testing" - "time" - - kmspb "cloud.google.com/go/kms/apiv1/kmspb" - . "github.com/onsi/gomega" - "google.golang.org/grpc" -) - -var ( - testResourceID = "projects/test-flux/locations/global/keyRings/test-flux/cryptoKeys/sops" - decryptedData = "decrypted data" - encryptedData = "encrypted data" -) - -func TestMasterKey_EncryptedDataKey(t *testing.T) { - g := NewWithT(t) - key := MasterKey{EncryptedKey: encryptedData} - g.Expect(key.EncryptedDataKey()).To(BeEquivalentTo(encryptedData)) -} - -func TestMasterKey_SetEncryptedDataKey(t *testing.T) { - g := NewWithT(t) - enc := "encrypted key" - key := &MasterKey{} - key.SetEncryptedDataKey([]byte(enc)) - g.Expect(key.EncryptedDataKey()).To(BeEquivalentTo(enc)) -} - -func TestMasterKey_EncryptIfNeeded(t *testing.T) { - g := NewWithT(t) - key := MasterKey{EncryptedKey: "encrypted key"} - g.Expect(key.EncryptedDataKey()).To(BeEquivalentTo(key.EncryptedKey)) - - err := key.EncryptIfNeeded([]byte("sops data key")) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(key.EncryptedDataKey()).To(BeEquivalentTo(key.EncryptedKey)) -} - -func TestMasterKey_ToString(t *testing.T) { - rsrcId := testResourceID - g := NewWithT(t) - key := MasterKeyFromResourceID(rsrcId) - g.Expect(key.ToString()).To(Equal(rsrcId)) -} - -func TestMasterKey_ToMap(t *testing.T) { - g := NewWithT(t) - key := MasterKey{ - credentialJSON: []byte("sensitive creds"), - CreationDate: time.Date(2016, time.October, 31, 10, 0, 0, 0, time.UTC), - ResourceID: testResourceID, - EncryptedKey: "this is encrypted", - } - g.Expect(key.ToMap()).To(Equal(map[string]interface{}{ - "resource_id": testResourceID, - "enc": "this is encrypted", - "created_at": "2016-10-31T10:00:00Z", - })) -} - -func TestMasterKey_createCloudKMSService(t *testing.T) { - g := NewWithT(t) - - tests := []struct { - key MasterKey - errString string - }{ - { - key: MasterKey{ - ResourceID: "/projects", - credentialJSON: []byte("some secret"), - }, - errString: "no valid resourceId", - }, - { - key: MasterKey{ - ResourceID: testResourceID, - credentialJSON: []byte(`{ "client_id": ".apps.googleusercontent.com", - "client_secret": "", - "type": "authorized_user"}`), - }, - }, - } - - for _, tt := range tests { - _, err := tt.key.newKMSClient() - if tt.errString != "" { - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring(tt.errString)) - } else { - g.Expect(err).To(BeNil()) - } - } -} - -func TestMasterKey_Decrypt(t *testing.T) { - g := NewWithT(t) - - mockKeyManagement.err = nil - mockKeyManagement.reqs = nil - mockKeyManagement.resps = append(mockKeyManagement.resps[:0], &kmspb.DecryptResponse{ - Plaintext: []byte(decryptedData), - }) - key := MasterKey{ - grpcConn: newGRPCServer("0"), - ResourceID: testResourceID, - EncryptedKey: "encryptedKey", - } - data, err := key.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(data).To(BeEquivalentTo(decryptedData)) -} - -func TestMasterKey_Encrypt(t *testing.T) { - g := NewWithT(t) - - mockKeyManagement.err = nil - mockKeyManagement.reqs = nil - mockKeyManagement.resps = append(mockKeyManagement.resps[:0], &kmspb.EncryptResponse{ - Ciphertext: []byte(encryptedData), - }) - - key := MasterKey{ - grpcConn: newGRPCServer("0"), - ResourceID: testResourceID, - } - err := key.Encrypt([]byte("encrypt")) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(key.EncryptedDataKey()).To(BeEquivalentTo(base64.StdEncoding.EncodeToString([]byte(encryptedData)))) -} - -var ( - mockKeyManagement mockKeyManagementServer -) - -func newGRPCServer(port string) *grpc.ClientConn { - serv := grpc.NewServer() - kmspb.RegisterKeyManagementServiceServer(serv, &mockKeyManagement) - - lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%s", port)) - if err != nil { - log.Fatal(err) - } - go serv.Serve(lis) - - conn, err := grpc.Dial(lis.Addr().String(), grpc.WithInsecure()) - if err != nil { - log.Fatal(err) - } - - return conn -} diff --git a/internal/decryptors/sops/kustomize-controller/gcpkms/mock_kms_server_test.go b/internal/decryptors/sops/kustomize-controller/gcpkms/mock_kms_server_test.go deleted file mode 100644 index 1cb09ab..0000000 --- a/internal/decryptors/sops/kustomize-controller/gcpkms/mock_kms_server_test.go +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by gapic-generator. DO NOT EDIT. - -// Ref: https://github.com/googleapis/google-cloud-go/blob/4fe86a327f97ada275ce1744459129df38f9c95b/kms/apiv1/mock_test.go - -package gcpkms - -import ( - "context" - "fmt" - "io" - "strings" - - kmspb "cloud.google.com/go/kms/apiv1/kmspb" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/anypb" - - status "google.golang.org/genproto/googleapis/rpc/status" - "google.golang.org/grpc/metadata" -) - -var _ = io.EOF -var _ = anypb.New -var _ status.Status - -type mockKeyManagementServer struct { - // Embed for forward compatibility. - // Tests will keep working if more methods are added - // in the future. - kmspb.KeyManagementServiceServer - - reqs []proto.Message - - // If set, all calls return this error. - err error - - // responses to return if err == nil - resps []proto.Message -} - -func (s *mockKeyManagementServer) ListKeyRings(ctx context.Context, req *kmspb.ListKeyRingsRequest) (*kmspb.ListKeyRingsResponse, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.ListKeyRingsResponse), nil -} - -func (s *mockKeyManagementServer) ListCryptoKeys(ctx context.Context, req *kmspb.ListCryptoKeysRequest) (*kmspb.ListCryptoKeysResponse, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.ListCryptoKeysResponse), nil -} - -func (s *mockKeyManagementServer) ListCryptoKeyVersions(ctx context.Context, req *kmspb.ListCryptoKeyVersionsRequest) (*kmspb.ListCryptoKeyVersionsResponse, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.ListCryptoKeyVersionsResponse), nil -} - -func (s *mockKeyManagementServer) ListImportJobs(ctx context.Context, req *kmspb.ListImportJobsRequest) (*kmspb.ListImportJobsResponse, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.ListImportJobsResponse), nil -} - -func (s *mockKeyManagementServer) GetKeyRing(ctx context.Context, req *kmspb.GetKeyRingRequest) (*kmspb.KeyRing, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.KeyRing), nil -} - -func (s *mockKeyManagementServer) GetCryptoKey(ctx context.Context, req *kmspb.GetCryptoKeyRequest) (*kmspb.CryptoKey, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.CryptoKey), nil -} - -func (s *mockKeyManagementServer) GetCryptoKeyVersion(ctx context.Context, req *kmspb.GetCryptoKeyVersionRequest) (*kmspb.CryptoKeyVersion, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.CryptoKeyVersion), nil -} - -func (s *mockKeyManagementServer) GetPublicKey(ctx context.Context, req *kmspb.GetPublicKeyRequest) (*kmspb.PublicKey, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.PublicKey), nil -} - -func (s *mockKeyManagementServer) GetImportJob(ctx context.Context, req *kmspb.GetImportJobRequest) (*kmspb.ImportJob, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.ImportJob), nil -} - -func (s *mockKeyManagementServer) CreateKeyRing(ctx context.Context, req *kmspb.CreateKeyRingRequest) (*kmspb.KeyRing, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.KeyRing), nil -} - -func (s *mockKeyManagementServer) CreateCryptoKey(ctx context.Context, req *kmspb.CreateCryptoKeyRequest) (*kmspb.CryptoKey, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.CryptoKey), nil -} - -func (s *mockKeyManagementServer) CreateCryptoKeyVersion(ctx context.Context, req *kmspb.CreateCryptoKeyVersionRequest) (*kmspb.CryptoKeyVersion, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.CryptoKeyVersion), nil -} - -func (s *mockKeyManagementServer) ImportCryptoKeyVersion(ctx context.Context, req *kmspb.ImportCryptoKeyVersionRequest) (*kmspb.CryptoKeyVersion, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.CryptoKeyVersion), nil -} - -func (s *mockKeyManagementServer) CreateImportJob(ctx context.Context, req *kmspb.CreateImportJobRequest) (*kmspb.ImportJob, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.ImportJob), nil -} - -func (s *mockKeyManagementServer) UpdateCryptoKey(ctx context.Context, req *kmspb.UpdateCryptoKeyRequest) (*kmspb.CryptoKey, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.CryptoKey), nil -} - -func (s *mockKeyManagementServer) UpdateCryptoKeyVersion(ctx context.Context, req *kmspb.UpdateCryptoKeyVersionRequest) (*kmspb.CryptoKeyVersion, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.CryptoKeyVersion), nil -} - -func (s *mockKeyManagementServer) Encrypt(ctx context.Context, req *kmspb.EncryptRequest) (*kmspb.EncryptResponse, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.EncryptResponse), nil -} - -func (s *mockKeyManagementServer) Decrypt(ctx context.Context, req *kmspb.DecryptRequest) (*kmspb.DecryptResponse, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.DecryptResponse), nil -} - -func (s *mockKeyManagementServer) AsymmetricSign(ctx context.Context, req *kmspb.AsymmetricSignRequest) (*kmspb.AsymmetricSignResponse, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.AsymmetricSignResponse), nil -} - -func (s *mockKeyManagementServer) AsymmetricDecrypt(ctx context.Context, req *kmspb.AsymmetricDecryptRequest) (*kmspb.AsymmetricDecryptResponse, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.AsymmetricDecryptResponse), nil -} - -func (s *mockKeyManagementServer) UpdateCryptoKeyPrimaryVersion(ctx context.Context, req *kmspb.UpdateCryptoKeyPrimaryVersionRequest) (*kmspb.CryptoKey, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.CryptoKey), nil -} - -func (s *mockKeyManagementServer) DestroyCryptoKeyVersion(ctx context.Context, req *kmspb.DestroyCryptoKeyVersionRequest) (*kmspb.CryptoKeyVersion, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.CryptoKeyVersion), nil -} - -func (s *mockKeyManagementServer) RestoreCryptoKeyVersion(ctx context.Context, req *kmspb.RestoreCryptoKeyVersionRequest) (*kmspb.CryptoKeyVersion, error) { - md, _ := metadata.FromIncomingContext(ctx) - if xg := md["x-goog-api-client"]; len(xg) == 0 || !strings.Contains(xg[0], "gl-go/") { - return nil, fmt.Errorf("x-goog-api-client = %v, expected gl-go key", xg) - } - s.reqs = append(s.reqs, req) - if s.err != nil { - return nil, s.err - } - return s.resps[0].(*kmspb.CryptoKeyVersion), nil -} diff --git a/internal/decryptors/sops/kustomize-controller/hcvault/keysource.go b/internal/decryptors/sops/kustomize-controller/hcvault/keysource.go deleted file mode 100644 index cfbfde0..0000000 --- a/internal/decryptors/sops/kustomize-controller/hcvault/keysource.go +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (C) 2020 The Mozilla SOPS authors -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package hcvault - -import ( - "encoding/base64" - "fmt" - "path" - "time" - - "github.com/hashicorp/vault/api" -) - -var ( - // vaultTTL is the duration after which a MasterKey requires rotation. - vaultTTL = time.Hour * 24 * 30 * 6 -) - -// VaultToken used for authenticating towards a Vault server. -type VaultToken string - -// ApplyToMasterKey configures the token on the provided key. -func (t VaultToken) ApplyToMasterKey(key *MasterKey) { - key.vaultToken = string(t) -} - -// MasterKey is a Vault Transit backend path used to Encrypt and Decrypt -// SOPS' data key. -// -// Adapted from https://github.com/mozilla/sops/blob/v3.7.1/hcvault/keysource.go -// to be able to have fine-grain control over the used decryption keys -// without relying on the existence of environment variable or file. -type MasterKey struct { - KeyName string - EnginePath string - VaultAddress string - - EncryptedKey string - CreationDate time.Time - - vaultToken string -} - -// MasterKeyFromAddress creates a new MasterKey from a Vault address, Transit -// backend path and a key name. -func MasterKeyFromAddress(address, enginePath, keyName string) *MasterKey { - key := &MasterKey{ - VaultAddress: address, - EnginePath: enginePath, - KeyName: keyName, - CreationDate: time.Now().UTC(), - } - return key -} - -// Encrypt takes a SOPS data key, encrypts it with Vault Transit, and stores -// the result in the EncryptedKey field. -func (key *MasterKey) Encrypt(dataKey []byte) error { - client, err := vaultClient(key.VaultAddress, key.vaultToken) - if err != nil { - return err - } - - fullPath := key.encryptPath() - secret, err := client.Logical().Write(fullPath, encryptPayload(dataKey)) - if err != nil { - return fmt.Errorf("failed to encrypt sops data key to Vault transit backend '%s': %w", fullPath, err) - } - encryptedKey, err := encryptedKeyFromSecret(secret) - if err != nil { - return fmt.Errorf("failed to encrypt sops data key to Vault transit backend '%s': %w", fullPath, err) - } - key.EncryptedKey = encryptedKey - return nil -} - -// EncryptIfNeeded encrypts the provided SOPS data key, if it has not been -// encrypted yet. -func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error { - if key.EncryptedKey == "" { - return key.Encrypt(dataKey) - } - return nil -} - -// EncryptedDataKey returns the encrypted data key this master key holds. -func (key *MasterKey) EncryptedDataKey() []byte { - return []byte(key.EncryptedKey) -} - -// SetEncryptedDataKey sets the encrypted data key for this master key. -func (key *MasterKey) SetEncryptedDataKey(enc []byte) { - key.EncryptedKey = string(enc) -} - -// Decrypt decrypts the EncryptedKey field with Vault Transit and returns the result. -func (key *MasterKey) Decrypt() ([]byte, error) { - client, err := vaultClient(key.VaultAddress, key.vaultToken) - if err != nil { - return nil, err - } - - fullPath := key.decryptPath() - secret, err := client.Logical().Write(fullPath, decryptPayload(key.EncryptedKey)) - if err != nil { - return nil, fmt.Errorf("failed to decrypt sops data key from Vault transit backend '%s': %w", fullPath, err) - } - dataKey, err := dataKeyFromSecret(secret) - if err != nil { - return nil, fmt.Errorf("failed to decrypt sops data key from Vault transit backend '%s': %w", fullPath, err) - } - return dataKey, nil -} - -// NeedsRotation returns whether the data key needs to be rotated or not. -func (key *MasterKey) NeedsRotation() bool { - // TODO: manage rewrapping https://www.vaultproject.io/api/secret/transit/index.html#rewrap-data - return time.Since(key.CreationDate) > (vaultTTL) -} - -// ToString converts the key to a string representation. -func (key *MasterKey) ToString() string { - return fmt.Sprintf("%s/v1/%s/keys/%s", key.VaultAddress, key.EnginePath, key.KeyName) -} - -// ToMap converts the MasterKey to a map for serialization purposes. -func (key MasterKey) ToMap() map[string]interface{} { - out := make(map[string]interface{}) - out["vault_address"] = key.VaultAddress - out["key_name"] = key.KeyName - out["engine_path"] = key.EnginePath - out["enc"] = key.EncryptedKey - out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339) - return out -} - -// encryptPath returns the path for Encrypt requests. -func (key *MasterKey) encryptPath() string { - return path.Join(key.EnginePath, "encrypt", key.KeyName) -} - -// decryptPath returns the path for Decrypt requests. -func (key *MasterKey) decryptPath() string { - return path.Join(key.EnginePath, "decrypt", key.KeyName) -} - -// encryptPayload returns the payload for an encrypt request of the dataKey. -func encryptPayload(dataKey []byte) map[string]interface{} { - encoded := base64.StdEncoding.EncodeToString(dataKey) - return map[string]interface{}{ - "plaintext": encoded, - } -} - -// encryptedKeyFromSecret attempts to extract the encrypted key from the data -// of the provided secret. -func encryptedKeyFromSecret(secret *api.Secret) (string, error) { - if secret == nil || secret.Data == nil { - return "", fmt.Errorf("transit backend is empty") - } - encrypted, ok := secret.Data["ciphertext"] - if !ok { - return "", fmt.Errorf("no encrypted data") - } - encryptedKey, ok := encrypted.(string) - if !ok { - return "", fmt.Errorf("encrypted ciphertext cannot be cast to string") - } - return encryptedKey, nil -} - -// decryptPayload returns the payload for a decrypt request of the -// encryptedKey. -func decryptPayload(encryptedKey string) map[string]interface{} { - return map[string]interface{}{ - "ciphertext": encryptedKey, - } -} - -// dataKeyFromSecret attempts to extract the data key from the data of the -// provided secret. -func dataKeyFromSecret(secret *api.Secret) ([]byte, error) { - if secret == nil || secret.Data == nil { - return nil, fmt.Errorf("transit backend is empty") - } - decrypted, ok := secret.Data["plaintext"] - if !ok { - return nil, fmt.Errorf("no decrypted data") - } - plaintext, ok := decrypted.(string) - if !ok { - return nil, fmt.Errorf("decrypted plaintext data cannot be cast to string") - } - dataKey, err := base64.StdEncoding.DecodeString(plaintext) - if err != nil { - return nil, fmt.Errorf("cannot decode base64 plaintext into data key bytes") - } - return dataKey, nil -} - -// vaultClient returns a new Vault client, configured with the given address -// and token. -func vaultClient(address, token string) (*api.Client, error) { - cfg := api.DefaultConfig() - cfg.Address = address - client, err := api.NewClient(cfg) - if err != nil { - return nil, fmt.Errorf("cannot create Vault client: %w", err) - } - client.SetToken(token) - return client, nil -} diff --git a/internal/decryptors/sops/kustomize-controller/hcvault/keysource_test.go b/internal/decryptors/sops/kustomize-controller/hcvault/keysource_test.go deleted file mode 100644 index dfeabf2..0000000 --- a/internal/decryptors/sops/kustomize-controller/hcvault/keysource_test.go +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package hcvault - -import ( - "fmt" - logger "log" - "os" - "path" - "testing" - "time" - - "github.com/hashicorp/vault/api" - . "github.com/onsi/gomega" - "github.com/ory/dockertest/v3" - "go.mozilla.org/sops/v3/hcvault" -) - -var ( - // testVaultVersion is the version (image tag) of the Vault server image - // used to test against. - testVaultVersion = "1.10.0" - // testVaultToken is the token of the Vault server. - testVaultToken = "secret" - // testEnginePath is the path to mount the Vault Transit on. - testEnginePath = "sops" - // testVaultAddress is the HTTP/S address of the Vault server, it is set - // by TestMain after booting it. - testVaultAddress string -) - -// TestMain initializes a Vault server using Docker, writes the HTTP address to -// testVaultAddress, waits for it to become ready to serve requests, and enables -// Vault Transit on the testEnginePath. It then runs all the tests, which can -// make use of the various `test*` variables. -func TestMain(m *testing.M) { - // Uses a sensible default on Windows (TCP/HTTP) and Linux/MacOS (socket) - pool, err := dockertest.NewPool("") - if err != nil { - logger.Fatalf("could not connect to docker: %s", err) - } - - // 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}) - if err != nil { - logger.Fatalf("could not start resource: %s", err) - } - - purgeResource := func() { - if err := pool.Purge(resource); err != nil { - logger.Printf("could not purge resource: %s", err) - } - } - - testVaultAddress = fmt.Sprintf("http://127.0.0.1:%v", resource.GetPort("8200/tcp")) - // Wait until Vault is ready to serve requests - if err := pool.Retry(func() error { - cfg := api.DefaultConfig() - cfg.Address = testVaultAddress - cli, err := api.NewClient(cfg) - if err != nil { - return fmt.Errorf("cannot create Vault client: %w", err) - } - status, err := cli.Sys().InitStatus() - if err != nil { - return err - } - if status != true { - return fmt.Errorf("waiting on Vault server to become ready") - } - return nil - }); err != nil { - purgeResource() - logger.Fatalf("could not connect to docker: %s", err) - } - - if err = enableVaultTransit(testVaultAddress, testVaultToken, testEnginePath); err != nil { - purgeResource() - logger.Fatalf("could not enable Vault transit: %s", err) - } - - // Run the tests, but only if we succeeded in setting up the Vault server - var code int - if err == nil { - code = m.Run() - } - - // This can't be deferred, as os.Exit simpy does not care - if err := pool.Purge(resource); err != nil { - logger.Fatalf("could not purge resource: %s", err) - } - - os.Exit(code) -} - -func TestMasterKey_Encrypt(t *testing.T) { - g := NewWithT(t) - - key := MasterKeyFromAddress(testVaultAddress, testEnginePath, "encrypt") - (VaultToken(testVaultToken)).ApplyToMasterKey(key) - g.Expect(createVaultKey(key)).To(Succeed()) - - dataKey := []byte("the majority of your brain is fat") - g.Expect(key.Encrypt(dataKey)).To(Succeed()) - g.Expect(key.EncryptedKey).ToNot(BeEmpty()) - - client, err := vaultClient(key.VaultAddress, key.vaultToken) - g.Expect(err).ToNot(HaveOccurred()) - - payload := decryptPayload(key.EncryptedKey) - secret, err := client.Logical().Write(key.decryptPath(), payload) - g.Expect(err).ToNot(HaveOccurred()) - - decryptedData, err := dataKeyFromSecret(secret) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(decryptedData).To(Equal(dataKey)) - - key.EnginePath = "invalid" - g.Expect(key.Encrypt(dataKey)).To(HaveOccurred()) - - key.EnginePath = testEnginePath - key.vaultToken = "" - g.Expect(key.Encrypt(dataKey)).To(HaveOccurred()) -} - -func TestMasterKey_Encrypt_SOPS_Compat(t *testing.T) { - g := NewWithT(t) - - encryptKey := MasterKeyFromAddress(testVaultAddress, testEnginePath, "encrypt-compat") - (VaultToken(testVaultToken)).ApplyToMasterKey(encryptKey) - g.Expect(createVaultKey(encryptKey)).To(Succeed()) - - dataKey := []byte("foo") - g.Expect(encryptKey.Encrypt(dataKey)).To(Succeed()) - - t.Setenv("VAULT_ADDR", testVaultAddress) - t.Setenv("VAULT_TOKEN", testVaultToken) - decryptKey := hcvault.NewMasterKey(testVaultAddress, testEnginePath, "encrypt-compat") - decryptKey.EncryptedKey = encryptKey.EncryptedKey - dec, err := decryptKey.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(dec).To(Equal(dataKey)) -} - -func TestMasterKey_EncryptIfNeeded(t *testing.T) { - g := NewWithT(t) - - key := MasterKeyFromAddress(testVaultAddress, testEnginePath, "encrypt-if-needed") - (VaultToken(testVaultToken)).ApplyToMasterKey(key) - g.Expect(createVaultKey(key)).To(Succeed()) - - g.Expect(key.EncryptIfNeeded([]byte("data"))).To(Succeed()) - - encryptedKey := key.EncryptedKey - g.Expect(encryptedKey).ToNot(BeEmpty()) - - g.Expect(key.EncryptIfNeeded([]byte("some other data"))).To(Succeed()) - g.Expect(key.EncryptedKey).To(Equal(encryptedKey)) -} - -func TestMasterKey_EncryptedDataKey(t *testing.T) { - g := NewWithT(t) - - key := &MasterKey{EncryptedKey: "some key"} - g.Expect(key.EncryptedDataKey()).To(BeEquivalentTo(key.EncryptedKey)) -} - -func TestMasterKey_Decrypt(t *testing.T) { - g := NewWithT(t) - - key := MasterKeyFromAddress(testVaultAddress, testEnginePath, "decrypt") - (VaultToken(testVaultToken)).ApplyToMasterKey(key) - g.Expect(createVaultKey(key)).To(Succeed()) - - client, err := vaultClient(key.VaultAddress, key.vaultToken) - g.Expect(err).ToNot(HaveOccurred()) - - dataKey := []byte("the heart of a shrimp is located in its head") - secret, err := client.Logical().Write(key.encryptPath(), encryptPayload(dataKey)) - g.Expect(err).ToNot(HaveOccurred()) - - encryptedKey, err := encryptedKeyFromSecret(secret) - g.Expect(err).NotTo(HaveOccurred()) - - key.EncryptedKey = encryptedKey - got, err := key.Decrypt() - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(got).To(Equal(dataKey)) - - key.EnginePath = "invalid" - g.Expect(key.Encrypt(dataKey)).To(HaveOccurred()) - - key.EnginePath = testEnginePath - key.vaultToken = "" - g.Expect(key.Encrypt(dataKey)).To(HaveOccurred()) -} - -func TestMasterKey_Decrypt_SOPS_Compat(t *testing.T) { - g := NewWithT(t) - - decryptKey := MasterKeyFromAddress(testVaultAddress, testEnginePath, "decrypt-compat") - (VaultToken(testVaultToken)).ApplyToMasterKey(decryptKey) - g.Expect(createVaultKey(decryptKey)).To(Succeed()) - - dataKey := []byte("foo") - - t.Setenv("VAULT_ADDR", testVaultAddress) - t.Setenv("VAULT_TOKEN", testVaultToken) - encryptKey := hcvault.NewMasterKey(testVaultAddress, testEnginePath, "decrypt-compat") - g.Expect(encryptKey.Encrypt(dataKey)).To(Succeed()) - - decryptKey.EncryptedKey = encryptKey.EncryptedKey - dec, err := decryptKey.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(dec).To(Equal(dataKey)) -} - -func TestMasterKey_EncryptDecrypt_RoundTrip(t *testing.T) { - g := NewWithT(t) - - token := VaultToken(testVaultToken) - - encryptKey := MasterKeyFromAddress(testVaultAddress, testEnginePath, "roundtrip") - token.ApplyToMasterKey(encryptKey) - g.Expect(createVaultKey(encryptKey)).To(Succeed()) - - dataKey := []byte("some people have an extra bone in their knee") - g.Expect(encryptKey.Encrypt(dataKey)).To(Succeed()) - g.Expect(encryptKey.EncryptedKey).ToNot(BeEmpty()) - - decryptKey := MasterKeyFromAddress(testVaultAddress, testEnginePath, "roundtrip") - token.ApplyToMasterKey(decryptKey) - decryptKey.EncryptedKey = encryptKey.EncryptedKey - - decryptedData, err := decryptKey.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(decryptedData).To(Equal(dataKey)) -} - -func TestMasterKey_NeedsRotation(t *testing.T) { - g := NewWithT(t) - - key := MasterKeyFromAddress("", "", "") - g.Expect(key.NeedsRotation()).To(BeFalse()) - - key.CreationDate = key.CreationDate.Add(-(vaultTTL + time.Second)) - g.Expect(key.NeedsRotation()).To(BeTrue()) -} - -func TestMasterKey_ToString(t *testing.T) { - g := NewWithT(t) - - key := MasterKeyFromAddress("https://example.com", "engine", "key-name") - g.Expect(key.ToString()).To(Equal("https://example.com/v1/engine/keys/key-name")) -} - -func TestMasterKey_ToMap(t *testing.T) { - g := NewWithT(t) - - key := &MasterKey{ - KeyName: "test-key", - EnginePath: "engine", - VaultAddress: testVaultAddress, - EncryptedKey: "some-encrypted-key", - } - g.Expect(key.ToMap()).To(Equal(map[string]interface{}{ - "vault_address": key.VaultAddress, - "key_name": key.KeyName, - "engine_path": key.EnginePath, - "enc": key.EncryptedKey, - "created_at": "0001-01-01T00:00:00Z", - })) -} - -func Test_encryptedKeyFromSecret(t *testing.T) { - tests := []struct { - name string - secret *api.Secret - want string - wantErr bool - }{ - {name: "nil secret", secret: nil, wantErr: true}, - {name: "secret with nil data", secret: &api.Secret{Data: nil}, wantErr: true}, - {name: "secret without ciphertext data", secret: &api.Secret{Data: map[string]interface{}{"other": true}}, wantErr: true}, - {name: "ciphertext non string", secret: &api.Secret{Data: map[string]interface{}{"ciphertext": 123}}, wantErr: true}, - {name: "ciphertext data", secret: &api.Secret{Data: map[string]interface{}{"ciphertext": "secret string"}}, want: "secret string"}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - - got, err := encryptedKeyFromSecret(tt.secret) - if tt.wantErr { - g.Expect(err).To(HaveOccurred()) - g.Expect(got).To(BeEmpty()) - return - } - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(got).To(Equal(tt.want)) - }) - } -} - -func Test_dataKeyFromSecret(t *testing.T) { - tests := []struct { - name string - secret *api.Secret - want []byte - wantErr bool - }{ - {name: "nil secret", secret: nil, wantErr: true}, - {name: "secret with nil data", secret: &api.Secret{Data: nil}, wantErr: true}, - {name: "secret without plaintext data", secret: &api.Secret{Data: map[string]interface{}{"other": true}}, wantErr: true}, - {name: "plaintext non string", secret: &api.Secret{Data: map[string]interface{}{"plaintext": 123}}, wantErr: true}, - {name: "plaintext non base64", secret: &api.Secret{Data: map[string]interface{}{"plaintext": "notbase64"}}, wantErr: true}, - {name: "plaintext base64 data", secret: &api.Secret{Data: map[string]interface{}{"plaintext": "Zm9v"}}, want: []byte("foo")}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - - got, err := dataKeyFromSecret(tt.secret) - if tt.wantErr { - g.Expect(err).To(HaveOccurred()) - g.Expect(got).To(BeNil()) - return - } - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(got).To(Equal(tt.want)) - }) - } -} - -// enableVaultTransit enables the Vault Transit backend on the given enginePath. -func enableVaultTransit(address, token, enginePath string) error { - client, err := vaultClient(address, token) - if err != nil { - return fmt.Errorf("cannot create Vault client: %w", err) - } - - if err = client.Sys().Mount(enginePath, &api.MountInput{ - Type: "transit", - Description: "backend transit used by SOPS", - }); err != nil { - return fmt.Errorf("failed to mount transit on engine path '%s': %w", enginePath, err) - } - return nil -} - -// createVaultKey creates a new RSA-4096 Vault key using the data from the -// provided MasterKey. -func createVaultKey(key *MasterKey) error { - client, err := vaultClient(key.VaultAddress, key.vaultToken) - if err != nil { - return fmt.Errorf("cannot create Vault client: %w", err) - } - - p := path.Join(key.EnginePath, "keys", key.KeyName) - payload := make(map[string]interface{}) - payload["type"] = "rsa-4096" - if _, err = client.Logical().Write(p, payload); err != nil { - return err - } - - _, err = client.Logical().Read(p) - return err -} diff --git a/internal/decryptors/sops/kustomize-controller/keyservice/keyservice.go b/internal/decryptors/sops/kustomize-controller/keyservice/keyservice.go deleted file mode 100644 index 4d2dda5..0000000 --- a/internal/decryptors/sops/kustomize-controller/keyservice/keyservice.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package keyservice - -import ( - "go.mozilla.org/sops/v3/age" - "go.mozilla.org/sops/v3/keys" - "go.mozilla.org/sops/v3/pgp" -) - -// IsOfflineMethod returns true for offline decrypt methods or false otherwise -func IsOfflineMethod(mk keys.MasterKey) bool { - switch mk.(type) { - case *pgp.MasterKey, *age.MasterKey: - return true - default: - return false - } -} diff --git a/internal/decryptors/sops/kustomize-controller/keyservice/options.go b/internal/decryptors/sops/kustomize-controller/keyservice/options.go deleted file mode 100644 index 76204f1..0000000 --- a/internal/decryptors/sops/kustomize-controller/keyservice/options.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package keyservice - -import ( - extage "filippo.io/age" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/gcpkms" - "go.mozilla.org/sops/v3/keyservice" - - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/age" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/awskms" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/azkv" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/hcvault" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/pgp" -) - -// ServerOption is some configuration that modifies the Server. -type ServerOption interface { - // ApplyToServer applies this configuration to the given Server. - ApplyToServer(s *Server) -} - -// WithGnuPGHome configures the GnuPG home directory on the Server. -type WithGnuPGHome string - -// ApplyToServer applies this configuration to the given Server. -func (o WithGnuPGHome) ApplyToServer(s *Server) { - s.gnuPGHome = pgp.GnuPGHome(o) -} - -// WithVaultToken configures the Hashicorp Vault token on the Server. -type WithVaultToken string - -// ApplyToServer applies this configuration to the given Server. -func (o WithVaultToken) ApplyToServer(s *Server) { - s.vaultToken = hcvault.VaultToken(o) -} - -// WithAgeIdentities configures the parsed age identities on the Server. -type WithAgeIdentities []extage.Identity - -// ApplyToServer applies this configuration to the given Server. -func (o WithAgeIdentities) ApplyToServer(s *Server) { - s.ageIdentities = age.ParsedIdentities(o) -} - -// WithAWSKeys configures the AWS credentials on the Server -type WithAWSKeys struct { - CredsProvider *awskms.CredsProvider -} - -// ApplyToServer applies this configuration to the given Server. -func (o WithAWSKeys) ApplyToServer(s *Server) { - s.awsCredsProvider = o.CredsProvider -} - -// WithGCPCredsJSON configures the GCP service account credentials JSON on the -// Server. -type WithGCPCredsJSON []byte - -// ApplyToServer applies this configuration to the given Server. -func (o WithGCPCredsJSON) ApplyToServer(s *Server) { - s.gcpCredsJSON = gcpkms.CredentialJSON(o) -} - -// WithAzureToken configures the Azure credential token on the Server. -type WithAzureToken struct { - Token *azkv.Token -} - -// ApplyToServer applies this configuration to the given Server. -func (o WithAzureToken) ApplyToServer(s *Server) { - s.azureToken = o.Token -} - -// WithDefaultServer configures the fallback default server on the Server. -type WithDefaultServer struct { - Server keyservice.KeyServiceServer -} - -// ApplyToServer applies this configuration to the given Server. -func (o WithDefaultServer) ApplyToServer(s *Server) { - s.defaultServer = o.Server -} diff --git a/internal/decryptors/sops/kustomize-controller/keyservice/server.go b/internal/decryptors/sops/kustomize-controller/keyservice/server.go deleted file mode 100644 index 231d3cb..0000000 --- a/internal/decryptors/sops/kustomize-controller/keyservice/server.go +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package keyservice - -import ( - "fmt" - - "go.mozilla.org/sops/v3/keyservice" - "golang.org/x/net/context" - - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/age" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/awskms" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/azkv" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/gcpkms" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/hcvault" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/pgp" -) - -// Server is a key service server that uses SOPS MasterKeys to fulfill -// requests. It intercepts Encrypt and Decrypt requests made for key types -// that need to run in a contained environment, instead of the default -// implementation which heavily utilizes environment variables or the runtime -// environment. Any request not handled by the Server is forwarded to the -// embedded default server. -type Server struct { - // gnuPGHome is the GnuPG home directory used for the Encrypt and Decrypt - // operations for PGP key types. - // When empty, the requests will be handled using the systems' runtime - // keyring. - gnuPGHome pgp.GnuPGHome - - // ageIdentities are the parsed age identities used for Decrypt - // operations for age key types. - ageIdentities age.ParsedIdentities - - // vaultToken is the token used for Encrypt and Decrypt operations of - // Hashicorp Vault requests. - // When empty, the request will be handled by defaultServer. - vaultToken hcvault.VaultToken - - // azureToken is the credential token used for Encrypt and Decrypt - // operations of Azure Key Vault requests. - // When nil, the request will be handled by defaultServer. - azureToken *azkv.Token - - // awsCredsProvider is the Credentials object used for Encrypt and Decrypt - // operations of AWS KMS requests. - // When nil, the request will be handled by defaultServer. - awsCredsProvider *awskms.CredsProvider - - // gcpCredsJSON is the JSON credentials used for Decrypt and Encrypt - // operations of GCP KMS requests. When nil, a default client with - // environmental runtime settings will be used. - gcpCredsJSON gcpkms.CredentialJSON - - // defaultServer is the fallback server, used to handle any request that - // is not eligible to be handled by this Server. - defaultServer keyservice.KeyServiceServer -} - -// NewServer constructs a new Server, configuring it with the provided options -// before returning the result. -// When WithDefaultServer() is not provided as an option, the SOPS server -// implementation is configured as default. -func NewServer(options ...ServerOption) keyservice.KeyServiceServer { - s := &Server{} - for _, opt := range options { - opt.ApplyToServer(s) - } - if s.defaultServer == nil { - s.defaultServer = &keyservice.Server{ - Prompt: false, - } - } - return s -} - -// Encrypt takes an encrypt request and encrypts the provided plaintext with -// the provided key, returning the encrypted result. -func (ks Server) Encrypt(ctx context.Context, req *keyservice.EncryptRequest) (*keyservice.EncryptResponse, error) { - key := req.Key - switch k := key.KeyType.(type) { - case *keyservice.Key_PgpKey: - ciphertext, err := ks.encryptWithPgp(k.PgpKey, req.Plaintext) - if err != nil { - return nil, err - } - return &keyservice.EncryptResponse{ - Ciphertext: ciphertext, - }, nil - case *keyservice.Key_AgeKey: - ciphertext, err := ks.encryptWithAge(k.AgeKey, req.Plaintext) - if err != nil { - return nil, err - } - return &keyservice.EncryptResponse{ - Ciphertext: ciphertext, - }, nil - case *keyservice.Key_VaultKey: - if ks.vaultToken != "" { - ciphertext, err := ks.encryptWithHCVault(k.VaultKey, req.Plaintext) - if err != nil { - return nil, err - } - return &keyservice.EncryptResponse{ - Ciphertext: ciphertext, - }, nil - } - case *keyservice.Key_KmsKey: - cipherText, err := ks.encryptWithAWSKMS(k.KmsKey, req.Plaintext) - if err != nil { - return nil, err - } - return &keyservice.EncryptResponse{ - Ciphertext: cipherText, - }, nil - case *keyservice.Key_AzureKeyvaultKey: - ciphertext, err := ks.encryptWithAzureKeyVault(k.AzureKeyvaultKey, req.Plaintext) - if err != nil { - return nil, err - } - return &keyservice.EncryptResponse{ - Ciphertext: ciphertext, - }, nil - case *keyservice.Key_GcpKmsKey: - ciphertext, err := ks.encryptWithGCPKMS(k.GcpKmsKey, req.Plaintext) - if err != nil { - return nil, err - } - return &keyservice.EncryptResponse{ - Ciphertext: ciphertext, - }, nil - case nil: - return nil, fmt.Errorf("must provide a key") - } - // Fallback to default server for any other request. - return ks.defaultServer.Encrypt(ctx, req) -} - -// Decrypt takes a decrypt request and decrypts the provided ciphertext with -// the provided key, returning the decrypted result. -func (ks Server) Decrypt(ctx context.Context, req *keyservice.DecryptRequest) (*keyservice.DecryptResponse, error) { - key := req.Key - switch k := key.KeyType.(type) { - case *keyservice.Key_PgpKey: - plaintext, err := ks.decryptWithPgp(k.PgpKey, req.Ciphertext) - if err != nil { - return nil, err - } - return &keyservice.DecryptResponse{ - Plaintext: plaintext, - }, nil - case *keyservice.Key_AgeKey: - plaintext, err := ks.decryptWithAge(k.AgeKey, req.Ciphertext) - if err != nil { - return nil, err - } - return &keyservice.DecryptResponse{ - Plaintext: plaintext, - }, nil - case *keyservice.Key_VaultKey: - if ks.vaultToken != "" { - plaintext, err := ks.decryptWithHCVault(k.VaultKey, req.Ciphertext) - if err != nil { - return nil, err - } - return &keyservice.DecryptResponse{ - Plaintext: plaintext, - }, nil - } - case *keyservice.Key_KmsKey: - plaintext, err := ks.decryptWithAWSKMS(k.KmsKey, req.Ciphertext) - if err != nil { - return nil, err - } - return &keyservice.DecryptResponse{ - Plaintext: plaintext, - }, nil - case *keyservice.Key_AzureKeyvaultKey: - plaintext, err := ks.decryptWithAzureKeyVault(k.AzureKeyvaultKey, req.Ciphertext) - if err != nil { - return nil, err - } - return &keyservice.DecryptResponse{ - Plaintext: plaintext, - }, nil - case *keyservice.Key_GcpKmsKey: - plaintext, err := ks.decryptWithGCPKMS(k.GcpKmsKey, req.Ciphertext) - if err != nil { - return nil, err - } - return &keyservice.DecryptResponse{ - Plaintext: plaintext, - }, nil - case nil: - return nil, fmt.Errorf("must provide a key") - } - // Fallback to default server for any other request. - return ks.defaultServer.Decrypt(ctx, req) -} - -func (ks *Server) encryptWithPgp(key *keyservice.PgpKey, plaintext []byte) ([]byte, error) { - pgpKey := pgp.MasterKeyFromFingerprint(key.Fingerprint) - if ks.gnuPGHome != "" { - ks.gnuPGHome.ApplyToMasterKey(pgpKey) - } - err := pgpKey.Encrypt(plaintext) - if err != nil { - return nil, err - } - return []byte(pgpKey.EncryptedKey), nil -} - -func (ks *Server) decryptWithPgp(key *keyservice.PgpKey, ciphertext []byte) ([]byte, error) { - pgpKey := pgp.MasterKeyFromFingerprint(key.Fingerprint) - if ks.gnuPGHome != "" { - ks.gnuPGHome.ApplyToMasterKey(pgpKey) - } - pgpKey.EncryptedKey = string(ciphertext) - plaintext, err := pgpKey.Decrypt() - return plaintext, err -} - -func (ks Server) encryptWithAge(key *keyservice.AgeKey, plaintext []byte) ([]byte, error) { - // Unlike the other encrypt and decrypt methods, validation of configuration - // is not required here. As the encryption happens purely based on the - // Recipient from the key. - - ageKey := age.MasterKey{ - Recipient: key.Recipient, - } - if err := ageKey.Encrypt(plaintext); err != nil { - return nil, err - } - return []byte(ageKey.EncryptedKey), nil -} - -func (ks *Server) decryptWithAge(key *keyservice.AgeKey, ciphertext []byte) ([]byte, error) { - ageKey := age.MasterKey{ - Recipient: key.Recipient, - } - ks.ageIdentities.ApplyToMasterKey(&ageKey) - ageKey.EncryptedKey = string(ciphertext) - plaintext, err := ageKey.Decrypt() - return plaintext, err -} - -func (ks *Server) encryptWithHCVault(key *keyservice.VaultKey, plaintext []byte) ([]byte, error) { - vaultKey := hcvault.MasterKey{ - VaultAddress: key.VaultAddress, - EnginePath: key.EnginePath, - KeyName: key.KeyName, - } - ks.vaultToken.ApplyToMasterKey(&vaultKey) - if err := vaultKey.Encrypt(plaintext); err != nil { - return nil, err - } - return []byte(vaultKey.EncryptedKey), nil -} - -func (ks *Server) decryptWithHCVault(key *keyservice.VaultKey, ciphertext []byte) ([]byte, error) { - vaultKey := hcvault.MasterKey{ - VaultAddress: key.VaultAddress, - EnginePath: key.EnginePath, - KeyName: key.KeyName, - } - vaultKey.EncryptedKey = string(ciphertext) - ks.vaultToken.ApplyToMasterKey(&vaultKey) - plaintext, err := vaultKey.Decrypt() - return plaintext, err -} - -func (ks *Server) encryptWithAWSKMS(key *keyservice.KmsKey, plaintext []byte) ([]byte, error) { - context := make(map[string]string) - for key, val := range key.Context { - context[key] = val - } - awsKey := awskms.MasterKey{ - Arn: key.Arn, - Role: key.Role, - EncryptionContext: context, - } - if ks.awsCredsProvider != nil { - ks.awsCredsProvider.ApplyToMasterKey(&awsKey) - } - if err := awsKey.Encrypt(plaintext); err != nil { - return nil, err - } - return []byte(awsKey.EncryptedKey), nil -} - -func (ks *Server) decryptWithAWSKMS(key *keyservice.KmsKey, cipherText []byte) ([]byte, error) { - context := make(map[string]string) - for key, val := range key.Context { - context[key] = val - } - awsKey := awskms.MasterKey{ - Arn: key.Arn, - Role: key.Role, - EncryptionContext: context, - } - awsKey.EncryptedKey = string(cipherText) - - if ks.awsCredsProvider != nil { - ks.awsCredsProvider.ApplyToMasterKey(&awsKey) - } - return awsKey.Decrypt() -} - -func (ks *Server) encryptWithAzureKeyVault(key *keyservice.AzureKeyVaultKey, plaintext []byte) ([]byte, error) { - azureKey := azkv.MasterKey{ - VaultURL: key.VaultUrl, - Name: key.Name, - Version: key.Version, - } - if ks.azureToken != nil { - ks.azureToken.ApplyToMasterKey(&azureKey) - } - if err := azureKey.Encrypt(plaintext); err != nil { - return nil, err - } - return []byte(azureKey.EncryptedKey), nil -} - -func (ks *Server) decryptWithAzureKeyVault(key *keyservice.AzureKeyVaultKey, ciphertext []byte) ([]byte, error) { - azureKey := azkv.MasterKey{ - VaultURL: key.VaultUrl, - Name: key.Name, - Version: key.Version, - } - if ks.azureToken != nil { - ks.azureToken.ApplyToMasterKey(&azureKey) - } - azureKey.EncryptedKey = string(ciphertext) - plaintext, err := azureKey.Decrypt() - return plaintext, err -} - -func (ks *Server) encryptWithGCPKMS(key *keyservice.GcpKmsKey, plaintext []byte) ([]byte, error) { - gcpKey := gcpkms.MasterKey{ - ResourceID: key.ResourceId, - } - ks.gcpCredsJSON.ApplyToMasterKey(&gcpKey) - if err := gcpKey.Encrypt(plaintext); err != nil { - return nil, err - } - return gcpKey.EncryptedDataKey(), nil -} - -func (ks *Server) decryptWithGCPKMS(key *keyservice.GcpKmsKey, ciphertext []byte) ([]byte, error) { - gcpKey := gcpkms.MasterKey{ - ResourceID: key.ResourceId, - } - ks.gcpCredsJSON.ApplyToMasterKey(&gcpKey) - gcpKey.EncryptedKey = string(ciphertext) - plaintext, err := gcpKey.Decrypt() - return plaintext, err -} diff --git a/internal/decryptors/sops/kustomize-controller/keyservice/server_test.go b/internal/decryptors/sops/kustomize-controller/keyservice/server_test.go deleted file mode 100644 index de623d3..0000000 --- a/internal/decryptors/sops/kustomize-controller/keyservice/server_test.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package keyservice - -import ( - "fmt" - "os" - "testing" - - "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - "github.com/aws/aws-sdk-go-v2/credentials" - . "github.com/onsi/gomega" - "go.mozilla.org/sops/v3/keyservice" - "golang.org/x/net/context" - - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/age" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/awskms" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/azkv" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/gcpkms" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/hcvault" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/pgp" -) - -func TestServer_EncryptDecrypt_PGP(t *testing.T) { - const ( - mockPublicKey = "../pgp/testdata/public.gpg" - mockPrivateKey = "../pgp/testdata/private.gpg" - mockFingerprint = "B59DAF469E8C948138901A649732075EA221A7EA" - ) - - g := NewWithT(t) - - gnuPGHome, err := pgp.NewGnuPGHome() - g.Expect(err).ToNot(HaveOccurred()) - t.Cleanup(func() { - _ = os.RemoveAll(gnuPGHome.String()) - }) - g.Expect(gnuPGHome.ImportFile(mockPublicKey)).To(Succeed()) - - s := NewServer(WithGnuPGHome(gnuPGHome)) - key := KeyFromMasterKey(pgp.MasterKeyFromFingerprint(mockFingerprint)) - dataKey := []byte("some data key") - encResp, err := s.Encrypt(context.TODO(), &keyservice.EncryptRequest{ - Key: &key, - Plaintext: dataKey, - }) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(encResp.Ciphertext).ToNot(BeEmpty()) - g.Expect(encResp.Ciphertext).ToNot(Equal(dataKey)) - - g.Expect(gnuPGHome.ImportFile(mockPrivateKey)).To(Succeed()) - decResp, err := s.Decrypt(context.TODO(), &keyservice.DecryptRequest{ - Key: &key, - Ciphertext: encResp.Ciphertext, - }) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(decResp.Plaintext).To(Equal(dataKey)) -} - -func TestServer_EncryptDecrypt_age(t *testing.T) { - g := NewWithT(t) - - const ( - mockRecipient string = "age1lzd99uklcjnc0e7d860axevet2cz99ce9pq6tzuzd05l5nr28ams36nvun" - mockIdentity string = "AGE-SECRET-KEY-1G0Q5K9TV4REQ3ZSQRMTMG8NSWQGYT0T7TZ33RAZEE0GZYVZN0APSU24RK7" - ) - - s := NewServer() - key := KeyFromMasterKey(&age.MasterKey{Recipient: mockRecipient}) - dataKey := []byte("some data key") - encResp, err := s.Encrypt(context.TODO(), &keyservice.EncryptRequest{ - Key: &key, - Plaintext: dataKey, - }) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(encResp.Ciphertext).ToNot(BeEmpty()) - g.Expect(encResp.Ciphertext).ToNot(Equal(dataKey)) - - i := make(age.ParsedIdentities, 0) - g.Expect(i.Import(mockIdentity)).To(Succeed()) - - s = NewServer(WithAgeIdentities(i)) - decResp, err := s.Decrypt(context.TODO(), &keyservice.DecryptRequest{ - Key: &key, - Ciphertext: encResp.Ciphertext, - }) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(decResp.Plaintext).To(Equal(dataKey)) -} - -func TestServer_EncryptDecrypt_HCVault(t *testing.T) { - g := NewWithT(t) - - s := NewServer(WithVaultToken("token")) - key := KeyFromMasterKey(hcvault.MasterKeyFromAddress("https://example.com", "engine-path", "key-name")) - _, err := s.Encrypt(context.TODO(), &keyservice.EncryptRequest{ - Key: &key, - }) - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring("failed to encrypt sops data key to Vault transit backend")) - - _, err = s.Decrypt(context.TODO(), &keyservice.DecryptRequest{ - Key: &key, - }) - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring("failed to decrypt sops data key from Vault transit backend")) -} - -func TestServer_EncryptDecrypt_HCVault_Fallback(t *testing.T) { - g := NewWithT(t) - - fallback := NewMockKeyServer() - s := NewServer(WithDefaultServer{Server: fallback}) - - key := KeyFromMasterKey(hcvault.MasterKeyFromAddress("https://example.com", "engine-path", "key-name")) - encReq := &keyservice.EncryptRequest{ - Key: &key, - Plaintext: []byte("some data key"), - } - _, err := s.Encrypt(context.TODO(), encReq) - g.Expect(err).To(HaveOccurred()) - g.Expect(fallback.encryptReqs).To(HaveLen(1)) - g.Expect(fallback.encryptReqs).To(ContainElement(encReq)) - g.Expect(fallback.decryptReqs).To(HaveLen(0)) - - fallback = NewMockKeyServer() - s = NewServer(WithDefaultServer{Server: fallback}) - decReq := &keyservice.DecryptRequest{ - Key: &key, - Ciphertext: []byte("some ciphertext"), - } - _, err = s.Decrypt(context.TODO(), decReq) - g.Expect(fallback.decryptReqs).To(HaveLen(1)) - g.Expect(fallback.decryptReqs).To(ContainElement(decReq)) - g.Expect(fallback.encryptReqs).To(HaveLen(0)) -} - -func TestServer_EncryptDecrypt_awskms(t *testing.T) { - g := NewWithT(t) - s := NewServer(WithAWSKeys{ - CredsProvider: awskms.NewCredsProvider(credentials.StaticCredentialsProvider{}), - }) - - key := KeyFromMasterKey(awskms.NewMasterKeyFromArn("arn:aws:kms:us-west-2:107501996527:key/612d5f0p-p1l3-45e6-aca6-a5b005693a48", nil, "")) - _, err := s.Encrypt(context.TODO(), &keyservice.EncryptRequest{ - Key: &key, - }) - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring("failed to encrypt sops data key with AWS KMS")) - - _, err = s.Decrypt(context.TODO(), &keyservice.DecryptRequest{ - Key: &key, - }) - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring("failed to decrypt sops data key with AWS KMS")) -} - -func TestServer_EncryptDecrypt_azkv(t *testing.T) { - g := NewWithT(t) - - identity, err := azidentity.NewDefaultAzureCredential(nil) - g.Expect(err).ToNot(HaveOccurred()) - s := NewServer(WithAzureToken{Token: azkv.NewToken(identity)}) - - key := KeyFromMasterKey(azkv.MasterKeyFromURL("", "", "")) - _, err = s.Encrypt(context.TODO(), &keyservice.EncryptRequest{ - Key: &key, - }) - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring("failed to encrypt sops data key with Azure Key Vault")) - - _, err = s.Decrypt(context.TODO(), &keyservice.DecryptRequest{ - Key: &key, - }) - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring("failed to decrypt sops data key with Azure Key Vault")) - -} - -func TestServer_EncryptDecrypt_gcpkms(t *testing.T) { - g := NewWithT(t) - - creds := `{ "client_id": ".apps.googleusercontent.com", - "client_secret": "", - "type": "authorized_user"}` - s := NewServer(WithGCPCredsJSON([]byte(creds))) - - resourceID := "projects/test-flux/locations/global/keyRings/test-flux/cryptoKeys/sops" - key := KeyFromMasterKey(gcpkms.MasterKeyFromResourceID(resourceID)) - _, err := s.Encrypt(context.TODO(), &keyservice.EncryptRequest{ - Key: &key, - }) - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring("failed to encrypt sops data key with GCP KMS")) - - _, err = s.Decrypt(context.TODO(), &keyservice.DecryptRequest{ - Key: &key, - }) - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring("failed to decrypt sops data key with GCP KMS")) - -} - -func TestServer_EncryptDecrypt_Nil_KeyType(t *testing.T) { - g := NewWithT(t) - - s := NewServer(WithDefaultServer{NewMockKeyServer()}) - - expectErr := fmt.Errorf("must provide a key") - - _, err := s.Encrypt(context.TODO(), &keyservice.EncryptRequest{Key: &keyservice.Key{KeyType: nil}}) - g.Expect(err).To(Equal(expectErr)) - - _, err = s.Decrypt(context.TODO(), &keyservice.DecryptRequest{Key: &keyservice.Key{KeyType: nil}}) - g.Expect(err).To(Equal(expectErr)) -} diff --git a/internal/decryptors/sops/kustomize-controller/keyservice/utils_test.go b/internal/decryptors/sops/kustomize-controller/keyservice/utils_test.go deleted file mode 100644 index e73b897..0000000 --- a/internal/decryptors/sops/kustomize-controller/keyservice/utils_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package keyservice - -import ( - "context" - "fmt" - - "go.mozilla.org/sops/v3/keys" - "go.mozilla.org/sops/v3/keyservice" - - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/age" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/awskms" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/azkv" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/gcpkms" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/hcvault" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/pgp" -) - -// KeyFromMasterKey converts a SOPS internal MasterKey to an RPC Key that can -// be serialized with Protocol Buffers. -func KeyFromMasterKey(k keys.MasterKey) keyservice.Key { - switch mk := k.(type) { - case *pgp.MasterKey: - return keyservice.Key{ - KeyType: &keyservice.Key_PgpKey{ - PgpKey: &keyservice.PgpKey{ - Fingerprint: mk.Fingerprint, - }, - }, - } - case *hcvault.MasterKey: - return keyservice.Key{ - KeyType: &keyservice.Key_VaultKey{ - VaultKey: &keyservice.VaultKey{ - VaultAddress: mk.VaultAddress, - EnginePath: mk.EnginePath, - KeyName: mk.KeyName, - }, - }, - } - case *awskms.MasterKey: - return keyservice.Key{ - KeyType: &keyservice.Key_KmsKey{ - KmsKey: &keyservice.KmsKey{ - Arn: mk.Arn, - }, - }, - } - case *azkv.MasterKey: - return keyservice.Key{ - KeyType: &keyservice.Key_AzureKeyvaultKey{ - AzureKeyvaultKey: &keyservice.AzureKeyVaultKey{ - VaultUrl: mk.VaultURL, - Name: mk.Name, - Version: mk.Version, - }, - }, - } - case *age.MasterKey: - return keyservice.Key{ - KeyType: &keyservice.Key_AgeKey{ - AgeKey: &keyservice.AgeKey{ - Recipient: mk.Recipient, - }, - }, - } - case *gcpkms.MasterKey: - return keyservice.Key{ - KeyType: &keyservice.Key_GcpKmsKey{ - GcpKmsKey: &keyservice.GcpKmsKey{ - ResourceId: mk.ResourceID, - }, - }, - } - default: - panic(fmt.Sprintf("tried to convert unknown MasterKey type %T to keyservice.Key", mk)) - } -} - -type MockKeyServer struct { - encryptReqs []*keyservice.EncryptRequest - decryptReqs []*keyservice.DecryptRequest -} - -func NewMockKeyServer() *MockKeyServer { - return &MockKeyServer{ - encryptReqs: make([]*keyservice.EncryptRequest, 0), - decryptReqs: make([]*keyservice.DecryptRequest, 0), - } -} - -func (ks *MockKeyServer) Encrypt(_ context.Context, req *keyservice.EncryptRequest) (*keyservice.EncryptResponse, error) { - ks.encryptReqs = append(ks.encryptReqs, req) - return nil, fmt.Errorf("not actually implemented") -} - -func (ks *MockKeyServer) Decrypt(_ context.Context, req *keyservice.DecryptRequest) (*keyservice.DecryptResponse, error) { - ks.decryptReqs = append(ks.decryptReqs, req) - return nil, fmt.Errorf("not actually implemented") -} diff --git a/internal/decryptors/sops/kustomize-controller/pgp/keysource.go b/internal/decryptors/sops/kustomize-controller/pgp/keysource.go deleted file mode 100644 index 1d9343d..0000000 --- a/internal/decryptors/sops/kustomize-controller/pgp/keysource.go +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright (C) 2016-2020 The Mozilla SOPS authors -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package pgp - -import ( - "bytes" - "fmt" - "io" - "os" - "os/exec" - "os/user" - "path/filepath" - "strings" - "time" -) - -const ( - // SopsGpgExecEnv can be set as an environment variable to overwrite the - // GnuPG binary used. - SopsGpgExecEnv = "FLUX_SOPS_GPG_EXEC" -) - -var ( - // pgpTTL is the duration after which a MasterKey requires rotation. - pgpTTL = time.Hour * 24 * 30 * 6 -) - -// MasterKey is a PGP key used to securely store SOPS' data key by -// encrypting it and decrypting it. -// -// Adapted from https://github.com/mozilla/sops/blob/v3.7.2/pgp/keysource.go -// to be able to control the GPG home directory and have a "contained" -// environment. -// -// We are unable to drop the dependency on the GPG binary (although we -// wish!) because the builtin GPG support in Go is limited, it does for -// example not offer support for FIPS: -// * https://github.com/golang/go/issues/11658#issuecomment-120448974 -// * https://github.com/golang/go/issues/45188 -type MasterKey struct { - // Fingerprint contains the fingerprint of the PGP key used to Encrypt - // or Decrypt the data key with. - Fingerprint string - // EncryptedKey contains the SOPS data key encrypted with PGP. - EncryptedKey string - // CreationDate of the MasterKey, used to determine if the EncryptedKey - // needs rotation. - CreationDate time.Time - - // gnuPGHomeDir contains the absolute path to a GnuPG home directory. - // It can be injected by a (local) keyservice.KeyServiceServer using - // GnuPGHome.ApplyToMasterKey(). - gnuPGHomeDir string -} - -// MasterKeyFromFingerprint takes a PGP fingerprint and returns a -// new MasterKey with that fingerprint. -func MasterKeyFromFingerprint(fingerprint string) *MasterKey { - return &MasterKey{ - Fingerprint: strings.Replace(fingerprint, " ", "", -1), - CreationDate: time.Now().UTC(), - } -} - -// GnuPGHome is the absolute path to a GnuPG home directory. -// A new keyring can be constructed by combining the use of NewGnuPGHome() and -// Import() or ImportFile(). -type GnuPGHome string - -// NewGnuPGHome initializes a new GnuPGHome in a temporary directory. -// The caller is expected to handle the garbage collection of the created -// directory. -func NewGnuPGHome() (GnuPGHome, error) { - tmpDir, err := os.MkdirTemp("", "sops-gnupghome-") - if err != nil { - return "", fmt.Errorf("failed to create new GnuPG home: %w", err) - } - return GnuPGHome(tmpDir), nil -} - -// Import attempts to import the armored key bytes into the GnuPGHome keyring. -// It returns an error if the GnuPGHome does not pass Validate, or if the -// import failed. -func (d GnuPGHome) Import(armoredKey []byte) error { - if err := d.Validate(); err != nil { - return fmt.Errorf("cannot import armored key data into GnuPG keyring: %w", err) - } - - args := []string{"--batch", "--import"} - err, _, stderr := gpgExec(d.String(), args, bytes.NewReader(armoredKey)) - if err != nil { - return fmt.Errorf("failed to import armored key data into GnuPG keyring: %s", strings.TrimSpace(stderr.String())) - } - return nil -} - -// ImportFile attempts to import the armored key file into the GnuPGHome -// keyring. -// It returns an error if the GnuPGHome does not pass Validate, or if the -// import failed. -func (d GnuPGHome) ImportFile(path string) error { - b, err := os.ReadFile(path) - if err != nil { - return fmt.Errorf("cannot read armored key data from file: %w", err) - } - return d.Import(b) -} - -// Validate ensures the GnuPGHome is a valid GnuPG home directory path. -// When validation fails, it returns a descriptive reason as error. -func (d GnuPGHome) Validate() error { - if d == "" { - return fmt.Errorf("empty GNUPGHOME path") - } - if !filepath.IsAbs(d.String()) { - return fmt.Errorf("GNUPGHOME must be an absolute path") - } - fi, err := os.Lstat(d.String()) - if err != nil { - if os.IsNotExist(err) { - return fmt.Errorf("GNUPGHOME does not exist") - } - return fmt.Errorf("cannot stat GNUPGHOME: %w", err) - } - if !fi.IsDir() { - return fmt.Errorf("GNUGPHOME is not a directory") - } - if perm := fi.Mode().Perm(); perm != 0o700 { - return fmt.Errorf("GNUPGHOME has invalid permissions: got %#o wanted %#o", perm, 0o700) - } - return nil -} - -// String returns the GnuPGHome as a string. It does not Validate. -func (d GnuPGHome) String() string { - return string(d) -} - -// ApplyToMasterKey configures the GnuPGHome on the provided key if it passes -// Validate. -func (d GnuPGHome) ApplyToMasterKey(key *MasterKey) { - if err := d.Validate(); err == nil { - key.gnuPGHomeDir = d.String() - } -} - -// Encrypt encrypts the data key with the PGP key with the same -// fingerprint as the MasterKey. -func (key *MasterKey) Encrypt(dataKey []byte) error { - fingerprint := shortenFingerprint(key.Fingerprint) - - args := []string{ - "--no-default-recipient", - "--yes", - "--encrypt", - "-a", - "-r", - key.Fingerprint, - "--trusted-key", - fingerprint, - "--no-encrypt-to", - } - err, stdout, stderr := gpgExec(key.gnuPGHome(), args, bytes.NewReader(dataKey)) - if err != nil { - return fmt.Errorf("failed to encrypt sops data key with pgp: %s", strings.TrimSpace(stderr.String())) - } - - key.SetEncryptedDataKey(bytes.TrimSpace(stdout.Bytes())) - return nil -} - -// EncryptIfNeeded encrypts the data key with PGP only if it's needed, -// that is, if it hasn't been encrypted already. -func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error { - if key.EncryptedKey == "" { - return key.Encrypt(dataKey) - } - return nil -} - -// EncryptedDataKey returns the encrypted data key this master key holds. -func (key *MasterKey) EncryptedDataKey() []byte { - return []byte(key.EncryptedKey) -} - -// SetEncryptedDataKey sets the encrypted data key for this master key. -func (key *MasterKey) SetEncryptedDataKey(enc []byte) { - key.EncryptedKey = string(enc) -} - -// Decrypt uses PGP to obtain the data key from the EncryptedKey store -// in the MasterKey and returns it. -func (key *MasterKey) Decrypt() ([]byte, error) { - args := []string{ - "-d", - } - err, stdout, stderr := gpgExec(key.gnuPGHome(), args, strings.NewReader(key.EncryptedKey)) - if err != nil { - return nil, fmt.Errorf("failed to decrypt sops data key with pgp: %s", strings.TrimSpace(stderr.String())) - } - return stdout.Bytes(), nil -} - -// NeedsRotation returns whether the data key needs to be rotated -// or not. -func (key *MasterKey) NeedsRotation() bool { - return time.Since(key.CreationDate) > (pgpTTL) -} - -// ToString returns the string representation of the key, i.e. its -// fingerprint. -func (key *MasterKey) ToString() string { - return key.Fingerprint -} - -// ToMap converts the MasterKey into a map for serialization purposes. -func (key MasterKey) ToMap() map[string]interface{} { - out := make(map[string]interface{}) - out["fp"] = key.Fingerprint - out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339) - out["enc"] = key.EncryptedKey - return out -} - -// gnuPGHome determines the GnuPG home directory for the MasterKey, and returns -// its path. In order of preference: -// 1. MasterKey.gnuPGHomeDir -// 2. $GNUPGHOME -// 3. user.Current().HomeDir/.gnupg -// 4. $HOME/.gnupg -func (key *MasterKey) gnuPGHome() string { - if key.gnuPGHomeDir == "" { - dir := os.Getenv("GNUPGHOME") - if dir == "" { - usr, err := user.Current() - if err != nil { - return filepath.Join(os.Getenv("HOME"), ".gnupg") - } - return filepath.Join(usr.HomeDir, ".gnupg") - } - return dir - } - return key.gnuPGHomeDir -} - -// gpgExec runs the provided args with the gpgBinary, while restricting it to -// gnuPGHome. Stdout and stderr can be read from the returned buffers. -// When the command fails, an error is returned. -func gpgExec(gnuPGHome string, args []string, stdin io.Reader) (err error, stdout bytes.Buffer, stderr bytes.Buffer) { - if gnuPGHome != "" { - args = append([]string{"--no-default-keyring", "--homedir", gnuPGHome}, args...) - } - - cmd := exec.Command(gpgBinary(), args...) - cmd.Stdin = stdin - cmd.Stdout = &stdout - cmd.Stderr = &stderr - err = cmd.Run() - return -} - -// gpgBinary returns the GnuPG binary which must be used. -// It allows for runtime modifications by setting the environment variable -// SopsGpgExecEnv to the absolute path of the replacement binary. -func gpgBinary() string { - binary := "gpg" - if envBinary := os.Getenv(SopsGpgExecEnv); envBinary != "" && filepath.IsAbs(envBinary) { - binary = envBinary - } - return binary -} - -// shortenFingerprint returns the short ID of the given fingerprint. -// This is mostly used for compatability reasons, as older versions of GnuPG -// do not always like long IDs. -func shortenFingerprint(fingerprint string) string { - if offset := len(fingerprint) - 16; offset > 0 { - fingerprint = fingerprint[offset:] - } - return fingerprint -} diff --git a/internal/decryptors/sops/kustomize-controller/pgp/keysource_test.go b/internal/decryptors/sops/kustomize-controller/pgp/keysource_test.go deleted file mode 100644 index 4080084..0000000 --- a/internal/decryptors/sops/kustomize-controller/pgp/keysource_test.go +++ /dev/null @@ -1,419 +0,0 @@ -// Copyright (C) 2022 The Flux authors -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -package pgp - -import ( - "bytes" - "os" - "os/user" - "path/filepath" - "strings" - "testing" - "time" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - . "github.com/onsi/gomega" - "go.mozilla.org/sops/v3/pgp" -) - -var ( - mockPublicKey = "testdata/public.gpg" - mockPrivateKey = "testdata/private.gpg" - mockFingerprint = "B59DAF469E8C948138901A649732075EA221A7EA" -) - -func TestMasterKeyFromFingerprint(t *testing.T) { - g := NewWithT(t) - - key := MasterKeyFromFingerprint(mockFingerprint) - g.Expect(key.Fingerprint).To(Equal(mockFingerprint)) - g.Expect(key.CreationDate).Should(BeTemporally("~", time.Now(), time.Second)) - - key = MasterKeyFromFingerprint("B59DAF 469E8C94813 8901A 649732075E A221A7EA") - g.Expect(key.Fingerprint).To(Equal(mockFingerprint)) -} - -func TestNewGnuPGHome(t *testing.T) { - g := NewWithT(t) - - gnuPGHome, err := NewGnuPGHome() - g.Expect(err).NotTo(HaveOccurred()) - - g.Expect(gnuPGHome.String()).To(BeADirectory()) - t.Cleanup(func() { - _ = os.RemoveAll(gnuPGHome.String()) - }) - g.Expect(gnuPGHome.Validate()).ToNot(HaveOccurred()) -} - -func TestGnuPGHome_Import(t *testing.T) { - g := NewWithT(t) - - gnuPGHome, err := NewGnuPGHome() - g.Expect(err).NotTo(HaveOccurred()) - t.Cleanup(func() { - _ = os.RemoveAll(gnuPGHome.String()) - }) - - b, err := os.ReadFile(mockPublicKey) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(gnuPGHome.Import(b)).To(Succeed()) - - err, _, stderr := gpgExec(gnuPGHome.String(), []string{"--list-keys", mockFingerprint}, nil) - g.Expect(err).ToNot(HaveOccurred(), stderr.String()) - - b, err = os.ReadFile(mockPrivateKey) - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(gnuPGHome.Import(b)).To(Succeed()) - - err, _, stderr = gpgExec(gnuPGHome.String(), []string{"--list-secret-keys", mockFingerprint}, nil) - g.Expect(err).ToNot(HaveOccurred(), stderr.String()) - - g.Expect(gnuPGHome.Import([]byte("invalid armored data"))).To(HaveOccurred()) - - g.Expect(GnuPGHome("").Import(b)).To(HaveOccurred()) -} - -func TestGnuPGHome_ImportFile(t *testing.T) { - g := NewWithT(t) - - gnuPGHome, err := NewGnuPGHome() - g.Expect(err).NotTo(HaveOccurred()) - t.Cleanup(func() { - _ = os.RemoveAll(gnuPGHome.String()) - }) - - g.Expect(gnuPGHome.ImportFile(mockPublicKey)).To(Succeed()) - g.Expect(gnuPGHome.ImportFile("invalid")).To(HaveOccurred()) -} - -func TestGnuPGHome_Validate(t *testing.T) { - t.Run("empty path", func(t *testing.T) { - g := NewWithT(t) - - g.Expect(GnuPGHome("").Validate()).To(HaveOccurred()) - }) - - t.Run("relative path", func(t *testing.T) { - g := NewWithT(t) - - g.Expect(GnuPGHome("../../.gnupghome").Validate()).To(HaveOccurred()) - }) - - t.Run("file path", func(t *testing.T) { - g := NewWithT(t) - - tmpDir := t.TempDir() - f, err := os.CreateTemp(tmpDir, "file") - g.Expect(err).ToNot(HaveOccurred()) - defer f.Close() - - g.Expect(GnuPGHome(f.Name()).Validate()).To(HaveOccurred()) - }) - - t.Run("wrong permissions", func(t *testing.T) { - g := NewWithT(t) - - // Is created with 0755 - tmpDir := t.TempDir() - g.Expect(GnuPGHome(tmpDir).Validate()).To(HaveOccurred()) - }) - - t.Run("valid", func(t *testing.T) { - g := NewWithT(t) - - gnupgHome, err := NewGnuPGHome() - g.Expect(err).ToNot(HaveOccurred()) - t.Cleanup(func() { - _ = os.RemoveAll(gnupgHome.String()) - }) - g.Expect(gnupgHome.Validate()).To(Succeed()) - }) -} - -func TestGnuPGHome_String(t *testing.T) { - g := NewWithT(t) - - gnuPGHome := GnuPGHome("/some/absolute/path") - g.Expect(gnuPGHome.String()).To(Equal("/some/absolute/path")) -} - -func TestGnuPGHome_ApplyToMasterKey(t *testing.T) { - g := NewWithT(t) - - gnuPGHome, err := NewGnuPGHome() - g.Expect(err).ToNot(HaveOccurred()) - t.Cleanup(func() { - _ = os.RemoveAll(gnuPGHome.String()) - }) - - key := MasterKeyFromFingerprint(mockFingerprint) - gnuPGHome.ApplyToMasterKey(key) - g.Expect(key.gnuPGHomeDir).To(Equal(gnuPGHome.String())) - - gnuPGHome = "/non/existing/absolute/path/fails/validate" - gnuPGHome.ApplyToMasterKey(key) - g.Expect(key.gnuPGHomeDir).ToNot(Equal(gnuPGHome.String())) -} - -func TestMasterKey_Encrypt(t *testing.T) { - g := NewWithT(t) - - gnuPGHome, err := NewGnuPGHome() - g.Expect(err).ToNot(HaveOccurred()) - t.Cleanup(func() { - _ = os.RemoveAll(gnuPGHome.String()) - }) - g.Expect(gnuPGHome.ImportFile(mockPublicKey)).To(Succeed()) - - key := MasterKeyFromFingerprint(mockFingerprint) - gnuPGHome.ApplyToMasterKey(key) - data := []byte("oh no, my darkest secret") - g.Expect(key.Encrypt(data)).To(Succeed()) - - g.Expect(key.EncryptedKey).ToNot(BeEmpty()) - g.Expect(key.EncryptedKey).ToNot(Equal(data)) - - g.Expect(gnuPGHome.ImportFile(mockPrivateKey)).To(Succeed()) - - args := []string{ - "-d", - } - err, stdout, stderr := gpgExec(key.gnuPGHome(), args, strings.NewReader(key.EncryptedKey)) - g.Expect(err).ToNot(HaveOccurred(), stderr.String()) - g.Expect(stdout.Bytes()).To(Equal(data)) - - key.Fingerprint = "invalid" - err = key.Encrypt([]byte("invalid")) - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(Equal("failed to encrypt sops data key with pgp: gpg: 'invalid' is not a valid long keyID")) -} - -func TestMasterKey_Encrypt_SOPS_Compat(t *testing.T) { - g := NewWithT(t) - - gnuPGHome, err := NewGnuPGHome() - g.Expect(err).ToNot(HaveOccurred()) - t.Cleanup(func() { - _ = os.RemoveAll(gnuPGHome.String()) - }) - g.Expect(gnuPGHome.ImportFile(mockPrivateKey)).To(Succeed()) - - dataKey := []byte("foo") - - encryptKey := MasterKeyFromFingerprint(mockFingerprint) - gnuPGHome.ApplyToMasterKey(encryptKey) - g.Expect(encryptKey.Encrypt(dataKey)).To(Succeed()) - - t.Setenv("GNUPGHOME", gnuPGHome.String()) - decryptKey := pgp.NewMasterKeyFromFingerprint(mockFingerprint) - decryptKey.EncryptedKey = encryptKey.EncryptedKey - dec, err := decryptKey.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(dec).To(Equal(dataKey)) -} - -func TestMasterKey_EncryptIfNeeded(t *testing.T) { - g := NewWithT(t) - - gnuPGHome, err := NewGnuPGHome() - g.Expect(err).ToNot(HaveOccurred()) - t.Cleanup(func() { - _ = os.RemoveAll(gnuPGHome.String()) - }) - g.Expect(gnuPGHome.ImportFile(mockPrivateKey)).To(Succeed()) - - key := MasterKeyFromFingerprint(mockFingerprint) - gnuPGHome.ApplyToMasterKey(key) - g.Expect(key.EncryptIfNeeded([]byte("data"))).To(Succeed()) - - encryptedKey := key.EncryptedKey - g.Expect(encryptedKey).To(ContainSubstring("END PGP MESSAGE")) - - g.Expect(key.EncryptIfNeeded([]byte("some other data"))).To(Succeed()) - g.Expect(key.EncryptedKey).To(Equal(encryptedKey)) -} - -func TestMasterKey_EncryptedDataKey(t *testing.T) { - g := NewWithT(t) - - key := &MasterKey{EncryptedKey: "some key"} - g.Expect(key.EncryptedDataKey()).To(BeEquivalentTo(key.EncryptedKey)) -} - -func TestMasterKey_Decrypt(t *testing.T) { - g := NewWithT(t) - - gnuPGHome, err := NewGnuPGHome() - g.Expect(err).ToNot(HaveOccurred()) - t.Cleanup(func() { - _ = os.RemoveAll(gnuPGHome.String()) - }) - g.Expect(gnuPGHome.ImportFile(mockPrivateKey)).To(Succeed()) - - fingerprint := shortenFingerprint(mockFingerprint) - - data := []byte("this data is absolutely top secret") - err, stdout, stderr := gpgExec(gnuPGHome.String(), []string{ - "--no-default-recipient", - "--yes", - "--encrypt", - "-a", - "-r", - fingerprint, - "--trusted-key", - fingerprint, - "--no-encrypt-to", - }, bytes.NewReader(data)) - g.Expect(err).NotTo(HaveOccurred(), stderr.String()) - - encryptedData := stdout.String() - g.Expect(encryptedData).ToNot(BeEquivalentTo(data)) - - key := MasterKeyFromFingerprint(mockFingerprint) - gnuPGHome.ApplyToMasterKey(key) - key.EncryptedKey = encryptedData - - got, err := key.Decrypt() - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(got).To(Equal(data)) - - key.EncryptedKey = "absolute invalid" - got, err = key.Decrypt() - g.Expect(err).To(HaveOccurred()) - g.Expect(err.Error()).To(ContainSubstring("gpg: no valid OpenPGP data found")) - g.Expect(got).To(BeNil()) -} - -func TestMasterKey_Decrypt_SOPS_Compat(t *testing.T) { - g := NewWithT(t) - - gnuPGHome, err := NewGnuPGHome() - g.Expect(err).ToNot(HaveOccurred()) - t.Cleanup(func() { - _ = os.RemoveAll(gnuPGHome.String()) - }) - g.Expect(gnuPGHome.ImportFile(mockPrivateKey)).To(Succeed()) - - dataKey := []byte("foo") - - t.Setenv("GNUPGHOME", gnuPGHome.String()) - encryptKey := pgp.NewMasterKeyFromFingerprint(mockFingerprint) - g.Expect(encryptKey.Encrypt(dataKey)).To(Succeed()) - - decryptKey := MasterKeyFromFingerprint(mockFingerprint) - gnuPGHome.ApplyToMasterKey(decryptKey) - decryptKey.EncryptedKey = encryptKey.EncryptedKey - - dec, err := decryptKey.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(dec).To(Equal(dataKey)) -} - -func TestMasterKey_EncryptDecrypt_RoundTrip(t *testing.T) { - g := NewWithT(t) - - gnuPGHome, err := NewGnuPGHome() - g.Expect(err).ToNot(HaveOccurred()) - t.Cleanup(func() { - _ = os.RemoveAll(gnuPGHome.String()) - }) - g.Expect(gnuPGHome.ImportFile(mockPrivateKey)).To(Succeed()) - - key := MasterKeyFromFingerprint(mockFingerprint) - gnuPGHome.ApplyToMasterKey(key) - - data := []byte("some secret data") - g.Expect(key.Encrypt(data)).To(Succeed()) - g.Expect(key.EncryptedKey).ToNot(BeEmpty()) - - decryptedData, err := key.Decrypt() - g.Expect(err).ToNot(HaveOccurred()) - g.Expect(decryptedData).To(Equal(data)) -} - -func TestMasterKey_NeedsRotation(t *testing.T) { - g := NewWithT(t) - - key := MasterKeyFromFingerprint("") - g.Expect(key.NeedsRotation()).To(BeFalse()) - - key.CreationDate = key.CreationDate.Add(-(pgpTTL + time.Second)) - g.Expect(key.NeedsRotation()).To(BeTrue()) -} - -func TestMasterKey_ToString(t *testing.T) { - g := NewWithT(t) - - key := MasterKeyFromFingerprint(mockFingerprint) - g.Expect(key.ToString()).To(Equal(mockFingerprint)) -} - -func TestMasterKey_ToMap(t *testing.T) { - g := NewWithT(t) - - key := MasterKeyFromFingerprint(mockFingerprint) - key.EncryptedKey = "data" - g.Expect(key.ToMap()).To(Equal(map[string]interface{}{ - "fp": mockFingerprint, - "created_at": key.CreationDate.UTC().Format(time.RFC3339), - "enc": key.EncryptedKey, - })) -} - -func TestMasterKey_gnuPGHome(t *testing.T) { - g := NewWithT(t) - - key := &MasterKey{} - - usr, err := user.Current() - if err == nil { - g.Expect(key.gnuPGHome()).To(Equal(filepath.Join(usr.HomeDir, ".gnupg"))) - } else { - g.Expect(key.gnuPGHome()).To(Equal(filepath.Join(os.Getenv("HOME"), ".gnupg"))) - } - - gnupgHome := "/overwrite/home" - t.Setenv("GNUPGHOME", gnupgHome) - g.Expect(key.gnuPGHome()).To(Equal(gnupgHome)) - - key.gnuPGHomeDir = "/home/dir/overwrite" - g.Expect(key.gnuPGHome()).To(Equal(key.gnuPGHomeDir)) -} - -func Test_gpgBinary(t *testing.T) { - g := NewWithT(t) - - g.Expect(gpgBinary()).To(Equal("gpg")) - - overwrite := "/some/other/gpg" - t.Setenv(SopsGpgExecEnv, overwrite) - g.Expect(gpgBinary()).To(Equal(overwrite)) -} - -func Test_shortenFingerprint(t *testing.T) { - g := NewWithT(t) - - shortId := shortenFingerprint(mockFingerprint) - g.Expect(shortId).To(Equal("9732075EA221A7EA")) - - g.Expect(shortenFingerprint(shortId)).To(Equal(shortId)) -} - -func Fuzz_Pgp(f *testing.F) { - f.Fuzz(func(t *testing.T, seed, data []byte) { - fc := fuzz.NewConsumer(data) - masterKey := MasterKey{} - - if err := fc.GenerateStruct(&masterKey); err != nil { - return - } - - _ = masterKey.Encrypt(data) - _ = masterKey.EncryptIfNeeded(data) - }) -} diff --git a/internal/decryptors/sops/kustomize-controller/pgp/testdata/private.gpg b/internal/decryptors/sops/kustomize-controller/pgp/testdata/private.gpg deleted file mode 100644 index f3ca23e..0000000 --- a/internal/decryptors/sops/kustomize-controller/pgp/testdata/private.gpg +++ /dev/null @@ -1,81 +0,0 @@ ------BEGIN PGP PRIVATE KEY BLOCK----- - -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 -sgS+XwT2h+fdCBJHJYmboxXpxC02xHY= -=G8JF ------END PGP PRIVATE KEY BLOCK----- diff --git a/internal/decryptors/sops/kustomize-controller/pgp/testdata/public.gpg b/internal/decryptors/sops/kustomize-controller/pgp/testdata/public.gpg deleted file mode 100644 index c59ba01..0000000 --- a/internal/decryptors/sops/kustomize-controller/pgp/testdata/public.gpg +++ /dev/null @@ -1,41 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- - -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 -dg== -=05GI ------END PGP PUBLIC KEY BLOCK----- diff --git a/internal/decryptors/sops/sops.go b/internal/decryptors/sops/sops.go deleted file mode 100644 index f3f137b..0000000 --- a/internal/decryptors/sops/sops.go +++ /dev/null @@ -1,397 +0,0 @@ -package sops - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "os" - "path/filepath" - "sort" - "strings" - "sync" - "time" - - "github.com/bedag/subst/internal/decryptors" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/age" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/awskms" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/azkv" - intkeyservice "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/keyservice" - "github.com/bedag/subst/internal/decryptors/sops/kustomize-controller/pgp" - "go.mozilla.org/sops/v3" - "go.mozilla.org/sops/v3/aes" - "go.mozilla.org/sops/v3/cmd/sops/common" - "go.mozilla.org/sops/v3/cmd/sops/formats" - "go.mozilla.org/sops/v3/keyservice" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" -) - -const ( - // DecryptionProviderSOPS is the SOPS provider name. - DecryptionProviderSOPS = "sops" - // DecryptionPGPExt is the extension of the file containing an armored PGP - // key. - DecryptionPGPExt = ".asc" - // DecryptionAgeExt is the extension of the file containing an age key - // file. - DecryptionAgeExt = ".agekey" - // DecryptionVaultTokenFileName is the name of the file containing the - // Hashicorp Vault token. - DecryptionVaultTokenFileName = "sops.vault-token" - // DecryptionAWSKmsFile is the name of the file containing the AWS KMS - // credentials. - DecryptionAWSKmsFile = "sops.aws-kms" - // DecryptionAzureAuthFile is the name of the file containing the Azure - // credentials. - DecryptionAzureAuthFile = "sops.azure-kv" - // DecryptionGCPCredsFile is the name of the file containing the GCP - // credentials. - DecryptionGCPCredsFile = "sops.gcp-kms" - // maxEncryptedFileSize is the max allowed file size in bytes of an encrypted - // file. - maxEncryptedFileSize int64 = 5 << 20 - // unsupportedFormat is used to signal no sopsFormatToMarkerBytes format was - // detected by detectFormatFromMarkerBytes. - unsupportedFormat = formats.Format(-1) -) - -var ( - // sopsFormatToString is the counterpart to - // https://github.com/mozilla/sops/blob/v3.7.2/cmd/sops/formats/formats.go#L16 - sopsFormatToString = map[formats.Format]string{ - formats.Binary: "binary", - formats.Dotenv: "dotenv", - formats.Ini: "INI", - formats.Json: "JSON", - formats.Yaml: "YAML", - } - // sopsFormatToMarkerBytes contains a list of formats and their byte - // order markers, used to detect if a Secret data field is SOPS' encrypted. - sopsFormatToMarkerBytes = map[formats.Format][]byte{ - // formats.Binary is a JSON envelop at encrypted rest - formats.Binary: []byte("\"mac\": \"ENC["), - formats.Dotenv: []byte("sops_mac=ENC["), - formats.Ini: []byte("[sops]"), - formats.Json: []byte("\"mac\": \"ENC["), - formats.Yaml: []byte("mac: ENC["), - } -) - -// Decryptor performs decryption operations for a v1.Kustomization. -// The only supported decryption provider at present is -// DecryptionProviderSOPS. -type SOPSDecryptor struct { - // maxFileSize is the max size in bytes a file is allowed to have to be - // decrypted. Defaults to maxEncryptedFileSize. - maxFileSize int64 - // checkSopsMac instructs the decryptor to perform the SOPS data integrity - // check using the MAC. Not enabled by default, as arbitrary data gets - // injected into most resources, causing the integrity check to fail. - // Mostly kept around for feature completeness and documentation purposes. - checkSopsMac bool - - // gnuPGHome is the absolute path of the GnuPG home directory used to - // decrypt PGP data. When empty, the systems' GnuPG keyring is used. - // When set, ImportKeys() imports found PGP keys into this keyring. - gnuPGHome pgp.GnuPGHome - // ageIdentities is the set of age identities available to the decryptor. - ageIdentities age.ParsedIdentities - // vaultToken is the Hashicorp Vault token used to authenticate towards - // any Vault server. - vaultToken string - // awsCredsProvider is the AWS credentials provider object used to authenticate - // towards any AWS KMS. - awsCredsProvider *awskms.CredsProvider - // azureToken is the Azure credential token used to authenticate towards - // any Azure Key Vault. - azureToken *azkv.Token - // gcpCredsJSON is the JSON credential file of the service account used to - // authenticate towards any GCP KMS. - gcpCredsJSON []byte - - // keyServices are the SOPS keyservice.KeyServiceClient's available to the - // decryptor. - keyServices []keyservice.KeyServiceClient - localServiceOnce sync.Once - - // Interface decryptor config - Config decryptors.DecryptorConfig -} - -// NewDecryptor creates a new Decryptor for the given kustomization. -// gnuPGHome can be empty, in which case the systems' keyring is used. -func NewSOPSDecryptor(config decryptors.DecryptorConfig, gnuPGHome string) *SOPSDecryptor { - return &SOPSDecryptor{ - maxFileSize: maxEncryptedFileSize, - gnuPGHome: pgp.GnuPGHome(gnuPGHome), - Config: config, - } -} - -// NewTempDecryptor creates a new Decryptor, with a temporary GnuPG -// home directory to Decryptor.ImportKeys() into. -func NewSOPSTempDecryptor(config decryptors.DecryptorConfig) (*SOPSDecryptor, func(), error) { - gnuPGHome, err := pgp.NewGnuPGHome() - if err != nil { - return nil, nil, fmt.Errorf("cannot create keyring: %w", err) - } - cleanup := func() { _ = os.RemoveAll(gnuPGHome.String()) } - return NewSOPSDecryptor(config, gnuPGHome.String()), cleanup, nil -} - -// Only call this for Temporary Decryptors -func (d *SOPSDecryptor) RemoveKeyRing() error { - return os.RemoveAll(string(d.gnuPGHome)) -} - -// IsEncrypted returns true if the given data is encrypted by SOPS. -func (d *SOPSDecryptor) IsEncrypted(data []byte) (bool, error) { - if len(data) == 0 { - return false, nil - } - - jdata, err := decryptors.UnmarshalJSONorYAML(data) - if err != nil { - return false, err - } - - sopsField := jdata["sops"] - if sopsField == nil || sopsField == "" { - return false, nil - } - return true, nil -} - -// Read reads the input data, decrypts it, and returns the decrypted data. -func (d *SOPSDecryptor) Decrypt(data []byte) (content map[string]interface{}, err error) { - - content, err = decryptors.UnmarshalJSONorYAML(data) - if err != nil { - return nil, err - } - - if !d.Config.SkipDecrypt { - jcontent, err := json.Marshal(content) - if err != nil { - return nil, err - } - - data, err = d.SopsDecryptWithFormat(jcontent, formats.Json, formats.Json) - if err != nil { - return nil, err - } - - content, err = decryptors.UnmarshalJSONorYAML(data) - if err != nil { - return nil, err - } - } - - delete(content, "sops") - return content, nil -} - -// AddGPGKey adds given GPG key to the decryptor's keyring. -func (d *SOPSDecryptor) AddGPGKey(key []byte) error { - return d.gnuPGHome.Import(key) -} - -// AddAgeKey to the decryptor's identities. -func (d *SOPSDecryptor) AddAgeKey(key []byte) error { - return d.ageIdentities.Import(string(key)) -} - -// SetVaultToken sets the Vault token for the decryptor. -func (d *SOPSDecryptor) SetVaultToken(token []byte) { - vtoken := string(token) - vtoken = strings.Trim(strings.TrimSpace(vtoken), "\n") - d.vaultToken = vtoken -} - -// SetAWSCredentials adds AWS credentials for the decryptor. -// Reference: https://github.com/getsops/sops#aws-kms-encryption-context -func (d *SOPSDecryptor) SetAWSCredentials(token []byte) (err error) { - d.awsCredsProvider, err = awskms.LoadCredsProviderFromYaml(token) - return err -} - -// SetAzureAuthFile adds AWS credentials for the decryptor. -func (d *SOPSDecryptor) SetAzureCredentials(config []byte) (err error) { - conf := azkv.AADConfig{} - if err = azkv.LoadAADConfigFromBytes(config, &conf); err != nil { - return err - } - if d.azureToken, err = azkv.TokenFromAADConfig(conf); err != nil { - return err - } - - return nil -} - -// SetGCPCredentials adds GCP credentials for the decryptor. -func (d *SOPSDecryptor) SetGCPCredentials(config []byte) { - d.gcpCredsJSON = bytes.Trim(config, "\n") -} - -func (d *SOPSDecryptor) KeysFromSecret(secretName string, namespace string, client *kubernetes.Clientset, ctx context.Context) (err error) { - // Retrieve Secret - keySecret, err := client.CoreV1().Secrets(namespace).Get(ctx, secretName, metav1.GetOptions{}) - if k8serrors.IsNotFound(err) { - return &decryptors.MissingKubernetesSecret{Secret: secretName, Namespace: namespace} - } else if err != nil { - return err - } - - // Exract all keys from secret - for name, value := range keySecret.Data { - switch filepath.Ext(name) { - case DecryptionPGPExt: - if err = d.AddGPGKey(value); err != nil { - return fmt.Errorf("failed to import data from %s decryption Secret '%s': %w", name, secretName, err) - } - case DecryptionAgeExt: - if err = d.AddAgeKey(value); err != nil { - return fmt.Errorf("failed to import data from %s decryption Secret '%s': %w", name, secretName, err) - } - case filepath.Ext(DecryptionVaultTokenFileName): - // Make sure we have the absolute name - if name == DecryptionVaultTokenFileName { - d.SetVaultToken(value) - } - case filepath.Ext(DecryptionAWSKmsFile): - if name == DecryptionAWSKmsFile { - if d.SetAWSCredentials(value); err != nil { - return fmt.Errorf("failed to import data from %s decryption Secret '%s': %w", name, secretName, err) - } - } - case filepath.Ext(DecryptionAzureAuthFile): - if name == DecryptionAzureAuthFile { - if err = d.SetAzureCredentials(value); err != nil { - return fmt.Errorf("failed to import data from %s decryption Secret '%s': %w", name, secretName, err) - } - } - case filepath.Ext(DecryptionGCPCredsFile): - if name == DecryptionGCPCredsFile { - d.SetGCPCredentials(value) - } - } - } - - return nil -} - -// SopsDecryptWithFormat attempts to load a SOPS encrypted file using the store -// for the input format, gathers the data key for it from the key service, -// and then decrypts the file data with the retrieved data key. -// It returns the decrypted bytes in the provided output format, or an error. -func (d *SOPSDecryptor) SopsDecryptWithFormat(data []byte, inputFormat, outputFormat formats.Format) (_ []byte, err error) { - defer func() { - // It was discovered that malicious input and/or output instructions can - // make SOPS panic. Recover from this panic and return as an error. - if r := recover(); r != nil { - err = fmt.Errorf("failed to emit encrypted %s file as decrypted %s: %v", - sopsFormatToString[inputFormat], sopsFormatToString[outputFormat], r) - } - }() - - store := common.StoreForFormat(inputFormat) - - tree, err := store.LoadEncryptedFile(data) - if err != nil { - return nil, sopsUserErr(fmt.Sprintf("failed to load encrypted %s data", sopsFormatToString[inputFormat]), err) - } - - for _, group := range tree.Metadata.KeyGroups { - // Sort MasterKeys in the group so offline ones are tried first - sort.SliceStable(group, func(i, j int) bool { - return intkeyservice.IsOfflineMethod(group[i]) && !intkeyservice.IsOfflineMethod(group[j]) - }) - } - - metadataKey, err := tree.Metadata.GetDataKeyWithKeyServices(d.keyServiceServer()) - if err != nil { - return nil, sopsUserErr("cannot get sops data key", err) - } - - cipher := aes.NewCipher() - mac, err := tree.Decrypt(metadataKey, cipher) - if err != nil { - return nil, sopsUserErr("error decrypting sops tree", err) - } - - if d.checkSopsMac { - // Compute the hash of the cleartext tree and compare it with - // the one that was stored in the document. If they match, - // integrity was preserved - // Ref: go.mozilla.org/sops/v3/decrypt/decrypt.go - originalMac, err := cipher.Decrypt( - tree.Metadata.MessageAuthenticationCode, - metadataKey, - tree.Metadata.LastModified.Format(time.RFC3339), - ) - if err != nil { - return nil, sopsUserErr("failed to verify sops data integrity", err) - } - if originalMac != mac { - // If the file has an empty MAC, display "no MAC" - if originalMac == "" { - originalMac = "no MAC" - } - return nil, fmt.Errorf("failed to verify sops data integrity: expected mac '%s', got '%s'", originalMac, mac) - } - } - - outputStore := common.StoreForFormat(outputFormat) - out, err := outputStore.EmitPlainFile(tree.Branches) - if err != nil { - return nil, sopsUserErr(fmt.Sprintf("failed to emit encrypted %s file as decrypted %s", - sopsFormatToString[inputFormat], sopsFormatToString[outputFormat]), err) - } - return out, err -} - -// keyServiceServer returns the SOPS (local) key service clients used to serve -// decryption requests. loadKeyServiceServers() is only configured on the first -// call. -func (d *SOPSDecryptor) keyServiceServer() []keyservice.KeyServiceClient { - d.localServiceOnce.Do(func() { - d.loadKeyServiceServers() - }) - return d.keyServices -} - -// loadKeyServiceServers loads the SOPS (local) key service clients used to -// serve decryption requests for the current set of Decryptor -// credentials. -func (d *SOPSDecryptor) loadKeyServiceServers() { - serverOpts := []intkeyservice.ServerOption{ - intkeyservice.WithGnuPGHome(d.gnuPGHome), - intkeyservice.WithVaultToken(d.vaultToken), - intkeyservice.WithAgeIdentities(d.ageIdentities), - intkeyservice.WithGCPCredsJSON(d.gcpCredsJSON), - } - if d.azureToken != nil { - serverOpts = append(serverOpts, intkeyservice.WithAzureToken{Token: d.azureToken}) - } - serverOpts = append(serverOpts, intkeyservice.WithAWSKeys{CredsProvider: d.awsCredsProvider}) - server := intkeyservice.NewServer(serverOpts...) - d.keyServices = append(make([]keyservice.KeyServiceClient, 0), keyservice.NewCustomLocalClient(server)) -} - -func sopsUserErr(msg string, err error) error { - if userErr, ok := err.(sops.UserError); ok { - err = fmt.Errorf(userErr.UserError()) - } - return fmt.Errorf("%s: %w", msg, err) -} - -func detectFormatFromMarkerBytes(b []byte) formats.Format { - for k, v := range sopsFormatToMarkerBytes { - if bytes.Contains(b, v) { - return k - } - } - return unsupportedFormat -} diff --git a/internal/decryptors/sops/sops_test.go b/internal/decryptors/sops/sops_test.go deleted file mode 100644 index 3d54353..0000000 --- a/internal/decryptors/sops/sops_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package sops - -import ( - "os" - "path/filepath" - "testing" - - "github.com/bedag/subst/internal/decryptors" - "github.com/stretchr/testify/assert" -) - -func testdataPath() string { - basePath, _ := os.Getwd() - hackDirPath := filepath.Join(basePath, "testdata") - return hackDirPath -} - -func TestSOPSIsEncrypted(t *testing.T) { - d, _ := os.ReadFile(filepath.Join(testdataPath(), "secret-pgp.yaml")) - - decryptor := NewSOPSDecryptor(decryptors.DecryptorConfig{}, "") - isEncrypted, err := decryptor.IsEncrypted(d) - - assert.NoError(t, err, "Expected no error when checking if content is encrypted") - assert.True(t, isEncrypted, "Expected the content to be identified as encrypted") -} - -func TestSOPSIsNotEncrypted(t *testing.T) { - d, _ := os.ReadFile(filepath.Join(testdataPath(), "secret.yaml")) - - decryptor := NewSOPSDecryptor(decryptors.DecryptorConfig{}, "") - isEncrypted, err := decryptor.IsEncrypted(d) - - assert.NoError(t, err, "Expected no error when checking if content is encrypted") - assert.False(t, isEncrypted, "Expected the content to be identified as not encrypted") -} - -func TestSOPSGPGEncryption(t *testing.T) { - private, _ := os.ReadFile(filepath.Join(testdataPath(), "pgp.asc")) - d, _ := os.ReadFile(filepath.Join(testdataPath(), "secret-pgp.yaml")) - - decryptor, cleanup, err := NewSOPSTempDecryptor(decryptors.DecryptorConfig{}) - assert.NoError(t, err, "Expected no error when initializing decryptor") - defer cleanup() - - err = decryptor.AddGPGKey([]byte(private)) - assert.NoError(t, err, "Expected no error when adding GPG Key") - - enc, err := decryptor.Decrypt(d) - assert.NoError(t, err, "Expected no error when decrypting content") - - sData := enc["stringData"].(map[string]interface{}) - assert.Equal(t, "VERY_SECRET", sData["database_password"], "Expected the database_password to be decrypted") - assert.Equal(t, "MUCH_SECURE", sData["database_user"], "Expected the database_user to be decrypted") -} - -func TestSOPSAgeEncryption(t *testing.T) { - private, _ := os.ReadFile(filepath.Join(testdataPath(), "age.agekey")) - d, _ := os.ReadFile(filepath.Join(testdataPath(), "secret-age.yaml")) - - decryptor, cleanup, err := NewSOPSTempDecryptor(decryptors.DecryptorConfig{}) - assert.NoError(t, err, "Expected no error when initializing decryptor") - defer cleanup() - - err = decryptor.AddAgeKey([]byte(private)) - assert.NoError(t, err, "Expected no error when adding Age Key") - - enc, err := decryptor.Decrypt(d) - assert.NoError(t, err, "Expected no error when decrypting content") - - sData := enc["stringData"].(map[string]interface{}) - assert.Equal(t, "VERY_SECRET", sData["database_password"], "Expected the database_password to be decrypted") - assert.Equal(t, "MUCH_SECURE", sData["database_user"], "Expected the database_user to be decrypted") -} diff --git a/internal/decryptors/sops/testdata/.sops.pub.asc b/internal/decryptors/sops/testdata/.sops.pub.asc deleted file mode 100644 index b612848..0000000 --- a/internal/decryptors/sops/testdata/.sops.pub.asc +++ /dev/null @@ -1,64 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- - -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 -c73ZHZESEa+qNjfYk7pCGr3vew== -=N/nc ------END PGP PUBLIC KEY BLOCK----- - diff --git a/internal/decryptors/sops/testdata/.sops.yaml b/internal/decryptors/sops/testdata/.sops.yaml deleted file mode 100644 index 806e003..0000000 --- a/internal/decryptors/sops/testdata/.sops.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# creation rules are evaluated sequentially, the first match wins -creation_rules: - # files using age - - path_regex: \-age.yaml$ - encrypted_regex: ^(data|stringData)$ - age: age1p0wmaw5vk8f00753t3frs4rev0du4vqdkz7sx53ml98lrcsrnuqqwwp4tl - # fallback to PGP - - encrypted_regex: ^(data|stringData)$ - pgp: B01102D81246867C4BC24D863E7286BEE865E3C4 \ No newline at end of file diff --git a/internal/decryptors/sops/testdata/age.agekey b/internal/decryptors/sops/testdata/age.agekey deleted file mode 100644 index 8f54bea..0000000 --- a/internal/decryptors/sops/testdata/age.agekey +++ /dev/null @@ -1,3 +0,0 @@ -# created: 2023-08-09T15:30:46+02:00 -# public key: age1p0wmaw5vk8f00753t3frs4rev0du4vqdkz7sx53ml98lrcsrnuqqwwp4tl -AGE-SECRET-KEY-1JZFAV45XK9RFDCHD7JG5R5T5R68SY7GTGVLQ9KSZRTLV8K6JFFJQMY6LCY diff --git a/internal/decryptors/sops/testdata/pgp.asc b/internal/decryptors/sops/testdata/pgp.asc deleted file mode 100644 index d1d272c..0000000 --- a/internal/decryptors/sops/testdata/pgp.asc +++ /dev/null @@ -1,180 +0,0 @@ ------BEGIN PGP PRIVATE KEY BLOCK----- - -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 -N9iTukIave97 -=wD+k ------END PGP PRIVATE KEY BLOCK----- ------BEGIN PGP PUBLIC KEY BLOCK----- - -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 -c73ZHZESEa+qNjfYk7pCGr3vew== -=N/nc ------END PGP PUBLIC KEY BLOCK----- diff --git a/internal/decryptors/sops/testdata/secret-age.yaml b/internal/decryptors/sops/testdata/secret-age.yaml deleted file mode 100644 index c11d750..0000000 --- a/internal/decryptors/sops/testdata/secret-age.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: sops-age -stringData: - database_password: ENC[AES256_GCM,data:I9QUte/BtTaii6o=,iv:vaSxFo7yqfpOvWGUIfM8Aj+B25de7dYyCSF9dXeKOiU=,tag:deIwuIktYEKn2ufNKRSYyw==,type:str] - database_user: ENC[AES256_GCM,data:SOpbzI0uFndBExA=,iv:icQb5nb8KnjK6dnOdxvLQDIQHycY67y3oQMmT9dSjOc=,tag:fj+4a2T5YtCDIy/yoFaZkw==,type:str] -sops: - kms: [] - gcp_kms: [] - azure_kv: [] - hc_vault: [] - age: - - recipient: age1p0wmaw5vk8f00753t3frs4rev0du4vqdkz7sx53ml98lrcsrnuqqwwp4tl - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvMW9LZmtvWUFXaFVQZkFq - ZHFwV2oyYWhHeWIxMkM2ZkRWcSt4b2FpQmt3CmVYY3N6c002MTJWSHpBbDVuVW5B - UlVQcGg2UG1uSFJGOEt5QWVPUjRLUVEKLS0tIEpwamNScUdlY2NqemFlOGJ3Sm53 - YnVzaEVFL1JqMlpBc3NxQmtBRVlGZmsKVJ7n7Lgk4dhWu3FK33gxbldVhcMegKpC - i/2y+Ukg12cu1LpeU4GUqpIy96LzPYnfwjR15S5JhVRGs49b0hfJmw== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2023-08-09T13:31:49Z" - mac: ENC[AES256_GCM,data:XL5SwWzi0gYXSkFSWupvKYoXvyhrtyYELgy6hSqeDzrGO80XCgPY8+WOktlPu1d1FUb/F2Ez0RPoWROpyalVk3IkxADenCZ1JopTN8HZfMaBM6YxFqrAZ0mpqXVjK4LqxT+4kFojYChoU+RVEBexuhUFuQjFfQSFJ9bHo+WhlrU=,iv:bsxugaX/iczLKF6d5aDfztlO3iOmfWjBl00gRP33VsE=,tag:hL8lN0wXqG+95Yc+DaHRSg==,type:str] - pgp: [] - encrypted_regex: ^(data|stringData)$ - version: 3.7.2 diff --git a/internal/decryptors/sops/testdata/secret-pgp.yaml b/internal/decryptors/sops/testdata/secret-pgp.yaml deleted file mode 100644 index c95e53e..0000000 --- a/internal/decryptors/sops/testdata/secret-pgp.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: sops-pgp -stringData: - database_password: ENC[AES256_GCM,data:xap6r9CPuHN3T/c=,iv:oZ8gcWTlKWRBjW85h4ugCdTmzG4Ak1WsPAH69O6DYQs=,tag:kJYNlYCGvQX9aZYMq+0IRg==,type:str] - database_user: ENC[AES256_GCM,data:Wx5woeKL4Zs1EIM=,iv:fE7GCyPRUvMHbUtLDPSQqMn297Cls6LKVgnRnC2+2dA=,tag:NfsPXTXBAi5WClj4wW3Jyg==,type:str] -sops: - kms: [] - gcp_kms: [] - azure_kv: [] - hc_vault: [] - age: [] - lastmodified: "2023-08-09T13:29:15Z" - mac: ENC[AES256_GCM,data:CXNyGQZ74EFPYgTPzPfplyDjTXksqbShtli3y+gC/0FYaCi2nngjIj6nV5ibcnuHpCwjnJvkCp9vzfhamZn8LfO5qznLCZifZ2Wf9rjzmVPCVLNlxqyz50NKeRWveWoNqr/G1Z1rMadjARfCWtf1atFJyTx+XSDmR7dMh/qwPqY=,iv:kDelUNjBkTbyvSDwYKNhKAbJCNhTNxzQiPDUGfbKzKI=,tag:s4mXaSWpH3xqAgYcjJPULQ==,type:str] - pgp: - - created_at: "2023-08-09T13:29:14Z" - enc: | - -----BEGIN PGP MESSAGE----- - - hQIMA0RuT4C0qyryAQ//flqFS2clVtcKTxbzn5Ravj7v2/R/RY62HmBmxBoRVELS - OuFNqhxGsa9efrDgdQQKSsOxsEXTeTcZuJRGZvHHBaWZsWfvPN1Tfcm7RGTI6eIy - Qlb9r4X5D47oQdkN8LnK7JgV93ZIN4wLc2CnRFjSRz2bam3sImhhckBd6KZgQk9g - Zhf/+cGxp+sSM1MnHobVHabtLwEnks3stdJc/A5Gmo9W5NkGihGlfhNtaKOFXT8r - cmi/uOvEU6tDH61eIbmwECYF4m2VrI52ukyavPloo26VmpM7lCYB5I+RCWT+MsIk - 5U+iws4Ud/xbQ1sGMw2riPofde5rOKAzMAeJQl7ZHbUCgwjjVnlAMVw17Zr2ohEc - 6ZR2BbbJYZEZTsAWXstnyTZvTINRBXAg48Eh+Pp1bj0hAIV9ek7ffFeGSijw833C - EjAxgtiAZ7bs8o4PyUUa+QqsCvzDo8I6Z1xdTkmGBN12+jHUYNlsFy1iYlSWgZmC - CLkHKUKD8gWbNGRriuvSve6ZYbVt2u53llxJJaTYHdNAloQ8cX5BvI2Lbplyej+W - fCGdGq/ZA6xe8WhwE6X2kGD4GMGk5yrCvFeZRoemwiUAC6vrixVrhIPfexzFp35X - RqIR8hWRX6UIEMfig7egnBRzQ5GG5GymdYnfibAgZGqdIE6jDjQTtY4TogcLRHLU - aAEJAhAwoOvLNjNtH7jhvAmH683O0Qy21oaUMZSWz0NQZL6aeWn+F84iYtQcQ/eL - KR1ndRrGgI9BU5PEPudWgIAWY6EZN+ujazRdM4hp/ZPJhi71gIDW3HDNxK+/3oJ1 - vTjeGkz5RIOy - =BHWN - -----END PGP MESSAGE----- - fp: B01102D81246867C4BC24D863E7286BEE865E3C4 - encrypted_regex: ^(data|stringData)$ - version: 3.7.2 diff --git a/internal/decryptors/sops/testdata/secret.yaml b/internal/decryptors/sops/testdata/secret.yaml deleted file mode 100644 index 8866b74..0000000 --- a/internal/decryptors/sops/testdata/secret.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: secret -stringData: - database_password: "VERY_SECRET" - database_user: "MUCH_SECURE" \ No newline at end of file diff --git a/internal/utils/conversion.go b/internal/utils/conversion.go index dce6ac9..1f48cbf 100644 --- a/internal/utils/conversion.go +++ b/internal/utils/conversion.go @@ -13,7 +13,7 @@ import ( // Convert converts a map[string]interface{} to a map[interface{}]interface{}. func ToInterface(inputMap map[string]interface{}) map[interface{}]interface{} { - var convertedMap = make(map[interface{}]interface{}) + var convertedMap = make(map[interface{}]interface{}, len(inputMap)) for key, value := range inputMap { convertedMap[key] = value } @@ -28,7 +28,7 @@ func ToMap(i map[interface{}]interface{}) map[string]interface{} { // convert map[string]interface{} to map[string]string (Recursion) func mapify(input map[interface{}]interface{}) map[string]interface{} { - output := make(map[string]interface{}) + output := make(map[string]interface{}, len(input)) for k, v := range input { switch vv := v.(type) { case map[interface{}]interface{}: diff --git a/pkg/config/config.go b/pkg/config/config.go index 8f4bdfc..fa48e49 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -7,7 +7,7 @@ import ( "regexp" "time" - "github.com/sirupsen/logrus" + "github.com/rs/zerolog/log" flag "github.com/spf13/pflag" "github.com/spf13/cobra" @@ -74,7 +74,7 @@ func LoadConfiguration(cfgFile string, cmd *cobra.Command, directory string) (*C return nil, fmt.Errorf("secret-namespace must be set when --secret-name is set") } - logrus.Debugf("Configuration: %+v\n", cfg) + log.Debug().Msgf("Configuration: %+v\n", cfg) return cfg, nil } diff --git a/pkg/subst/build.go b/pkg/subst/build.go index 59deea5..af6b7b8 100644 --- a/pkg/subst/build.go +++ b/pkg/subst/build.go @@ -3,14 +3,14 @@ package subst import ( "context" "fmt" + "sync" decrypt "github.com/bedag/subst/internal/decryptors" ejson "github.com/bedag/subst/internal/decryptors/ejson" - sops "github.com/bedag/subst/internal/decryptors/sops" "github.com/bedag/subst/internal/kustomize" "github.com/bedag/subst/internal/utils" "github.com/bedag/subst/pkg/config" - "github.com/sirupsen/logrus" + "github.com/rs/zerolog/log" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" ) @@ -71,7 +71,7 @@ func (b *Build) BuildSubstitutions() (err error) { func (b *Build) Build() (err error) { if b.Substitutions == nil { - logrus.Debug("no resources to build") + log.Debug().Msg("no resources to build") return nil } @@ -87,43 +87,60 @@ func (b *Build) Build() (err error) { }() // Run Build - logrus.Debug("substitute manifests") + log.Debug().Msg("substitute manifests") + + var wg sync.WaitGroup + manifestsMutex := sync.Mutex{} for _, manifest := range b.Substitutions.Resources.Resources() { - var c map[interface{}]interface{} + wg.Add(1) + go func() { + defer wg.Done() - mBytes, _ := manifest.MarshalJSON() - for _, d := range decryptors { - isEncrypted, err := d.IsEncrypted(mBytes) - if err != nil { - logrus.Errorf("Error checking encryption for %s: %s", mBytes, err) - continue - } - if isEncrypted { - dm, err := d.Decrypt(mBytes) + var c map[interface{}]interface{} + + mBytes, _ := manifest.MarshalJSON() + // should not check every file if its encrypted + // already decrypted in substiqutions.go? + for _, d := range decryptors { + isEncrypted, err := d.IsEncrypted(mBytes) if err != nil { - return fmt.Errorf("failed to decrypt %s: %s", mBytes, err) + log.Error().Msgf("Error checking encryption for %s: %s", mBytes, err) + continue + } + if isEncrypted { + dm, err := d.Decrypt(mBytes) + if err != nil { + log.Error().Msgf("failed to decrypt %s: %s", mBytes, err) + return + } + c = utils.ToInterface(dm) + break } - c = utils.ToInterface(dm) - break } - } - if c == nil { - m, _ := manifest.AsYAML() + if c == nil { + m, _ := manifest.AsYAML() - c, err = utils.ParseYAML(m) - if err != nil { - return fmt.Errorf("UnmarshalJSON: %w", err) + c, err = utils.ParseYAML(m) + if err != nil { + log.Error().Msgf("UnmarshalJSON: %s", err) + return + } } - } - f, err := b.Substitutions.Eval(c, nil, false) - if err != nil { - return fmt.Errorf("spruce evaluation failed %s/%s: %s", manifest.GetNamespace(), manifest.GetName(), err) - } - b.Manifests = append(b.Manifests, f) + f, err := b.Substitutions.Eval(c, nil, false) + if err != nil { + log.Error().Msgf("spruce evaluation failed %s/%s: %s", manifest.GetNamespace(), manifest.GetName(), err) + return + } + manifestsMutex.Lock() + b.Manifests = append(b.Manifests, f) + manifestsMutex.Unlock() + }() } + wg.Wait() + return nil } @@ -139,14 +156,14 @@ func (b *Build) loadSubstitutions() (err error) { // Final attempt to evaluate eval, err := b.Substitutions.Eval(b.Substitutions.Subst, nil, false) if err != nil { - return fmt.Errorf("spruce evaluation failed") + return fmt.Errorf("spruce evaluation failed: %s", err) } b.Substitutions.Subst = eval if len(b.Substitutions.Subst) > 0 { - logrus.Debug("loaded substitutions: ", b.Substitutions.Subst) + log.Debug().Msgf("loaded substitutions: %+v", b.Substitutions.Subst) } else { - logrus.Debug("no substitutions found") + log.Debug().Msg("no substitutions found") } return nil @@ -165,17 +182,6 @@ func (b *Build) decryptors() (decryptors []decrypt.Decryptor, cleanups []func(), } decryptors = append(decryptors, ed) - if b.cfg.SopsTempKeyring { - sd, sopsCleanup, err := sops.NewSOPSTempDecryptor(c) - if err != nil { - return nil, nil, err - } - cleanups = append(cleanups, sopsCleanup) - decryptors = append(decryptors, sd) - } else { - decryptors = append(decryptors, sops.NewSOPSDecryptor(c, b.cfg.SopSKeyring)) - } - if b.cfg.SecretSkip { return } @@ -190,13 +196,13 @@ func (b *Build) decryptors() (decryptors []decrypt.Decryptor, cleanups []func(), if err == nil { b.kubeClient, err = kubernetes.NewForConfig(cfg) if err != nil { - logrus.Debug("could not load kubernetes client: %s", err) + log.Debug().Msgf("could not load kubernetes client: %s", err) } else { ctx := context.Background() for _, decr := range decryptors { err = decr.KeysFromSecret(b.cfg.SecretName, b.cfg.SecretNamespace, b.kubeClient, ctx) if err != nil { - logrus.Debug("failed to load secrets from Kubernetes: %s", err) + log.Debug().Msgf("failed to load secrets from Kubernetes: %s", err) } } diff --git a/pkg/subst/resources.go b/pkg/subst/resources.go index 7d7c2b3..b74d653 100644 --- a/pkg/subst/resources.go +++ b/pkg/subst/resources.go @@ -20,7 +20,7 @@ var ( func (s *Substitutions) addResource(in map[interface{}]interface{}) (err error) { // Create the resource fmt.Printf("resource: %v\n", in) - res := defaultResourceFactor.FromMap(utils.ToMap(in)) + res, err := defaultResourceFactor.FromMap(utils.ToMap(in)) if err != nil { log.Fatalf("Failed to create resource: %v", err) } diff --git a/pkg/subst/substitutions.go b/pkg/subst/substitutions.go index 7ad58b2..c8c8000 100644 --- a/pkg/subst/substitutions.go +++ b/pkg/subst/substitutions.go @@ -12,7 +12,7 @@ import ( "github.com/bedag/subst/internal/utils" "github.com/bedag/subst/internal/wrapper" "github.com/geofffranks/spruce" - "github.com/sirupsen/logrus" + "github.com/rs/zerolog/log" "sigs.k8s.io/kustomize/api/resmap" ) @@ -53,7 +53,7 @@ func NewSubstitutions(cfg SubstitutionsConfig, decrypts []decrypt.Decryptor, res if err != nil { return nil, err } - logrus.Debug("using regex: ", init.Config.SubstFileRegex) + log.Debug().Msgf("using regex: %s", init.Config.SubstFileRegex) } @@ -64,7 +64,7 @@ func NewSubstitutions(cfg SubstitutionsConfig, decrypts []decrypt.Decryptor, res if err != nil { return nil, err } - init.Add(utils.ToInterface(envs), true) + err = init.Add(utils.ToInterface(envs), true) if err != nil { return nil, err } @@ -139,7 +139,7 @@ func (s *Substitutions) Walk(path string, f fs.FileInfo) error { if matchingRegex.MatchString(f.Name()) { var c map[interface{}]interface{} - logrus.Debug("processing: ", full, "") + log.Debug().Msgf("processing: %s", full) file, err := utils.NewFile(full) if err != nil { return err @@ -159,7 +159,7 @@ func (s *Substitutions) Walk(path string, f fs.FileInfo) error { break } if isEncrypted { - logrus.Debugf("decrypted: %s", full) + log.Debug().Msgf("decrypted: %s", full) file.Byte() dm, err := d.Decrypt(file.Byte()) if err != nil { @@ -178,7 +178,7 @@ func (s *Substitutions) Walk(path string, f fs.FileInfo) error { } if c[resourcesField] != nil { - logrus.Debugf("detected resources in %s", full) + log.Debug().Msgf("detected resources in %s", full) err = s.addResources(c[resourcesField].([]interface{})) if err != nil { return fmt.Errorf("failed to add resources from %s: %s", full, err) @@ -191,7 +191,7 @@ func (s *Substitutions) Walk(path string, f fs.FileInfo) error { return fmt.Errorf("failed to merge %s: %s", full, err) } - logrus.Debug("loaded: ", full, "") + log.Debug().Msgf("loaded: %s", full) } return nil } diff --git a/subst/cmd/render.go b/subst/cmd/render.go index 2c5e579..fa4a2f3 100644 --- a/subst/cmd/render.go +++ b/subst/cmd/render.go @@ -8,7 +8,7 @@ import ( "github.com/bedag/subst/internal/utils" "github.com/bedag/subst/pkg/config" "github.com/bedag/subst/pkg/subst" - "github.com/sirupsen/logrus" + "github.com/rs/zerolog/log" "github.com/spf13/cobra" flag "github.com/spf13/pflag" ) @@ -92,15 +92,21 @@ func render(cmd *cobra.Command, args []string) error { if m.Manifests != nil { for _, f := range m.Manifests { if configuration.Output == "json" { - utils.PrintJSON(f) + err = utils.PrintJSON(f) + if err != nil { + log.Error().Msgf("failed to print JSON: %s", err) + } } else { - utils.PrintYAML(f) + err = utils.PrintYAML(f) + if err != nil { + log.Error().Msgf("failed to print JSON: %s", err) + } } } } } elapsed := time.Since(start) // Calculate elapsed time - logrus.Debug("Build time: ", elapsed) + log.Debug().Msgf("Build time: %s", elapsed) return nil } diff --git a/subst/cmd/root.go b/subst/cmd/root.go index 1aed233..9d38f44 100644 --- a/subst/cmd/root.go +++ b/subst/cmd/root.go @@ -2,11 +2,10 @@ package cmd import ( "fmt" - "io" "os" "path/filepath" - "github.com/sirupsen/logrus" + "github.com/rs/zerolog" flag "github.com/spf13/pflag" "github.com/MakeNowJust/heredoc" @@ -30,14 +29,14 @@ func NewRootCmd() *cobra.Command { //Here is where we define the PreRun func, using the verbose flag value //We use the standard output for logs. cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { - if err := setUpLogs(os.Stdout, v); err != nil { + if err := setUpLogs(v); err != nil { return err } return nil } //Default value is the warn level - cmd.PersistentFlags().StringVarP(&v, "verbosity", "v", logrus.WarnLevel.String(), "Log level (debug, info, warn, error, fatal, panic") + cmd.PersistentFlags().StringVarP(&v, "verbosity", "v", zerolog.WarnLevel.String(), "Log level (debug, info, warn, error, fatal, panic") cmd.AddCommand(newDiscoverCmd()) cmd.AddCommand(newVersionCmd()) @@ -60,19 +59,18 @@ func Execute() { } // setUpLogs set the log output ans the log level -func setUpLogs(out io.Writer, level string) error { - logrus.SetOutput(out) - lvl, err := logrus.ParseLevel(level) +func setUpLogs(level string) error { + lvl, err := zerolog.ParseLevel(level) if err != nil { return err } - logrus.SetLevel(lvl) + zerolog.SetGlobalLevel(lvl) return nil } func addCommonFlags(flags *flag.FlagSet) { flags.StringVar(&cfgFile, "config", "", "Config file") - flags.String("file-regex", "(.*subst\\.yaml|.*(ejson))", heredoc.Doc(` + flags.String("file-regex", "(subst\\.yaml|.*(ejson))", heredoc.Doc(` Regex Pattern to discover substitution files`)) flags.Bool("debug", false, heredoc.Doc(` Print CLI calls of external tools to stdout (caution: setting this may diff --git a/subst/cmd/substitutions.go b/subst/cmd/substitutions.go index 49ca161..d559577 100644 --- a/subst/cmd/substitutions.go +++ b/subst/cmd/substitutions.go @@ -7,6 +7,7 @@ import ( "github.com/bedag/subst/internal/utils" "github.com/bedag/subst/pkg/config" "github.com/bedag/subst/pkg/subst" + "github.com/rs/zerolog/log" "github.com/spf13/cobra" ) @@ -49,9 +50,15 @@ func substitutions(cmd *cobra.Command, args []string) error { if m != nil { if len(m.Substitutions.Subst) > 0 { if configuration.Output == "json" { - utils.PrintJSON(m.Substitutions.Subst) + err = utils.PrintJSON(m.Substitutions.Subst) + if err != nil { + log.Error().Msgf("failed to print JSON: %s", err) + } } else { - utils.PrintYAML(m.Substitutions.Subst) + err = utils.PrintYAML(m.Substitutions.Subst) + if err != nil { + log.Error().Msgf("failed to print JSON: %s", err) + } } } } diff --git a/subst/main.go b/subst/main.go index 1cce116..0781f1d 100644 --- a/subst/main.go +++ b/subst/main.go @@ -1,9 +1,21 @@ package main import ( + "github.com/KimMachineGun/automemlimit/memlimit" "github.com/bedag/subst/subst/cmd" + + _ "go.uber.org/automaxprocs" ) func main() { + memlimit.SetGoMemLimitWithOpts( + memlimit.WithRatio(0.9), + memlimit.WithProvider( + memlimit.ApplyFallback( + memlimit.FromCgroup, + memlimit.FromSystem, + ), + ), + ) cmd.Execute() }