From 0308f0f1bb5cbf9957c0574d3db4b5f33bc0492c Mon Sep 17 00:00:00 2001 From: jeffgarratt Date: Fri, 10 Mar 2017 10:12:27 -0500 Subject: [PATCH] [FAB-1141] Enabling TLS in bootstrap feature Changed the AnchorPeer port to 7051 as it shares the peer channel. Now set CORE_PEER_TSL_ROOTCERT_FILE to signing orgs cert for the signer for the peer. Use RSA based keys for TLS certs to enable python to golang TLS connections without handshake failures. Change-Id: I94d653e9d9e666f90665a62bb5bbbfd376bc9b23 Signed-off-by: jeffgarratt --- bddtests/compose-defaults.yml | 5 +- bddtests/docker-compose-next-4.yml | 12 +++ bddtests/features/bootstrap.feature | 12 +-- bddtests/steps/bdd_grpc_util.py | 59 ++------------- bddtests/steps/bootstrap_impl.py | 17 ++--- bddtests/steps/bootstrap_util.py | 111 ++++++++++++++++++++++------ bddtests/steps/endorser_impl.py | 8 +- bddtests/steps/endorser_util.py | 11 ++- bddtests/steps/orderer_util.py | 24 +++--- 9 files changed, 152 insertions(+), 107 deletions(-) diff --git a/bddtests/compose-defaults.yml b/bddtests/compose-defaults.yml index 475eaaf2d8d..1e1108cf07c 100644 --- a/bddtests/compose-defaults.yml +++ b/bddtests/compose-defaults.yml @@ -9,6 +9,9 @@ services: # - CORE_VM_ENDPOINT=http://172.17.0.1:2375 - CORE_LOGGING_LEVEL=DEBUG - CORE_PEER_NETWORKID=${CORE_PEER_NETWORKID} + - CORE_PEER_TLS_ENABLED=true + # This disables mutual auth for gossip + - CORE_PEER_GOSSIP_SKIPHANDSHAKE=true volumes: - ./volumes/peer:/var/hyperledger/bddtests/volumes/peer - /var/run/docker.sock:/var/run/docker.sock @@ -16,7 +19,7 @@ services: # $$GOPATH (double dollar) required to prevent docker-compose doing its own # substitution before the value gets to the container #command: sh -c "exec $$GOPATH/src/github.com/hyperledger/fabric/bddtests/scripts/start-peer.sh" - command: sh -c "sleep 2; peer node start --peer-defaultchain=false" + command: peer node start --peer-defaultchain=false # Use these options if coverage desired for peers #image: hyperledger/fabric-peer-coverage diff --git a/bddtests/docker-compose-next-4.yml b/bddtests/docker-compose-next-4.yml index c99e33ab51d..58b3862753e 100644 --- a/bddtests/docker-compose-next-4.yml +++ b/bddtests/docker-compose-next-4.yml @@ -9,6 +9,10 @@ services: environment: - ORDERER_GENERAL_LOCALMSPDIR=${ORDERER0_ORDERER_GENERAL_LOCALMSPDIR} - ORDERER_GENERAL_LOCALMSPID=${ORDERER0_ORDERER_GENERAL_LOCALMSPID} + - ORDERER_GENERAL_TLS_ENABLED=true + - ORDERER_GENERAL_TLS_PRIVATEKEY=${ORDERER0_ORDERER_GENERAL_TLS_PRIVATEKEY} + - ORDERER_GENERAL_TLS_CERTIFICATE=${ORDERER0_ORDERER_GENERAL_TLS_CERTIFICATE} + - ORDERER_GENERAL_TLS_ROOTCAS=${ORDERER0_ORDERER_GENERAL_TLS_ROOTCAS} peer0: @@ -24,6 +28,8 @@ services: - CORE_PEER_LOCALMSPID=${PEER0_CORE_PEER_LOCALMSPID} - CORE_PEER_TLS_CERT_FILE=${PEER0_CORE_PEER_TLS_CERT_FILE} - CORE_PEER_TLS_KEY_FILE=${PEER0_CORE_PEER_TLS_KEY_FILE} + - CORE_PEER_TLS_ROOTCERT_FILE=${PEER0_CORE_PEER_TLS_ROOTCERT_FILE} + - CORE_PEER_TLS_SERVERHOSTOVERRIDE=${PEER0_CORE_PEER_TLS_SERVERHOSTOVERRIDE} depends_on: - orderer0 # ports: @@ -42,6 +48,8 @@ services: - CORE_PEER_LOCALMSPID=${PEER1_CORE_PEER_LOCALMSPID} - CORE_PEER_TLS_CERT_FILE=${PEER1_CORE_PEER_TLS_CERT_FILE} - CORE_PEER_TLS_KEY_FILE=${PEER1_CORE_PEER_TLS_KEY_FILE} + - CORE_PEER_TLS_ROOTCERT_FILE=${PEER1_CORE_PEER_TLS_ROOTCERT_FILE} + - CORE_PEER_TLS_SERVERHOSTOVERRIDE=${PEER1_CORE_PEER_TLS_SERVERHOSTOVERRIDE} depends_on: - orderer0 - peer0 @@ -58,6 +66,8 @@ services: - CORE_PEER_LOCALMSPID=${PEER2_CORE_PEER_LOCALMSPID} - CORE_PEER_TLS_CERT_FILE=${PEER2_CORE_PEER_TLS_CERT_FILE} - CORE_PEER_TLS_KEY_FILE=${PEER2_CORE_PEER_TLS_KEY_FILE} + - CORE_PEER_TLS_ROOTCERT_FILE=${PEER2_CORE_PEER_TLS_ROOTCERT_FILE} + - CORE_PEER_TLS_SERVERHOSTOVERRIDE=${PEER2_CORE_PEER_TLS_SERVERHOSTOVERRIDE} depends_on: - orderer0 - peer0 @@ -74,6 +84,8 @@ services: - CORE_PEER_LOCALMSPID=${PEER3_CORE_PEER_LOCALMSPID} - CORE_PEER_TLS_CERT_FILE=${PEER3_CORE_PEER_TLS_CERT_FILE} - CORE_PEER_TLS_KEY_FILE=${PEER3_CORE_PEER_TLS_KEY_FILE} + - CORE_PEER_TLS_ROOTCERT_FILE=${PEER3_CORE_PEER_TLS_ROOTCERT_FILE} + - CORE_PEER_TLS_SERVERHOSTOVERRIDE=${PEER3_CORE_PEER_TLS_SERVERHOSTOVERRIDE} depends_on: - orderer0 - peer0 diff --git a/bddtests/features/bootstrap.feature b/bddtests/features/bootstrap.feature index 44024016022..c3e72b500de 100644 --- a/bddtests/features/bootstrap.feature +++ b/bddtests/features/bootstrap.feature @@ -11,7 +11,7 @@ Feature: Bootstrap As a blockchain entrepreneur I want to bootstrap a new blockchain network - #@doNotDecompose + @doNotDecompose @generateDocs Scenario Outline: Bootstrap a development network with 4 peers (2 orgs) and 1 orderer (1 org), each having a single independent root of trust (No fabric-ca, just openssl) #creates 1 self-signed key/cert pair per orderer organization @@ -118,7 +118,7 @@ Feature: Bootstrap And the user "dev0Org0" creates a ConfigUpdate Tx "configUpdateTx1" using cert alias "dev0Org0App1" using signed ConfigUpdateEnvelope "createChannelConfigUpdate1" - And the user "dev0Org0" broadcasts ConfigUpdate Tx "configUpdateTx1" to orderer "orderer0" to create channel "com.acme.blockchain.jdoe.Channel1" + And the user "dev0Org0" using cert alias "dev0Org0App1" broadcasts ConfigUpdate Tx "configUpdateTx1" to orderer "orderer0" to create channel "com.acme.blockchain.jdoe.Channel1" # Sleep as the deliver takes a bit to have the first block ready And I wait "2" seconds @@ -161,7 +161,7 @@ Feature: Bootstrap # Under the covers, create a deployment spec, etc. And user "dev0Org0" using cert alias "dev0Org0App1" creates a install proposal "installProposal1" for channel "com.acme.blockchain.jdoe.Channel1" using chaincode spec "cc_spec" - And user "dev0Org0" sends proposal "installProposal1" to endorsers with timeout of "30" seconds with proposal responses "installProposalResponses": + And user "dev0Org0" using cert alias "dev0Org0App1" sends proposal "installProposal1" to endorsers with timeout of "30" seconds with proposal responses "installProposalResponses": | Endorser | | peer0 | | peer2 | @@ -174,7 +174,7 @@ Feature: Bootstrap # Under the covers, create a deployment spec, etc. When user "dev0Org0" using cert alias "dev0Org0App1" creates a instantiate proposal "instantiateProposal1" for channel "com.acme.blockchain.jdoe.Channel1" using chaincode spec "cc_spec" - And user "dev0Org0" sends proposal "instantiateProposal1" to endorsers with timeout of "90" seconds with proposal responses "instantiateProposalResponses": + And user "dev0Org0" using cert alias "dev0Org0App1" sends proposal "instantiateProposal1" to endorsers with timeout of "30" seconds with proposal responses "instantiateProposalResponses": | Endorser | | peer0 | | peer2 | @@ -215,7 +215,7 @@ Feature: Bootstrap # Under the covers, create a deployment spec, etc. And user "dev0Org0" using cert alias "dev0Org0App1" creates a proposal "queryProposal1" for channel "com.acme.blockchain.jdoe.Channel1" using chaincode spec "querySpec1" - And user "dev0Org0" sends proposal "queryProposal1" to endorsers with timeout of "30" seconds with proposal responses "queryProposal1Responses": + And user "dev0Org0" using cert alias "dev0Org0App1" sends proposal "queryProposal1" to endorsers with timeout of "30" seconds with proposal responses "queryProposal1Responses": | Endorser | | peer0 | | peer2 | @@ -239,7 +239,7 @@ Feature: Bootstrap # Under the covers, create a deployment spec, etc. And user "dev0Org0" using cert alias "dev0Org0App1" creates a proposal "invokeProposal1" for channel "com.acme.blockchain.jdoe.Channel1" using chaincode spec "invocationSpec1" - And user "dev0Org0" sends proposal "invokeProposal1" to endorsers with timeout of "30" seconds with proposal responses "invokeProposal1Responses": + And user "dev0Org0" using cert alias "dev0Org0App1" sends proposal "invokeProposal1" to endorsers with timeout of "30" seconds with proposal responses "invokeProposal1Responses": | Endorser | | peer0 | | peer2 | diff --git a/bddtests/steps/bdd_grpc_util.py b/bddtests/steps/bdd_grpc_util.py index 16e496273af..bb73d8d76af 100644 --- a/bddtests/steps/bdd_grpc_util.py +++ b/bddtests/steps/bdd_grpc_util.py @@ -14,64 +14,17 @@ # limitations under the License. # -import re - -import bdd_test_util - import grpc -def getGRPCChannel(ipAddress): - channel = grpc.insecure_channel("{0}:{1}".format(ipAddress, 7051), options = [('grpc.max_message_length', 100*1024*1024)]) +def getGRPCChannel(ipAddress, port, root_certificates, ssl_target_name_override): + # channel = grpc.insecure_channel("{0}:{1}".format(ipAddress, 7051), options = [('grpc.max_message_length', 100*1024*1024)]) + # creds = grpc.ssl_channel_credentials(root_certificates=root_certificates, private_key=private_key, certificate_chain=certificate_chain) + creds = grpc.ssl_channel_credentials(root_certificates=root_certificates) + channel = grpc.secure_channel("{0}:{1}".format(ipAddress, port), creds, + options=(('grpc.ssl_target_name_override', ssl_target_name_override,),('grpc.default_authority', ssl_target_name_override,),)) print("Returning GRPC for address: {0}".format(ipAddress)) return channel -def getGRPCChannelAndUser(context, enrollId): - '''Returns a tuple of GRPC channel and UserRegistration instance. The channel is open to the composeService that the user registered with.''' - userRegistration = bdd_test_util.getUserRegistration(context, enrollId) - - # Get the IP address of the server that the user registered on - ipAddress = bdd_test_util.ipFromContainerNamePart(userRegistration.composeService, context.compose_containers) - - channel = getGRPCChannel(ipAddress) - - return (channel, userRegistration) - - -def getDeployment(context, ccAlias): - '''Return a deployment with chaincode alias from prior deployment, or None if not found''' - deployment = None - if 'deployments' in context: - pass - else: - context.deployments = {} - if ccAlias in context.deployments: - deployment = context.deployments[ccAlias] - # else: - # raise Exception("Deployment alias not found: '{0}'. Are you sure you have deployed a chaincode with this alias?".format(ccAlias)) - return deployment - -def getArgsFromContextForUser(context, enrollId): - # Update the chaincodeSpec ctorMsg for invoke - args = [] - if 'table' in context: - if context.table: - # There are function arguments - userRegistration = bdd_test_util.getUserRegistration(context, enrollId) - # Allow the user to specify expressions referencing tags in the args list - pattern = re.compile('\{(.*)\}$') - for arg in context.table[0].cells: - m = pattern.match(arg) - if m: - # tagName reference found in args list - tagName = m.groups()[0] - # make sure the tagName is found in the users tags - assert tagName in userRegistration.tags, "TagName '{0}' not found for user '{1}'".format(tagName, userRegistration.getUserName()) - args.append(userRegistration.tags[tagName]) - else: - #No tag referenced, pass the arg - args.append(arg) - return args - def toStringArray(items): itemsAsStr = [] for item in items: diff --git a/bddtests/steps/bootstrap_impl.py b/bddtests/steps/bootstrap_impl.py index 9cfde6197d4..87bf4654ee7 100644 --- a/bddtests/steps/bootstrap_impl.py +++ b/bddtests/steps/bootstrap_impl.py @@ -14,9 +14,7 @@ # from behave import * -import bdd_grpc_util import endorser_util -import bdd_test_util import bootstrap_util import orderer_util import compose @@ -204,19 +202,19 @@ def step_impl(context, userName, certAlias, configUpdateTxName, createChannelSig typeAsString="CONFIG_UPDATE") user.setTagValue(configUpdateTxName, envelope_for_config_update) -@given(u'the user "{userName}" broadcasts ConfigUpdate Tx "{configTxName}" to orderer "{orderer}" to create channel "{channelId}"') -def step_impl(context, userName, configTxName, orderer, channelId): +@given(u'the user "{userName}" using cert alias "{certAlias}" broadcasts ConfigUpdate Tx "{configTxName}" to orderer "{orderer}" to create channel "{channelId}"') +def step_impl(context, userName, certAlias, configTxName, orderer, channelId): directory = bootstrap_util.getDirectory(context) user = directory.getUser(userName=userName) configTxEnvelope = user.tags[configTxName] - bootstrap_util.broadcastCreateChannelConfigTx(context=context, composeService=orderer, chainId=channelId, user=user, configTxEnvelope=configTxEnvelope) + bootstrap_util.broadcastCreateChannelConfigTx(context=context,certAlias=certAlias, composeService=orderer, chainId=channelId, user=user, configTxEnvelope=configTxEnvelope) @when(u'the user "{userName}" broadcasts transaction "{transactionAlias}" to orderer "{orderer}" on channel "{channelId}"') def step_impl(context, userName, transactionAlias, orderer, channelId): directory = bootstrap_util.getDirectory(context) user = directory.getUser(userName=userName) transaction = user.tags[transactionAlias] - bootstrap_util.broadcastCreateChannelConfigTx(context=context, composeService=orderer, chainId=channelId, user=user, configTxEnvelope=transaction) + bootstrap_util.broadcastCreateChannelConfigTx(context=context, certAlias=None, composeService=orderer, chainId=channelId, user=user, configTxEnvelope=transaction) @when(u'user "{userName}" using cert alias "{certAlias}" connects to deliver function on orderer "{composeService}"') @@ -225,7 +223,7 @@ def step_impl(context, userName, certAlias, composeService): user = directory.getUser(userName=userName) nodeAdminTuple = user.tags[certAlias] cert = directory.findCertForNodeAdminTuple(nodeAdminTuple) - user.connectToDeliverFunction(context, composeService, cert, nodeAdminTuple=nodeAdminTuple) + user.connectToDeliverFunction(context, composeService, certAlias, nodeAdminTuple=nodeAdminTuple) @when(u'user "{userName}" sends deliver a seek request on orderer "{composeService}" with properties') def step_impl(context, userName, composeService): @@ -256,8 +254,9 @@ def step_impl(context, userName, certAlias, genisisBlockName, joinChannelResult) timeout = 10 directory = bootstrap_util.getDirectory(context) user = directory.getUser(userName) + nodeAdminTuple = user.tags[certAlias] # Find the cert using the cert tuple information saved for the user under certAlias - signersCert = directory.findCertForNodeAdminTuple(user.tags[certAlias]) + signersCert = directory.findCertForNodeAdminTuple(nodeAdminTuple) # Retrieve the genesis block from the returned value of deliver (Will be list with first block as genesis block) genesisBlock = user.tags[genisisBlockName][0] @@ -267,7 +266,7 @@ def step_impl(context, userName, certAlias, genisisBlockName, joinChannelResult) # Send proposal to each specified endorser, waiting 'timeout' seconds for response/error endorsers = [row['Peer'] for row in context.table.rows] - proposalResponseFutures = [endorserStub.ProcessProposal.future(signedProposal, int(timeout)) for endorserStub in endorser_util.getEndorserStubs(context, endorsers)] + proposalResponseFutures = [endorserStub.ProcessProposal.future(signedProposal, int(timeout)) for endorserStub in endorser_util.getEndorserStubs(context,composeServices=endorsers, directory=directory, nodeAdminTuple=nodeAdminTuple)] resultsDict = dict(zip(endorsers, [respFuture.result() for respFuture in proposalResponseFutures])) user.setTagValue(joinChannelResult, resultsDict) diff --git a/bddtests/steps/bootstrap_util.py b/bddtests/steps/bootstrap_util.py index 802d199d935..342cc5dae28 100644 --- a/bddtests/steps/bootstrap_util.py +++ b/bddtests/steps/bootstrap_util.py @@ -167,6 +167,7 @@ def __init__(self, name): self.name = name # Create a ECDSA key, then a crypto pKey from the DER for usage with cert requests, etc. self.ecdsaSigningKey = createECDSAKey() + self.rsaSigningKey = createRSAKey() self.pKey = crypto.load_privatekey(crypto.FILETYPE_ASN1, self.ecdsaSigningKey.to_der()) # Signing related ecdsa config self.hashfunc = hashlib.sha256 @@ -174,10 +175,16 @@ def __init__(self, name): self.sigdecode = ecdsa.util.sigdecode_der def createCertRequest(self, nodeName): - req = createCertRequest(self.pKey, CN=nodeName) + req = createCertRequest(self.pKey, C="US", ST="North Carolina", L="RTP", O="IBM", CN=nodeName) # print("request => {0}".format(crypto.dump_certificate_request(crypto.FILETYPE_PEM, req))) return req + def createTLSCertRequest(self, nodeName): + req = createCertRequest(self.rsaSigningKey, C="US", ST="North Carolina", L="RTP", O="IBM", CN=nodeName) + # print("request => {0}".format(crypto.dump_certificate_request(crypto.FILETYPE_PEM, req))) + return req + + def computeHash(self, data): s = self.hashfunc() s.update(data) @@ -215,12 +222,15 @@ class Organization(Entity): def __init__(self, name): Entity.__init__(self, name) - req = createCertRequest(self.pKey, CN=name) + req = createCertRequest(self.pKey, C="US", ST="North Carolina", L="RTP", O="IBM", CN=name) numYrs = 1 self.signedCert = createCertificate(req, (req, self.pKey), 1000, (0, 60 * 60 * 24 * 365 * numYrs), isCA=True) # Which networks this organization belongs to self.networks = [] + def __repr__(self): + return "name:{0}, networks: {1}".format(self.name, self.networks) + def getSelfSignedCert(self): return self.signedCert @@ -239,6 +249,12 @@ def getMSPConfig(self): mspConfig = mspconfig_pb2.MSPConfig(config=fabricMSPConfig.SerializeToString(), type=0) return mspConfig + def isInNetwork(self, network): + for n in self.networks: + if str(n)==str(network): + return True + return False + def getMspPrincipalAsRole(self, mspRoleTypeAsString): mspRole = msp_principal_pb2.MSPRole(msp_identifier=self.name, Role=msp_principal_pb2.MSPRole.MSPRoleType.Value(mspRoleTypeAsString)) mspPrincipal = msp_principal_pb2.MSPPrincipal( @@ -315,6 +331,15 @@ def findNodeAdminTuple(self, userName, contextName, orgName): assert nodeAdminTuple in self.ordererAdminTuples, "Node admin tuple not found for: {0}".format(nodeAdminTuple) return nodeAdminTuple + def getTrustedRootsForPeerNetworkAsPEM(self): + pems = [peerOrg.getCertAsPEM() for peerOrg in [org for org in self.getOrganizations().values() if org.isInNetwork(Network.Peer)]] + return "".join(pems) + + def getTrustedRootsForOrdererNetworkAsPEM(self): + pems = [ordererOrg.getCertAsPEM() for ordererOrg in [org for org in self.getOrganizations().values() if org.isInNetwork(Network.Orderer)]] + return "".join(pems) + + def registerOrdererAdminTuple(self, userName, ordererName, organizationName): ' Assign the user as orderer admin' ordererAdminTuple = NodeAdminTuple(user=userName, nodeName=ordererName, organization=organizationName) @@ -614,7 +639,7 @@ def getAnchorPeersConfigGroup(context, nodeAdminTuples): for (k,nodeAdminTuple) in group: anchorPeer = anchorPeers.anchor_peers.add() anchorPeer.host = nodeAdminTuple.nodeName - anchorPeer.port = 5611 + anchorPeer.port = 7051 # anchorPeer.cert = crypto.dump_certificate(crypto.FILETYPE_PEM, # directory.findCertForNodeAdminTuple(nodeAdminTuple)) config_group.groups[ApplicationGroup].groups[orgName].values[BootstrapHelper.KEY_ANCHOR_PEERS].value=toValue(anchorPeers) @@ -861,21 +886,40 @@ def getVolumePath(self, composition, pathType=PathType.Local): def getLocalMspConfigPath(self, composition, compose_service, pathType=PathType.Local): return "{0}/{1}/localMspConfig".format(self.getVolumePath(composition, pathType), compose_service) + def getLocalTLSConfigPath(self, composition, compose_service, pathType=PathType.Local): + return os.path.join(self.getVolumePath(composition, pathType), compose_service, "tls_config") + def _getPathAndUserInfo(self, directory , composition, compose_service, nat_discriminator="Signer", pathType=PathType.Local): matchingNATs = [nat for nat in directory.getNamedCtxTuples() if ((compose_service in nat.user) and (nat_discriminator in nat.user) and ((compose_service in nat.nodeName)))] assert len(matchingNATs)==1, "Unexpected number of matching NodeAdminTuples: {0}".format(matchingNATs) localMspConfigPath = self.getLocalMspConfigPath(composition=composition, compose_service=compose_service,pathType=pathType) - return (localMspConfigPath, matchingNATs[0].user) + return (localMspConfigPath, matchingNATs[0]) def getLocalMspConfigPrivateKeyPath(self, directory , composition, compose_service, pathType=PathType.Local): - (localMspConfigPath, user) = self._getPathAndUserInfo(directory=directory, composition=composition, compose_service=compose_service, pathType=pathType) - return "{0}/keystore/{1}.pem".format(localMspConfigPath, user) + (localMspConfigPath, nodeAdminTuple) = self._getPathAndUserInfo(directory=directory, composition=composition, compose_service=compose_service, pathType=pathType) + return "{0}/keystore/{1}.pem".format(localMspConfigPath, nodeAdminTuple.user) def getLocalMspConfigPublicCertPath(self, directory , composition, compose_service, pathType=PathType.Local): - (localMspConfigPath, user) = self._getPathAndUserInfo(directory=directory, composition=composition, compose_service=compose_service, pathType=pathType) - return "{0}/signcerts/{1}.pem".format(localMspConfigPath, user) + (localMspConfigPath, nodeAdminTuple) = self._getPathAndUserInfo(directory=directory, composition=composition, compose_service=compose_service, pathType=pathType) + return "{0}/signcerts/{1}.pem".format(localMspConfigPath, nodeAdminTuple.user) + + def getTLSKeyPaths(self, pnt , composition, compose_service, pathType=PathType.Local): + localTLSConfigPath = self.getLocalTLSConfigPath(composition, compose_service, pathType=pathType) + certPath = os.path.join(localTLSConfigPath, + "{0}-{1}-{2}-tls.crt".format(pnt.user, pnt.nodeName, pnt.organization)) + keyPath = os.path.join(localTLSConfigPath, + "{0}-{1}-{2}-tls.key".format(pnt.user, pnt.nodeName, pnt.organization)) + return (keyPath, certPath) + def getLocalMspConfigRootCertPath(self, directory , composition, compose_service, pathType=PathType.Local): + (localMspConfigPath, nodeAdminTuple) = self._getPathAndUserInfo(directory=directory, composition=composition, compose_service=compose_service, pathType=pathType) + return "{0}/cacerts/{1}.pem".format(localMspConfigPath, nodeAdminTuple.organization) + + def _createCryptoMaterial(self,directory , composition, compose_service, network): + self._writeMspFiles(directory , composition, compose_service, network) + self._writeTLSFiles(directory , composition, compose_service, network) + def _writeMspFiles(self, directory , composition, compose_service, network): localMspConfigPath = self.getLocalMspConfigPath(composition, compose_service) os.makedirs("{0}/{1}".format(localMspConfigPath, "signcerts")) @@ -904,6 +948,20 @@ def _writeMspFiles(self, directory , composition, compose_service, network): with open("{0}/keystore/{1}.pem".format(localMspConfigPath, pnt.user), "w") as f: f.write(user.ecdsaSigningKey.to_pem()) + def _writeTLSFiles(self, directory , composition, compose_service, network): + localTLSConfigPath = self.getLocalTLSConfigPath(composition, compose_service) + os.makedirs(localTLSConfigPath) + # Find the peer signer Tuple for this peer and add to signcerts folder + for pnt, cert in [(peerNodeTuple, cert) for peerNodeTuple, cert in directory.ordererAdminTuples.items() if + compose_service in peerNodeTuple.user and "signer" in peerNodeTuple.user.lower()]: + user = directory.getUser(userName=pnt.user) + userTLSCert = directory.getOrganization(pnt.organization).createCertificate(user.createTLSCertRequest(pnt.nodeName)) + (keyPath, certPath) = self.getTLSKeyPaths(pnt=pnt, composition=composition, compose_service=compose_service, pathType=PathType.Local) + with open(keyPath, 'w') as f: + f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, user.rsaSigningKey)) + with open(certPath, 'w') as f: + f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, userTLSCert)) + def _getMspId(self, compose_service, directory): matchingNATs = [nat for nat in directory.getNamedCtxTuples() if ((compose_service in nat.user) and ("Signer" in nat.user) and ((compose_service in nat.nodeName)))] assert len(matchingNATs)==1, "Unexpected number of matching NodeAdminTuples: {0}".format(matchingNATs) @@ -934,10 +992,10 @@ def composing(self, composition, context): directory = getDirectory(context) for ordererService in self.getOrdererList(composition): - self._writeMspFiles(directory=directory, - compose_service=ordererService, - composition=composition, - network=Network.Orderer) + self._createCryptoMaterial(directory=directory, + compose_service=ordererService, + composition=composition, + network=Network.Orderer) def decomposing(self, composition, context): 'Will remove the orderer volume path folder for the context' @@ -951,8 +1009,13 @@ def getEnv(self, composition, context, env): localMspConfigPath = self.getLocalMspConfigPath(composition, ordererService, pathType=PathType.Container) env["{0}_ORDERER_GENERAL_LOCALMSPDIR".format(ordererService.upper())] = localMspConfigPath env["{0}_ORDERER_GENERAL_LOCALMSPID".format(ordererService.upper())] = self._getMspId(compose_service=ordererService, directory=directory) - - + # TLS Settings + (_, pnt) = self._getPathAndUserInfo(directory=directory, composition=composition, compose_service=ordererService, pathType=PathType.Container) + (keyPath, certPath) = self.getTLSKeyPaths(pnt=pnt, composition=composition, compose_service=ordererService, pathType=PathType.Container) + env["{0}_ORDERER_GENERAL_TLS_CERTIFICATE".format(ordererService.upper())] = certPath + env["{0}_ORDERER_GENERAL_TLS_PRIVATEKEY".format(ordererService.upper())] = keyPath + # env["{0}_ORDERER_GENERAL_TLS_ROOTCAS".format(ordererService.upper())] = "[{0}]".format(self.getLocalMspConfigRootCertPath( + # directory=directory, composition=composition, compose_service=ordererService, pathType=PathType.Container)) class PeerCompositionCallback(compose.CompositionCallback, CallbackHelper): 'Responsible for setting up Peer nodes upon composition' @@ -971,10 +1034,10 @@ def composing(self, composition, context): directory = getDirectory(context) for peerService in self.getPeerList(composition): - self._writeMspFiles(directory=directory, - compose_service=peerService, - composition=composition, - network=Network.Peer) + self._createCryptoMaterial(directory=directory, + compose_service=peerService, + composition=composition, + network=Network.Peer) def decomposing(self, composition, context): 'Will remove the orderer volume path folder for the context' @@ -989,10 +1052,14 @@ def getEnv(self, composition, context, env): env["{0}_CORE_PEER_LOCALMSPID".format(peerService.upper())] = self._getMspId(compose_service=peerService, directory=directory) # TLS Settings # env["{0}_CORE_PEER_TLS_ENABLED".format(peerService.upper())] = self._getMspId(compose_service=peerService, directory=directory) - env["{0}_CORE_PEER_TLS_CERT_FILE".format(peerService.upper())] = self.getLocalMspConfigPublicCertPath( - directory=directory, composition=composition, compose_service=peerService, pathType=PathType.Container) - env["{0}_CORE_PEER_TLS_KEY_FILE".format(peerService.upper())] = self.getLocalMspConfigPrivateKeyPath( + (_, pnt) = self._getPathAndUserInfo(directory=directory, composition=composition, compose_service=peerService, pathType=PathType.Container) + (keyPath, certPath) = self.getTLSKeyPaths(pnt=pnt, composition=composition, compose_service=peerService, pathType=PathType.Container) + env["{0}_CORE_PEER_TLS_CERT_FILE".format(peerService.upper())] = certPath + env["{0}_CORE_PEER_TLS_KEY_FILE".format(peerService.upper())] = keyPath + env["{0}_CORE_PEER_TLS_ROOTCERT_FILE".format(peerService.upper())] = self.getLocalMspConfigRootCertPath( directory=directory, composition=composition, compose_service=peerService, pathType=PathType.Container) + env["{0}_CORE_PEER_TLS_SERVERHOSTOVERRIDE".format(peerService.upper())] = peerService + def createChainCreationPolicyNames(context, chainCreationPolicyNames, chaindId): channel = common_dot_configtx_pb2.ConfigGroup() @@ -1034,7 +1101,7 @@ def setOrdererBootstrapGenesisBlock(genesisBlock): 'Responsible for setting the GensisBlock for the Orderer nodes upon composition' -def broadcastCreateChannelConfigTx(context, composeService, chainId, configTxEnvelope, user): +def broadcastCreateChannelConfigTx(context, certAlias, composeService, chainId, configTxEnvelope, user): dataFunc = lambda x: configTxEnvelope user.broadcastMessages(context=context, numMsgsToBroadcast=1, composeService=composeService, chainID=chainId, dataFunc=dataFunc) diff --git a/bddtests/steps/endorser_impl.py b/bddtests/steps/endorser_impl.py index bd7e3becdeb..9b7fd677064 100644 --- a/bddtests/steps/endorser_impl.py +++ b/bddtests/steps/endorser_impl.py @@ -106,8 +106,8 @@ def step_impl(context, userName, certAlias, proposalAlias, channelName, ccSpecAl -@when(u'user "{userName}" sends proposal "{proposalAlias}" to endorsers with timeout of "{timeout}" seconds with proposal responses "{proposalResponsesAlias}"') -def step_impl(context, userName, proposalAlias, timeout, proposalResponsesAlias): +@when(u'user "{userName}" using cert alias "{certAlias}" sends proposal "{proposalAlias}" to endorsers with timeout of "{timeout}" seconds with proposal responses "{proposalResponsesAlias}"') +def step_impl(context, userName, certAlias, proposalAlias, timeout, proposalResponsesAlias): assert 'table' in context, "Expected table of endorsers" directory = bootstrap_util.getDirectory(context=context) user = directory.getUser(userName=userName) @@ -117,7 +117,9 @@ def step_impl(context, userName, proposalAlias, timeout, proposalResponsesAlias) # Send proposal to each specified endorser, waiting 'timeout' seconds for response/error endorsers = [row['Endorser'] for row in context.table.rows] - endorserStubs = endorser_util.getEndorserStubs(context, endorsers) + nodeAdminTuple = user.tags[certAlias] + + endorserStubs = endorser_util.getEndorserStubs(context, composeServices=endorsers, directory=directory, nodeAdminTuple=nodeAdminTuple) proposalResponseFutures = [endorserStub.ProcessProposal.future(signedProposal, int(timeout)) for endorserStub in endorserStubs] resultsDict = dict(zip(endorsers, [respFuture.result() for respFuture in proposalResponseFutures])) user.setTagValue(proposalResponsesAlias, resultsDict) diff --git a/bddtests/steps/endorser_util.py b/bddtests/steps/endorser_util.py index 06009e10be7..899bf4e59c4 100644 --- a/bddtests/steps/endorser_util.py +++ b/bddtests/steps/endorser_util.py @@ -133,11 +133,18 @@ def createInstallChaincodeSpecForBDD(ccDeploymentSpec, chainID): args=['install', ccDeploymentSpec.SerializeToString()]) return lc_chaincode_spec -def getEndorserStubs(context, composeServices): +def getEndorserStubs(context, composeServices, directory, nodeAdminTuple): stubs = [] + user = directory.getUser(nodeAdminTuple.user) + signingOrg = directory.getOrganization(nodeAdminTuple.organization) + for composeService in composeServices: ipAddress = bdd_test_util.ipFromContainerNamePart(composeService, context.compose_containers) - channel = bdd_grpc_util.getGRPCChannel(ipAddress) + # natForPeerSigner = directory.findNodeAdminTuple(userName="{0}Signer".format(composeService), contextName=composeService, orgName="peerOrg0") + # signerCert = directory.getCertAsPEM(natForPeerSigner) + root_certificates = directory.getTrustedRootsForPeerNetworkAsPEM() + channel = bdd_grpc_util.getGRPCChannel(ipAddress=ipAddress, port=7051, root_certificates=root_certificates, + ssl_target_name_override=composeService) newEndorserStub = peer_pb2_grpc.EndorserStub(channel) stubs.append(newEndorserStub) return stubs diff --git a/bddtests/steps/orderer_util.py b/bddtests/steps/orderer_util.py index 4a66dd85084..cea6ed47693 100644 --- a/bddtests/steps/orderer_util.py +++ b/bddtests/steps/orderer_util.py @@ -17,11 +17,13 @@ import time import datetime import Queue -from orderer import ab_pb2 +from orderer import ab_pb2, ab_pb2_grpc from common import common_pb2 import bdd_test_util import bootstrap_util +import bdd_grpc_util + from grpc.beta import implementations from grpc.framework.interfaces.face.face import AbortionError @@ -161,11 +163,12 @@ def closeStreams(self): for compose_service, deliverStreamHelper in self.abDeliversStreamHelperDict.iteritems(): deliverStreamHelper.sendQueue.put(None) - def connectToDeliverFunction(self, context, composeService, cert, nodeAdminTuple, timeout=1): + def connectToDeliverFunction(self, context, composeService, certAlias, nodeAdminTuple, timeout=1): 'Connect to the deliver function and drain messages to associated orderer queue' assert not composeService in self.abDeliversStreamHelperDict, "Already connected to deliver stream on {0}".format(composeService) streamHelper = DeliverStreamHelper(directory=self.directory, - ordererStub=self.getABStubForComposeService(context, composeService), + ordererStub=self.getABStubForComposeService(context=context, + composeService=composeService), entity=self, nodeAdminTuple=nodeAdminTuple) self.abDeliversStreamHelperDict[composeService] = streamHelper return streamHelper @@ -195,8 +198,13 @@ def getABStubForComposeService(self, context, composeService): if composeService in self.atomicBroadcastStubsDict: return self.atomicBroadcastStubsDict[composeService] # Get the IP address of the server that the user registered on - channel = getGRPCChannel(*bdd_test_util.getPortHostMapping(context.compose_containers, composeService, 7050)) - newABStub = ab_pb2.beta_create_AtomicBroadcast_stub(channel) + root_certificates = self.directory.getTrustedRootsForOrdererNetworkAsPEM() + # ipAddress = "{0}:{1}".format(*bdd_test_util.getPortHostMapping(context.compose_containers, composeService, 7050)) + ipAddress = bdd_test_util.ipFromContainerNamePart(composeService, context.compose_containers) + print("ipAddress in getABStubForComposeService == {0}".format(ipAddress)) + channel = bdd_grpc_util.getGRPCChannel(ipAddress=ipAddress, port=7050, root_certificates=root_certificates, ssl_target_name_override=composeService) + # channel = getGRPCChannel(*bdd_test_util.getPortHostMapping(context.compose_containers, composeService, 7050)) + newABStub = ab_pb2_grpc.AtomicBroadcastStub(channel) self.atomicBroadcastStubsDict[composeService] = newABStub return newABStub @@ -263,9 +271,3 @@ def generateBroadcastMessages(chainID = TEST_CHAIN_ID, numToGenerate = 3, timeTo for msg in messages: yield msg time.sleep(timeToHoldOpen) - - -def getGRPCChannel(host='localhost', port=7050): - channel = implementations.insecure_channel(host, port) - print("Returning GRPC for address: {0}:{1}".format(host,port)) - return channel