Skip to content

Add mutating_webhook_resource_limits problem - #847

Merged
Saadmrp1038 merged 9 commits into
SREGym:mainfrom
keremakcali:feature/mutating_webhook_resource_limits_problem
Jun 6, 2026
Merged

Add mutating_webhook_resource_limits problem#847
Saadmrp1038 merged 9 commits into
SREGym:mainfrom
keremakcali:feature/mutating_webhook_resource_limits_problem

Conversation

@keremakcali

@keremakcali keremakcali commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

The real-world failure story

The motivating failure case for this implementation involves a mutating admission webhook silently rewriting pod memory limits at admission time, applying a value far higher than what was expected and breaking deployments via namespace quota rejection. The Deployment spec was untouched and showed the configured limit; the wrong value only existed on the mutated pod and matched nothing in the cluster's declared configuration.

The same mechanism — a webhook silently rewriting pod resource limits at admission time — can fail in either direction. The incident above showed it with a value too high; this problem simulates the inverse, where the injected value is too low and the pod OOMKills on startup. A low limit was chosen because reliably reproducing the quota-rejection symptom depends on the cluster having tight quotas, which isn't guaranteed across environments, whereas an aggressively low limit deterministically triggers OOMKill on any reasonable workload.

Reference:
kubernetes/autoscaler#8401

How the failure is simulated in SREGym

Problem class: sregym/conductor/problems/mutating_webhook_resource_limits.py
Registry key: mutating_webhook_resource_limits_social_network

The fault builds a fully self-contained webhook backend in platform-ops — a python:3.12-alpine container running a small HTTPS server whose code lives in a ConfigMap, so no custom image or registry is needed. Fresh TLS material is generated on every injection: one CA, one server cert signed by that CA, and the correct caBundle wired into the MutatingWebhookConfiguration so TLS validates and admission always goes through.

Once the backend is up, a MutatingWebhookConfiguration named pod-policy.platform.k8s.io is created scoped to social-network via namespaceSelector, with failurePolicy: Fail and a /mutate handler. From that point every pod CREATE in the namespace gets a JSON patch injecting 16Mi as the first container's memory limit. One nginx-thrift pod is then deleted to trigger an immediate mutated replacement and make the symptom visible right away.

The patch logic covers three cases depending on what the incoming pod spec has — no resources field, resources but no limits, or limits already present — using the add op throughout to avoid JSON Patch path-not-found errors.

INJECTED_MEMORY_LIMIT = "16Mi" is a class constant so the injected value is defined once and flows through the webhook server code, the root cause description, and all print statements automatically.

