kubernetes/kops · error

host %v did not have public-key

Error message

host %v did not have public-key

What it means

The Host object exists, but its spec.publicKey field is empty, so the verifier has no PEM public key against which to verify the node's token signature. It fails closed rather than allowing an unsigned/unverifiable bootstrap.

Source

Thrown at pkg/bootstrap/pkibootstrap/pkiverifier/verifier.go:149

func (v *verifier) getSigningKey(ctx context.Context, tokenData *pkibootstrap.AuthTokenData) (*bootstrap.VerifyResult, crypto.PublicKey, error) {
	nodeName := tokenData.Instance
	id := types.NamespacedName{
		Namespace: "kops-system",
		Name:      nodeName,
	}
	var host kops.Host
	if err := v.client.Get(ctx, id, &host); err != nil {
		if apierrors.IsNotFound(err) {
			return nil, nil, fmt.Errorf("host not found for %v", id)
		}
		return nil, nil, fmt.Errorf("error getting host %v: %w", id, err)
	}

	// TODO: Check instance-group matches request (does it matter?)

	if host.Spec.PublicKey == "" {
		return nil, nil, fmt.Errorf("host %v did not have public-key", id)
	}
	instanceGroup := host.Spec.InstanceGroup
	if instanceGroup == "" {
		return nil, nil, fmt.Errorf("host %v did not have spec.instanceGroup", id)
	}
	pubKey, err := pki.ParsePEMPublicKey([]byte(host.Spec.PublicKey))
	if err != nil {
		return nil, nil, fmt.Errorf("failed to parse public key: %w", err)
	}

	var sans []string

	result := &bootstrap.VerifyResult{
		NodeName:          nodeName,
		InstanceGroupName: instanceGroup,
		CertificateNames:  sans,
	}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Populate spec.publicKey on the Host with the node's PEM-encoded public key (e.g. openssl ec -in ec-priv-key.pem -pubout) and re-run bootstrap
  2. Check the process/controller responsible for setting spec.publicKey and fix why it did not write the key
  3. Verify the PEM stored is a supported key type (ECDSA prime256v1; Go does not support secp256k1)
  4. Recreate the Host object from the correct template if it was created incomplete

Example fix

// before
// spec:
//   instanceGroup: nodes

// after
// spec:
//   instanceGroup: nodes
//   publicKey: |
//     -----BEGIN PUBLIC KEY-----
//     MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
//     -----END PUBLIC KEY-----
Defensive patterns

Strategy: validation

Validate before calling

var host kops.Host
if err := c.Get(ctx, types.NamespacedName{Namespace: "kops-system", Name: nodeName}, &host); err != nil {
    return err
}
if host.Spec.PublicKey == "" {
    return fmt.Errorf("Host %s must have spec.publicKey set to a PEM public key before bootstrap", nodeName)
}

Type guard

func hostHasPublicKey(h *kops.Host) bool {
    return h != nil && strings.TrimSpace(h.Spec.PublicKey) != ""
}

Try / catch

result, err := verifier.VerifyToken(ctx, req, token, body)
if err != nil {
    if strings.Contains(err.Error(), "did not have public-key") {
        return fmt.Errorf("populate Host spec.publicKey for %s and retry", nodeName)
    }
    return err
}

Prevention

When it happens

Trigger: VerifyToken -> getSigningKey fetches a Host whose spec.PublicKey == "" — the Host was created without a publicKey, a controller cleared it, or a manifest template omitted the field.

Common situations: Hand-written Host manifest missing spec.publicKey; automation (e.g. cloud integration) created the Host before writing the machine's public key; key provisioning step failed silently; a kops version change altered how/where the key is populated.

Related errors


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