Skip to content

Commit 96bbb37

Browse files
authored
feat(operator): Add security context defaults (#1292)
This makes our containers compliant with the "restricted" Pod Security Standard and any admission policies that require it. OpenShift already injects these today (and has done so in the past) so that's why I'm pretty certain our operators & products can deal with these.
1 parent 3d0cb22 commit 96bbb37

2 files changed

Lines changed: 82 additions & 5 deletions

File tree

‎crates/stackable-operator/CHANGELOG.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
### Changed
8+
9+
- `SecurityContextBuilder::with_stackable_defaults` sets `allowPrivilegeEscalation: false` and
10+
`capabilities.drop: [ALL]`, and `PodSecurityContextBuilder::with_stackable_defaults` additionally
11+
sets `seccompProfile.type: RuntimeDefault` ([#1292]).
12+
Together these satisfy the `restricted` Pod Security Standard, which plain Kubernetes enforces
13+
only if the namespace opts in and which OpenShift's SCCs inject during admission either way, so
14+
the Pods OpenShift ends up running are unchanged.
15+
And because OpenShift has already applied these for years we're pretty sure that this change is safe for us.
16+
Operators already call `PodSecurityContextBuilder::with_stackable_defaults`, so the
17+
`seccompProfile` default reaches their Pods with the dependency bump alone, nothing else to do.
18+
`allowPrivilegeEscalation` and `capabilities` have no Pod-level equivalent, so a Pod is only
19+
covered once every one of its containers is built with `SecurityContextBuilder`.
20+
21+
[#1292]: https://github.com/stackabletech/operator-rs/pull/1292
22+
723
## [0.119.0] - 2026-09-23
824

925
### Removed

‎crates/stackable-operator/src/builder/pod/security.rs‎

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,30 @@ pub struct SecurityContextBuilder {
1414
impl SecurityContextBuilder {
1515
/// Construct a new [`SecurityContextBuilder`] that is pre-filled with Stackable's defaults.
1616
///
17-
/// We currently don't have any defaults we set.
17+
/// The defaults are:
1818
///
19-
/// We intentionally don't set `runAsNonRoot`, as we set that in
19+
/// * `allowPrivilegeEscalation: false`
20+
/// * `capabilities.drop: [ALL]`
21+
///
22+
/// Neither field exists on [`PodSecurityContext`],
23+
/// so both have to be set on every container to take effect.
24+
///
25+
/// We intentionally don't set `runAsNonRoot` or `seccompProfile`, as we set those in
2026
/// [`PodSecurityContextBuilder::with_stackable_defaults`] already and don't want to confuse
21-
/// users by setting it on the Pod and container.
27+
/// users by setting them on the Pod and container.
2228
pub fn with_stackable_defaults() -> Self {
23-
Self {
29+
let mut builder = Self {
2430
security_context: SecurityContext::default(),
25-
}
31+
};
32+
33+
builder
34+
.allow_privilege_escalation(false)
35+
.capabilities(Capabilities {
36+
drop: Some(vec!["ALL".to_owned()]),
37+
..Capabilities::default()
38+
});
39+
40+
builder
2641
}
2742

2843
pub fn allow_privilege_escalation(&mut self, value: bool) -> &mut Self {
@@ -175,6 +190,10 @@ impl PodSecurityContextBuilder {
175190
/// Currently the defaults are:
176191
///
177192
/// * `runAsNonRoot: true`
193+
/// * `seccompProfile.type: RuntimeDefault`
194+
///
195+
/// `seccompProfile` is set here rather than per container so that it also covers containers
196+
/// built elsewhere.
178197
pub fn with_stackable_defaults() -> Self {
179198
// We are using the builder functions to ensure that builder functions exist to override these settings.
180199
let mut builder = Self {
@@ -184,6 +203,10 @@ impl PodSecurityContextBuilder {
184203
// Reason: Running as root is bad
185204
builder.run_as_non_root(true);
186205

206+
// Reason: The runtime's default profile blocks dangerous syscalls without breaking
207+
// ordinary applications.
208+
builder.seccomp_profile_type("RuntimeDefault");
209+
187210
builder
188211
}
189212

@@ -435,6 +458,44 @@ mod tests {
435458
run_as_non_root: Some(true),
436459
run_as_user: Some(1001),
437460
run_as_group: Some(1001),
461+
capabilities: Some(Capabilities {
462+
drop: Some(vec!["ALL".to_owned()]),
463+
..Default::default()
464+
}),
465+
..Default::default()
466+
}
467+
);
468+
}
469+
470+
#[test]
471+
fn security_context_builder_defaults() {
472+
let context = SecurityContextBuilder::with_stackable_defaults().build();
473+
474+
assert_eq!(
475+
context,
476+
SecurityContext {
477+
allow_privilege_escalation: Some(false),
478+
capabilities: Some(Capabilities {
479+
drop: Some(vec!["ALL".to_owned()]),
480+
..Default::default()
481+
}),
482+
..Default::default()
483+
}
484+
);
485+
}
486+
487+
#[test]
488+
fn pod_security_context_builder_defaults() {
489+
let context = PodSecurityContextBuilder::with_stackable_defaults().build();
490+
491+
assert_eq!(
492+
context,
493+
PodSecurityContext {
494+
run_as_non_root: Some(true),
495+
seccomp_profile: Some(SeccompProfile {
496+
type_: "RuntimeDefault".to_owned(),
497+
..Default::default()
498+
}),
438499
..Default::default()
439500
}
440501
);

0 commit comments

Comments
 (0)