Skip to content

Commit 1718ecc

Browse files
committed
feat(secretserver): Add support for Delinea Secret Server
Signed-off-by: Dargel, Philipp <philipp.dargel@governikus.de>
1 parent 78aa85f commit 1718ecc

File tree

4 files changed

+150
-0
lines changed

4 files changed

+150
-0
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ It supports various backends including:
2727
- HTTP JSON
2828
- Keychain
2929
- Scaleway
30+
- [Delinea SecretServer](https://delinea.com/products/secret-server)
3031
- Infisical
3132

3233
- Use `vals eval -f refs.yaml` to replace all the `ref`s in the file to actual values and secrets.
@@ -309,6 +310,7 @@ Please see the [relevant unit test cases](https://github.com/helmfile/vals/blob/
309310
- [HTTP JSON](#http-json)
310311
- [Fetch string value](#fetch-string-value)
311312
- [Fetch integer value](#fetch-integer-value)
313+
- [Delinea Secret Server](#secretserver)
312314
- [Advanced Usages](#advanced-usages)
313315
- [Discriminating config and secrets](#discriminating-config-and-secrets)
314316
- [Non-Goals](#non-goals)
@@ -1152,6 +1154,19 @@ Depending on which one is chosen with the `INFISICAL_AUTH_METHOD` environment va
11521154
- **GCP ID Token**: `GCP_ID_TOKEN`
11531155
- `INFISICAL_GCP_AUTH_IDENTITY_ID`: your Infisical Machine Identity ID.
11541156

1157+
### SecretServer
1158+
1159+
This provider allows retrieval of secrets from [Delinea SecretSever](https://delinea.com/products/secret-server) using their [REST API](https://docs.delinea.com/online-help/secret-server/api-scripting/rest-api/index.htm)
1160+
1161+
Environment variables:
1162+
1163+
- `SECRETSERVER_TOKEN`: The API Token to authenticate with. Can be created using their [OAuth Endpoint](https://updates.thycotic.net/secretserver/restapiguide/OAuth/)
1164+
- `SECRETSERVER_URL`: The URL to the SecretServer instance.
1165+
1166+
Examples:
1167+
1168+
- `ref+secretserver://12345/password`: gets the `password` field of the secret with id `12345` from the SecretServer running at the URL provdied in `SECRETSERVER_URL`
1169+
11551170
## Advanced Usages
11561171

11571172
### Discriminating config and secrets
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package secretserver
2+
3+
import (
4+
"crypto/tls"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
"net/http"
9+
"os"
10+
"strings"
11+
12+
"github.com/helmfile/vals/pkg/api"
13+
)
14+
15+
type secretServerSecret struct {
16+
Items []secretServerSecretItem `json:"items"`
17+
}
18+
19+
type secretServerSecretItem struct {
20+
Slug string `json:"slug"`
21+
ItemValue string `json:"itemValue"`
22+
}
23+
24+
type provider struct {
25+
APIVersion string
26+
SSLVerify bool
27+
}
28+
29+
func New(cfg api.StaticConfig) *provider {
30+
p := &provider{}
31+
v := cfg.String("ssl_verify")
32+
p.SSLVerify = v != "false"
33+
34+
if a := cfg.String("api_version"); a == "" {
35+
p.APIVersion = "v1"
36+
} else {
37+
p.APIVersion = a
38+
}
39+
40+
return p
41+
}
42+
43+
func (p *provider) GetString(key string) (string, error) {
44+
splits := strings.Split(key, "/")
45+
if len(splits) != 2 {
46+
return "", fmt.Errorf("malformed key")
47+
}
48+
secretID := splits[0]
49+
fieldName := splits[1]
50+
51+
g, err := p.getSecret(secretID)
52+
if err != nil {
53+
return "", err
54+
}
55+
56+
for _, item := range g.Items {
57+
if item.Slug == fieldName {
58+
return item.ItemValue, nil
59+
}
60+
}
61+
62+
return "", fmt.Errorf("cannot find field %s in secret", fieldName)
63+
}
64+
65+
func (p *provider) GetStringMap(key string) (map[string]interface{}, error) {
66+
secretMap := map[string]interface{}{}
67+
68+
secret, err := p.getSecret(key)
69+
if err != nil {
70+
return secretMap, err
71+
}
72+
73+
for _, item := range secret.Items {
74+
secretMap[item.Slug] = item.ItemValue
75+
}
76+
77+
return secretMap, nil
78+
}
79+
80+
func (p *provider) getSecret(secretID string) (secretServerSecret, error) {
81+
var secret secretServerSecret
82+
accessToken, ok := os.LookupEnv("SECRETSERVER_TOKEN")
83+
if !ok {
84+
return secret, errors.New("missing SECRETSERVER_TOKEN environment variable")
85+
}
86+
baseUrl, ok := os.LookupEnv("SECRETSERVER_URL")
87+
if !ok {
88+
return secret, errors.New("missing SECRETSERVER_URL environment variable")
89+
}
90+
91+
url := fmt.Sprintf("%s/api/%s/secrets/%s",
92+
baseUrl,
93+
p.APIVersion,
94+
secretID)
95+
96+
tr := &http.Transport{
97+
TLSClientConfig: &tls.Config{InsecureSkipVerify: !p.SSLVerify},
98+
}
99+
client := &http.Client{Transport: tr}
100+
req, err := http.NewRequest(http.MethodGet, url, nil)
101+
if err != nil {
102+
return secret, err
103+
}
104+
req.Header = http.Header{
105+
"Content-Type": {"application/json"},
106+
"Authorization": {fmt.Sprintf("Bearer %s", accessToken)},
107+
}
108+
109+
res, err := client.Do(req)
110+
if err != nil {
111+
return secret, err
112+
}
113+
114+
defer func() {
115+
_ = res.Body.Close()
116+
}()
117+
118+
if res.StatusCode != http.StatusOK {
119+
return secret, fmt.Errorf("SecretServer request %s failed: %s", req.URL, res.Status)
120+
}
121+
122+
err = json.NewDecoder(res.Body).Decode(&secret)
123+
if err != nil {
124+
return secret, fmt.Errorf("cannot decode JSON: %v", err)
125+
}
126+
return secret, nil
127+
}

pkg/stringprovider/stringprovider.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/helmfile/vals/pkg/providers/pulumi"
2525
"github.com/helmfile/vals/pkg/providers/s3"
2626
"github.com/helmfile/vals/pkg/providers/scaleway"
27+
"github.com/helmfile/vals/pkg/providers/secretserver"
2728
"github.com/helmfile/vals/pkg/providers/sops"
2829
"github.com/helmfile/vals/pkg/providers/ssm"
2930
"github.com/helmfile/vals/pkg/providers/tfstate"
@@ -86,6 +87,8 @@ func New(l *log.Logger, provider api.StaticConfig, awsLogLevel string) (api.Lazy
8687
return httpjson.New(l, provider), nil
8788
case "scaleway":
8889
return scaleway.New(l, provider), nil
90+
case "secretserver":
91+
return secretserver.New(provider), nil
8992
case "infisical":
9093
return infisical.New(l, provider), nil
9194
}

vals.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import (
4545
"github.com/helmfile/vals/pkg/providers/pulumi"
4646
"github.com/helmfile/vals/pkg/providers/s3"
4747
"github.com/helmfile/vals/pkg/providers/scaleway"
48+
"github.com/helmfile/vals/pkg/providers/secretserver"
4849
"github.com/helmfile/vals/pkg/providers/servercore"
4950
"github.com/helmfile/vals/pkg/providers/sops"
5051
"github.com/helmfile/vals/pkg/providers/ssm"
@@ -111,6 +112,7 @@ const (
111112
ProviderBitwarden = "bw"
112113
ProviderLockbox = "yclockbox"
113114
ProviderScaleway = "scw"
115+
ProviderSecretserver = "secretserver"
114116
ProviderInfisical = "infisical"
115117
ProviderServercore = "servercore"
116118
)
@@ -298,6 +300,9 @@ func (r *Runtime) prepare() (*expansion.ExpandRegexMatch, error) {
298300
case ProviderScaleway:
299301
p := scaleway.New(r.logger, conf)
300302
return p, nil
303+
case ProviderSecretserver:
304+
p := secretserver.New(conf)
305+
return p, nil
301306
case ProviderInfisical:
302307
p := infisical.New(r.logger, conf)
303308
return p, nil

0 commit comments

Comments
 (0)