{"record":{"id":"27af0eabbd6ff063","repo":"slackhq/nebula","slug":"can-not-sign-a-ca-certificate-with-another","errorCode":null,"errorMessage":"can not sign a CA certificate with another","messagePattern":"can not sign a CA certificate with another","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cert/sign.go","lineNumber":84,"sourceCode":"\t\t\thashed := sha256.Sum256(certBytes)\n\t\t\treturn ecdsa.SignASN1(rand.Reader, pk, hashed[:])\n\t\t}\n\t\treturn t.SignWith(signer, curve, sp)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"invalid curve: %s\", t.Curve)\n\t}\n}\n\n// SignWith does the same thing as sign, but uses the function in `sp` to calculate the signature.\n// You should only use SignWith if you do not have direct access to your private key.\nfunc (t *TBSCertificate) SignWith(signer Certificate, curve Curve, sp SignerLambda) (Certificate, error) {\n\tif curve != t.Curve {\n\t\treturn nil, fmt.Errorf(\"curve in cert and private key supplied don't match\")\n\t}\n\n\tif signer != nil {\n\t\tif t.IsCA {\n\t\t\treturn nil, fmt.Errorf(\"can not sign a CA certificate with another\")\n\t\t}\n\n\t\terr := checkCAConstraints(signer, t.NotBefore, t.NotAfter, t.Groups, t.Networks, t.UnsafeNetworks)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tissuer, err := signer.Fingerprint()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error computing issuer: %v\", err)\n\t\t}\n\t\tt.issuer = issuer\n\t} else {\n\t\tif !t.IsCA {\n\t\t\treturn nil, fmt.Errorf(\"self signed certificates must have IsCA set to true\")\n\t\t}\n\t}\n","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/cert/sign.go#L66-L102","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set t.IsCA = false when the certificate is meant to be signed by an existing CA.","If you truly want a new CA, pass signer = nil so it is self-signed (IsCA must then be true).","Check the CLI flags: do not pass both -ca and a signing key/cert combination that implies CA signing."],"exampleFix":"// before\ncert := cert.NebulaCertificate{Details: cert.NebulaCertificateDetails{IsCA: true}}\nsigned, err := cert.Sign(caCert, caKey, cert)\n\n// after\ncert := cert.NebulaCertificate{Details: cert.NebulaCertificateDetails{IsCA: false}}\nsigned, err := cert.Sign(caCert, caKey, cert)","handlingStrategy":"validation","validationCode":"if t.Details.IsCA && signer != nil {\n    return fmt.Errorf(\"refusing to sign: IsCA is true and a signer was provided\")\n}","typeGuard":null,"tryCatchPattern":"signed, err := cert.Sign(signer, key, t)\nif err != nil {\n    if strings.Contains(err.Error(), \"can not sign a CA certificate\") {\n        // fix IsCA or signer selection\n    }\n    return err\n}","preventionTips":["Only set IsCA: true in code paths that self-sign (nil signer).","Centralize certificate construction in one helper that enforces IsCA/signer invariants.","Review CLI flag combinations that pass both a CA flag and a signer."],"tags":["pki","certificate","ca"],"backgroundTag":"ca-signing-not-permitted","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}