Skip to content

fix: decouple skills from the privileged sandbox and default to restricted PSS - #2462

Open
QuentinBisson wants to merge 1 commit into
kagent-dev:mainfrom
QuentinBisson:fix/skills-restricted-pss-v1alpha3
Open

fix: decouple skills from the privileged sandbox and default to restricted PSS#2462
QuentinBisson wants to merge 1 commit into
kagent-dev:mainfrom
QuentinBisson:fix/skills-restricted-pss-v1alpha3

Conversation

@QuentinBisson

Copy link
Copy Markdown
Contributor

Supersedes #2334, which the stale bot closed and GitHub refuses to reopen. Reimplemented against v1alpha3.SandboxAgent after #2422 removed the deployment-backed agent API. Fixes #1997 and #2244, both re-verified against current main rather than taken from the issue text.

What changes

  • buildSkillsRuntime no longer sets needCodeExecIsolation = true whenever spec.skills is configured. Loading skills needs no elevated privileges: the skills-init init container does the git clone and image pull, and the privileged container only exists for the in-pod srt/bubblewrap sandbox.
  • buildContainerSecurityContext returns a restricted-PSS-compliant default (allowPrivilegeEscalation: false, runAsNonRoot: true, capabilities.drop: [ALL], seccompProfile: RuntimeDefault) instead of nil when no isolation is requested. The skills-init container gets the same defaults.
  • python/Dockerfile.full uses a numeric USER 1001:1001 so the kubelet can verify runAsNonRoot without an explicit runAsUser. The other three runtime images already use numeric users.

Without this, agent pods render with no container securityContext at all and are rejected outright on clusters enforcing restricted Pod Security admission.

How it differs from #2334

The original PR seeded needCodeExecIsolation from the declarative.executeCodeBlocks opt-in and left the workaround from #1997 (allowPrivilegeEscalation: false on the agent CR) as the escape hatch. Neither exists anymore: executeCodeBlocks is gone from the API, and v1alpha3.SandboxAgent has no user-supplied securityContext, so buildContainerSecurityContext is always called with a nil base. On current main the only thing that can request a privileged container is spec.skills, and that is itself rejected by CEL on SandboxAgent — so restricted clusters have the bug with no way out.

I kept the needCodeExecIsolation parameter and the privileged branch of buildContainerSecurityContext rather than deleting them, since they are the seam an explicit sandbox opt-in would use when skills return to SandboxAgent. They are unreachable today; happy to delete them instead if you prefer.

Verification

  • go build ./core/..., go test ./core/... (1289 passed), go vet, golangci-lint
  • New security_context_test.go covers the restricted default, the skills path, and the privileged branch. No other test in the package asserted the old nil default.

@QuentinBisson
QuentinBisson force-pushed the fix/skills-restricted-pss-v1alpha3 branch from 66b2c7e to 9e9d31f Compare August 17, 2026 13:40
@github-actions github-actions Bot added the bug Something isn't working label Aug 17, 2026
@QuentinBisson
QuentinBisson marked this pull request as ready for review August 17, 2026 14:00
@QuentinBisson
QuentinBisson requested a review from a team as a code owner August 17, 2026 14:00
Copilot AI lite review requested due to automatic review settings August 17, 2026 14:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the SandboxAgent manifest translation so that configuring spec.skills no longer implicitly requires privileged containers, and so that generated agent/init containers default to a Pod Security Standards (PSS) restricted-compliant container securityContext. It also adjusts the full Python runtime image to use a numeric user so runAsNonRoot can be enforced without requiring an explicit runAsUser.

Changes:

  • Stop coupling skills setup to code-exec isolation/privilege requirements (skills init remains unprivileged).
  • Default generated container securityContext to PSS restricted settings when no isolation is requested, and apply the same defaults to the skills init container.
  • Switch python/Dockerfile.full to USER 1001:1001 for kubelet runAsNonRoot verification, and add translator unit tests covering these securityContext defaults.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
python/Dockerfile.full Uses a numeric USER to support runAsNonRoot validation without setting runAsUser.
go/core/internal/controller/translator/agent/security_context_test.go Adds tests asserting restricted defaults, skills non-privileged behavior, and the privileged branch behavior.
go/core/internal/controller/translator/agent/manifest_builder.go Removes skills→privileged coupling and introduces restricted-PSS default container security context behavior.
Suppressed comments (1)

go/core/internal/controller/translator/agent/manifest_builder.go:444

  • new(false) / new(true) are being called with boolean values, which does not compile in Go (the built-in new takes a type). Initialize local bool vars and take their addresses for these pointer fields.
func restrictedSecurityContext() *corev1.SecurityContext {
	return &corev1.SecurityContext{
		AllowPrivilegeEscalation: new(false),
		RunAsNonRoot:             new(true),
		Capabilities: &corev1.Capabilities{

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +84 to +88
base := &corev1.SecurityContext{
AllowPrivilegeEscalation: new(false),
ReadOnlyRootFilesystem: new(true),
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not correct: new(expr) with a value argument is valid as of Go 1.26 (accepted spec change), and this module is on go 1.26.3. It is already the established idiom on main — e.g. NeedLeaderElection: new(true) in core/internal/controller/sandboxagent_controller.go and six sibling controllers. go build ./core/..., go vet and the package tests all pass.

return restrictedSecurityContext()
}

return &corev1.SecurityContext{Privileged: new(true)}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not correct: new(expr) with a value argument is valid as of Go 1.26 (accepted spec change), and this module is on go 1.26.3. It is already the established idiom on main — e.g. NeedLeaderElection: new(true) in core/internal/controller/sandboxagent_controller.go and six sibling controllers. go build ./core/..., go vet and the package tests all pass.

…icted PSS

Skills no longer force Privileged=true on the agent container, and
containers without an explicit securityContext now render the restricted
Pod Security Standards defaults (allowPrivilegeEscalation=false,
runAsNonRoot=true, drop ALL capabilities, seccomp RuntimeDefault). The
skills-init container gets the same defaults.

python/Dockerfile.full switches to a numeric USER so the kubelet can
verify runAsNonRoot without an explicit runAsUser.

Fixes kagent-dev#1997
Fixes kagent-dev#2244

Signed-off-by: QuentinBisson <quentin@giantswarm.io>
@QuentinBisson
QuentinBisson force-pushed the fix/skills-restricted-pss-v1alpha3 branch from 9e9d31f to bbece3e Compare August 17, 2026 19:01
@QuentinBisson

QuentinBisson commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto main (bc92b68). Re-verified locally after the rebase: go build, go vet, and the touched Go/Python test packages pass.

The three remaining red checks are not from this branch, they reproduce identically on main at the same commit (run 32034286380):

  • go-unit-tests: the go/api/v1alpha2 and go/api/v1alpha3 CEL tests fail in setup, not in assertions. setup-envtest cannot write the 1.36.2-linux-amd64 assets ("unable to create file kubectl/etcd from archive to disk"), so the control plane never starts. Looks like parallel test packages racing on the same go/bin/k8s store.
  • upgrade-tests (prev-stable) / rolling-upgrade-tests (prev-stable): UPGRADE FAILED: context deadline exceeded in TestUpgrade/upgrade_with_helm.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Skills path sets privileged: true unconditionally, incompatible with restricted-PSS clusters

2 participants