Skip to content

compliance-framework/plugin-k8s

Repository files navigation

plugin-kubernetes

A Continuous Compliance Framework plugin that collects Kubernetes resources and evaluates OPA/Rego policies against them.

Supports multiple authentication methods (EKS via AWS STS, kubeconfig for any cluster) and concurrent multi-cluster collection.

Breaking change in this release. The plugin now implements the RunnerV2 interface and evaluates policies per individual resource rather than per cluster. The Rego input schema has moved from v1 (input.clusters[...]) to v2 (input.main + input.context). Existing policies that read input.clusters[...] must be rewritten. Companion policy repos (plugin-k8s-policies, plugin-k8s-opres-policies) will be updated in a coordinated follow-up.

Plugin Configuration

The plugin receives configuration as flat string fields from the CCF agent. All structured values are JSON-encoded strings.

Field Required Description
clusters Yes JSON array of cluster connection configs
resources Yes JSON array of Kubernetes resource types to collect (e.g. "nodes", "pods", "deployments")
main_resources No JSON array — subset of resources that produces per-instance subjects and evidence. Defaults to all of resources. Types in resources but not in main_resources are collected as policy context only.
identity_labels No JSON object mapping identity-label key → ordered list of metadata.labels keys to try. Default: {"app_name": ["app.kubernetes.io/name", "app"]}. First match wins; falls back to metadata.name when none are present.
namespace_include No JSON array of namespaces to include (empty = all)
namespace_exclude No JSON array of namespaces to exclude
policy_labels No JSON object of key-value labels added to evidence metadata
policy_input No JSON object of custom fields merged into the Rego input document. Reserved keys: schema_version, source, main, context, subject, fleet.

Cluster Configuration

Each entry in clusters has:

Field Required Provider Description
name Yes all Unique display name for this cluster
provider No all "eks" (default) or "kubeconfig"
region Yes eks AWS region
cluster_name Yes eks EKS cluster name
role_arn No eks IAM role ARN to assume before authenticating
kubeconfig No kubeconfig Path to kubeconfig file (default: ~/.kube/config)
context No kubeconfig Kubeconfig context to use (default: current-context)

Subjects and evidence model

During Init, the plugin registers one SubjectTemplate per entry in main_resources (e.g. k8s-pods, k8s-nodes). Each template has the identity label keys:

  • cluster_name
  • namespace (only for namespaced resources)
  • app_name (resolved from metadata.labels via identity_labels; falls back to metadata.name)
  • name

For cluster-scoped resources such as nodes, the emitted evidence labels still include namespace with an empty string so label contracts stay stable, but the corresponding SubjectTemplate omits namespace from its identity keys and rendering.

During Eval, every concrete Kubernetes resource instance that matches a main_resources type becomes its own subject and receives its own evidence for every configured policy path. Policies are invoked once per (resource instance, policy path) pair. Evidence is batched per cluster, but may be sent via multiple CreateEvidence calls when the evidenceBatchSize threshold is reached.

Configuration Examples

Single EKS cluster — evidence per pod, nodes as context

{
  "clusters": "[{\"name\":\"prod\",\"region\":\"us-east-1\",\"cluster_name\":\"prod-eks\"}]",
  "resources": "[\"nodes\",\"pods\"]",
  "main_resources": "[\"pods\"]",
  "namespace_exclude": "[\"kube-system\",\"kube-public\"]",
  "policy_labels": "{\"team\":\"platform\",\"environment\":\"production\"}",
  "policy_input": "{\"expected_azs\":[\"us-east-1a\",\"us-east-1b\",\"us-east-1c\"]}"
}

Custom identity label

{
  "clusters": "[{\"name\":\"prod\",\"region\":\"us-east-1\",\"cluster_name\":\"prod-eks\"}]",
  "resources": "[\"pods\"]",
  "identity_labels": "{\"app_name\":[\"app.company.io/service\",\"app.kubernetes.io/name\"],\"team\":[\"team.company.io/owner\"]}"
}

EKS with cross-account role assumption

