Least Privilege in Someone Else's Cloud: Securing Vendor Access for BYOC

Product Marketing Engineer

TL;DR: A practical guide to granting scoped, auditable, time-bound access to a customer's cloud environment. Skip standing keys, prefer federated short-lived credentials, and get to the checklist below if you need to move fast.

Bring-your-own-cloud (BYOC) deployments have flipped the vendor access problem inside out.

Instead of pulling customer data into a vendor's environment, the vendor's software runs inside the customer's AWS, GCP, or Azure account.

That's better for data gravity, latency, and compliance, but it introduces a permission problem nobody had five years ago: how does a vendor operate software inside a cloud they don't own without becoming the customer's biggest security liability?

The default answer for a lot of vendors is still "give us a long-lived IAM user with the keys we need." That answer is wrong in 2025, and enterprise security teams have started rejecting it outright.

This piece walks through what to require instead, whether you're the vendor building a BYOC product or the security engineer reviewing one.

Why standing credentials keep showing up in breach reports

The pattern is familiar. A vendor asks for an IAM user with a specific policy attached. The customer creates it, copies the access key and secret into the vendor's dashboard, and forgets about it. Two years later, one of the following happens:

  • The vendor's config database is breached and every customer's keys are now in a paste on a criminal forum.

  • A vendor engineer leaves, and nobody rotates the credentials they had access to.

  • The IAM policy attached to that user has drifted. Someone added s3:* to debug an issue in 2023 and never took it back off.

  • A key ends up committed to a repo. Nobody notices because it's the vendor's repo, not the customer's.

The 2024 Snowflake customer incidents, the ongoing stream of GitHub-leaked-keys advisories from AWS, and the credential-theft patterns Mandiant tracks in its M-Trends reports all rhyme. Standing credentials are stationary targets, and cloud attackers are extremely good at finding stationary targets.

The fix isn't better key management. The fix is not having a key at all.

The three access models, ranked

Here's the landscape a vendor can choose from, worst to best.

Model

How it works

Risk profile

When it's acceptable

Long-lived service account / IAM user

Vendor stores an access key and secret indefinitely

High. Compromise = permanent access until manual rotation. Detection lag is measured in months.

Legacy integrations. Nowhere else.

Cross-account IAM role (assume-role with external ID)

Customer creates a role the vendor's account is allowed to assume. Vendor calls sts:AssumeRole to get temporary credentials.

Medium. No long-lived secrets on the vendor side, but the trust relationship is standing.

Most vendor BYOC integrations today. Reasonable default.

Federated short-lived credentials (OIDC / workload identity)

Vendor's workloads present a signed identity token from their own IdP (or from GitHub Actions, EKS, GKE, etc.). Customer's cloud validates the token and issues short-lived credentials directly.

Low. No shared secret at all. Every access is auditable back to a specific workload identity.

The target state. Especially for automated, machine-to-machine vendor access.

The mental model: standing credentials are a password you never rotate. Assume-role is a password that expires after an hour but you still have to protect the underlying secret that lets you initiate the swap. Federation is proving who you are cryptographically each time, without a shared secret in the first place.

Cross-account IAM roles: the acceptable middle ground

If you're building a BYOC product today and OIDC federation isn't on the table yet, cross-account roles are the floor. The pattern in AWS:

Customer creates a role that trusts the vendor's AWS account with a required external ID.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "AWS": "arn:aws:iam::VENDOR_ACCOUNT_ID:role/vendor-worker" },
    "Action": "sts:AssumeRole",
    "Condition": {
      "StringEquals": { "sts:ExternalId": "cust-9f3a-unique-per-tenant" }
    }
  }]
}

A few things to get right, because these are where implementations quietly break:

The external ID must be unique per customer and unguessable. Its whole job is preventing the confused deputy problem where one of your customers tricks your service into assuming a role in another customer's account. A UUID per customer works. Sequential IDs, customer names, or shared constants do not.

Scope the trust to the exact vendor role, not the whole account. The Principal should be the specific IAM role your workers use, not arn:aws:iam::VENDOR:root. If your entire account is trusted, any principal in your account can assume the customer's role. Blast radius matters.

Scope the permissions policy narrowly. The attached policy is where most BYOC implementations get sloppy. Ask for resource-level ARNs, not wildcards. Ask for the specific actions, not s3:*. If your product only reads from one bucket, the policy should name that bucket. GCP equivalent: bind the service account to the specific project and resource, not the folder or organization.

Set a session duration you actually need. The default is one hour. If your workload runs for 10 minutes, don't ask for 12.

Here's what a tight S3-reader policy looks like, versus what vendors often ask for:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["s3:GetObject", "s3:ListBucket"],
    "Resource": [
      "arn:aws:s3:::customer-data-ingress-cust9f3a",
      "arn:aws:s3:::customer-data-ingress-cust9f3a/*"
    ]
  }]
}

Compare that to s3:* on *, which is unfortunately common in vendor docs. If a vendor's onboarding guide asks for a wildcard on a service, ask them why.

Federated credentials: what to aim for

Federation via OIDC is where you want to end up. The vendor's compute (a Kubernetes pod, a GitHub Actions workflow, a CI job, an application on EKS/GKE) has a cryptographic identity. The customer's cloud is configured to trust that identity provider. No secret ever crosses the boundary.

