When building out APIs on with Cloud Run, it’s easy to get lost in the fun of building and deploying and seeing your service running live without much thought to the rest of your cloud infrastructure. What permission does your service account that’s running in Cloud Run have access to? In this post I’ll show you how to secure your application with an example application that connects to a Firestore database with the application of the principle of least privilege.
Google Cloud makes it really easy to get up and running quickly with a number of quick start guides, easy CLI tools and “click-ops” options if you’re so inclined to deploy your cloud services. But a number of those guides don’t really talk to how to make sure you’re not handing over the keys to your kingdom (read: cloud infrastructure). By default the compute engine service account which is automatically created for you as part of the project creation process (unless you’re in an organisation with service controls) will have very wide ranging permissions to be able to perform an number of tasks. This includes things like running VMs or building and publishing code artifacts to the artifact registry. The ability to connect to Cloud SQL or Big Query and so on. These permissions can be damaging if one of your APIs are compromised as the attacker will now have programmatic access to your cloud and be able to do all sorts of nefarious activities.
Thankfully, theres a number of ways to remediate this problem by understanding what kind of activities and services your API uses and then ensuring that it has the right permissions to do what it needs to, and nothing else. For example, you might want to give your API the ability to connect and perform SQL queries in a Cloud SQL instance, but that API has no need to talk to Big Query, nor anything else.
In this example, let’s use Firestore as our database, and we’ll setup a service account that our Cloud Run service will use which has just the right permissions to be able to read and write data from the Firestore database, but nothing else.
Let’s start by creating a role which the service account will be able to use, that has just the right amount of permission required to read
write and delete data from Firestore. In the web console, you can create a service account by accessing the
IAM and admin in the left menu
main. Then click
Create Role from the top menu. On the menu that appears, give your role a title, a description (not
required, but I recommend), an ID, and select a launch stage. You can leave launch stage as the default if you want or you can select
General Availability. The launch stage is simply informational.
With the basic info of the role set, now it’s time to assign the permissions we need. Still on the
Create Role screen, select
Add Permissions. In the filter, type in
datastore.entities and then it will filter down to the entities sub-permission of Firestore. In
this case we want to allow our API to only have permission to read, write, and delete records in our Firestore database, so let’s add the
With the five check boxes ticked, click
Add. Then, back on the
Create Role screen, click
Create. This will create our custom role.
Now it’s time to create the service account and assign it the minimum permissions via the role we just created. On the left menu of IAM,
Service accounts in the left sub-menu. On this page, you can see the existing service accounts which are already setup on your
project. Up the top, add a new service account by clicking the
+ CREATE SERVICE ACCOUNT option. On the menu that appears, give the service
account a name, which will auto populate an ID for this service account. Optionally, give the service account a description which will make
it easy to find and understand it’s intent when you try to find it again later on, then click the
Create and continue button.
Select Role section that appears in step 2 of creating a service account, click on the dropdown, and then choose the
option in the
Quick Access section. There you’ll see the custom role we just created. Select it, and then click on the
Done menu option
down the bottom. This will create the service account.
Now that we have a service account with the minimum permission needed to read and write to Firestore, let’s assign it to our Cloud Run API.
Navigate to your Cloud Run service, and then choose to
Edit and Deploy a new version. On the
Security tab, use the dropdown to select a
new service account and select the service account we just created. Then click
Congratulations! You just secured your Cloud Run API with the minimum permissions it needs in order to securely run in the cloud.