Architecture Overview
Xpander self-hosted uses a three-tier IAM role architecture for connector authentication:| Role | Purpose | Permissions |
|---|---|---|
| Base Role | Attached to pods via EKS Pod Identity. Has no direct permissions — only assumes connector-specific roles. | sts:AssumeRole + sts:TagSession on each connector role |
| Connector Roles | One per data source/service. Contains only the permissions needed for that connector. | Service-specific (e.g., redshift-data:*, eks:*) + self-assume |
| Cross-Account Trust | Each connector role can optionally trust the xpander platform for cloud-managed operations. | Trust with ExternalId condition |
Why Three Tiers?
- Least privilege — Each connector only has access to its specific service. A Redshift connector can’t access EKS and vice versa.
- Auditability — CloudTrail shows exactly which connector role accessed which resource. No shared credentials.
- Independent lifecycle — Add or remove connectors without touching other roles. Revoke one connector’s access without affecting others.
- Role chaining — The AI Gateway assumes the appropriate connector role per request. This is standard AWS role chaining, fully supported by xpander.
If you’re using a single IAM role for all connectors (the setup from AWS Operator Setup), the instructions on this page show how to split that into per-connector roles for better isolation and auditability.
Step 1: Base Role
The base role is the only role attached to pods. It has no service permissions — it can only assume other roles.Step 2: Connector Roles
Each connector role needs a trust policy with four statements:| Statement | Purpose | ExternalId Required? |
|---|---|---|
PodIdentity | Direct pod access (fallback) | No |
CrossAccountWithExternalId | xpander cloud platform operations | Yes |
SelfAssume | AI Gateway internal re-assume for credential refresh | No |
AllowFromBaseRole | Role chaining from the Pod Identity base role | No |
Trust Policy Template
Permission Policies Per Connector
Each connector role needs its own service permissions plus self-assume.Self-Assume + Session Tagging (required for all connector roles)
Redshift Connector
Redshift Connector
EKS Connector
EKS Connector
Step 3: Configure in xpander UI
In the connector settings, set the IAM Role ARN to the connector-specific role (not the base role):| Connector | Role ARN |
|---|---|
| Redshift | arn:aws:iam::<ACCOUNT_ID>:role/xpander-redshift-access |
| EKS | arn:aws:iam::<ACCOUNT_ID>:role/xpander-eks-connector |
Adding a New Connector
When adding a new AWS connector (e.g., S3, DynamoDB, Athena):Create the connector role
Use the trust policy template above.
Attach service-specific permissions
Add the service permissions + the self-assume policy.
Role Creation Order
IAM roles can’t reference themselves during creation. Follow this order:- Create the role with only
PodIdentity+CrossAccountWithExternalIdtrust statements - Wait 10 seconds for IAM propagation
- Update the trust policy to add
SelfAssume+AllowFromBaseRolestatements
Troubleshooting
AccessDenied: sts:TagSession
AccessDenied: sts:TagSession
AccessDenied: sts:AssumeRole (self-assume)
AccessDenied: sts:AssumeRole (self-assume)
Cause: The
SelfAssume trust statement has an ExternalId condition, or the self-assume permission policy is missing.Fix: The SelfAssume and AllowFromBaseRole statements must NOT have conditions. The AI Gateway does not pass an ExternalId during internal role operations.AccessDenied: sts:AssumeRole (from base role)
AccessDenied: sts:AssumeRole (from base role)
Cause: Base role doesn’t have permission to assume the connector role, or the connector role’s trust policy doesn’t list the base role.Fix: Check both sides:
- Base role permission policy lists the connector role ARN
- Connector role trust policy has
AllowFromBaseRolestatement
Connector works in cloud but not self-hosted
Connector works in cloud but not self-hosted
Cause: In cloud mode, the xpander platform assumes the role directly with the ExternalId. In self-hosted mode, the AI Gateway chains through the base role without an ExternalId.Fix: Add the
AllowFromBaseRole trust statement to the connector role.Security Recommendations
- Scope resources — Use specific ARNs in permission policies, not
"Resource": "*"where possible - Separate roles per data source — Don’t combine Redshift + EKS permissions in one role
- Rotate ExternalId — The organization ID is used as ExternalId. If compromised, rotate it in the xpander platform
- Monitor with CloudTrail — Each connector role appears separately in CloudTrail, making it easy to audit which connector accessed which resource
- Tag roles — Tag all roles with
Environment,Project,ManagedByfor governance - Restrict base role — The base role should ONLY have
sts:AssumeRole+sts:TagSession. Never attach service permissions directly to it

