kubernetes/kops · error

intermediate certificate from

Error message

intermediate certificate from %s exceeds %d bytes

What it means

To reject pathologically large or malicious responses, fetchCertificate caps the body at intermediateCertMaxResponseBytes. Because the LimitReader allows one extra byte, a body that fills the limit plus one means the response exceeded the cap, and this error is returned instead of attempting to parse an oversized payload.

Solutions

  1. Verify the URL returns a single DER-encoded certificate (curl the URL and inspect with openssl)
  2. Check for proxies/captive portals intercepting the request
  3. If legitimately larger certificates are needed, raise intermediateCertMaxResponseBytes in the code
Defensive patterns

Strategy: validation

Validate before calling

// Probe that the endpoint returns small, certificate-like content before fetching
resp, err := http.Get(url)
if err != nil { return err }
ct := resp.Header.Get("Content-Type")
cl := resp.ContentLength
resp.Body.Close()
if cl > 100000 || (ct != "" && strings.Contains(ct, "text/html")) {
    return fmt.Errorf("endpoint %s unlikely to serve a DER certificate", url)
}

Try / catch

_, err := fetchCertificate(client, url)
if err != nil && strings.Contains(err.Error(), "exceeds") {
    return nil, fmt.Errorf("AIA endpoint %s returned oversized payload; check for proxy/captive portal", url)
}

Prevention

When it happens

Trigger: An AIA URL returns a body larger than intermediateCertMaxResponseBytes — e.g. a misconfigured endpoint serving a bundle, an HTML error page with inline assets, or a compromised/hostile endpoint.

Common situations: Proxy or captive portal returning a large HTML page instead of the DER certificate; endpoint serving a PKCS#7 bundle instead of a single DER certificate; security probing of the AIA URL.

Understand the failure class

Related errors


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

Appendix: source

Thrown at upup/pkg/fi/cloudup/azure/attest.go:447

func fetchCertificate(client *http.Client, url string) (*x509.Certificate, error) {
	resp, err := client.Get(url)
	if err != nil {
		return nil, fmt.Errorf("fetching intermediate certificate from %s: %w", url, err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("fetching intermediate certificate from %s: status %d", url, resp.StatusCode)
	}

	// Cap the body read to reject pathologically large responses. Read one extra byte so we can
	// distinguish "at the limit" from "exceeded limit".
	body, err := io.ReadAll(io.LimitReader(resp.Body, intermediateCertMaxResponseBytes+1))
	if err != nil {
		return nil, fmt.Errorf("reading intermediate certificate from %s: %w", url, err)
	}
	if len(body) > intermediateCertMaxResponseBytes {
		return nil, fmt.Errorf("intermediate certificate from %s exceeds %d bytes", url, intermediateCertMaxResponseBytes)
	}

	cert, err := x509.ParseCertificate(body)
	if err != nil {
		return nil, fmt.Errorf("parsing intermediate certificate from %s: %w", url, err)
	}
	return cert, nil
}

// validateFetchedIntermediateForSigner checks that a fetched intermediate is actually the issuer
// referenced by the signer certificate before it is used or cached. This is a structural check
// only; the cryptographic signature is verified later by verifySignerCertChain.
func validateFetchedIntermediateForSigner(signer *x509.Certificate, cert *x509.Certificate) error {
	if signer == nil {
		return fmt.Errorf("signer certificate is required")
	}
	if cert == nil {
		return fmt.Errorf("fetched certificate is required")

View on GitHub (pinned to 4c8573c808)