Skip to content

Commit 4d28244

Browse files
committed
Add support for Delinea Secret Server
Signed-off-by: Gerben Welter <[email protected]>
1 parent 7271864 commit 4d28244

File tree

7 files changed

+241
-859
lines changed

7 files changed

+241
-859
lines changed

docs/backends.md

+46
Original file line numberDiff line numberDiff line change
@@ -643,3 +643,49 @@ type: Opaque
643643
data:
644644
password: <path:secret-id#key | base64encode>
645645
```
646+
647+
### Delinea Secret Server
648+
649+
**Note**: The Delinea Secret Server backend does not support versioning.
650+
651+
##### Delinea Authentication
652+
Refer to the [REST API documentation on your Delinea Secret Server](https://your-delinea-server/SecretServer/Documents/restapi/) for API authentication.
653+
654+
These are the parameters for Delinea:
655+
```
656+
AVP_TYPE: delineasecretserver
657+
AVP_DELINEA_URL: The URL of the Dilenea Secret Server
658+
AVP_DELINEA_USER: The account for authentication
659+
AVP_DELINEA_PASSWORD: The password for authentication
660+
661+
Optional:
662+
AVP_DELINEA_DOMAIN: The authentication domain (e.g. the Active Directory domain)
663+
```
664+
##### Examples
665+
Examples assume that the secrets are not saved base64 encoded in the Secret Server.
666+
667+
###### Path Annotation
668+
669+
```yaml
670+
kind: Secret
671+
apiVersion: v1
672+
metadata:
673+
name: test-secret
674+
annotations:
675+
avp.kubernetes.io/path: "secret-id"
676+
type: Opaque
677+
stringData:
678+
password: <key>
679+
```
680+
681+
###### Inline Path
682+
683+
```yaml
684+
kind: Secret
685+
apiVersion: v1
686+
metadata:
687+
name: test-secret
688+
type: Opaque
689+
data:
690+
password: <path:secret-id#key | base64encode>
691+
```

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ require (
7070
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
7171
github.com/BurntSushi/toml v1.2.0 // indirect
7272
github.com/DataDog/datadog-go v3.2.0+incompatible // indirect
73+
github.com/DelineaXPM/tss-sdk-go/v2 v2.0.0
7374
github.com/Jeffail/gabs v1.1.1 // indirect
7475
github.com/Masterminds/goutils v1.1.1 // indirect
7576
github.com/Masterminds/semver v1.5.0 // indirect

go.sum

+16-859
Large diffs are not rendered by default.

pkg/backends/delineasecretsmanager.go

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package backends
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"strconv"
7+
8+
delineasecretserver "github.com/DelineaXPM/tss-sdk-go/v2/server"
9+
"github.com/argoproj-labs/argocd-vault-plugin/pkg/utils"
10+
)
11+
12+
// DelineaSecretServer is a struct for working with a Delinea Secrets Manager backend
13+
type DelineaSecretServer struct {
14+
Client *delineasecretserver.Server
15+
}
16+
17+
// NewDelineaSecretServerBackend initializes a new Delinea Secrets Manager backend
18+
func NewDelineaSecretServerBackend(client *delineasecretserver.Server) *DelineaSecretServer {
19+
return &DelineaSecretServer{
20+
Client: client,
21+
}
22+
}
23+
24+
// Login does nothing as a "login" is handled on the instantiation of the Delinea sdk
25+
func (a *DelineaSecretServer) Login() error {
26+
return nil
27+
}
28+
29+
// GetSecrets gets secrets from Delinea Secret Server and returns the formatted data
30+
// Currently there is no implementation present for versions nor annotations
31+
func (a *DelineaSecretServer) GetSecrets(path string, version string, annotations map[string]string) (map[string]interface{}, error) {
32+
33+
// Delinea users pass the path of a secret
34+
// ex: <path:123#username>
35+
// So we query the secret id, and return a map
36+
37+
input, err := strconv.Atoi(path)
38+
if err != nil {
39+
return nil, fmt.Errorf("could not read path %s, error: %s", path, err)
40+
}
41+
secret, err := a.Client.Secret(input)
42+
if err != nil {
43+
return nil, fmt.Errorf("could not access secret %s, error: %s", path, err)
44+
}
45+
utils.VerboseToStdErr("Delinea Secret Server getting path %s", path)
46+
47+
secret_json, err := json.MarshalIndent(secret, "", " ")
48+
if err != nil {
49+
return nil, fmt.Errorf("enable to parse json secret %s, error: %s", path, err)
50+
}
51+
52+
validation := make(map[string]interface{})
53+
54+
if secret != nil {
55+
err := json.Unmarshal(secret_json, &validation)
56+
if err != nil {
57+
return nil, err
58+
}
59+
} else {
60+
return nil, fmt.Errorf("could not decode secret json %s", secret_json)
61+
}
62+
63+
utils.VerboseToStdErr("Delinea Secret Server decoding json %s", secret)
64+
65+
secret_map := make(map[string]interface{})
66+
67+
for index := range secret.Fields {
68+
secret_map[secret.Fields[index].FieldName] = secret.Fields[index].ItemValue
69+
secret_map[secret.Fields[index].Slug] = secret.Fields[index].ItemValue
70+
}
71+
72+
utils.VerboseToStdErr("Delinea Secret Server constructed map %s", secret_map)
73+
return secret_map, nil
74+
75+
}
76+
77+
// GetIndividualSecret will get the specific secret (placeholder) from the SM backend
78+
// For Delinea Secret Server, we only support placeholders replaced from the k/v pairs of a secret which cannot be individually addressed
79+
// So, we use GetSecrets and extract the specific placeholder we want
80+
func (v *DelineaSecretServer) GetIndividualSecret(kvpath, secret, version string, annotations map[string]string) (interface{}, error) {
81+
data, err := v.GetSecrets(kvpath, version, annotations)
82+
if err != nil {
83+
return nil, err
84+
}
85+
return data[secret], nil
86+
}

pkg/config/config.go

+27
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/1Password/connect-sdk-go/connect"
1414
"github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
1515
kvauth "github.com/Azure/azure-sdk-for-go/services/keyvault/auth"
16+
delineasecretserver "github.com/DelineaXPM/tss-sdk-go/v2/server"
1617
"github.com/IBM/go-sdk-core/v5/core"
1718
ibmsm "github.com/IBM/secrets-manager-go-sdk/secretsmanagerv1"
1819
"github.com/argoproj-labs/argocd-vault-plugin/pkg/auth/vault"
@@ -251,6 +252,32 @@ func New(v *viper.Viper, co *Options) (*Config, error) {
251252

252253
backend = backends.NewKeeperSecretsManagerBackend(client)
253254
}
255+
case types.DelineaSecretServerbackend:
256+
{
257+
// Get required Delinea specific env variables
258+
if !v.IsSet(types.EnvAvpDelineaURL) ||
259+
!v.IsSet(types.EnvAvpDelineaUser) ||
260+
!v.IsSet(types.EnvAvpDelineaPassword) {
261+
return nil, fmt.Errorf("%s, %s and %s are required for Delinea Secret Server",
262+
types.EnvAvpDelineaURL,
263+
types.EnvAvpDelineaUser,
264+
types.EnvAvpDelineaPassword,
265+
)
266+
}
267+
268+
tss, _ := delineasecretserver.New(delineasecretserver.Configuration{
269+
Credentials: delineasecretserver.UserCredential{
270+
Username: v.GetString(types.EnvAvpDelineaUser),
271+
Password: v.GetString(types.EnvAvpDelineaPassword),
272+
Domain: v.GetString(types.EnvAvpDelineaDomain),
273+
},
274+
ServerURL: v.GetString(types.EnvAvpDelineaURL),
275+
})
276+
if err != nil {
277+
return nil, err
278+
}
279+
backend = backends.NewDelineaSecretServerBackend(tss)
280+
}
254281
default:
255282
return nil, fmt.Errorf("Must provide a supported Vault Type, received %s", v.GetString(types.EnvAvpType))
256283
}

pkg/config/config_test.go

+60
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,16 @@ fDGt+yaf3RaZbVwHSVLzxiXGsu1WQJde3uJeNh5c6z+5
209209
},
210210
"*backends.KeeperSecretsManager",
211211
},
212+
{
213+
map[string]interface{}{
214+
"AVP_TYPE": "delineasecretserver",
215+
"AVP_AUTH_TYPE": "userpass",
216+
"AVP_DELINEA_URL": "http://my-delinea-server",
217+
"AVP_DELINEA_USER": "username",
218+
"AVP_DELINEA_PASSWORD": "password",
219+
},
220+
"*backends.DelineaSecretServer",
221+
},
212222
}
213223
for _, tc := range testCases {
214224
for k, v := range tc.environment {
@@ -425,6 +435,56 @@ func TestNewConfigMissingParameter(t *testing.T) {
425435
},
426436
"*backends.OnePasswordConnect",
427437
},
438+
{
439+
map[string]interface{}{
440+
"AVP_TYPE": "delineasecretserver",
441+
"AVP_AUTH_TYPE": "userpass",
442+
"AVP_DELINEA_URL": "http://my-delinea-server",
443+
"AVP_DELINEA_USER": "username",
444+
},
445+
"*backends.DelineaSecretServer",
446+
},
447+
{
448+
map[string]interface{}{
449+
"AVP_TYPE": "delineasecretserver",
450+
"AVP_AUTH_TYPE": "userpass",
451+
"AVP_DELINEA_URL": "http://my-delinea-server",
452+
"AVP_DELINEA_PASSWORD": "password",
453+
},
454+
"*backends.DelineaSecretServer",
455+
},
456+
{
457+
map[string]interface{}{
458+
"AVP_TYPE": "delineasecretserver",
459+
"AVP_AUTH_TYPE": "userpass",
460+
"AVP_DELINEA_USER": "username",
461+
"AVP_DELINEA_PASSWORD": "password",
462+
},
463+
"*backends.DelineaSecretServer",
464+
},
465+
{
466+
map[string]interface{}{
467+
"AVP_TYPE": "delineasecretserver",
468+
"AVP_AUTH_TYPE": "userpass",
469+
"AVP_DELINEA_USER": "username",
470+
},
471+
"*backends.DelineaSecretServer",
472+
},
473+
{
474+
map[string]interface{}{
475+
"AVP_TYPE": "delineasecretserver",
476+
"AVP_AUTH_TYPE": "userpass",
477+
"AVP_DELINEA_PASSWORD": "password",
478+
},
479+
"*backends.DelineaSecretServer",
480+
},
481+
{
482+
map[string]interface{}{
483+
"AVP_TYPE": "delineasecretserver",
484+
"AVP_AUTH_TYPE": "userpass",
485+
},
486+
"*backends.DelineaSecretServer",
487+
},
428488
}
429489
for _, tc := range testCases {
430490
for k, v := range tc.environment {

pkg/types/constants.go

+5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ const (
2727
EnvAvpPassword = "AVP_PASSWORD"
2828
EnvPathValidation = "AVP_PATH_VALIDATION"
2929
EnvAvpKSMConfigPath = "AVP_KEEPER_CONFIG_PATH"
30+
EnvAvpDelineaURL = "AVP_DELINEA_URL"
31+
EnvAvpDelineaUser = "AVP_DELINEA_USER"
32+
EnvAvpDelineaPassword = "AVP_DELINEA_PASSWORD"
33+
EnvAvpDelineaDomain = "AVP_DELINEA_DOMAIN"
3034

3135
// Backend and Auth Constants
3236
VaultBackend = "vault"
@@ -36,6 +40,7 @@ const (
3640
AzureKeyVaultbackend = "azurekeyvault"
3741
Sopsbackend = "sops"
3842
YandexCloudLockboxbackend = "yandexcloudlockbox"
43+
DelineaSecretServerbackend = "delineasecretserver"
3944
OnePasswordConnect = "1passwordconnect"
4045
KeeperSecretsManagerBackend = "keepersecretsmanager"
4146
K8sAuth = "k8s"

0 commit comments

Comments
 (0)