Google Cloud Storage Access Analysis

Identity and Access Management (IAM) for Google Cloud uses IAM Role Bindings, which link three constructs together:

  1. Principals: Users, Groups, Domains or the Public as a whole
  2. IAM Roles: Permissions for cloud actions
  3. Resources: Any cloud resource with a resource identifier

This structure mirrors how JupiterOne models Microsoft Azure role-based access control (RBAC), as described in our previous article, Azure Access Review using Optional Traversals in JupiterOne. The following scenario demonstrates how to generate the primary J1QL query to secure a Google Cloud Storage bucket.

Scenario: Query IAM Role Bindings

You have the answer to life, the universe, and everything...

42


… and you want to keep it safe in a super-secret storage bucket in Google Cloud. In order to make sure no one else can manipulate it, you decide to do an access analysis of your storage bucket. The first thing to check is to see if there are any nefarious individuals that have admin access to your bucket.

Using JupiterOne Query Language (J1QL), you can query for the IAM Role Bindings that are attached to this bucket which allow the permission  storage.admin .

/* Resource */
Find google_storage_bucket
with name='j1-gc-integration-dev-v3-super-secret-stuff'

/* Binding */
that ALLOWS AccessPolicy
with permissions='storage.admin'

/* Principal */
that ASSIGNED (User|Domain|UserGroup)

return TREE

 

Google Cloud Storage - JupiterOne - 01

Well, that’s not good!  Not only does this shady mknoedel individual have admin access to your super-secret storage bucket, so does everyone else! These bindings need to be removed.

Google Cloud Storage - JupiterOne - 02

Job well done! Your secret is now safe… or is it? Google Cloud uses a Resource Hierarchy, which allows users to attach role bindings at different organizational levels, with each level being a potential vector for granting access to your bucket.

Google Cloud Storage - JupiterOne - 03

You’ll need to check those bindings as well, just to be safe. Using J1QL, you can query for the IAM Role Bindings that are attached anywhere on the organization hierarchy which allow the permission storage.admin .

/* Resource */
Find google_storage_bucket
with name='j1-gc-integration-dev-v3-super-secret-stuff'

that HAS google_cloud_api_service

/* Recursively check for project, folder, and org bindings */
(that HAS google_cloud_project)?
/* folders can be nested so be sure to query multiple levels */
(that HAS google_cloud_folder)?
(that HAS google_cloud_folder)?
(that HAS google_cloud_folder)?
(that HAS google_cloud_organization)? as policyLevel

/* Binding */
that ALLOWS AccessPolicy
with permissions='storage.admin'

/* Principal */
that ASSIGNED (User|Domain|UserGroup)

return TREE

Don’t know what the `(...)?` syntax means? Check it out here!

organization-traversal

Good thing we checked! This unknown user mknoedel is granted admin access to our bucket at all levels of the organization hierarchy. Let’s remove these bindings. 

Cloud Storage supports “Convenience Members”, which are a special set of principles that can be applied specifically to IAM bucket policies. A convenience member acts as a bridge between the principals granted a basic role and an IAM Role. The IAM Role granted to the convenience value is, in effect, also granted to all principals of the specified basic role for the specified project ID.

Using J1QL, we can find all the bindings that have Convenience Members that escalate the permissions of basic roles to  storage.admin  for our bucket.

/* Resource */
Find google_storage_bucket
with name='j1-gc-integration-dev-v3-super-secret-stuff'

/* Binding */
that ALLOWS AccessPolicy
with permissions='storage.admin'

/* expand "Convenience" members */
(that ASSIGNED >> AccessRole)?
(that USES << AccessPolicy)?

/* Principal */
that ASSIGNED (User|Domain|UserGroup)

return TREE

Google Cloud Storage - JupiterOne - 05

Gotcha! Anyone who has a Google account with a domain of “mknoedel.com” has admin access to our super-secret bucket. You’ll need to remove this binding as well.