In AWS, this is IAM Roles for Service Accounts (IRSA) if the vendor runs on EKS, or the newer EKS Pod Identity. For GitHub Actions-based access, it's the GitHub OIDC provider configured as an IAM identity provider in the customer account. In GCP, it's Workload Identity Federation. In Azure, it's Workload Identity federation for managed identities.

The setup on the customer side, in AWS, looks roughly like this. First, the customer registers the vendor's OIDC issuer:

# Register the vendor's OIDC issuer as an IAM identity provider
aws iam create-open-id-connect-provider \
  --url https://oidc.vendor.example.com \
  --client-id-list sts.amazonaws.com \
  --thumbprint-list

Then they create a role whose trust policy pins to a specific subject claim in the vendor's tokens:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::CUST_ACCOUNT:oidc-provider/oidc.vendor.example.com"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": {
        "oidc.vendor.example.com:sub": "system:serviceaccount:vendor-prod:worker-cust9f3a",
        "oidc.vendor.example.com:aud": "sts.amazonaws.com"
      }
    }
  }]
}

The sub condition is the important part. It ties the role to a single Kubernetes service account in a single namespace in the vendor's cluster. If the vendor is doing this properly, they've isolated each customer's worker into its own namespace or its own service account. Compromise of one customer's workload doesn't give you the others.

Why this is better than assume-role: there is no secret. The vendor's workload can't be phished, can't leak a key in a log, can't have credentials stolen from a laptop. The token is minted at request time, lives for minutes, and is cryptographically bound to the workload it came from.

Scoping patterns that actually hold up

Regardless of which credential model you land on, the scoping matters more than the token format. A federated credential with AdministratorAccess attached is worse than a long-lived key with s3:GetObject on one bucket.

Some patterns that tend to survive contact with real production:

One role per capability, not one role for everything. If your product does ingestion, transformation, and results write-back, that's three roles. Each is scoped to what it actually needs. This is annoying to set up and pays off the first time you have to explain to a security team why your ingestion worker can't delete the customer's logs.

Resource ARN allowlists over service wildcards. Every ARN you can name explicitly is an ARN a compromised credential can't escape to.

Deny-by-default with explicit allows. Use Deny statements to block anything sensitive that the role should never touch, even if the allow policy is misconfigured. iam:*, sts:AssumeRole on other roles, and anything touching billing or account-level settings are common candidates.

Condition keys for network scope. If your vendor workload only egresses from known IPs or through a specific VPC endpoint, use aws:SourceIp or aws:SourceVpce conditions. Even if a key leaks, it's useless outside your network.

Time-bound access for humans. If your support engineers occasionally need to run diagnostics in a customer environment, that's not a permanent role, it's a JIT (just-in-time) grant.

Tools like AWS IAM Identity Center session policies, or a broker in front of sts:AssumeRole, can enforce approval and duration. Some ZTNA products (Twingate included) can front the entire access request with identity-aware policies, session recording, and time-bound grants so that even the assume-role call requires an approved session.

Audit logging: what to actually turn on

An access model you can't audit is a hope more than it's a security control.

For AWS: CloudTrail should be enabled in every region where the vendor operates, with events flowing to an S3 bucket the vendor cannot delete from. Both the management events and, if the vendor touches S3 or Lambda, the data events. The bucket should have Object Lock or MFA-delete configured. GuardDuty should be watching for anomalous API patterns from the vendor role.

For GCP: Cloud Audit Logs, specifically Admin Activity and Data Access logs for the services the vendor uses, exported to a log sink outside the vendor's blast radius.

For Azure: Activity Log and diagnostic settings for the resources the vendor touches, routed to a Log Analytics workspace or storage account.

What you actually want to be able to answer, in under five minutes:

  • Which principal accessed which resource, when, and from where?

  • Did anything the vendor role touched fall outside its documented scope?

  • When was the vendor role last assumed, and by which workload identity?

  • Has the policy on the role changed since it was created? If so, who changed it, and was there a ticket?

The last one is the one that gets skipped, and it's the one that matters most. Policy drift is how tight roles become permissive over months.

Rotation and revocation

If you're using federated credentials, rotation is largely automatic: every token has a lifetime of minutes. The rotation you care about is the identity provider's signing keys, which the cloud validates automatically.

If you're using cross-account roles, there's no key to rotate. What you rotate is the external ID, on a schedule or after any suspected compromise. Rotating it is a coordination cost with the vendor, so a lot of shops don't.

That's a mistake. If a vendor's config store is breached and every customer's external ID leaks, the confused-deputy protection those IDs provided is gone.

If you're still using long-lived keys (and you shouldn't be) rotate them every 90 days maximum, and audit for unused keys weekly. AWS Access Analyzer can flag unused permissions and roles. Use it.

Revocation matters more than rotation. When something goes wrong, how fast can the customer cut off vendor access?

  • Cross-account role: delete the role or remove the trust relationship. Effective immediately for new sessions; existing sessions expire on their own within the session duration (which is why short session durations matter).

  • Federated OIDC role: same as above, plus remove the OIDC provider registration for a harder kill.

  • Long-lived keys: deactivate the IAM user's access keys. Existing signed requests will fail on next use.

A customer should be able to revoke a vendor's access without calling the vendor. If revocation requires a support ticket to the vendor, that's a failed access model.

What a vendor is responsible for that isn't credentials

Least privilege on the credential is table stakes. Vendors operating in a customer's cloud also carry weight for:

