External Secret Operator

External Secrets Operator (ESO)

External Secrets Operator (ESO) is a Kubernetes (K8s) operator that integrates with external secret management systems like Akeyless. The operator reads information from Akeyless APIs and automatically injects the values into a K8s Secret.

The goal of the ESO is to synchronize secrets from Akeyless into K8s. ESO is a collection of custom API resources - ExternalSecret, SecretStore, and ClusterSecretStore that provides a user-friendly abstraction for the external API that stores and manages the lifecycle of the secrets for you.

The ESO runs within your K8s cluster as a deployment resource. It utilizes CustomResourceDefinitions to configure access to secret providers through SecretStore resources and manages K8s secret resources with ExternalSecret resources.

You can use two types of resources to fetch secrets from Akeyless:

  • SecretStore: Defines how to access secrets from Akeyless within a specific namespace.

  • ClusterSecretStore: Defines how to access secrets from Akeyless across the entire Kubernetes cluster.

In addition to retrieving secrets from Akeyless to your K8s cluster, you can use the PushSecret resource to push a local K8s secret from your cluster to Akeyless.

Prerequisites

  • Helm installed
  • K8s v1.16 or higher

Installing with Helm

Add External Secrets official repository to your helm and install:

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

Authentication

Akeyless official provider support the following Auth Methods:

👍

Note

This guide demonstrates authentication using API Key and K8s Auth Methods. However, for security purposes, it’s highly recommended to avoid using API Keys in production.

To set an auth method for the external secret operator, first create a K8s Secret with the relevant settings, for example:

apiVersion: v1
kind: Secret
metadata:
  name: akeyless-secret-creds
type: Opaque
stringData:
  accessId: <Access ID>
  accessType: api_key
  accessTypeParam: <Access Key>
apiVersion: v1
kind: Secret
metadata:
  name: akeyless-secret-creds
type: Opaque
stringData:
  accessId: <Access ID>
  accessType: k8s
  accessTypeParam: <k8s-conf-name>

Where:

  • name: A name for the k8s secret to store the Authentication details.

  • accessId: The Auth Method Access ID.

  • accessType: The Auth method type.

  • accessTypeParam: Access Key for API Key or k8s-conf-name for K8s. For more options, check the official provider docs.

Apply the configuration:

kubectl apply -f akeylesscreds.yaml

SecretStore

The SecretStore resource is namespaced and defines how to authenticate to Akeyles, in the following example, a reference to the akeyless-secret-creds that was created earlier is used.

Set the SecretStore resource:

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: akeyless-secret-store
spec:
  provider:
    akeyless:
      akeylessGWApiURL: "https://api.akeyless.io"
      authSecretRef:
        secretRef:
          accessID:
            name: akeyless-secret-creds
            key: accessId
          accessType:
            name: akeyless-secret-creds
            key: accessType
          accessTypeParam:
            name: akeyless-secret-creds
            key: accessTypeParam

Where:

  • akeylessGWApiURL: The URL of your Gateway API v2 endpoint: https://Your-Gateway-URL:8000/api/v2. (or using your gateway url at port 8081)

  • authSecretRef: References a K8s Secret akeyless-secret-creds containing authentication credentials.

  • secretRef: Refers to a K8s Secret named akeyless-secret-creds, which contains values for accessID, accessType, and accessTypeParam.

Apply the configuration:

kubectl apply -f secretstore.yaml

Explicit Secret Store

Authentication with Akeyless can be done using credentials stored in the akeyless-secret-creds K8s secret through the SecretStore. Alternatively, you can authenticate directly using your k8s Auth settings.

Using an explicit secret store provides key benefits for access control and security. By segregating secrets based on service accounts, you can ensure that each service account only has access to the secrets it needs.

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: akeyless-secret-store
spec:
  provider:
    akeyless:
      akeylessGWApiURL: "https://api.akeyless.io"
      authSecretRef:
        kubernetesAuth:
          accessID: <AccessID>
          k8sConfName: <K8s Conf Name>
          serviceAccountRef:
            name: <ServiceAccount Name>

Where:

  • accessId: The K8s Auth Method Access ID.

  • k8sConfName: The name of the K8s Conf on the Gateway.

  • serviceAccountRef: The name of the K8s service account used to fetch secrets from Akeyless. Only secrets defined in a role associated with that service account under claim service_account_name can be accessed.

ExternalSecret

To retrieve a secret from Akeyless and store it as a K8s secret in your cluster, create an ExternalSecret resource that specifies which secret to fetch:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: akeyless-external-secret-example
spec:
  refreshInterval: 1h

  secretStoreRef:
    kind: SecretStore 
    name: akeyless-secret-store 

  target:
    name: akeyless-secret-to-create 
    creationPolicy: Owner

  data:
    - secretKey: secretKey 
      remoteRef:
        key: /path/to/your/secret 

Where:

  • refreshInterval: The amount of time before the values are read again

  • secretStoreRef: Reference to the SecretStore that was created earlier, in case of ClusterSecretStore set the Kind to ClusterSecretStore

  • target: Name of the K8s secret to create.

  • secretKey: The key of the secret that will be created locally in the k8s cluster.

  • key: Full path to the secret in Akeyless

