Skip to content

Vault / OpenBao Integration

This guide walks through integrating HashiCorp Vault (or OpenBao) with Riptides so that workloads can retrieve secrets — API keys, database passwords, cloud credentials — without ever storing Vault tokens inside the cluster.

Every workload managed by Riptides receives a SPIFFE SVID (X.509 certificate). When a workload needs a secret from Vault, Riptides presents that SVID as a JWT to Vault’s JWT auth method. Vault validates the JWT against the Riptides control plane’s OIDC discovery endpoint, confirms the workload’s SPIFFE ID matches the configured role, and issues the requested secret. No long-lived Vault tokens are stored or distributed.

The flow:

  1. The control plane reads the CredentialBinding and presents the workload’s JWT-SVID to Vault’s JWT auth method.
  2. Vault validates the JWT using the issuer URL.
  3. Vault returns the requested credential.
  4. The control plane pushes the credential to the daemon, which loads it into the kernel.
  5. The kernel injects the credential into outbound requests at connection time.
  • A running Riptides control plane
  • A Vault (or OpenBao) instance reachable from the cluster
  • kubectl access to the riptides-system namespace

Enable the JWT auth method in Vault and point it at the Riptides control plane’s OIDC discovery URL.

Terminal window
# Enable the JWT auth method at a custom path
vault auth enable -path=jwt jwt
# Configure it to use the Riptides control plane for token validation
vault write auth/jwt/config \
oidc_discovery_url="https://<your-controlplane-url>" \
default_role="<default-role-name>"

Replace <your-controlplane-url> with the address of your Riptides control plane (for example, controlplane.example.com).

Define a policy that grants read access to the secret path your workload needs.

Terminal window
vault policy write my-api-keys - <<EOF
path "secret/data/creds/my-role" {
capabilities = ["read"]
}
EOF

Bind the policy to a role that accepts JWTs from the Riptides control plane. The bound_audiences field must match the audience you configure in the CredentialSource, and bound_subject should match the workload’s SPIFFE ID.

Terminal window
vault write auth/jwt/role/my-api-keys \
role_type="jwt" \
bound_audiences="<your-vault-audience>" \
bound_subject="spiffe://<trust-domain>/<workload-path>" \
user_claim="sub" \
token_policies="my-api-keys" \
token_ttl="1h"

Step 4: Create a CredentialSource (Vault Token Type)

Section titled “Step 4: Create a CredentialSource (Vault Token Type)”

The CredentialSource tells Riptides where Vault lives and which secret to fetch. The token type is used when Vault returns a value you want to inject as a bearer token or API key.

apiVersion: core.riptides.io/v1alpha1
kind: CredentialSource
metadata:
name: vault-api-keys
namespace: riptides-system
spec:
vault:
address: "https://vault.example.com"
audience:
- "your-vault-audience"
jwtAuthMethodPath: jwt
path: "secret/creds/my-role"
role: "my-api-keys"
type:
token:
source: api_key

Field reference:

FieldDescription
addressFull URL of the Vault server.
audienceList of audiences included in the JWT presented to Vault. Must match the role’s bound_audiences.
jwtAuthMethodPathMount path of the JWT auth method in Vault.
pathVault secret path to read from.
roleVault role name to authenticate as.
type.token.sourceField name inside the Vault response to extract (e.g., api_key, token, secret).

Apply it:

Terminal window
riptides-cli ctl apply -f credentialsource-vault.yaml

Verify the source is available:

Terminal window
riptides-cli ctl get credentialsource vault-api-keys
# STATE should show AVAILABLE

Step 5: Create a Service for the Target API

Section titled “Step 5: Create a Service for the Target API”

Define an external Service so Riptides knows where the API lives. The labels on this Service will be referenced by the egress selectors and injection selectors.

apiVersion: core.riptides.io/v1alpha1
kind: Service
metadata:
name: my-api-svc
namespace: riptides-system
spec:
addresses:
- address: api.example.com
port: 443
external: true
labels:
api: my-api
Terminal window
riptides-cli ctl apply -f service-api.yaml

