In Test Ansible using policy as code with Conftest, I described how to create a basic policy using Open Policy Agent (OPA) to validate an Ansible playbook with the Conftest utility. The policy in that example used a list of software versions hard-coded directly into the policy. However, most policy scenarios involve decoupling the policy from the data that drives the policy decision.
[ Write your first Ansible playbook in this hands-on interactive lab. ]
This article shows you how to separate policy from data. This enables more robust and reusable policy definitions, and this approach can even integrate with external systems to inject data into the policy evaluation process.
Define external data
Policy decisions are rarely made in isolation. Instead, they leverage contextual information about an environment in the decision-making process. This contextual information may change frequently. For example, consider a policy to prevent terminated employees from accessing an internal resource. A policy engine needs two pieces of information to make a decision: the policy itself (terminated employees cannot access internal resources) and contextual information about employees, such as a list of usernames for those who are currently employed by the company.
Attempting to store the contextual data inside the policy itself is messy and difficult to maintain. For example, imagine trying to keep a list of current employees directly in an OPA Rego file. Any change to the current employee list requires knowledge of Rego and modification to the policy code.
[ Read Introduction to policy as code with automation. ]
Conftest supports loading external data to support policy decisions. The -d flag accepts JSON or YAML files to load additional data into the policy engine.
To demonstrate this, I'll extend the example from my previous article to use external data in the decision-making process. First, create a data.yaml file to hold the external data for policy evaluation:
$ touch data.yaml
$ tree
.
├── data.yaml
├── inventory.yaml
├── playbook.yaml
└── policy
└── main.rego
[ Get the YAML cheat sheet for tips. ]
The data.yaml file contains the list of allowed MariaDB versions defined in a list variable:
$ cat data.yaml
allowed_mariadb_versions:
- "10.8.6"
- "10.9.4"
Next, remove the allowed_mariadb_versions list that is hard-coded in the policy:
allowed_mariadb_versions = [
"10.8.6",
"10.9.4"
]
Add an import statement to import the data that is passed to Conftest:
import data.allowed_mariadb_versions
The imported data structure uses the same variable name as the previous policy, so no other changes are needed. The final main.rego is shown below:
package main
import future.keywords.contains
import future.keywords.if
import future.keywords.in
import data.allowed_mariadb_versions
deny_unapproved_version contains msg if {
some task in input.tasks
package_name := task["ansible.builtin.package"].name
regex.match("MariaDB-server.*", package_name)
version := split(package_name, "-")[2]
not version in allowed_mariadb_versions
msg := sprintf("Version %v of Mariadb-Server is not allowed", [version])
}
The test functions exactly as it did in the previous article and logs an error if the MariaDB version is not on the allowed list:
$ conftest test playbook.yaml -d data.yaml
FAIL - playbook.yaml - main - Version 10.8.4 of Mariadb-Server is not allowed
1 test, 0 passed, 0 warnings, 1 failure, 0 exceptions
Defining the data in a separate file decouples the policy from the supporting contextual information. Updates, such as the addition of a new MariaDB version, no longer require a direct edit to the code. Instead, an entry can simply be added to the YAML file without any knowledge of Rego. This YAML or JSON file could even be rendered by an external process, such as a database lookup, prior to policy evaluation.
[ Boost your skills with this learning path: Getting started with Red Hat OpenShift Service on AWS (ROSA) ]
Extend the policy
Decoupling policy from data also allows for more elegant and reusable policy. For example, the current policy can be extended to support other applications. I stated in my previous article that policy should be expressed in plain language first. Modifying the current policy results in the following description:
Applications should be installed only from an approved list of applications and their versions
This policy version is more generic and can support many applications. Coupling this policy definition with external data results in a short but flexible policy definition.
First, define a list of allowed_software in the data.yaml file:
allowed_software:
- "MariaDB-server-10.8.6-1.fc36"
- "MariaDB-server-10.9.4-1.fc36"
- "httpd-2.4.55-1.fc36"
Remove the deny_unapproved_version policy from the previous exercise and define a new deny_unapproved_packages policy. This policy checks to see if the provided package name in the Ansible task is in the list of allowed packages from the data.yaml file. The full main.rego is shown below:
package main
import future.keywords.contains
import future.keywords.if
import future.keywords.in
import data.allowed_software
deny_unapproved_packages contains msg if {
some task in input.tasks
package_name := task["ansible.builtin.package"].name
not package_name in allowed_software
msg := sprintf("%v is not in the list of approved packages", [package_name])
}
This policy definition is very short, but it is flexible enough to support multiple scenarios without any hard-coded knowledge of the software. Finally, update the Ansible playbook to contain a mix of allowed and forbidden software:
- name: Install software
hosts: webservers
tasks:
- name: Install MariaDB-server
ansible.builtin.package:
name: MariaDB-server-10.8.4-1.fc36
state: present
- name: Install Apache server
ansible.builtin.package:
name: httpd-2.4.55-1.fc36
state: present
- name: Install telnet
ansible.builtin.package:
name: telnet
state: present
Running Conftest confirms that the policy was successfully implemented:
MariaDB-server-10.8.4-1.fc36generates an error because the version is not in the list of approved packagestelnetgenerates an error because it is not in the list of approved packageshttpd-2.4.55-1.fc36successfully passes the policy evaluation because it is an allowed package with the proper version
$ conftest test playbook.yaml -d data.yaml
FAIL - playbook.yaml - main - MariaDB-server-10.8.4-1.fc36 is not in the list of approved packages
FAIL - playbook.yaml - main - telnet is not in the list of approved packages
2 tests, 0 passed, 0 warnings, 2 failures, 0 exceptions
Wrapping up
Conftest leverages data provided by YAML or JSON files in its policy evaluation process. Combining well-defined policies with external data sources enables you to define robust and reusable policies. These policies take advantage of contextual information about their environment, and they can integrate with existing data sources to intelligently evaluate policy compliance for your configuration files.
[ Learn about upcoming webinars, in-person events, and more opportunities to increase your knowledge at Red Hat events. ]
Über den Autor
Anthony Critelli is a Linux systems engineer with interests in automation, containerization, tracing, and performance. He started his professional career as a network engineer and eventually made the switch to the Linux systems side of IT. He holds a B.S. and an M.S. from the Rochester Institute of Technology.
Ähnliche Einträge
A 5-step playbook for unified automation and AI
AI ambitions meet automation reality: The case for a unified automation platform
What Is Product Security? | Compiler
Technically Speaking | Security for the AI supply chain
Nach Thema durchsuchen
Automatisierung
Das Neueste zum Thema IT-Automatisierung für Technologien, Teams und Umgebungen
Künstliche Intelligenz
Erfahren Sie das Neueste von den Plattformen, die es Kunden ermöglichen, KI-Workloads beliebig auszuführen
Open Hybrid Cloud
Erfahren Sie, wie wir eine flexiblere Zukunft mit Hybrid Clouds schaffen.
Sicherheit
Erfahren Sie, wie wir Risiken in verschiedenen Umgebungen und Technologien reduzieren
Edge Computing
Erfahren Sie das Neueste von den Plattformen, die die Operations am Edge vereinfachen
Infrastruktur
Erfahren Sie das Neueste von der weltweit führenden Linux-Plattform für Unternehmen
Anwendungen
Entdecken Sie unsere Lösungen für komplexe Herausforderungen bei Anwendungen
Virtualisierung
Erfahren Sie das Neueste über die Virtualisierung von Workloads in Cloud- oder On-Premise-Umgebungen