kubernetes/kops · error

challenge not set

Error message

challenge not set

What it means

DoCallbackChallenge validates that the nodeup BootstrapRequest carries a fully populated Challenge before running the callback challenge flow. If bootstrapRequest.Challenge is nil, there is nothing to respond with, so the client aborts immediately with 'challenge not set'. The server did not provide (or the nodeup code did not copy) the challenge into the request.

Source

Thrown at pkg/bootstrap/challenge_client.go:71

	}, c.keystore)
	if err != nil {
		return nil, fmt.Errorf("error creating certificate: %w", err)
	}

	// TODO: Caching and rotation
	clientCertificate := &tls.Certificate{
		PrivateKey:  privateKey.Key,
		Certificate: [][]byte{certificate.Certificate.Raw},
		Leaf:        certificate.Certificate,
	}
	return clientCertificate, nil
}

func (c *ChallengeClient) DoCallbackChallenge(ctx context.Context, clusterName string, targetEndpoint string, bootstrapRequest *nodeup.BootstrapRequest) error {
	challenge := bootstrapRequest.Challenge

	if challenge == nil {
		return fmt.Errorf("challenge not set")
	}
	if challenge.ChallengeID == "" {
		return fmt.Errorf("challenge.id not set")
	}
	if len(challenge.ChallengeSecret) == 0 {
		return fmt.Errorf("challenge.secret not set")
	}
	if challenge.Endpoint == "" {
		return fmt.Errorf("challenge.endpoint not set")
	}
	if len(challenge.ServerCA) == 0 {
		return fmt.Errorf("challenge.ca not set")
	}

	clientCertificate, err := c.getClientCertificate(ctx, clusterName)
	if err != nil {
		return err
	}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Ensure nodeup and the kops server are from the same kops version (challenge support must exist on both sides)
  2. Check the server actually issues challenges (NewChallengeServer wired in) and includes Challenge in the bootstrap response
  3. If the node constructs BootstrapRequest manually, populate the Challenge field from the server's response
  4. Disable/enable challenge behavior consistently on client and server configurations

Example fix

// before
req := &nodeup.BootstrapRequest{ClusterName: cluster}
client.DoCallbackChallenge(ctx, cluster, endpoint, req) // Challenge nil
// after
challenge, err := fetchChallengeFromServer(ctx)
if err != nil { return err }
req := &nodeup.BootstrapRequest{ClusterName: cluster, Challenge: challenge}
client.DoCallbackChallenge(ctx, cluster, endpoint, req)
Defensive patterns

Strategy: validation

Validate before calling

if req.Challenge == nil {
  return errors.New("server did not provide a bootstrap challenge")
}
if req.Challenge.ChallengeID == "" || len(req.Challenge.ChallengeSecret) == 0 || req.Challenge.Endpoint == "" {
  return errors.New("bootstrap challenge incomplete")
}

Type guard

func challengeComplete(c *nodeup.Challenge) bool {
  return c != nil && c.ChallengeID != "" && len(c.ChallengeSecret) > 0 && c.Endpoint != ""
}

Try / catch

if err := challengeClient.DoCallbackChallenge(ctx, cluster, endpoint, req); err != nil {
  return fmt.Errorf("callback challenge failed: %w", err)
}

Prevention

When it happens

Trigger: bootstrap calls DoCallbackChallenge with a BootstrapRequest whose Challenge field is nil — e.g. the server response omitted the challenge, or the nodeup version predates challenge support and never populates it.

Common situations: Version skew between nodeup binary and kops controller (challenge feature added later); server not running with challenge enforcement so no challenge is issued; custom bootstrap flows constructing BootstrapRequest manually without Challenge.

Related errors


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