Recommended resources
The freely available resources this lesson will direct you to.
Hello! Welcome back to our course on architecting security products.
In our last lesson, we designed the core of our Cloud Security Posture Management (CSPM) tool: a policy engine capable of evaluating declarative rules against our cloud resource inventory. We now have a robust mechanism for asking, "Is this configuration secure?"
This raises a critical question: Who writes these rules, and how? Relying on engineers to code complex policies in a language like Rego isn't scalable. Furthermore, how do we connect our internal findings, like "S3 bucket is public," to the formal language of compliance frameworks that stakeholders care about, such as the CIS Benchmarks?
Today, we will address these challenges directly. Our learning outcome is to: Architect a rule authoring subsystem and a process for mapping rules to external benchmarks like CIS.
We will design the systems that empower security analysts to create, manage, and test policies without being policy-language experts. We will also design the data model to link our internal rules to industry-standard controls, making our CSPM's output relevant for audits and compliance reporting.
1. The Need for Abstraction: From Raw Policy to an Authoring System
In the previous lesson, we saw the power of expressing policy as code using Open Policy Agent (OPA) and its language, Rego. This approach is flexible and powerful, but it also has a steep learning curve.
To appreciate the complexity we need to abstract away, let's look at how policies are developed and tested in a "policy as code" workflow.
This video, 'Open Policy Agent Deep Dive,' which we touched on last time, includes a detailed demonstration of writing and unit-testing Rego policies.
Please watch the segment from 27:16 to 45:09. You don't need to follow every line of code, but focus on the overall workflow: defining rules, writing separate test files, using the opa test runner, and incrementally building confidence through a test-driven approach. Notice the level of detail involved in mocking input data for each test case.
This workflow is excellent for developers but presents a bottleneck for security teams. A security analyst, who is an expert in cloud threats but not necessarily in Rego syntax, needs a more accessible way to contribute their knowledge to the system.
This is the primary motivation for building a rule authoring subsystem. The goal is to provide a higher-level, user-friendly interface for creating policies, which the system then translates into the low-level format the policy engine understands.
2. Architecting a Rule Preprocessing Pipeline
A common and effective pattern for this abstraction is to create a policy preprocessor. Instead of having analysts write raw Rego, we define a simplified, structured format (like YAML) for them to express rule logic. A backend service then takes this simplified input, combines it with common defaults, and generates the final, executable policy files.
Cloud Custodian, an open-source tool for cloud security and governance, uses this exact architecture. Its policygen.py script is a perfect case study for the system we need to design.
Cloud Custodian Architecture ...
This article, 'Cloud Custodian Architecture, Deployment and Policy Preprocessing,' details their policy management pipeline. It's a fantastic real-world example of the system we're designing.
Please read the sections 'Policy Preprocessing,' 'Interpolating Defaults into Policies,' and review the policygen.py source code provided. Focus on: The overall flow: How it reads individual .yml files from a directory. Defaults Interpolation: The concept of a defaults.yml file and the logic for merging it with individual policies. The Code: Skim through the _merge_conf and _array_merge functions in the policygen.py source to see how this deep merge is implemented. This gives you a concrete sense of the internal logic.
Based on this model, we can design our CSPM's rule authoring pipeline:
-
Simplified Rule Schema: Define a simple YAML or JSON schema for security analysts. It would include fields like
name,description,severity,resource_type, and a straightforward way to definefiltersandconditions.Example simplified rule:
name: s3-public-read-acl description: "Checks for S3 buckets with a public-read ACL." resource_type: "aws_s3_bucket" severity: "critical" logic: - field: "attributes.acl" operator: "equals" value: "public-read" -
Defaults Configuration: Maintain a central configuration for common elements, such as metadata tags or default actions (e.g., creating a ticket in a specific project).
-
Rule Preprocessor Service: A backend microservice that acts as our "policygen." When an analyst saves a rule via the UI, this service:
a. Receives the simplified rule object.
b. Performs a deep merge with the defaults configuration.
c. Translates the simplifiedlogicblock into the corresponding Rego syntax.
d. Runs sanity checks to prevent common errors (as described in the Cloud Custodian article).
e. Outputs a complete, valid Rego policy file.
This architecture decouples the user experience of writing rules from the execution details of the policy engine, making the system vastly more maintainable and accessible.
3. Data Modeling for Benchmark Mapping
Now that we have a system for creating internal rules, we need to connect them to external benchmarks. This is primarily a data modeling challenge. We must create a schema that represents frameworks, their specific controls, and the relationship to our internal rules.
The open-source CISO Assistant project provides an excellent and comprehensive data model that we can adapt.
The CISO Assistant data model documentation provides a detailed blueprint for managing compliance frameworks and controls.
Please review the document, focusing on the Entity-Relationship Diagrams (ERDs) and definitions for these key objects: FRAMEWORK: The top-level benchmark (e.g., CIS AWS Foundations v2.0.0). REQUIREMENT_NODE: A specific, auditable requirement within a framework (e.g., 1.1 Avoid the use of the 'root' account). REQUIREMENT_MAPPING_SET: An object specifically designed to model the relationship between requirements from different frameworks. This is the core of the mapping process. REFERENCE_CONTROL: Can be thought of as a template for a reusable control, which aligns well with our concept of an internal CSPM rule.
Let's break down the essential entities for our mapping subsystem based on this model:
-
Framework: Represents a benchmark.urn(unique ID),name(e.g., "NIST Cybersecurity Framework"),provider("NIST"),version("1.1").
-
RequirementNode: A specific control within a framework. This is the "benchmark" we map to.urn,ref_id(e.g., "PR.AC-1"),name("Identity and Access Control"),parent_urn(to model the hierarchy),description.
-
CspmRule(Our internal entity): This represents a rule created in our authoring system.id,name("s3-public-read-acl"),description,rego_code,severity.
-
RuleToRequirementMapping: A join table that creates the many-to-many relationship.cspm_rule_id(foreign key toCspmRule).requirement_node_urn(foreign key toRequirementNode).relationship_type(e.g.,equal to,subset of, as described in theREQUIREMENT_MAPPING_SETsection). This adds valuable context for how completely our rule covers the benchmark requirement.rationale(text explaining the mapping).
This data model, visualized in the ERD below, allows us to build a robust system where a single CSPM rule can satisfy multiple requirements across different frameworks, and a single requirement can be addressed by a collection of our rules.

4. Synthesizing the Full Architecture
Let's bring these two subsystems—rule authoring and benchmark mapping—together into a single, cohesive architecture.

Here is the end-to-end flow for our system:
-
Data Ingestion (Pre-requisite): Our system ingests benchmark data (
Frameworks,RequirementNodes) into our database from authoritative sources (e.g., CIS, NIST). This populates the universe of controls we can map to. -
Rule Authoring:
- A security analyst logs into the Rule Editor UI.
- They create a new
CspmRule, providing a name, description, and logic using the simplified YAML/JSON interface. - Crucially, the UI also presents a searchable list of
RequirementNodesfrom the ingested frameworks. The analyst selects one or more requirements that their new rule addresses.
-
Rule Processing & Storage:
- The browser sends this data to the Rule Editor Backend API.
- The API validates the input and stores the
CspmRuleand itsRuleToRequirementMappingrelationships in the database. - It then triggers the Rule Preprocessor Service.
-
Policy Generation & Deployment:
- The Rule Preprocessor reads the simplified rule logic, generates the full Rego code, and saves it.
- This new Rego policy is then versioned (e.g., in a Git repository) and deployed to the OPA policy engine's
Policy Loader, making it active for the next scan.
-
Reporting:
- When the policy engine runs, it generates findings.
- When a finding is triggered by the "s3-public-read-acl" rule, our Findings Processor can now query the database, join through the
RuleToRequirementMappingtable, and enrich the finding with data like: "This violates CIS AWS Foundations v2.0.0, Control 2.1.2: Ensure S3 bucket policy is configured to prevent public access."
This architecture provides a complete, end-to-end solution for authoring, managing, and contextualizing security policies within our CSPM.
Conclusion
In this lesson, we designed the critical systems that bridge the gap between low-level policy execution and high-level security governance. We moved from simply running policies to architecting how they are created and contextualized.
Key Takeaways:
- Abstraction is Essential: A rule authoring subsystem provides a necessary abstraction layer, enabling security experts to define policies without needing to be programming experts.
- Preprocessing is a Powerful Pattern: A pipeline that takes simplified user input, merges it with defaults, and generates executable code is a robust and proven architecture for managing policy complexity.
- Mapping is a Data Modeling Problem: Connecting internal rules to external benchmarks relies on a well-designed relational data model that represents frameworks, controls, and their many-to-many relationships.
- End-to-End Integration: The full value is realized when the authoring, mapping, and evaluation systems are integrated, allowing findings to be automatically enriched with compliance context.
Preview of the Next Lesson
We have now designed the data model for mapping rules to benchmarks. The next logical step is to use this data to generate meaningful reports for auditors, managers, and compliance officers.
In our next lesson, we will design a compliance mapping system that links CSPM findings to regulatory frameworks such as SOC 2 and PCI-DSS. We will focus on the reporting layer, exploring how to aggregate findings, calculate compliance scores, and visualize posture against different industry standards.