Skip to content

Sidecar cannot reach orderers under a two-tier TLS PKI: config-block trust pools drop TlsIntermediateCerts #757

Description

@tinystone007

Environment

  • fabric-x-committer v1.0.4 (08a469d)
  • fabric-x-common v0.2.8 (via the committer's go.mod)
  • fabric-x-orderer v1.0.3, arma consensus, 4 orderer orgs
  • TLS PKI: one network TLS root CA → one intermediate CA per org → node leaf certificates

Symptom

With node TLS leaves signed by per-org intermediate CAs (instead of a root that signs
leaves directly), the sidecar cannot establish any orderer connection:

transport: authentication handshake failed: tls: failed to verify certificate:
x509: certificate signed by unknown authority

No blocks are delivered, and every client waiting on a transaction status times out
(failed to wait for transaction status event: context deadline exceeded).

The config block is not the problem: the org MSPs in it carry both tls_root_certs
and tls_intermediate_certs (standard MSP directories, assembled by configtxgen).

Root cause

Two call sites build client TLS trust pools from the config block using
GetTLSRootCerts() only, silently dropping the intermediates the config carries:

  1. fabric-x-common, common/channelconfig/load.go (LoadConfigBlockMaterial), consumed
    by the committer's utils/ordererdial.NewDialInfo for orderer connections:

    CACerts: org.MSP().GetTLSRootCerts(),
  2. fabric-x-committer, utils/serialization/transaction.go
    (ExtractAppTLSCAsFromEnvelope), for application-org client verification:

    certs = append(certs, org.MSP().GetTLSRootCerts()...)

Servers cannot compensate by presenting a chained certificate file: the envelope TLS
certificate hash is computed over the whole server certificate file, so server.crt
must stay leaf-only. With a leaf-only presentation and a roots-only pool, chain
building has no path to the trust anchor.

Single-tier deployments never see this because there are no intermediates to drop —
which is presumably why it survived: every leaf is signed by the root itself.

Fix

Append the intermediates at both sites:

// load.go
CACerts: append(org.MSP().GetTLSRootCerts(), org.MSP().GetTLSIntermediateCerts()...),

// transaction.go
certs = append(certs, org.MSP().GetTLSRootCerts()...)
certs = append(certs, org.MSP().GetTLSIntermediateCerts()...)

We run a committer build with exactly these two one-line patches applied and the
handshake failures disappear; the same topology commits transactions end to end.
(Verification detail: openssl verify with the pool contents confirms the chain is
valid the moment the intermediate is present; the certificates were never the issue.)

Happy to submit PRs for both repositories if the direction looks right to you.

Reproduction sketch

  1. Create a TLS root CA and one intermediate CA per orderer org; sign every node TLS
    leaf with its org's intermediate.
  2. Lay out standard MSP directories (tlscacerts/ = root, tlsintermediatecerts/ =
    intermediate) and produce the genesis block with configtxgen.
  3. Start orderers and the committer sidecar; the sidecar's orderer dials fail with
    certificate signed by unknown authority even though the config block contains the
    full chain material.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions