Skip to content

Commit 9581c11

Browse files
authored
feat: added Yandex Cloud Lockbox backend provider (#705)
* feat: added Yandex Cloud Lockbox backend provider Signed-off-by: ysignat <ignatovegors@gmail.com>
1 parent 0a765c5 commit 9581c11

File tree

5 files changed

+145
-0
lines changed

5 files changed

+145
-0
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ It supports various backends including:
2222
- Conjur
2323
- HCP Vault Secrets
2424
- Bitwarden
25+
- [Yandex Cloud Lockbox](https://yandex.cloud/en/docs/lockbox/)
2526
- HTTP JSON
2627
- Keychain
2728

@@ -246,6 +247,8 @@ Please see the [relevant unit test cases](https://github.com/helmfile/vals/blob/
246247
- [Conjur](#conjur)
247248
- [HCP Vault Secrets](#hcp-vault-secrets)
248249
- [Bitwarden](#bitwarden)
250+
- [Yandex Cloud Lockbox](#yandex-cloud-lockbox)
251+
- [Authentication](#authentication-2)
249252
- [HTTP JSON](#http-json)
250253
- [Fetch string value](#fetch-string-value)
251254
- [Fetch integer value](#fetch-integer-value)
@@ -898,6 +901,23 @@ Examples:
898901
- `ref+bw://4d084b01-87e7-4411-8de9-2476ab9f3f48/{username,password,uri,notes,item}` gets username, password, uri, notes or the whole item of the given item id
899902
- `ref+bw://4d084b01-87e7-4411-8de9-2476ab9f3f48/notes#/key1` gets the *key1* from the yaml stored as note in the item
900903
904+
### Yandex Cloud Lockbox
905+
906+
Retrieve secrets from [Yandex Cloud Lockbox](https://yandex.cloud/en/docs/lockbox/). Path is used to specify secret ID. Optionally a specific secret version can be retrieved (using current version by default). If fragment is specified, retrieves a specific key from the secret.
907+
908+
- `ref+yclockbox://SECRET_ID[?version_id=VERSION][#KEY]`
909+
910+
Examples:
911+
912+
- `ref+yclockbox://e6qeoqvd88dcpf044n5i` - get whole secret `e6qeoqvd88dcpf044n5i` from the current version
913+
- `ref+yclockbox://e6qeoqvd88dcpf044n5i?version_id=e6qn22seoaprg9cbe1dj` - get whole secret `e6qeoqvd88dcpf044n5i` from the `e6qn22seoaprg9cbe1dj` version
914+
- `ref+yclockbox://e6qeoqvd88dcpf044n5i?version_id=e6qn22seoaprg9cbe1dj#oauth_secret` - get secret entry from the `oauth_secret` key of `e6qn22seoaprg9cbe1dj` version of `e6qeoqvd88dcpf044n5i` secret
915+
- `ref+yclockbox://e6qeoqvd88dcpf044n5i#oauth_secret` - get secret entry from the `oauth_secret` key of current version of `e6qeoqvd88dcpf044n5i` secret
916+
917+
#### Authentication
918+
919+
Vals aquires Yandex Cloud IAM token from the `YC_TOKEN` environment variable. The easiest way to get it is to run `yc iam create-token`. See [Yandex Cloud Lockbox docs](https://yandex.cloud/en/docs/lockbox/api-ref/authentication) for more details on authentication
920+
901921
### HTTP JSON
902922
903923
This provider retrieves values stored in JSON hosted by a HTTP frontend.

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ require (
2525
github.com/hashicorp/vault/api v1.20.0
2626
github.com/stretchr/testify v1.10.0
2727
github.com/tidwall/gjson v1.18.0
28+
github.com/yandex-cloud/go-genproto v0.12.0
29+
github.com/yandex-cloud/go-sdk v0.11.0
2830
golang.org/x/oauth2 v0.30.0
2931
google.golang.org/api v0.246.0
3032
gopkg.in/yaml.v3 v3.0.1
@@ -53,6 +55,7 @@ require (
5355
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
5456
github.com/extism/go-sdk v1.7.0 // indirect
5557
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
58+
github.com/ghodss/yaml v1.0.0 // indirect
5659
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
5760
github.com/go-openapi/analysis v0.23.0 // indirect
5861
github.com/go-openapi/errors v0.22.1 // indirect

go.sum

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e h1:y/1nzrdF+RPds
223223
github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e/go.mod h1:awFzISqLJoZLm+i9QQ4SgMNHDqljH6jWV0B36V5MrUM=
224224
github.com/getsops/sops/v3 v3.10.2 h1:7t7lBXFcXJPsDMrpYoI36r8xIhjWUmEc8Qdjuwyo+WY=
225225
github.com/getsops/sops/v3 v3.10.2/go.mod h1:Dmtg1qKzFsAl+yqvMgjtnLGTC0l7RnSM6DDtFG7TEsk=
226+
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
227+
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
226228
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
227229
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
228230
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -383,6 +385,8 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2Em
383385
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
384386
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
385387
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
388+
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
389+
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
386390
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
387391
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
388392
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@@ -481,6 +485,10 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo
481485
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
482486
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
483487
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
488+
github.com/yandex-cloud/go-genproto v0.12.0 h1:jg8UhoVs0+XpZW9cttdyOTFsCSmhyB1Qsf962UW1/Qg=
489+
github.com/yandex-cloud/go-genproto v0.12.0/go.mod h1:0LDD/IZLIUIV4iPH+YcF+jysO3jkSvADFGm4dCAuwQo=
490+
github.com/yandex-cloud/go-sdk v0.11.0 h1:f79LZWX3PLVXSb8QIp7efCWkja69OuGu8hcnToWURFw=
491+
github.com/yandex-cloud/go-sdk v0.11.0/go.mod h1:n9XO+J+P1//Em9eWxOhulhU+6TZp1iCUfzVcmFn/g9U=
484492
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
485493
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
486494
github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s=
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package yclockbox
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"os"
8+
9+
"github.com/yandex-cloud/go-genproto/yandex/cloud/lockbox/v1"
10+
sdk "github.com/yandex-cloud/go-sdk"
11+
12+
"github.com/helmfile/vals/pkg/api"
13+
"github.com/helmfile/vals/pkg/log"
14+
)
15+
16+
const (
17+
TOKEN_ENV = "YC_TOKEN"
18+
)
19+
20+
// Format: ref+yclockbox://SECRET_ID[?version_id=VERSION_ID][#KEY]
21+
type provider struct {
22+
logger *log.Logger
23+
client lockbox.PayloadServiceClient
24+
versionId string
25+
}
26+
27+
func New(l *log.Logger, cfg api.StaticConfig) *provider {
28+
token, ok := os.LookupEnv(TOKEN_ENV)
29+
if !ok {
30+
l.Debugf("yclockbox: Missing %s environment variable", TOKEN_ENV)
31+
}
32+
33+
sdk, err := sdk.Build(
34+
context.TODO(),
35+
sdk.Config{
36+
Credentials: sdk.NewIAMTokenCredentials(
37+
token,
38+
),
39+
},
40+
)
41+
42+
if err != nil {
43+
l.Debugf("yclockbox: SDK initialization error: %s", err)
44+
return nil
45+
}
46+
47+
p := &provider{
48+
logger: l,
49+
client: sdk.LockboxPayload().Payload(),
50+
}
51+
52+
if v := cfg.String("version_id"); cfg.Exists("version_id") {
53+
p.versionId = v
54+
}
55+
56+
return p
57+
}
58+
59+
func (p *provider) GetString(key string) (string, error) {
60+
if p == nil {
61+
return "", fmt.Errorf("yclockbox: provider is nil")
62+
}
63+
secret, err := p.GetStringMap(key)
64+
65+
if err != nil {
66+
p.logger.Debugf("yclockbox: get string failed: key=%s", key)
67+
return "", err
68+
}
69+
70+
res, err := json.Marshal(secret)
71+
72+
if err != nil {
73+
p.logger.Debugf("yclockbox: marshaling failed")
74+
return "", err
75+
}
76+
77+
return string(res), nil
78+
}
79+
80+
func (p *provider) GetStringMap(key string) (map[string]any, error) {
81+
if p == nil {
82+
return nil, fmt.Errorf("yclockbox: provider is nil")
83+
}
84+
secret, err := p.client.Get(
85+
context.Background(),
86+
&lockbox.GetPayloadRequest{
87+
SecretId: key,
88+
VersionId: p.versionId,
89+
},
90+
)
91+
if err != nil {
92+
p.logger.Debugf("yclockbox: %s", err)
93+
return nil, err
94+
}
95+
96+
res := map[string]interface{}{}
97+
98+
for _, entry := range secret.Entries {
99+
var value string
100+
if entry.GetTextValue() != "" {
101+
value = entry.GetTextValue()
102+
} else {
103+
value = string(entry.GetBinaryValue())
104+
}
105+
res[entry.GetKey()] = value
106+
}
107+
108+
return res, nil
109+
}

vals.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import (
4646
"github.com/helmfile/vals/pkg/providers/ssm"
4747
"github.com/helmfile/vals/pkg/providers/tfstate"
4848
"github.com/helmfile/vals/pkg/providers/vault"
49+
"github.com/helmfile/vals/pkg/providers/yclockbox"
4950
"github.com/helmfile/vals/pkg/stringmapprovider"
5051
"github.com/helmfile/vals/pkg/stringprovider"
5152
)
@@ -103,6 +104,7 @@ const (
103104
ProviderHCPVaultSecrets = "hcpvaultsecrets"
104105
ProviderHttpJsonManager = "httpjson"
105106
ProviderBitwarden = "bw"
107+
ProviderLockbox = "yclockbox"
106108
)
107109

108110
var (
@@ -279,6 +281,9 @@ func (r *Runtime) prepare() (*expansion.ExpandRegexMatch, error) {
279281
case ProviderBitwarden:
280282
p := bitwarden.New(r.logger, conf)
281283
return p, nil
284+
case ProviderLockbox:
285+
p := yclockbox.New(r.logger, conf)
286+
return p, nil
282287
}
283288
return nil, fmt.Errorf("no provider registered for scheme %q", scheme)
284289
}

0 commit comments

Comments
 (0)