Why separate policy from your code?
Mar 5th, 2021
Over the last decade, while working at Microsoft, HP, Puppet, Splunk, and Hulu, I worked on many projects related to the adoption or migration towards (cloud-native) service architectures. One recurring anti-pattern we struggled with were services with deeply embedded authorization logic, unable to evolve quickly enough to meet the changing business, scale, and technological requirements.
Trying to address these challenges, I often faced the same common pain points:
- Each service has similar logic to ensure that calls to resources were only made after passing authorization checks. That logic was duplicated across each of the (micro)services, which made it hard to update authorization logic across all of them and was a constant source of misconfigurations and errors
- Different services written in different languages had to express authorization logic in their native language, which reduced opportunities for reuse
- It was hard to figure out after the fact why authorization decisions were made, and trace the decision back to the input data in a consistent way
In order to solve these challenges, we ended up pulling the authorization policy out of the application code, gaining a number of advantages.
First off, decoupling the policy and the application code enables independent versioning of the policy artifacts. As policies are represented using some DSL, we can utilize a standard source code control system, in order to align with the normal application development workflow. Using a source code control system provides insight into who changed what and when. This can provide a basic audit trail, without having to roll out anything fancy like a distributed ledger/blockchain solution :)
In larger applications, policy definitions have the same DRY requirements as the application code. Having reusable building blocks implies interdependencies, which means we need to be able to package policies in a way that guarantees consistency and stability.
Managing and representing policies separately from the application code that consumes them enables the use of additional tooling to validate the correctness of policies, independent of the programming language used for the application. This, in turn, can be further extended into (programmatic) consistency and conformance validation of policies.
Decoupling and abstracting policy from code also enables the reuse of policy across applications, since the policy representation (DSL) is no longer tied to the application’s implementation programming language. This also helps us enforce policies in different applications or layers inside a distributed architecture, which gets us one step closer to a Zero Trust model.
Last but most importantly, decoupling promotes the desired separation of roles and responsibilities between security, development teams, and auditors, which is the foundation for getting to trustworthy decision making.
It should come as no surprise that one of the foundational principles for Aserto is the separation of policy from code. Hit us up at firstname.lastname@example.org or on Twitter if you want to talk about Authorization!