Network isolation of the vendor's control plane. If the vendor has a control plane that manages the customer-side workload, how does it reach in? A public endpoint the vendor authenticates against is one option, but it means the customer has to allow outbound traffic to that endpoint from their environment. A better option for many BYOC products is a zero trust connector that establishes outbound-only connectivity from the customer's environment to the vendor. No inbound ports opened on the customer side, no public endpoints exposed on the vendor side.

Twingate's model works this way for internal application access, and the pattern generalizes: outbound-only, identity-aware, deny-by-default access from a specific workload to a specific vendor resource is materially safer than opening firewall rules or IP allowlisting.

Software supply chain for the customer-deployed component. If a vendor ships a container image or a Helm chart into a customer's cluster, the customer's security team is going to ask about signing, SBOMs, and vulnerability scanning. Have answers ready.

Tenancy isolation on the vendor side. If one customer's workload can talk to another customer's workload in the vendor's infrastructure, the whole least-privilege story on the customer side collapses. Namespaces, network policies, and separate service accounts per customer aren't optional.

The checklist

If you're a vendor, implement this. If you're a security reviewer, require it.

Credential model

  • No long-lived access keys or service account keys are stored by the vendor for customer-cloud access.

  • Cross-account IAM roles use a unique, unguessable external ID per customer.

  • The Principal in the trust policy is a specific vendor role ARN, not the vendor's root account.

  • Where the vendor's compute supports it, OIDC/workload identity federation is used instead of assume-role.

  • The federated sub claim ties the role to a specific service account or workload per customer.

  • Session durations are set to the minimum needed (default one hour or less).

Scoping

  • The permissions policy lists specific actions, not service wildcards.

  • Resource ARNs are named explicitly. No Resource: "*" unless there's a documented reason.

  • Separate roles exist for materially different capabilities (ingest, transform, write-back, support).

  • Explicit Deny statements block sensitive actions (iam:*, billing, cross-role assume, etc.) even if allows are misconfigured.

  • Condition keys constrain source IP, VPC endpoint, or region where applicable.

  • Human access (support engineers) is JIT and approval-gated, not a standing role.

Audit

  • CloudTrail / Cloud Audit Logs / Activity Log is enabled for every region and service the vendor touches.

  • Logs export to a location the vendor cannot modify or delete from.

  • Data-plane events (S3, Lambda, BigQuery, storage) are logged, not just management-plane events.

  • Anomaly detection (GuardDuty, Security Command Center, Defender for Cloud) is watching the vendor's identity.

  • Policy changes to vendor roles trigger an alert and require a documented change.

Rotation and revocation

  • External IDs rotate on a defined schedule and immediately on suspected compromise.

  • The customer can revoke vendor access without vendor involvement, in under 15 minutes.

  • Unused permissions and roles are flagged and pruned (Access Analyzer or equivalent).

  • The vendor has a documented runbook for what happens when a customer revokes access.

Vendor-side hygiene

  • Per-customer isolation exists in the vendor's own infrastructure (namespace, service account, network policy).

  • Container images and IaC deployed into customer clouds are signed and have SBOMs available.

  • The vendor's control-plane-to-customer connectivity does not require the customer to open inbound firewall rules or expose public endpoints.

  • The vendor documents exactly which permissions each role uses and why — no undocumented capabilities.

If your vendor can't check every box in the first three sections, the answer isn't to negotiate. The answer is to fix it before you deploy, or to find a vendor who already has.

Closing

For more on how Twingate handles identity-aware access, outbound-only Connectors, and just-in-time access for internal and vendor-operated resources, see the Twingate documentation.

New to Twingate? You can use Twingate for free for up to 5 users, request a personalized demo, or reach out to the team over on the Twingate subreddit.

Rapidly implement a modern Zero Trust network that is more secure and maintainable than VPNs.

/

BYOC Vendor Access

Least Privilege in Someone Else's Cloud: Securing Vendor Access for BYOC

Product Marketing Engineer

TL;DR: A practical guide to granting scoped, auditable, time-bound access to a customer's cloud environment. Skip standing keys, prefer federated short-lived credentials, and get to the checklist below if you need to move fast.

Bring-your-own-cloud (BYOC) deployments have flipped the vendor access problem inside out.

Instead of pulling customer data into a vendor's environment, the vendor's software runs inside the customer's AWS, GCP, or Azure account.

That's better for data gravity, latency, and compliance, but it introduces a permission problem nobody had five years ago: how does a vendor operate software inside a cloud they don't own without becoming the customer's biggest security liability?

The default answer for a lot of vendors is still "give us a long-lived IAM user with the keys we need." That answer is wrong in 2025, and enterprise security teams have started rejecting it outright.

This piece walks through what to require instead, whether you're the vendor building a BYOC product or the security engineer reviewing one.

Why standing credentials keep showing up in breach reports

The pattern is familiar. A vendor asks for an IAM user with a specific policy attached. The customer creates it, copies the access key and secret into the vendor's dashboard, and forgets about it. Two years later, one of the following happens:

  • The vendor's config database is breached and every customer's keys are now in a paste on a criminal forum.

  • A vendor engineer leaves, and nobody rotates the credentials they had access to.

  • The IAM policy attached to that user has drifted. Someone added s3:* to debug an issue in 2023 and never took it back off.

  • A key ends up committed to a repo. Nobody notices because it's the vendor's repo, not the customer's.

The 2024 Snowflake customer incidents, the ongoing stream of GitHub-leaked-keys advisories from AWS, and the credential-theft patterns Mandiant tracks in its M-Trends reports all rhyme. Standing credentials are stationary targets, and cloud attackers are extremely good at finding stationary targets.

