Skip to content

fix(cert-manager): move the webhook off the kubelet's port - #3359

Merged
myasnikovdaniil merged 1 commit into
mainfrom
fix/cert-manager-webhook-port
Jul 20, 2026
Merged

myasnikovdaniil merged 1 commit into
mainfrom
fix/cert-manager-webhook-port

Conversation

@lexfrei

@lexfrei Aleksei Sviridkin (lexfrei) commented Jul 19, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

The cert-manager webhook listens on port 10250 — the kubelet's port. When a connection to the webhook Service resolves to a node IP rather than the pod IP, it reaches the kubelet, which answers the TLS handshake with its own node serving certificate. The API server then rejects every cert-manager admission call in the cluster:

failed calling webhook "webhook.cert-manager.io": ... x509: certificate is valid for srv3, not cert-manager-webhook.cozy-cert-manager.svc

The certificate is valid — it is simply the kubelet's, issued for the node name. That signature is what separates this from the cainjector failure mode, which reports certificate signed by unknown authority; that possibility was checked and ruled out on the run where this was observed. Here it blocked the whole platform bootstrap and pointed the operator at cert-manager rather than at the routing layer.

The value comes straight from the vendored chart, where upstream chose it deliberately for a different constraint: on GKE private clusters the API server may only reach nodes on 443 and 10250. cozystack did not override it.

Moving the webhook to 10260 is upstream's own remedy for this exact error. The cert-manager troubleshooting guide documents the identical signature — under a different cause, EKS Fargate, where each microVM runs a kubelet that already holds 10250 — and prescribes --set webhook.securePort=10260.

This does not fix the misroute, whose root cause is still unestablished; the run carries no dataplane state, so candidates such as a stale service map during bootstrap remain unproven. What it removes is the trap that turns a rare, transient misroute into a cluster-wide failure attributed to the wrong component. With the collision gone, the same misroute surfaces as a connection refusal.

The override lives in the package-root values file, which make update leaves alone, so it survives re-vendoring.

Upgrade safety

The rendered webhook Deployment has one replica and no explicit strategy, so the defaults apply: maxUnavailable 0 and maxSurge 1. The replacement Pod must become Ready before the old one is removed. The Service targets a named port rather than a number, so during the overlap the EndpointSlice controller routes to each Pod on the port it actually serves. There is no window with zero ready endpoints, and no maintenance step is required. Had the Service pinned the port numerically, this upgrade itself would have broken cluster-wide admission.

Not addressed here

external-secrets-operator runs its webhook on 10250 with the same failurePolicy: Fail, so it carries the identical exposure and deserves the same treatment; metrics-server also uses 10250 but is an APIService rather than an admission webhook. Both are left out to keep this change to one component.

Closes #3355

Release note

fix(cert-manager): move the admission webhook off port 10250, which the kubelet also serves. A connection misrouted to a node answered with the kubelet's certificate, failing every cert-manager admission call cluster-wide with a misleading x509 error.

Summary by CodeRabbit

  • New Features

    • Configured the cert-manager webhook to use secure port 10260.
    • Added coverage to verify secure port configuration, HTTPS service routing, and disabled host networking.
  • Documentation

    • Added configuration comments explaining the secure port selection and its role in avoiding service routing conflicts.

The vendored chart defaults webhook.securePort to 10250, which is the
port the kubelet serves on. Upstream picks that value deliberately, for
GKE private clusters where apiservers may reach nodes only on 443 and
10250; it is free of consequence there and unsafe here.

When a connection to the webhook Service resolves to a node IP instead
of a Pod IP, it reaches the kubelet, which completes the TLS handshake
with its node serving certificate. The apiserver then rejects every
cert-manager admission call in the cluster with

  x509: certificate is valid for <node>, not
  cert-manager-webhook.cozy-cert-manager.svc

and, because the webhook is failurePolicy: Fail, that stalls platform
bootstrap. The certificate is valid; it is simply the wrong server's.
The error names cert-manager and points away from the routing layer
that actually failed, which makes a rare misroute expensive to
diagnose.

