Skip to content

Kubernetes Secret-Based Bearer Tokens

This guide shows how to store API keys in Kubernetes Secrets and have Riptides deliver them transparently to workloads as Authorization: Bearer headers — no application code changes required.

Many external APIs (LLM providers, SaaS platforms, internal services) authenticate requests using bearer tokens passed in the Authorization header. Traditionally, applications read these tokens from environment variables or mounted files. With Riptides, you can instead:

  1. Store the API key in a standard Kubernetes Secret.
  2. Create a CredentialSource that references the Secret.
  3. Create a CredentialBinding that tells the kernel which outbound traffic should receive the token.
  4. The Riptides kernel automatically adds the Authorization: Bearer <token> header to matching requests.

Your application code makes plain HTTP/HTTPS requests to the API. The kernel intercepts them and injects the credential before the request leaves the node.


  • A running Riptides control plane with daemons deployed
  • kubectl access to the riptides-system namespace
  • An API key for the external service you want to call

Store your API key in a standard Kubernetes Secret in the riptides-system namespace.

Terminal window
kubectl create secret generic my-api-key \
--from-literal=token='<your-api-key>' \
-n riptides-system

Or as YAML:

apiVersion: v1
kind: Secret
metadata:
name: my-api-key
namespace: riptides-system
type: Opaque
stringData:
token: "<your-api-key>"
Terminal window
riptides-cli ctl apply -f secret.yaml

Security note: The Secret lives in the riptides-system namespace, not in your application namespace. Access is mediated through CredentialSource and CredentialBinding resources, so the workload never directly accesses the Secret.


The CredentialSource tells Riptides where to find the credential and what type it is.

apiVersion: core.riptides.io/v1alpha1
kind: CredentialSource
metadata:
name: my-api-bearer
namespace: riptides-system
spec:
kubernetes:
secretRef:
key: token
name: my-api-key
type: BearerToken

Field reference:

FieldDescription
secretRef.nameName of the Kubernetes Secret.
secretRef.keyKey within the Secret that holds the token value.
typeCredential type. BearerToken means Riptides will inject it as an Authorization: Bearer header.
Terminal window
riptides-cli ctl apply -f credentialsource.yaml

Verify:

Terminal window
riptides-cli ctl get credentialsource my-api-bearer
# STATE should show AVAILABLE

Define a Riptides Service for the external API endpoint. This tells Riptides which destination addresses should be considered part of this service.

apiVersion: core.riptides.io/v1alpha1
kind: Service
metadata:
name: my-api-svc
namespace: riptides-system
spec:
addresses:
- address: api.example.com
port: 443
tags:
- my-api
external: true
labels:
api: my-api

Key fields:

  • external: true: Marks this as an external service (not running inside the cluster).
  • labels: Used by CredentialBinding injection selectors and WorkloadIdentity egress selectors to match traffic to this service.
Terminal window
riptides-cli ctl apply -f service.yaml

The CredentialBinding connects the CredentialSource to a specific workload and defines how the credential is delivered.

apiVersion: core.riptides.io/v1alpha1
kind: CredentialBinding
metadata:
name: my-workload-api-access
namespace: riptides-system
spec:
credentialSource: my-api-bearer
propagation:
injection:
selectors:
- api: my-api
workloadID: "my-namespace/app/my-workload"

How injection works:

  • The selectors match against Service labels. The daemon evaluates the connection context and determines which services should receive the credential. When the workload makes an outbound connection to a Service with label api: my-api, the kernel injects the Authorization: Bearer <token> header.
  • The workload does not need to know about the token at all. It simply makes requests to https://api.example.com.
Terminal window
riptides-cli ctl apply -f credentialbinding.yaml

Verify:

Terminal window
riptides-cli ctl get credentialbinding my-workload-api-access
# STATE should show OK

Step 5: Create a WorkloadIdentity with Egress

Section titled “Step 5: Create a WorkloadIdentity with Egress”

The WorkloadIdentity assigns a SPIFFE identity to your workload and enables TLS intercept for outbound connections. The CredentialBinding created in the previous step controls which services receive the credential via its propagation.injection.selectors.

apiVersion: core.riptides.io/v1alpha1
kind: WorkloadIdentity
metadata:
name: my-workload
namespace: riptides-system
spec:
connection:
tls:
mode: PERMISSIVE
intercept: true
scope:
daemonGroup:
id: "<cluster>/<daemongroup-path>"
selectors:
- k8s:label:app: my-workload
k8s:pod:namespace: my-namespace
process:name: node
workloadID: "my-namespace/app/my-workload"

Key fields:

  • connection.tls.intercept: true: The kernel intercepts outbound connections and handles TLS + credential injection.
  • selectors: Identifies which processes on which pods receive this identity.
Terminal window
riptides-cli ctl apply -f workloadidentity.yaml

Here is a full working example for calling an LLM provider’s API.

apiVersion: v1
kind: Secret
metadata:
name: llm-api-key
namespace: riptides-system
type: Opaque
stringData:
token: "<your-llm-api-key>"
apiVersion: core.riptides.io/v1alpha1
kind: CredentialSource
metadata:
name: llm-bearer
namespace: riptides-system
spec:
kubernetes:
secretRef:
key: token
name: llm-api-key
type: BearerToken
apiVersion: core.riptides.io/v1alpha1
kind: Service
metadata:
name: llm-api-svc
namespace: riptides-system
spec:
addresses:
- address: api.llm-provider.com
port: 443
external: true
labels:
api: llm-provider
apiVersion: core.riptides.io/v1alpha1
kind: CredentialBinding
metadata:
name: assistant-llm-access
namespace: riptides-system
spec:
credentialSource: llm-bearer
propagation:
injection:
selectors:
- api: llm-provider
workloadID: "my-namespace/app/assistant-api"
apiVersion: core.riptides.io/v1alpha1
kind: WorkloadIdentity
metadata:
name: assistant-api
namespace: riptides-system
spec:
connection:
tls:
mode: PERMISSIVE
intercept: true
scope:
daemonGroup:
id: "<cluster>/<daemongroup-path>"
selectors:
- k8s:label:app: assistant
k8s:pod:namespace: my-namespace
process:name: node
workloadID: "my-namespace/app/assistant-api"
Terminal window
riptides-cli ctl apply -f secret.yaml
riptides-cli ctl apply -f credentialsource.yaml
riptides-cli ctl apply -f service.yaml
riptides-cli ctl apply -f credentialbinding.yaml
riptides-cli ctl apply -f workloadidentity.yaml

Your application can now call https://api.llm-provider.com/v1/messages without setting any Authorization header. The Riptides kernel adds it automatically.


A single workload can access multiple external APIs. Create a separate CredentialSource, Service, and CredentialBinding for each API — each CredentialBinding specifies its own propagation.injection.selectors to control which service it targets. The WorkloadIdentity only needs connection.tls.intercept: true set once at the top level.


  1. CredentialSource is AVAILABLE:

    Terminal window
    riptides-cli ctl get credentialsource my-api-bearer
  2. CredentialBinding is OK:

    Terminal window
    riptides-cli ctl get credentialbinding my-workload-api-access
  3. WorkloadIdentity is assigned:

    Terminal window
    riptides-cli ctl get workloadidentity my-workload
  4. Make a request from your workload to the external API. The response should succeed without your application setting any authentication headers.