The CredentialBinding connects a CredentialSource to a workload and configures how the credential is delivered. You can use injection (the kernel adds the credential to outbound requests matching selector labels) and/or sysfs (credential is written to a file on the host, readable by the workload process).

apiVersion: core.riptides.io/v1alpha1
kind: CredentialBinding
metadata:
name: vault-api-keys-binding
namespace: riptides-system
spec:
credentialSource: vault-api-keys
propagation:
injection:
selectors:
- api: my-api
sysfs: {}
workloadID: "my-namespace/app/my-workload"

Propagation modes:

  • injection: The daemon evaluates the connection context and determines which services receive the credential based on the selector labels. The kernel then injects the credential (e.g., as an Authorization: Bearer header) into matching outbound requests. No code changes needed.
  • sysfs: The credential is written to a file at a deterministic path under /sys/module/riptides/credentials/. Your workload can read it directly.

When both are specified, the credential is available through either mechanism.

Terminal window
riptides-cli ctl apply -f credentialbinding-vault.yaml

After applying, check the binding status:

Terminal window
riptides-cli ctl get credentialbinding vault-api-keys-binding -o yaml

A healthy binding shows state: OK and, when sysfs is enabled, a sysfs.files section with the path to the credential file:

status:
state: OK
sysfs:
files:
- path: /sys/module/riptides/credentials/<hash>/vault-api-keys-binding/token.jwt
type: TOKEN

Step 7: Create a WorkloadIdentity with Egress

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

The WorkloadIdentity assigns a SPIFFE ID 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: Tells the kernel to intercept outbound connections and handle TLS + credential injection.
Terminal window
riptides-cli ctl apply -f workloadidentity.yaml

Because the egress rule uses tls.intercept: true, the kernel module terminates and re-originates the TLS connection. Applications that perform their own TLS certificate verification need to trust the Riptides CA.

Set the appropriate CA bundle environment variable for your application:

Terminal window
# For Python requests
export REQUESTS_CA_BUNDLE=/sys/module/riptides/certs/ca-certificates.crt
# For Node.js
export NODE_EXTRA_CA_CERTS=/sys/module/riptides/certs/ca-certificates.crt
# For cURL
export CURL_CA_BUNDLE=/sys/module/riptides/certs/ca-certificates.crt

If your application uses the injection propagation mode exclusively (the kernel injects credentials into plain HTTP requests), the kernel handles TLS entirely and no CA configuration is needed. The CA bundle is only required when the application itself initiates a TLS connection to a destination where tls.intercept: true is configured.

See Trusting the Riptides CA for the full list of supported environment variables.


In addition to the token type, Vault CredentialSources support cloud-native credential generation. These use Vault’s secrets engines to produce short-lived cloud credentials:

spec:
vault:
address: "https://vault.example.com"
audience:
- "your-vault-audience"
jwtAuthMethodPath: jwt
path: "aws/creds/my-role"
role: "my-aws-role"
type:
aws: {}

Vault’s AWS secrets engine generates temporary IAM credentials. The workload receives AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN.

spec:
vault:
address: "https://vault.example.com"
audience:
- "your-vault-audience"
jwtAuthMethodPath: jwt
path: "gcp/token/my-role"
role: "my-gcp-role"
type:
gcp: {}
spec:
vault:
address: "https://vault.example.com"
audience:
- "your-vault-audience"
jwtAuthMethodPath: jwt
path: "azure/creds/my-role"
role: "my-azure-role"
type:
azure: {}

These cloud types remove the need for static cloud credentials entirely. Vault generates short-lived credentials on demand, and Riptides delivers them transparently to the workload.


After deploying all resources, confirm the end-to-end flow:

  1. CredentialSource is AVAILABLE:

    Terminal window
    riptides-cli ctl get credentialsource vault-api-keys
  2. CredentialBinding is OK:

    Terminal window
    riptides-cli ctl get credentialbinding vault-api-keys-binding
  3. WorkloadIdentity is assigned:

    Terminal window
    riptides-cli ctl get workloadidentity my-workload
  4. Your workload can reach the API without any credential configuration in its code or environment variables. The Riptides kernel handles authentication transparently.