Security Profiles Operator v1: Stable APIs, Security Hardened, and Shaping Upstream Kubernetes

Posted on June 26, 2026 by Sascha Grunert (Red Hat)

CNCF projects highlighted in this post

Linux provides powerful kernel-level security mechanisms, seccomp, SELinux, and AppArmor, that restrict what containerized workloads can do. Each uses profiles that define permitted behavior, but writing, distributing, and maintaining those profiles by hand is tedious and error-prone. The Security Profiles Operator (SPO) solves this by letting you manage security profiles as Kubernetes custom resources, record profiles from live workloads, and bind them to pods declaratively.

Withv1.0.0, the Security Profiles Operator graduates all eight of its Custom Resource Definition (CRD) APIs to v1. This is the project’s first stable release, backed by a third-party security audit, a full cycle of hardening work, and a zero-downtime migration path from every previous API version.

Six years of API evolution

SPO started in April 2020 as a seccomp-only operator. Over the following years, the project grew to cover SELinux (late 2020), AppArmor (late 2021), profile recording via audit logs and eBPF, OCI-based profile distribution, and more. Each feature introduced new CRDs, and those CRDs stayed at alpha or beta while the APIs matured through real-world use.

Some of these APIs have been stable in practice for years: SeccompProfile shipped at v1beta1 for over four years, SPOD at v1alpha1 for over five. Downstream consumers needed a stable version label to commit to long-term support. The SPO has been available on OperatorHub since 2022 and has shipped as part of Red Hat OpenShift since version 4.12. The window before v1 was the last chance to make breaking changes, and the team used it.

The cleanup involved many pull requests:

  • Structural changes. All CRDs now share a common status type based on upstream Kubernetes conditions. The SPOD spec was reorganized from a flat list of 30+ fields into logical groups (SELinux, Enricher, Webhook, Scheduling, Security). Shared base types were extracted to avoid duplication across CRDs.
  • Type corrections. Several field types were updated to follow Kubernetes API conventions, including replacing unsigned with signed integers. External types that created unnecessary import dependencies were internalized.
  • Convention alignment. Enum values moved to PascalCase (e.g., logs to Logs, RUNNING to Running). Every field received +optional or +required markers. Validation markers were added across all types.

The one deliberate exception: seccomp’s SCMP_ACT_ and SCMP_CMP_ constants keep their uppercase names to match the OCI runtime spec and Linux kernel headers verbatim.

Security audit and hardening

Before graduating to v1, SPO underwent a security code audit. The audit found zero critical vulnerabilities. It confirmed that file paths written to the host are derived from object metadata (not from user-controlled spec fields), that commands are constructed as argument arrays (no shell injection surface), and that RBAC defaults do not grant unnecessary privileges to non-admin users.

The audit identified areas for hardening, especially around the boundary where a tenant’s custom resource gets translated into kernel-level LSM state. The v1.0.0 release addresses these findings:

RawSelinuxProfile: gating and validation. RawSelinuxProfile lets users write arbitrary SELinux CIL policy, which the operator installs on the node. The audit flagged this as the highest-risk path. In v1.0.0, a new enableRawSelinuxProfiles field on the SPOD configuration lets cluster admins disable raw SELinux profile support entirely. A validating admission webhook now rejects invalid raw policies instead of letting them fail at reconciliation.

SelinuxProfile: permissive mode control. The permissive boolean on SelinuxProfile was replaced with a mode enum (Enforcing or Permissive), removing the risk that an unset field could accidentally enable permissive mode.

AppArmor input sanitization. AppArmor profiles accept template inputs for profile names, executable paths, and capabilities. The audit noted that these inputs were loaded without content validation. SPO now applies strict regex validation on all of them and prevents overwriting profiles that are already loaded in the kernel.

Field size limits and validation markers. RawSelinuxProfile.spec.policy now carries a maxLength of 500 KB, limiting the input size before it reaches the SELinux CIL compiler on the node. Validation markers were added across all CRD types.

Additional hardening beyond the audit scope

  • Regex backtracking: Greedy regex operators in the seccomp, SELinux, and AppArmor log parsers were replaced with bounded patterns to prevent crafted audit log lines from causing excessive backtracking.
  • Path restriction: HostProcVolumePath is now validated to match /proconly. The seccomp ListenerPath is restricted to the operator’s socket directory.
  • eBPF recorder resource limits: The eBPF-based profile recorder now caps the number of recorded files and maximum path length, preventing OOM on workloads with high filesystem activity.
  • Process cache accuracy: The process cache now keys on PID plus process start time, preventing stale cache hits after PID reuse.
  • Recording annotation handling: The recording webhook correctly overwrites existing annotations instead of silently skipping already-annotated pods.
  • Metrics cardinality: Unbounded labels were dropped from Prometheus counters to prevent high-cardinality metrics.

Zero-downtime migration

Upgrading to v1.0.0 requires no manual migration steps. Conversion webhooks handle translation between old and new API versions transparently:

  • Old manifests still work: You can continue applying resources using v1alpha1, v1alpha2, or v1beta1APIversions. The conversion webhook translates them to v1 before storage.
  • Old API versions are still served: kubectl get with an old API version returns resources with old-style enum values, even though v1 is the storage version.
  • Enum values map bidirectionally: The conversion layer translates between old-style (logs, RUNNING) and new-style (Logs, Running) enum values in both directions.

Here’s how a ProfileRecording looks before and after:

For CRDs without enum changes, only the apiVersion line changes. A full migration guide covers Go API consumer updates, enum constant changes, and scheme registration. Old API versions will remain available for backward compatibility and will be removed in a future release.

From operator to upstream Kubernetes

SPO has always been closely connected to upstream security profile work in Kubernetes. The project builds on the seccomp GA API surface (Kubernetes 1.19) and grew alongside related proposals like ConfigMap-based seccomp profiles and built-in seccomp profiles with complain mode.

The latest example of this connection is KEP 6061: OCI Artifact-Based Security Profile Distribution, proposed for an upcoming Kubernetes release as alpha. SPO pioneered OCI-based profile distribution years ago, allowing users to push seccomp, SELinux, and AppArmor profiles to OCI registries and reference them directly from pod specs. KEP 6061 brings this concept into the kubelet natively, adding a PullSecurityProfileArtifact CRI API call so container runtimes can fetch profiles from OCI registries on demand.

The KEP follows the same trust model as localhost profiles: a deny-by-default allowlist at the kubelet, with Pod Security Admission treating OCI profiles like localhost. SPO will continue to provide the higher-level features that the kubelet does not cover: profile recording from live workloads, structured SELinux policy authoring, profile binding to container images, and audit log enrichment.

What's next

Now that v1 has shipped, the project’s priorities are:

  • Removing old API versions after at least one release cycle.
  • Continued hardening, including tighter RBAC scoping and safer path operations in privileged components.
  • KEP 6061 integration, connecting SPO’s OCI distribution features with the native kubelet support as it matures through alpha and beta.

Try v1.0.0, read the migration guide, and join the conversation in #security-profiles-operator on Kubernetes Slack.

SPO is built by over 70 contributors across multiple organizations. Thanks to everyone who contributed code, filed issues, tested pre-releases, and participated in the security audit. v1 is yours as much as ours.

The latest from our blog

Securing CI/CD for an open source project, part 3: Credentials, verification, and what's next

Building a Cluster-Aware AI Agent with Kubernetes, Argo CD, and GitOps

From Awareness to Engineered Accessibility in Open Source