gravitational/teleport · error

override certificate should not be included in chain

Error message

override certificate should not be included in chain

What it means

validateCertificateOverride checks that the override certificate is not duplicated inside its own chain. The chain must contain only issuers above the override certificate; if chain[0] has the same Subject as the override certificate itself, the config is redundant/ambiguous and this error is thrown with the field hint 'chain[0]'.

Source

Thrown at lib/subca/parsed.go:290

		const maxChainLength = 10
		if len(co.GetChain()) > maxChainLength {
			return nil, "chain", fmt.Errorf(
				"certificate chain has too many entries (%d > %d)", len(co.GetChain()), maxChainLength)
		}

		chain = make([]*x509.Certificate, len(co.GetChain()))
		prev := cert
		for i, chainPEM := range co.GetChain() {
			chainCert, err := ParseCertificateOverrideCertificate(chainPEM)
			if err != nil {
				return nil, fmt.Sprintf("chain[%d]", i), err
			}
			chainSub := chainCert.Subject.String()

			// Certificate not in chain.
			if i == 0 && cert.Subject.String() == chainSub {
				return nil, fmt.Sprintf("chain[%d]", i),
					errors.New("override certificate should not be included in chain")
			}

			// Issuer/Subject relationship.
			if issuer := prev.Issuer.String(); issuer != chainSub {
				return nil, fmt.Sprintf("chain[%d]", i),
					fmt.Errorf("chain out of order, subject=%q (want %q)", chainSub, issuer)
			}

			// Verify signature.
			if err := prev.CheckSignatureFrom(chainCert); err != nil {
				return nil,
					fmt.Sprintf("chain[%d]", i),
					fmt.Errorf("chain signature check failed, previous certificate not signed by current: %w", err)
			}

			// Note: we purposefully avoid time-based chain validation at this layer,
			// as that could make an override that was once valid impossible to
			// bootstrap or update without destructive action.

View on GitHub (pinned to 1283425b60)

Solutions

  1. Remove the duplicate certificate from the beginning of the chain; the chain should start with the direct issuer of the override certificate.
  2. If the certificate is the root of what you have, drop the chain entirely and supply only the override certificate.
  3. Rebuild the chain bottom-up: chain[0] must issue the override cert, each following entry must issue the previous one.

Example fix

// before
chain[0] = override cert (same Subject) -> rejected
// after
chain[0] = intermediate that signed the override cert
chain[1] = root (optional)
Defensive patterns

Strategy: validation

Validate before calling

certSub := parseSubject(override.Cert)
if len(override.Chain) > 0 && parseSubject(override.Chain[0]) == certSub {
    return fmt.Errorf("override certificate duplicated in chain[0]")
}

Prevention

When it happens

Trigger: Calling ValidateAndParseCAOverride where co.Chain[0]'s Subject string equals the Subject of the parsed override certificate, i.e. the same cert was pasted into both fields.

Common situations: Copying an entire PEM file (override cert + intermediates) into both 'cert' and 'chain'; scripts that split PEM bundles off-by-one so the CA lands in both fields; hand-built chains that start with the CA itself instead of its issuer.

Understand the failure class

Related errors


AI-assisted analysis of gravitational/teleport@1283425b60 (2026-09-02). Data as JSON: /api/errors/88636d17bd36ad4b. Report an issue: GitHub.