# Sync HashiCorp Vault Secrets into Kubernetes Native Secrets with ESO

[External Secrets Operator](https://external-secrets.io/) (ESO) does exactly what the name suggests. It can sync Secrets from remote providers like HashiCorp Vault, AWS SecretManager etc.

Secrets from Vault can be imported into the Kubernetes cluster using [csi-secrets-store](https://secrets-store-csi-driver.sigs.k8s.io/topics/sync-as-kubernetes-secret.html) and [hashicorp/vault](https://github.com/hashicorp/vault-helm) provider. But the CRD configuration seems a bit complex for noobs like me. So I ended up using ESO which seems easiest.

First, you need to install ESO with Helm inside the cluster. I am using `eso-system` as the namespace but feel free to choose another.

```bash
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets -n eso-system --create-namespace
```

Once the release is installed, 3 pods should appear and wait until they all are in both **Running** and **Healthy** state.

```plaintext
NAME                                                READY   STATUS
external-secrets-cert-controller-655d7bb477-kp2ck   1/1     Running
external-secrets-f9bc79d45-xk9lm                    1/1     Running
external-secrets-webhook-5b4779cbf8-mspsl           1/1     Running
```

To sync Secrets from Vault, we need to authorize the ESO service account to make valid calls to Vault APIs. I used a custom Token with a policy that allows only read access to the Vault KV engine. We need to pass the Token to ESO using Kubernetes secret.

```bash
kubectl create secret generic vault-token -n eso-system --from-literal=token=hvs.9lownuRz5bZO221dgkKXG5hQB
```

Once the Vault Token is ready, we can create a Vault Secret Store and tell ESO to establish a connection with it.

However, you can create multiple **SecretStore** in multiple namespaces. In that case, each **ExternalSecret** object will look for **SecretStore** in the same namespace.

In my case, I used one **ClusterSecretStore** and referenced multiple **ExternalSecret** in multiple namespaces to a single Store, to reduce the complexity.

Let's create our first ClusterSecretStore. Apply the following using `kubectl apply -f`

```yaml
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: vault-backend
spec:
  provider:
    vault:
      server: "https://vault.your.host:8200"
      path: "kv"
      version: "v1"
      auth:
        tokenSecretRef:
          name: "vault-token"
          namespace: eso-system
          key: "token"
```

Now check if the Secret Store is ready to be used. The field `Ready=True` is seen if the Vault Token is correct.

```plaintext
$ kubectl get clustersecretstores 
NAME            AGE   STATUS   READY
vault-backend   12h   Valid    True
```

When the Secret Store is ready, it's time to put some values in Vault. You can use Vault UI. For my case, I used the CLI to enable Vault **v1** KV engine and add some values there.

```bash
$ vault kv put kv/webapp-prod SPRING_DATASOURCE_USER=john SPRING_DATASOURCE_PASSWORD=1234qwer
$ vault kv get kv/webapp-prod
=============== Data ===============
Key                           Value
---                           -----
SPRING_DATASOURCE_PASSWORD    1234qwer
SPRING_DATASOURCE_USER        john
```

Now let's create an ExternalSecret object inside the cluster which will tell SecretStore to sync the above-mentioned secrets from Vault.

```yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: webapp-secret
  namespace: webapp-prod
spec:
  refreshInterval: 30s
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: webapp-secret
    creationPolicy: Owner
  dataFrom:
  - extract:
      key: kv/webapp-prod
```

I have used the `extract` option which will fetch all KV pairs from Vault. Also, I used the Kubernetes namespace as the Vault KV Path to easily map multiple secrets into multiple namespaces.

Anyway, let's check if the ExternalSecret syncing is successful.

```plaintext
$ kubectl get externalsecret -n webapp-prod
NAME            STORE           REFRESH   STATUS         READY
webapp-secret   vault-backend   30s       SecretSynced   True
```

The state `SecretSynced` indicates that ESO has successfully fetched the secrets from Vault as per the instruction. Let's check if the Kubernetes native Secret object is created.

```plaintext
$ kubectl get secret webapp-secret -n webapp-prod -o yaml | head -4
apiVersion: v1
data:
  SPRING_DATASOURCE_HOST: MTI3LjAuMC4x
  SPRING_DATASOURCE_PASSWORD: MTIzNHF3ZXI=
```

If you `base64 -d` them, you will notice that the values are the same as they are in Vault. Feel free to add more KV pairs in the same path and the new values should appear in Kubernetes Secret as well,