The fix isn't better key management. The fix is not having a key at all.

The three access models, ranked

Here's the landscape a vendor can choose from, worst to best.

Model

How it works

Risk profile

When it's acceptable

Long-lived service account / IAM user

Vendor stores an access key and secret indefinitely

High. Compromise = permanent access until manual rotation. Detection lag is measured in months.

Legacy integrations. Nowhere else.

Cross-account IAM role (assume-role with external ID)

Customer creates a role the vendor's account is allowed to assume. Vendor calls sts:AssumeRole to get temporary credentials.

Medium. No long-lived secrets on the vendor side, but the trust relationship is standing.

Most vendor BYOC integrations today. Reasonable default.

Federated short-lived credentials (OIDC / workload identity)

Vendor's workloads present a signed identity token from their own IdP (or from GitHub Actions, EKS, GKE, etc.). Customer's cloud validates the token and issues short-lived credentials directly.

Low. No shared secret at all. Every access is auditable back to a specific workload identity.

The target state. Especially for automated, machine-to-machine vendor access.

The mental model: standing credentials are a password you never rotate. Assume-role is a password that expires after an hour but you still have to protect the underlying secret that lets you initiate the swap. Federation is proving who you are cryptographically each time, without a shared secret in the first place.

Cross-account IAM roles: the acceptable middle ground

If you're building a BYOC product today and OIDC federation isn't on the table yet, cross-account roles are the floor. The pattern in AWS:

Customer creates a role that trusts the vendor's AWS account with a required external ID.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "AWS": "arn:aws:iam::VENDOR_ACCOUNT_ID:role/vendor-worker" },
    "Action": "sts:AssumeRole",
    "Condition": {
      "StringEquals": { "sts:ExternalId": "cust-9f3a-unique-per-tenant" }
    }
  }]
}

A few things to get right, because these are where implementations quietly break:

The external ID must be unique per customer and unguessable. Its whole job is preventing the confused deputy problem where one of your customers tricks your service into assuming a role in another customer's account. A UUID per customer works. Sequential IDs, customer names, or shared constants do not.

Scope the trust to the exact vendor role, not the whole account. The Principal should be the specific IAM role your workers use, not arn:aws:iam::VENDOR:root. If your entire account is trusted, any principal in your account can assume the customer's role. Blast radius matters.

Scope the permissions policy narrowly. The attached policy is where most BYOC implementations get sloppy. Ask for resource-level ARNs, not wildcards. Ask for the specific actions, not s3:*. If your product only reads from one bucket, the policy should name that bucket. GCP equivalent: bind the service account to the specific project and resource, not the folder or organization.

Set a session duration you actually need. The default is one hour. If your workload runs for 10 minutes, don't ask for 12.

Here's what a tight S3-reader policy looks like, versus what vendors often ask for:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["s3:GetObject", "s3:ListBucket"],
    "Resource": [
      "arn:aws:s3:::customer-data-ingress-cust9f3a",
      "arn:aws:s3:::customer-data-ingress-cust9f3a/*"
    ]
  }]
}

Compare that to s3:* on *, which is unfortunately common in vendor docs. If a vendor's onboarding guide asks for a wildcard on a service, ask them why.

Federated credentials: what to aim for

Federation via OIDC is where you want to end up. The vendor's compute (a Kubernetes pod, a GitHub Actions workflow, a CI job, an application on EKS/GKE) has a cryptographic identity. The customer's cloud is configured to trust that identity provider. No secret ever crosses the boundary.

In AWS, this is IAM Roles for Service Accounts (IRSA) if the vendor runs on EKS, or the newer EKS Pod Identity. For GitHub Actions-based access, it's the GitHub OIDC provider configured as an IAM identity provider in the customer account. In GCP, it's Workload Identity Federation. In Azure, it's Workload Identity federation for managed identities.

The setup on the customer side, in AWS, looks roughly like this. First, the customer registers the vendor's OIDC issuer:

# Register the vendor's OIDC issuer as an IAM identity provider
aws iam create-open-id-connect-provider \
  --url https://oidc.vendor.example.com \
  --client-id-list sts.amazonaws.com \
  --thumbprint-list

Then they create a role whose trust policy pins to a specific subject claim in the vendor's tokens:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::CUST_ACCOUNT:oidc-provider/oidc.vendor.example.com"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": {
        "oidc.vendor.example.com:sub": "system:serviceaccount:vendor-prod:worker-cust9f3a",
        "oidc.vendor.example.com:aud": "sts.amazonaws.com"
      }
    }
  }]
}

The sub condition is the important part. It ties the role to a single Kubernetes service account in a single namespace in the vendor's cluster. If the vendor is doing this properly, they've isolated each customer's worker into its own namespace or its own service account. Compromise of one customer's workload doesn't give you the others.

Why this is better than assume-role: there is no secret. The vendor's workload can't be phished, can't leak a key in a log, can't have credentials stolen from a laptop. The token is minted at request time, lives for minutes, and is cryptographically bound to the workload it came from.

Scoping patterns that actually hold up

Regardless of which credential model you land on, the scoping matters more than the token format. A federated credential with AdministratorAccess attached is worse than a long-lived key with s3:GetObject on one bucket.

Some patterns that tend to survive contact with real production:

One role per capability, not one role for everything. If your product does ingestion, transformation, and results write-back, that's three roles. Each is scoped to what it actually needs. This is annoying to set up and pays off the first time you have to explain to a security team why your ingestion worker can't delete the customer's logs.

