Skip to content

fix(webhook): validate that renewal windows are reachable within the certificate lifetime - #9305

Open
somaz94 wants to merge 1 commit into
cert-manager:masterfrom
somaz94:fix/renewal-window-feasibility
Open

somaz94 wants to merge 1 commit into
cert-manager:masterfrom
somaz94:fix/renewal-window-feasibility

Conversation

@somaz94

@somaz94 somaz94 commented Sep 8, 2026

Copy link
Copy Markdown

Pull Request Motivation

Closes #9146.

spec.renewal.windows is validated for cron syntax, timezone and the presence of windowDuration, but not for whether a window is reachable at all. A 1 hour certificate with cron 0 0 * * * and windowDuration: 1m is admitted today, and then every reconcile fails to find a window: readiness keeps it at Ready=False with reason WindowError, and the trigger controller renews outside the windows anyway.

applyRenewBeforeWithWindows looks for a window start in [notBefore-windowDuration, notAfter+windowDuration), so duration + 2*windowDuration is the widest range a start can fall into. The webhook now measures the shortest gap between cron activations and rejects the window when it is longer than that range, since hitting it would otherwise depend on where the certificate's validity happens to fall. A cron that never matches a date, like 0 0 30 2 *, is rejected as well.

Existing objects keep working: the check is skipped on update while spec.duration and spec.renewal are unchanged, the same way allowLegacyRenewBeforePercentageOnUpdate does it.

This does not overlap #9109, which fixes the panic on the trigger path. That one is the crash, this one is the class of specs that reach the error. They do touch the same file, so I can rebase whenever #9109 lands.

Locally: go test ./internal/... plus the pki, certificates controller and webhook packages pass, and golangci-lint run on the changed package with the pinned v2.13.2 reports 0 issues. The new tests fail without the production change.

Written with Claude Code (Opus 5).

Kind

/kind bug

Release Note

The webhook now rejects Certificate renewal windows that recur less often than the certificate's renewal search range, and windows whose cron never matches a date. Existing Certificates keep working until their duration or renewal configuration changes.

…certificate lifetime

Signed-off-by: somaz <genius5711@gmail.com>
@cert-manager-prow cert-manager-prow Bot added release-note Denotes a PR that will be considered when it comes time to generate release notes. kind/bug Categorizes issue or PR as related to a bug. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. dco-signoff: yes Indicates that all commits in the pull request have the valid DCO sign-off message. labels Sep 8, 2026
@cert-manager-prow

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign wallrj for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@cert-manager-prow

Copy link
Copy Markdown
Contributor

Hi @somaz94. Thanks for your PR.

I'm waiting for a cert-manager member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@cert-manager-prow cert-manager-prow Bot added needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Sep 8, 2026
@somaz94
somaz94 marked this pull request as ready for review September 8, 2026 05:22
@cert-manager-prow cert-manager-prow Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Sep 8, 2026
@hjoshi123

Copy link
Copy Markdown
Collaborator

@somaz94 Thank you for picking this up but I feel i need to push back here. I don't really like the approach here and feels like there are a lot of factors that have not been thought through. I would love to know what made you take this approach.

For example:

  • The webhook only knows spec.duration; the controller uses the issued cert's actual validity, which many issuers (ACME/LE, Venafi policy) override in either direction. For example: considering LE a 24h spec.duration with a weekly window is rejected even though the actual 90‑day LE cert would hit it every time — and the reverse (spec says 90d, CA issues 7d) sails through and still breaks. The check will both reject working configs and accept broken ones.
  • Also windows are union in nature the way its implemented. So how would you handle the duration validation there?

A naive validation is easy to implement here but something like this it has the ability to break both valid and invalid ones for the wrong reasons. I am not saying that validation is a bad thing, I get the issue but we need to be careful and thoughtful before we add such strict validations. I also feel, if we are to fix the behavior mentioned in the bug we could look at readiness controller too to see how we can gracefully retry the renewal mechanism. Long term that is the better option than stricter validation in my opinion.

I understand that this was an AI assisted PR but I would prefer to understand your thought process on the PR and how you think this mechanism should work ideally.

@somaz94

somaz94 commented Sep 10, 2026

Copy link
Copy Markdown
Author

Fair pushback, and I think you're right on both counts.

The approach came from the issue rather than from me. #9146 suggested an admission feasibility check on the grounds that spec.duration is known at admission time, and I implemented that as written. Your first point is what breaks that premise. RenewalTime takes notBefore and notAfter and works from notAfter.Sub(notBefore), which the code itself calls actualDuration, and the search bounds are derived from those too. The webhook only ever sees spec.duration and cannot know what the issuer actually handed back, so it is measuring a different quantity than the controller uses. That is not something I can patch inside a validating webhook.

The union point is right as well. I validate each window on its own, while applyRenewBeforeWithWindows accumulates across all of them, so a spec whose windows are jointly reachable can still be rejected because one of them is sparse.

The one piece I would still argue for is the cron that matches no date at all, like 0 0 30 2 *. That can never be entered whatever the issued duration turns out to be and whatever else is in the union, so it does not rest on either premise.

On how it should work ideally, I now agree with you that this belongs in the renewal path rather than in admission. Admission cannot see the value the decision actually depends on, so a check there is guessing, and guessing wrong in both directions is worse than not checking at all. I am happy to cut this down to the never matches case, or to close it and look at graceful retry in the readiness and trigger controllers instead. Which would you prefer?

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

Labels

dco-signoff: yes Indicates that all commits in the pull request have the valid DCO sign-off message. kind/bug Categorizes issue or PR as related to a bug. needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Renewal windows that can never be satisfied pass admission; the Certificate is left permanently Ready=False and renewed outside its windows

2 participants