Credits:
The overall structure of the problem class and the fault injection pattern follow the approach established by @mohamedharake in admission_webhook_tls_mismatch (#777).

Runtime behaviour

Tested via the SREGym CLI on a local Kind cluster.

Phase Observation
App deploy social-network deploys normally before fault injection
Backend setup platform-ops/platform-policy-controller is running as a reachable HTTPS service
Fault injection One nginx-thrift pod is deleted; replacement is created and immediately mutated
Symptom nginx-thrift pod repeatedly enters OOMKilled / CrashLoopBackOff
Diagnostic trap kubectl get deployment nginx-thrift -o yaml shows no memory limit set
Diagnostic signal kubectl describe pod <nginx-thrift-xxx> shows Limits: memory: 16Mi
Blast radius Scoped to social-network via namespaceSelector; rest of cluster unaffected
Recovery Deleting the MutatingWebhookConfiguration and restarting the deployment restores normal behaviour

Checking the Deployment spec returns nothing for resources, but inspecting the actual running pod shows 16Mi injected under limits. The spec and the pod disagree — which only happens if something modified the pod between spec and runtime.

Expected agent behaviour

Tested end-to-end with the claudecode agent (claude-sonnet-4-6). Diagnosis passed (composite 0.78), mitigation passed. TTL 97.6s, TTM 167.4s.

Expected successful agent path:

  1. Notice nginx-thrift is repeatedly OOMKilling.
  2. Check kubectl describe pod on the crashing pod and find Limits: memory: 16Mi.
  3. Check the Deployment spec and find no memory limit set — notice the discrepancy.
  4. Suspect admission-time mutation as the only explanation for spec and pod disagreeing.
  5. Run kubectl get mutatingwebhookconfigurations and find pod-policy.platform.k8s.io.
  6. Inspect the webhook configuration and confirm it patches memory limits in social-network.
  7. Mitigate by deleting the webhook configuration and restarting the deployment.

The intended challenge is that the visible symptom looks like an application problem — OOMKill, CrashLoopBackOff — but the root cause is a cluster-scoped admission webhook that is invisible from those angles and only discoverable by comparing what the Deployment spec declares against what the pod actually runs with.

/validate-problem mutating_webhook_resource_limits_social_network

@keremakcali
keremakcali marked this pull request as ready for review June 1, 2026 17:49
@Saadmrp1038
Saadmrp1038 self-requested a review June 1, 2026 19:12
@Saadmrp1038

Saadmrp1038 commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

@keremakcali Can you please link some real-world incident stories that inspired this fault? Not generic troubleshooting guides. Preferably post-mortems. Without an incident to ground it in, it's harder to judge the relevance of a fault. Although I do love that the diagnosis for this fault is not quite straight forward!

@Saadmrp1038 Saadmrp1038 added the problem Adding a new problem to the benchmark label Jun 1, 2026
@keremakcali

keremakcali commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

Oh, I'm so sorry. I pasted the wrong reference link. I unfortunately don't have an official post-mortem because I had a hard time finding one that wasn't already implemented or mentioned in posted RFCs, and that I could understand well enough to believe I could implement something reliably (or i am bad at googling). So I'm using this GitHub issue as the basis of this implementation instead. Also, I just updated the PR text to better reflect how i decided to go through with this implementation. I hope it's alright @Saadmrp1038 .

@Saadmrp1038

Copy link
Copy Markdown
Collaborator

@keremakcali That's alright. I will need to review to see how the agent performs on this fault over multiple runs. It seems good at first glance. Maybe we can tweak some stuff to make it harder.

@keremakcali

Copy link
Copy Markdown
Contributor Author

Maybe we can tweak some stuff to make it harder.

@Saadmrp1038 Sounds great! Thanks a lot for the reply.

@Saadmrp1038 Saadmrp1038 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@keremakcali Thanks for the effort! The implementation looks good. I tested it end-to-end and everything works correctly.

One thing is as it is now the problem is too easy. There are a couple of tweaks that can be done to make it harder for the agent to solve:

  1. Consider patching the nginx-thrift deployment with an explicit memory limit (e.g. 256Mi limit / 128Mi request) BEFORE activating the webhook, so the deployment spec shows a real value. Right now resources: {} in the spec immediately signals something injected this field when the pod shows 16Mi. The webhook currently only patches limits.memory. If you set both requests and limits in the spec, you'll need the webhook to patch both fields (otherwise Kubernetes rejects the pod with requests > limits). This also feels like a more realistic patch.

  2. Consider installing 3-4 decoy MutatingWebhookConfigurations (e.g. cert-manager, istio, kyverno names). Although make sure they don't target/affect anything.

Please ping me when you've addressed them.

 - Memory limit/request patching before activating the webhook
 - Decoy pods for increased complexity
@keremakcali

keremakcali commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Hey @Saadmrp1038, I implemented these changes. Tried it out with the agent and it seems like it still can solve it. I tried to address some minor details it had used in the runs as well with not much success. The main thing that makes it easy for the agent seems to be how easy it is to notice a single OOMKilling pod that has a different memory request/limit from its spec. All the other complexities just increase the runtime until it finally finds the correct webhook that mutates it from decoys. I have an idea to make this aspect harder, so I wanted to ask if it makes sense:

I was planning to add more pods that get affected by this mutating webhook that injects a very low memory limit. In this case some pods will OOMKill but some will not because they don't require as much memory. I did one such run and the agent did fix the OOMKilling pods but didn't notice the non-OOMKilling pods. Maybe the mitigation oracle should check if it addressed all affected pods. This part is where I am not sure if it makes sense though, because it feels a little bit like an artificially created trap. But at the same time, if those limits persist they might eventually crash due to things like traffic or other stuff that increase their memory usage, which makes them worth checking in the oracle. I would appreciate your feedback on this.

@Saadmrp1038

Saadmrp1038 commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

@keremakcali The mitigation is easy that I know. Does it get diagnosis right too? My thought was the agent would see another plausible hypothesis and submit that as root cause without exploring all the options.

@keremakcali

Copy link
Copy Markdown
Contributor Author

@Saadmrp1038 Yeah, actually in diagnosis a lot of the time it frames the nginx-thrift as the faulty component even though it is the victim. I think in one run it thought a decoy MWC was the culprit. Those are the problems that i saw about diagnosis. It almost always figures out the right MWC in the mitigation phase eventually tho. I am not super sure what to get of all these but yeah diagnosis is usually problematic. I was mostly focusing on the mitigation...

@Saadmrp1038

Saadmrp1038 commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

@keremakcali Diagnosis failing but mitigation succeeding is also an interesting thing imo. The agent, when diagnosing, initially finds a plausible reason and stops. It doesn't try to make sure there isn't any other possible cause for the issue. But when mitigating, when the mitigation strategy based on the diagnosis fails, the agent then expands its search and finds out that the root cause is somewhere else.


That's why previous suggestions were mostly about adding distractors and not about making the mitigation harder. Because I don't think we can make the mitigaiton for this fault that hard anyway.

@keremakcali

Copy link
Copy Markdown
Contributor Author

@Saadmrp1038 Hmm.. That makes a lot of sense. In that case i think i will be pushing the changes and we see what happens.

 - Make decoys realisticly named
 - Similar minor changes
@keremakcali

Copy link
Copy Markdown
Contributor Author

Hi @Saadmrp1038 just pushed the implementation addressing the tweaks.

@keremakcali
keremakcali requested a review from Saadmrp1038 June 5, 2026 20:48
@Saadmrp1038

Copy link
Copy Markdown
Collaborator

@keremakcali Looks good to me. Claude Code most of the time fails diagnosis by a few points due to not targeting the webhook configuration itself as the faulty component but rather blaming the backend service (platform-policy-controller). Stratus fails more drastically as it never checks mutating webhooks at all and submits a wrong diagnosis blaming the pod/deployment configuration.

Congrats on your first PR merged here 🎉

@Saadmrp1038
Saadmrp1038 merged commit d634957 into SREGym:main Jun 6, 2026
vsmart-06 pushed a commit to vsmart-06/SREGym that referenced this pull request Jun 15, 2026
* Add mutating_webhook_resource_limits problem

* Add mutating_webhook_resource_limits to Problem List

* mutating_webhook_resource_limits problem docstring fix

Removed incorrect information about mitigation options.

* Additions to mutating_webhook_limits_resource_limits problem

 - Memory limit/request patching before activating the webhook
 - Decoy pods for increased complexity

* More additions to mutating_webhook problem

 - Make decoys realisticly named
 - Similar minor changes

---------

Co-authored-by: Saad Mohammad Rafid Pial <saadmrp222@gmail.com>
Saadmrp1038 added a commit that referenced this pull request Jul 22, 2026
* Updated default mitigation oracle

* Stop timeout for 0 replicas

* TBC in CloudLab

* Pushing all attempts at the problem

* Enable idempotence

* Add mutating_webhook_resource_limits problem (#847)

* Add mutating_webhook_resource_limits problem

* Add mutating_webhook_resource_limits to Problem List

* mutating_webhook_resource_limits problem docstring fix

Removed incorrect information about mitigation options.

* Additions to mutating_webhook_limits_resource_limits problem

 - Memory limit/request patching before activating the webhook
 - Decoy pods for increased complexity

* More additions to mutating_webhook problem

 - Make decoys realisticly named
 - Similar minor changes

---------

Co-authored-by: Saad Mohammad Rafid Pial <saadmrp222@gmail.com>

* Derive the default problem namespace from the application (#876)

* add the the probem redundency

* base.

* the fixed and dynamic app name are fixed. hte namespace are also fixed.

* unessessry formating is reverted.

* unessessry formating is reverted.

* formating correction.

* formating correction.

* formating correction.

* add fault injection decorator markers to test_problem_bsae.py

---------

Co-authored-by: Saad Mohammad Rafid Pial <saadmrp222@gmail.com>

* Confluent kafka attempt

* Custom image attempt

* Threaded producer and added consumer

* Syntax error

* Added some imports

* fix edge cases

* run ruff check adn format

* New OOM attempt

* Force crash on OOM

* Add file_descriptor_exhaustion problem (#832)

* add file_descriptor_exhaustion problem

* Modify fault injection and recovery methods

* Fix fault recovery

* Change fault injection method
Add fault injection by flooding connections

* Update fault injection method and add custom oracle

---------

Co-authored-by: Saad Mohammad Rafid Pial <saadmrp222@gmail.com>

* Finalizer deadlock controller clean (#873)

* finalizzer code is reaoslve dadlock benchmark.

* yaml file corrected.

* reverting hte comments back to their place.

* read only rbac

* controller log is generalized and avoiding any sregym related name exposing.

* applied ruff linter formatter accordingly.

* replace metadata naming and includding all the oprations on mitigation side.

---------

Co-authored-by: Saad Mohammad Rafid Pial <saadmrp222@gmail.com>

* Add: secret_rotation_stale_env_credentials_astronomy_shop problem (#851)

* Add secret rotation stale env credentials problem

* Simplify secret rotation mitigation oracle checks

* Require rotated PostgreSQL in oracle

* Fix stale DB credential rotation shortcuts

  - Remove old password from postgresql-init ConfigMap
  - Keep PostgreSQL password rotation durable after pod restart
  - Reject mitigations that revert otelu to the old password
  - Remove old password leaks from unrelated pods

* Fix fault recovery to satisfy the oracle

* Fix problem registry import ordering

---------

Co-authored-by: Saad Mohammad Rafid Pial <saadmrp222@gmail.com>

* Add cumulative_admission_webhook_timeout_hotel_reservation problem (#886)

* Add cumulative_admission_webhook_timeout_hotel_reservation problem

* Pin webhook backends to worker nodes via nodeAffinity

* Make webhook backends realistic admission services

Replace the dummy webhook backends with HTTPS admission servers that terminate TLS using Secret-mounted certificates, validate AdmissionReview

requests, and return proper admission responses. Certificates are signed by the same CA referenced in each webhook's `caBundle`, with SANs

covering the backend Service DNS names.

The admission logic enforces a trusted image registry allowlist and a

per-container CPU limit. All Hotel Reservation workloads satisfy these

policies, so the webhooks remain transparent during normal operation and

recovery.

Also:
* Vary webhook `timeoutSeconds` (12, 8, 11, 9) to make the cumulative
  timeout less obvious.

* Rename the CA CN from `sregym-webhook-ca` to
  `compliance-webhook-ca`.

* Remove a duplicate `rule.from_` check in the oracle.

* Fix create-or-replace handling for webhooks, NetworkPolicies,
  Deployments, and Services so re-injection updates existing resources
  instead of failing or silently skipping changes.

* Model the fault as a missing apiserver NetworkPolicy allow

* Model the fault as a missing apiserver allow with a near-miss policy

---------

Co-authored-by: Saad Mohammad Rafid Pial <saadmrp222@gmail.com>

* Add psa_restricted_blocks_recreation problem (#891)

Labelling a namespace pod-security.kubernetes.io/enforce=restricted leaves
running pods untouched but makes the apiserver reject recreated pods that run
as root / set no seccomp profile, so a deployment silently goes
under-replicated. Unlike the existing webhook problems, the policy is enforced
by the apiserver via a namespace label, not a webhook config.

* Add copilot cli agent (#893)

* add copilot cli agent

* remove -s flag

* Fix MCP retry error handling and CRLF parsing in CI (#896)

* fix(mcp): broaden MCP retry exceptions and return error instead of raising

* fix(ci): strip CRLF from PR description before parsing problem ID

* Fix kind cluster detection (#897)

* Fix custom kind cluster detection

* Handle failed kind detection output

* fix edge cases

* run ruff check adn format

* Shell state persistence (#903)

* Include shell persistence

* Include shell persistence

* Isolate problem IDs from agent runtime artifacts

* update SREGym-applications (18620ed) (#910)

* Add Calico route-reflector label drift problem (#828)

* Add Calico route-reflector label drift problem

* Address Calico route-reflector review comments

* Harden Calico cleanup ownership tracking

* Fix Codex agent install verification

* Tighten Codex platform package detection

* Restore kubelet eviction reset cleanup

* Neutralize Calico ownership metadata

---------

Co-authored-by: Munim Thahmid <95485323+munimthahmid@users.noreply.github.com>
Co-authored-by: Saad Mohammad Rafid Pial <saadmrp222@gmail.com>

* Fail alert oracle on Prometheus query errors (#912)

* Decouple failure configmaps from mongo pods in `hotel-reservation` (#899)

* decouple failure configmaps from mongo pods

* remove empty failure configmaps creation

* Support local LLM endpoints for Stratus and OpenCode (#917)

* Support local LLM endpoints for Stratus

* Prefer env vars for local LLM endpoints

* Support local LLM endpoints for OpenCode

* Route local endpoints into agent containers

* Normalize OpenCode local judge models

---------

Co-authored-by: Saad Mohammad Rafid Pial <saadmrp222@gmail.com>

* Preflight check the judge model (#924)

* Add support for uniform agent trajectory (ATIF format) (#918)

* add support for ATIF trajectory conversion and normalization, add claudecode adapter

* fix: make _finish_problem idempotent

* isolate .codex mount in container runner

* better temp dir cleanup

* add codex adapter

* add opencode adapter

* add copilot adapter

* add stratus adapter

* fix antropic caching for stratus

* add gemini adapter

* store normalized ATIF trajectories in SQLite indexed by problem type

* edge tests

* bump litellm, tikoken

* update docker lib versions

* Add node_clock_drift Problem  (#894)

* adding clock drift problem

* fixing race condition causing sidecar log delay

* fixing recover_fault to reverse time sync removal applied, improve cleanup

* global cleanup added + fixed restoration for node_clock_drift problem

* fixing mitigation oracle to ensure correct node checked

* adding problem to non emulated cluster problems list

* extending wait

* indentation error fix

* fixing time restoration in global cleanup

* adding in check to make sure it picks worker node

* adding another control plane filter, fixing clock step-back in global recovery

* increase sidecar rollout timeout, make podnames/labels generic, idempotancy check

* formatting fixes

* format fix

* fix

* fix ruff check/format issues

---------

Co-authored-by: Saad Mohammad Rafid Pial <saadmrp222@gmail.com>

* Minimum viable python version

* Changed diagnosis oracle

* Cleaned up fault recovery

* Edit tasklist.yml

* Removed old Java artifacts

* Updated mitigation oracle to be more robust

* Added polling

* Fail Kafka producer leak injection on timeout

---------

Co-authored-by: Saad Mohammad Rafid Pial <saadmrp222@gmail.com>
Co-authored-by: Kerem Akcalioglu <blldr001@gmail.com>
Co-authored-by: ermias mulugeta <61350760+ermias19@users.noreply.github.com>
Co-authored-by: Tejas Shukla <85878274+TejasShukla2007@users.noreply.github.com>
Co-authored-by: Petr Myagkov <73434154+Ipetr14@users.noreply.github.com>
Co-authored-by: Omar Faruqe Riyad <riyad.omf@gmail.com>
Co-authored-by: Mohammad Tamimul Ehsan <54908501+TamimEhsan@users.noreply.github.com>
Co-authored-by: Munim Thahmid <95485323+munimthahmid@users.noreply.github.com>
Co-authored-by: Om Kumar <128745874+W-OK-E@users.noreply.github.com>
Co-authored-by: Munim Thahmid <munimthahmid2@gmail.com>
Co-authored-by: Tanzim Hossain Romel <romel.rcs@gmail.com>
Co-authored-by: haani-maybe <171415359+haani-maybe@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

problem Adding a new problem to the benchmark

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants