{"record":{"id":"50039f2b363cd791","repo":"slackhq/nebula","slug":"s-w","errorCode":null,"errorMessage":"%s: %w","messagePattern":"%s: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cert/ca_pool.go","lineNumber":103,"sourceCode":"// Parsed certificates will be verified and must be a CA\nfunc (ncp *CAPool) AddCAFromPEM(pemBytes []byte) ([]byte, error) {\n\tc, pemBytes, err := UnmarshalCertificateFromPEM(pemBytes)\n\tif err != nil {\n\t\treturn pemBytes, err\n\t}\n\n\terr = ncp.AddCA(c)\n\tif err != nil {\n\t\treturn pemBytes, err\n\t}\n\n\treturn pemBytes, nil\n}\n\n// AddCA verifies a Nebula CA certificate and adds it to the pool.\nfunc (ncp *CAPool) AddCA(c Certificate) error {\n\tif !c.IsCA() {\n\t\treturn fmt.Errorf(\"%s: %w\", c.Name(), ErrNotCA)\n\t}\n\n\tif !c.CheckSignature(c.PublicKey()) {\n\t\treturn fmt.Errorf(\"%s: %w\", c.Name(), ErrNotSelfSigned)\n\t}\n\n\tsum, err := c.Fingerprint()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not calculate fingerprint for provided CA; error: %w; %s\", err, c.Name())\n\t}\n\n\tcc := &CachedCertificate{\n\t\tCertificate:    c,\n\t\tFingerprint:    sum,\n\t\tInvertedGroups: make(map[string]struct{}),\n\t}\n\n\tfor _, g := range c.Groups() {","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/cert/ca_pool.go#L85-L121","documentation":"CAPool.AddCA rejects any certificate whose IsCA() flag is false, wrapping ErrNotCA with the certificate's name. Only CA certificates may be added to the CA pool; leaf/host certificates are signed by CAs and must not be trusted as signers.","triggerScenarios":"Calling AddCA (directly or via AddCAFromPEM / NewCAPoolFromPEMReader) with a Certificate that was issued as a host/client certificate rather than a CA certificate.","commonSituations":"Passing a node cert (e.g. from nebula.crt) where the CA cert (ca.crt) is expected, mixing up the two files when building the CA pool from PEM files.","solutions":["Pass the CA certificate (ca.crt content) instead of the host certificate (host.crt)","Verify the certificate was generated with the CA role (IsCA true) via nebula-cert","Check file ordering when concatenating PEMs so only CA certs end up in the pool"],"exampleFix":"// before\npool.AddCA(hostCert) // ErrNotCA\n// after\npool.AddCA(caCert)","handlingStrategy":"validation","validationCode":"func isCACert(c cert.Certificate) bool {\n    return c != nil && c.IsCA()\n}\n// only call pool.AddCA when isCACert(c) is true","typeGuard":"func isCA(c cert.Certificate) bool {\n    return c != nil && c.IsCA()\n}","tryCatchPattern":"if err := pool.AddCA(c); err != nil {\n    if errors.Is(err, cert.ErrNotCA) {\n        log.Fatalf(\"%s is not a CA certificate; pass ca.crt instead of host cert\", c.Name())\n    }\n    return err\n}","preventionTips":["Keep ca.crt and host.crt files clearly named and separated","Use errors.Is(err, cert.ErrNotCA) to distinguish this from other AddCA failures","Only feed NewCAPoolFromPEMReader PEM streams containing CA certs"],"tags":["certificate","pki","ca-pool"],"backgroundTag":"certificate-not-a-ca","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"}