{"record":{"id":"ea397a9f57823ed4","repo":"slackhq/nebula","slug":"verify-cert-w","errorCode":null,"errorMessage":"verify cert: %w","messagePattern":"verify cert: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"handshake/machine.go","lineNumber":374,"sourceCode":"\t\treturn fmt.Errorf(\"recombine cert: %w\", err)\n\t}\n\n\tif !bytes.Equal(rc.PublicKey(), m.hs.PeerStatic()) {\n\t\tm.failed = true\n\t\treturn ErrPublicKeyMismatch\n\t}\n\n\t// Version negotiation, if the peer sent a different version and we have it, switch\n\tif rc.Version() != m.myVersion {\n\t\tif m.getCred(rc.Version()) != nil {\n\t\t\tm.myVersion = rc.Version()\n\t\t}\n\t}\n\n\tverified, err := m.verifier(rc)\n\tif err != nil {\n\t\tm.failed = true\n\t\treturn fmt.Errorf(\"verify cert: %w\", err)\n\t}\n\n\tm.result.RemoteCert = verified\n\tm.remoteCertSet = true\n\treturn nil\n}\n\nfunc (m *Machine) marshalOutgoing(flags msgFlags) ([]byte, error) {\n\tif !flags.expectsPayload && !flags.expectsCert {\n\t\treturn nil, nil\n\t}\n\n\tvar p Payload\n\tif flags.expectsPayload {\n\t\tif !m.indexAllocated {\n\t\t\tindex, err := m.allocIndex()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"%w: %w\", ErrIndexAllocation, err)","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/handshake/machine.go#L356-L392","documentation":"validateCert wraps errors from m.verifier(rc) as 'verify cert: %w'. After recombining the peer certificate, the node verifies it against its CA/network constraints (CA expiry, certificate validity window, blocked list, name/network checks). Any error from that verification is surfaced here and marks the handshake failed.","triggerScenarios":"ProcessPacket -> processPayload -> validateCert where the verifier rejects the recombined certificate: expired CA or cert, cert outside its validity window, certificate revoked/blocked, subnets not allowed by the CA, or internal verifier errors.","commonSituations":"Certificate or CA passed its expiry (common after forgetting to rotate nebula certs), clock skew between hosts making the validity window invalid, certs signed by a CA no longer in the trusted set, subnet claims exceeding CA limits after re-addressing.","solutions":["Check certificate and CA expiry (`nebula-cert print -path cert.crt`) and re-issue/re-sign expired certs","Sync clocks (NTP) on both hosts to fix validity-window rejections","Ensure the CA bundle in the config includes the CA that signed the peer's cert","Update the CA to allow the claimed subnets/groups, or reissue the peer cert with compliant network claims"],"exampleFix":"// before: expired cert\nnebula-cert print -path host.crt  // Expired: 2024-01-01\n// after: re-sign with longer validity\nnebula-cert sign -ca ca.crt -key ca.key -name host -ip 10.0.0.2/24 -duration 8760h","handlingStrategy":"try-catch","validationCode":"// proactively monitor expiry on all nodes\nif time.Now().After(cert.ValidAfter) || time.Now().After(cert.ValidUntil) {\n\treturn fmt.Errorf(\"certificate or CA out of validity window\")\n}","typeGuard":null,"tryCatchPattern":"_, _, err := machine.ProcessPacket(pkt)\nif err != nil && strings.Contains(err.Error(), \"verify cert:\") {\n\t// check cert/CA expiry, clock sync, and trusted CA bundle before retry\n}","preventionTips":["Run scheduled checks for cert/CA expiry and alert well before the deadline","Sync clocks with NTP on every host","Include all required CAs in the nebula config's pki section","Keep cert durations generous but documented; reissue before expiry"],"tags":["handshake","certificate","verification","expired-cert"],"backgroundTag":"certificate-verification-failed","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"}