Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Update README with client-side CAB instructions #1607

Merged
merged 6 commits into from
Feb 6, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 55 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -949,16 +949,19 @@ googleapis.com domain.
### Downscoping with Credential Access Boundaries

[Downscoping with Credential Access Boundaries](https://cloud.google.com/iam/docs/downscoping-short-lived-credentials)
enables the ability to downscope, or restrict, the Identity and Access Management (IAM) permissions
that a short-lived credential can use for Cloud Storage.
enables restricting the Identity and Access Management (IAM) permissions that a
short-lived credential can use for Cloud Storage. This involves creating a
`CredentialAccessBoundary` that defines the restrictions applied to the
downscoped token. Using downscoped credentials ensures tokens in flight always
have the least privileges (Principle of Least Privilege).

The `DownscopedCredentials` class can be used to produce a downscoped access token from a
`CredentialAccessBoundary` and a source credential. The Credential Access Boundary specifies which
resources the newly created credential can access, as well as an upper bound on the permissions that
are available on each resource. Using downscoped credentials ensures tokens in flight always have
the least privileges (Principle of Least Privilege).
#### Creating a CredentialAccessBoundary

The snippet below shows how to initialize a CredentialAccessBoundary with one AccessBoundaryRule
The Credential Access Boundary specifies which resources the newly created credential can access,
as well as an upper bound on the permissions that are available on each resource.
It consists of one or more `AccessBoundaryRule` objects.

The snippet below shows how to initialize a `CredentialAccessBoundary` with one `AccessBoundaryRule`
which specifies that the downscoped token will have readonly access to objects starting with
"customer-a" in bucket "bucket-123":
```java
Expand All @@ -980,12 +983,32 @@ CredentialAccessBoundary credentialAccessBoundary =
CredentialAccessBoundary.newBuilder().addRule(rule).build();
```

#### Common Usage Pattern

The common pattern of usage is to have a token broker with elevated access generate these downscoped
credentials from higher access source credentials and pass the downscoped short-lived access tokens
to a token consumer via some secure authenticated channel for limited access to Google Cloud Storage
resources.

Using the CredentialAccessBoundary created above in the Token Broker:
#### Generating Downscoped Tokens

There are two ways to generate downscoped tokens using a
CredentialAccessBoundary:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: If possible, could there be like a table or a pro/ cons comparison between the two that explicitly spells out what any considerations/ impacts of one vs the other?

Also, is there a need for something like a migration guide? To help users potentially migrate from server-side to client-side?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, is there an general recommendation that that your team would provide between the two. I know it's possible that may not be a recommendation.

i.e. Prefer client-side unless ... X,Y,Z blocker?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked with the team, we don't have a general recommendation, or a migration guide. Depending on their use case, and whether or not they need many unique downscoped tokens or they can re-use existing ones, they can decide between the two optiosn.

Copy link
Contributor

@lqiu96 lqiu96 Feb 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, that's fine that there is no recommendation. To clarify my above messages, I think the wording below makes it seem like there is almost no reason not to choose client-side CAB.

From a new user's perspective: Client side minimizes the amount of calls to STS when rules change frequently. If my rules don't even change that frequent, I can imagine that there would be even less calls to STS and making it even more efficient.

My point is that I think from a new user perspective, I don't know when/ why I would consider server-side CAB. All I see if pros for client-side over server-side.


* Server-side: Uses the `DownscopedCredentials` class. Each time a
downscoped token is needed, the client makes a call to the Security Token Service (STS).
This is suitable for applications that require downscoped tokens infrequently.
* Client-side: Uses the `ClientSideCredentialAccessBoundaryFactory` class. This
approach minimizes calls to STS. The client retrieves necessary cryptographic
material once and then generates multiple downscoped tokens locally. This is
more efficient for applications that need to generate many downscoped tokens.

#### Server-side CAB

The `DownscopedCredentials` class can be used to produce a downscoped access
token from a source credential and the `CredentialAccessBoundary` created above
in the Token Broker:

```java
// Retrieve the source credentials from ADC.
GoogleCredentials sourceCredentials = GoogleCredentials.getApplicationDefault()
Expand All @@ -994,7 +1017,7 @@ GoogleCredentials sourceCredentials = GoogleCredentials.getApplicationDefault()
// Initialize the DownscopedCredentials class.
DownscopedCredentials downscopedCredentials =
DownscopedCredentials.newBuilder()
.setSourceCredential(credentials)
.setSourceCredential(sourceCredentials)
.setCredentialAccessBoundary(credentialAccessBoundary)
.build();

Expand All @@ -1003,45 +1026,40 @@ DownscopedCredentials downscopedCredentials =
AccessToken downscopedAccessToken = downscopedCredentials.refreshAccessToken();
```

A token broker can be set up on a server in a private network. Various workloads
(token consumers) in the same network will send authenticated requests to that broker for downscoped
tokens to access or modify specific google cloud storage buckets.
#### Client-side CAB

The broker will instantiate downscoped credentials instances that can be used to generate short
lived downscoped access tokens which will be passed to the token consumer.
For client-side CAB, the `ClientSideCredentialAccessBoundaryFactory` is used
with a source credential. After initializing the factory, the `generateToken()`
method can be called repeatedly with different `CredentialAccessBoundary`
objects to create multiple downscoped tokens.

Putting it all together:
```java
// Retrieve the source credentials from ADC.
GoogleCredentials sourceCredentials = GoogleCredentials.getApplicationDefault()
.createScoped("https://www.googleapis.com/auth/cloud-platform");

// Create an Access Boundary Rule which will restrict the downscoped token to having readonly
// access to objects starting with "customer-a" in bucket "bucket-123".
String availableResource = "//storage.googleapis.com/projects/_/buckets/bucket-123";
String availablePermission = "inRole:roles/storage.objectViewer";
String expression = "resource.name.startsWith('projects/_/buckets/bucket-123/objects/customer-a')";

CredentialAccessBoundary.AccessBoundaryRule rule =
CredentialAccessBoundary.AccessBoundaryRule.newBuilder()
.setAvailableResource(availableResource)
.addAvailablePermission(availablePermission)
.setAvailabilityCondition(
new AvailabilityCondition(expression, /* title= */ null, /* description= */ null))
.build();

// Initialize the DownscopedCredentials class.
DownscopedCredentials downscopedCredentials =
DownscopedCredentials.newBuilder()
.setSourceCredential(credentials)
.setCredentialAccessBoundary(CredentialAccessBoundary.newBuilder().addRule(rule).build())
// Initialize the ClientSideCredentialAccessBoundaryFactory.
ClientSideCredentialAccessBoundaryFactory factory =
ClientSideCredentialAccessBoundaryFactory.newBuilder()
.setSourceCredential(sourceCredentials)
.build();

// Retrieve the downscoped access token.
// Generate the downscoped access token.
// This will need to be passed to the Token Consumer.
AccessToken downscopedAccessToken = downscopedCredentials.refreshAccessToken();
AccessToken downscopedAccessToken = factory.generateToken(credentialAccessBoundary);
```

#### Using Downscoped Access Tokens

A token broker can be set up on a server in a private network. Various workloads
(token consumers) in the same network will send authenticated requests to that
broker for downscoped tokens to access or modify specific google cloud storage
buckets.

The broker will instantiate downscoped credentials instances that can be used to
generate short-lived downscoped access tokens which will be passed to the token
consumer.

These downscoped access tokens can be used by the Token Consumer via `OAuth2Credentials` or
`OAuth2CredentialsWithRefresh`. This credential can then be used to initialize a storage client
instance to access Google Cloud Storage resources with restricted access.
Expand Down
Loading