k3s-io/k3s · error
header node name does not match auth node name
Error message
header node name does not match auth node name
What it means
Node-password authentication accepts either a shared token or kubelet client-cert node identity (system:node:<name>). When request authentication resolved the caller as a node identity and that identity's node name differs from the node name header sent with the request, the request is rejected with HTTP 400 as a probable spoof or misconfiguration.
Source
Thrown at pkg/nodepassword/validate.go:58
// Node password authentication is used when requesting kubelet certificates, and verifies that the
// credentials are valid for the requested node name, and that the node password is valid if it exists.
// These checks prevent a user with access to one agent from requesting kubelet certificates that
// could be used to impersonate another cluster member.
func GetNodeAuthValidator(ctx context.Context, control *config.Control) NodeAuthValidator {
deferredNodes := map[string]bool{}
var mu sync.Mutex
return func(req *http.Request) (string, int, error) {
node, err := getNodeInfo(req)
if err != nil {
return "", http.StatusBadRequest, err
}
// node identity auth uses an existing kubelet client cert instead of auth token.
// If used, validate that the node identity matches the requested node name.
nodeName, isNodeAuth := identifier.NodeIdentity(node.User)
if isNodeAuth && nodeName != node.Name {
return "", http.StatusBadRequest, errors.New("header node name does not match auth node name")
}
if controller == nil {
if node.Name == os.Getenv("NODE_NAME") {
// If we're verifying our own password, verify it locally and ensure a secret later.
return verifyLocalPassword(ctx, control, &mu, deferredNodes, node)
} else if control.DisableAPIServer && !isNodeAuth {
// If we're running on an etcd-only node, and the request didn't use Node Identity auth,
// defer node password verification until an apiserver joins the cluster.
return verifyRemotePassword(ctx, control, &mu, deferredNodes, node)
}
// Otherwise, reject the request until the controller is ready.
return "", http.StatusServiceUnavailable, util.ErrCoreNotReady
}
// verify that the node exists, if using Node Identity auth
if err := controller.verifyNode(ctx, node); err != nil {
return "", http.StatusUnauthorized, errView on GitHub (pinned to 6ba341e396)
Solutions
- On the affected node, remove the duplicated credentials/identity state (e.g. wipe /var/lib/rancher/k3s and rejoin) so it gets its own client cert.
- Ensure the node name (hostname / --node-name) matches the name in the client cert CN system:node:<name> and the header value.
- Never copy node data directories between hosts; treat node certs as node-specific.
Example fix
# before: cloned node keeps node-a's certs but registers as node-b # after: wipe and rejoin with its own identity rm -rf /var/lib/rancher/k3s/agent k3s agent --server https://10.0.0.10:6443 --token ...
Defensive patterns
Strategy: validation
Validate before calling
if nodeName, ok := identifier.NodeIdentity(user); ok && nodeName != strings.ToLower(reqNodeName) {
return http.StatusBadRequest, fmt.Errorf("node identity %q != header %q", nodeName, reqNodeName)
} Prevention
- Never clone node data directories; node certs identify the node.
- Rejoin (wipe agent state) after renaming a node so cert CN and hostname agree.
When it happens
Trigger: A kubelet credential minted for node A authenticates a request whose <program>-Node-Name header says node B; typically one node reusing another node's client cert or client-cert/data-dir copied between machines.
Common situations: Cloning VMs/images including /var/lib/rancher/k3s; a node renamed after join; an attempt to proxy requests for other nodes using a single node's identity.
Related errors
- invalid username/password combination
- password hash not found in node secret
- node name not set
- node password not set
- insufficient PSK bytes
AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15).
Data as JSON: /api/errors/db4a7febc7eb4fd0.
Report an issue: GitHub.