Skip to content

Commit

Permalink
[FABN-1160] Fix TLS logic for discovered peers
Browse files Browse the repository at this point in the history
The SDK assumes by default that peers and orderers
returned by service discovery will be running with
TLS (grpcs://). This is a bad assumption when we
are connecting to a gateway peer that is not running
with TLS (grpc://).

Change the logic so that if we connect to a peer
to perform discovery over TLS, then we assume that
the discovered peers and orderers also use TLS.
Likewise if we connect to a peer to perform discovery
without TLS, then assume that the discovered peers
and orderers also do not use TLS.

It is highly unlikely (impossible even?) that you
can have a mixed Fabric network with TLS and non-TLS
peers.

Change-Id: I9b8e0056cd6c6c743d6d7feab561cdaa8f7d3f05
Signed-off-by: Simon Stone <[email protected]>
  • Loading branch information
Simon Stone committed Mar 4, 2019
1 parent 847d2da commit c3b884b
Show file tree
Hide file tree
Showing 6 changed files with 373 additions and 56 deletions.
38 changes: 23 additions & 15 deletions docs/tutorials/discovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ To use the service the application will have to connect with just one peer.
* `channel.initialize()` - This method has been enhanced by adding an option to
query a peer using the new service discovery to initialize the channel object.
This method may be call at anytime to reinitialize the channel. When using discovery,
this may be used to assign a new target peer providing the discover service.
this may be used to assign a new target peer providing the discovery service.
The initialize() method is also required to instantiate the handlers, by default
the handlers shipped with the fabric-client are designed to use the discovery
results.
Expand Down Expand Up @@ -57,8 +57,16 @@ organizations active on the network at the time of the query. see {@link Client#
channel to be initialized, service discovery will be used. (default false)
* `discovery-cache-life` - integer (time in milliseconds) - The amount of time the
service discovery results are considered valid. (default 300000 - 5 minutes)
* `discovery-protocol` - string - The protocol to use when building URL's for the
discovered endpoints. The Discover Service only provides host:port. (default 'grpcs').
* `override-discovery-protocol` - string - Override the protocol to use when
building URL's for the discovered endpoints. The Discovery Service only provides
host:port. By default, if you connect to the Discovery Service without TLS (grpc://),
then all discovered endpoints will be connected to without TLS. If you connect to
the Discovery Service with TLS (grpcs://), then all discovered endpoints will be
connected to with TLS. You can use this configuration setting to force either grpc
or grpcs for all discovered endpoints, regardless of how you connected to the
Discovery Service. Please note that it is highly recommended not to connect to the
Discovery Service or any discovered endpoints without TLS (grpc://), as all information
will be sent over plaintext, un-encrypted.
* `endorsement-handler` - string - The path to the endorsement handler. Allows for a
custom handler to be used. This handler is used in the `sendTransactionProposal`
method to determine the target peers and how to send the proposal.
Expand All @@ -85,7 +93,7 @@ the `discovery-cache-life` system setting. By default the cache life is
```
Client.setConfigSetting('discover-cache-life', <milliseconds>);
```
If there are no service discover results, the handler will send the
If there are no service discovery results, the handler will send the
endorsement request to the peers that have been assigned to the channel with
the `endorsingPeer` role (a peer that has nor been assigned a role will default
to having that role, this means that a role must be explicitly turned off).
Expand Down Expand Up @@ -147,7 +155,7 @@ assigned automatically when using the service discovery.

### To Initialize
By default the fabric-client will not use the service discovery. To enable the
use of the service, set the config setting to true or use the discover parameter
use of the service, set the config setting to true or use the `discover` parameter
on the `initialize()` call.

note: {@link Channel#initialize} must be run to both enable discovery and to
Expand Down Expand Up @@ -191,15 +199,15 @@ await channel.initialize({
});
```

The return results of initialization with service discover will be the MSP configurations,
The return results of initialization with service discovery will be the MSP configurations,
the peers, the orderers, and the endorsing plans for all chaincodes on the
channel in JSON format. The results are stored and cached internally and the caller
does not have to do anything with the results, they are provided only for reference.

The initialize call also allows for changing the endorsement handler by specifying
a path to a custom endorsement handler. The handler may be changed independently
of using the service discovery. The default endorsement handler however does use
discover service results to determine the endorsing peers.
discovery service results to determine the endorsing peers.

```
await channel.initialize({
Expand Down Expand Up @@ -279,10 +287,10 @@ const peer = client.newPeer(....);
channel.addPeer(peer);
await channel.initialize({discover:true});
```
When the channel is initialized using service discover and peers and orderers are added
to the channel, a peer with the address that was used for service discover
When the channel is initialized using service discovery and peers and orderers are added
to the channel, a peer with the address that was used for service discovery
will likely be on the list of discovered peers. A peer with the address used for
service discover will not be added again to the channel as a peer with that address
service discovery will not be added again to the channel as a peer with that address
has already been assigned to the channel.

The name a peer will be known by may be set by using the `name` setting when
Expand Down Expand Up @@ -310,7 +318,7 @@ await channel.initialize({discover:true, target:peer});
When the channel is initialized using service discovery and peers and orderers
are added to the channel, a peer with the address that was used for service
discovery will likely be on the list of discovered peers. A peer instance with
the address used for service discover will be added to the channel with the
the address used for service discovery will be added to the channel with the
same address as the peer instance used for service discovery because the peer
instance used on the initialize call is not added to the channel, it is only
used on the initialize call.
Expand All @@ -327,7 +335,7 @@ channel. The peer must have the role `discover`. As with all roles, if the role
is not defined and set to false, the peer will have that role on the channel by
default.

The following example shows a peer that is going to be used primarily for service discover.
The following example shows a peer that is going to be used primarily for service discovery.

```
channels:
Expand Down Expand Up @@ -392,11 +400,11 @@ peer0.org1.example.com:7051
```
### To Endorse
As discussed above, the `channel.sendTransactionProposal()` will now use a pluggable
handler. The fabric-client will come with a handler that will use service discover.
handler. The fabric-client will come with a handler that will use service discovery.
By default the `endorsement-handler` configuration setting will point to the
`DiscoveryEndorsementHandler`. If the channel has been initialized using the
service discovery and there are no targets define on the `sendTransactionProposal`
call, the handler will use the service discover results based on the chaincode
call, the handler will use the service discovery results based on the chaincode
of the proposal request to determine the target peers
to perform the endorsements.
```
Expand Down Expand Up @@ -529,7 +537,7 @@ added to the channel. By default the `commit-handler` configuration setting
will point to the `BasicCommitHandler`. This handler will send the transaction
to each orderer, one at a time, that has been assigned to the channel until it
gets a `SUCCESS` response or until the list is exhausted. The orderers may been
added manually, due to a service discover initialization or combination of the two.
added manually, due to a service discovery initialization or combination of the two.
If an orderer is specified on the call, only that orderer will be used.


Expand Down
2 changes: 1 addition & 1 deletion fabric-client/config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"grpc-wait-for-ready-timeout": 3000,
"discovery-as-localhost": true,
"discovery-cache-life": 300000,
"discovery-protocol": "grpcs",
"override-discovery-protocol": null,
"endorsement-handler": "fabric-client/lib/impl/DiscoveryEndorsementHandler.js",
"commit-handler": "fabric-client/lib/impl/BasicCommitHandler.js"
}
45 changes: 29 additions & 16 deletions fabric-client/lib/Channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,10 @@ const Channel = class {
throw Error('No MSP information found');
}
if (discovery_results.orderers) {
this._buildDiscoveryOrderers(discovery_results, discovery_results.msps, request);
this._buildDiscoveryOrderers(discovery_results, discovery_results.msps, discover_request);
}
if (discovery_results.peers_by_org) {
this._buildDiscoveryPeers(discovery_results, discovery_results.msps, request);
this._buildDiscoveryPeers(discovery_results, discovery_results.msps, discover_request);
}

discovery_results.endorsement_plans = [];
Expand All @@ -312,7 +312,12 @@ const Channel = class {
if (discover_interest_results &&
discover_interest_results.endorsement_plans &&
discover_interest_results.endorsement_plans[0]) {
const plan = this._buildDiscoveryEndorsementPlan(discover_interest_results, plan_id, discovery_results.msps, request);
const plan = this._buildDiscoveryEndorsementPlan(
discover_interest_results,
plan_id,
discovery_results.msps,
discover_request
);
discovery_results.endorsement_plans.push(plan);
logger.debug('%s - Added an endorsement plan for %s', method, plan_id);
} else {
Expand Down Expand Up @@ -371,7 +376,7 @@ const Channel = class {
}
}

_buildDiscoveryOrderers(discovery_results, msps, options) {
_buildDiscoveryOrderers(discovery_results, msps, discover_request) {
const method = '_buildDiscoveryOrderers';
logger.debug('%s - build orderers', method);

Expand All @@ -385,13 +390,13 @@ const Channel = class {
endpoint.host,
endpoint.port,
msps,
options
discover_request
);
}
}
}

_buildDiscoveryPeers(discovery_results, msps, options) {
_buildDiscoveryPeers(discovery_results, msps, discover_request) {
const method = '_buildDiscoveryPeers';
logger.debug('%s - build peers', method);

Expand All @@ -410,14 +415,14 @@ const Channel = class {
peer.endpoint,
peer.mspid,
msps,
options
discover_request
);
logger.debug('%s - peer:%j', method, peer);
}
}
}

_buildDiscoveryEndorsementPlan(discovery_results, plan_id, msps, options) {
_buildDiscoveryEndorsementPlan(discovery_results, plan_id, msps, discover_request) {
const method = '_buildDiscoveryEndorsementPlan';
logger.debug('%s - build endorsement plan for %s', method, plan_id);

Expand All @@ -431,7 +436,7 @@ const Channel = class {
peer.endpoint,
peer.mspid,
msps,
options
discover_request
);
logger.debug('%s - peer:%j', method, peer);
}
Expand Down Expand Up @@ -1409,12 +1414,12 @@ const Channel = class {
return peers;
}

_buildOrdererName(msp_id, host, port, msps, request) {
_buildOrdererName(msp_id, host, port, msps, discover_request) {
const method = '_buildOrdererName';
logger.debug('%s - start', method);

const name = host + ':' + port;
const url = this._buildUrl(host, port, request);
const url = this._buildUrl(host, port, discover_request);
let found = null;
this._orderers.forEach((orderer) => {
if (orderer.getUrl() === url) {
Expand All @@ -1435,13 +1440,13 @@ const Channel = class {
return found.getName();
}

_buildPeerName(endpoint, msp_id, msps, request) {
_buildPeerName(endpoint, msp_id, msps, discover_request) {
const method = '_buildPeerName';
logger.debug('%s - start', method);

const name = endpoint;
const host_port = endpoint.split(':');
const url = this._buildUrl(host_port[0], host_port[1], request);
const url = this._buildUrl(host_port[0], host_port[1], discover_request);
let found = null;
this._channel_peers.forEach((peer) => {
if (peer.getUrl() === url) {
Expand All @@ -1462,7 +1467,7 @@ const Channel = class {
return found.getName();
}

_buildUrl(hostname, port, request) {
_buildUrl(hostname, port, discover_request) {
const method = '_buildUrl';
logger.debug('%s - start', method);

Expand All @@ -1473,7 +1478,16 @@ const Channel = class {
t_hostname = 'localhost';
}

const protocol = sdk_utils.getConfigSetting('discovery-protocol', 'grpcs');
// If we connect to the discovery peer over TLS, any peers returned by
// discovery should also use TLS. If we connect to the discovery peer
// without TLS, then any peers returned by discovery should not use TLS.
// A mixed set of TLS and non-TLS peers is unlikely but possible via the
// override.
let protocol = discover_request.target.isTLS() ? 'grpcs' : 'grpc';
const overrideProtocol = sdk_utils.getConfigSetting('override-discovery-protocol');
if (overrideProtocol) {
protocol = overrideProtocol;
}
const url = protocol + '://' + t_hostname + ':' + port;

return url;
Expand All @@ -1482,7 +1496,6 @@ const Channel = class {
_buildOptions(name, url, host, msp) {
const method = '_buildOptions';
logger.debug('%s - start', method);

const caroots = this._buildTlsRootCerts(msp);
const opts = {
'pem': caroots,
Expand Down
27 changes: 22 additions & 5 deletions fabric-client/lib/Remote.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,15 @@ class Remote {
'url:' + this._url +
'}';
}

/**
* Determine whether or not this remote endpoint uses TLS.
* @returns {boolean} True if this endpoint uses TLS, false otherwise.
*/
isTLS() {
return this._endpoint.isTLS();
}

}

module.exports = Remote;
Expand All @@ -246,14 +255,13 @@ class Endpoint {
constructor(url, pem, clientKey, clientCert) {

const purl = urlParser.parse(url, true);
let protocol;
if (purl.protocol) {
protocol = purl.protocol.toLowerCase().slice(0, -1);
this.protocol = purl.protocol.toLowerCase().slice(0, -1);
}
if (protocol === 'grpc') {
if (this.protocol === 'grpc') {
this.addr = purl.host;
this.creds = grpc.credentials.createInsecure();
} else if (protocol === 'grpcs') {
} else if (this.protocol === 'grpcs') {
if (!(typeof pem === 'string')) {
throw new Error('PEM encoded certificate is required.');
}
Expand All @@ -278,10 +286,19 @@ class Endpoint {
} else {
const error = new Error();
error.name = 'InvalidProtocol';
error.message = 'Invalid protocol: ' + protocol + '. URLs must begin with grpc:// or grpcs://';
error.message = 'Invalid protocol: ' + this.protocol + '. URLs must begin with grpc:// or grpcs://';
throw error;
}
}

/**
* Determine whether or not this endpoint uses TLS.
* @returns {boolean} True if this endpoint uses TLS, false otherwise.
*/
isTLS() {
return this.protocol === 'grpcs';
}

}

module.exports.Endpoint = Endpoint;
Loading

0 comments on commit c3b884b

Please sign in to comment.