Resource ARN allowlists over service wildcards. Every ARN you can name explicitly is an ARN a compromised credential can't escape to.

Deny-by-default with explicit allows. Use Deny statements to block anything sensitive that the role should never touch, even if the allow policy is misconfigured. iam:*, sts:AssumeRole on other roles, and anything touching billing or account-level settings are common candidates.

Condition keys for network scope. If your vendor workload only egresses from known IPs or through a specific VPC endpoint, use aws:SourceIp or aws:SourceVpce conditions. Even if a key leaks, it's useless outside your network.

Time-bound access for humans. If your support engineers occasionally need to run diagnostics in a customer environment, that's not a permanent role, it's a JIT (just-in-time) grant.

Tools like AWS IAM Identity Center session policies, or a broker in front of sts:AssumeRole, can enforce approval and duration. Some ZTNA products (Twingate included) can front the entire access request with identity-aware policies, session recording, and time-bound grants so that even the assume-role call requires an approved session.

Audit logging: what to actually turn on

An access model you can't audit is a hope more than it's a security control.

For AWS: CloudTrail should be enabled in every region where the vendor operates, with events flowing to an S3 bucket the vendor cannot delete from. Both the management events and, if the vendor touches S3 or Lambda, the data events. The bucket should have Object Lock or MFA-delete configured. GuardDuty should be watching for anomalous API patterns from the vendor role.

For GCP: Cloud Audit Logs, specifically Admin Activity and Data Access logs for the services the vendor uses, exported to a log sink outside the vendor's blast radius.

For Azure: Activity Log and diagnostic settings for the resources the vendor touches, routed to a Log Analytics workspace or storage account.

What you actually want to be able to answer, in under five minutes:

  • Which principal accessed which resource, when, and from where?

  • Did anything the vendor role touched fall outside its documented scope?

  • When was the vendor role last assumed, and by which workload identity?

  • Has the policy on the role changed since it was created? If so, who changed it, and was there a ticket?

The last one is the one that gets skipped, and it's the one that matters most. Policy drift is how tight roles become permissive over months.

Rotation and revocation

If you're using federated credentials, rotation is largely automatic: every token has a lifetime of minutes. The rotation you care about is the identity provider's signing keys, which the cloud validates automatically.

If you're using cross-account roles, there's no key to rotate. What you rotate is the external ID, on a schedule or after any suspected compromise. Rotating it is a coordination cost with the vendor, so a lot of shops don't.

That's a mistake. If a vendor's config store is breached and every customer's external ID leaks, the confused-deputy protection those IDs provided is gone.

If you're still using long-lived keys (and you shouldn't be) rotate them every 90 days maximum, and audit for unused keys weekly. AWS Access Analyzer can flag unused permissions and roles. Use it.

Revocation matters more than rotation. When something goes wrong, how fast can the customer cut off vendor access?

  • Cross-account role: delete the role or remove the trust relationship. Effective immediately for new sessions; existing sessions expire on their own within the session duration (which is why short session durations matter).

  • Federated OIDC role: same as above, plus remove the OIDC provider registration for a harder kill.

  • Long-lived keys: deactivate the IAM user's access keys. Existing signed requests will fail on next use.

A customer should be able to revoke a vendor's access without calling the vendor. If revocation requires a support ticket to the vendor, that's a failed access model.

What a vendor is responsible for that isn't credentials

Least privilege on the credential is table stakes. Vendors operating in a customer's cloud also carry weight for:

Network isolation of the vendor's control plane. If the vendor has a control plane that manages the customer-side workload, how does it reach in? A public endpoint the vendor authenticates against is one option, but it means the customer has to allow outbound traffic to that endpoint from their environment. A better option for many BYOC products is a zero trust connector that establishes outbound-only connectivity from the customer's environment to the vendor. No inbound ports opened on the customer side, no public endpoints exposed on the vendor side.

Twingate's model works this way for internal application access, and the pattern generalizes: outbound-only, identity-aware, deny-by-default access from a specific workload to a specific vendor resource is materially safer than opening firewall rules or IP allowlisting.

Software supply chain for the customer-deployed component. If a vendor ships a container image or a Helm chart into a customer's cluster, the customer's security team is going to ask about signing, SBOMs, and vulnerability scanning. Have answers ready.

Tenancy isolation on the vendor side. If one customer's workload can talk to another customer's workload in the vendor's infrastructure, the whole least-privilege story on the customer side collapses. Namespaces, network policies, and separate service accounts per customer aren't optional.

The checklist

If you're a vendor, implement this. If you're a security reviewer, require it.

Credential model

  • No long-lived access keys or service account keys are stored by the vendor for customer-cloud access.

  • Cross-account IAM roles use a unique, unguessable external ID per customer.

  • The Principal in the trust policy is a specific vendor role ARN, not the vendor's root account.

  • Where the vendor's compute supports it, OIDC/workload identity federation is used instead of assume-role.

  • The federated sub claim ties the role to a specific service account or workload per customer.

  • Session durations are set to the minimum needed (default one hour or less).

Scoping

  • The permissions policy lists specific actions, not service wildcards.

  • Resource ARNs are named explicitly. No Resource: "*" unless there's a documented reason.

  • Separate roles exist for materially different capabilities (ingest, transform, write-back, support).

  • Explicit Deny statements block sensitive actions (iam:*, billing, cross-role assume, etc.) even if allows are misconfigured.

  • Condition keys constrain source IP, VPC endpoint, or region where applicable.

  • Human access (support engineers) is JIT and approval-gated, not a standing role.

