diff --git a/asset-transfer-basic/chaincode-external/.gitignore b/asset-transfer-basic/chaincode-external/.gitignore index 9db4ee63..438df6cb 100644 --- a/asset-transfer-basic/chaincode-external/.gitignore +++ b/asset-transfer-basic/chaincode-external/.gitignore @@ -1,2 +1,3 @@ *.tar.gz *.tgz +crypto/*.pem diff --git a/asset-transfer-basic/chaincode-external/README.md b/asset-transfer-basic/chaincode-external/README.md index 679064be..de06f89c 100755 --- a/asset-transfer-basic/chaincode-external/README.md +++ b/asset-transfer-basic/chaincode-external/README.md @@ -172,3 +172,65 @@ node app.js ``` If all goes well, the program should run exactly the same as described in the "Writing Your First Application" tutorial. + +## Enabling TLS for chaincode and peer communication + +**Note:** This section uses an example of self-signed certificate. You may use your organization hosted CA to issue the certificate and generate a key for production deployment. + +In the sample so far, you connected both peers in `test-network` to the single instance of chaincode server. However, if you would like to enable TLS between the peer nodes and the chaincode server, each peer node needs to have its own CA certificate. Enabling TLS is made possible at runtime in the chaincode. + +- As a first step generate a keypair that can be used. Run these commands from the `fabric-samples/asset-transfer-basic/chaincode-external` directory. + +*Find instructions to install `openssl` in [openssl.org](https://www.openssl.org/)* + +For `org1.example.com` + +``` +openssl req -nodes -x509 -newkey rsa:4096 -keyout crypto/key1.pem -out crypto/cert1.pem -subj "/C=IN/ST=KA/L=Bangalore/O=example Inc/OU=Developer/CN=asset-transfer-basic.org1.example.com/emailAddress=dev@asset-transfer-basic.org1.example.com" +``` + +For `org2.example.com` + +``` +openssl req -nodes -x509 -newkey rsa:4096 -keyout crypto/key2.pem -out crypto/cert2.pem -subj "/C=IN/ST=KA/L=Bangalore/O=example Inc/OU=Developer/CN=asset-transfer-basic.org2.example.com/emailAddress=dev@asset-transfer-basic.org2.example.com" +``` + +- Copy the CA file contents for both `org1.example.com` & `org2.example.com` + +``` +cp ../../test-network/organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem crypto/rootcert1.pem +cp ../../test-network/organizations/peerOrganizations/org2.example.com/ca/ca.org2.example.com-cert.pem crypto/rootcert2.pem +``` + +- Generate a client key and cert for auth purpose. You need a key and cert generated from the CA of each organization. Peer nodes act as clients to chaincode server. + +- Change the `connection.json` with the below contents. The `root_cert` parameter is the root CA certificate which the chaincode server is run with. You may run the below commands to get the certificate file contents as strings and copy them when needed. + +``` +awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' crypto/cert1.pem +awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' crypto/cert2.pem +``` + +Similarly, replace the `client_key` and the `client_cert` contents with the values from the previous step. + +``` +{ + "address": "asset-transfer-basic.org1.example.com:9999", + "dial_timeout": "10s", + "tls_required": true, + "client_auth_required": true, + "client_key": "-----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY-----", + "client_cert": "-----BEGIN CERTIFICATE---- ... -----END CERTIFICATE-----", + "root_cert": "-----BEGIN CERTIFICATE---- ... -----END CERTIFICATE-----" +} +``` + +- Follow the instructions in [Package](#packaging-and-installing-chaincode) and [Install](#installing-the-external-chaincode) steps for each organization. Remember that the chaincode server's address for the second organization is `asset-transfer-basic.org2.example.com:9999`. + +- Copy the appropriate `CHAINCODE_ID` to both [chaincode1.env](./chaincode1.env) and [chaincode2.env](./chaincode2.env) files. Bring up the chaincode containers using the docker-compose command below + +``` +docker-compose up -f docker-compose-chaincode.yaml up --build -d +``` + +- Follow the instructions in [Finish Deployment](#finish-deploying-the-asset-transfer-basic-external-chaincode-) for each organization seperately. diff --git a/asset-transfer-basic/chaincode-external/assetTransfer.go b/asset-transfer-basic/chaincode-external/assetTransfer.go index d66e0735..ab2b8ecd 100644 --- a/asset-transfer-basic/chaincode-external/assetTransfer.go +++ b/asset-transfer-basic/chaincode-external/assetTransfer.go @@ -7,8 +7,10 @@ package main import ( "encoding/json" "fmt" + "io/ioutil" "log" "os" + "strconv" "github.com/hyperledger/fabric-chaincode-go/shim" "github.com/hyperledger/fabric-contract-api-go/contractapi" @@ -224,12 +226,66 @@ func main() { CCID: config.CCID, Address: config.Address, CC: chaincode, - TLSProps: shim.TLSProperties{ - Disabled: true, - }, + TLSProps: getTLSProperties(), } if err := server.Start(); err != nil { log.Panicf("error starting asset-transfer-basic chaincode: %s", err) } } + +func getTLSProperties() shim.TLSProperties { + // Check if chaincode is TLS enabled + tlsDisabledStr := getEnvOrDefault("CHAINCODE_TLS_DISABLED", "true") + key := getEnvOrDefault("CHAINCODE_TLS_KEY", "") + cert := getEnvOrDefault("CHAINCODE_TLS_CERT", "") + clientCACert := getEnvOrDefault("CHAINCODE_CLIENT_CA_CERT", "") + + // convert tlsDisabledStr to boolean + tlsDisabled := getBoolOrDefault(tlsDisabledStr, false) + var keyBytes, certBytes, clientCACertBytes []byte + var err error + + if !tlsDisabled { + keyBytes, err = ioutil.ReadFile(key) + if err != nil { + log.Panicf("error while reading the crypto file: %s", err) + } + certBytes, err = ioutil.ReadFile(cert) + if err != nil { + log.Panicf("error while reading the crypto file: %s", err) + } + } + // Did not request for the peer cert verification + if clientCACert != "" { + clientCACertBytes, err = ioutil.ReadFile(clientCACert) + if err != nil { + log.Panicf("error while reading the crypto file: %s", err) + } + } + + return shim.TLSProperties{ + Disabled: tlsDisabled, + Key: keyBytes, + Cert: certBytes, + ClientCACerts: clientCACertBytes, + } +} + +func getEnvOrDefault(env, defaultVal string) string { + value, ok := os.LookupEnv(env) + if !ok { + value = defaultVal + } + return value +} + +// Note that the method returns default value if the string +// cannot be parsed! +func getBoolOrDefault(value string, defaultVal bool) bool { + parsed, err := strconv.ParseBool(value) + if err!= nil { + return defaultVal + } + return parsed +} diff --git a/asset-transfer-basic/chaincode-external/chaincode.env b/asset-transfer-basic/chaincode-external/chaincode.env index d029f584..3daa988c 100644 --- a/asset-transfer-basic/chaincode-external/chaincode.env +++ b/asset-transfer-basic/chaincode-external/chaincode.env @@ -6,3 +6,19 @@ CHAINCODE_SERVER_ADDRESS=asset-transfer-basic.org1.example.com:9999 # on install. The `peer lifecycle chaincode queryinstalled` command can be # used to get the ID after install if required CHAINCODE_ID=basic_1.0:0262396ccaffaa2174bc09f750f742319c4f14d60b16334d2c8921b6842c090c + +# Optional parameters that will be used for TLS connection between peer node +# and the chaincode. +# TLS is disabled by default, uncomment the following line to enable TLS connection +# CHAINCODE_TLS_DISABLED=false + +# Following variables will be ignored if TLS is not enabled. +# They need to be in PEM format +# CHAINCODE_TLS_KEY=/path/to/private/key/file +# CHAINCODE_TLS_CERT=/path/to/public/cert/file + +# The following variable will be used by the chaincode server to verify the +# connection from the peer node. +# Note that when this is set a single chaincode server cannot be shared +# across organizations unless their root CA is same. +# CHAINCODE_CLIENT_CA_CERT=/path/to/peer/organization/root/ca/cert/file diff --git a/asset-transfer-basic/chaincode-external/chaincode1.env b/asset-transfer-basic/chaincode-external/chaincode1.env new file mode 100644 index 00000000..26319adb --- /dev/null +++ b/asset-transfer-basic/chaincode-external/chaincode1.env @@ -0,0 +1,24 @@ +# CHAINCODE_SERVER_ADDRESS must be set to the host and port where the peer can +# connect to the chaincode server +CHAINCODE_SERVER_ADDRESS=asset-transfer-basic.org1.example.com:9999 + +# CHAINCODE_ID must be set to the Package ID that is assigned to the chaincode +# on install. The `peer lifecycle chaincode queryinstalled` command can be +# used to get the ID after install if required +CHAINCODE_ID=basic_1.0:6726c6b6d8ff66fcf5710b72c6ce512d24f118c51c3de510b3d43e51fa592a7d + +# Optional parameters that will be used for TLS connection between peer node +# and the chaincode. +# TLS is disabled by default, uncomment the following line to enable TLS connection +CHAINCODE_TLS_DISABLED=false + +# Following variables will be ignored if TLS is not enabled. +# They need to be in PEM format +CHAINCODE_TLS_KEY=/crypto/key1.pem +CHAINCODE_TLS_CERT=/crypto/cert1.pem + +# The following variable will be used by the chaincode server to verify the +# connection from the peer node. +# Note that when this is set a single chaincode server cannot be shared +# across organizations unless their root CA is same. +CHAINCODE_CLIENT_CA_CERT=/crypto/rootcert1.pem diff --git a/asset-transfer-basic/chaincode-external/chaincode2.env b/asset-transfer-basic/chaincode-external/chaincode2.env new file mode 100644 index 00000000..7884f24a --- /dev/null +++ b/asset-transfer-basic/chaincode-external/chaincode2.env @@ -0,0 +1,24 @@ +# CHAINCODE_SERVER_ADDRESS must be set to the host and port where the peer can +# connect to the chaincode server +CHAINCODE_SERVER_ADDRESS=asset-transfer-basic.org2.example.com:9999 + +# CHAINCODE_ID must be set to the Package ID that is assigned to the chaincode +# on install. The `peer lifecycle chaincode queryinstalled` command can be +# used to get the ID after install if required +CHAINCODE_ID=basic_1.0:e8f9052385e3763ecf5635591155da05d8efbb6905ccbfc1c7229eb6bd28df1b + +# Optional parameters that will be used for TLS connection between peer node +# and the chaincode. +# TLS is disabled by default, uncomment the following line to enable TLS connection +CHAINCODE_TLS_DISABLED=false + +# Following variables will be ignored if TLS is not enabled. +# They need to be in PEM format +CHAINCODE_TLS_KEY=/crypto/key2.pem +CHAINCODE_TLS_CERT=/crypto/cert2.pem + +# The following variable will be used by the chaincode server to verify the +# connection from the peer node. +# Note that when this is set a single chaincode server cannot be shared +# across organizations unless their root CA is same. +CHAINCODE_CLIENT_CA_CERT=/crypto/rootcert2.pem diff --git a/asset-transfer-basic/chaincode-external/crypto/.gitkeep b/asset-transfer-basic/chaincode-external/crypto/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/asset-transfer-basic/chaincode-external/docker-compose-chaincode.yaml b/asset-transfer-basic/chaincode-external/docker-compose-chaincode.yaml new file mode 100644 index 00000000..f7aa6f29 --- /dev/null +++ b/asset-transfer-basic/chaincode-external/docker-compose-chaincode.yaml @@ -0,0 +1,32 @@ +version: "3.6" + +networks: + docker_test: + external: true + +services: + asset-transfer-basic.org1.example.com: + build: . + container_name: asset-transfer-basic.org1.example.com + hostname: asset-transfer-basic.org1.example.com + volumes: + - ./crypto:/crypto + env_file: + - chaincode1.env + networks: + docker_test: + expose: + - 9999 + + asset-transfer-basic.org2.example.com: + build: . + container_name: asset-transfer-basic.org2.example.com + hostname: asset-transfer-basic.org2.example.com + volumes: + - ./crypto:/crypto + env_file: + - chaincode2.env + networks: + docker_test: + expose: + - 9999