Thinking In Policy-as-Code Logic
The key to implementing policy logic is to properly determine what problem you’re trying to solve, what information you’ll need to solve that problem, and what “success” looks like. There are various ways to do this, but this is the process that we generally follow:
Identify The Problem
You need to identify the type of policy you’re going to implement. Generally, there are three types:
Strict
General
Cumulative
Strict
A Strict policy is one that has no grey area, no wiggle room. Infrastructure-as-Code policies are almost always of this type:
“You must use a container image from a trusted source”
“You must not allocate more than 4GB of memory”
“You must have at least one tag”
And so forth. In the non-IaC space, these are often more arbitrary and problem-specific, but generally:
“The caller must have a valid JWT”
“The caller must have a certain role”
“The caller must be in a specific department”
General
One of the benefits of Policy-Based Authorization Control is that it gives flexibility to decisions that previously might require the application developers to implement the logic. For example:
“A salesperson can only generate orders for less than $5000, unless they have a valid managerial override code”
“On fridays, saturdays or sundays, nothing can be deployed to production except by people with the ‘senior operator’ role”
“Only people in the finance department can consume finance department services between 8pm and 5am”
Cumulative
Cumulative rules allow us to detect and deter Denial-of-Service attacks, duplicate requests and ‘death by a thousand cut’ style exploits. Note - this is an area which Policy-as-Code solutions are not yet mature.
Identifying the Information
There are two parts to identifying the information:
Evaluate the request for completeness and correctness
Identify what data you need to make the decision, and where you’ll acquire it
Evaluating the Request
In an ideal world, every request is in the exact same format, with no extra data, no missing data and no variance in the request form. Alas. These are some of the questions you’ll need to ask to be able to properly evaluate the policy decision request:
Are any elements of the request optional?
Are there any special things that need to be done if optional data is not present?
Is it acceptable for the request to have ‘extra’ information?
This is a typical occurrence in long-lived systems - the request includes additional information as the services / applications develop.
For example, consider a policy decision about whether someone is authorized to sell a car. The original request might just include information about the seller, and the value of the car. Over time, the car-selling system might add additional information (color, model, etc) that is not necessary for the decision.
Very conservative/cautious policy decisions might reject the request because of this extra data, while more ‘everyday’ policy decisions might ignore the extra data.
Are there variants in how the data is formatted?
For example, calendar dates in Europe vs the US. Spaces after or before words? Commas vs periods for decimal formats.
Identify the Data
For any given policy, you’ll almost certainly have to acquire some information in order to properly make a decision. This will vary significantly from policy to policy. Here are some questions to ask as you go about evaluating the data:
Was the data I needed sent as part of the request?
If so, can I trust that the data is not forged?
If not, this is basically not going to work, find a different way
There are a few ways to prove that data is not forged:
The data includes a hash with a shared secret, and you can validate the hash
The data was signed by a trusted private key, and you have the public key
The request is coming from a trusted source (IP, etc)
Alternatively, is the data I need part of my ‘trusted data’ that my infrastructure is holding for me?
For example, in OPA/Rego, the ‘/data’ section can reasonably be considered ‘trustworthy’
Under certain situations, local key-value databases (redis, memcached, etcd) might also be considered trusted.
Finally, if I have to reach out to a third-party system to acquire the data I need:
Is the third-party system fast and reliable?
Can I trust that this third-party system hasn’t been compromised?
Is there a way for this third-party data to be put into the ‘trusted data’ by some external process?
Note: Generally speaking, you want to avoid having to reach out to a third-party system when speed is of the essence. If you’re trying to manage policy on a high-transaction, high-throughput service, adding the round-trip to a third party system for every policy decision is almost certainly untenable.
What Does Success Look Like?
The last piece is to make sure that the people who are requesting the policy and the people who are implementing the Policy-as-Code logic have a shared understanding of what the results should be. This typically requires going through a list of questions, which will vary quite a bit from situation to situation. Here are some that we use regularly:
“Should we respond immediately upon a single ‘no’ decision, or should we accumulate all of the ‘no’ decisions for a given request, and send back all of the ‘no’ decisions in one block?
Do we send back details about why something was rejected, and if so, do we send back codes, messages, or internationalized messages?
Are there situations where we can have decisions that include some success and some failure, and how do we handle this?
Note: This is almost certainly an indication of a policy that is trying to do too much
Do we want to send details back with a ‘success’ decision, and if so, what is the format of those details?
Thanks!
That’s all for now on this topic. This article is likely to change over time, because the types of decisions that are made in Policy-as-Code solutions are themselves evolving over time. Have any suggestions or feedback? Contact us at: info@paclabs.io