SPEAKER 1: Hi, folks, and welcome to the session
on advanced IAM, where we'll be covering tips and tricks
around IAM policy management.
I'm Blake, a product manager here at Google
where I primarily focus on the Cloud Identity and Access
Management product, which serves as a central point for access
policy management and authorization
enforcement across the Google Cloud platform.
We're here today to dive a bit deeper than usual
into how you manage your access policies,
particularly through the API, as well
as best practices in this area.
We're going to be taking a look at the policies themselves,
where they can be configured across your cloud
infrastructure, how, based on your policies,
Google determines when to grant access to a user,
different versions of the policy schema
that are available, and finally, how you can manage
your policies more safely by following
some simple best practices.
Let's look at how IAM policies appear themselves
when you're managing them through the G Cloud command
line or directly through the API.
Amongst some other aspects that we'll get into in a bit,
they're primarily made up of bindings.
In this example, you can see the first binding grants
the storage admin role to an individual user, Alex,
as well as to a group called Admins.
There's a second binding in this policy
that grants Bob the compute admin role.
Roles, of course, are groups of granular permissions in GCP.
The storage admin role would let you
create buckets and retrieve or store objects in those buckets.
The compute admin role would contain
the permissions to do things like create VM instances.
So you may have noticed that the policies only
talk about who can do what.
They didn't indicate the where--
in effect, in what areas of your cloud infrastructure
they can perform these actions.
That's where policy attachment comes in.
Your cloud infrastructure in GCP is organized
in the resource hierarchy.
All of it exists under the Organization node,
which typically represents the company you work for.
Under this, you can create folders
for further categorization, such as business units, regions,
or environment types.
Ultimately, though, your cloud resources themselves,
such as the storage buckets, VMs, or database instances,
will end up being created and managed in Projects.
Now IAM policies can be attached to any of these nodes.
The higher they're placed in the hierarchy,
the more they grant access to.
This is because the effects of policies inherent down.
Because projects usually end up being a logical grouping
of resources that represent a particular workload,
they're a frequent attachment point for IAM policies
to keep the effect of those policies scoped
to just that workload.
However, for many resource types,
it is possible to set the policy even lower.
You could, for example, grant a user access just
to a single VM in a project rather than all of them.
So let's take a look at how you can set policies
at different levels in GCP.
The policies are retrieved or modified
through APIs that are made available
by the service related to where you want to set the policy.
For example, setting policies and projects, folders,
or the Organization node is all done through the resource
manager service.
Or setting a policy on a particular Pub/Sub topic
would be done through the Pub/Sub service.
The method to modify IAM policies
is called setIamPolicy.
Here, you can see it exposed on the Resource Manager
endpoint for projects or the Pub/Sub endpoint related
to topics.
But how can you find which resource types support
IAM policy attachment and which APIs to call for each?
One good place to start is the API reference documentation.
Start by looking up the reference for the service
related to where you want to set a policy.
Next, find the resource type upon which
you wish to set that policy.
And lastly, look for the setIamPolicy method.
Note too, where this exists, there
will also be a getIamPolicy method,
which is what you can use to retrieve any existing policy
attached to that resource.
All of this documentation is available
on cloud.google.com/docs.
But what if you want to get a bit fancier?
Google provides an API discovery service
which itself is an API you can use to pull information
about APIs that Google makes available, including
the methods they provide.
Let's take a look at the listApis call.
As you probably expected from its name,
it returns a list of Google APIs you can use.
In the example here, we're looking
at the compute API entry that was returned from this call.
This, of course, refers to the Google Cloud Compute Engine
API.
In its listing, it also includes the discovery rest URL
for the service.
Let's query that.
In reviewing the discovery document for a specific API,
you can see all resource types it exposes
and the methods supported on each of those resource types.
So for example, we can see here that there
is a setIamPolicy method available for OS images,
meaning that you can set different IAM
policies on different OS images if you wish.
For you to set the right policies,
it's helpful to understand how Google evaluates them
when making access decisions.
Referring back to our resource hierarchy,
let's say Alice wants to delete a particular bucket,
called bucket A, inside of this project.
This is the IAM policy set on that project.
In it, we can see a binding that grants Alice the storage admin
role.
So to IAM, what are our inputs here?
To evaluate, we effectively need to determine
if a certain principle, Alice, has the storage buckets delete
permission to be used against bucket A. To do so,
we need to grab any potentially relevant IAM
policies in the hierarchy.
This includes grabbing the policy
on the bucket itself, the project it's in,
the folder that project is under,
and the policy on the organization node itself.
So we have our query, and we have all the relevant policies
here.
Evaluation ultimately happens binding by binding.
The default state is not to grant access
unless a policy explicitly says to.
Starting at the bucket, we can see its policy contains
no bindings.
So this policy does not grant access.
In moving up to the project, however,
we see our binding from before that grants Alice the storage
admin role.
Because Alice is indicated in the policy
and the storage admin role contains the storage
buckets delete permission, this binding would grant access.
In continuing, however, we encounter another binding.
Alice is not listed in this binding,
nor does the computer admin role contain the needed permission.
At the folder level, we see that group Foo
is granted the owner role.
But because Alice is not in that group,
this binding also does not grant her access.
This is similar when looking at the policy on the organization
node.
Alice is neither a member of the Admins group,
nor is she a member of the security group.
All that said, the evaluation decision
here is to allow her to delete the bucket.
This is because all IAM bindings are additive.
If any grant access, the action will be allowed.
Especially when you have a large amount of infrastructure
deployed to Google Cloud, it can sometimes
be difficult to determine whether a user does or does not
have access to perform a given action on a specific resource.
To help you better understand and visualize the evaluation
logic we just went through, Google
provides a tool in the cloud console web UI called
the policy troubleshooter.
In it, you can enter exactly the same three pieces of input
we just talked about--
the principle, the permission, and the resource.
Policy troubleshooter will then show you the effective access
decision as well as all the bindings and their effects
in each relevant IAM policy.
This can help you make adjustments to policies
if the resulting access decision is not what you intended.
To recap, where you attach policies matters.
You can simplify policy management
by setting them further up in the hierarchy
to grant more access but also lower down on the project
or on individual resources when access
needs to be more fine grained.
All bindings are additive, so it's
important to understand the bindings you've configured
to ensure you're not granting access to someone you did not
intend.
Understanding the permissions in the roles you're granting
and who are members of the groups to whom you've
granted access as well as, of course,
who controls the membership in those groups,
are both important aspects of making informed policy
management decisions.
And finally, you can use tools that Google
provides to better understand the effective state
of your policies.
One more thing before we move on just because sometimes this
trips people up--
for some types of permissions, they're
checked relative to the parent node
compared to where you might think that they would be.
Let's take the example of Alice trying to create a bucket.
Here, we see she's exercising the storage buckets create
permission.
But the resource upon which this check is being performed
is the project in which she's trying to create the bucket.
These parent checks occur on permissions
related to creating new resources
as well as listing resources within a certain node.
So particularly for list permissions,
make sure you're granted these on the parent container
if you wish for a user to be able to enumerate
all resources of a certain type within that container.
Moving on, IAM has added some new features
since it first launched.
Recently, one such feature required
us to version the schema of the policy content itself.
Let's take a look at a basic policy example.
In February of this year, though, we
introduced a feature called IAM conditions, which
allows you to configure when bindings should apply or not.
Here, you can see a condition was added to the binding that
states its effect is only valid for requests
made before January 1 of 2021.
But you might have also noticed the presence
of this condition bumped the version field in the policy
from 1 to 3.
When IAM originally launched, there
were two assumptions baked into the policy schema.
One was that each finding included
one role and a list of members, and in any given policy,
there would only be one binding for a given role.
Conditions fundamentally changed this assumption.
Now you could have two bindings for the same role granted
to different people under different conditions.
This has an effect on how you retrieve policies
as well as how you modify them.
When retrieving a policy using getIamPolicy,
you can specify which schema version you're requesting.
If you request version three, it will return the policy
with the conditions objects included.
If, however, you specify version one
or do not specify a version, you will not
receive all the details of the conditions.
Similarly for setIamPolicy, if you're
submitting a policy that includes conditions objects,
you must set its version field to three.
Taking a look here, if you ran getIamPolicy
with the requested policy version set to three
you'll see the same return we saw
earlier with the conditioned object intact.
However, schema version one is requested
for a policy that contains conditions,
you'll only see an indicator that there
is a condition on a binding via a hash concatenated
with the granted role name.
In reality, though, this is pretty simple.
For existing and future programmatic integrations
that need to read or write IAM policies,
just ensure they understand the syntax and semantics associated
with the version three schema.
Namely, this means your code should understand
that there can be multiple instances of the same roll
across bindings in a given policy
and that some of those bindings may include a condition.
So as long as this is the case, you
can always request version three on getIamPolicy
and set version three on setIamPolicy.
Finally today, let's talk about best practices
when it comes to safely modifying IAM policies.
Critically here is understanding the get modify set flow.
To modify any IAM policy, you must first
retrieve the current state of the policy.
This is done by executing a getIamPolicy call
against the resource to which policy is attached.
Once retrieved, your code can modify the policy in any way
necessary.
For example, you may wish to add an additional binding
to the policy--
in this case, giving Bob the compute admin role.
Once you or your code have made the desired changes,
the entire policy is submitted back
using the setIamPolicy call.
In this example, that includes any bindings
that were already there, along with the additional binding
that you've added.
A common mistake many people make early on in using IAM
is only submitting their changes in setIamPolicy
rather than the entire desired state of the policy inclusive
of their changes.
And any local modification performed by you or your code
should follow the rules associated
with the policy schema version being requested or set.
Another best practice is ensuring
appropriate concurrency control is in place for your policy
modifications.
Let's take a look at an example where Alice and Bob are both
trying to modify a policy at roughly the same time.
First, Alice executes getIamPolicy
as the start of her get modify set sequence.
She or her code then begin making any desired
modifications to the policy locally.
Bob then kicks off his get modify set flow by requesting
the policy currently set and begins his local modifications.
Here is code then complete their changes
and successfully set the updated policy.
Shortly after, however, Alice's local modifications
are complete, and her updated policy is set.
This, of course, has a very unexpected outcome.
Because the basis on which Alice's local modifications
were performed was the result of a getIamPolicy issued
before Bob's setIamPolicy, Bob's changes
were effectively overwritten by Alice's setIamPolicy.
To prevent situations like this where two people may
accidentally step on each other's changes,
IAM supports e-tags to provide concurrency control.
When retrieving an IAM policy, it
will include an e-tag field that represents
the state of the policy as it exists at that time.
During your modification, be sure to preserve
this e-tag field exactly as it was returned from getIamPolicy,
then ensure the same e-tag is included in the payload
during your setIamPolicy.
If the policy on that resource has changed since you last
retrieved it, your setIamPolicy call will appropriately fail.
So to recap, again, always preserve the e-tag field
all the way through the get modify set flow.
If the policy on a resource has changed,
your setIamPolicy call will fail with an HTTP 409
conflict response.
And in this case, your client can simply
retry the entire get modify set operation so as
to incorporate whatever the latest state of the policy
is into its changes.
While not directly related to the access management
focus of this breakout, IAM policies
do contain a field related to the audit logging configuration
called AuditConfigs.
When performing changes intended to grant or revoke access,
just be sure not to drop or otherwise modify
this field, as you may inadvertently change your audit
logging configuration.
Great, so let's pull what we just
went through in this breakout together into a single example.
Here, we have a Python script intended
to add Alice to a project's IAM policy,
granting her the computer admin role.
Note that we first begin by retrieving the policy as it
currently exists on the project and doing
so with the requested policy version of three
because our code complies with all the rules defined
in the version three schema.
We store that retrieved policy locally.
Now moving on to the second part of the Python script,
we can begin our local modifications.
You'll know that we focus our change specifically
to the binding section of the retrieve policy,
simply appending Alice and her new role grant
to the retrieved policy.
Keeping the changes focused here safely
preserves the tag and AuditConfigs fields
as originally retrieved.
Next, because we know our code complies
with the semantics of the policy three schema version,
we can go ahead and set the policy version to three.
While there weren't any conditions included
on the new binding in this specific example,
your code will probably need to accept changes
of many different types.
Some of those may include conditions.
As a result, any IAM clients you write
can always express version three in their set IAM policy calls
so long as the version three semantics
are always safely followed.
Finally, we submit the updated policy
to Google Cloud using the SetIamPolicy call.
Last recap of the breakout-- make
sure to preserve the e-tag throughout the policy
modification process.
Only touch the fields you need when modifying,
and make sure any programmatic integrations
you develop follow the assumptions
baked into the version three policy schema.
And that's all I have for today.
I'd like to thank everyone for watching.
And if you can't wait to get even more IAM info,
we have a couple other great sessions
dropping this week I encourage you to check out
covering IAM Recommender and Policy
Intelligence, both of which can be
invaluable in helping you achieve least
privilege in your environments.
Thanks again.
Have a great day.