kubernetes/kops · error

Forbidden: cannot register node name

Error message

Forbidden: cannot register node name '%s' with client cert '%s'

What it means

handleCreateDiscoveryEndpoint enforces that a supplied metadata.name matches the ClientID extracted from the client's mTLS certificate. If the body sets a name different from the certificate identity, the server refuses the registration with 403. Sending no name (empty) is allowed and gets the cert identity.

Solutions

  1. Omit metadata.name in the POST body and let the server use the cert's ClientID.
  2. Set metadata.name exactly equal to the CN/ClientID of the presented client certificate.
  3. Issue/use a certificate whose CN matches the desired endpoint name.
  4. Check for stale hardcoded names in your manifests/templates.

Example fix

// before
body := []byte(`{"metadata":{"name":"wrong-node"}}`)
// after: name matches cert CN
body := []byte(`{"metadata":{"name":"ip-10-0-0-5"}}`)
Defensive patterns

Strategy: validation

Validate before calling

if body.Name != "" && body.Name != certClientID {
    return fmt.Errorf("body name %q does not match cert ClientID %q", body.Name, certClientID)
}

Prevention

When it happens

Trigger: POST create where body's metadata.name is non-empty and != the cert CN/ClientID, e.g. a node trying to register under another node's name or a copied manifest retaining an old name.

Common situations: Reusing a manifest from another node without clearing metadata.name; renaming nodes while reusing certs; automation that templates the wrong name; attempting to impersonate another endpoint (correctly blocked).

Understand the failure class

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/5fd03b6bc1a2d757. Report an issue: GitHub.

Appendix: source

Thrown at discovery/pkg/discovery/server.go:162

		}
	}

	s.writeJSON(w, http.StatusOK, resp)
}

func (s *Server) handleCreateDiscoveryEndpoint(w http.ResponseWriter, r *http.Request, userInfo *UserInfo) {
	universeID := r.PathValue("universe")
	ns := r.PathValue("namespace")

	var input api.DiscoveryEndpoint
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		http.Error(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	// Validation: ensure the name matches the clientID from the cert
	if input.ObjectMeta.Name != "" && input.ObjectMeta.Name != userInfo.ClientID {
		http.Error(w, fmt.Sprintf("Forbidden: cannot register node name '%s' with client cert '%s'", input.ObjectMeta.Name, userInfo.ClientID), http.StatusForbidden)
		return
	}

	// Validation: ensure the namespace in body matches the URL
	if input.ObjectMeta.Namespace != ns {
		http.Error(w, "Forbidden: namespace does not match", http.StatusForbidden)
		return
	}

	if err := s.Store.UpsertDiscoveryEndpoint(r.Context(), universeID, &input); err != nil {
		http.Error(w, fmt.Sprintf("Error creating endpoint: %v", err), http.StatusInternalServerError)
		return
	}

	// Return the created object
	s.writeJSON(w, http.StatusCreated, input)
}

View on GitHub (pinned to 4c8573c808)