Skip to content
Declarative security and compliance policies

Declarative security and compliance policies

July 12, 2026

Hardcoding security and compliance policies is a bad idea: not every project share the same requirements. Xenguard uses Open Policy Agent and its policy Language, Rego, to let users define their own policies as code.

Introduction

The security and compliance of our products are impacted by the supply chain. Before we integrate externally provided components into our products, they should undergo quality assurance.

Xenguard uses a declarative language that enables the definition and evaluation of security and compliance requirements (i.e., policies) against packages fetched from software repositories.

Rationale

Software projects may be subject to security and compliance requirements from various sources. In an enterprise setting, developers are handed a set of requirements from the security and legal teams, and are asked to verify that external software packages and dependencies meet these requirements before integrating them. For example, many commercial vendors explicitely exclude software components that may cause a copy left effect to avoid potential legal complications.

Verifying the conformance of each (transitive) software dependency is a tedious and costly task that requires coordination between various teams. Although there are tools that can automate this process, they are usually proprietary and not free. Xenguard allows users to define their policies corresponding to their requirements and verifies software dependencies against those policies.

Defining policies

Lets start with an example:

package proxy

default allow := false

# Allow queries for downloding packages if:
# - package is older than 7 days
allow if {
	input.target == "PROXY_TARGET_DOWNLOAD"
	input.stage == "PROXY_STAGE_PREHANDLER"

	min_age := time.parse_duration_ns("7d")
	now := time.now_ns()
	package_age := now - to_number(input.metadata.time)
	package_age > min_age
}

This is a simple policy for Xenguard Proxy defined in the OPA Rego policy language. It is composed of ruless where values from input are used to set the value of allow. In this example the proxy provides the policy engine with a concrete input object (in JSON). By default no proxy action is allowed. The only allowed action is if the proxy wants to download a package that statisfies the cool-down requirement of 7 days.

In Xenguard, the structure of input object for each system (e.g., Proxy) is defined beforehand, so those who define policies can know which values can be used in policy rules. For example, at the time of writing, the Proxy provides the following data to the policy engine:

/**
 * PackageMetadata contains fields that are relevant to make a proxy policy deicision.
 */
message PackageMetadata {
    string name = 1;
    string version = 2;
    Ecosystem ecosystem = 3;
    optional string purl = 4;
    repeated string license = 5;
    int64 time = 6;
}

Respectively – and in addition to the aforementioned example – rules can be defined based on a packages license (e.g., to avoid copy-left effect) or its Package-URL (e.g., to block a known compromised package version).

Evaluating policies

The Open Policy Agent (OPA) evaluates policies based on the provided input. Xenguard embeds OPA as a Go library within the Authentication Engine. Currently, only the Proxy interacts with the Policy Engine. Other systems, such as the Audit Engine, are planned to use OPA for authorization decisions in the future.

Decoupling policy definition and policy evaluation has three advantages. First, it avoids hardcoding policies. Second, it enables each team (e.g., legal, secops, etc.) to define their own requirements as policies, bundle them together, and load them into the policy engine. Finally, policies can be versioned and stored just like the rest of the codebase.

Final words

Integrating OPA into Xenguard was more difficult than I expected. For one thing, debugging policies can be complicated. For example, I had a timestamp (Unix time) that Protobuf marshaled as a string, but OPA expected it to be a number in the input JSON. As a result, all policy evaluations failed, and I could not figure out why.

Another challenge I anticipate is designing a user-friendly interface. Existing approaches, such as Nexus Firewall, have their own web GUIs that don’t require users to learn a new language, like Rego. Although tools like Rego Playground can make it easier for users, I still need to conduct a user study to determine the most appropriate approach to policy definition.

Last updated on • Pouyan Fotouhi Tehrani