Pulling it All Together: Access Analysis with J1QL

You can do the entire access analysis with the J1QL query: Who has  storage.admin  on the  j1-gc-integration-dev-v3-super-secret-stuff  key vault?

/* Resource */
Find google_storage_bucket
with name='j1-gc-integration-dev-v3-super-secret-stuff'

(that HAS google_cloud_api_service)?

/* Recursively check for project, folder, and org bindings */
(that HAS google_cloud_project)?
/* folders can be nested so be sure to query multiple levels */
(that HAS google_cloud_folder)?
(that HAS google_cloud_folder)?
(that HAS google_cloud_folder)?
(that HAS google_cloud_organization)? as policyLevel

/* Binding */
that ALLOWS AccessPolicy
with permissions='storage.admin'

/* expand "Convenience" members */
(that ASSIGNED >> AccessRole)?
(that USES << AccessPolicy)?

/* Principal */
that ASSIGNED (User|Domain|UserGroup)

return TREE

full-access-graph-with-markup (1)

Takeaways

With J1QL you gain a deep insight into who has access to your Google Storage buckets. Going even further, you can use the sort of analysis examined above to easily evaluate how your infrastructure lines up with Google Cloud’s IAM Best Practices:

  • Use the principle of least privilege when granting access.

    Am I using the
    principle of least privilege when granting access to my buckets?

    Use the above query to search for storage.admin over all buckets. When found, reduce the scope to something less aggressive like storage.objectReader.

  • Avoid granting roles with setIamPolicy permission to people you do not know.

    Does anyone with an email not in the domain jupiterone.com have setIamPolicy permission to my buckets?

/* Resource */
Find google_storage_bucket

(that HAS google_cloud_api_service)?

/* Recursively check for project, folder, and org bindings */
(that HAS google_cloud_project)?
/* folders can be nested so be sure to query multiple levels */
(that HAS google_cloud_folder)?
(that HAS google_cloud_folder)?
(that HAS google_cloud_folder)?
(that HAS google_cloud_organization)? as policyLevel

/* Binding */
that ALLOWS AccessPolicy
with permissions='storage.setIamPolicy'

/* expand "Convenience" members */
(that ASSIGNED >> AccessRole)?
(that USES << AccessPolicy)?

/* Principal */
that ASSIGNED (User|Domain|UserGroup)
/* Optionally unwind Groups and Domains to Users */
(that HAS User)? as principal

where principal.email!='jupiterone.com' AND
principal.domainName!='jupiterone.com'

return TREE
  • Be careful how you grant permissions to anonymous users.

    Have I assigned any of my storage buckets to Everyone?
/* Resource */
Find google_storage_bucket

(that HAS google_cloud_api_service)?

/* Recursively check for project, folder, and org bindings */
(that HAS google_cloud_project)?
/* folders can be nested so be sure to query multiple levels */
(that HAS google_cloud_folder)?
(that HAS google_cloud_folder)?
(that HAS google_cloud_folder)?
(that HAS google_cloud_organization)? as policyLevel

/* Binding */
that ALLOWS AccessPolicy

/* expand "Convenience" members */
(that ASSIGNED >> AccessRole)?
(that USES << AccessPolicy)?

/* Principal */
that ASSIGNED Everyone

return TREE

Resources

 

JupiterOne - Smart Search Data Sheet
J1 Smart Search enables you to query any of your resources, environments, and all of your cyber asset relationship data.

 

avatar

Posted By Michael Knoedel

Michael is a full stack developer who has a passion and a talent for design. He's engaged in a career in computer science that not only allows him to solve highly-challenging technical problems which he has proven to excel at, but also allows him to use his extensive design skills to facilitate excellent user experiences. He is a disciplined and talented developer who can do it all.

To hear more from Michael, get our newsletter. No spam, just the good stuff once or twice a month. Sign up below.

PREVIOUS ARTICLE