Skip to content

Add support for Delinea Secret Server #457

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions docs/backends.md
Original file line number Diff line number Diff line change
Expand Up @@ -643,3 +643,49 @@ type: Opaque
data:
password: <path:secret-id#key | base64encode>
```

### Delinea Secret Server

**Note**: The Delinea Secret Server backend does not support versioning.

##### Delinea Authentication
Refer to the [REST API documentation on your Delinea Secret Server](https://your-delinea-server/SecretServer/Documents/restapi/) for API authentication.

These are the parameters for Delinea:
```
AVP_TYPE: delineasecretserver
AVP_DELINEA_URL: The URL of the Dilenea Secret Server
AVP_DELINEA_USER: The account for authentication
AVP_DELINEA_PASSWORD: The password for authentication

Optional:
AVP_DELINEA_DOMAIN: The authentication domain (e.g. the Active Directory domain)
```
##### Examples
Examples assume that the secrets are not saved base64 encoded in the Secret Server.

###### Path Annotation

```yaml
kind: Secret
apiVersion: v1
metadata:
name: test-secret
annotations:
avp.kubernetes.io/path: "secret-id"
type: Opaque
stringData:
password: <key>
```

###### Inline Path

```yaml
kind: Secret
apiVersion: v1
metadata:
name: test-secret
type: Opaque
data:
password: <path:secret-id#key | base64encode>
```
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ require (
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/BurntSushi/toml v1.2.0 // indirect
github.com/DataDog/datadog-go v3.2.0+incompatible // indirect
github.com/DelineaXPM/tss-sdk-go/v2 v2.0.0
github.com/Jeffail/gabs v1.1.1 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
Expand Down
875 changes: 16 additions & 859 deletions go.sum

Large diffs are not rendered by default.

86 changes: 86 additions & 0 deletions pkg/backends/delineasecretsmanager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package backends

import (
"encoding/json"
"fmt"
"strconv"

delineasecretserver "github.com/DelineaXPM/tss-sdk-go/v2/server"
"github.com/argoproj-labs/argocd-vault-plugin/pkg/utils"
)

// DelineaSecretServer is a struct for working with a Delinea Secrets Manager backend
type DelineaSecretServer struct {
Client *delineasecretserver.Server
}

// NewDelineaSecretServerBackend initializes a new Delinea Secrets Manager backend
func NewDelineaSecretServerBackend(client *delineasecretserver.Server) *DelineaSecretServer {
return &DelineaSecretServer{
Client: client,
}
}

// Login does nothing as a "login" is handled on the instantiation of the Delinea sdk
func (a *DelineaSecretServer) Login() error {
return nil
}

// GetSecrets gets secrets from Delinea Secret Server and returns the formatted data
// Currently there is no implementation present for versions nor annotations
func (a *DelineaSecretServer) GetSecrets(path string, version string, annotations map[string]string) (map[string]interface{}, error) {

// Delinea users pass the path of a secret
// ex: <path:123#username>
// So we query the secret id, and return a map

input, err := strconv.Atoi(path)
if err != nil {
return nil, fmt.Errorf("could not read path %s, error: %s", path, err)
}
secret, err := a.Client.Secret(input)
if err != nil {
return nil, fmt.Errorf("could not access secret %s, error: %s", path, err)
}
utils.VerboseToStdErr("Delinea Secret Server getting path %s", path)

secret_json, err := json.MarshalIndent(secret, "", " ")
if err != nil {
return nil, fmt.Errorf("enable to parse json secret %s, error: %s", path, err)
}

validation := make(map[string]interface{})

if secret != nil {
err := json.Unmarshal(secret_json, &validation)
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("could not decode secret json %s", secret_json)
}

utils.VerboseToStdErr("Delinea Secret Server decoding json %s", secret)

secret_map := make(map[string]interface{})

for index := range secret.Fields {
secret_map[secret.Fields[index].FieldName] = secret.Fields[index].ItemValue
secret_map[secret.Fields[index].Slug] = secret.Fields[index].ItemValue
}

utils.VerboseToStdErr("Delinea Secret Server constructed map %s", secret_map)
return secret_map, nil

}

// GetIndividualSecret will get the specific secret (placeholder) from the SM backend
// For Delinea Secret Server, we only support placeholders replaced from the k/v pairs of a secret which cannot be individually addressed
// So, we use GetSecrets and extract the specific placeholder we want
func (v *DelineaSecretServer) GetIndividualSecret(kvpath, secret, version string, annotations map[string]string) (interface{}, error) {
data, err := v.GetSecrets(kvpath, version, annotations)
if err != nil {
return nil, err
}
return data[secret], nil
}
27 changes: 27 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/1Password/connect-sdk-go/connect"
"github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
kvauth "github.com/Azure/azure-sdk-for-go/services/keyvault/auth"
delineasecretserver "github.com/DelineaXPM/tss-sdk-go/v2/server"
"github.com/IBM/go-sdk-core/v5/core"
ibmsm "github.com/IBM/secrets-manager-go-sdk/secretsmanagerv1"
"github.com/argoproj-labs/argocd-vault-plugin/pkg/auth/vault"
Expand Down Expand Up @@ -251,6 +252,32 @@ func New(v *viper.Viper, co *Options) (*Config, error) {

backend = backends.NewKeeperSecretsManagerBackend(client)
}
case types.DelineaSecretServerbackend:
{
// Get required Delinea specific env variables
if !v.IsSet(types.EnvAvpDelineaURL) ||
!v.IsSet(types.EnvAvpDelineaUser) ||
!v.IsSet(types.EnvAvpDelineaPassword) {
return nil, fmt.Errorf("%s, %s and %s are required for Delinea Secret Server",
types.EnvAvpDelineaURL,
types.EnvAvpDelineaUser,
types.EnvAvpDelineaPassword,
)
}

tss, _ := delineasecretserver.New(delineasecretserver.Configuration{
Credentials: delineasecretserver.UserCredential{
Username: v.GetString(types.EnvAvpDelineaUser),
Password: v.GetString(types.EnvAvpDelineaPassword),
Domain: v.GetString(types.EnvAvpDelineaDomain),
},
ServerURL: v.GetString(types.EnvAvpDelineaURL),
})
if err != nil {
return nil, err
}
backend = backends.NewDelineaSecretServerBackend(tss)
}
default:
return nil, fmt.Errorf("Must provide a supported Vault Type, received %s", v.GetString(types.EnvAvpType))
}
Expand Down
60 changes: 60 additions & 0 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,16 @@ fDGt+yaf3RaZbVwHSVLzxiXGsu1WQJde3uJeNh5c6z+5
},
"*backends.KeeperSecretsManager",
},
{
map[string]interface{}{
"AVP_TYPE": "delineasecretserver",
"AVP_AUTH_TYPE": "userpass",
"AVP_DELINEA_URL": "http://my-delinea-server",
"AVP_DELINEA_USER": "username",
"AVP_DELINEA_PASSWORD": "password",
},
"*backends.DelineaSecretServer",
},
}
for _, tc := range testCases {
for k, v := range tc.environment {
Expand Down Expand Up @@ -425,6 +435,56 @@ func TestNewConfigMissingParameter(t *testing.T) {
},
"*backends.OnePasswordConnect",
},
{
map[string]interface{}{
"AVP_TYPE": "delineasecretserver",
"AVP_AUTH_TYPE": "userpass",
"AVP_DELINEA_URL": "http://my-delinea-server",
"AVP_DELINEA_USER": "username",
},
"*backends.DelineaSecretServer",
},
{
map[string]interface{}{
"AVP_TYPE": "delineasecretserver",
"AVP_AUTH_TYPE": "userpass",
"AVP_DELINEA_URL": "http://my-delinea-server",
"AVP_DELINEA_PASSWORD": "password",
},
"*backends.DelineaSecretServer",
},
{
map[string]interface{}{
"AVP_TYPE": "delineasecretserver",
"AVP_AUTH_TYPE": "userpass",
"AVP_DELINEA_USER": "username",
"AVP_DELINEA_PASSWORD": "password",
},
"*backends.DelineaSecretServer",
},
{
map[string]interface{}{
"AVP_TYPE": "delineasecretserver",
"AVP_AUTH_TYPE": "userpass",
"AVP_DELINEA_USER": "username",
},
"*backends.DelineaSecretServer",
},
{
map[string]interface{}{
"AVP_TYPE": "delineasecretserver",
"AVP_AUTH_TYPE": "userpass",
"AVP_DELINEA_PASSWORD": "password",
},
"*backends.DelineaSecretServer",
},
{
map[string]interface{}{
"AVP_TYPE": "delineasecretserver",
"AVP_AUTH_TYPE": "userpass",
},
"*backends.DelineaSecretServer",
},
}
for _, tc := range testCases {
for k, v := range tc.environment {
Expand Down
5 changes: 5 additions & 0 deletions pkg/types/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ const (
EnvAvpPassword = "AVP_PASSWORD"
EnvPathValidation = "AVP_PATH_VALIDATION"
EnvAvpKSMConfigPath = "AVP_KEEPER_CONFIG_PATH"
EnvAvpDelineaURL = "AVP_DELINEA_URL"
EnvAvpDelineaUser = "AVP_DELINEA_USER"
EnvAvpDelineaPassword = "AVP_DELINEA_PASSWORD"
EnvAvpDelineaDomain = "AVP_DELINEA_DOMAIN"

// Backend and Auth Constants
VaultBackend = "vault"
Expand All @@ -36,6 +40,7 @@ const (
AzureKeyVaultbackend = "azurekeyvault"
Sopsbackend = "sops"
YandexCloudLockboxbackend = "yandexcloudlockbox"
DelineaSecretServerbackend = "delineasecretserver"
OnePasswordConnect = "1passwordconnect"
KeeperSecretsManagerBackend = "keepersecretsmanager"
K8sAuth = "k8s"
Expand Down