Audit

  • CloudTrail / Cloud Audit Logs / Activity Log is enabled for every region and service the vendor touches.

  • Logs export to a location the vendor cannot modify or delete from.

  • Data-plane events (S3, Lambda, BigQuery, storage) are logged, not just management-plane events.

  • Anomaly detection (GuardDuty, Security Command Center, Defender for Cloud) is watching the vendor's identity.

  • Policy changes to vendor roles trigger an alert and require a documented change.

Rotation and revocation

  • External IDs rotate on a defined schedule and immediately on suspected compromise.

  • The customer can revoke vendor access without vendor involvement, in under 15 minutes.

  • Unused permissions and roles are flagged and pruned (Access Analyzer or equivalent).

  • The vendor has a documented runbook for what happens when a customer revokes access.

Vendor-side hygiene

  • Per-customer isolation exists in the vendor's own infrastructure (namespace, service account, network policy).

  • Container images and IaC deployed into customer clouds are signed and have SBOMs available.

  • The vendor's control-plane-to-customer connectivity does not require the customer to open inbound firewall rules or expose public endpoints.

  • The vendor documents exactly which permissions each role uses and why — no undocumented capabilities.

If your vendor can't check every box in the first three sections, the answer isn't to negotiate. The answer is to fix it before you deploy, or to find a vendor who already has.

Closing

For more on how Twingate handles identity-aware access, outbound-only Connectors, and just-in-time access for internal and vendor-operated resources, see the Twingate documentation.

New to Twingate? You can use Twingate for free for up to 5 users, request a personalized demo, or reach out to the team over on the Twingate subreddit.

Rapidly implement a modern Zero Trust network that is more secure and maintainable than VPNs.

Least Privilege in Someone Else's Cloud: Securing Vendor Access for BYOC

Product Marketing Engineer

TL;DR: A practical guide to granting scoped, auditable, time-bound access to a customer's cloud environment. Skip standing keys, prefer federated short-lived credentials, and get to the checklist below if you need to move fast.

Bring-your-own-cloud (BYOC) deployments have flipped the vendor access problem inside out.

Instead of pulling customer data into a vendor's environment, the vendor's software runs inside the customer's AWS, GCP, or Azure account.

That's better for data gravity, latency, and compliance, but it introduces a permission problem nobody had five years ago: how does a vendor operate software inside a cloud they don't own without becoming the customer's biggest security liability?

The default answer for a lot of vendors is still "give us a long-lived IAM user with the keys we need." That answer is wrong in 2025, and enterprise security teams have started rejecting it outright.

This piece walks through what to require instead, whether you're the vendor building a BYOC product or the security engineer reviewing one.

Why standing credentials keep showing up in breach reports

The pattern is familiar. A vendor asks for an IAM user with a specific policy attached. The customer creates it, copies the access key and secret into the vendor's dashboard, and forgets about it. Two years later, one of the following happens:

  • The vendor's config database is breached and every customer's keys are now in a paste on a criminal forum.

  • A vendor engineer leaves, and nobody rotates the credentials they had access to.

  • The IAM policy attached to that user has drifted. Someone added s3:* to debug an issue in 2023 and never took it back off.

  • A key ends up committed to a repo. Nobody notices because it's the vendor's repo, not the customer's.

The 2024 Snowflake customer incidents, the ongoing stream of GitHub-leaked-keys advisories from AWS, and the credential-theft patterns Mandiant tracks in its M-Trends reports all rhyme. Standing credentials are stationary targets, and cloud attackers are extremely good at finding stationary targets.

The fix isn't better key management. The fix is not having a key at all.

The three access models, ranked

Here's the landscape a vendor can choose from, worst to best.

Model

How it works

Risk profile

When it's acceptable

Long-lived service account / IAM user

Vendor stores an access key and secret indefinitely

High. Compromise = permanent access until manual rotation. Detection lag is measured in months.

Legacy integrations. Nowhere else.

Cross-account IAM role (assume-role with external ID)

Customer creates a role the vendor's account is allowed to assume. Vendor calls sts:AssumeRole to get temporary credentials.

Medium. No long-lived secrets on the vendor side, but the trust relationship is standing.

Most vendor BYOC integrations today. Reasonable default.

Federated short-lived credentials (OIDC / workload identity)

Vendor's workloads present a signed identity token from their own IdP (or from GitHub Actions, EKS, GKE, etc.). Customer's cloud validates the token and issues short-lived credentials directly.

Low. No shared secret at all. Every access is auditable back to a specific workload identity.

The target state. Especially for automated, machine-to-machine vendor access.

The mental model: standing credentials are a password you never rotate. Assume-role is a password that expires after an hour but you still have to protect the underlying secret that lets you initiate the swap. Federation is proving who you are cryptographically each time, without a shared secret in the first place.

Cross-account IAM roles: the acceptable middle ground

If you're building a BYOC product today and OIDC federation isn't on the table yet, cross-account roles are the floor. The pattern in AWS:

Customer creates a role that trusts the vendor's AWS account with a required external ID.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "AWS": "arn:aws:iam::VENDOR_ACCOUNT_ID:role/vendor-worker" },
    "Action": "sts:AssumeRole",
    "Condition": {
      "StringEquals": { "sts:ExternalId": "cust-9f3a-unique-per-tenant" }
    }
  }]
}

