{"record":{"id":"cd14f0102c9af613","repo":"kubernetes/kops","slug":"reading-intermediate-certificate-from-s-w","errorCode":null,"errorMessage":"reading intermediate certificate from %s: %w","messagePattern":"reading intermediate certificate from (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/azure/attest.go","lineNumber":444,"sourceCode":"}\n\n// fetchCertificate fetches and parses a DER-encoded certificate from the given URL.\nfunc fetchCertificate(client *http.Client, url string) (*x509.Certificate, error) {\n\tresp, err := client.Get(url)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"fetching intermediate certificate from %s: %w\", url, err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"fetching intermediate certificate from %s: status %d\", url, resp.StatusCode)\n\t}\n\n\t// Cap the body read to reject pathologically large responses. Read one extra byte so we can\n\t// distinguish \"at the limit\" from \"exceeded limit\".\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, intermediateCertMaxResponseBytes+1))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"reading intermediate certificate from %s: %w\", url, err)\n\t}\n\tif len(body) > intermediateCertMaxResponseBytes {\n\t\treturn nil, fmt.Errorf(\"intermediate certificate from %s exceeds %d bytes\", url, intermediateCertMaxResponseBytes)\n\t}\n\n\tcert, err := x509.ParseCertificate(body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parsing intermediate certificate from %s: %w\", url, err)\n\t}\n\treturn cert, nil\n}\n\n// validateFetchedIntermediateForSigner checks that a fetched intermediate is actually the issuer\n// referenced by the signer certificate before it is used or cached. This is a structural check\n// only; the cryptographic signature is verified later by verifySignerCertChain.\nfunc validateFetchedIntermediateForSigner(signer *x509.Certificate, cert *x509.Certificate) error {\n\tif signer == nil {\n\t\treturn fmt.Errorf(\"signer certificate is required\")","sourceCodeStart":426,"sourceCodeEnd":462,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/azure/attest.go#L426-L462","documentation":"After a successful HTTP 200 response, fetchCertificate reads the body with io.ReadAll wrapped in a LimitReader sized to intermediateCertMaxResponseBytes plus one byte. If the read itself fails mid-stream (connection reset, truncated response, timeout), this error wraps the underlying I/O error together with the URL.","triggerScenarios":"The response body read aborts before EOF while downloading an intermediate certificate — server closed the connection early, TLS truncation, or an intermediate network device dropped the stream.","commonSituations":"Flaky proxies/LBs closing keep-alive connections; very constrained networks with aggressive idle timeouts; CA endpoint under load aborting the transfer.","solutions":["Retry the fetch; transient truncation usually succeeds on a second attempt","Bypass or fix the proxy/LB that is terminating the connection early","Increase HTTP client timeouts so slow responses are not cut off mid-read"],"exampleFix":"// before\nclient := &http.Client{} // default: short/no body-read tolerance, may abort mid-stream\n// after\nclient := &http.Client{Timeout: 30 * time.Second}\n// plus retry on io.ErrUnexpectedEOF before failing the chain build","handlingStrategy":"retry","validationCode":"// Use a client with sane timeouts so reads are not cut off silently\nclient := &http.Client{Timeout: 30 * time.Second}","typeGuard":null,"tryCatchPattern":"_, err := fetchCertificate(client, url)\nif err != nil && (errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, context.DeadlineExceeded)) {\n    cert, err = fetchCertificate(client, url) // single retry on truncated reads\n}","preventionTips":["Set explicit HTTP client timeouts","Avoid proxies that close keep-alive connections mid-transfer","Retry idempotent GETs once or twice on I/O errors"],"tags":["network","azure","tls","aia-fetch","io-error"],"backgroundTag":"response-body-read-failed","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}