Remove SCC-injected security contexts for cross-cluster migration#24
Remove SCC-injected security contexts for cross-cluster migration#24nachandr wants to merge 1 commit into
Conversation
Signed-off-by: Nandini Chandra <nachandr@redhat.com>
📝 WalkthroughWalkthroughAdded a new Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@openshift.go`:
- Around line 428-430: The allow-list that skips security-context stripping is
missing "DeploymentConfig", so update the conditional that checks kind (the if
in openshift.go comparing kind to "Pod","Deployment", etc.) to also include
"DeploymentConfig" so its spec.template.spec pod template has SCC
UID/GID/SELinux cleared; additionally, apply the corresponding patch from the
existing DeploymentConfig branch in cmd.go so PVC rename logic for
DeploymentConfig is preserved (ensure the cmd.go changes that handle PVC
renaming for DeploymentConfig are appended/merged into the current cmd.go
implementation).
- Around line 437-485: The current code deletes entire securityContext objects
(via unstructured.RemoveNestedField and delete(container, "securityContext")),
which loses user-authored settings; instead, modify logic in RemoveNestedField
calls and in stripContainerSecurityContext to only remove cluster-injected
fields: runAsUser, fsGroup, supplementalGroups, seLinuxOptions (and any
cluster-specific UID/GID fields), leaving other keys (privileged, capabilities,
allowPrivilegeEscalation, readOnlyRootFilesystem, seccompProfile) intact;
implement this by reading the securityContext map (use unstructured.NestedMap or
container["securityContext"]), deleting those specific keys, writing the map
back (and only removing the whole securityContext if the map becomes empty), and
optionally gate the existing destructive behavior behind an explicit option/flag
if full removal is desired; update references: modified.Object,
unstructured.RemoveNestedField usage, and the stripContainerSecurityContext
closure to perform targeted-key removal rather than delete(container,
"securityContext").
- Line 498: The call to jsonpatch.CreatePatch does not exist in the
evanphx/json-patch API; replace it by using
jsonpatch.CreateMergePatch(originalJSON, modifiedJSON) if an RFC7396 merge patch
is acceptable (rename patch variable to mergePatch and use jsonpatch.MergePatch
APIs accordingly), or else implement RFC6902 operations yourself by constructing
the JSON Patch operation array (add/replace/remove objects) from originalJSON
and modifiedJSON and use that array as the patch payload instead of calling
CreatePatch; update references to the variable created from CreatePatch and any
apply/HTTP patch code to match the chosen approach.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
| if kind != "Pod" && kind != "Deployment" && kind != "StatefulSet" && | ||
| kind != "DaemonSet" && kind != "Job" && kind != "CronJob" && | ||
| kind != "ReplicaSet" && kind != "ReplicationController" { |
There was a problem hiding this comment.
Include DeploymentConfig in security-context stripping.
DeploymentConfig has a pod template at spec.template.spec, but this allow-list excludes it. Migrated OpenShift apps using DeploymentConfigs can still retain source-cluster SCC UID/GID/SELinux values and hit the validation failure this PR is intended to prevent.
Suggested fix direction
if kind != "Pod" && kind != "Deployment" && kind != "StatefulSet" &&
kind != "DaemonSet" && kind != "Job" && kind != "CronJob" &&
- kind != "ReplicaSet" && kind != "ReplicationController" {
+ kind != "ReplicaSet" && kind != "ReplicationController" &&
+ kind != "DeploymentConfig" {
return jsonpatch.Patch{}, nil
}Also append this patch from the existing DeploymentConfig branch in cmd.go so PVC renames are preserved.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if kind != "Pod" && kind != "Deployment" && kind != "StatefulSet" && | |
| kind != "DaemonSet" && kind != "Job" && kind != "CronJob" && | |
| kind != "ReplicaSet" && kind != "ReplicationController" { | |
| if kind != "Pod" && kind != "Deployment" && kind != "StatefulSet" && | |
| kind != "DaemonSet" && kind != "Job" && kind != "CronJob" && | |
| kind != "ReplicaSet" && kind != "ReplicationController" && | |
| kind != "DeploymentConfig" { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@openshift.go` around lines 428 - 430, The allow-list that skips
security-context stripping is missing "DeploymentConfig", so update the
conditional that checks kind (the if in openshift.go comparing kind to
"Pod","Deployment", etc.) to also include "DeploymentConfig" so its
spec.template.spec pod template has SCC UID/GID/SELinux cleared; additionally,
apply the corresponding patch from the existing DeploymentConfig branch in
cmd.go so PVC rename logic for DeploymentConfig is preserved (ensure the cmd.go
changes that handle PVC renaming for DeploymentConfig are appended/merged into
the current cmd.go implementation).
| // Remove pod-level spec.securityContext | ||
| unstructured.RemoveNestedField(modified.Object, "spec", "securityContext") | ||
|
|
||
| // For workload controllers, remove spec.template.spec.securityContext | ||
| if kind != "Pod" { | ||
| if kind == "CronJob" { | ||
| // CronJob has spec.jobTemplate.spec.template.spec | ||
| unstructured.RemoveNestedField(modified.Object, "spec", "jobTemplate", "spec", "template", "spec", "securityContext") | ||
| } else { | ||
| // Deployment/StatefulSet/DaemonSet/Job/ReplicaSet/ReplicationController have spec.template.spec | ||
| unstructured.RemoveNestedField(modified.Object, "spec", "template", "spec", "securityContext") | ||
| } | ||
| } | ||
|
|
||
| // Helper function to strip container securityContext | ||
| stripContainerSecurityContext := func(containersPath ...string) { | ||
| containers, found, _ := unstructured.NestedSlice(modified.Object, containersPath...) | ||
| if found { | ||
| for i, c := range containers { | ||
| if container, ok := c.(map[string]interface{}); ok { | ||
| delete(container, "securityContext") | ||
| containers[i] = container | ||
| } | ||
| } | ||
| unstructured.SetNestedSlice(modified.Object, containers, containersPath...) | ||
| } | ||
| } | ||
|
|
||
| // Determine base path for containers | ||
| var basePath []string | ||
| if kind == "Pod" { | ||
| basePath = []string{"spec"} | ||
| } else if kind == "CronJob" { | ||
| basePath = []string{"spec", "jobTemplate", "spec", "template", "spec"} | ||
| } else { | ||
| basePath = []string{"spec", "template", "spec"} | ||
| } | ||
|
|
||
| // Remove container-level securityContext | ||
| containersPath := append(basePath, "containers") | ||
| stripContainerSecurityContext(containersPath...) | ||
|
|
||
| // Remove initContainers securityContext | ||
| initContainersPath := append(basePath, "initContainers") | ||
| stripContainerSecurityContext(initContainersPath...) | ||
|
|
||
| // Remove ephemeralContainers securityContext (if present) | ||
| ephemeralContainersPath := append(basePath, "ephemeralContainers") | ||
| stripContainerSecurityContext(ephemeralContainersPath...) |
There was a problem hiding this comment.
Preserve user-authored security settings instead of deleting the whole securityContext.
This removes fields such as privileged, capabilities, allowPrivilegeEscalation, readOnlyRootFilesystem, and seccompProfile along with SCC-injected UID/GID/SELinux values. That can weaken migrated workloads or change their runtime behavior. Prefer deleting only cluster-specific fields like runAsUser, fsGroup, supplementalGroups, and seLinuxOptions, or make the destructive behavior explicit via an option.
Safer direction
- unstructured.RemoveNestedField(modified.Object, "spec", "securityContext")
+ removeSCCInjectedPodSecurityContextFields(modified.Object, "spec", "securityContext")
...
- delete(container, "securityContext")
+ removeSCCInjectedContainerSecurityContextFields(container, "securityContext")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@openshift.go` around lines 437 - 485, The current code deletes entire
securityContext objects (via unstructured.RemoveNestedField and
delete(container, "securityContext")), which loses user-authored settings;
instead, modify logic in RemoveNestedField calls and in
stripContainerSecurityContext to only remove cluster-injected fields: runAsUser,
fsGroup, supplementalGroups, seLinuxOptions (and any cluster-specific UID/GID
fields), leaving other keys (privileged, capabilities, allowPrivilegeEscalation,
readOnlyRootFilesystem, seccompProfile) intact; implement this by reading the
securityContext map (use unstructured.NestedMap or
container["securityContext"]), deleting those specific keys, writing the map
back (and only removing the whole securityContext if the map becomes empty), and
optionally gate the existing destructive behavior behind an explicit option/flag
if full removal is desired; update references: modified.Object,
unstructured.RemoveNestedField usage, and the stripContainerSecurityContext
closure to perform targeted-key removal rather than delete(container,
"securityContext").
| return nil, err | ||
| } | ||
|
|
||
| patch, err := jsonpatch.CreatePatch(originalJSON, modifiedJSON) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the imported json-patch module and all CreatePatch references.
rg -n 'github\.com/evanphx/json-patch|CreatePatch\s*\(' go.mod go.sum .Repository: migtools/crane-plugin-openshift
Length of output: 1500
🌐 Web query:
Does github.com/evanphx/json-patch provide a CreatePatch function in the version imported by a Go module?
💡 Result:
No, github.com/evanphx/json-patch does not provide a CreatePatch function in any version imported by a Go module. The library supports creating RFC 7396 JSON Merge Patches using CreateMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error), but there is no equivalent function to automatically generate RFC 6902 JSON Patches (arrays of operations) from two documents. RFC 6902 patches must be manually constructed as JSON arrays of operations and decoded using DecodePatch(buf []byte) (Patch, error).
Citations:
- 1: https://pkg.go.dev/github.com/evanphx/json-patch/v5
- 2: https://github.com/evanphx/json-patch/blob/master/patch.go
- 3: https://github.com/evanphx/json-patch/blob/master/README.md
- 4: https://github.com/evanphx/json-patch
- 5: https://github.com/evanphx/json-patch/blob/master/v5/merge.go
- 6: https://godoc.org/gopkg.in/evanphx/json-patch.v5
Replace jsonpatch.CreatePatch with correct library API or switch libraries.
github.com/evanphx/json-patch v4.11.0+incompatible does not expose a CreatePatch function. The library provides CreateMergePatch() for RFC 7396 JSON Merge Patches only; RFC 6902 JSON Patches must be manually constructed as operation arrays. Either build patch operations directly or use a diff library that provides JSON Patch generation.
🧰 Tools
🪛 golangci-lint (2.11.4)
[error] 498-498: undefined: jsonpatch.CreatePatch
(typecheck)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@openshift.go` at line 498, The call to jsonpatch.CreatePatch does not exist
in the evanphx/json-patch API; replace it by using
jsonpatch.CreateMergePatch(originalJSON, modifiedJSON) if an RFC7396 merge patch
is acceptable (rename patch variable to mergePatch and use jsonpatch.MergePatch
APIs accordingly), or else implement RFC6902 operations yourself by constructing
the JSON Patch operation array (add/replace/remove objects) from originalJSON
and modifiedJSON and use that array as the patch payload instead of calling
CreatePatch; update references to the variable created from CreatePatch and any
apply/HTTP patch code to match the chosen approach.
Fixes issue
The changes add a stripSecurityContext function that removes pod-level and container-level security contexts from workload resources (Pods, Deployments, StatefulSets, etc.) to prevent SCC validation failures when migrating between OpenShift clusters with different namespace UID ranges.
Summary by CodeRabbit