Summary
A customer can weaken two of the three confinement settings on a sandbox container to any value the API enum allows, and no runtime class can refuse it. Capabilities are bounded — a class publishes grantableCapabilities and a request beyond it is rejected with a reason naming the class. Privilege escalation and the seccomp profile have no equivalent published ceiling, so allowPrivilegeEscalation: true and seccompProfile: Unconfined are accepted by every class in service today.
Separately, a container that both adds and drops the same capability is stored as written instead of being refused or normalised.
This is not presently an exposure. Every runtime class in service is VM-isolated: each instance runs its own guest kernel, so these settings take effect inside the customer's own virtual machine and reach nothing shared. The gap is structural. A future class backed by shared-kernel isolation would inherit the same permissiveness silently, because the class contract has no way to state a limit and validation has nothing to reject against.
Follows #310, which introduced the published security context surface.
Evidence
Verified against the staging datum-cloud project control plane with kubectl apply --dry-run=server, on a sandbox Workload using class: general-purpose.
| Input |
Result |
Stored as |
allowPrivilegeEscalation: true |
accepted, no warning |
true |
seccompProfile: {type: Unconfined} |
accepted, no warning |
Unconfined |
capabilities: {add: [CHOWN], drop: [CHOWN]} |
accepted |
add: [CHOWN], drop: [CHOWN, ALL] |
For contrast, an add beyond the class's grantable set is rejected naming the class and the set it grants.
The gap is deliberate and documented, not accidental. Capabilities are the only security setting checked against the class. A class publishes defaultSecurityContext values for allowPrivilegeEscalation and seccompProfile, and those are explicitly documented as a default rather than a limit — so whatever the container states for either is accepted. grantableCapabilities is the only ceiling the class contract expresses.
For the contradictory add/drop case, each capability name in add and drop is checked on its own and the two lists are never compared, so an overlap passes; ALL is then appended to drop, producing the stored value above.
The question is already recorded as an open question in the runtime classes enhancement, including the note that adding a limit later is a compatible change because it only narrows what is accepted. This issue tracks it as work rather than restating it.
Reproduction
Against a control plane with the RuntimeClasses gate enabled and the general-purpose class published:
apiVersion: compute.datumapis.com/v1alpha
kind: Workload
metadata:
name: confinement-probe
spec:
runtimeClass:
class: general-purpose
template:
spec:
runtime:
sandbox:
containers:
- name: probe
image: nginx:alpine
securityContext:
allowPrivilegeEscalation: true
seccompProfile:
type: Unconfined
capabilities:
add: [CHOWN]
drop: [CHOWN]
placements:
- name: default
cityCodes: [DFW]
kubectl apply --dry-run=server -f probe.yaml -o yaml
The object is admitted and the values above are returned in the dry-run result.
Expected behaviour
For maintainers to decide: should a runtime class be able to bound privilege escalation and the seccomp profile the way it bounds capabilities, so a class can refuse a container that asks for less confinement than the class is willing to serve? If so, the class contract needs a published limit for each, and validation needs to reject against it.
The contradictory add/drop case is separable and smaller: a container that both adds and drops the same capability has stated something self-contradictory, and the API should refuse it or normalise it rather than store it.
Summary
A customer can weaken two of the three confinement settings on a sandbox container to any value the API enum allows, and no runtime class can refuse it. Capabilities are bounded — a class publishes
grantableCapabilitiesand a request beyond it is rejected with a reason naming the class. Privilege escalation and the seccomp profile have no equivalent published ceiling, soallowPrivilegeEscalation: trueandseccompProfile: Unconfinedare accepted by every class in service today.Separately, a container that both adds and drops the same capability is stored as written instead of being refused or normalised.
This is not presently an exposure. Every runtime class in service is VM-isolated: each instance runs its own guest kernel, so these settings take effect inside the customer's own virtual machine and reach nothing shared. The gap is structural. A future class backed by shared-kernel isolation would inherit the same permissiveness silently, because the class contract has no way to state a limit and validation has nothing to reject against.
Follows #310, which introduced the published security context surface.
Evidence
Verified against the staging
datum-cloudproject control plane withkubectl apply --dry-run=server, on a sandbox Workload usingclass: general-purpose.allowPrivilegeEscalation: truetrueseccompProfile: {type: Unconfined}Unconfinedcapabilities: {add: [CHOWN], drop: [CHOWN]}add: [CHOWN],drop: [CHOWN, ALL]For contrast, an
addbeyond the class's grantable set is rejected naming the class and the set it grants.The gap is deliberate and documented, not accidental. Capabilities are the only security setting checked against the class. A class publishes
defaultSecurityContextvalues forallowPrivilegeEscalationandseccompProfile, and those are explicitly documented as a default rather than a limit — so whatever the container states for either is accepted.grantableCapabilitiesis the only ceiling the class contract expresses.For the contradictory add/drop case, each capability name in
addanddropis checked on its own and the two lists are never compared, so an overlap passes;ALLis then appended todrop, producing the stored value above.The question is already recorded as an open question in the runtime classes enhancement, including the note that adding a limit later is a compatible change because it only narrows what is accepted. This issue tracks it as work rather than restating it.
Reproduction
Against a control plane with the RuntimeClasses gate enabled and the
general-purposeclass published:The object is admitted and the values above are returned in the dry-run result.
Expected behaviour
For maintainers to decide: should a runtime class be able to bound privilege escalation and the seccomp profile the way it bounds capabilities, so a class can refuse a container that asks for less confinement than the class is willing to serve? If so, the class contract needs a published limit for each, and validation needs to reject against it.
The contradictory add/drop case is separable and smaller: a container that both adds and drops the same capability has stated something self-contradictory, and the API should refuse it or normalise it rather than store it.