Skip to content

Commit 6914dae

Browse files
authored
feat(secretserver): use official secretserver library (#930)
1 parent 00abcbb commit 6914dae

6 files changed

Lines changed: 74 additions & 93 deletions

File tree

README.md

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,14 +1191,34 @@ Depending on which one is chosen with the `INFISICAL_AUTH_METHOD` environment va
11911191

11921192
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)
11931193

1194-
Environment variables:
1194+
#### Configuration
11951195

1196-
- `SECRETSERVER_TOKEN`: The API Token to authenticate with. Can be created using their [OAuth Endpoint](https://updates.thycotic.net/secretserver/restapiguide/OAuth/)
1197-
- `SECRETSERVER_URL`: The URL to the SecretServer instance.
1196+
For on-prem instances set `TSS_SERVER_URL`. For cloud use set `TSS_TLD` to the top level domain and `TSS_TENANT` to your tenant id. If `TSS_SERVER_URL` is set other connection variables are ignored.
11981197

1199-
Examples:
1198+
#### Authentication
1199+
1200+
Authentication is done via environment variables:
1201+
1202+
- `TSS_USERNAME`: username to authenticate with
1203+
- `TSS_PASSWORD`: password to authenticate with
1204+
- `TSS_DOMAIN`: optional domain for the user
1205+
1206+
Alternatively you can provide an OAuth token directly via `TSS_TOKEN`. If you do all other authentication environment variables are ignored.
1207+
1208+
#### Parameters
1209+
1210+
You can disable ssl certificate verification by setting `ssl_verify=false` in the URLs
1211+
query.
1212+
1213+
#### Examples
1214+
1215+
- `ref+tss://12345#/password`: gets the `password` field of the secret with id `12345`
1216+
- `ref+tss://secret-name/password`: gets the `password` field of the secret with the name `secret-name`. The name has to uniquely identify the secret
1217+
1218+
1219+
#### Limitations
12001220

1201-
- `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`
1221+
The content of file fields, like certificates can't be retrieved. They will be replaced with the string `*** Not Valid For Display ***`.
12021222

12031223
## Advanced Usages
12041224

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0
1212
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
1313
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0
14+
github.com/DelineaXPM/tss-sdk-go/v3 v3.0.1
1415
github.com/DopplerHQ/cli v0.5.11-0.20230908185655-7aef4713e1a4
1516
github.com/a8m/envsubst v1.4.3
1617
github.com/antchfx/jsonquery v1.3.6

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mo
8484
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
8585
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
8686
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
87+
github.com/DelineaXPM/tss-sdk-go/v3 v3.0.1 h1:4JBJukbaTjv2gJogF3MxZkrt7i+ayRhM//FgdJTKJ3Q=
88+
github.com/DelineaXPM/tss-sdk-go/v3 v3.0.1/go.mod h1:VmyoHQ25FhSVHTI3/ptQNOviNEMfCy2ALAf/3E4Eqxg=
8789
github.com/DopplerHQ/cli v0.5.11-0.20230908185655-7aef4713e1a4 h1:s7/zwMi5w+KnlumDVbX1+P6mNAk5o7Wvx0VmvrQ7Bm0=
8890
github.com/DopplerHQ/cli v0.5.11-0.20230908185655-7aef4713e1a4/go.mod h1:ipnA9Lpn5YM+FDSQZ7VWNjcuVurchInoGKm+v7O0sGs=
8991
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c=

pkg/providers/secretserver/secretserver.go

Lines changed: 42 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -2,126 +2,85 @@ package secretserver
22

33
import (
44
"crypto/tls"
5-
"encoding/json"
6-
"errors"
75
"fmt"
8-
"net/http"
96
"os"
7+
"strconv"
108
"strings"
119

10+
tssSdk "github.com/DelineaXPM/tss-sdk-go/v3/server"
11+
1212
"github.com/helmfile/vals/pkg/api"
1313
)
1414

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-
2415
type provider struct {
25-
APIVersion string
26-
SSLVerify bool
16+
tss tssSdk.Server
2717
}
2818

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
19+
func New(cfg api.StaticConfig) (*provider, error) {
20+
tss, err := tssSdk.New(tssSdk.Configuration{
21+
Credentials: tssSdk.UserCredential{
22+
Domain: os.Getenv("TSS_DOMAIN"),
23+
Username: os.Getenv("TSS_USERNAME"),
24+
Password: os.Getenv("TSS_PASSWORD"),
25+
Token: os.Getenv("TSS_TOKEN"),
26+
},
27+
ServerURL: os.Getenv("TSS_SERVER_URL"),
28+
TLD: os.Getenv("TSS_TLD"),
29+
Tenant: os.Getenv("TSS_TENANT"),
30+
TLSClientConfig: &tls.Config{InsecureSkipVerify: cfg.String("ssl_verify") == "false"},
31+
})
32+
if err != nil {
33+
return nil, err
3834
}
39-
40-
return p
35+
return &provider{tss: *tss}, nil
4136
}
4237

4338
func (p *provider) GetString(key string) (string, error) {
4439
splits := strings.Split(key, "/")
4540
if len(splits) != 2 {
46-
return "", fmt.Errorf("malformed key")
41+
return "", fmt.Errorf("malformed key '%s'", key)
4742
}
4843
secretID := splits[0]
4944
fieldName := splits[1]
5045

51-
g, err := p.getSecret(secretID)
46+
secret, err := p.getSecret(secretID)
5247
if err != nil {
5348
return "", err
5449
}
5550

56-
for _, item := range g.Items {
57-
if item.Slug == fieldName {
58-
return item.ItemValue, nil
59-
}
51+
if field, ok := secret.Field(fieldName); ok {
52+
return field, nil
53+
} else {
54+
return "", fmt.Errorf("cannot find field %s in secret %s", fieldName, secretID)
6055
}
61-
62-
return "", fmt.Errorf("cannot find field %s in secret", fieldName)
6356
}
6457

6558
func (p *provider) GetStringMap(key string) (map[string]interface{}, error) {
66-
secretMap := map[string]interface{}{}
67-
6859
secret, err := p.getSecret(key)
6960
if err != nil {
70-
return secretMap, err
61+
return nil, err
7162
}
7263

73-
for _, item := range secret.Items {
64+
secretMap := map[string]interface{}{}
65+
for _, item := range secret.Fields {
66+
secretMap[item.FieldName] = item.ItemValue
7467
secretMap[item.Slug] = item.ItemValue
7568
}
7669

7770
return secretMap, nil
7871
}
7972

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)
73+
func (p *provider) getSecret(key string) (*tssSdk.Secret, error) {
74+
if i, err := strconv.Atoi(key); err == nil {
75+
return p.tss.Secret(i)
76+
} else {
77+
secrets, err := p.tss.Secrets(key, "Name")
78+
if err != nil {
79+
return nil, err
80+
}
81+
if len(secrets) != 1 {
82+
return nil, fmt.Errorf("expected exactly one secret with name '%s' but got %d", key, len(secrets))
83+
}
84+
return &secrets[0], nil
12585
}
126-
return secret, nil
12786
}

pkg/stringprovider/stringprovider.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ func New(l *log.Logger, provider api.StaticConfig, awsLogLevel string) (api.Lazy
8787
return httpjson.New(l, provider), nil
8888
case "scaleway":
8989
return scaleway.New(l, provider), nil
90-
case "secretserver":
91-
return secretserver.New(provider), nil
90+
case "tss":
91+
return secretserver.New(provider)
9292
case "infisical":
9393
return infisical.New(l, provider), nil
9494
}

vals.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ const (
114114
ProviderBitwarden = "bw"
115115
ProviderLockbox = "yclockbox"
116116
ProviderScaleway = "scw"
117-
ProviderSecretserver = "secretserver"
117+
ProviderSecretserver = "tss"
118118
ProviderInfisical = "infisical"
119119
ProviderServercore = "servercore"
120120
)
@@ -306,8 +306,7 @@ func (r *Runtime) prepare() (*expansion.ExpandRegexMatch, error) {
306306
p := scaleway.New(r.logger, conf)
307307
return p, nil
308308
case ProviderSecretserver:
309-
p := secretserver.New(conf)
310-
return p, nil
309+
return secretserver.New(conf)
311310
case ProviderInfisical:
312311
p := infisical.New(r.logger, conf)
313312
return p, nil

0 commit comments

Comments
 (0)