Skip to content

Secretless AWS Access

This guide shows how to access AWS services (such as Amazon Bedrock) from your workloads without storing or distributing any AWS credentials. Riptides federates workload identity to AWS via OIDC, and the kernel module automatically signs outbound requests with AWS SigV4 — your application makes plain HTTP calls.

Traditional approaches to AWS access require distributing long-lived IAM access keys or mounting service account tokens. Both create secrets that must be rotated, stored securely, and audited.

With Riptides, the flow is:

  1. The Riptides control plane acts as an OIDC identity provider
  2. AWS IAM is configured to trust this OIDC provider
  3. A CredentialSource references the IAM role to assume
  4. A CredentialBinding binds the credential to your workload’s identity
  5. The kernel module intercepts outbound HTTP requests to AWS and attaches SigV4 signatures automatically

Your application never sees or handles AWS credentials.

  • A Riptides control plane with an OIDC endpoint (e.g., https://cp.example.com/oidc)
  • An AWS account with permissions to create IAM roles and OIDC identity providers
  • A workload deployed on a Kubernetes cluster with the Riptides daemon installed
  • kubectl configured with access to the riptides-system namespace
  • The oidc-login kubectl plugin (kubelogin) for control plane authentication. See Install the oidc-login Plugin.

In the AWS Console or via CLI, register the Riptides control plane as an OIDC identity provider.

AWS Console:

  1. Navigate to IAM > Identity providers > Add provider
  2. Select OpenID Connect
  3. Set the Provider URL to your Riptides control plane OIDC URL (e.g., https://<your-env-id>.console.riptides.io/oidc)
  4. Set the Audience to sts.amazonaws.com
  5. Click Add provider

AWS CLI:

Note: Riptides uses a Let’s Encrypt certificate, so AWS will validate the OIDC provider against its trusted CA bundle and --thumbprint-list is not required.

Terminal window
aws iam create-open-id-connect-provider \
--url https://<your-env-id>.console.riptides.io/oidc \
--client-id-list sts.amazonaws.com

Create an IAM role that AWS workloads will assume. The trust policy restricts assumption to a specific SPIFFE ID, ensuring only the intended workload can obtain credentials.

AWS Console:

  1. Navigate to IAM > Roles > Create role
  2. Select Custom trust policy
  3. Apply the below mentioned json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/<your-env-id>.console.riptides.io/oidc"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"<your-env-id>.console.riptides.io/oidc:aud": "sts.amazonaws.com"
}
}
}
]
}

AWS CLI:

Save the above JSON to trust-policy.json, replacing the account ID and <your-env-id> placeholders:

Terminal window
aws iam create-role \
--role-name my-app-bedrock-role \
--assume-role-policy-document file://trust-policy.json

Attach the appropriate permissions policy for your use case. For example, to access Amazon Bedrock:

AWS Console:

  1. Select the created Role
  2. Add permissions > Create inline policy
  3. Policy editor > JSON
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream",
"bedrock:InvokeAgent"
],
"Resource": "*"
}
]
}

AWS CLI:

Save the above JSON to bedrock-policy.json, replacing my-app-bedrock-role with your role name:

Terminal window
aws iam put-role-policy \
--role-name my-app-bedrock-role \
--policy-name bedrock-access \
--policy-document file://bedrock-policy.json

The WorkloadIdentity must exist before other resources that reference its workloadID. It assigns a SPIFFE identity to the workload and enables TLS intercept for outbound connections so the kernel can inject credentials.

First, find the daemon running on the host:

Terminal window
riptides-cli ctl get daemons

Retrieve its ID:

Terminal window
riptides-cli ctl get daemon <daemon-name> -o jsonpath='{.spec.workloadID}'

Use that value in the scope.daemon.id field below:

Terminal window
riptides-cli ctl apply -f - <<EOF
apiVersion: core.riptides.io/v1alpha1
kind: WorkloadIdentity
metadata:
name: my-app
namespace: riptides-system
spec:
connection:
tls:
mode: PERMISSIVE
intercept: true
scope:
daemon:
id: "<daemon-id>"
selectors:
- k8s:label:app: my-app
k8s:pod:namespace: my-app
process:name: python3
workloadID: my-app/workload
EOF

Key field:

  • connection.tls.intercept: true: The kernel intercepts outbound TLS connections and handles credential injection. Required for the injection propagation mode to work.

The CredentialSource tells Riptides how to obtain AWS credentials. For AWS, it references the IAM role ARN. Riptides uses the workload’s SPIFFE-based JWT-SVID to call sts:AssumeRoleWithWebIdentity and obtain temporary credentials, which are automatically rotated before expiration.

Terminal window
riptides-cli ctl apply -f - <<EOF
apiVersion: core.riptides.io/v1alpha1
kind: CredentialSource
metadata:
name: my-app-aws-creds
namespace: riptides-system
spec:
aws:
roleArn: arn:aws:iam::123456789012:role/<replace with the created identity provider name>
EOF

Verify:

Terminal window
riptides-cli ctl describe credentialsource my-app-aws-creds
# STATE should show AVAILABLE

