{"record":{"id":"c0381afe90291de2","repo":"hyperledger/fabric","slug":"duplicate-consenter-server-cert-s-client-cert","errorCode":null,"errorMessage":"duplicate consenter: server cert: %s, client cert: %s","messagePattern":"duplicate consenter: server cert: (.+?), client cert: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/consensus/etcdraft/util.go","lineNumber":78,"sourceCode":"func MetadataHasDuplication(md *etcdraft.ConfigMetadata) error {\n\tif md == nil {\n\t\treturn errors.New(\"nil metadata\")\n\t}\n\n\tfor _, consenter := range md.GetConsenters() {\n\t\tif consenter == nil {\n\t\t\treturn errors.New(\"nil consenter in metadata\")\n\t\t}\n\t}\n\n\tseen := make(map[string]struct{})\n\tfor _, consenter := range md.GetConsenters() {\n\t\tserverKey := string(consenter.GetServerTlsCert())\n\t\tclientKey := string(consenter.GetClientTlsCert())\n\t\t_, duplicateServerCert := seen[serverKey]\n\t\t_, duplicateClientCert := seen[clientKey]\n\t\tif duplicateServerCert || duplicateClientCert {\n\t\t\treturn errors.Errorf(\"duplicate consenter: server cert: %s, client cert: %s\", serverKey, clientKey)\n\t\t}\n\n\t\tseen[serverKey] = struct{}{}\n\t\tseen[clientKey] = struct{}{}\n\t}\n\treturn nil\n}\n\n// MetadataFromConfigValue reads and translates configuration updates from config value into raft metadata\n// In case consensus type is changed to BFT the raft metadata will be nil\nfunc MetadataFromConfigValue(configValue *common.ConfigValue) (*etcdraft.ConfigMetadata, *orderer.ConsensusType, error) {\n\tconsensusTypeValue := &orderer.ConsensusType{}\n\tif err := proto.Unmarshal(configValue.GetValue(), consensusTypeValue); err != nil {\n\t\treturn nil, nil, errors.Wrap(err, \"failed to unmarshal consensusType config update\")\n\t}\n\n\tif consensusTypeValue.GetType() != \"etcdraft\" {\n\t\treturn nil, consensusTypeValue, nil","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/consensus/etcdraft/util.go#L60-L96","documentation":"MetadataHasDuplication tracks every server and client TLS certificate seen across consenters; if any certificate (server or client, byte-identical) appears for two consenters, the metadata is rejected. Duplicate certs would map two raft nodes to the same TLS identity, breaking leader election and messaging security.","triggerScenarios":"A channel config update adds or modifies a consenter reusing the server or client TLS cert PEM bytes of an existing consenter (or reusing the same cert within one consenter's server/client fields across nodes).","commonSituations":"Operators copying an existing orderer's TLS certs when onboarding a new node instead of issuing fresh ones; swapping certs for a replacement node but reusing the old node's certs; cert rotation scripts applying one cert to multiple orderers.","solutions":["Issue unique server and client TLS certificates for each consenter and regenerate the config update","Search the metadata for the duplicated cert (the error prints the certs) and replace the offending entry","Re-run VerifyConfigMetadata before submitting the corrected update"],"exampleFix":"// before\nconsenter2.ServerTlsCert = consenter1.ServerTlsCert // duplicate\n// after\nconsenter2.ServerTlsCert = newServerCertFromTLS2\nconsenter2.ClientTlsCert = newClientCertFromTLS2","handlingStrategy":"validation","validationCode":"func certsUnique(md *etcdraft.ConfigMetadata) error {\n\tseen := map[string]string{} // cert -> consenter\n\tfor i, c := range md.GetConsenters() {\n\t\tfor _, cert := range [][]byte{c.GetServerTlsCert(), c.GetClientTlsCert()} {\n\t\t\tkey := string(cert)\n\t\t\tif owner, dup := seen[key]; dup {\n\t\t\t\treturn fmt.Errorf(\"cert of consenter %d already used by %s\", i, owner)\n\t\t\t}\n\t\t\tseen[key] = fmt.Sprintf(\"consenter-%d\", i)\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Issue unique TLS certs per orderer node — never copy between nodes","Track cert assignments in an inventory when rotating certificates","Run MetadataHasDuplication locally on the proposed metadata before submitting","When replacing a node, revoke and replace the old node's certs rather than reusing them"],"tags":["raft","config","tls","duplicate","consenter"],"backgroundTag":"duplicate-consenter-cert","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}