This page explains how to use Credential Access Boundaries to downscope, or restrict, the Identity and Access Management (IAM) permissions that a short-lived credential can use.
How Credential Access Boundaries work
To downscope permissions, you define a Credential Access Boundary that specifies which resources the short-lived credential can access, as well as an upper bound on the permissions that are available on each resource. You can then create a short-lived credential, then exchange it for a new credential that respects the Credential Access Boundary.
If you need to give members a distinct set of permissions for each session, using Credential Access Boundaries can be more efficient than creating many different service accounts and granting each service account a different set of roles. For example, if one of your customers needs to access Cloud Storage data that you control, you can create a service account that can access every Cloud Storage bucket that you own, then apply a Credential Access Boundary that only allows access to the bucket with your customer's data.
Examples of Credential Access Boundaries
The following sections show examples of Credential Access Boundaries for common use cases.
Limit permissions for a bucket
The following example shows a simple Credential Access Boundary. It applies to
the Cloud Storage bucket example-bucket
, and it sets the upper bound to
the permissions included in the Storage Object Viewer role
(roles/storage.objectViewer
):
{
"accessBoundary": {
"accessBoundaryRules": [
{
"availablePermissions": [
"inRole:roles/storage.objectViewer"
],
"availableResource": "//storage.googleapis.com/projects/_/buckets/example-bucket"
}
]
}
}
Limit permissions for multiple buckets
The following example shows a Credential Access Boundary that includes rules for multiple buckets:
- The Cloud Storage bucket
example-bucket-1
: For this bucket, only the permissions in the Storage Object Viewer role (roles/storage.objectViewer
) are available. - The Cloud Storage bucket
example-bucket-2
: For this bucket, only the permissions in the Storage Object Creator role (roles/storage.objectCreator
) are available.
{
"accessBoundary": {
"accessBoundaryRules": [
{
"availablePermissions": [
"inRole:roles/storage.objectViewer"
],
"availableResource": "//storage.googleapis.com/projects/_/buckets/example-bucket-1"
},
{
"availablePermissions": [
"inRole:roles/storage.objectCreator"
],
"availableResource": "//storage.googleapis.com/projects/_/buckets/example-bucket-2"
}
]
}
}
Limit permissions for specific objects
You can also use IAM Conditions to specify
which Cloud Storage objects a member can access. For example, you can add a
condition that makes permissions available for objects whose name starts with
customer-a
:
{ "accessBoundary": { "accessBoundaryRules": [ { "availablePermissions": [ "inRole:roles/storage.objectViewer" ], "availableResource": "//storage.googleapis.com/projects/_/buckets/example-bucket", "availabilityCondition": { "expression" : "resource.name.startsWith('projects/_/buckets/example-bucket/objects/customer-a')" } } ] } }
Limit permissions when listing objects
When you list the objects in a Cloud Storage bucket, you
are calling a method on a bucket resource, not an object resource. As a result,
if a condition is evaluated for a list request, and the condition refers to the
resource name, then the resource name identifies the bucket,
not an object within the bucket. For example, when you list objects in
example-bucket
, the resource name is projects/_/buckets/example-bucket
.
This naming convention can lead to unexpected behavior when you list objects.
For example, suppose you want a Credential Access Boundary that allows view
access to objects in example-bucket
with the prefix customer-a/invoices/
.
You might try to use the following condition in the Credential Access Boundary:
Incomplete: Condition that checks only the resource name
resource.name.startsWith('projects/_/buckets/example-bucket/objects/customer-a/invoices/')
This condition works for reading objects, but not for listing objects:
- When a member tries to read an object in
example-bucket
with the prefixcustomer-a/invoices/
, the condition evaluates totrue
. - When a member tries to list objects with that prefix, the condition evaluates
to
false
. The value ofresource.name
isprojects/_/buckets/example-bucket
, which does not start withprojects/_/buckets/example-bucket/objects/customer-a/invoices/
.
To prevent this issue, in addition to using resource.name.startsWith()
, your
condition can check an API attribute named
storage.googleapis.com/objectListPrefix
. This attribute contains the value of
the prefix
parameter that was used to filter the list of objects. As a result,
you can write a condition that refers to the value of the prefix
parameter.
The following example shows how to use the API attribute in a condition. It
allows reading and listing objects in example-bucket
with the prefix
customer-a/invoices/
:
Complete: Condition that checks the resource name and the prefix
resource.name.startsWith('projects/_/buckets/example-bucket/objects/customer-a/invoices/') || api.getAttribute('storage.googleapis.com/objectListPrefix', '') .startsWith('customer-a/invoices/')
You can now use this condition in a Credential Access Boundary:
{
"accessBoundary": {
"accessBoundaryRules": [
{
"availablePermissions": [
"inRole:roles/storage.objectViewer"
],
"availableResource": "//storage.googleapis.com/projects/_/buckets/example-bucket",
"availabilityCondition": {
"expression":
"resource.name.startsWith('projects/_/buckets/example-bucket/objects/customer-a/invoices/') || api.getAttribute('storage.googleapis.com/objectListPrefix', '').startsWith('customer-a/invoices/')"
}
}
]
}
}
Before you begin
Before you use Credential Access Boundaries, make sure you meet the following requirements:
You need to downscope permissions only for Cloud Storage, not for other Google Cloud services.
If you need to downscope permissions for additional Google Cloud services, you can create multiple service accounts and grant a different set of roles to each service account.
You can use OAuth 2.0 access tokens for authentication. Other types of short-lived credentials do not support Credential Access Boundaries.
Creating a downscoped short-lived credential
To create an OAuth 2.0 access token with downscoped permissions, follow these steps:
- Grant the appropriate IAM roles to a user or service account.
- Define a Credential Access Boundary that sets an upper bound on the permissions that are available to the user or service account.
- Create an OAuth 2.0 access token for the user or service account.
- Exchange the OAuth 2.0 access token for a new token that respects the Credential Access Boundary.
You can then use the new, downscoped OAuth 2.0 access token to authenticate requests to Cloud Storage.
Granting IAM roles
A Credential Access Boundary sets an upper bound on the available permissions for a resource. It can subtract permissions from a member, but it cannot add permissions that the member does not already have.
As a result, you must also grant roles to the member that provide the permissions they need, either on a Cloud Storage bucket or on a higher-level resource, such as the project.
For example, suppose you need to create a downscoped short-lived credential that allows a service account to create objects in a bucket:
- At a minimum, you must grant a role to the service account that includes the
storage.objects.create
permission, such as the Storage Object Creator role (roles/storage.objectCreator
). The Credential Access Boundary must also include this permission. - You can also grant a role that includes more permissions, such as the Storage
Object Admin role (
roles/storage.objectAdmin
). The service account can use only the permissions that appear in both the role grant and the Credential Access Boundary.
To learn about predefined roles for Cloud Storage, see Cloud Storage roles.
Defining a Credential Access Boundary
A Credential Access Boundary is a JSON object that contains a list of access boundary rules. Each rule contains the following information:
- The resource that the rule applies to.
- The upper bound of the permissions that are available on that resource.
- Optional: A condition that further restricts permissions. A condition includes
the following:
- A condition expression that evaluates to
true
orfalse
. If it evaluates totrue
, access is allowed; otherwise, access is denied. - Optional: A title that identifies the condition.
- Optional: A description with more information about the condition.
- A condition expression that evaluates to
If you apply a Credential Access Boundary to a short-lived credential, then the credential can access only the resources in the Credential Access Boundary. No permissions are available on other resources.
A Credential Access Boundary can contain up to 10 access boundary rules. You can apply only one Credential Access Boundary to each short-lived credential.
A Credential Access Boundary contains the following fields:
Fields | |
---|---|
accessBoundary |
A container for the Credential Access Boundary. |
accessBoundary.accessBoundaryRules[] |
A list of access boundary rules to apply to a short-lived credential. |
accessBoundary.accessBoundaryRules[].availablePermissions[] |
A list that defines the upper bound on the available permissions for the resource.
Each value is the identifier for an IAM
predefined role or
custom role, with
the prefix |
accessBoundary.accessBoundaryRules[].availableResource |
The full resource name of the Cloud Storage bucket that the rule
applies to. Use the format
|
accessBoundary.accessBoundaryRules[].availabilityCondition |
Optional. A condition that restricts the availability of permissions to specific Cloud Storage objects. Use this field if you want to make permissions available for specific objects, rather than all objects in a Cloud Storage bucket. |
accessBoundary.accessBoundaryRules[].availabilityCondition.expression |
A condition expression that specifies the Cloud Storage objects where permissions are available.
To learn how to refer to specific objects in a condition expression,
see
|
accessBoundary.accessBoundaryRules[].availabilityCondition.title |
Optional. A short string that identifies the purpose of the condition. |
accessBoundary.accessBoundaryRules[].availabilityCondition.description |
Optional. Details about the purpose of the condition. |
For examples, see Examples of Credential Access Boundaries on this page.
Create a JSON file that defines a Credential Access Boundary. You will use this file in a later step.
Creating an OAuth 2.0 access token
Before you create a downscoped short-lived credential, you must create a normal
OAuth 2.0 access token. You can then exchange the normal credential for a
downscoped credential. When you create the access token, use the OAuth 2.0 scope
https://www.googleapis.com/auth/cloud-platform
.
To create an access token for a service account, you can complete the server-to-server OAuth 2.0 flow, or you can use the Service Account Credentials API to generate an OAuth 2.0 access token.
To create an access token for a user, see Obtaining OAuth 2.0 access tokens. You can also use the OAuth 2.0 Playground to create an access token for your own Google Account.
Exchanging the OAuth 2.0 access token
After you create an OAuth 2.0 access token, you can exchange the access token for a new token that respects the Credential Access Boundary. You exchange the access token through the Security Token Service API, which is part of Identity Platform.
To exchange the access token, use the following HTTP method and URL:
POST https://sts.googleapis.com/v1beta/token
Set the Content-Type
header in the request to
application/x-www-form-urlencoded
. Include the following fields in the request
body:
Fields | |
---|---|
grant_type |
Use the value
|
options |
The percent-encoded Credential Access Boundary. |
requested_token_type |
Use the value
|
subject_token |
The OAuth 2.0 access token that you want to exchange. |
subject_token_type |
Use the value
|
The response is a JSON object that contains the following fields:
Fields | |
---|---|
access_token |
A new OAuth 2.0 access token that respects the Credential Access Boundary. |
expires_in |
The amount of time until the new access token expires, in seconds. This field is present only if the original access token represents a service account. When this field is not present, the new access token has the same time to expire as the original access token. |
issued_token_type |
Contains the value
|
token_type |
Contains the value |
For example, if the Credential Access Boundary is stored in the file
./access-boundary.json
, you can use the following
curl
command to exchange the access token. Replace
original-token
with the original access token:
curl -H "Content-Type:application/x-www-form-urlencoded" \
-X POST \
https://sts.googleapis.com/v1beta/token \
-d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange&subject_token_type=urn:ietf:params:oauth:token-type:access_token&requested_token_type=urn:ietf:params:oauth:token-type:access_token&subject_token=original-token" \
--data-urlencode "options=$(cat ./access-boundary.json)"
The response is similar to the following example:
{
"access_token": "ya29.dr.AbCDeFg-123456...",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer",
"expires_in": 3600
}
What's next
- Learn about access control for Cloud Storage.
- Create a short-lived service account credential.
- Create an OAuth 2.0 access token for a service account, using the server-to-server OAuth 2.0 flow or the Service Account Credentials API.
- Create an OAuth 2.0 access token for a user.
- See the permissions in each predefined role.
- Learn about custom roles.