# Kubernetes External Secrets Operator

<figure><img src="https://762006384-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJXOXEifAmpyvNVL1to%2Fuploads%2FTuTzLzBatbwf6hlcKEGt%2Fksm-externalsecrets-kubernetes.jpg?alt=media&#x26;token=16302139-00a4-4e61-8c25-8d24f83c911b" alt=""><figcaption></figcaption></figure>

## Overview&#x20;

Kubernetes [External Secrets Operator](https://charts.external-secrets.io) injects secrets into Kubernetes by synchronizing them from various external APIs. This guide primarily focuses on the setup process for External Secrets, facilitating the synchronization of secrets from your Keeper Vault into Kubernetes.

The External Operator documentation can be found [at this link](https://external-secrets.io/latest/provider/keeper-security/).

## Features&#x20;

* Seamless synchronization of secrets from Keeper Vault into Kubernetes via External Secrets.&#x20;
* Real-time access to secrets from Keeper Vault across all pods.

## Prerequisites&#x20;

Before proceeding with the setup, ensure you have the following:

* Keeper Secrets Manager (KSM) access (See the [Quick Start Guide](https://docs.keeper.io/en/keeperpam/secrets-manager/quick-start-guide) for more details)
  * Secrets Manager add-on enabled for your Keeper account
  * Membership in a Role with the Secrets Manager enforcement policy enabled
* A Keeper [Secrets Manager Application](https://docs.keeper.io/en/keeperpam/about/terminology#application) with secrets shared to it&#x20;
  * See the [Quick Start Guide](https://docs.keeper.io/en/keeperpam/quick-start-guide#2.-create-an-application) for instructions on creating an Application
  * Make sure you add the proper permissions to your device in order to be able to read and write secrets
* An initialized Keeper [Secrets Manager Configuration](https://docs.keeper.io/en/keeperpam/secrets-manager/about/secrets-manager-configuration)&#x20;
* The following commands installed:
  * [`helm`](https://helm.sh/) - package manager for Kubernetes
  * [`kubectl`](https://kubernetes.io/docs/reference/kubectl/) - command line tool for Kubernetes&#x20;

## Setup

### Install External Secrets With Helm

To install External Secrets with Helm, run the following commands:

```shell
helm repo add external-secrets https://charts.external-secrets.io

helm install external-secrets \
    external-secrets/external-secrets \
    -n external-secrets \
    --create-namespace
```

### Create Kubernetes Secret to store Base64 KSM Config&#x20;

After creating a [Secrets Manager Configuration](https://docs.keeper.io/secrets-manager/secrets-manager/about/secrets-manager-configuration) for your device, you will have a Base64 JSON string that contains connection tokens, encryption keys, identifiers and domain information used to authenticate and decrypt data from the Keeper Secrets Manager APIs.&#x20;

The Base64 JSON config string will be set by External Secrets to authenticate against Keeper Security and defined in a regular Kubernetes Secret.

Invoking the following command will create a Kubernetes Secret which is used to authenticate to Keeper Secrets Manager:

{% code lineNumbers="true" %}

```yaml
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: ksm-config-secret # name of the k8s Secret where KSM config is stored
type: Opaque
data:
  ksm_config: "[REPLACE WITH YOUR BASE64 JSON string]"
EOF
```

{% endcode %}

**Note:** Lines 2-8 in the above code snippet can be stored in a YAML file and applied with the command `kubectl apply`. For example, you can store lines 2-8 in `secrets.yaml` and execute the following:&#x20;

```bash
kubectl apply -f secret.yaml
```

### Create SecretStore&#x20;

After creating a Kubernetes Secret with `ksm_config` defined to your Base64 JSON string, you can now create your SecretStore.&#x20;

Invoking the following command will create your SecretStore:

<pre class="language-yaml" data-line-numbers data-full-width="false"><code class="lang-yaml"><strong>kubectl apply -f - &#x3C;&#x3C;EOF
</strong>apiVersion: external-secrets.io/v1beta1   
kind: SecretStore
metadata:
<strong>  name: my-external-secrets-secretstore   # name of the SecretStore where retrieved secrets will be stored once fetched from Keeper Secrets Manager (KSM)
</strong>spec:
  provider:
    keepersecurity:                       # name of the SecretStore provider, in this case KeeperSecurity
<strong>      authRef:
</strong>        name: ksm-config-secret           # name of the k8s Secret where KSM config is stored
        key: ksm_config                   # key in the k8s Secret where KSM config is stored
      folderID: "[SHARED FOLDER UID]"     # UID of the shared folder in KeeperSecurity where the records 
                                          #   are stored. Make sure the folder is shared into the KSM Application
EOF
</code></pre>

{% hint style="info" %}
In the above code snippet, define `folderID` with the UID of the shared folder where the records are stored in your Vault
{% endhint %}

{% hint style="info" %}
In case of a `ClusterSecretStore`, Be sure to provide `namespace` for `SecretAccessKeyRef` with the namespace of the secret that we just created.
{% endhint %}

**Note:** Lines 2-13 in the above code snippet can be stored in a YAML file and applied with the command `kubectl apply`. For example, you can store lines 2-13 in `secretstore.yaml` and execute the following:&#x20;

```bash
kubectl apply -f secretstore.yaml
```

### Create ExternalSecret

Next, you need to create your ExternalSecret.&#x20;

The following code snippet will create your External Secret and store the values of the login & password field for the specified record into the Kubernetes Secret. These fields are defined in the `target.template.data` section and refreshed every 30 seconds. For a full list of supported fields, visit this [page](https://docs.keeper.io/secrets-manager/secrets-manager/about/field-record-types).

{% code lineNumbers="true" fullWidth="false" %}

```yaml
kubectl apply -f - <<EOF
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
 name: ksm-external-secret
spec:
 refreshInterval: 12h               # rate how often SecretManager pulls KeeperSecurity. 
                                    #   In this case every 12 hours, for the example. 
                                    #   We recommend this value to be 12h or more.
 secretStoreRef:                    # reference to the SecretStore defined above to authenticate against Keeper Security
   kind: SecretStore                # tells External Secrets the type of the secret store, should be same as the one defined above
   name: my-external-secrets-secretstore  

 dataFrom:                          # tells External Secrets which record to use to fetch from Keeper Secrets Manager (KSM)
   - extract:
       key: "[RECORD UID]"          # UID of the record in Keeper where the secrets are going to be fetched from
 target:                            # tells External Secrets the target location where to store the secrets once fetched from Keeper Security
   name: my-external-secrets-values # name of the k8s Secret to be created
   creationPolicy: Owner            # tells External Secrets to create the k8s Secret if it doesn't exist
   template:
     engineVersion: v2          
     data:
       username: "{{ .login }}"     # tells External Secrets to store the value of 
                                    # the login field in Keeper Security into the k8s Secret under the key username
       password: "{{ .password }}"  # tells External Secrets to store the value of 
                                    # the password field in Keeper Security into the k8s Secret under the key password
       name: "{{  (fromJson .name).first }} {{  (fromJson .name).middle }} {{  (fromJson .name).last }}" # decode json string into vars
EOF
```

{% endcode %}

{% hint style="info" %}
In the above code snippet, replace "\[RECORD UID]" with the UID of your desired record
{% endhint %}

{% hint style="info" %}
For complex [types](https://docs.keeper.io/secrets-manager/secrets-manager/about/field-record-types), like `name`, `phone`, `bankAccount`, which does not match with a single string value, external secrets will return the complete JSON string. Use the JSON template functions to decode.
{% endhint %}

**Note:** Lines 2-27 in the above code snippet can be stored in a YAML file and applied with the command `kubectl apply`. For example, you can store lines 2-27 in `externalsecret.yaml` and execute the following:&#x20;

```bash
kubectl apply -f externalsecret.yaml
```

#### Behavior <a href="#behavior" id="behavior"></a>

* How a Record is equated to an ExternalSecret:
  * `remoteRef.key` is equated to a Record's ID
  * `remoteRef.property` is equated to one of the following options:
    * Fields: [Record's field's Type](https://docs.keeper.io/secrets-manager/secrets-manager/about/field-record-types)
    * CustomFields: Record's field's Label
    * Files: Record's file's Name
    * If empty, defaults to the complete Record in JSON format
  * `remoteRef.version` is currently not supported.
* `dataFrom`:
  * `find.path` is currently not supported.
  * `find.name.regexp` is equated to one of the following options:
    * Fields: Record's field's Type
    * CustomFields: Record's field's Label
    * Files: Record's file's Name
  * `find.tags` are not supported at this time.

### Limitations <a href="#limitations" id="limitations"></a>

There are some limitations using this provider.

* Keeper Secret Manager does not work with legacy non-typed records
* Using tags `find.tags` is not supported by KSM
* Using path `find.path` is not supported at the moment

### Push Secrets

Push Secret will only work with a custom KeeperSecurity Record type `ExternalSecrets`

#### Behavior <a href="#behavior_1" id="behavior_1"></a>

* `selector`:
* `secret.name`: name of the kubernetes secret to be pushed
* `data.match`:
* `secretKey`: key on the selected secret to be pushed
* `remoteRef.remoteKey`: Secret and key to be created on the remote provider
  * Format: SecretName/SecretKey

### Create PushSecret

To create a Keeper Security record from Kubernetes a `Kind=PushSecret` is needed.

<pre class="language-yaml" data-line-numbers data-full-width="false"><code class="lang-yaml"><strong>kubectl apply -f - &#x3C;&#x3C;EOF
</strong>apiVersion: external-secrets.io/v1alpha1
kind: PushSecret
metadata:
  name: example
spec:
  secretStoreRefs:
    - name: keeper
      kind: SecretStore
  refreshInterval: "1h"
  deletionPolicy: Delete
  selector:
    secret:
      name: secret-name # k8s secret to be pushed
  data:
    - match:
        secretKey: secret-key # k8s key within the secret to be pushed
        remoteRef:
          remoteKey: remote-secret-name/remote-secret-key # This will create a record called "remote-secret-name" with a key "remote-secret-key"
EOF
</code></pre>

**Note:** Lines 2-19 in the above code snippet can be stored in a YAML file and applied with the command `kubectl apply`. For example, you can store lines 2-19 in `pushsecret.yaml` and execute the following:&#x20;

```bash
kubectl apply -f pushsecret.yaml
```

{% hint style="warning" %}
Make sure there's only one record with the title `remote-secret-name` in the KSM Application in use.
{% endhint %}

#### Limitations <a href="#limitations_1" id="limitations_1"></a>

* Only possible to push one key per secret at the moment
* If the record with the selected name exists but the key does not exists the record can not be updated.

## Verifying Setup&#x20;

After setting up your [Kubernetes Secret](#create-kubernetes-secret-to-store-base64-ksm-config), [SecretStore](#create-secretstore), and [ExternalSecret](#create-externalsecret), you can extract secrets with the command `kubectl get secrets`

In the above code snippets, the name of the secret is my-external-secrets-values and we store the following record values:&#x20;

```yaml
data:
       username: "{{ .login }}"                                      
       password: "{{ .password }}"
```

To get the login and password values, invoke the following command:

```
$ kubectl get secret my-external-secrets-values -o jsonpath="{.data}"
{"password":"a2lsbCB5b3U=","username":"SSB3aWxs"}
```

The above response is encoded, to decode, invoke the following:

{% code fullWidth="false" %}

```
$ kubectl get secret my-external-secrets-values -o jsonpath="{.data.password}" | base64 --decode
pAs$w0rd
```

{% endcode %}

## Conclusion

In conclusion, this guide has detailed a step-by-step process for integrating the Keeper Secrets Manager with Kubernetes via the Kubernetes External Secrets Operator. By following these steps, you'll be able to seamlessly synchronize your secrets stored in Keeper Vault into your Kubernetes environment. This not only provides a secure method to manage your secrets but also facilitates real-time access across all your pods.

The processes outlined, including setting up the External Secrets operator, creating a Kubernetes Secret, SecretStore, and ExternalSecret, are key to this integration. Upon successful setup, the provided commands allow you to verify the integration and retrieve stored secrets effortlessly.

The integration of Keeper Secrets Manager with Kubernetes enhances the security infrastructure of your applications running in the Kubernetes environment. It provides a solid foundation for managing your secrets, thereby improving overall operational efficiency and security posture.

Remember to replace all placeholders in the command snippets with your specific information, and don't hesitate to refer back to this guide anytime you need to set up or manage your External Secrets in Kubernetes.