{
  "clusters": "[{\"name\":\"prod\",\"region\":\"us-east-1\",\"cluster_name\":\"prod-eks\",\"role_arn\":\"arn:aws:iam::123456789012:role/eks-readonly\"}]",
  "resources": "[\"nodes\",\"pods\"]",
  "policy_input": "{\"expected_azs\":[\"us-east-1a\",\"us-east-1b\"]}"
}

Local cluster via kubeconfig (kind, minikube, k3s)

{
  "clusters": "[{\"name\":\"local-dev\",\"provider\":\"kubeconfig\"}]",
  "resources": "[\"pods\",\"services\",\"deployments\"]",
  "namespace_include": "[\"default\",\"app\"]"
}

Multi-cluster (mixed providers)

{
  "clusters": "[{\"name\":\"prod-east\",\"region\":\"us-east-1\",\"cluster_name\":\"prod-east-eks\"},{\"name\":\"prod-west\",\"region\":\"us-west-2\",\"cluster_name\":\"prod-west-eks\"},{\"name\":\"dev\",\"provider\":\"kubeconfig\",\"context\":\"kind-dev\"}]",
  "resources": "[\"nodes\",\"pods\"]",
  "main_resources": "[\"pods\"]",
  "namespace_exclude": "[\"kube-system\"]",
  "policy_input": "{\"expected_azs\":[\"us-east-1a\",\"us-east-1b\",\"us-west-2a\",\"us-west-2b\"]}"
}

Policy Input Schema (v2)

Every policy evaluation receives one main resource and the full cluster snapshot as context.

{
  "schema_version": "v2",
  "source": "plugin-kubernetes",
  "main": { /* the single Kubernetes resource being evaluated (unstructured) */ },
  "subject": {
    /* stable per-resource identity metadata derived for evidence and policy use */
  },
  "context": {
    "cluster": { "name": "prod", "region": "us-east-1", "provider": "eks" },
    "resources": {
      "nodes": [ /* every collected node in this cluster */ ],
      "pods":  [ /* every collected pod in this cluster — includes the one in input.main */ ]
    }
  },
  "fleet": {
    /* multi-cluster collection metadata always included in the evaluation input */
  }
}

Any fields from policy_input config are merged at the top level. Reserved keys (schema_version, source, main, context, subject, fleet) cannot be overridden.

Field reference

Path Type Source Description
input.schema_version string Plugin Always "v2"
input.source string Plugin Always "plugin-kubernetes"
input.main object Plugin The full unstructured Kubernetes resource currently being evaluated
input.subject object Plugin Normalized subject identity for the resource under evaluation (cluster_name, resource_type, name, identifier, identity_labels, and namespace for namespaced resources)
input.context.cluster object Plugin {name, region, provider} for the cluster this resource belongs to
input.context.resources object Plugin Map of resource type → array of Kubernetes objects (full cluster snapshot, including the main resource)
input.fleet object Plugin Multi-cluster snapshot keyed by cluster name, each with {cluster, resources}
input.<custom_key> any policy_input User-defined fields

Example: writing a policy

package compliance_framework.pod_has_node_binding

import rego.v1

violation contains {"remarks": msg} if {
    input.main.kind == "Pod"
    not input.main.spec.nodeName
    msg := sprintf("Pod %q has no nodeName assigned", [input.main.metadata.name])
}

# Cross-reference sibling resources via input.context
violation contains {"remarks": msg} if {
    input.main.kind == "Pod"
    node_name := input.main.spec.nodeName
    not node_exists(node_name)
    msg := sprintf("Pod %q binds to unknown node %q", [input.main.metadata.name, node_name])
}

node_exists(name) if {
    some n in input.context.resources.nodes
    n.metadata.name == name
}

title := "Pod node binding"
description := "Every pod must bind to a known node in its cluster."

Key patterns:

  • input.main is always the single resource under evaluation.
  • input.context.resources.<type> is the full cluster snapshot; iterate with some r in ....
  • input.context.cluster gives the cluster name/region/provider for labels and messaging.
  • Filter out the main resource from peer checks with r.metadata.uid != input.main.metadata.uid.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages