fix(windows): write TLS bootstrap-config in NodePrep, not BasePrep#8795
Merged
Conversation
PIS/VHD-cached Windows nodes fail to register because the on-disk bootstrap kubeconfig (c:\k\bootstrap-config) holds the stale bake-time TLS bootstrap token. BasePrep wrote bootstrap-config, but it is skipped on cached nodes (base_prep.complete marker), so the live token from $global:TLSBootstrapToken never overwrote the baked one and the apiserver rejected the kubelet CSR as Unauthorized. kubelet, installed/started by Install-KubernetesServices in NodePrep, is the only consumer of bootstrap-config. Move Write-BootstrapKubeConfig from BasePrep into NodePrep (before Install-KubernetesServices) so it is written from the live token on every real provision. No VHD rebake is required since the CSE script is rendered fresh into CustomData each boot. The Calico setup in BasePrep uses the separate cert-based 'config' from Write-KubeConfig, which is unchanged, so BasePrep keeps working. Work item: 38630884 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a Windows node registration failure for PIS / VHD-cached clusters where a stale, bake-time TLS bootstrap token in c:\k\bootstrap-config causes kubelet CSR requests to be rejected as Unauthorized. The fix aligns bootstrap-config generation with the actual provisioning phase so the live $global:TLSBootstrapToken from CustomData is always used when kubelet starts.
Changes:
- Move
Write-BootstrapKubeConfigfromBasePreptoNodePrep. - Ensure
bootstrap-configis written beforeInstall-KubernetesServices(i.e., before kubelet is started) during real node provisioning.
Same PIS/VHD-cache class of bug as the bootstrap-config move. BasePrep
wrote c:\k\config (the cert-based kubeconfig from Write-KubeConfig) with
bake-time cluster material (CA, server, AgentCertificate). BasePrep is
skipped on cached nodes, so on a PIS node config keeps the bake cluster's
values and is never refreshed.
config is only consumed in NodePrep:
- Calico's GetCalicoKubeConfig (Start-InstallCalico) uses it to reach
the API server, then it is removed at the end of NodePrep; and
- in legacy cert-auth mode (no token, no secure bootstrap) it is
kubelet's own kubeconfig.
Move Write-KubeConfig (and its temporary/permanent log block) into
NodePrep, before Install-KubernetesServices, so it is written from the
live values on every real provision. No consumer of config exists in
BasePrep, so the move is safe; the existing conditional removal at the
end of NodePrep is unchanged.
Work item: 38630884
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Trim the verbose per-kubeconfig comment to a short rationale so it is less likely to drift from the code as consumers change. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Add a teeth-y regression guard for the BasePrep->NodePrep kubeconfig fix. The VHDCaching legacy-TLS-bootstrap scenario now injects a stale sentinel bootstrap token at the BAKE (pre-provision) stage only, while the real cluster token is used at provision time. If bootstrap-config were written in BasePrep (the bug), the cached VHD would carry the stale token and the node would fail to register; because it is written in NodePrep, the live token wins. The validator asserts bootstrap-config contains the live token and never the sentinel. Adds Config.PreProvisionBootstrapConfigMutator, a bake-stage-only hook, wired into runScenarioWithPreProvision. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
timmy-wright
approved these changes
Jun 30, 2026
Move remaining cluster-specific Windows config writes to NodePrep so PIS cached VHDs do not bake stale kubeconfig, cert, azurestack, or azure.json data. NodePrep regenerates the files from live CustomData before runtime consumers start. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…compat The CSE scripts package cached on the VHD (currently v0.0.52, pinned via windowsCSEScriptsPackageURL) configures containerd during BasePrep and reads the pause image from C:\k\kubeclusterconfig.json. Moving Write-KubeClusterConfig entirely to NodePrep meant the file did not exist when the cached containerd logic ran, failing CSE with 'Cannot find path C:\k\kubeclusterconfig.json' on both cached and non-cached Windows nodes (a new-CSE / old-VHD-scripts skew). Write kubeclusterconfig.json in BasePrep again so the cached scripts find it, while still rewriting it from live CustomData in NodePrep so PIS/VHD-cached nodes (which skip BasePrep) refresh the cluster-specific values. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
ff17e16 to
46181f2
Compare
timmy-wright
approved these changes
Jul 1, 2026
Member
Author
|
The E2E tests that cover windows and windows VHD caching passed. |
r2k1
approved these changes
Jul 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Work item: AB#38630884
Summary
Fixes AB#38630884: PIS / VHD-caching Windows nodes fail to register with
cannot create certificate signing request: Unauthorized.Root cause
On PIS/VHD-cached clusters the Windows VHD is baked from a temporary VMSS with TLS bootstrapping enabled. During the bake,
BasePrepwritesc:\k\bootstrap-configwith the bake-time TLS bootstrap token and drops aC:\AzureData\base_prep.completemarker — both captured into the cached VHD.On the real node the CSE skips
BasePrepbecause that marker exists, sobootstrap-configkeeps the stale baked token instead of the fresh$global:TLSBootstrapTokendelivered via CustomData. The apiserver then rejects the kubelet's first CSR asUnauthorized.The bootstrap-config write was simply left in
BasePrepwhen the script was split intoBasePrep/NodePrep(commit63b9d9b9, #6601).Fix
Move
Write-BootstrapKubeConfigfromBasePrepintoNodePrep, beforeInstall-KubernetesServices. kubelet (installed/started byInstall-KubernetesServices) is the only consumer ofbootstrap-config, so writing it inNodePrepguarantees the live token is used on every real provision.bootstrap-configfile is baked. The correctedNodePrepwrite takes effect on new nodes from existing cached VHDs.BasePrepnever starts kubelet nor connects to the API server, so nothing there consumes the token-basedbootstrap-config. Calico inBasePrepuses the separate cert-basedconfigfromWrite-KubeConfig, which is unchanged.Validation
go test ./pkg/agent/...passes;make generate-testdataproduced no snapshot drift.c:\k\bootstrap-configtoken ==$global:TLSBootstrapTokenand the node reaches Ready.Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com