drift-sentinel

Drift Sentinel

Drift Sentinel is a Kubernetes validating admission controller that blocks unintended workload drift on UPDATE operations.

It compares the old and new object, strips Kubernetes-managed noise, applies rule-defined include and exclude scopes, and only allows changes that fall under explicitly mutable paths.

Features

Supported Annotations And Labels

ConfigMap rule discovery:

Resource bypass:

Namespace mode override:

Namespace webhook opt-out label in the default chart:

How A Decision Is Made

For each UPDATE admission request:

  1. Match the highest-priority rule by namespace glob and resource selector.
  2. If no rule matches, allow the request.
  3. If the resource has the rule bypass annotation set to true, allow the request.
  4. Resolve a namespace mode override if present.
  5. Strip implicit system fields from both objects: status, metadata.managedFields, metadata.resourceVersion, metadata.generation, metadata.uid, metadata.creationTimestamp, metadata.selfLink
  6. Apply rule include paths if configured.
  7. Apply rule exclude paths.
  8. Diff the remaining object scope.
  9. If there are no changes, allow the request.
  10. Partition changed paths into mutable and immutable paths.
  11. Enforce the effective mode.

Rule ConfigMap Schema

Rules are stored in any namespace as annotated ConfigMaps:

apiVersion: v1
kind: ConfigMap
metadata:
  name: drift-sentinel-production
  namespace: drift-sentinel
  annotations:
    drift-sentinel.k8s.io/rule: "true"
data:
  spec: |
    mode: enforce
    priority: 200
    namespaces:
      - "prod-*"
    selectors:
      - apiGroup: "apps"
        kind: "Deployment"
      - apiGroup: "apps"
        kind: "StatefulSet"
    labels:
      - "app=api-service"
      - "team"
    users:
      - "system:admin"
      - "system:serviceaccount:team-a:release-bot"
    exclude:
      - "status"
      - "metadata.managedFields"
      - "metadata.resourceVersion"
    mutable:
      - "spec.template.spec.containers[*].image"
      - "spec.template.spec.initContainers[*].image"
    bypass: "drift-sentinel.k8s.io/bypass"

Supported rule fields:

Supported Path Syntax

The path matcher supports a constrained JSONPath-like syntax:

This is not full JSONPath. The implementation is intentionally narrow and deterministic.

Mode Behavior

Mode Violation Behavior
enforce deny the request with 403
warn allow the request and log the violation
dry-run allow the request and log a would deny reason
off allow the request without blocking

Precedence:

  1. Resource bypass annotation
  2. Namespace mode override annotation
  3. Rule mode

Default Webhook Scope

The Helm chart registers the webhook for:

These defaults are configurable in values.yaml under webhook.rules.

The engine itself matches on API group and kind from rules, but the webhook configuration controls which requests are actually sent to Drift Sentinel.

Helm Chart

Chart path:

The chart renders:

Certificate behavior:

Default chart behavior:

Basic install:

helm repo add drift-sentinel https://dheeth.github.io/drift-sentinel
helm install drift-sentinel drift-sentinel/drift-sentinel -n drift-sentinel --create-namespace

Install with embedded rules:

rules:
  - name: drift-sentinel-production
    namespace: drift-sentinel
    spec: |
      mode: enforce
      priority: 200
      namespaces:
        - "prod-*"
      selectors:
        - apiGroup: "apps"
          kind: "Deployment"
      mutable:
        - "spec.template.spec.containers[*].image"

Example standalone rule manifests are available in:

Local Development

Run against a local kubeconfig:

$env:DRIFT_SENTINEL_KUBECONFIG="$HOME\.kube\config"
go run ./cmd/server

The service defaults to :8080 locally. For webhook-style TLS serving, set:

Runtime Configuration

Variable Default Purpose
DRIFT_SENTINEL_ADDRESS :8080 HTTP or HTTPS listen address
DRIFT_SENTINEL_LOG_LEVEL INFO slog log level
DRIFT_SENTINEL_HEALTH_PATH /healthz health endpoint path
DRIFT_SENTINEL_METRICS_PATH /metrics metrics endpoint path
DRIFT_SENTINEL_VALIDATE_PATH /validate admission endpoint path
DRIFT_SENTINEL_KUBECONFIG empty use local kubeconfig instead of in-cluster config
DRIFT_SENTINEL_TLS_CERT_FILE empty TLS cert file path
DRIFT_SENTINEL_TLS_KEY_FILE empty TLS key file path
DRIFT_SENTINEL_WATCH_RESYNC 30s informer resync period
DRIFT_SENTINEL_STARTUP_SYNC_TIMEOUT 30s max time to wait for informer cache sync on startup
DRIFT_SENTINEL_READ_HEADER_TIMEOUT 5s HTTP read header timeout
DRIFT_SENTINEL_READ_TIMEOUT 15s HTTP read timeout
DRIFT_SENTINEL_WRITE_TIMEOUT 15s HTTP write timeout
DRIFT_SENTINEL_IDLE_TIMEOUT 60s HTTP idle timeout
DRIFT_SENTINEL_SHUTDOWN_TIMEOUT 10s graceful shutdown timeout

Endpoints

The admission endpoint expects admission.k8s.io/v1 AdmissionReview requests and returns AdmissionReview responses.

Metrics

The current metrics surface includes:

Logging

Admission decisions are logged as structured JSON, including:

Current Limitations

Repository Guide