Google Cloud Storage is Google’s storage service for storing and retrieving data with high reliability, performance, and availability. Storage services tend to be a weak point in terms of security for many companies and organizations, as they often contain sensitive information but are hard to configure correctly.
Here you can find a thorough examination of Google Cloud Platform’s (GCP) storage service, how to access buckets, and how to make sure your buckets are configured as intended.
GCP’s Cloud Storage service lets you create buckets and store objects in them. Each bucket, like all other GCP resources, resides in a project following the structure below:
Within a bucket, you can create objects of your data and manage access to every level of its hierarchy, starting at the project level, drilling down to the bucket/object level (depending on the bucket's configuration).
Before we take a deeper dive into how to access buckets and objects in GCP, here are a few terms you should understand and with which you should be familiar:
- Basic/Primitive Roles - Legacy roles predate Identity Access Management (IAM). They contain an extensive number of permissions and are very powerful. The three basic roles are Owner, Editor, and Viewer, in descending order of power. When you examine a bucket’s permissions, you will see that some IAM bindings are added automatically. Among them are bindings for the project owners, editors, and viewers.
For example (IAM permissions for a freshly created bucket):
gsutil iam get gs://MY-BUCKET
- “allUsers” - A special identifier that represents anyone on the internet. Not all resource types support this principal type. Among them is the project resource, meaning that you cannot assign project-level permissions to allUsers.
- “allAuthenticatedUsers” - A special identifier that represents all service accounts and users on the internet who have authenticated with a Google Account. This includes personal Gmail accounts. Users who are not authenticated, such as anonymous visitors, are not included. Not all resource types support this principal type, among them is the project resource, meaning that you cannot assign project-level permissions to allAuthenticatedUsers.
Access to GCP buckets
In managing access to buckets and objects, you can choose between IAM and Access Control Lists (ACL).
- IAM – The permission mechanism in all of GCP’s resources. IAM provides you with the ability to granularly grant access to specific resources and prevents access to others. If you are not familiar with IAM check GCP’s documentation.
- ACL – A mechanism used to define who has access to your buckets and objects and what level of access they have. ACLs consist of two parts, permission and scope (or grantee). The former refers to actions that can be performed (such as read and write), and the latter defines the “who” (user, group, and more). For more information about ACLs, visit GCP’s documentation.
IAM Permissions vs. ACLs Although it seems as if IAM permissions and ACLs are two different mechanisms, they are the same. When adding an ACL, it is translated to IAM Permissions. Each ACL’s level of access is equivalent to an IAM role:
Here is a short demonstration:
Let's first take a look at the IAM and ACLs of a new bucket.
gsutil iam get gs://MY-BUCKET
gsutil acl get gs://MY-BUCKET
Now, let's add an ACL that gives read access to all users on the bucket level.
gsutil acl ch -u AllUsers:R gs://MY-BUCKET
Let's again check the IAM and ACLs of our bucket.
gsutil acl get gs://MY-BUCKET
gsutil iam get gs://MY-BUCKET
We can see that when the ACLs of our bucket were updated, the IAM was updated (automatically) as well.
With some bucket configurations, it is possible to set different access levels for different objects in the same bucket. The relationship between ACLs and IAM permissions remains consistent when we use them on the object level.
Another thing to keep in mind is that the other way around (e.g., IAM to ACL) is not that simple:
- Assigning one of the bucket's legacy roles (ACL roles) as IAM permissions at the bucket level will appear in the bucket’s ACLs.
- Assigning one of the object’s legacy roles (ACL roles) as IAM permissions at the bucket level, will not appear in the bucket’s ACLs or in the object’s ACLs (although it applies to them).
- Assigning a role that is not a storage legacy role (it can be a predefined role or a custom role, with or without storage permissions) as IAM permissions at the bucket (or object) level, will not appear in the ACLs.
Also worth mentioning here is the storage.buckets.setIamPolicy. If a user has this permission on a bucket, he can add and remove permissions related to the bucket and its objects. For instance, he can assign himself the “roles/storage.admin” role that contains storage.buckets.* and storage.objects.*. This is equivalent to granting full access to the bucket and its objects. It is possible to control the objects’ permissions from the bucket’s IAM policy because GCP’s resources are organized hierarchically. Every resource inherits permissions from all of the resources which precede it. Objects are inferior in the hierarchy to the bucket in which they reside, so each permission granted at the bucket level will apply to all of the bucket’s objects.
Access Control Types
When you create a bucket, you need to choose whether you want to apply permissions using uniform or fine-grained access:
- Uniform - Uniform bucket-level access only allows you to use IAM to manage permissions. The IAM permissions will apply to all objects contained in the bucket. It also allows you to use features that are not available when working with ACLs, such as IAM Conditions and Cloud Audit Logs. When you enable uniform bucket-level access on a bucket, ACLs are disabled, and only bucket-level Identity and Access Management can grant access to that bucket and the objects it contains. If you change the access control to uniform after the bucket is created, you revoke all access granted by objects’ ACLs and the ability to administrate permissions using bucket ACLs.
- Fine-grained - With fine-grained access you can use both IAM and ACLs for the bucket as well as for individual objects. It is important to understand that this still means that if you give permissions such as read objects at the bucket level, it will apply to all objects it contains.
How do you choose between uniform and fine-grained access? In most cases, you should select uniform access, as it leaves less room for confusion and minimizes misconfigurations. However, if you do need to customize access to individual objects within a bucket, or if you need to migrate data from Amazon S3, you will have to choose the fine-grained option.
In GCP Cloud Storage, a bucket is created with no public access allowed. As for objects, it depends on their bucket. If there is public access to objects granted at the bucket level, each object, old or new, will be public. Otherwise, they will not be publicly accessible by default.
To make a bucket public, you are going to need to grant the desired permissions to the “allUsers” or “allAuthenticatedUsers” groups. For example, if I want to give read access to a specific object to everyone, I can run the following commands:
gsutil acl ch -u AllUsers:R gs://BUCKET_NAME/OBJECT_NAME
gsutil iam ch allUsers:legacyObjectReader gs://BUCKET_NAME/OBJECT_NAME
As a direct result, if you do not grant any storage permissions to “allUsers” or “allAuthenticatedUsers” groups, your bucket will remain private. For a safety measure, GCP offers the ability to enforce public access prevention. Using this ability revokes existing public access permissions (IAM and ACL) and prevents adding new permissions that allow public access. It does not matter if the bucket access control is fine-grained or uniform, any existing IAM policies and ACLs granting access to "allUsers" and "allAuthenticatedUsers" will be overridden (but not removed).
GCP Public Bucket Evaluation
GCP gives each bucket a public access evaluation and presents it in the management console.
There are three levels of access:
- Public to the internet - The evaluation logic is simple - the prevent public access ability is disabled, and there are one or more bucket level permissions (IAM or ACLs) that give access to “allUsers” or “allAuthenticatedUsers,” the bucket will be categorized as public. This evaluation will remain even if the given permissions are irrelevant. For instance, assigning a custom role with just the compute.instances.create permission to “allUsers,” still receives the “public to the internet” evaluation.
- Subject to object ACLs - The bucket access control is fine-grained, the prevent public access ability is disabled, and there are no “allUsers” or “allAuthenticatedUsers” permissions given at the bucket level. Whether there is public access to objects depends on the permissions of each object. If there are public access permissions granted on the object, it will be public. Otherwise, it will be private. This status will only appear when the access control is fine-grained since it is not possible to assign permissions per object when the access control is uniform.
- Not public – The prevent public access ability is enabled, or the bucket access control is uniform, and there are no “allUsers” or “allAuthenticatedUsers” permissions given at the bucket level. Buckets with fine-grained access control will get the “Not Public” evaluation, only when the prevent public access ability is on.
Below is a simple flow chart:
Why GCP’s Evaluation is Problematic
There are a few issues with GCP’s evaluation. To begin with, the “Public to the internet” evaluation does not necessarily point at a public bucket. If the allUsers permissions do not allow them any storage access the evaluation is wrong. However, the more critical issue is with the “Subject to object ACLs” evaluation. This evaluation does not provide us with enough information or detail on whether our objects are secure. We must go over each object’s ACLs to get a complete picture.
How Lightspin Improves Visibility into your Buckets and Objects
To help you deal with these issues, Lightspin has created an open-source Python tool you can use to get a full picture of your buckets and objects. It provides you with information about buckets and objects that are publicly accessible, alongside the actual access level to them. Check it out!
Tips to follow for best practices
- Try our tool! It will show you your public buckets and objects so you can make sure everything is set as intended.
- Don't be complacent. Make checking your bucket and object configurations a periodic practice - you can never be too careful when it comes to buckets and correct configuration :)
- Stick to uniform. Try to use uniform access control for your buckets rather than fine-grained. Uniform buckets are much easier to manage, leave less room for errors, and the ability to prevent public access works as expected.