slackhq/nebula · error

certificate expires after signing certificate

Error message

certificate expires after signing certificate

What it means

checkCAConstraints enforces that a signed certificate's validity window fits inside the signer's. This error means the certificate's notAfter is later than the signing CA's notAfter, i.e. the cert would outlive its issuer. It is returned both when checking existing certs (CheckCAConstraints) and when signing new ones (SignWith).

Source

Thrown at cert/ca_pool.go:290

	i := 0
	for k := range ncp.CAs {
		fp[i] = k
		i++
	}

	return fp
}

// CheckCAConstraints returns an error if the sub certificate violates constraints present in the signer certificate.
func CheckCAConstraints(signer Certificate, sub Certificate) error {
	return checkCAConstraints(signer, sub.NotBefore(), sub.NotAfter(), sub.Groups(), sub.Networks(), sub.UnsafeNetworks())
}

// checkCAConstraints is a very generic function allowing both Certificates and TBSCertificates to be tested.
func checkCAConstraints(signer Certificate, notBefore, notAfter time.Time, groups []string, networks, unsafeNetworks []netip.Prefix) error {
	// Make sure this cert isn't valid after the root
	if notAfter.After(signer.NotAfter()) {
		return fmt.Errorf("certificate expires after signing certificate")
	}

	// Make sure this cert wasn't valid before the root
	if notBefore.Before(signer.NotBefore()) {
		return fmt.Errorf("certificate is valid before the signing certificate")
	}

	// If the signer has a limited set of groups make sure the cert only contains a subset
	signerGroups := signer.Groups()
	if len(signerGroups) > 0 {
		for _, g := range groups {
			if !slices.Contains(signerGroups, g) {
				return fmt.Errorf("certificate contained a group not present on the signing ca: %s", g)
			}
		}
	}

	// If the signer has a limited set of ip ranges to issue from make sure the cert only contains a subset

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Shorten the certificate's notAfter to be <= the signing CA's notAfter
  2. Renew/extend the CA certificate's validity, then re-sign
  3. Use SignWith with an expiry computed as min(desired, signer.NotAfter())

Example fix

// before
nc, err := ca.SignWith(pubKey, curve, opts) // opts.NotAfter after CA expiry
// after
expiry := desiredExpiry
if ca.NotAfter().Before(expiry) {
    expiry = ca.NotAfter()
}
opts.NotAfter = expiry
nc, err := ca.SignWith(pubKey, curve, opts)
Defensive patterns

Strategy: validation

Validate before calling

if sub.NotAfter().After(signer.NotAfter()) {
    return fmt.Errorf("cert would outlive its CA; renew CA or shorten cert validity")
}
err := pool.CheckCAConstraints(signer, sub)

Prevention

When it happens

Trigger: Calling CAPool.CheckCAConstraints(signer, sub) where sub.NotAfter() is after signer.NotAfter(); or SignWith with an expiry parameter beyond the signing CA's expiration.

Common situations: Issuing a 10-year host certificate signed by a 1-year CA; config generators defaulting cert lifetime independently of CA lifetime; renewing the CA with a shorter validity than long-lived leaf certs.

Understand the failure class

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/8ab83d51cba1b9a3. Report an issue: GitHub.