Define a Riptides Service resource for the AWS API endpoint your workload needs to reach.

Terminal window
riptides-cli ctl apply -f - <<EOF
apiVersion: core.riptides.io/v1alpha1
kind: Service
metadata:
name: bedrock-runtime-svc
namespace: riptides-system
spec:
addresses:
- address: bedrock-agent-runtime.us-east-1.amazonaws.com
port: 443
external: true
labels:
app: my-app
service: bedrock-runtime
EOF

Key fields:

  • external: true: Indicates this is an external service outside the user’s environment.
  • labels: Used by CredentialBinding injection selectors to match this Service.

The CredentialBinding connects the CredentialSource to your workload and configures how the credential is delivered.

Terminal window
riptides-cli ctl apply -f - <<EOF
apiVersion: core.riptides.io/v1alpha1
kind: CredentialBinding
metadata:
name: my-app-aws-binding
namespace: riptides-system
spec:
workloadID: my-app/workload
credentialSource: my-app-aws-creds
propagation:
injection:
selectors:
- app: my-app
service: bedrock-runtime
EOF

Verify:

Terminal window
riptides-cli ctl describe credentialbinding my-app-aws-binding
# STATE should show OK

When the workload makes an outbound HTTPS request to the AWS endpoint, the kernel intercepts the connection and injects the AWS credentials. The workload does not handle any credentials.

Because tls.intercept: true causes the kernel to terminate and re-originate TLS, processes need to trust the Riptides CA. The daemon automatically merges the intercept CA into the OS trust store, so on most hosts no extra configuration is needed.

In containers or environments where the OS trust store is not updated by the daemon, set the appropriate CA bundle environment variable for your runtime:

Terminal window
# For the AWS SDK
export AWS_CA_BUNDLE=/sys/module/riptides/certs/ca-certificates.crt
# 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

See Trusting the Riptides CA for the full list of runtime-specific environment variables.

Step 7: Make Plain HTTP Calls from Your Application

Section titled “Step 7: Make Plain HTTP Calls from Your Application”

Your application makes standard HTTP requests to the AWS API. The kernel handles TLS and SigV4 signing transparently.

import requests
# No AWS SDK needed -- no credentials in the application
response = requests.post(
"https://bedrock-agent-runtime.us-east-1.amazonaws.com/agents/AGENT123/agentAliases/ALIAS456/sessions/session-001/text",
json={
"inputText": "What is the status of my order?"
},
headers={
"Content-Type": "application/json"
}
)
print(response.json())

Under the hood:

Credential provisioning (happens when you apply the CredentialBinding):

  1. The control plane authenticates to AWS STS using the workload’s SPIFFE JWT-SVID via AssumeRoleWithWebIdentity.
  2. AWS STS returns temporary credentials (access key, secret key, session token).
  3. The control plane pushes the credentials to the daemon, which loads them into the kernel module.
  4. Riptides automatically refreshes credentials before they expire.

At connection time:

  1. The application opens a connection to bedrock-agent-runtime.us-east-1.amazonaws.com:443.
  2. The daemon evaluates the connection context and sets a reference to the appropriate credential in the evaluation context.
  3. The kernel module intercepts the connection, checks the evaluation context for a credential reference, and signs the HTTP request with AWS SigV4.
  4. The signed request is forwarded to the AWS API over TLS.

How It Works: The OIDC Federation Flow (Reference)

Section titled “How It Works: The OIDC Federation Flow (Reference)”

Phase 1 — Credential provisioning (triggered by CredentialBinding creation):

Riptides Control Plane
|
|-- (1) Workload has SPIFFE ID: spiffe://example.com/my-app/workload
|-- (2) Control plane presents workload's JWT-SVID to AWS STS AssumeRoleWithWebIdentity
|-- (3) AWS validates the JWT against the Riptides OIDC endpoint
|-- (4) AWS returns temporary credentials (access key, secret key, session token)
|-- (5) Control plane pushes credentials to daemon → daemon loads them into kernel module
|-- (6) Credentials are automatically refreshed before expiration

Phase 2 — Connection-time injection:

Workload (plain HTTP)
|
v
Riptides Kernel Module (intercepts outbound connection)
|
|-- (1) Daemon evaluates connection context
|-- (2) Daemon determines credential to inject; sets reference in eval context
|-- (3) Kernel checks eval context for credential reference
|-- (4) Kernel signs the HTTP request with SigV4 using the referenced credentials
|
v
AWS API (e.g., Bedrock)

The temporary credentials are automatically rotated before they expire. Your application is never aware that AWS credentials exist.

SymptomLikely CauseFix
CredentialSource stuck in PENDINGOIDC provider not reachable from AWSVerify the Riptides control plane URL is publicly accessible and the OIDC discovery endpoint returns valid metadata
CredentialBinding state is not OKMismatched workloadID between CredentialBinding and WorkloadIdentityEnsure both resources use the same workloadID value
Requests to AWS time outMissing or incorrect Service addressConfirm the Service address matches the actual AWS endpoint hostname for your region