slackhq/nebula · error

can not sign a CA certificate with another

Error message

can not sign a CA certificate with another

What it means

SignWith refuses to sign a CA certificate (t.IsCA == true) when an explicit signer certificate is supplied. The library forbids CA-to-CA signing: an existing CA must never be signed by another CA, since CAs are self-signed roots. This is a deliberate security invariant of nebula's PKI.

Source

Thrown at cert/sign.go:84

			hashed := sha256.Sum256(certBytes)
			return ecdsa.SignASN1(rand.Reader, pk, hashed[:])
		}
		return t.SignWith(signer, curve, sp)
	default:
		return nil, fmt.Errorf("invalid curve: %s", t.Curve)
	}
}

// SignWith does the same thing as sign, but uses the function in `sp` to calculate the signature.
// You should only use SignWith if you do not have direct access to your private key.
func (t *TBSCertificate) SignWith(signer Certificate, curve Curve, sp SignerLambda) (Certificate, error) {
	if curve != t.Curve {
		return nil, fmt.Errorf("curve in cert and private key supplied don't match")
	}

	if signer != nil {
		if t.IsCA {
			return nil, fmt.Errorf("can not sign a CA certificate with another")
		}

		err := checkCAConstraints(signer, t.NotBefore, t.NotAfter, t.Groups, t.Networks, t.UnsafeNetworks)
		if err != nil {
			return nil, err
		}

		issuer, err := signer.Fingerprint()
		if err != nil {
			return nil, fmt.Errorf("error computing issuer: %v", err)
		}
		t.issuer = issuer
	} else {
		if !t.IsCA {
			return nil, fmt.Errorf("self signed certificates must have IsCA set to true")
		}
	}

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Set t.IsCA = false when the certificate is meant to be signed by an existing CA.
  2. If you truly want a new CA, pass signer = nil so it is self-signed (IsCA must then be true).
  3. Check the CLI flags: do not pass both -ca and a signing key/cert combination that implies CA signing.

Example fix

// before
cert := cert.NebulaCertificate{Details: cert.NebulaCertificateDetails{IsCA: true}}
signed, err := cert.Sign(caCert, caKey, cert)

// after
cert := cert.NebulaCertificate{Details: cert.NebulaCertificateDetails{IsCA: false}}
signed, err := cert.Sign(caCert, caKey, cert)
Defensive patterns

Strategy: validation

Validate before calling

if t.Details.IsCA && signer != nil {
    return fmt.Errorf("refusing to sign: IsCA is true and a signer was provided")
}

Try / catch

signed, err := cert.Sign(signer, key, t)
if err != nil {
    if strings.Contains(err.Error(), "can not sign a CA certificate") {
        // fix IsCA or signer selection
    }
    return err
}

Prevention

When it happens

Trigger: Calling SignWith (directly or via cert.Sign or cmd/nebula-cert signCert/ca) with a t whose IsCA field is true while passing a non-nil signer certificate.

Common situations: Users trying to chain CAs (intermediate CA signed by a root CA), reusing the CA-signing flag (-ca) with an existing signer, or copy-pasting CA-generation code but accidentally still passing a signer.

Understand the failure class

Related errors


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