Apply the configuration:

kubectl apply -f externalsecret.yaml

Getting the K8s secret:

kubectl get secret akeyless-secret-to-create -o jsonpath='{.data.secretKey}' | base64 -d

Using DataFrom

DataFrom can be used to get a secret as a JSON string and attempt to parse it, where each key will be used as the secret key in the K8s Secret:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: akeyless-external-secret-example-json
spec:
  refreshInterval: 1h

  secretStoreRef:
    kind: SecretStore 
    name: akeyless-secret-store 

  target:
    name: akeyless-secret-to-create-json 
    creationPolicy: Owner

  dataFrom:
  - extract:
      key: /path/to/your/secret/keyname 

Where:

  • refreshInterval: The amount of time before the values are read again

  • secretStoreRef: Reference to the SecretStore.

  • target: Name of the K8s secret to create.

  • key: Full path to the secret in Akeyless

Getting the K8s secret:

kubectl get secret akeyless-secret-to-create-json -o jsonpath='{.data}'

Working with Certificates

Another example is when working with Akeyless Certificate, the certificate item contains two separate PEM blocks, the actual Certificate and the Private Key, to split them into different keys you can configure the resource accordingly:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: akeyless-external-secret-example
spec:
  refreshInterval: 1h

  secretStoreRef:
    kind: SecretStore 
    name: akeyless-secret-store 

  target:
    name: akeyless-secret-to-create 
    creationPolicy: Owner

  data:
    - secretKey: tls.crt
      remoteRef:
        key: /path/to/your/secret 
        property: certificate_pem

    - secretKey: tls.key
      remoteRef:
        key: /path/to/your/secret 
        property: private_key_pem

Where:

  • refreshInterval: The amount of time before the values are read again

  • secretStoreRef: Reference to the SecretStore.

  • target: Name of the K8s secret to create.

  • secretKey: The Secret keys that will be created.

  • key: Full path to the secret in Akeyless

  • Property: The existing keys of the secret as stored in Akeyless.

Apply the configuration:

kubectl apply -f externalsecret.yaml

Getting the K8s secret:

kubectl get secret akeyless-secret-to-create -o jsonpath='{.data.tls\.crt}' | base64 -d 
kubectl get secret akeyless-secret-to-create -o jsonpath='{.data.tls\.key}' | base64 -d 

ClusterSecretStore

The ClusterSecretStore is cluster-wide and can be accessed by ExternalSecrets from any namespace, offering centralized secret management:

👍

Note

The namespace value is required in the secretRef section.

Set the ClusterSecretStore resource:

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: akeyless-cluster-secret-store
spec:
  provider:
    akeyless:
      akeylessGWApiURL: "https://api.akeyless.io"
      authSecretRef:
        secretRef:
          accessID:
            name: akeyless-secret-creds
            key: accessId
            namespace: <namespace>
          accessType:
            name: akeyless-secret-creds
            key: accessType
            namespace: <namespace>
          accessTypeParam:
            name: akeyless-secret-creds
            key: accessTypeParam
            namespace: <namespace>

Where:

  • akeylessGWApiURL: The URL of your Gateway API v2 endpoint: https://Your-Gateway-URL:8000/api/v2 (or using your gateway url at port 8081).

  • authSecretRef: Reference to the K8s Secret that holds the authentication details, in our example akeyless-secret-creds.

Run the following command to create the ClusterSecretStore resource:

kubectl apply -f clustersecretstore.yaml

Push Secret

The PushSecret resource is namespaced and is used to push secrets from your K8s Cluster to Akeyless.

Let's create a local K8s secret in the cluster, which will then be pushed to Akeyless:

kubectl create secret generic --from-literal=cache-pass=mypassword k8s-created-secret

Upon successful secret creation, a K8s secret named k8s-created-secret will be created in your cluster.

Next, we will create the PushSecret resource, which will be used to push the k8s-created-secret K8s Secret, to Akeyless:

apiVersion: external-secrets.io/v1alpha1
kind: PushSecret
metadata:
  name: push-secret
spec:
  refreshInterval: 5s
  updatePolicy: Replace
  deletionPolicy: Delete
  secretStoreRefs:
    - name: akeyless-secret-store
      kind: SecretStore
  selector:
    secret:
      name: k8s-created-secret
  data:
    - match:
        remoteRef:
          remoteKey: eso-created/my-secret

Where:

  • refreshInterval: The amount of time before the values are read again

  • secretStoreRef: Reference to the SecretStore

  • updatePolicy: Policy to overwrite existing secrets in the provider on sync

  • deletePolicy: The provider secret will be deleted if the PushSecret is deleted

  • remoteKey The location within the provider where the secret will be stored

Apply the configuration:

kubectl apply -f pushsecret.yaml

Upon successful execution, a secret named k8s-created-secret will be created in Akeyless, with the value of cache-pass=mypassword

Tutorial

Check out our tutorial video on Sync Secrets to K8s with External Secrets Operator (ESO).