cilium/cilium · error

failed to parse certificate: %w

Error message

failed to parse certificate: %w

What it means

During the TLS handshake the peer presented certificates in raw (DER) form which the handler parses with x509.ParseCertificate; one of the peer's certificates is malformed and cannot be parsed into an x509.Certificate. This happens inside the handshake verification callback, so the handshake is aborted.

Source

Thrown at pkg/auth/mutual_authhandler.go:133

	// set up TLS socket

	//nolint:gosec // InsecureSkipVerify is not insecure as we do the verification in VerifyPeerCertificate
	tlsConn := tls.Client(conn, &tls.Config{
		ServerName: m.cert.NumericIdentityToSNI(ar.remoteIdentity),
		GetClientCertificate: func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
			return clientCert, nil
		},
		MinVersion:         tls.VersionTLS13,
		InsecureSkipVerify: true, // not insecure as we do the verification in VerifyPeerCertificate
		VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
			// verifiedChains will be nil as we set InsecureSkipVerify to true

			chain := make([]*x509.Certificate, len(rawCerts))
			for i, rawCert := range rawCerts {
				cert, err := x509.ParseCertificate(rawCert)
				if err != nil {
					return fmt.Errorf("failed to parse certificate: %w", err)
				}
				chain[i] = cert
			}

			peerExpirationTime, err := m.verifyPeerCertificate(&ar.remoteIdentity, caBundle, [][]*x509.Certificate{chain})
			if peerExpirationTime != nil && peerExpirationTime.Before(*expirationTime) {
				expirationTime = peerExpirationTime // send down the lowest expiration time of the two certificates
			}
			return err
		},
		ClientCAs: caBundle,
		RootCAs:   caBundle,
	})
	defer tlsConn.Close()

	if err := tlsConn.Handshake(); err != nil {
		return nil, fmt.Errorf("failed to perform TLS handshake: %w", err)
	}

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Restart the remote node's Cilium agent so it re-issues/re-loads its certificate
  2. Force certificate re-issuance via the certificate provider (SPIRE re-attestation) on the peer
  3. Verify no proxy/TLS-terminating middlebox sits between the nodes on the auth port
  4. Capture the peer's certificate bytes (tls debug) and confirm they are valid DER; upgrade Cilium if a known parsing bug matches
Defensive patterns

Strategy: try-catch

Try / catch

if err := authManager.Authenticate(ctx, key); err != nil {
	if strings.Contains(err.Error(), "failed to parse certificate") {
		// peer certificate corrupt: trigger remote agent cert re-issuance, then retry
	}
}

Prevention

When it happens

Trigger: the VerifyPeerCertificate callback receives rawCerts from the TLS stack and x509.ParseCertificate(rawCert) errors — truncated or corrupted certificate bytes, or a peer sending a non-X.509/unexpected certificate format.

Common situations: remote agent serving a corrupt or truncated certificate after a failed rotation; a middlebox/proxy interfering with TLS; version mismatch where peers negotiate unexpected cert formats; memory corruption during cert storage.

Understand the failure class

Related errors


AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31). Data as JSON: /api/errors/e2ad5915988a78da. Report an issue: GitHub.