A few things to get right, because these are where implementations quietly break:

The external ID must be unique per customer and unguessable. Its whole job is preventing the confused deputy problem where one of your customers tricks your service into assuming a role in another customer's account. A UUID per customer works. Sequential IDs, customer names, or shared constants do not.

Scope the trust to the exact vendor role, not the whole account. The Principal should be the specific IAM role your workers use, not arn:aws:iam::VENDOR:root. If your entire account is trusted, any principal in your account can assume the customer's role. Blast radius matters.

Scope the permissions policy narrowly. The attached policy is where most BYOC implementations get sloppy. Ask for resource-level ARNs, not wildcards. Ask for the specific actions, not s3:*. If your product only reads from one bucket, the policy should name that bucket. GCP equivalent: bind the service account to the specific project and resource, not the folder or organization.

Set a session duration you actually need. The default is one hour. If your workload runs for 10 minutes, don't ask for 12.

Here's what a tight S3-reader policy looks like, versus what vendors often ask for:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["s3:GetObject", "s3:ListBucket"],
    "Resource": [
      "arn:aws:s3:::customer-data-ingress-cust9f3a",
      "arn:aws:s3:::customer-data-ingress-cust9f3a/*"
    ]
  }]
}

Compare that to s3:* on *, which is unfortunately common in vendor docs. If a vendor's onboarding guide asks for a wildcard on a service, ask them why.

Federated credentials: what to aim for

Federation via OIDC is where you want to end up. The vendor's compute (a Kubernetes pod, a GitHub Actions workflow, a CI job, an application on EKS/GKE) has a cryptographic identity. The customer's cloud is configured to trust that identity provider. No secret ever crosses the boundary.

In AWS, this is IAM Roles for Service Accounts (IRSA) if the vendor runs on EKS, or the newer EKS Pod Identity. For GitHub Actions-based access, it's the GitHub OIDC provider configured as an IAM identity provider in the customer account. In GCP, it's Workload Identity Federation. In Azure, it's Workload Identity federation for managed identities.

The setup on the customer side, in AWS, looks roughly like this. First, the customer registers the vendor's OIDC issuer:

# Register the vendor's OIDC issuer as an IAM identity provider
aws iam create-open-id-connect-provider \
  --url https://oidc.vendor.example.com \
  --client-id-list sts.amazonaws.com \
  --thumbprint-list

Then they create a role whose trust policy pins to a specific subject claim in the vendor's tokens:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::CUST_ACCOUNT:oidc-provider/oidc.vendor.example.com"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": {
        "oidc.vendor.example.com:sub": "system:serviceaccount:vendor-prod:worker-cust9f3a",
        "oidc.vendor.example.com:aud": "sts.amazonaws.com"
      }
    }
  }]
}

The sub condition is the important part. It ties the role to a single Kubernetes service account in a single namespace in the vendor's cluster. If the vendor is doing this properly, they've isolated each customer's worker into its own namespace or its own service account. Compromise of one customer's workload doesn't give you the others.

Why this is better than assume-role: there is no secret. The vendor's workload can't be phished, can't leak a key in a log, can't have credentials stolen from a laptop. The token is minted at request time, lives for minutes, and is cryptographically bound to the workload it came from.

Scoping patterns that actually hold up

Regardless of which credential model you land on, the scoping matters more than the token format. A federated credential with AdministratorAccess attached is worse than a long-lived key with s3:GetObject on one bucket.

Some patterns that tend to survive contact with real production:

One role per capability, not one role for everything. If your product does ingestion, transformation, and results write-back, that's three roles. Each is scoped to what it actually needs. This is annoying to set up and pays off the first time you have to explain to a security team why your ingestion worker can't delete the customer's logs.

Resource ARN allowlists over service wildcards. Every ARN you can name explicitly is an ARN a compromised credential can't escape to.

Deny-by-default with explicit allows. Use Deny statements to block anything sensitive that the role should never touch, even if the allow policy is misconfigured. iam:*, sts:AssumeRole on other roles, and anything touching billing or account-level settings are common candidates.

Condition keys for network scope. If your vendor workload only egresses from known IPs or through a specific VPC endpoint, use aws:SourceIp or aws:SourceVpce conditions. Even if a key leaks, it's useless outside your network.

Time-bound access for humans. If your support engineers occasionally need to run diagnostics in a customer environment, that's not a permanent role, it's a JIT (just-in-time) grant.

Tools like AWS IAM Identity Center session policies, or a broker in front of sts:AssumeRole, can enforce approval and duration. Some ZTNA products (Twingate included) can front the entire access request with identity-aware policies, session recording, and time-bound grants so that even the assume-role call requires an approved session.

Audit logging: what to actually turn on

An access model you can't audit is a hope more than it's a security control.

For AWS: CloudTrail should be enabled in every region where the vendor operates, with events flowing to an S3 bucket the vendor cannot delete from. Both the management events and, if the vendor touches S3 or Lambda, the data events. The bucket should have Object Lock or MFA-delete configured. GuardDuty should be watching for anomalous API patterns from the vendor role.

For GCP: Cloud Audit Logs, specifically Admin Activity and Data Access logs for the services the vendor uses, exported to a log sink outside the vendor's blast radius.

For Azure: Activity Log and diagnostic settings for the resources the vendor touches, routed to a Log Analytics workspace or storage account.

