-
Notifications
You must be signed in to change notification settings - Fork 40.9k
Deny pod admission for static pods referencing API objects #131837
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Deny pod admission for static pods referencing API objects #131837
Conversation
CC @haircommander @liggitt Tagging from the discussion in the original issue. I'd love to hear what you folks think! Thanks! |
/retest |
1 similar comment
/retest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR.
Should we also deny static Pods that specify .spec.resources.claims
and / or .spec.resourceClaims
?
(We perhaps shouldn't assume that these claims will fail anyway).
True, but if they don't make sense for static pods I'm okay with denying pods with |
/retest |
pkg/features/kube_features.go
Outdated
// owner: @sreeram-venkitesh | ||
// | ||
// Denies pod admission if static pods reference other API objects. | ||
StaticPodStrictValidation featuregate.Feature = "StaticPodStrictValidation" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"strict" is pretty ambiguous ... let's target this specifically at static pods referencing API objects, so call this "PreventStaticPodAPIReferences" or something similar.
That will also let us make use of the same gate in the kubelet and in NodeRestriction admission
pkg/features/kube_features.go
Outdated
@@ -1783,6 +1788,10 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate | |||
{Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // GA in 1.31, remove in 1.33 | |||
}, | |||
|
|||
StaticPodStrictValidation: { | |||
{Version: version.MustParse("1.34"), Default: false, PreRelease: featuregate.Alpha}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would consider starting this as beta and on-by-default ... this is a bugfix with an escape hatch mitigation for a few releases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. Fixed this in 8f86d9d.
test/e2e/feature/feature.go
Outdated
@@ -445,6 +445,10 @@ var ( | |||
// TODO: document the feature (owning SIG, when to use this feature for a test) | |||
StatefulSet = framework.WithFeature(framework.ValidFeatures.Add("StatefulSet")) | |||
|
|||
// Owner: sig-node | |||
// Denies pod admission if static pods reference other API objects. | |||
StaticPodStrictValidation = framework.WithFeature(framework.ValidFeatures.Add("StaticPodStrictValidation")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we don't need an e2e feature for this, there's no other configuration associated with it, if you write an e2e test, use framework.WithFeatureGate(features.PreventStaticPodAPIReferences)
, etc
pkg/kubelet/lifecycle/handlers.go
Outdated
if !kubetypes.IsStaticPod(pod) { | ||
return PodAdmitResult{Admit: true} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a single method that takes a pod and returns an error if the pod references API objects will be easier to ensure is called correctly. It might make sense to extract the checks done at
kubernetes/plugin/pkg/admission/noderestriction/admission.go
Lines 307 to 340 in 889cd83
// don't allow a node to create a pod that references any other API objects | |
if pod.Spec.ServiceAccountName != "" { | |
return admission.NewForbidden(a, fmt.Errorf("node %q can not create pods that reference a service account", nodeName)) | |
} | |
hasSecrets := false | |
podutil.VisitPodSecretNames(pod, func(name string) (shouldContinue bool) { hasSecrets = true; return false }, podutil.AllContainers) | |
if hasSecrets { | |
return admission.NewForbidden(a, fmt.Errorf("node %q can not create pods that reference secrets", nodeName)) | |
} | |
hasConfigMaps := false | |
podutil.VisitPodConfigmapNames(pod, func(name string) (shouldContinue bool) { hasConfigMaps = true; return false }, podutil.AllContainers) | |
if hasConfigMaps { | |
return admission.NewForbidden(a, fmt.Errorf("node %q can not create pods that reference configmaps", nodeName)) | |
} | |
for _, vol := range pod.Spec.Volumes { | |
if vol.VolumeSource.Projected != nil { | |
for _, src := range vol.VolumeSource.Projected.Sources { | |
if src.ClusterTrustBundle != nil { | |
return admission.NewForbidden(a, fmt.Errorf("node %q can not create pods that reference clustertrustbundles", nodeName)) | |
} | |
} | |
} | |
} | |
for _, v := range pod.Spec.Volumes { | |
if v.PersistentVolumeClaim != nil { | |
return admission.NewForbidden(a, fmt.Errorf("node %q can not create pods that reference persistentvolumeclaims", nodeName)) | |
} | |
} | |
if len(pod.Spec.ResourceClaims) > 0 { | |
return admission.NewForbidden(a, fmt.Errorf("node %q can not create pods that reference resourceclaims", nodeName)) | |
} |
Then the NodeRestriction admission plugin could call that, and the kubelet could call it from tryDecodeSinglePod / tryDecodePodList where it has an internal pod, and only hits that code path when loading static pods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@liggitt Please check 4809d0f. I've extracted the logic to a method in pkg/api/pod/util.go
. This is then called from both NodeRestriction admission's admitPodCreate
and tryDecodeSinglePod
for static pods.
I have a question about tryDecodeSinglePod
. Does this only process static pods? I see that this is called from pkg/kubelet/config/file.go
and pkg/kubelet/config/http.go
. The file one makes sense - reading static pod manifests, but I was not sure about the tryDecodeSinglePod call here in extractFromURL in pkg/kubelet/config/http.go. generatePodName also tells me this is static pod specific.
One more question: Compared to my earlier approach of adding a new AdmitHandler, this logic would now be executed only when parsing static pods. The AdmitHandler approach is redundant because,it would process all pods and then inside the handler check if the pod is a static pod. But we can avoid checking all the pods. Am I correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The kubelet can load static pods from file or from a non-API server URL. Both of those should have this enforcement.
doing the enforcement there would be instead of a kubelet admission handler
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, I forgot that we can specify --manifest-url
too. Thanks!
8f86d9d
to
4809d0f
Compare
dfad17a
to
aab9a2f
Compare
pkg/api/pod/util.go
Outdated
ErrStaticPodResourceClaims = errors.New("can not create static pods that reference resourceclaims") | ||
) | ||
|
||
func HasAPIObjectReferences(pod *api.Pod) (bool, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
document what this does, something like "returns true if the pod spec references any API objects, along with the plural resource of the first reference encountered"
I would make this return a string resource name, rather than do the error wrapping here and exposing error variables
// HasAPIObjectReference returns true if a reference to an API object is found in the pod spec,
// along with the plural resource of the referenced API type, or an error if an unknown field is encountered.
func HasAPIObjectReference(pod *api.Pod) (bool, string, error)
0c512ae
to
c05007e
Compare
c05007e
to
bedcb38
Compare
/retest |
@liggitt Can I squash and clean up the commits here? |
yup, looks good to squash, thanks! |
… referencing API objects
c92f30a
to
f9a5aec
Compare
/retest |
/lgtm |
LGTM label has been added. Git tree hash: 92ca3dde1f4a5d2a04d90c55b28d3e32557f904f
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: liggitt, sreeram-venkitesh The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
The Kubernetes project has merge-blocking tests that are currently too flaky to consistently pass. This bot retests PRs for certain kubernetes repos according to the following rules:
You can:
/retest |
/retest |
What type of PR is this?
/kind feature
What this PR does / why we need it:
Currently, mirror pod reconciliation for static pods which reference API objects will fail. However the pod itself is not denied admission and the node would be silently running the static pod's container. As discussed in #103587 (comment), we want to tighten the validation here so that pod would not be running at all in the node. Currently the static pod would be running in the node while the mirror pod would not be created (which is enforced in the API as per #103587 (comment)). This PR updates the validation logic so that the pod is denied admission for static pods referencing API objects and make sure that the container is not created in the node.
Which issue(s) this PR fixes:
Fixes #103587
Special notes for your reviewer:
Notes:
canAdmitPod
. This is based on @gjkim42's comment Fail to create static pod referencing ServiceAccount #105368 (comment).PredicateAdmitHandler
in pkg/kubelet/lifecycle/predicate.gohostPath
andemptyDir
volume mounts are made valid for static pods based on @enj's comment: Kubelet should enforce that static pods do not reference other API objects #103587 (comment)StaticPodStrictValidation
feature gate with which we can toggle the changes to the validation.Question:
Does this PR introduce a user-facing change?
Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.:
(The feature gate should be documented. I will update the docs PR here once opened.)