Serve on 10260 instead, the port upstream's troubleshooting guide
prescribes for this exact error. It documents the failure on EKS
Fargate, where a microVM forwards every port to the pod except the one
its own kubelet already holds; the cause there is understood and ours
is not, but the shape is the same, down to the certificate that comes
back naming the node. What the fix needs of the port is that nothing
answers on it in a node's network namespace, and nothing in the
platform does. The same misroute is then refused at connect time, which
is an honest and immediately diagnosable error. This does not prevent
the misroute, whose cause is unestablished; it removes the collision
that turns one into a cluster-wide failure attributed to the wrong
component.

The Service targets the container port by name, so it follows the
change without an edit, and a rolling update resolves the old and new
ports per Pod rather than dropping the endpoint. Tests pin the listener
flag together with the advertised containerPort so the two cannot drift
apart.

Assisted-By: Claude <[email protected]>
Signed-off-by: Aleksei Sviridkin <[email protected]>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical issue where the cert-manager webhook, by defaulting to port 10250, conflicts with the kubelet. This collision causes TLS handshake failures when traffic is misrouted to a node IP, leading to cluster-wide admission errors. By moving the webhook to port 10260, the change ensures that such misroutes result in clear connection refusals rather than misleading certificate errors, while also adding automated tests to prevent future regressions.

