Skip to content

feat: allow overriding kubelet kubeconfig tls-server-name when KubePrism is enabled#13425

Closed
IvanHunters wants to merge 1 commit into
siderolabs:mainfrom
IvanHunters:feat/kubeprism-tls-server-name
Closed

feat: allow overriding kubelet kubeconfig tls-server-name when KubePrism is enabled#13425
IvanHunters wants to merge 1 commit into
siderolabs:mainfrom
IvanHunters:feat/kubeprism-tls-server-name

Conversation

@IvanHunters

Copy link
Copy Markdown

Pull Request

Refs #13424

What? (description)

Add a new optional field tlsServerName to the KubePrism feature config:

machine:
  features:
    kubePrism:
      enabled: true
      port: 7445
      tlsServerName: cluster-xyz.example.com   # new, optional, default empty

When set, the generated kubelet kubeconfig carries clusters[0].cluster.tls-server-name set to that value, so the kubelet uses it for SNI and certificate hostname verification. KubePrism keeps binding to 127.0.0.1:<port> and the kubelet keeps dialing https://127.0.0.1:<port> as the TCP destination. Only the TLS SNI/hostname that the kubelet sends and verifies against is decoupled from the TCP target.

When the field is empty (default), behavior is unchanged.

Why? (reasoning)

In internal/app/machined/pkg/controllers/secrets/kubelet.go the endpoint is hardcoded:

localEndpoint, err := url.Parse(fmt.Sprintf("https://127.0.0.1:%d", cfgProvider.Machine().Features().KubePrism().Port()))

This forces SNI=127.0.0.1 over the proxied connection. In setups where KubePrism's upstream is reached through an SNI-routing L4 proxy (for example nginx-ingress in ssl-passthrough mode in front of a Kamaji-hosted apiserver), SNI=127.0.0.1 does not match any ingress route and the proxy serves a fallback certificate without IP SANs, so the kubelet rejects with:

cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs

Setting tls-server-name in the kubeconfig makes Go's tls.Config.ServerName (and therefore SNI) be the configured value, while the TCP destination stays loopback. This is the natural Kubernetes-native way to decouple the two, and aligns with how kubeconfigs already support this field.

Changes

  • New optional tlsServerName field on KubePrism in pkg/machinery/config/types/v1alpha1.
  • New TLSServerName() string method on the config.KubePrism provider interface.
  • New EndpointTLSServerName field on secrets.KubeletSpec resource (protobuf id 6, omitempty), so the kubeconfig writer has access to the value.
  • secrets.KubeletController populates EndpointTLSServerName from the KubePrism config when KubePrism is enabled, and leaves it empty otherwise.
  • k8s.KubeletServiceController.writePKI sets cluster.TLSServerName on the bootstrap kubeconfig.
  • k8s.KubeletServiceController.updateKubeconfig sets cluster.TLSServerName when refreshing the kubelet runtime kubeconfig.
  • Regenerated v1alpha1_types_doc.go and updated the website config reference for v1.14.

Acceptance

Please use the following checklist:

  • you linked an issue (if applicable)
  • you included tests (if applicable)
  • you ran conformance (make conformance)
  • you formatted your code (make fmt)
  • you linted your code (make lint)
  • you generated documentation (make docs)
  • you ran unit-tests (make unit-tests)

Notes on the unchecked items:

  • make conformance and make lint go through the project's Docker/buildkit pipeline and were not run locally; CI will exercise them.
  • Unit tests for the touched packages compile and run cleanly locally (go test ./pkg/machinery/config/... passes; secrets/k8s controllers compile cleanly with GOOS=linux).
  • The new test case TestReconcileKubePrismTLSServerName in internal/app/machined/pkg/controllers/secrets/kubelet_test.go asserts that EndpointTLSServerName is propagated when configured.

Test plan

  • Build a Talos image with this change.
  • On a cluster where KubePrism's upstream apiserver is fronted by an SNI-routing L4 proxy, set machine.features.kubePrism.tlsServerName to the apiserver's hostname.
  • Verify /etc/kubernetes/kubelet.conf and /etc/kubernetes/bootstrap-kubeconfig contain tls-server-name: <hostname> under the cluster entry.
  • Verify the kubelet successfully connects and registers the node.
  • Verify that leaving tlsServerName unset produces an unchanged kubeconfig (no tls-server-name key) and behaves identically to current Talos.

@github-project-automation github-project-automation Bot moved this to To Do in Planning May 24, 2026
@talos-bot talos-bot moved this from To Do to In Review in Planning May 24, 2026
@IvanHunters
IvanHunters force-pushed the feat/kubeprism-tls-server-name branch from 7470709 to f2a6763 Compare May 24, 2026 12:54
… KubePrism

Add an optional `tlsServerName` field to `machine.features.kubePrism`.
When set, the generated kubelet kubeconfig carries
`clusters[0].cluster.tls-server-name` set to that value, so the kubelet
uses it for SNI and certificate hostname verification while still dialing
`https://127.0.0.1:<kubePrismPort>` as the TCP destination.

Without this knob, the endpoint in the kubelet kubeconfig is hardcoded
to `https://127.0.0.1:<port>` (see
`internal/app/machined/pkg/controllers/secrets/kubelet.go`), which forces
SNI=`127.0.0.1` over the proxied connection. In setups where KubePrism's
upstream apiserver is reached through an SNI-routing L4 proxy (for
example nginx-ingress in ssl-passthrough mode in front of a Kamaji-hosted
apiserver), SNI=`127.0.0.1` does not match any route and the proxy serves
a fallback certificate, so the kubelet rejects the connection with
`cannot validate certificate for 127.0.0.1 because it doesn't contain any
IP SANs`.

Setting `tls-server-name` in the kubeconfig is the Kubernetes-native way
to decouple the TCP destination from the TLS SNI/hostname check, and
matches how kubeconfigs already support this field.

When the field is empty (default), behavior is unchanged.

Refs siderolabs#13424

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
@IvanHunters
IvanHunters force-pushed the feat/kubeprism-tls-server-name branch from f2a6763 to 13b3d38 Compare May 24, 2026 12:54
@smira smira moved this from In Review to On Hold in Planning May 25, 2026
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

This PR is stale because it has been open 45 days with no activity.

@github-actions github-actions Bot added the Stale label Jul 9, 2026
smira added a commit to smira/talos that referenced this pull request Jul 21, 2026
Mostly one-to-one translation, but it also incorporates work from
the PR siderolabs#13425 to add support for TLSServerName.

Fixes siderolabs#13424

Fixes siderolabs#13777

Co-authored-by: IvanHunters <xorokhotnikov@gmail.com>
Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
smira added a commit to smira/talos that referenced this pull request Jul 21, 2026
Mostly one-to-one translation, but it also incorporates work from
the PR siderolabs#13425 to add support for TLSServerName.

Fixes siderolabs#13424

Fixes siderolabs#13777

Co-authored-by: IvanHunters <xorokhotnikov@gmail.com>
Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
@smira

smira commented Jul 21, 2026

Copy link
Copy Markdown
Member

Merged into #13820, thank you!

@smira smira closed this Jul 21, 2026
@github-project-automation github-project-automation Bot moved this from On Hold to Done in Planning Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants