# BeyondTrust Password Safe

External Secrets Operator integrates with [BeyondTrust Password Safe](https://www.beyondtrust.com/docs/beyondinsight-password-safe/).

Warning: The External Secrets Operator secure usage involves taking several measures. Please see [Security Best Practices](https://external-secrets.io/latest/guides/security-best-practices/) for more information.

Warning: If the BT provider secret is deleted it will still exist in the Kubernetes secrets.

## Prerequisites
The BT provider supports retrieval of a secret from BeyondInsight/Password Safe versions 23.1 or greater.

For this provider to retrieve a secret the Password Safe/Secrets Safe instance must be preconfigured with the secret in question and authorized to read it.

## Authentication

BeyondTrust [OAuth Authentication](https://www.beyondtrust.com/docs/beyondinsight-password-safe/ps/admin/configure-api-registration.htm).

1. Create an API access registration in BeyondInsight
2. Create or use an existing Secrets Safe Group
3. Create or use an existing Application User
4. Add API registration to the Application user
5. Add the user to the group
6. Add the Secrets Safe Feature to the group

> NOTE: The ClientID and ClientSecret must be stored in a Kubernetes secret in order for the SecretStore to read the configuration.

If you're using client credentials authentication:
```sh
kubectl create secret generic bt-secret --from-literal ClientSecret="<your secret>"
kubectl create secret generic bt-id --from-literal ClientId="<your ID>"
```

If you're using API Key authentication:
```sh
kubectl create secret generic bt-apikey --from-literal ApiKey="<your apikey>"
```

## Client Certificate

If using `retrievalType: MANAGED_ACCOUNT`, you will also need to download the pfx certificate from Secrets Safe, extract that certificate and create two Kubernetes secrets.

```sh
openssl pkcs12 -in client_certificate.pfx -nocerts -out ps_key.pem -nodes
openssl pkcs12 -in client_certificate.pfx -clcerts -nokeys -out ps_cert.pem

# Copy the text from the ps_key.pem to a file.
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----

# Copy the text from the ps_cert.pem to a file.
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

kubectl create secret generic bt-certificate --from-file=ClientCertificate=./ps_cert.pem
kubectl create secret generic bt-certificatekey --from-file=ClientCertificateKey=./ps_key.pem
```

## Creating a SecretStore

You can follow the below example to create a `SecretStore` resource.
You can also use a `ClusterSecretStore` allowing you to reference secrets from all namespaces. [ClusterSecretStore](https://external-secrets.io/latest/api/clustersecretstore/)

```sh
kubectl apply -f secret-store.yml
```

```yaml
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
  name: secretstore-beyondtrust
spec:
  provider:
    beyondtrust:
      server:
        apiUrl: https://example.com/BeyondTrust/api/public/v3/
        retrievalType: MANAGED_ACCOUNT  # or SECRET
        separator: "/" # folder separator used to split remoteRef.key paths; defaults to "/"
        decrypt: true # SECRET retrievalType only: when false the password field is omitted; defaults to true
        verifyCA: true
        clientTimeOutSeconds: 45
        apiVersion: "3.0" # The recommended version is 3.1. If no version is specified, the default API version 3.0 will be used.
      auth:
        certificate: # omit certificates if retrievalType is SECRET
          secretRef:
            name: bt-certificate
            key: ClientCertificate
        certificateKey:
          secretRef:
            name: bt-certificatekey
            key: ClientCertificateKey
        clientSecret: # define this section if using client credentials authentication
          secretRef:
            name: bt-secret
            key: ClientSecret
        clientId: # define this section if using client credentials authentication
          secretRef:
            name: bt-id
            key: ClientId
        apiKey: # define this section if using Api Key authentication
          secretRef:
            name: bt-apikey
            key: ApiKey
```

## Creating an ExternalSecret

You can follow the below example to create a `ExternalSecret` resource. Secrets can be referenced by path.
You can also use a `ClusterExternalSecret` allowing you to reference secrets from all namespaces.

`remoteRef.key` is the secret or managed-account path. Path segments are joined by the `separator` configured on the store (default `/`), for example `system01/managed_account01`. Reference each secret explicitly under `data`; `dataFrom` is not supported (see Limitations).

```sh
kubectl apply -f external-secret.yml
```

```yaml
{% include 'beyondtrust-external-secret.yaml' %}
```

## Get the K8s secret

```shell
# WARNING: this command will reveal the stored secret in plain text
kubectl get secret my-beyondtrust-secret -o jsonpath="{.data.secretKey}" | base64 --decode && echo
```

## Creating a Secret

The following example shows how to create a Kubernetes `Secret` that will later be pushed to BeyondTrust.

```sh
kubectl apply -f beyondtrust-secret.yml
```

```yaml
{% include 'beyondtrust-secret.yaml' %}
```

## Creating an ClusterSecretStore

The following example demonstrates how to create a `ClusterSecretStore` configured to use the BeyondTrust provider.

```sh
kubectl apply -f beyondtrust-cluster-secret-store.yml
```

```yaml
{% include 'beyondtrust-cluster-secret-store.yaml' %}
```

## Creating an PushSecret

The example below demonstrates how to create a `PushSecret` resource to push secret data to BeyondTrust.

```sh
kubectl apply -f beyondtrust-push-secret.yml
```

```yaml
{% include 'beyondtrust-push-secret.yaml' %}
```

## Limitations

- The provider reads individual secrets via `data[].remoteRef.key` and writes via `PushSecret`. `dataFrom.extract` and `dataFrom.find` are not implemented (`GetSecretMap` and `GetAllSecrets` return "not implemented"), so reference each secret explicitly by key.
- `PushSecret` with `deletionPolicy: Delete` is not supported. Removing the `PushSecret` or `ExternalSecret` does not delete the secret in BeyondTrust, because the `DeleteSecret` operation is not implemented.