Highlights

  • Webhook Port Migration: Updated the cert-manager webhook to listen on port 10260 instead of 10250 to avoid conflicts with the kubelet.
  • Regression Testing: Added a new test suite to ensure the webhook port configuration is maintained and correctly applied in the deployment.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment Gemini (@gemini-code-assist) Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@github-actions github-actions Bot added area/uncategorized PR auto-labeler could not map title scope to a known area/*; please review kind/bug Categorizes issue or PR as related to a bug size/M This PR changes 30-99 lines, ignoring generated files labels Jul 19, 2026
@dosubot dosubot Bot added area/platform Issues or PRs related to platform infrastructure (bundle, flux, talos, installer) kind/backport Categorizes issue or PR as requiring a backport to the current release line labels Jul 19, 2026
@coderabbitai

coderabbitai Bot commented Jul 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7befbaf4-9ca5-4fd6-a067-48fb349eab1c

📥 Commits

Reviewing files that changed from the base of the PR and between 7e206e2 and df85632.

📒 Files selected for processing (2)
  • packages/system/cert-manager/tests/webhook_secure_port_test.yaml
  • packages/system/cert-manager/values.yaml

📝 Walkthrough

Walkthrough

Cert-manager’s webhook secure port is set to 10260. A new test suite verifies the deployment port configuration, disabled host networking, and Service routing through the https port name.

Changes

Cert-manager webhook port

Layer / File(s) Summary
Secure port configuration and validation
packages/system/cert-manager/values.yaml, packages/system/cert-manager/tests/webhook_secure_port_test.yaml
The webhook uses secure port 10260, while tests validate the deployment flag and container port, absence of hostNetwork, and Service routing through https on port 443.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Suggested reviewers: androndo

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: moving cert-manager's webhook off the kubelet port.
Linked Issues check ✅ Passed The PR implements the requested securePort change to 10260 and adds tests to keep the webhook Service and Deployment aligned.
Out of Scope Changes check ✅ Passed The changes stay focused on the webhook port fix and its verification; no unrelated code changes are introduced.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/cert-manager-webhook-port

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request configures the cert-manager webhook to serve on port 10260 instead of the default 10250 to prevent port collision with the kubelet and avoid misrouted TLS handshake errors. It also introduces a Helm unit test suite to verify this configuration. Feedback on the test suite points out that the notExists assertion is not supported by helm-unittest and should be replaced with isNull.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +48 to +49
- notExists:
path: spec.template.spec.hostNetwork

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.

high

The notExists assertion is not supported by helm-unittest. Using it will cause the test suite to fail with an error. To assert that a field is absent or nil, you should use the isNull assertion instead.

      - isNull:
          path: spec.template.spec.hostNetwork

@myasnikovdaniil myasnikovdaniil 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.

LGTM — correct diagnosis, minimal surface, upgrade-safe by construction, and the reasoning is preserved where the next maintainer will actually read it.

Business context: the cert-manager webhook listens on 10250, the kubelet's port, so a connection that lands on a node IP instead of the pod IP reaches the kubelet and gets its valid-but-wrong certificate — admission calls fail with an x509 error that points at cert-manager rather than at the routing, and bootstrap stalls entirely.

Reviewed together with #3360, #3361 and #3362 as a series, since all four contend for ports in the node network namespace.

Verified

  • The upgrade-safety claim holds. charts/cert-manager/values.yaml:756 (replicaCount: 1) and :811 (strategy: {}) mean Kubernetes defaults to maxUnavailable: 25% → 0 / maxSurge: 25% → 1, i.e. surge-first: the replacement must pass readiness before the old pod is removed. The Service targets targetPort: https by name, so the EndpointSlice controller resolves per-pod during the overlap — old pod on 10250, new pod on 10260, each reached correctly. There is no window with zero ready endpoints, so no maintenance note is needed. The PR's own statement that a numeric targetPort "would have broken cluster-wide admission" is accurate — that is exactly the hazard #3360 had to patch around.
  • The schema reasoning is sound. charts/cert-manager/values.schema.json carries 28 "additionalProperties": false entries and securePort is a defined key (:1893, :2248), so an upstream rename would fail helm loudly rather than silently ignore the override. That justifies the test comment's decision not to cover that case. (Worth contrasting with #3360, where the opposite is true and the test really is the only guard.)
  • The premise is corroborated upstream in cert-manager's own words — values.schema.json:2017: "Since the default port for the webhook conflicts with kubelet on the host network, webhook.securePort should be changed to an available port if running in hostNetwork mode."
  • 10260 is genuinely free. The literal appears nowhere else in the tree as a port. The aggregate host-netns TCP set across every default-enabled workload is {2379, 2380, 6641-6643, 7946, 8080, 8081, 9100, 9120, 9140, 9141, 9234, 9443, 9690-9693, 9879, 9963, 9964, 10250, 10257, 10259, 10660, 10661, 10665, 19443, 3366, 3367, 7572} — no collision. No Talos service conflicts either: the repo ships no Talos machine-config at all (packages/core/talos/ is imager profiles only).
  • Tests run in CI. packages/system/cert-manager/Makefile:6-7 already had a test: target on main and hack/helm-unit-tests.sh:22 gates discovery on exactly that, so the new suite is picked up. helm unittest . → 2 suites, 4 tests, all pass.
  • hostNetwork: false default confirmed at charts/cert-manager/values.yaml:1085.

Follow-up (non-blocking)

Worth one sentence about the sibling. After this and #3360 both land, cert-manager's webhook and the external-secrets webhook both target 10260. That is harmless as shipped — both are pod-netns, so each binds 10260 in its own namespace. The trap is that both charts document hostNetwork as the supported remedy for managed clusters where the apiserver cannot reach pod IPs (cert-manager's schema says so verbatim, quoted above). An operator who enables hostNetwork on both webhooks gets the second pod CrashLooping with bind: address already in use — the exact failure mode #3361 exists to avoid for metrics-server.

The hostNetwork caveat at values.yaml:37-40 currently only warns about the cilium network policy. Suggest adding: "external-secrets' webhook also serves 10260; enabling hostNetwork on both requires giving one of them a different port."

Note on merge order

None required for this series. The concern that #3361 might transiently squat 10260 does not apply — metrics-server moves 10250 → 4443 and never transits 10260, and its hostNetwork is off by default (metrics-server/charts/metrics-server/values.yaml:70-76) and enabled nowhere in the tree. Merge in any order.

@myasnikovdaniil
myasnikovdaniil merged commit 3affec9 into main Jul 20, 2026
20 of 21 checks passed
@myasnikovdaniil
myasnikovdaniil deleted the fix/cert-manager-webhook-port branch July 20, 2026 07:26
myasnikovdaniil added a commit that referenced this pull request Jul 20, 2026
## What this PR does

The external-secrets webhook listens on port 10250 — the kubelet's port
— with `failurePolicy: Fail`. This is the same trap being removed from
cert-manager in #3359: a connection to the webhook Service that resolves
to a node IP instead of the pod IP reaches the kubelet, which completes
the handshake with its own node serving certificate, and the API server
rejects the call with an x509 error that blames the webhook's PKI rather
than the routing layer. The port collision does not cause the misroute;
it turns a rare transient one into a confidently misattributed failure.

The blast radius here is wider than admission alone:
`crds.conversion.enabled` defaults to true and points every
ExternalSecret CRD's conversion webhook at the same Service, so a
misrouted connection also breaks CRD conversion — reads at non-storage
versions, not just writes.

The webhook moves to 10260, matching #3359. The controller-runtime
default 9443 was considered and rejected: the etcd kube-rbac-proxy binds
it on host network, which would reproduce exactly the same
wrong-certificate failure with a different certificate. Nothing in the
tree binds 10260.

Unlike cert-manager, the upstream chart's Service targets the port
numerically — `targetPort: {{ .Values.webhook.port }}` — and a numeric
targetPort is copied into every endpoint verbatim rather than resolved
per pod. With one replica, a port change would direct traffic at a port
the still-running old pod does not serve while the replacement is not
yet Ready, failing every admission and conversion call in the gap;
surge-first rollout does not help because both pods sit behind the same
number. A one-line vendored-chart patch points the Service at the
`webhook` container-port name upstream already declares (the metrics
port in the same Service is already named), which closes the gap: during
the overlap each pod is routed on the port it actually serves. The patch
is applied by `make update` with `--forward`, so an already-applied hunk
fails loudly instead of being silently reversed. Upstream has already
made the same change — merged as external-secrets/external-secrets#5108
and shipped since chart 0.19.1 — but the vendored chart here is 0.10.4,
nine minor releases earlier, so the patch carries the fix until the
chart is bumped past that point. The `--forward` guard is exactly what
will flag that moment: on a bumped chart the patch fails loudly,
signalling it should be dropped.

external-secrets-operator ships disabled by default and is enabled
per-cluster via `bundles.enabledPackages`, so this is defensive
hardening for opt-in installs rather than a response to an observed
incident.

`metrics-server` is a third instance of the same port collision, left
out here: it serves an APIService rather than an admission webhook, a
lesser failure class deserving its own change.

### Release note

```release-note
fix(external-secrets): move the webhook off port 10250, which the kubelet also serves, and make its Service target the port by name so the move rolls out without an admission gap
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved webhook Service routing by switching to the named
`targetPort` (`webhook`) for pod traffic.
* Configured the webhook listener to run on port **10260** to avoid
prior connection/TLS certificate misrouting issues.
  * Preserved admission validation behavior and CRD conversion routing.
* **Tests**
* Added Helm chart regression tests covering webhook port wiring,
networking settings, failure policy behavior, and CRD conversion.
* **Chores**
* Added Helm chart unit testing to the development workflow and extended
the chart update process to apply the webhook routing adjustment.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
myasnikovdaniil added a commit that referenced this pull request Jul 20, 2026
## What this PR does

metrics-server is the third component serving on port 10250 — the
kubelet's port — after the webhooks addressed in #3359 and #3360. It
ships default-enabled in the platform bundle and is also installed into
every tenant Kubernetes cluster with no value overrides, so a
package-root override propagates to both.

The hard defect here is different from the webhook cases. The chart has
first-class `hostNetwork.enabled` support, documented upstream for
clusters where the API server cannot reach pod IPs — and on the default
port that mode cannot start at all: the kubelet already holds 10250, so
the process dies with `bind: address already in use`. Upstream is aware
(kubernetes-sigs/metrics-server#1026 carries the exact panic) and
declined to change the chart default, advising hostNetwork users to set
their own port. This override is that port, set ahead of need.

The misleading-certificate angle of the sibling fixes mostly does not
apply: the chart defaults `apiService.insecureSkipTLSVerify` to true, so
a connection misrouted to a node would fail silently rather than with a
wrong-certificate error — and with endpoints resolving to pod IPs such a
misroute is not established to occur here at all. The comment states
that as a hypothesis, not a fact. What is factual is the consumer set:
`kubectl top`, tenant HPAs, and the console's cluster-usage page all
read `metrics.k8s.io`; the shipped VPA does not — it is configured with
Prometheus storage.

The port is 4443 rather than the siblings' 10260, for a reason worth
keeping: metrics-server is the one component of the three with supported
hostNetwork, and parking it on 10260 would put a live host-network
listener on exactly the port the webhook fixes rely on being silent —
turning their honest connection refusals back into answered handshakes.
4443 is the value metrics-server's own manifests shipped through v0.6.4,
before v0.7.0 moved them to 10250; it sits outside the 10248-10259
node-component block, and nothing in the tree binds it.

No chart patch is needed: the Service, both probes and the
ServiceMonitor already reference the port by name, so the rollout-gap
that required a vendored patch in #3360 does not exist here. The package
also gains the standard `test:` target it was missing.

Not verified without a cluster: that the aggregated APIService stays
Available through an upgrade, and that no supported substrate filters
4443 inbound on nodes — the tree configures no host firewall, but Talos
and managed substrates are outside what a render can prove. There is no
e2e coverage of `metrics.k8s.io` at all; a wrong guess here would
surface as HPAs silently not scaling.

### Release note

```release-note
fix(metrics-server): move the listener off port 10250, which the kubelet also holds. The default made hostNetwork mode unable to start at all, and the new port keeps node-port collisions out of the metrics path.
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
  * Configured the metrics server to listen on secure port 4443.
* Updated service routing and health checks to use the named HTTPS port,
improving reliability when port settings change.
  * Ensured pod networking does not rely on host networking.

* **Tests**
* Added automated chart tests covering container ports, service routing,
and liveness/readiness probes.
  * Added a test command for running the chart test suite.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@github-actions

Copy link
Copy Markdown

Created backport PR for release-1.5:

Please cherry-pick the changes locally and resolve any conflicts.

git fetch origin backport-3359-to-release-1.5
git worktree add --checkout .worktree/backport-3359-to-release-1.5 backport-3359-to-release-1.5
cd .worktree/backport-3359-to-release-1.5
git reset --hard HEAD^
git cherry-pick -x df85632c8909e775580a7c8732dffae8b8e473ca
git push --force-with-lease

myasnikovdaniil added a commit that referenced this pull request Jul 20, 2026
…opes (#3362)

## What this PR does

PRs titled with the `cert-manager`, `external-secrets` or
`metrics-server` scopes landed on `area/uncategorized` because the
labeler's scope map had no entry for them — all three surfaced at once
in the current port-collision series (#3359, #3360, #3361).
metrics-server feeds the monitoring pipeline, so it maps to
`area/monitoring`; cert-manager and external-secrets are platform
services, so they map to `area/platform`.

### Release note

```release-note
NONE
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Chores**
  - Improved pull request labeling automation.
- Pull requests involving metrics, certificate management, and external
secrets are now categorized more accurately with the appropriate area
labels.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
myasnikovdaniil added a commit that referenced this pull request Jul 31, 2026
## What this PR does

The cert-manager webhook listens on port 10250 — the kubelet's port.
When a connection to the webhook Service resolves to a node IP rather
than the pod IP, it reaches the kubelet, which answers the TLS handshake
with its own node serving certificate. The API server then rejects every
cert-manager admission call in the cluster:

```text
failed calling webhook "webhook.cert-manager.io": ... x509: certificate is valid for srv3, not cert-manager-webhook.cozy-cert-manager.svc
```

The certificate is valid — it is simply the kubelet's, issued for the
node name. That signature is what separates this from the cainjector
failure mode, which reports `certificate signed by unknown authority`;
that possibility was checked and ruled out on the run where this was
observed. Here it blocked the whole platform bootstrap and pointed the
operator at cert-manager rather than at the routing layer.

The value comes straight from the vendored chart, where upstream chose
it deliberately for a different constraint: on GKE private clusters the
API server may only reach nodes on 443 and 10250. cozystack did not
override it.

Moving the webhook to 10260 is upstream's own remedy for this exact
error. The cert-manager troubleshooting guide documents the identical
signature — under a different cause, EKS Fargate, where each microVM
runs a kubelet that already holds 10250 — and prescribes `--set
webhook.securePort=10260`.

This does not fix the misroute, whose root cause is still unestablished;
the run carries no dataplane state, so candidates such as a stale
service map during bootstrap remain unproven. What it removes is the
trap that turns a rare, transient misroute into a cluster-wide failure
attributed to the wrong component. With the collision gone, the same
misroute surfaces as a connection refusal.

The override lives in the package-root values file, which `make update`
leaves alone, so it survives re-vendoring.

### Upgrade safety

The rendered webhook Deployment has one replica and no explicit
strategy, so the defaults apply: `maxUnavailable` 0 and `maxSurge` 1.
The replacement Pod must become Ready before the old one is removed. The
Service targets a named port rather than a number, so during the overlap
the EndpointSlice controller routes to each Pod on the port it actually
serves. There is no window with zero ready endpoints, and no maintenance
step is required. Had the Service pinned the port numerically, this
upgrade itself would have broken cluster-wide admission.

### Not addressed here

`external-secrets-operator` runs its webhook on 10250 with the same
`failurePolicy: Fail`, so it carries the identical exposure and deserves
the same treatment; `metrics-server` also uses 10250 but is an
APIService rather than an admission webhook. Both are left out to keep
this change to one component.

Closes #3355

### Release note

```release-note
fix(cert-manager): move the admission webhook off port 10250, which the kubelet also serves. A connection misrouted to a node answered with the kubelet's certificate, failing every cert-manager admission call cluster-wide with a misleading x509 error.
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Configured the cert-manager webhook to use secure port 10260.
* Added coverage to verify secure port configuration, HTTPS service
routing, and disabled host networking.

* **Documentation**
* Added configuration comments explaining the secure port selection and
its role in avoiding service routing conflicts.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

(cherry picked from commit 3affec9)
(cherry picked from commit ee8344d7bf782a608574ff3a064cb5c766ac49ff)
myasnikovdaniil added a commit that referenced this pull request Jul 31, 2026
Backport of #3359 onto release-1.5.

The cert-manager webhook listened on 10250, the port the kubelet also
serves. When a connection to the webhook Service resolved to a node IP
rather than a Pod IP it reached the kubelet, which completed the TLS
handshake with its own node serving certificate, and the API server then
rejected every cert-manager admission call cluster-wide:

  failed calling webhook "webhook.cert-manager.io": ... x509: certificate
  is valid for srv3, not cert-manager-webhook.cozy-cert-manager.svc

The certificate is valid — it is simply the wrong server's — so the error
reads as a cert-manager fault and hides the misroute that caused it.
Moving the webhook to 10260 is upstream's own remedy for this signature.
It does not fix the misroute; it removes the collision that turns a rare
transient one into a cluster-wide outage attributed to the wrong
component.

The override lives in the package-root values file, which `make update`
leaves alone, so it survives re-vendoring.

Stacked on the #3199 backport (#3202): release-1.5 ships an EMPTY
packages/system/cert-manager/values.yaml, so every resource override in
that file arrived after v1.5.2. The bot's cherry-pick of this fix
conflicted for that reason alone — nothing to merge into — and applies
cleanly once the cainjector backport is in place.

Upgrade safety is unchanged from the original: one replica, no explicit
strategy, so maxUnavailable 0 / maxSurge 1 apply and the replacement Pod
must be Ready before the old one goes. The Service targets a NAMED port,
so during the overlap each Pod is reached on the port it actually serves
and there is no window with zero ready endpoints.

Closes #3355

(cherry picked from commit 3affec9)

Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
myasnikovdaniil added a commit that referenced this pull request Aug 4, 2026
…belet's port (#3366)

# Description
Backport of #3359 to `release-1.5`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/platform Issues or PRs related to platform infrastructure (bundle, flux, talos, installer) area/uncategorized PR auto-labeler could not map title scope to a known area/*; please review kind/backport Categorizes issue or PR as requiring a backport to the current release line kind/bug Categorizes issue or PR as related to a bug size/M This PR changes 30-99 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cert-manager webhook securePort 10250 collides with kubelet — a misrouted Service connection yields a valid-but-wrong certificate and blocks bootstrap

2 participants