What you actually want to be able to answer, in under five minutes:

  • Which principal accessed which resource, when, and from where?

  • Did anything the vendor role touched fall outside its documented scope?

  • When was the vendor role last assumed, and by which workload identity?

  • Has the policy on the role changed since it was created? If so, who changed it, and was there a ticket?

The last one is the one that gets skipped, and it's the one that matters most. Policy drift is how tight roles become permissive over months.

Rotation and revocation

If you're using federated credentials, rotation is largely automatic: every token has a lifetime of minutes. The rotation you care about is the identity provider's signing keys, which the cloud validates automatically.

If you're using cross-account roles, there's no key to rotate. What you rotate is the external ID, on a schedule or after any suspected compromise. Rotating it is a coordination cost with the vendor, so a lot of shops don't.

That's a mistake. If a vendor's config store is breached and every customer's external ID leaks, the confused-deputy protection those IDs provided is gone.

If you're still using long-lived keys (and you shouldn't be) rotate them every 90 days maximum, and audit for unused keys weekly. AWS Access Analyzer can flag unused permissions and roles. Use it.

Revocation matters more than rotation. When something goes wrong, how fast can the customer cut off vendor access?

  • Cross-account role: delete the role or remove the trust relationship. Effective immediately for new sessions; existing sessions expire on their own within the session duration (which is why short session durations matter).

  • Federated OIDC role: same as above, plus remove the OIDC provider registration for a harder kill.

  • Long-lived keys: deactivate the IAM user's access keys. Existing signed requests will fail on next use.

A customer should be able to revoke a vendor's access without calling the vendor. If revocation requires a support ticket to the vendor, that's a failed access model.

What a vendor is responsible for that isn't credentials

Least privilege on the credential is table stakes. Vendors operating in a customer's cloud also carry weight for:

Network isolation of the vendor's control plane. If the vendor has a control plane that manages the customer-side workload, how does it reach in? A public endpoint the vendor authenticates against is one option, but it means the customer has to allow outbound traffic to that endpoint from their environment. A better option for many BYOC products is a zero trust connector that establishes outbound-only connectivity from the customer's environment to the vendor. No inbound ports opened on the customer side, no public endpoints exposed on the vendor side.

Twingate's model works this way for internal application access, and the pattern generalizes: outbound-only, identity-aware, deny-by-default access from a specific workload to a specific vendor resource is materially safer than opening firewall rules or IP allowlisting.

Software supply chain for the customer-deployed component. If a vendor ships a container image or a Helm chart into a customer's cluster, the customer's security team is going to ask about signing, SBOMs, and vulnerability scanning. Have answers ready.

Tenancy isolation on the vendor side. If one customer's workload can talk to another customer's workload in the vendor's infrastructure, the whole least-privilege story on the customer side collapses. Namespaces, network policies, and separate service accounts per customer aren't optional.

The checklist

If you're a vendor, implement this. If you're a security reviewer, require it.

Credential model

  • No long-lived access keys or service account keys are stored by the vendor for customer-cloud access.

  • Cross-account IAM roles use a unique, unguessable external ID per customer.

  • The Principal in the trust policy is a specific vendor role ARN, not the vendor's root account.

  • Where the vendor's compute supports it, OIDC/workload identity federation is used instead of assume-role.

  • The federated sub claim ties the role to a specific service account or workload per customer.

  • Session durations are set to the minimum needed (default one hour or less).

Scoping

  • The permissions policy lists specific actions, not service wildcards.

  • Resource ARNs are named explicitly. No Resource: "*" unless there's a documented reason.

  • Separate roles exist for materially different capabilities (ingest, transform, write-back, support).

  • Explicit Deny statements block sensitive actions (iam:*, billing, cross-role assume, etc.) even if allows are misconfigured.

  • Condition keys constrain source IP, VPC endpoint, or region where applicable.

  • Human access (support engineers) is JIT and approval-gated, not a standing role.

Audit

  • CloudTrail / Cloud Audit Logs / Activity Log is enabled for every region and service the vendor touches.

  • Logs export to a location the vendor cannot modify or delete from.

  • Data-plane events (S3, Lambda, BigQuery, storage) are logged, not just management-plane events.

  • Anomaly detection (GuardDuty, Security Command Center, Defender for Cloud) is watching the vendor's identity.

  • Policy changes to vendor roles trigger an alert and require a documented change.

Rotation and revocation

  • External IDs rotate on a defined schedule and immediately on suspected compromise.

  • The customer can revoke vendor access without vendor involvement, in under 15 minutes.

  • Unused permissions and roles are flagged and pruned (Access Analyzer or equivalent).

  • The vendor has a documented runbook for what happens when a customer revokes access.

Vendor-side hygiene

  • Per-customer isolation exists in the vendor's own infrastructure (namespace, service account, network policy).

  • Container images and IaC deployed into customer clouds are signed and have SBOMs available.

  • The vendor's control-plane-to-customer connectivity does not require the customer to open inbound firewall rules or expose public endpoints.

  • The vendor documents exactly which permissions each role uses and why — no undocumented capabilities.

If your vendor can't check every box in the first three sections, the answer isn't to negotiate. The answer is to fix it before you deploy, or to find a vendor who already has.

Closing

For more on how Twingate handles identity-aware access, outbound-only Connectors, and just-in-time access for internal and vendor-operated resources, see the Twingate documentation.

New to Twingate? You can use Twingate for free for up to 5 users, request a personalized demo, or reach out to the team over on the Twingate subreddit.