Configure Google Cloud Armor using OpenAPI

Christoph Grotz
Google Cloud - Community
4 min readJan 6, 2022

TL;DR I built a simple tool for generating Cloud Armor security policies from OpenAPI 3 specifications. Go here to check it out https://github.com/GoogleCloudPlatform/professional-services/tree/main/tools/openapi-to-cloud-armor

Using a Web Application Firewall (WAF) to protect your applications against threats can be considered a best practice. Managed, cost efficient services like Google’s Cloud Armor provide us with protection against common attacks like SQL injection, Cross-site scripting, or even new vulnerabilities like the recent log4shell (link).

To improve the security posture even more it’s a great idea, to configure WAF rules based on the applications API. But this can be a cumbersome task. Ideally, you would want changes on your API to propagate automatically to your WAF rules as part of your CI/CD pipelines.

If you have an OpenAPI specification, either through an API-first approach (link), or generate one from your implemented code (link), you have a great basis for doing exactly this. The OpenAPI specification contains the various operations that can be called on your API, and thus could be parsed to create the rules for your WAF based on the HTTP methods and paths.

The OpenAPI-to-Cloud-Armor converter I implemented runs through an OpenAPI specification and creates a Cloud Armor security policy based on the paths and HTTP methods in the specification. The rule has a default deny with low priority, valid paths and methods will result in an allow.

Cloud Armor has some limits and quotas we need to work with in regards to rule size, rule amount and rule length:

  • There is a quota, that we can have at most 20 rules using expressions in a project.
  • A rule using the expression language can only have 5 subexpressions
  • There can only be one regex match per expression
  • An expression can’t be longer then 2048 characters and a subexpression can’t be longer then 1024 characters

This means for large rules, we might need to make some compromises and optimisations in regards to rule structure. This means that an approach for having one rule per API operation is not going to work, and we need to combine methods and paths. The converter can therefore be run in three modes, that can be chosen via the corresponding flags:

  • Methodwise creates a rule for each HTTP method, and a regular expression with all paths that are allowed on this method. It’s reasonable to assume that the number of rules would be 4 (GET, PUT, POST, DELETE), but if there are many paths the regular expression for the paths might exceed the length limitation for subexpressions.
  • Pathwise creates a rule for each path with an or for all methods allowed on the path. It’s the most straightforward approach to the rules. But can lead to an exhaustion of rules quota if there are many paths in the API.
  • Compressed creates a single rule, with all defined HTTP methods and paths. It has a slight risk that a path can be accessed with an invalid method, and that the single rule expression exceeds the length limitation for subexpressions.

Personally I think that the Methodwise mode is the most useful one, since it creates a limited set of rules, and the subexpression length limit should be suitable for most APIs. Hence I made this mode the default mode ;-).

The paths are checked using regular expressions. The regular expressions are reduced, since a path /user/login might already be included in the path /user/{userId} of another operation. No further compression then that is done at the moment.

In order to generate a security policy for the popular Petstore API example, you simply have to execute the following command:

go run main.go -input https://raw.githubusercontent.com/openapitools/openapi-generator/master/modules/openapi-generator/src/test/resources/3_0/petstore.yaml

The resulting policies YAML will contain 5 rules. The last rule with the lowest priority a default deny rule. And 4 rules for the GET, POST, PUT and DELETE methods, with the various paths reduced to a single regular expression for each method. The following is only the rule for the GET methods of the resulting YAML:

- action: allow
description: Generated rules for method GET
kind: compute#securityPolicyRule
priority: 1000
match:
expr:
expression: request.method=='GET' && request.path.matches('/pet/[^/]*$|/store/inventory$|/store/order/[^/]*$|/user/[^/]*$')

You can find the complete code for the converter on GitHub. If you give it a spin or integrate it in your CI/CD pipeline I would appreciate your feedback.

--

--

Christoph Grotz
Google Cloud - Community

I’m a technology enthusiast and focusing on Digital Transformation, Internet of Things and Cloud.