Identity & Security
Identity & Security
Welcome back to our series on best practices for managing and securing your Google Cloud infrastructure at scale. In a previous post, we talked about how to use the open-source tools Forseti and Config Validator to scan for non-compliant tools in your environment. Today, we’ll go one step further and show you another best practice for security operations: the systematic use of labels.
Labels are created using the Cloud Resource Manager API. There are a lot of ways using labels can help you, but the most common use cases are security, operations and billing. We recommend using labels to add metadata to your projects or resources to include useful information like: team, project owner, on-call rotation, data classification, cost center, and compliance requirements (e.g., PCI, HIPAA, etc.).
To get the most out of labels, they have to be both reliable and accurate. In this article, we mostly cover the reliable part, but we also address some of the accuracy part as well, by showing you how to sanity-test the labels you create. Then, you’ll learn how to leverage example security policies (as constraint templates) to describe your security requirements in code, and use Forseti to look for resources that violate these constraints in your cloud environment.
To do so, we’ll use the label_enforce template in this example, with the goal of helping you to build a central repository in which to keep your security policies as code (in the form of constraint files), and to use both Forseti and Terraform Validator to look for non-compliant resources.
Defining your labels
Defining your labels is usually done before you deploy your project. Your organization may have some firm requirements about labeling that you must follow. For example, many companies have broad policies about labels and provide a custom list of required labels and a target location (e.g., when/where to apply them).
If you don’t have such a list, it is highly recommended that you get started writing your own. It could look something like this:
Note: Labels created with the Google Cloud Resource Manager API have specific requirements. Check out the latest official documentation to learn more.
In addition to the master list of labels, each application can add their own label requirements if they need more granularity. These requirements can vary depending on where in the organization your resources live.
For example, many organizations have a set of rules for top-level folders (say shared-services, BU1 and BU2) and a different set of rules for others (like Marketing or Finance). It is quite common for organizations to have more than one set of requirements for their labels.
You can enforce these various policies by building custom constraints from scratch or leveraging existing templates that are publicly available, either as-is, or as a starting point. (We’ll discuss how to write your own label templates in a follow-up article.)
Forseti Config Validator constraints
Forseti is Google’s sponsored open source project, and among many other things, it offers a set of predefined policies (constraint templates) for Config Validator, that you can use directly or modify to fit your custom needs. For example, if you want to ensure your infrastructure adheres to your global requirements, you can leverage the Forseti Config Validator tools and constraint templates.
Constraints are based on Open Policy Agent (OPA),which makes it easier to integrate with other systems and removes a lot of the burden of writing and maintaining your own policy engine / enforcer system. A best practice is to translate your security requirements into code, that all your security tools can reuse, for consistency.
Here is a high-level overview of how to implement security policies as code with a central policy library repository:
With this approach, you can enforce your infrastructure requirements by using constraints that are checked before new resources get deployed to GCP (using the Terraform Validator). You can also use the same constraints to continuously scan your infrastructure for potential violations. Finally, you can report these findings to the Cloud Security Command Center (Cloud SCC) to visualize the results. To learn more about this last part, please read the previous post in this series, which describes how to set up Forseti and Cloud SCC. This lets you catch non-compliant resources, regardless of how they got deployed to your environment in the first place.
Let’s take a closer look at the existing templates in the policy-library repository to understand how it all works:
As a user, there are three folders you should focus on: docs, policies and samples.
The docs folder contains user guides to help you get started. The policies folder contains all the published constraint templates that you can reuse to implement your company’s security policies. Each template comes with a sample constraint in the samples folder.
A template consists of a YAML definition of an OPA rule that describes what constitutes a violation based on the input the rule receives. The input that the rule receives is made of two components:
The asset (part of which is a GCP resource) that needs to be evaluated; this asset is aJSON object, following the Cloud Asset Inventory format.
The constraint parameters. These are used to let you customize what constitutes a violation based on your own requirements. This varies from template to template; you can look at the template file directly to learn about its required parameters and acceptable values.
In other words, you can reuse a constraint template in various ways by creating constraint files (also written in YAML) that implement a template, and craft parameters so that non-compliant resources will be flagged as violations by Forseti.
Each policy in the library has an example of how to use its associated template in the samples folder. Let’s take a look at the enforce_label sample constraint, to learn more about its usage:
# required parameter: list of label objects that resources should have.
# A label object is composed of a key value pair like:
# "label_key": "label_value_regex_to_match"
# Any missing label results in a violation. For instance a resource with no label1 or label2 label,
# in this sample case, would raise 2 violations: one for label1 being absent and one for label2.
# In the same spirit, a resource with label1 or label2 present, but with values not matching their respective regex
# would also raise one violation per mismatch.
# In the following example, valid values for a label named "label1" would be only "label1-value",
# but a label named label2 could have various values like "label2-value", "label2-valueOK" etc.
# A violation is raised if the label value does not match the pattern passed as a parameter here.
- "label1": "^label1-value$"
- "label2": "^label2-value.*$"
# optional parameter: list of resource types to scan for labels
# any resource that is not of these types will not raise any violation.
# In this sample use case, only non-compliant projects and buckets would be flagged.
# If not passed, all tested resource types would be scanned for (see template for full list)
In order to use a template, you must refer to its “kind” value (computer-friendly name). In the above template, the kind value is “GCPEnforceLabelConstraintV1” which you need to reference in your constraint.You can also pass some parameters to it, to describe the normal state of your GCP resources, i.e., which labels should always be there, and what kind of value they should have.
The only required parameter is “mandatory_labels”, which is basically a list of key/value pairs, where the key is the actual label key you want to check for, and the value is the pattern this label’s value should follow (a simple regex).
For instance, a good value could be something like:
- "call-center-region": "eu-*"
In this example, the enforce_label template accepts any value for the label “call-center-region” that matches the regex “eu-*”.
There is also an optional parameter “resource_types_to_scan”, which lets you specify which resource types need to have these labels. A common use case for this is to mandate labels at the project level only, meaning that the resources within the project do not need to have the labels attached directly. If this optional parameter is not passed, the template uses a default list of resource types that have been tested. You can find this list in the template file.
If you need to use this template for a resource type that has not been tested, this type should still be supported as long as the labels object for your resource is in the default location (i.e., under resource.data.labels in your Cloud Asset Inventory export). If not, feel free to either fork the code and create a pull request for your new type, or create an issue for us to add the missing resource type.
Going back to the original task, if you need to enforce the “cost-center” label from the previous section, you can define it in your constraint like this, specifying that “fin-us”, “mkt-eu” and “it-jp” are acceptable values for this label.
Any resource where this label is missing or the value does not match this regex, will raise a violation, if they live within the target location:
- "cost-center": "fin-us|mkt-eu|it-jp"
In general, it is easier to get started using one of the sample constraints in an existing templates. Once you have a satisfactory constraint, simply copy over the policies/constraints folder and commit your changes to your local repository.
Note: Only the constraints in the policies/constraints folder will be used by Forseti (or other tools).
It is recommended that you use a CI/CD pipeline to automate your policy deployments and audit every change. The target artifact of your policy pipeline will probably be a Cloud Storage bucket (for Forseti) that should be highly restricted. You do not want to let people update your policies manually, for obvious security reasons!
Finding mislabeled resources
Once Forseti successfully sends out its findings to Cloud SCC, you can visualize them by selecting the Forseti Tile on the Cloud SCC dashboard and selecting the “Config Validator
Violations” item on the “findings” menu (left).
If you click on a single violation on the right side, you get more information about it:
In this case you can see at the bottom that the label in violation is “cost-center” for this project (“my-invalid-project”). You can fix it by verifying the labels for this project in the resource manager:
As you can see, this project does have the correct labels set (cost-center and owner), but the cost-center label has a invalid value (fin-123 does not match mkt-XXX). If you fix it and scan again, you should see this violation disappear from the Cloud SCC default dashboard (you can always find it back by adjusting the time in the Cloud SCC UI).
Now let’s run the Forseti script again (or wait at most two hours for the next cron job to be triggered).
And make sure you have one less violation this time:
You now have a system in place that can continuously scan your cloud infrastructure and make sure the environment complies with policies that can be audited at any time. Having these policies in a separate repository lets your security team enforce your security requirements in one place, without having to manage the underlying infrastructure.
In follow-up articles you will learn how to get started writing your own constraint template. Then you’ll see how to re-use these same policies to prevent non-compliant resources from being deployed in the first place, using the terraform-validator and a CI/CD pipeline for your terraform deployments.
OPA / rego: