This guide provides a streamlined walkthrough for configuring OIDC-based S3 federation with Azure Entra ID on VAST (version 5.4+).
STS Primer: Temporary Credentials for S3 Access
What is STS?
Security Token Service (STS) is an AWS-compatible API that issues temporary, limited-privilege credentials. Instead of distributing long-lived access keys, applications exchange an identity token (JWT) for short-lived credentials that automatically expire.
VAST implements the AssumeRoleWithWebIdentity STS action, which accepts a JWT from an OIDC-compliant identity provider (e.g., Azure Entra ID, Keycloak, Okta) and returns temporary S3 credentials.
Why Customers Request STS
Driver | Problem with Long-Lived Keys | How STS Helps |
|---|---|---|
Security/Compliance | Static keys can be leaked, shared, or forgotten | Credentials expire automatically (15 min – 12 hours) |
Audit Requirements | Hard to trace which user performed an action | Each session tied to authenticated identity via JWT claims |
Key Rotation Burden | Manual rotation is error-prone and disruptive | No rotation needed - credentials are ephemeral |
Least Privilege | Broad keys often over-permissioned | Scoped to specific IAM role and identity policy |
Typical triggers: SOC 2 audits, infosec reviews, cloud security posture assessments, or migration from on-prem to hybrid environments.
Core Concepts
OIDC Provider: The external identity system (Azure Entra ID, Keycloak, etc.) that authenticates users and issues JWT tokens. VAST fetches the provider's public keys to verify token signatures.
Trust Policy: A JSON document attached to an IAM role that specifies who can assume the role. It matches JWT claims (iss, aud, optionally sub) against expected values.
Identity Policy: A JSON document that specifies what the assumed role can do (e.g., s3:GetObject, s3:PutObject on specific buckets).
IAM Role: Links a trust policy (authentication) to identity policies (authorization). Users don't get S3 keys directly - they assume a role.
Authentication Flow
Credential Lifecycle
There are two independent expiration timers that applications must track:
Credential | Issuer | Typical Lifetime | Indicated By |
|---|---|---|---|
JWT ( | OIDC Provider (Azure/Keycloak) | ~1 hour |
|
STS temp credentials | VAST | 15 min – 12 hours |
|
Application responsibility:
Before calling STS: check if JWT is expired (compare
expclaim to current time)Before calling S3: check if STS credentials are expired (compare
Expirationto current time)
Credential Renewal Flow:
Step-by-step:
User authenticates with OIDC provider → receives JWT (valid ~1 hour)
Application calls
AssumeRoleWithWebIdentitywith JWT → receives temp creds (valid 15 min)The application uses temp creds for S3 operations
When temp creds expire, the application calls STS again with the same JWT → receives fresh temp creds
Repeat step 4 as long as JWT is valid
When JWT expires, STS call returns
ExpiredTokenerrorThe application must re-authenticate with OIDC provider to get a new JWT
Error messages from VAST:
Error Code | HTTP Status | Meaning |
|---|---|---|
| 400 | JWT |
| 400 | JWT signature invalid, malformed, or claims don't match trust policy |
How to check JWT expiration (client-side):
# Decode JWT and check exp claim
cat /tmp/jwt_token.txt | cut -d'.' -f2 | base64 -d 2>/dev/null | jq '.exp'
# Compare to current time
date +%s
# If current_time >= exp, JWT is expired → re-authenticateKey Points for the field
Credentials are tenant-scoped: STS credentials only work against VIPs assigned to that tenant.
JWT lifetime ≠ credential lifetime: JWTs typically last ~1 hour; STS credentials can be 15 minutes to 12 hours (configurable per role).
Verify the JWT using tools like
No key management overhead: Unlike static S3 keys, credentials self-expire.
Prerequisites
VAST cluster running version 5.4+
Azure Entra ID tenant with permissions to create App Registrations
Azure CLI (
az) installedvcli access to the VAST cluster
AWS CLI installed on the client machine
jqinstalled for JSON parsing
Configuration Variables
Step 1: Create Azure Entra ID App Registration
Why: Azure Entra ID acts as the identity provider (IdP). The app registration defines your application and allows users to authenticate and receive JWT tokens that VAST can verify.
1.1 Login to Azure
az loginValidate: You should see your subscription details and be returned to the command prompt.
1.2 Get your Azure Tenant ID
az account show --query tenantId -o tsvValidate: Output is a GUID like xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. Save this as AZURE_TENANT_ID.
1.3 Create the App Registration
az ad app create \
--display-name "vast-oidc-federation" \
--sign-in-audience "AzureADMyOrg" \
--enable-id-token-issuance true \
--enable-access-token-issuance trueValidate: Output shows JSON with "displayName": "vast-oidc-federation".
1.4 Get the Application (Client) ID
az ad app list --display-name "vast-oidc-federation" --query "[0].appId" -o tsvValidate: Output is a GUID. Save this as AZURE_APP_ID.
1.5 Create a Service Principal
az ad sp create --id <AZURE_APP_ID>Why: Required for users to authenticate against the app.
Validate: Output shows JSON with "appId" matching your AZURE_APP_ID.
Set these variables at the start - they'll be used throughout:
# Azure Configuration
AZURE_TENANT_ID="<your-azure-tenant-id>"
AZURE_APP_ID="<your-app-client-id>"
# VAST Configuration
VAST_TENANT_NAME="<your-vast-tenant-name>"
VAST_TENANT_ID=<your-vast-tenant-id>
VAST_VIP="<your-vast-vip-address>"
VAST_POLICY_ID=<your-view-policy-id>
# Names
OIDC_PROVIDER_NAME="entra-id-v1"
IAM_ROLE_NAME="oidc-s3-role"
IDENTITY_POLICY_NAME="s3-full-access"
BUCKET_NAME="mybucket"1.6 Enable Public Client Flows
az ad app update --id <AZURE_APP_ID> --is-fallback-public-client trueWhy: Required for device code flow (no client secret needed).
Validate: No output on success. Verify with:
az ad app show --id <AZURE_APP_ID> --query "isFallbackPublicClient"Expected: true
1.7 Grant OpenID Permissions
#
az ad app permission add \
--id <AZURE_APP_ID> \
--api 00000003-0000-0000-c000-000000000000 \
--api-permissions e1fe6dd8-ba31-4d61-89e7-88639da4683d=ScopeGUID | Meaning |
|---|---|
| Microsoft Graph API identifier |
| The |
Why: Grants the openid scope so the app can request ID tokens.
About these GUIDs (these are fixed Microsoft values, the same for all Azure tenants):
Reference: Microsoft Graph permission IDs
Validate: No output on success.
1.8 Admin Consent (if you have admin rights)
az ad app permission admin-consent --id <AZURE_APP_ID>Note: If you're not a tenant admin, skip this - users will be prompted to consent on first login.
Validate: No output on success.
1.9 Record the Discovery URL
https://login.microsoftonline.com/<AZURE_TENANT_ID>/v2.0/.well-known/openid-configurationValidate: Open this URL in a browser - you should see a JSON document with keys like issuer, authorization_endpoint, jwks_uri.
Step 2: Create OIDC Provider on VAST
Why: VAST needs to know about the identity provider to validate JWT tokens. The discovery URL tells VAST where to fetch the provider's public keys (JWKS) for signature verification.
2.1 Create the OIDC Provider
vcli: admin> oidc create \
--name "entra-id-v1" \
--discovery-url "https://login.microsoftonline.com/<AZURE_TENANT_ID>/v2.0/.well-known/openid-configuration" \
--user-jwt-attribute "email"Why --user-jwt-attribute "email": This tells VAST which JWT claim to use for identifying the user. The email claim from Azure tokens maps users to VAST identities.
Validate: Command returns successfully with provider details.
2.2 Verify Provider State
vcli: admin> oidc list
Validate: Look for your provider in the output. Check:
Name=entra-id-v1// the name you put in earlier for the OIDCState=OK
If State is not OK (e.g., PROVIDER_ACCESS_FAILURE):
vcli: admin> oidc refresh_keys --id <OIDC_ID>Common causes of PROVIDER_ACCESS_FAILURE:
VAST cluster cannot reach
login.microsoftonline.com(firewall/DNS)Discovery URL is incorrect (typo in tenant ID)
2.3 Note the OIDC Provider ID
From the oidc list output, note the ID column value. You'll need this for Step 3.
Step 3: Associate OIDC Provider with Tenant
Why: The OIDC provider must be linked to a VAST tenant so that users authenticating via this provider can access resources in that tenant. Without this association, STS requests will fail.
3.1 Open Tenant Settings in Web UI

Navigate to VAST Web UI → User Management → Tenants
Right-click your tenant → Edit
3.2 Associate the OIDC Provider
Under Providers And Users Access, find the OIDC dropdown
Select your provider (
entra-id-v1)Click Update
3.3 Verify the Association
vcli: admin> tenant list
vcli: admin> tenant show --id 10
+-----------------------------------------------------+-----------------------------------------------------------------------+
| ID | 10 |
| Name | bgolliher-sts |
| Enable privileged domain user restore access | True |
... snip ...
| Active Directory provider ID | 1 |
| Open LDAP provider ID | None |
| NIS provider ID | None |
| Local provider ID | 1 |
| Kerberos provider ID | None |
| OIDC provider ID | 3 |
| Encryption Group | |
... snip ...
| Data-engine-enabled | False |
| VIP Pools IP ranges | ['172.200.201.70 - 172.200.201.72', '172.200.201.7 - 172.200.201.10'] |
| Access IP Ranges | [] |
| QoS | None |
| Tenant Admins Group Name | |
| App Users Group Name | |
| Allowed NFSv4 File Delegations | READ_WRITE |
| Grant Unrequested NFSv4 File Delegations by Default | True |
| SMB Encryption State | OFF |
+-----------------------------------------------------+-----------------------------------------------------------------------+
vcli: admin>Validate: Look for your tenant row and check:
The
OIDC provider IDcolumn shows the ID from Step 2.3If this column is empty or shows a different ID, repeat Step 3.2
Step 4: Create Identity Policy
Why: The identity policy defines what S3 actions the IAM role can perform. This is the authorization layer - it answers "what can this role do?"
4.1 Create the Identity Policy
vcli: admin> identitypolicy create \
--name "s3-full-access" \
--tenant-id <VAST_TENANT_ID> \
--policy '{"Version":"2012-10-17","Statement":[{"Sid":"FullS3Access","Effect":"Allow","Action":"s3:*","Resource":"*"}]}'About the policy JSON:
Field | Value | Meaning |
|---|---|---|
|
| AWS policy language version (required, always this value) |
|
| Statement ID - a label for this rule (mainly for readability) |
|
| Whether to |
|
| Which S3 operations are permitted ( |
|
| Which buckets/objects this applies to ( |
Validate: Command returns successfully with policy details.
4.2 Verify and Note the Policy ID
vcli: admin> identitypolicy list --tenant-id <VAST_TENANT_ID>Validate: Your policy appears in the list. Note the ID column value - you'll need this for Step 6.
Step 5: Create IAM Role with Trust Policy
Why: The IAM role links authentication (who can assume it) to authorization (what it can do). It connects the OIDC provider to the identity policy.
5.1 Understand the Trust Policy
The trust policy specifies who can assume this role. It must match the JWT claims from your OIDC provider:
Trust Policy Field | Must Match JWT Claim | Example Value |
|---|---|---|
|
|
|
|
|
|
Important: The Principal.Federated value is the iss claim with <https://> removed and oidc-provider/ prepended.
5.2 Create the IAM Role
vcli: admin> iamrole create \
--name "oidc-s3-role" \
--tenant-id <VAST_TENANT_ID> \
--description "Role for OIDC federated S3 access" \
--identity-policies-ids <IDENTITY_POLICY_ID> \
--trust-policy '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Federated":"oidc-provider/login.microsoftonline.com/<AZURE_TENANT_ID>/v2.0"},"Action":"sts:AssumeRoleWithWebIdentity","Condition":{"StringEquals":{"login.microsoftonline.com/<AZURE_TENANT_ID>/v2.0:aud":"<AZURE_APP_ID>"}}}]}'Validate: Command returns successfully with role details.
5.3 Verify the Role Configuration
vcli: admin> iamrole listValidate: Your role appears. Note the ID column value.
To see full details, including the attached identity policy:
vcli: admin> iamrole show --id <ROLE_ID> --identity-policiesValidate: The output shows your identity policy attached to the role.
vcli: admin> iamrole show --id 7 --identity-policies
+----+------------------+
| Id | Name |
+----+------------------+
| 21 | S3ReadOnlyPolicy |
+----+------------------+
vcli: admin> iamrole show --id 8 --identity-policies
+----+--------------------+
| Id | Name |
+----+--------------------+
| 23 | s3-sts-full-access |
+----+--------------------+
vcli: admin>Step 6: Create S3 Bucket (View)
Why: VAST requires an S3 view (bucket) to exist before you can PUT/GET objects. In VAST, an S3 bucket is backed by a "view" - a logical mapping to a filesystem path with protocol and access settings.
6.1 Identify the View Policy ID
You need a view policy to create a view. List existing policies:
vcli: admin> viewpolicy listValidate: Note the ID of an appropriate policy (e.g., a default policy). Save this as VIEW_POLICY_ID.
6.2 Create the View (Bucket)
vcli: admin> view create \
--path /mybucket \
--bucket mybucket \
--protocols S3 \
--policy-id <VIEW_POLICY_ID> \
--tenant-id <VAST_TENANT_ID> \
--bucket-owner <OWNER_EMAIL> \
--create-dirAbout the parameters:
Parameter | Meaning |
|---|---|
| Filesystem path where bucket data is stored |
| S3 bucket name (what clients use) |
| Enable S3 protocol access |
| View policy governing access rules |
| Which tenant owns this bucket |
| Email of the bucket owner |
| Create the directory if it doesn't exist |
Validate: Command returns successfully with view details.
6.3 Verify the View
vcli: admin> view list --tenant-id <VAST_TENANT_ID>Validate: Check:
Bucketcolumn showsmybucketProtocolscolumn includesS3Tenant IDmatches your tenant
Step 7: Authenticate and Get JWT Token
Why: To assume the IAM role, you need a valid JWT (JSON Web Token) from Azure. We use the device code flow, which allows authentication without embedding secrets in scripts - users authenticate interactively via a browser.
7.1 Request a Device Code
curl -s -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=<AZURE_APP_ID>" \
-d "scope=openid profile email" \
"https://login.microsoftonline.com/<AZURE_TENANT_ID>/oauth2/v2.0/devicecode"Validate: Response is JSON containing:
user_code- A short code likeABCD1234to enter in the browserdevice_code- A longer code used in the next step (save this)verification_uri- URL to visit (usually<https://microsoft.com/devicelogin)>message- Human-readable instructions
Example response:
{
"user_code": "ABCD1234",
"device_code": "DAQABAAEAAAAm-06...",
"verification_uri": "https://microsoft.com/devicelogin",
"message": "To sign in, use a web browser to open..."
}7.2 Authenticate in Browser
Open
<https://microsoft.com/devicelogin> in a browserEnter the
user_codefrom Step 8.1Sign in with your Azure credentials
Approve the permissions request
Validate: Browser shows "You have signed in to the vast-oidc-federation application on your device." Yours will differ based on what you call your application.
7.3 Exchange Device Code for Token
After browser authentication completes, request the token:
curl -s -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=<AZURE_APP_ID>" \
-d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
-d "device_code=<DEVICE_CODE_FROM_STEP_8.1>" \
"https://login.microsoftonline.com/<AZURE_TENANT_ID>/oauth2/v2.0/token"Validate: Response is JSON containing id_token, access_token, and other fields.
7.4 Extract and Save the ID Token
IMPORTANT: Use the id_token. The id_token contains the correct iss and aud claims that VAST validates.
From the JSON response, extract the id_token value and save it:
# If you saved the response to a variable:
echo "$TOKEN_RESPONSE" | jq -r .id_token > /tmp/jwt_token.txt7.5 Verify the Token Claims
Decode and inspect the JWT payload:
cat /tmp/jwt_token.txt | cut -d'.' -f2 | base64 -d 2>/dev/null | jq .Validate: Check these claims match your configuration:
Claim | Expected Value |
|---|---|
|
|
|
|
| Unix timestamp in the future (token not expired) |
| Your email address |
If iss or aud don't match what's in your trust policy (Step 6), the STS call will fail.
Step 8: Assume Role and Get Temporary Credentials
Why: AssumeRoleWithWebIdentity is the STS API call that exchanges your JWT for temporary S3 credentials. This is the core of the federation flow.
8.1 Call AssumeRoleWithWebIdentity
aws sts assume-role-with-web-identity \
--duration-seconds 900 \
--role-arn "arn:vast::<VAST_TENANT_NAME>:role/oidc-s3-role" \
--role-session-name "test-session" \
--web-identity-token "$(cat /tmp/jwt_token.txt)" \
--endpoint-url "https://<VAST_VIP>" \
--no-verify-sslAbout the parameters:
Parameter | Meaning |
|---|---|
| How long are credentials valid (900 = 15 minutes) |
| The IAM role to assume (format: |
| A label for this session (for audit logs) |
| The JWT from Step 8 |
| VAST VIP address (must be assigned to the tenant) |
| Skip TLS certificate verification (for self-signed certs) |
Validate: Response is JSON containing Credentials with:
AccessKeyId- Temporary access keySecretAccessKey- Temporary secret keySessionToken- Session token (required for all S3 calls)Expiration- When these credentials expire
Example successful response:
{
"Credentials": {
"AccessKeyId": "TEMP...",
"SecretAccessKey": "...",
"SessionToken": "...",
"Expiration": "2025-11-15T12:30:00Z"
},
"AssumedRoleUser": {
"AssumedRoleId": "...",
"Arn": "arn:vast::your-tenant:assumed-role/oidc-s3-role/test-session"
}
}8.2 Export Credentials for AWS CLI
To use these credentials with subsequent AWS CLI commands, export them as environment variables:
export AWS_ACCESS_KEY_ID="<AccessKeyId from response>"
export AWS_SECRET_ACCESS_KEY="<SecretAccessKey from response>"
export AWS_SESSION_TOKEN="<SessionToken from response>"Or, if you captured the response in a variable:
STS_RESPONSE=$(aws sts assume-role-with-web-identity \
--duration-seconds 900 \
--role-arn "arn:vast::<VAST_TENANT_NAME>:role/oidc-s3-role" \
--role-session-name "test-session" \
--web-identity-token "$(cat /tmp/jwt_token.txt)" \
--endpoint-url "https://<VAST_VIP>" \
--no-verify-ssl 2>/dev/null)
export AWS_ACCESS_KEY_ID=$(echo "$STS_RESPONSE" | jq -r .Credentials.AccessKeyId)
export AWS_SECRET_ACCESS_KEY=$(echo "$STS_RESPONSE" | jq -r .Credentials.SecretAccessKey)
export AWS_SESSION_TOKEN=$(echo "$STS_RESPONSE" | jq -r .Credentials.SessionToken)8.3 Verify Credentials Are Set
echo "Access Key: $AWS_ACCESS_KEY_ID"
echo "Expiration: $(echo "$STS_RESPONSE" | jq -r .Credentials.Expiration)"Validate:
AWS_ACCESS_KEY_IDis not emptyExpiration time is in the future
Common Errors at This Step
Error | Cause | Fix |
|---|---|---|
| JWT | Check Step 7.5 claims match Step 5.1 |
| JWT has expired | Get a new token (repeat Step 7) |
| Role doesn't exist or wrong tenant | Verify role name and tenant name |
Connection refused | Wrong VIP or VIP not assigned to tenant | Check Step 4 |
Step 9: Test S3 Operations
Why: Verify the complete federation flow works end-to-end by performing actual S3 operations with your temporary credentials.
Prerequisite: Ensure environment variables from Step 8.2 are exported in your current shell.
9.1 List Buckets
aws s3api list-buckets \
--endpoint-url "https://<VAST_VIP>" \
--no-verify-sslValidate: Response shows your bucket in the Buckets array.
9.2 Upload an Object (PUT)
echo "Hello from OIDC federation!" | aws s3 cp - s3://mybucket/test.txt \
--endpoint-url "https://<VAST_VIP>" \
--no-verify-sslValidate: No error output. You can verify with the next step.
9.3 List Bucket Contents
aws s3 ls s3://mybucket/ \
--endpoint-url "https://<VAST_VIP>" \
--no-verify-sslValidate: Output shows test.txt with timestamp and size.
9.4 Download an Object (GET)
aws s3 cp s3://mybucket/test.txt - \
--endpoint-url "https://<VAST_VIP>" \
--no-verify-sslValidate: Output is Hello from OIDC federation!
9.5 Test Nested Objects (Optional)
echo "Nested content" | aws s3 cp - s3://mybucket/folder/nested.txt \
--endpoint-url "https://<VAST_VIP>" \
--no-verify-ssl
aws s3 ls s3://mybucket/ --recursive \
--endpoint-url "https://<VAST_VIP>" \
--no-verify-sslValidate: Output shows both test.txt and folder/nested.txt.
Common Errors at This Step
Error | Cause | Fix |
|---|---|---|
| Credentials not set or expired | Re-export from Step 8.2, or get new STS creds |
| Identity policy doesn't allow this action | Check Step 5 policy allows the S3 action |
| Bucket doesn't exist or wrong name | Verify bucket name from Step 6 |
Success!
If all operations complete without errors, your OIDC STS federation is working correctly. The temporary credentials will expire after the duration specified in Step 9 (default: 15 minutes).
To continue working after expiration:
If JWT is still valid: Repeat Step 8 to get new STS credentials
If JWT expired: Repeat Steps 7-8 to get a new JWT and STS credentials
Minimal SDK Examples (STS + PUT)
These snippets assume /tmp/jwt_token.txt already contains a valid id_token from Step 8 and use the VAST endpoint for both STS and S3.
Python (boto3)
import boto3
# Example values – replace if different
VAST_ENDPOINT = "https://10.0.0.10"
ROLE_ARN = "arn:vast::acme-tenant:role/oidc-s3-role"
BUCKET = "demo-bucket"
OBJECT_KEY = "demo-boto3.txt"
with open("/tmp/jwt_token.txt", "r", encoding="utf-8") as f:
web_identity_token = f.read().strip()
sts = boto3.client("sts", endpoint_url=VAST_ENDPOINT, verify=False)
creds = sts.assume_role_with_web_identity(
RoleArn=ROLE_ARN,
RoleSessionName="boto3-demo",
WebIdentityToken=web_identity_token,
DurationSeconds=900,
)["Credentials"]
s3 = boto3.client(
"s3",
endpoint_url=VAST_ENDPOINT,
aws_access_key_id=creds["AccessKeyId"],
aws_secret_access_key=creds["SecretAccessKey"],
aws_session_token=creds["SessionToken"],
verify=False,
)
s3.put_object(Bucket=BUCKET, Key=OBJECT_KEY, Body=b"hello from boto3")
print(f"Uploaded {OBJECT_KEY} to {BUCKET}")Go (AWS SDK v2)
package main
import (
"context"
"crypto/tls"
"fmt"
"net/http"
"os"
"strings"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go-v2/service/sts"
)
const (
// Example values – replace if different
vastEndpoint = "https://10.0.0.10"
roleArn = "arn:vast::acme-tenant:role/oidc-s3-role"
bucketName = "demo-bucket"
objectKey = "demo-go.txt"
)
func main() {
token, err := os.ReadFile("/tmp/jwt_token.txt")
if err != nil {
panic(err)
}
// Use the VAST endpoint for all services; disable TLS verification if needed.
resolver := aws.EndpointResolverWithOptionsFunc(
func(service, region string, options ...interface{}) (aws.Endpoint, error) {
return aws.Endpoint{URL: vastEndpoint, HostnameImmutable: true}, nil
})
cfg, err := config.LoadDefaultConfig(
context.Background(),
config.WithEndpointResolverWithOptions(resolver),
config.WithHTTPClient(&http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}),
)
if err != nil {
panic(err)
}
stsClient := sts.NewFromConfig(cfg)
out, err := stsClient.AssumeRoleWithWebIdentity(context.Background(), &sts.AssumeRoleWithWebIdentityInput{
RoleArn: aws.String(roleArn),
RoleSessionName: aws.String("go-demo"),
WebIdentityToken: aws.String(string(token)),
DurationSeconds: aws.Int32(900),
})
if err != nil {
panic(err)
}
staticCreds := aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(
aws.ToString(out.Credentials.AccessKeyId),
aws.ToString(out.Credentials.SecretAccessKey),
aws.ToString(out.Credentials.SessionToken),
))
s3Client := s3.NewFromConfig(cfg, func(o *s3.Options) {
o.Credentials = staticCreds
o.UsePathStyle = true
})
_, err = s3Client.PutObject(context.Background(), &s3.PutObjectInput{
Bucket: aws.String(bucketName),
Key: aws.String(objectKey),
Body: strings.NewReader("hello from go"),
})
if err != nil {
panic(err)
}
fmt.Printf("Uploaded %s to %s\n", objectKey, bucketName)
}Verify Credential Expiration
Why: Confirm that temporary credentials properly expire after the specified duration.
# Wait for credentials to expire (15+ minutes)
sleep 960
# This should fail with ExpiredToken or InvalidAccessKeyId
aws s3 ls s3://mybucket/ \
--endpoint-url "https://<VAST_VIP>" \
--no-verify-sslExpected error:
An error occurred (ExpiredToken) when calling the ListObjectsV2 operation: The provided token has expired.Helper Script
Save this as sts_auth.sh for repeated testing:
#!/bin/bash
CLIENT_ID="<AZURE_APP_ID>"
TENANT_ID="<AZURE_TENANT_ID>"
VAST_ENDPOINT="https://<VAST_VIP>"
ROLE_ARN="arn:vast::<VAST_TENANT_NAME>:role/oidc-s3-role"
TOKEN_FILE="/tmp/jwt_token.txt"
# Check if token is expired
GET_NEW_TOKEN=false
if [ ! -f "$TOKEN_FILE" ]; then
GET_NEW_TOKEN=true
else
EXP=$(cat "$TOKEN_FILE" | cut -d'.' -f2 | base64 -d 2>/dev/null | jq -r .exp 2>/dev/null)
NOW=$(date +%s)
if [ -z "$EXP" ] || [ "$NOW" -ge "$EXP" ]; then
GET_NEW_TOKEN=true
fi
fi
if [ "$GET_NEW_TOKEN" = true ]; then
echo "=== Getting new token ===" >&2
DEVICE_RESPONSE=$(curl -s -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=${CLIENT_ID}" \
-d "scope=openid profile email" \
"https://login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/devicecode")
USER_CODE=$(echo "$DEVICE_RESPONSE" | jq -r .user_code)
DEVICE_CODE=$(echo "$DEVICE_RESPONSE" | jq -r .device_code)
echo "Go to: https://microsoft.com/devicelogin" >&2
echo "Enter code: $USER_CODE" >&2
read -p "Press Enter after completing browser login..." >&2
curl -s -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=${CLIENT_ID}" \
-d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
-d "device_code=${DEVICE_CODE}" \
"https://login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/token" | jq -r .id_token > "$TOKEN_FILE"
else
echo "=== Using cached token ===" >&2
fi
STS_RESPONSE=$(aws sts assume-role-with-web-identity \
--duration-seconds 900 \
--role-arn "$ROLE_ARN" \
--role-session-name "test-session" \
--web-identity-token "$(cat $TOKEN_FILE)" \
--endpoint-url "$VAST_ENDPOINT" \
--no-verify-ssl 2>/dev/null)
echo "export AWS_ACCESS_KEY_ID=$(echo "$STS_RESPONSE" | jq -r .Credentials.AccessKeyId)"
echo "export AWS_SECRET_ACCESS_KEY=$(echo "$STS_RESPONSE" | jq -r .Credentials.SecretAccessKey)"
echo "export AWS_SESSION_TOKEN=$(echo "$STS_RESPONSE" | jq -r .Credentials.SessionToken)"
echo "# Credentials expire: $(echo "$STS_RESPONSE" | jq -r .Credentials.Expiration)" >&2Usage:
eval $(./sts_auth.sh)
aws s3 ls s3://mybucket/ --endpoint-url "https://<VAST_VIP>" --no-verify-sslTroubleshooting
"Token verification failed" on AssumeRoleWithWebIdentity
Wrong token type: Use
id_token, notaccess_tokenIssuer mismatch: Check
issin JWT matches trust policy principalAudience mismatch: Check
audin JWT matches trust policy conditionExpired token: JWT has limited lifetime (~1 hour)
"InvalidAccessKeyId" on S3 operations
Wrong VIP: Must use a VIP assigned to the tenant
Expired credentials: STS credentials expire after
duration-secondsCredentials not exported: Run
exportcommands after assume-role
"InvalidSecurity" on CreateBucket
Policy restriction: Identity policy may not allow
s3:CreateBucketBucket exists: Bucket names are globally unique per cluster
OIDC provider shows "PROVIDER_ACCESS_FAILURE"
Verify discovery URL is reachable from VAST cluster
Refresh keys:
oidc refresh_keys --id <ID>
vcli Command Reference
Task | Command |
|---|---|
Create OIDC provider |
|
List OIDC providers |
|
Refresh OIDC keys |
|
Create identity policy |
|
Create IAM role |
|
List IAM roles |
|
Show IAM role |
|
Create S3 view |
|
Revoke role keys |
|