Repository navigation
b2_01_policy_definition___evaluation_
Welcome to the ec-cli tutorial! This is the first chapter where we'll explore the very heart of ec-cli: Policy Definition & Evaluation.
Imagine you're baking a cake for a competition. There's a rulebook that says what ingredients are allowed, how long it should bake, and maybe even how it should be decorated. You (the baker) need to follow these rules, and a judge will check your cake against the rulebook.
In the world of software security and compliance, ec-cli acts like that judge, and the policies are the rulebook.
Modern software development involves many steps and tools. How can we ensure that the final software (like a container image) meets our organization's security standards and compliance requirements?
- Is it built from an approved base image?
- Does it contain known critical security vulnerabilities?
- Was it built by a trusted pipeline?
- Does it have the necessary documentation (like SBOMs)?
Checking all this manually for every piece of software is slow and error-prone. We need an automated way to define the rules and check our software against them. This is where Policy Definition & Evaluation comes in.
Our Goal: To understand how ec-cli uses predefined rules (Policies) to automatically check if our software artifacts (like container images) meet specific security and compliance standards.
Let's break down the core ideas:
-
Policy Definition (The Rulebook):
- A "policy" is a set of rules that define what's acceptable or required for your software.
- These rules are often written in a special language called Rego, which comes from the Open Policy Agent (OPA) project. Think of Rego as the specific language the rulebook is written in. You don't need to be a Rego expert to use
ec-cli, but it's good to know it's under the hood. - Policies aren't just floating in the air; they need to be stored somewhere.
ec-clican fetch these policy rules from various Policy Sources. Common sources include:- Git repositories (like GitHub or GitLab)
- OCI registries (like Docker Hub or Quay.io, where policies can be stored as OCI artifacts)
-
Artifact (The Cake / The Player):
- This is the "thing" we want to check against the rules. In
ec-cli, this is often a container image, but it could also be other files or data. We'll learn more about howec-cligathers information about artifacts in the Application Snapshot chapter.
- This is the "thing" we want to check against the rules. In
-
Policy Evaluation (The Judging):
- This is the process where
ec-clitakes the artifact, fetches the relevant policies (rules), and checks if the artifact complies with those rules. -
ec-cliacts as the evaluator or "referee", applying the rulebook to the player.
- This is the process where
Let's revisit our simple use case: Verify if a container image is built using an approved base image and has no critical vulnerabilities.
-
Define the Policy: Somewhere (e.g., in a Git repository), there would be policy rules written in Rego. Conceptually, they might look like this (this is not exact Rego, just for illustration):
FAIL if image_base_image is not "registry.example.com/approved-base:latest"FAIL if vulnerability_severity is "critical"WARN if vulnerability_severity is "high"
-
Fetch the Policy: When you run
ec-cli, you tell it where to find these rules. You might use a command likeec fetch policy(though validation commands often fetch implicitly) to download them.# Example: Fetching policy rules from a specific source # (You usually don't need to run this separately for validation) ec fetch policy --source github.com/your-org/security-policies//path/to/rules
This command tells
ec-clito go to that Git repository, find the rules in the specified path, and download them locally (often to a temporary directory). -
Evaluate the Artifact: You then use a command like
ec validate imageto perform the check.# Example: Validate an image against a defined policy configuration ec validate image --image registry.example.com/my-app:v1.0 \ --policy your-org/standard-policy-
--image: Specifies the container image (the artifact) we want to check. -
--policy: Tellsec-cliwhich set of policies to use (this configuration might point to the Git repo we mentioned earlier, or an OCI artifact, or a Kubernetes resource).
-
-
Get the Results:
ec-cliruns the evaluation. It checks the image's base image and vulnerability scan results (we'll see how it gets this info in Application Snapshot) against the Rego rules it fetched. The output will tell you if the image passed or failed, and why. We'll cover this in detail in the Output Reporting chapter.- Success: All rules passed!
- Failure: One or more critical rules failed (e.g., wrong base image, critical vulnerability found).
- Warning: Some non-critical rules triggered warnings (e.g., high severity vulnerability found).
What happens internally when you run ec validate image?
High-Level Steps:
-
Parse Input:
ec-clireads the command-line arguments (image reference, policy reference, etc.). -
Load Policy Configuration: It figures out where the policy rules and associated configuration live based on the
--policyflag (Git, OCI, Kubernetes). -
Fetch Policy Code: It downloads the actual Rego policy code and any associated data files from the specified sources (using logic similar to
ec fetch policy). - Gather Artifact Data: It collects information about the specified image(s), such as metadata, attestations (like vulnerability scans or SBOMs), and signatures. This collected data is often structured as an Application Snapshot.
- Prepare Input for Evaluator: It bundles the gathered artifact data into a format (usually JSON) that the policy engine can understand.
- Evaluate: It feeds the policy code and the artifact data (as input) into an evaluation engine (like OPA or Conftest). The engine runs the Rego rules against the input.
- Process Results: It takes the raw results from the engine (passes, failures, warnings).
- Generate Report: It formats these results into a user-friendly report based on the requested output format (Output Reporting).
Simplified Sequence Diagram:
sequenceDiagram
participant User
participant CLI as ec validate image
participant PolicyFetcher as Policy Source Fetcher
participant Evaluator as OPA/Conftest Engine
participant Reporter as Output Formatter
User->>CLI: Run command with image & policy refs
CLI->>PolicyFetcher: Request policy code for ref
PolicyFetcher-->>CLI: Return policy code location
CLI->>Evaluator: Prepare artifact data (Input)
CLI->>Evaluator: Evaluate Input against Policy Code
Evaluator-->>CLI: Raw evaluation results
CLI->>Reporter: Process results
Reporter-->>User: Display formatted report (Pass/Fail)
Let's look at some simplified code concepts.
1. Handling Validation Commands (cmd/validate/image.go, cmd/validate/input.go)
These files define the ec validate subcommands. They parse flags like --image, --policy, --strict and orchestrate the validation process.
// Simplified concept from cmd/validate/image.go
func validateImageCmd(validate imageValidationFunc) *cobra.Command {
// ... (flag definitions for --image, --policy, etc.) ...
cmd := &cobra.Command{
Use: "image",
Short: "Validate conformance of container images...",
PreRunE: func(cmd *cobra.Command, args []string) error {
// 1. Determine the input (image refs)
data.spec = applicationsnapshot.DetermineInputSpec(...)
// 2. Load and pre-process the policy configuration
data.policy, _, err = policy.PreProcessPolicy(ctx, policyOptions)
// ... handle errors ...
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
// 3. Initialize evaluator(s) based on policy
evaluators := // ... create evaluators ...
// 4. Loop through images/components and validate each one
for comp := range jobs {
out, err := validate(ctx, comp, data.spec, data.policy, evaluators, ...)
// ... collect results ...
}
// 5. Generate the final report
report, _ := applicationsnapshot.NewReport(...)
report.WriteAll(...) // Calls Output Reporting logic
// 6. Handle --strict flag (exit code)
// ...
return nil
},
}
// ...
return cmd
}This shows the orchestration: parse flags (PreRunE), run the core validation logic (RunE), which involves getting policy, getting evaluators, running validation per component, and reporting results.
2. Representing and Loading Policies (internal/policy/policy.go)
This package defines the Policy interface and handles loading the policy configuration (spec) from various sources (Kubernetes, files, JSON strings).
// Simplified concept from internal/policy/policy.go
type Policy interface {
// Spec returns the loaded policy configuration details
Spec() ecc.EnterpriseContractPolicySpec
// CheckOpts provides options for signature verification (covered later)
CheckOpts() (*cosign.CheckOpts, error)
// ... other methods ...
}
// loadPolicy handles getting the policy spec from different places
func (p *policy) loadPolicy(ctx context.Context, policyRef string) error {
if strings.Contains(policyRef, ":") { // Is it inline JSON/YAML or maybe git/oci?
// Try parsing as EnterpriseContractPolicySpec YAML/JSON
err := yaml.Unmarshal([]byte(policyRef), &p.EnterpriseContractPolicySpec)
// ... handle errors and different formats ...
} else {
// Assume it's a Kubernetes reference [namespace/]name
k8s, _ := kubernetes.NewClient(ctx)
ecp, err := k8s.FetchEnterpriseContractPolicy(ctx, policyRef)
// ... handle errors ...
p.EnterpriseContractPolicySpec = ecp.Spec
}
return nil
}This code figures out if the --policy value is raw configuration or a Kubernetes reference and loads the EnterpriseContractPolicySpec accordingly.
3. Fetching Policy Sources (internal/policy/source/source.go)
This handles the actual downloading of policy code (Rego files, data files) from Git, OCI, etc.
// Simplified concept from internal/policy/source/source.go
type PolicySource interface {
// GetPolicy downloads the policy code to a local path (dest)
GetPolicy(ctx context.Context, dest string, showMsg bool) (string, error)
PolicyUrl() string // The source URL (e.g., git://..., oci://...)
Type() PolicyType // Is it policy code, data, config?
}
// PolicyUrl implements PolicySource for remote URLs
type PolicyUrl struct {
Url string
Kind PolicyType
}
func (p *PolicyUrl) GetPolicy(ctx context.Context, workDir string, showMsg bool) (string, error) {
// Use a downloader (like go-getter) to fetch p.Url
// Place it in a unique destination within workDir
dest := uniqueDestination(workDir, string(p.Kind), p.Url)
// Download logic here, often cached
metadata, err := downloader.Download(ctx, dest, p.Url, showMsg)
// Update p.Url with pinned version (e.g., git sha, image digest) if possible
p.Url, _ = metadata.GetPinnedURL(p.Url)
return dest, err
}This shows how different sources are handled via an interface, and how a specific URL type fetches the content.
4. The Evaluator Interface (internal/evaluator/evaluator.go)
This defines a standard way to interact with different policy evaluation engines.
// Simplified concept from internal/evaluator/evaluator.go
type Evaluator interface {
// Evaluate runs the policies against the given input data
Evaluate(ctx context.Context, target EvaluationTarget) ([]Outcome, error)
// Destroy cleans up any resources used by the evaluator
Destroy()
}
// EvaluationTarget holds the input data (artifact info) for the policy engine
type EvaluationTarget struct {
Inputs []string // Paths to JSON files containing artifact data
Target string // Usually "main" or the specific policy target
}
// Outcome represents the results from a single evaluation run
type Outcome struct {
FileName string // Policy file where results originated
Namespace string // Policy namespace (e.g., lib.release)
Successes []Result // Rules that passed
Failures []Result // Rules that failed (violations)
Warnings []Result // Rules that generated warnings
// ... Skipped, Exceptions ...
}This interface allows ec-cli to potentially use different backends (like Conftest or a native OPA integration) for the actual Rego evaluation, just by calling the Evaluate method.
We've learned that Policy Definition & Evaluation is the core engine of ec-cli. It allows us to:
- Define security and compliance rules (policies), often using Rego.
- Fetch these rules from various sources like Git or OCI registries.
- Evaluate software artifacts (like container images) against these rules automatically.
- Get clear results (Pass/Fail/Warn) indicating compliance.
Think of it as the automated "judge" ensuring your software "cake" follows the "rulebook".
But how does the judge get the specific details about the cake to check against the rules? In the next chapter, we'll explore the Application Snapshot, which represents the collected data about the artifact being evaluated.
Generated by AI Codebase Knowledge Builder