Linux offers robust kernel-level security features—seccomp, SELinux, and AppArmor—that limit what containerized workloads are allowed to do. Each of these relies on profiles that define acceptable behavior, but manually authoring, distributing, and maintaining those profiles is both tedious and prone to mistakes. The Security Profiles Operator (SPO) addresses this challenge by enabling you to manage security profiles as Kubernetes custom resources, capture profiles from running workloads, and attach them to pods using declarative configuration.
With v1.0.0, the Security Profiles Operator promotes all eight of its Custom Resource Definition (CRD) APIs to v1. This marks the project’s first stable release, supported by an independent third-party security audit, an extensive hardening effort, and a seamless migration path from every prior API version.
SPO began in April 2020 as an operator focused exclusively on seccomp. In the years since, the project expanded to include SELinux support (late 2020), AppArmor (late 2021), profile recording through audit logs and eBPF, OCI-based profile distribution, and additional capabilities. Each new feature brought new CRDs, and those CRDs remained at alpha or beta maturity while the APIs were refined through production use.
Several of these APIs have been functionally stable for a long time: SeccompProfile was at v1beta1 for more than four years, and SPOD sat at v1alpha1 for over five. Downstream adopters needed a stable version designation to justify long-term support commitments. The SPO has been listed on OperatorHub since 2022 and has been included as part of Red Hat OpenShift starting with version 4.12. The period leading up to v1 represented the final opportunity to introduce breaking changes, and the team took full advantage of it.
The cleanup effort spanned numerous pull requests:
- Structural improvements. All CRDs now use a common status type modeled on upstream Kubernetes conditions. The SPOD spec was restructured from a flat collection of 30+ fields into logically grouped categories (SELinux, Enricher, Webhook, Scheduling, Security). Shared base types were factored out to reduce duplication across CRDs.
- Type fixes. Several field types were corrected to align with Kubernetes API conventions, including swapping unsigned integers for signed ones. External types that introduced unnecessary import dependencies were moved internally.
- Convention compliance. Enum values were converted to PascalCase (e.g.,
logsbecameLogs,RUNNINGbecameRunning). Every field was annotated with either +optional or +required markers. Validation markers were added throughout all types.
The one intentional exception: seccomp’s SCMP_ACT_* and SCMP_CMP_* constants retain their original uppercase form to stay consistent with the OCI runtime spec and Linux kernel headers.
Prior to graduating to v1, SPO went through a security code audit. The audit uncovered zero critical vulnerabilities. It verified that file paths written to the host are derived from object metadata rather than user-controlled spec fields, that commands are assembled as argument arrays (eliminating shell injection vectors), and that default RBAC settings do not grant excessive privileges to non-admin users.
The audit surfaced areas that needed additional hardening, particularly at the boundary where a tenant’s custom resource gets translated into kernel-level LSM state. The v1.0.0 release incorporates these improvements:
RawSelinuxProfile: gating and validation. RawSelinuxProfile allows users to supply arbitrary SELinux CIL policy, which the operator then installs on the node. The audit identified this as the riskiest code path. In v1.0.0, a new enableRawSelinuxProfiles field on the SPOD configuration lets cluster administrators disable raw SELinux profile support altogether. A validating admission webhook now rejects malformed raw policies upfront instead of allowing them to fail during reconciliation.
SelinuxProfile: permissive mode control. The permissive boolean on SelinuxProfile was replaced with a mode enum (Enforcing or Permissive), eliminating the risk that an unset field could inadvertently activate 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 any content validation. SPO now enforces strict regex validation on all of them and prevents overwriting profiles that are already loaded into the kernel.
Field size limits and validation markers. RawSelinuxProfile.spec.policy now has a maxLength of 500 KB, capping the input size before it ever reaches the SELinux CIL compiler on the node. Validation markers were added across all CRD types.
Additional hardening beyond the audit’s scope
- Regex backtracking: Greedy regex operators in the seccomp, SELinux, and AppArmor log parsers were replaced with bounded patterns to prevent specially crafted audit log lines from triggering excessive backtracking.
- Path restriction:
HostProcVolumePathis now validated to match only/proc. The seccompListenerPathis confined to the operator’s socket directory. - eBPF recorder resource limits: The eBPF-based profile recorder now caps the number of recorded files and the maximum path length, preventing out-of-memory conditions on workloads with heavy filesystem activity.
- Process cache accuracy: The process cache now keys on both PID and process start time, preventing stale cache hits that could occur after PID reuse.
- Recording annotation handling: The recording webhook now correctly overwrites existing annotations instead of silently skipping pods that are already annotated.
- Metrics cardinality: Unbounded labels were removed from Prometheus counters to prevent high-cardinality metrics from accumulating.
Upgrading to v1.0.0 requires no manual intervention. Conversion webhooks transparently handle translation between old and new API versions:
- Old manifests remain valid: You can continue applying resources using
v1alpha1,v1alpha2, orv1beta1API versions. The conversion webhook translates them to v1 before storage. - Old API versions remain served: Running
kubectl getwith a legacy API version returns resources with the original-style enum values, even though v1 is the storage version. - Enum values map bidirectionally: The conversion layer translates between legacy (
logs, RUNNING) and modern (Logs, Running) enum values in both directions.
Here’s
Here’s how a ProfileRecording resource appears in both the older and newer versions:
# Previous format (v1alpha1)
apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
kind: ProfileRecording
metadata:
name: my-recording
spec:
kind: SeccompProfile
recorder: logs
mergeStrategy: none
podSelector:
matchLabels:
app: my-app
# Updated format (v1)
apiVersion: security-profiles-operator.x-k8s.io/v1
kind: ProfileRecording
metadata:
name: my-recording
spec:
kind: SeccompProfile
recorder: Logs
mergeStrategy: None
podSelector:
matchLabels:
app: my-appFor Custom Resource Definitions that don’t include enum modifications, the sole alteration is the apiVersion field. A comprehensive migration guide addresses Go SDK consumer adjustments, enumerator value updates, and scheme registration changes. Previous API versions will stay accessible for legacy support and will be phased out in a subsequent release.
The Security Profiles Operator (SPO) has consistently maintained strong alignment with upstream security profile initiatives in Kubernetes. The project leverages the stable seccomp API surface introduced in Kubernetes 1.19 and has advanced in parallel with related proposals such as ConfigMap-hosted seccomp profiles and native seccomp profiles operating in complain mode.
The most recent illustration of this synergy is KEP 6061: OCI Artifact-Based Security Profile Distribution, which has been submitted for inclusion as an alpha feature in an upcoming Kubernetes release. SPO was among the first to implement OCI-based profile distribution, enabling users to publish seccomp, SELinux, and AppArmor profiles to OCI registries and reference them directly within pod specifications. KEP 6061 integrates this capability directly into the kubelet by introducing a PullSecurityProfileArtifact CRI API call, allowing container runtimes to retrieve profiles from OCI registries on demand.
The KEP adopts the same trust model as localhost profiles: a deny-by-default allowlist enforced at the kubelet level, with Pod Security Admission handling OCI profiles identically to localhost profiles. SPO will continue delivering the higher-level capabilities that the kubelet does not address: recording profiles from running workloads, structured SELinux policy authoring, binding profiles to container images, and enriching audit logs.
With the v1 release now available, the project’s focus areas include:
- Deprecating legacy API versions after a minimum of one full release cycle.
- Ongoing security hardening, encompassing stricter RBAC scoping and more secure path operations within privileged components.
- KEP 6061 integration, bridging SPO’s OCI distribution capabilities with the native kubelet support as it progresses through alpha and beta stages.
Give v1.0.0 a try, consult the migration guide, and get involved in the #security-profiles-operator channel on Kubernetes Slack.
SPO is the product of contributions from over 70 individuals spanning multiple organizations. Thank you to everyone who submitted code, reported issues, validated pre-releases, and took part in the security audit. This v1 release belongs to the community as much as it does to us.



