From 3c63eac4e303e8348c42637d7ed337727645b9ce Mon Sep 17 00:00:00 2001 From: Chris Elder Date: Thu, 23 May 2024 11:08:06 -0400 Subject: [PATCH] Add CA capability to test-network-nano-bash This change adds: - shell scripts to start CAs for each org - optional flag (-c) to network.sh to start CAs - generate crypto material in the same format as cryptogen using the CAs - describe how to start the CAs using terminals Signed-off-by: Chris Elder --- test-network-nano-bash/.gitignore | 1 + test-network-nano-bash/README.md | 15 + .../ca_config/ca/fabric-ca-server-config.yaml | 511 ++++++++++++++++++ .../tlsca/fabric-ca-server-config.yaml | 494 +++++++++++++++++ test-network-nano-bash/ca/ca_utils.sh | 117 ++++ test-network-nano-bash/ca/config.yaml | 14 + .../ca/createEnrollments.sh | 118 ++++ test-network-nano-bash/ca_terminal_setup.png | Bin 0 -> 61294 bytes test-network-nano-bash/generate_artifacts.sh | 43 +- test-network-nano-bash/network.sh | 30 +- test-network-nano-bash/ordererca.sh | 39 ++ test-network-nano-bash/org1ca.sh | 39 ++ test-network-nano-bash/org2ca.sh | 39 ++ 13 files changed, 1451 insertions(+), 9 deletions(-) create mode 100644 test-network-nano-bash/ca/ca_config/ca/fabric-ca-server-config.yaml create mode 100644 test-network-nano-bash/ca/ca_config/tlsca/fabric-ca-server-config.yaml create mode 100755 test-network-nano-bash/ca/ca_utils.sh create mode 100644 test-network-nano-bash/ca/config.yaml create mode 100755 test-network-nano-bash/ca/createEnrollments.sh create mode 100644 test-network-nano-bash/ca_terminal_setup.png create mode 100755 test-network-nano-bash/ordererca.sh create mode 100755 test-network-nano-bash/org1ca.sh create mode 100755 test-network-nano-bash/org2ca.sh diff --git a/test-network-nano-bash/.gitignore b/test-network-nano-bash/.gitignore index ad2d6f784f..2dc34f0328 100644 --- a/test-network-nano-bash/.gitignore +++ b/test-network-nano-bash/.gitignore @@ -1,6 +1,7 @@ channel-artifacts/ crypto-config/ data/ +data_ca/ logs/ *.gz chaincode-external/ diff --git a/test-network-nano-bash/README.md b/test-network-nano-bash/README.md index 7d276eccf8..8c8540c028 100644 --- a/test-network-nano-bash/README.md +++ b/test-network-nano-bash/README.md @@ -59,6 +59,16 @@ If you have trouble running bash scripts in your environment, you can just as ea Note the syntax of running the scripts. The peer admin scripts set the admin environment variables and must be run with the `source` command in order that the exported environment variables can be utilized by any subsequent user commands. +## Running each component separately with CAs + +These instructions are for running the CAs from terminal sessions. Open terminal windows for 3 CAs as seen in the following terminal setup. These instructions should be followd before opening the ordering and peer windows described above. + +![CA Terminal setup](ca_terminal_setup.png) + +- cd to the `test-network-nano-bash` directory in each terminal window +- Before running the `./generate_artifacts.sh -ca` in the first orderer terminal, run `./ordererca.sh`, `./org1ca.sh`, `./org2ca.sh` in the repsective terminals. +- In the first orderer terminal, run `./generate_artifacts.sh -ca` to generate crypto material using the CAs and application channel genesis block and configuration transactions (calls configtxgen). The artifacts will be created in the `crypto-config` and `channel-artifacts` directories. If you are running BFT consensus then run `./generate_artifacts.sh BFT -ca`. All artifacts generated with the CA will conform to the same directory structure as cryptogen. + ## Starting the network with one command Using the individual scripts above gives you more control of the process of starting a Fabric network and demonstrates how all the required components fit together, however the same network can also be started using a single script for convenience. @@ -73,6 +83,11 @@ For BFT consensus type: ./network.sh start -o BFT ``` +For Raft consensus type using CAs: +```shell +./network.sh start -ca +``` + After the network has started, use separate terminals to run peer commands. You will need to configure the peer environment for each new terminal. For example to run against peer1, use: diff --git a/test-network-nano-bash/ca/ca_config/ca/fabric-ca-server-config.yaml b/test-network-nano-bash/ca/ca_config/ca/fabric-ca-server-config.yaml new file mode 100644 index 0000000000..51291b5a39 --- /dev/null +++ b/test-network-nano-bash/ca/ca_config/ca/fabric-ca-server-config.yaml @@ -0,0 +1,511 @@ +############################################################################# +# This is a configuration file for the fabric-ca-server command. +# +# COMMAND LINE ARGUMENTS AND ENVIRONMENT VARIABLES +# ------------------------------------------------ +# Each configuration element can be overridden via command line +# arguments or environment variables. The precedence for determining +# the value of each element is as follows: +# 1) command line argument +# Examples: +# a) --port 443 +# To set the listening port +# b) --ca.keyfile ../mykey.pem +# To set the "keyfile" element in the "ca" section below; +# note the '.' separator character. +# 2) environment variable +# Examples: +# a) FABRIC_CA_SERVER_PORT=443 +# To set the listening port +# b) FABRIC_CA_SERVER_CA_KEYFILE="../mykey.pem" +# To set the "keyfile" element in the "ca" section below; +# note the '_' separator character. +# 3) configuration file +# 4) default value (if there is one) +# All default values are shown beside each element below. +# +# FILE NAME ELEMENTS +# ------------------ +# The value of all fields whose name ends with "file" or "files" are +# name or names of other files. +# For example, see "tls.certfile" and "tls.clientauth.certfiles". +# The value of each of these fields can be a simple filename, a +# relative path, or an absolute path. If the value is not an +# absolute path, it is interpreted as being relative to the location +# of this configuration file. +# +############################################################################# + +# Version of config file +version: v1.5.9 + +# Server's listening port (default: 7054) +port: 7052 + +# Cross-Origin Resource Sharing (CORS) +cors: + enabled: false + origins: + - "*" + +# Enables debug logging (default: false) +debug: false + +# Size limit of an acceptable CRL in bytes (default: 512000) +crlsizelimit: 512000 + +############################################################################# +# TLS section for the server's listening port +# +# The following types are supported for client authentication: NoClientCert, +# RequestClientCert, RequireAnyClientCert, VerifyClientCertIfGiven, +# and RequireAndVerifyClientCert. +# +# Certfiles is a list of root certificate authorities that the server uses +# when verifying client certificates. +############################################################################# +tls: + # Enable TLS (default: false) + enabled: true + # TLS for the server's listening port + certfile: + keyfile: + clientauth: + type: noclientcert + certfiles: + +############################################################################# +# The CA section contains information related to the Certificate Authority +# including the name of the CA, which should be unique for all members +# of a blockchain network. It also includes the key and certificate files +# used when issuing enrollment certificates (ECerts). +# The chainfile (if it exists) contains the certificate chain which +# should be trusted for this CA, where the 1st in the chain is always the +# root CA certificate. +############################################################################# +ca: + # Name of this CA + name: ca + # Key file (is only used to import a private key into BCCSP) + keyfile: + # Certificate file (default: ca-cert.pem) + certfile: + # Chain file + chainfile: + # Ignore Certificate Expiration in the case of re-enroll + reenrollIgnoreCertExpiry: false + +############################################################################# +# The gencrl REST endpoint is used to generate a CRL that contains revoked +# certificates. This section contains configuration options that are used +# during gencrl request processing. +############################################################################# +crl: + # Specifies expiration for the generated CRL. The number of hours + # specified by this property is added to the UTC time, the resulting time + # is used to set the 'Next Update' date of the CRL. + expiry: 24h + +############################################################################# +# The registry section controls how the fabric-ca-server does two things: +# 1) authenticates enrollment requests which contain a username and password +# (also known as an enrollment ID and secret). +# 2) once authenticated, retrieves the identity's attribute names and values. +# These attributes are useful for making access control decisions in +# chaincode. +# There are two main configuration options: +# 1) The fabric-ca-server is the registry. +# This is true if "ldap.enabled" in the ldap section below is false. +# 2) An LDAP server is the registry, in which case the fabric-ca-server +# calls the LDAP server to perform these tasks. +# This is true if "ldap.enabled" in the ldap section below is true, +# which means this "registry" section is ignored. +############################################################################# +registry: + # Maximum number of times a password/secret can be reused for enrollment + # (default: -1, which means there is no limit) + maxenrollments: -1 + + # Contains identity information which is used when LDAP is disabled + identities: + - name: admin + pass: adminpw + type: client + affiliation: "" + attrs: + hf.Registrar.Roles: "*" + hf.Registrar.DelegateRoles: "*" + hf.Revoker: true + hf.IntermediateCA: true + hf.GenCRL: true + hf.Registrar.Attributes: "*" + hf.AffiliationMgr: true + +############################################################################# +# Database section +# Supported types are: "sqlite3", "postgres", and "mysql". +# The datasource value depends on the type. +# If the type is "sqlite3", the datasource value is a file name to use +# as the database store. Since "sqlite3" is an embedded database, it +# may not be used if you want to run the fabric-ca-server in a cluster. +# To run the fabric-ca-server in a cluster, you must choose "postgres" +# or "mysql". +############################################################################# +db: + type: sqlite3 + datasource: ../db/fabric-ca-server.db + tls: + enabled: false + certfiles: + client: + certfile: + keyfile: + +############################################################################# +# LDAP section +# If LDAP is enabled, the fabric-ca-server calls LDAP to: +# 1) authenticate enrollment ID and secret (i.e. username and password) +# for enrollment requests; +# 2) To retrieve identity attributes +############################################################################# +ldap: + # Enables or disables the LDAP client (default: false) + # If this is set to true, the "registry" section is ignored. + enabled: false + # The URL of the LDAP server + url: ldap://:@:/ + # TLS configuration for the client connection to the LDAP server + tls: + certfiles: + client: + certfile: + keyfile: + # Attribute related configuration for mapping from LDAP entries to Fabric CA attributes + attribute: + # 'names' is an array of strings containing the LDAP attribute names which are + # requested from the LDAP server for an LDAP identity's entry + names: ['uid','member'] + # The 'converters' section is used to convert an LDAP entry to the value of + # a fabric CA attribute. + # For example, the following converts an LDAP 'uid' attribute + # whose value begins with 'revoker' to a fabric CA attribute + # named "hf.Revoker" with a value of "true" (because the boolean expression + # evaluates to true). + # converters: + # - name: hf.Revoker + # value: attr("uid") =~ "revoker*" + converters: + - name: + value: + # The 'maps' section contains named maps which may be referenced by the 'map' + # function in the 'converters' section to map LDAP responses to arbitrary values. + # For example, assume a user has an LDAP attribute named 'member' which has multiple + # values which are each a distinguished name (i.e. a DN). For simplicity, assume the + # values of the 'member' attribute are 'dn1', 'dn2', and 'dn3'. + # Further assume the following configuration. + # converters: + # - name: hf.Registrar.Roles + # value: map(attr("member"),"groups") + # maps: + # groups: + # - name: dn1 + # value: peer + # - name: dn2 + # value: client + # The value of the user's 'hf.Registrar.Roles' attribute is then computed to be + # "peer,client,dn3". This is because the value of 'attr("member")' is + # "dn1,dn2,dn3", and the call to 'map' with a 2nd argument of + # "group" replaces "dn1" with "peer" and "dn2" with "client". + maps: + groups: + - name: + value: + +############################################################################# +# Affiliations section. Fabric CA server can be bootstrapped with the +# affiliations specified in this section. Affiliations are specified as maps. +# For example: +# businessunit1: +# department1: +# - team1 +# businessunit2: +# - department2 +# - department3 +# +# Affiliations are hierarchical in nature. In the above example, +# department1 (used as businessunit1.department1) is the child of businessunit1. +# team1 (used as businessunit1.department1.team1) is the child of department1. +# department2 (used as businessunit2.department2) and department3 (businessunit2.department3) +# are children of businessunit2. +# Note: Affiliations are case sensitive except for the non-leaf affiliations +# (like businessunit1, department1, businessunit2) that are specified in the configuration file, +# which are always stored in lower case. +############################################################################# +affiliations: + org1: + - department1 + - department2 + org2: + - department1 + +############################################################################# +# Signing section +# +# The "default" subsection is used to sign enrollment certificates; +# the default expiration ("expiry" field) is "8760h", which is 1 year in hours. +# +# The "ca" profile subsection is used to sign intermediate CA certificates; +# the default expiration ("expiry" field) is "43800h" which is 5 years in hours. +# Note that "isca" is true, meaning that it issues a CA certificate. +# A maxpathlen of 0 means that the intermediate CA cannot issue other +# intermediate CA certificates, though it can still issue end entity certificates. +# (See RFC 5280, section 4.2.1.9) +# +# The "tls" profile subsection is used to sign TLS certificate requests; +# the default expiration ("expiry" field) is "8760h", which is 1 year in hours. +############################################################################# +signing: + default: + usage: + - digital signature + expiry: 8760h + profiles: + ca: + usage: + - cert sign + - crl sign + expiry: 43800h + caconstraint: + isca: true + maxpathlen: 0 + tls: + usage: + - signing + - key encipherment + - server auth + - client auth + - key agreement + expiry: 8760h + +########################################################################### +# Certificate Signing Request (CSR) section. +# This controls the creation of the root CA certificate. +# The expiration for the root CA certificate is configured with the +# "ca.expiry" field below, whose default value is "131400h" which is +# 15 years in hours. +# The pathlength field is used to limit CA certificate hierarchy as described +# in section 4.2.1.9 of RFC 5280. +# Examples: +# 1) No pathlength value means no limit is requested. +# 2) pathlength == 1 means a limit of 1 is requested which is the default for +# a root CA. This means the root CA can issue intermediate CA certificates, +# but these intermediate CAs may not in turn issue other CA certificates +# though they can still issue end entity certificates. +# 3) pathlength == 0 means a limit of 0 is requested; +# this is the default for an intermediate CA, which means it can not issue +# CA certificates though it can still issue end entity certificates. +# The "hosts" field will be used to specify Subject Alternative Names +# if the server creates a self-signed TLS certificate. +########################################################################### +csr: + cn: fabric-ca-server + keyrequest: + algo: ecdsa + size: 256 + names: + - C: US + ST: "North Carolina" + L: + O: Hyperledger + OU: Fabric + hosts: + - chriss-mbp.raleigh.ibm.com + - localhost + ca: + expiry: 131400h + pathlength: 1 + +########################################################################### +# Each CA can issue both X509 enrollment certificate as well as Idemix +# Credential. This section specifies configuration for the issuer component +# that is responsible for issuing Idemix credentials. +########################################################################### +idemix: + # Specifies pool size for revocation handles. A revocation handle is an unique identifier of an + # Idemix credential. The issuer will create a pool revocation handles of this specified size. When + # a credential is requested, issuer will get handle from the pool and assign it to the credential. + # Issuer will repopulate the pool with new handles when the last handle in the pool is used. + # A revocation handle and credential revocation information (CRI) are used to create non revocation proof + # by the prover to prove to the verifier that her credential is not revoked. + rhpoolsize: 1000 + + # The Idemix credential issuance is a two step process. First step is to get a nonce from the issuer + # and second step is send credential request that is constructed using the nonce to the isuser to + # request a credential. This configuration property specifies expiration for the nonces. By default is + # nonces expire after 15 seconds. The value is expressed in the time.Duration format (see https://golang.org/pkg/time/#ParseDuration). + nonceexpiration: 15s + + # Specifies interval at which expired nonces are removed from datastore. Default value is 15 minutes. + # The value is expressed in the time.Duration format (see https://golang.org/pkg/time/#ParseDuration) + noncesweepinterval: 15m + + # Specifies the Elliptic Curve used by Identity Mixer. + # It can be any of: {"amcl.Fp256bn", "gurvy.Bn254", "amcl.Fp256Miraclbn"}. + # If unspecified, it defaults to 'amcl.Fp256bn'. + curve: amcl.Fp256bn + +############################################################################# +# BCCSP (BlockChain Crypto Service Provider) section is used to select which +# crypto library implementation to use +############################################################################# +bccsp: + default: SW + sw: + hash: SHA2 + security: 256 + filekeystore: + # The directory used for the software file-based keystore + keystore: msp/keystore + +############################################################################# +# Multi CA section +# +# Each Fabric CA server contains one CA by default. This section is used +# to configure multiple CAs in a single server. +# +# 1) --cacount +# Automatically generate non-default CAs. The names of these +# additional CAs are "ca1", "ca2", ... "caN", where "N" is +# This is particularly useful in a development environment to quickly set up +# multiple CAs. Note that, this config option is not applicable to intermediate CA server +# i.e., Fabric CA server that is started with intermediate.parentserver.url config +# option (-u command line option) +# +# 2) --cafiles +# For each CA config file in the list, generate a separate signing CA. Each CA +# config file in this list MAY contain all of the same elements as are found in +# the server config file except port, debug, and tls sections. +# +# Examples: +# fabric-ca-server start -b admin:adminpw --cacount 2 +# +# fabric-ca-server start -b admin:adminpw --cafiles ca/ca1/fabric-ca-server-config.yaml +# --cafiles ca/ca2/fabric-ca-server-config.yaml +# +############################################################################# + +cacount: + +cafiles: +- ../tlsca/fabric-ca-server-config.yaml + +############################################################################# +# Intermediate CA section +# +# The relationship between servers and CAs is as follows: +# 1) A single server process may contain or function as one or more CAs. +# This is configured by the "Multi CA section" above. +# 2) Each CA is either a root CA or an intermediate CA. +# 3) Each intermediate CA has a parent CA which is either a root CA or another intermediate CA. +# +# This section pertains to configuration of #2 and #3. +# If the "intermediate.parentserver.url" property is set, +# then this is an intermediate CA with the specified parent +# CA. +# +# parentserver section +# url - The URL of the parent server +# caname - Name of the CA to enroll within the server +# +# enrollment section used to enroll intermediate CA with parent CA +# profile - Name of the signing profile to use in issuing the certificate +# label - Label to use in HSM operations +# +# tls section for secure socket connection +# certfiles - PEM-encoded list of trusted root certificate files +# client: +# certfile - PEM-encoded certificate file for when client authentication +# is enabled on server +# keyfile - PEM-encoded key file for when client authentication +# is enabled on server +############################################################################# +intermediate: + parentserver: + url: + caname: + + enrollment: + hosts: + profile: + label: + + tls: + certfiles: + client: + certfile: + keyfile: + +############################################################################# +# CA configuration section +# +# Configure the number of incorrect password attempts are allowed for +# identities. By default, the value of 'passwordattempts' is 10, which +# means that 10 incorrect password attempts can be made before an identity get +# locked out. +############################################################################# +cfg: + identities: + passwordattempts: 10 + +############################################################################### +# +# Operations section +# +############################################################################### +operations: + # host and port for the operations server + listenAddress: 127.0.0.1:9443 + + # TLS configuration for the operations endpoint + tls: + # TLS enabled + enabled: false + + # path to PEM encoded server certificate for the operations server + cert: + file: + + # path to PEM encoded server key for the operations server + key: + file: + + # require client certificate authentication to access all resources + clientAuthRequired: false + + # paths to PEM encoded ca certificates to trust for client authentication + clientRootCAs: + files: [] + +############################################################################### +# +# Metrics section +# +############################################################################### +metrics: + # statsd, prometheus, or disabled + provider: disabled + + # statsd configuration + statsd: + # network type: tcp or udp + network: udp + + # statsd server address + address: 127.0.0.1:8125 + + # the interval at which locally cached counters and gauges are pushed + # to statsd; timings are pushed immediately + writeInterval: 10s + + # prefix is prepended to all emitted statsd metrics + prefix: server diff --git a/test-network-nano-bash/ca/ca_config/tlsca/fabric-ca-server-config.yaml b/test-network-nano-bash/ca/ca_config/tlsca/fabric-ca-server-config.yaml new file mode 100644 index 0000000000..2c4b0489aa --- /dev/null +++ b/test-network-nano-bash/ca/ca_config/tlsca/fabric-ca-server-config.yaml @@ -0,0 +1,494 @@ +############################################################################# +# This is a configuration file for the fabric-ca-server command. +# +# COMMAND LINE ARGUMENTS AND ENVIRONMENT VARIABLES +# ------------------------------------------------ +# Each configuration element can be overridden via command line +# arguments or environment variables. The precedence for determining +# the value of each element is as follows: +# 1) command line argument +# Examples: +# a) --port 443 +# To set the listening port +# b) --ca.keyfile ../mykey.pem +# To set the "keyfile" element in the "ca" section below; +# note the '.' separator character. +# 2) environment variable +# Examples: +# a) FABRIC_CA_SERVER_PORT=443 +# To set the listening port +# b) FABRIC_CA_SERVER_CA_KEYFILE="../mykey.pem" +# To set the "keyfile" element in the "ca" section below; +# note the '_' separator character. +# 3) configuration file +# 4) default value (if there is one) +# All default values are shown beside each element below. +# +# FILE NAME ELEMENTS +# ------------------ +# The value of all fields whose name ends with "file" or "files" are +# name or names of other files. +# For example, see "tls.certfile" and "tls.clientauth.certfiles". +# The value of each of these fields can be a simple filename, a +# relative path, or an absolute path. If the value is not an +# absolute path, it is interpretted as being relative to the location +# of this configuration file. +# +############################################################################# + +# Version of config file +version: 1.4.9 + +# Server's listening port (default: 7054) +port: 7054 + +# Cross-Origin Resource Sharing (CORS) +cors: + enabled: false + origins: + - "*" + +# Enables debug logging (default: false) +debug: false + +# Size limit of an acceptable CRL in bytes (default: 512000) +crlsizelimit: 512000 + +############################################################################# +# TLS section for the server's listening port +# +# The following types are supported for client authentication: NoClientCert, +# RequestClientCert, RequireAnyClientCert, VerifyClientCertIfGiven, +# and RequireAndVerifyClientCert. +# +# Certfiles is a list of root certificate authorities that the server uses +# when verifying client certificates. +############################################################################# +tls: + # Enable TLS (default: false) + enabled: true + # TLS for the server's listening port + certfile: + keyfile: + clientauth: + type: noclientcert + certfiles: + +############################################################################# +# The CA section contains information related to the Certificate Authority +# including the name of the CA, which should be unique for all members +# of a blockchain network. It also includes the key and certificate files +# used when issuing enrollment certificates (ECerts) and transaction +# certificates (TCerts). +# The chainfile (if it exists) contains the certificate chain which +# should be trusted for this CA, where the 1st in the chain is always the +# root CA certificate. +############################################################################# +ca: + # Name of this CA + name: tlsca + # Key file (is only used to import a private key into BCCSP) + keyfile: + # Certificate file (default: ca-cert.pem) + certfile: + # Chain file + chainfile: + +############################################################################# +# The gencrl REST endpoint is used to generate a CRL that contains revoked +# certificates. This section contains configuration options that are used +# during gencrl request processing. +############################################################################# +crl: + # Specifies expiration for the generated CRL. The number of hours + # specified by this property is added to the UTC time, the resulting time + # is used to set the 'Next Update' date of the CRL. + expiry: 24h + +############################################################################# +# The registry section controls how the fabric-ca-server does two things: +# 1) authenticates enrollment requests which contain a username and password +# (also known as an enrollment ID and secret). +# 2) once authenticated, retrieves the identity's attribute names and +# values which the fabric-ca-server optionally puts into TCerts +# which it issues for transacting on the Hyperledger Fabric blockchain. +# These attributes are useful for making access control decisions in +# chaincode. +# There are two main configuration options: +# 1) The fabric-ca-server is the registry. +# This is true if "ldap.enabled" in the ldap section below is false. +# 2) An LDAP server is the registry, in which case the fabric-ca-server +# calls the LDAP server to perform these tasks. +# This is true if "ldap.enabled" in the ldap section below is true, +# which means this "registry" section is ignored. +############################################################################# +registry: + # Maximum number of times a password/secret can be reused for enrollment + # (default: -1, which means there is no limit) + maxenrollments: -1 + + # Contains identity information which is used when LDAP is disabled + identities: + - name: admin + pass: adminpw + type: client + affiliation: "" + attrs: + hf.Registrar.Roles: "*" + hf.Registrar.DelegateRoles: "*" + hf.Revoker: true + hf.IntermediateCA: true + hf.GenCRL: true + hf.Registrar.Attributes: "*" + hf.AffiliationMgr: true + +############################################################################# +# Database section +# Supported types are: "sqlite3", "postgres", and "mysql". +# The datasource value depends on the type. +# If the type is "sqlite3", the datasource value is a file name to use +# as the database store. Since "sqlite3" is an embedded database, it +# may not be used if you want to run the fabric-ca-server in a cluster. +# To run the fabric-ca-server in a cluster, you must choose "postgres" +# or "mysql". +############################################################################# +db: + type: sqlite3 + datasource: ../db/fabric-ca-server.db + tls: + enabled: false + certfiles: + client: + certfile: + keyfile: + +############################################################################# +# LDAP section +# If LDAP is enabled, the fabric-ca-server calls LDAP to: +# 1) authenticate enrollment ID and secret (i.e. username and password) +# for enrollment requests; +# 2) To retrieve identity attributes +############################################################################# +ldap: + # Enables or disables the LDAP client (default: false) + # If this is set to true, the "registry" section is ignored. + enabled: false + # The URL of the LDAP server + url: ldap://:@:/ + # TLS configuration for the client connection to the LDAP server + tls: + certfiles: + client: + certfile: + keyfile: + # Attribute related configuration for mapping from LDAP entries to Fabric CA attributes + attribute: + # 'names' is an array of strings containing the LDAP attribute names which are + # requested from the LDAP server for an LDAP identity's entry + names: ['uid','member'] + # The 'converters' section is used to convert an LDAP entry to the value of + # a fabric CA attribute. + # For example, the following converts an LDAP 'uid' attribute + # whose value begins with 'revoker' to a fabric CA attribute + # named "hf.Revoker" with a value of "true" (because the boolean expression + # evaluates to true). + # converters: + # - name: hf.Revoker + # value: attr("uid") =~ "revoker*" + converters: + - name: + value: + # The 'maps' section contains named maps which may be referenced by the 'map' + # function in the 'converters' section to map LDAP responses to arbitrary values. + # For example, assume a user has an LDAP attribute named 'member' which has multiple + # values which are each a distinguished name (i.e. a DN). For simplicity, assume the + # values of the 'member' attribute are 'dn1', 'dn2', and 'dn3'. + # Further assume the following configuration. + # converters: + # - name: hf.Registrar.Roles + # value: map(attr("member"),"groups") + # maps: + # groups: + # - name: dn1 + # value: peer + # - name: dn2 + # value: client + # The value of the user's 'hf.Registrar.Roles' attribute is then computed to be + # "peer,client,dn3". This is because the value of 'attr("member")' is + # "dn1,dn2,dn3", and the call to 'map' with a 2nd argument of + # "group" replaces "dn1" with "peer" and "dn2" with "client". + maps: + groups: + - name: + value: + +############################################################################# +# Affiliations section. Fabric CA server can be bootstrapped with the +# affiliations specified in this section. Affiliations are specified as maps. +# For example: +# businessunit1: +# department1: +# - team1 +# businessunit2: +# - department2 +# - department3 +# +# Affiliations are hierarchical in nature. In the above example, +# department1 (used as businessunit1.department1) is the child of businessunit1. +# team1 (used as businessunit1.department1.team1) is the child of department1. +# department2 (used as businessunit2.department2) and department3 (businessunit2.department3) +# are children of businessunit2. +# Note: Affiliations are case sensitive except for the non-leaf affiliations +# (like businessunit1, department1, businessunit2) that are specified in the configuration file, +# which are always stored in lower case. +############################################################################# +affiliations: + org1: + - department1 + - department2 + org2: + - department1 + +############################################################################# +# Signing section +# +# The "default" subsection is used to sign enrollment certificates; +# the default expiration ("expiry" field) is "8760h", which is 1 year in hours. +# +# The "ca" profile subsection is used to sign intermediate CA certificates; +# the default expiration ("expiry" field) is "43800h" which is 5 years in hours. +# Note that "isca" is true, meaning that it issues a CA certificate. +# A maxpathlen of 0 means that the intermediate CA cannot issue other +# intermediate CA certificates, though it can still issue end entity certificates. +# (See RFC 5280, section 4.2.1.9) +# +# The "tls" profile subsection is used to sign TLS certificate requests; +# the default expiration ("expiry" field) is "8760h", which is 1 year in hours. +############################################################################# +signing: + default: + authremote: {} + caconstraint: {} + expiry: 131400h0m0s + usage: + - signing + - key encipherment + - server auth + - client auth + - key agreement + profiles: null + +########################################################################### +# Certificate Signing Request (CSR) section. +# This controls the creation of the root CA certificate. +# The expiration for the root CA certificate is configured with the +# "ca.expiry" field below, whose default value is "131400h" which is +# 15 years in hours. +# The pathlength field is used to limit CA certificate hierarchy as described +# in section 4.2.1.9 of RFC 5280. +# Examples: +# 1) No pathlength value means no limit is requested. +# 2) pathlength == 1 means a limit of 1 is requested which is the default for +# a root CA. This means the root CA can issue intermediate CA certificates, +# but these intermediate CAs may not in turn issue other CA certificates +# though they can still issue end entity certificates. +# 3) pathlength == 0 means a limit of 0 is requested; +# this is the default for an intermediate CA, which means it can not issue +# CA certificates though it can still issue end entity certificates. +########################################################################### +csr: + cn: fabric-tlsca-server + keyrequest: + algo: ecdsa + size: 256 + names: + - C: US + ST: "North Carolina" + L: + O: Hyperledger + OU: Fabric + hosts: + - localhost + - 127.0.0.1 + ca: + expiry: 131400h + pathlength: 1 + +########################################################################### +# Each CA can issue both X509 enrollment certificate as well as Idemix +# Credential. This section specifies configuration for the issuer component +# that is responsible for issuing Idemix credentials. +########################################################################### +idemix: + # Specifies pool size for revocation handles. A revocation handle is an unique identifier of an + # Idemix credential. The issuer will create a pool revocation handles of this specified size. When + # a credential is requested, issuer will get handle from the pool and assign it to the credential. + # Issuer will repopulate the pool with new handles when the last handle in the pool is used. + # A revocation handle and credential revocation information (CRI) are used to create non revocation proof + # by the prover to prove to the verifier that her credential is not revoked. + rhpoolsize: 1000 + + # The Idemix credential issuance is a two step process. First step is to get a nonce from the issuer + # and second step is send credential request that is constructed using the nonce to the isuser to + # request a credential. This configuration property specifies expiration for the nonces. By default is + # nonces expire after 15 seconds. The value is expressed in the time.Duration format (see https://golang.org/pkg/time/#ParseDuration). + nonceexpiration: 15s + + # Specifies interval at which expired nonces are removed from datastore. Default value is 15 minutes. + # The value is expressed in the time.Duration format (see https://golang.org/pkg/time/#ParseDuration) + noncesweepinterval: 15m + +############################################################################# +# BCCSP (BlockChain Crypto Service Provider) section is used to select which +# crypto library implementation to use +############################################################################# +bccsp: + default: SW + sw: + hash: SHA2 + security: 256 + filekeystore: + # The directory used for the software file-based keystore + keystore: msp/keystore + +############################################################################# +# Multi CA section +# +# Each Fabric CA server contains one CA by default. This section is used +# to configure multiple CAs in a single server. +# +# 1) --cacount +# Automatically generate non-default CAs. The names of these +# additional CAs are "ca1", "ca2", ... "caN", where "N" is +# This is particularly useful in a development environment to quickly set up +# multiple CAs. Note that, this config option is not applicable to intermediate CA server +# i.e., Fabric CA server that is started with intermediate.parentserver.url config +# option (-u command line option) +# +# 2) --cafiles +# For each CA config file in the list, generate a separate signing CA. Each CA +# config file in this list MAY contain all of the same elements as are found in +# the server config file except port, debug, and tls sections. +# +# Examples: +# fabric-ca-server start -b admin:adminpw --cacount 2 +# +# fabric-ca-server start -b admin:adminpw --cafiles ca/ca1/fabric-ca-server-config.yaml +# --cafiles ca/ca2/fabric-ca-server-config.yaml +# +############################################################################# + +cacount: + +cafiles: + +############################################################################# +# Intermediate CA section +# +# The relationship between servers and CAs is as follows: +# 1) A single server process may contain or function as one or more CAs. +# This is configured by the "Multi CA section" above. +# 2) Each CA is either a root CA or an intermediate CA. +# 3) Each intermediate CA has a parent CA which is either a root CA or another intermediate CA. +# +# This section pertains to configuration of #2 and #3. +# If the "intermediate.parentserver.url" property is set, +# then this is an intermediate CA with the specified parent +# CA. +# +# parentserver section +# url - The URL of the parent server +# caname - Name of the CA to enroll within the server +# +# enrollment section used to enroll intermediate CA with parent CA +# profile - Name of the signing profile to use in issuing the certificate +# label - Label to use in HSM operations +# +# tls section for secure socket connection +# certfiles - PEM-encoded list of trusted root certificate files +# client: +# certfile - PEM-encoded certificate file for when client authentication +# is enabled on server +# keyfile - PEM-encoded key file for when client authentication +# is enabled on server +############################################################################# +intermediate: + parentserver: + url: + caname: + + enrollment: + hosts: + profile: + label: + + tls: + certfiles: + client: + certfile: + keyfile: + +############################################################################# +# CA configuration section +# +# Configure the number of incorrect password attempts are allowed for +# identities. By default, the value of 'passwordattempts' is 10, which +# means that 10 incorrect password attempts can be made before an identity get +# locked out. +############################################################################# +cfg: + identities: + passwordattempts: 10 + +############################################################################### +# +# Operations section +# +############################################################################### +operations: + # host and port for the operations server + listenAddress: 127.0.0.1:9443 + + # TLS configuration for the operations endpoint + tls: + # TLS enabled + enabled: false + + # path to PEM encoded server certificate for the operations server + cert: + file: + + # path to PEM encoded server key for the operations server + key: + file: + + # require client certificate authentication to access all resources + clientAuthRequired: false + + # paths to PEM encoded ca certificates to trust for client authentication + clientRootCAs: + files: [] + +############################################################################### +# +# Metrics section +# +############################################################################### +metrics: + # statsd, prometheus, or disabled + provider: disabled + + # statsd configuration + statsd: + # network type: tcp or udp + network: udp + + # statsd server address + address: 127.0.0.1:8125 + + # the interval at which locally cached counters and gauges are pushsed + # to statsd; timings are pushed immediately + writeInterval: 10s + + # prefix is prepended to all emitted statsd merics + prefix: server diff --git a/test-network-nano-bash/ca/ca_utils.sh b/test-network-nano-bash/ca/ca_utils.sh new file mode 100755 index 0000000000..640e0a78a2 --- /dev/null +++ b/test-network-nano-bash/ca/ca_utils.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env sh +# +# SPDX-License-Identifier: Apache-2.0 +# + +###################################################################################### +# createEnrollment() +# +# This is a convenience method for creating enrollments only +# Primary purpose it to create enrollment certificates for CA admins. +###################################################################################### + +function createEnrollment() { + + local port=$1 # port of the CA used for creating the enrollment + local username=$2 # username of the registered user on the CA + local password=$3 # password of the registered user on the CA + local orgname=$4 # name of the org (e.g. Org1, Org2) can be blank + local component_dir=$5 # path of the component, this will be the directory where the artifacts will be created + local tlscert=$6 # tls cert for connecting to the CA + + # Enroll the identity + fabric-ca-client enroll -d -u https://${username}:${password}@localhost:${port} --caname ca --mspdir "${component_dir}/msp" --tls.certfiles $tlscert + + # Rename private key to mimic cryptogen + find ${component_dir} -type f -name '*_sk' | sed -e 'p;s/\(.*\)\/\(.*\)$/\1\/priv_sk/' | xargs -n2 mv -v + + # Rename the cacert to mimic cryptogen + mv ${component_dir}/msp/cacerts/localhost-${port}-ca.pem ${component_dir}/msp/cacerts/ca.${orgname:+$orgname.}example.com-cert.pem + +} + +###################################################################################### +# createMSP() +# +# This is a convenience method for creating the Membership Service Provider directories +# +###################################################################################### + +function createMSP() { + + local caname=$1 # name of the ca (ordererca, org1ca, org2ca) + local orgname=$2 # name of the org (org1, org2) Ordering Org is blank + local org_dir=$3 # directory of the organizatio + + mkdir -p ${org_dir}/msp/admincerts + mkdir -p ${org_dir}/msp/cacerts + mkdir -p ${org_dir}/msp/tlscacerts + + cp data_ca/${caname}/ca/ca-cert.pem ${org_dir}/msp/cacerts/ca.${orgname:+$orgname.}example.com-cert.pem + cp data_ca/${caname}/tlsca/ca-cert.pem ${org_dir}/msp/tlscacerts/tlsca.${orgname:+$orgname.}example.com-cert.pem + awk -v cacert_name="ca.${orgname:+$orgname.}example.com-cert" '{gsub(/ca.example.com-cert/,cacert_name)}1' ca/config.yaml > ${org_dir}/msp/config.yaml + +} + +###################################################################################### +# registerAndEnroll() +# +# This is a convenience method for creating enrollments and TLS certificates +# Primary purpose it to create enrollment certificates for org admin identities, and +# enrollent and TLS certificates for peers and orderers. +###################################################################################### + +function registerAndEnroll() { + + local port=$1 # port of the CA used for creating the enrollment + local username=$2 # username of the user to register on the CA + local password=$3 # password of the user to register on the CA + local type=$4 # type of registation, must be one of (peer, orderer, admin) + local orgname=$5 # name of the org (e.g. Org1, Org2) can be blank + local component_dir=$6 # directory of the component, this will be the directory where the artifacts will be created + local org_dir=$7 # directory of the organization, this is the directory that contains the credentials for the registration + local tlscert=$8 # tls cert for connecting to the CA + + if [ "$type" = "admin" ]; then + local attrs="hf.Registrar.Roles=client,hf.Registrar.Attributes=*,hf.Revoker=true,hf.GenCRL=true,admin=true:ecert,abac.init=true:ecert" + else + local attrs="" + fi + + # Register the username + fabric-ca-client register -d -u https://localhost:${port} --id.name ${username} --id.secret ${password} --id.type ${type} --id.attrs "${attrs}" --caname ca --tls.certfiles $tlscert --mspdir "${org_dir}/ca/msp" + + # Enroll the identity + fabric-ca-client enroll -d -u https://${username}:${password}@localhost:${port} --caname ca --mspdir "${component_dir}/msp" --tls.certfiles $tlscert + + # Rename private key to mimic cryptogen + find ${component_dir} -type f -name '*_sk' | sed -e 'p;s/\(.*\)\/\(.*\)$/\1\/priv_sk/' | xargs -n2 mv -v + + # Rename the cacert to mimic cryptogen + mv ${component_dir}/msp/cacerts/localhost-${port}-ca.pem ${component_dir}/msp/cacerts/ca.${orgname:+$orgname.}example.com-cert.pem + + # Set the cacert name and copy the config.json for NodeOU + awk -v cacert_name="ca.${orgname:+$orgname.}example.com-cert" '{gsub(/ca.example.com-cert/,cacert_name)}1' ca/config.yaml > ${component_dir}/msp/config.yaml + + # If this is a peer or orderer type then create a TLS cert + if [ "$type" = "peer" ] || [ "$type" = "orderer" ]; then + + # Enroll the TLS cert + fabric-ca-client enroll -d -u https://${username}:${password}@localhost:${port} --caname tlsca --mspdir "${component_dir}/tls" --tls.certfiles $tlscert --csr.hosts 'localhost,127.0.0.1' + + # Rename private key to mimic cryptogen + find ${component_dir} -type f -name '*_sk' | sed -e 'p;s/\(.*\)\/\(.*\)$/\1\/priv_sk/' | xargs -n2 mv -v + + # Copy and rename TLS certs and keys to mimic cryptogen + cp ${component_dir}/tls/cacerts/localhost-${port}-tlsca.pem ${component_dir}/tls/ca.crt + cp ${component_dir}/tls/keystore/priv_sk ${component_dir}/tls/server.key + cp ${component_dir}/tls/signcerts/cert.pem ${component_dir}/tls/server.crt + + # Rename the tls cacert to mimic cryptogen + mv ${component_dir}/tls/cacerts/localhost-${port}-tlsca.pem ${component_dir}/tls/cacerts/tlsca.${orgname:+$orgname.}example.com-cert.pem + + fi + +} + + diff --git a/test-network-nano-bash/ca/config.yaml b/test-network-nano-bash/ca/config.yaml new file mode 100644 index 0000000000..8846e9d4d9 --- /dev/null +++ b/test-network-nano-bash/ca/config.yaml @@ -0,0 +1,14 @@ +NodeOUs: + Enable: true + ClientOUIdentifier: + Certificate: cacerts/ca.example.com-cert.pem + OrganizationalUnitIdentifier: client + PeerOUIdentifier: + Certificate: cacerts/ca.example.com-cert.pem + OrganizationalUnitIdentifier: peer + AdminOUIdentifier: + Certificate: cacerts/ca.example.com-cert.pem + OrganizationalUnitIdentifier: admin + OrdererOUIdentifier: + Certificate: cacerts/ca.example.com-cert.pem + OrganizationalUnitIdentifier: orderer diff --git a/test-network-nano-bash/ca/createEnrollments.sh b/test-network-nano-bash/ca/createEnrollments.sh new file mode 100755 index 0000000000..5b109b192a --- /dev/null +++ b/test-network-nano-bash/ca/createEnrollments.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env sh +# +# SPDX-License-Identifier: Apache-2.0 +# +export PATH="${PWD}"/../../fabric/build/bin:"${PWD}"/../bin:"$PATH" + +export crypto_dir=$PWD/crypto-config + +export orderer_org_dir=${crypto_dir}/ordererOrganizations/example.com +export org1_dir=${crypto_dir}/peerOrganizations/org1.example.com +export org2_dir=${crypto_dir}/peerOrganizations/org2.example.com + +export orderer1_dir=${orderer_org_dir}/orderers/orderer.example.com +export orderer2_dir=${orderer_org_dir}/orderers/orderer2.example.com +export orderer3_dir=${orderer_org_dir}/orderers/orderer3.example.com +export orderer4_dir=${orderer_org_dir}/orderers/orderer4.example.com +export orderer5_dir=${orderer_org_dir}/orderers/orderer5.example.com + +export peer0org1_dir=${org1_dir}/peers/peer0.org1.example.com +export peer1org1_dir=${org1_dir}/peers/peer1.org1.example.com + +export peer0org2_dir=${org2_dir}/peers/peer0.org2.example.com +export peer1org2_dir=${org2_dir}/peers/peer1.org2.example.com + +export orderer_org_tls=${PWD}/data_ca/ordererca/ca/ca-cert.pem +export org1_tls=${PWD}/data_ca/org1ca/ca/ca-cert.pem +export org2_tls=${PWD}/data_ca/org2ca/ca/ca-cert.pem + +# import utilies +. ca/ca_utils.sh + +###################################################################################### +# Create admin certificates for the CAs +###################################################################################### + +# Enroll CA Admin for ordererca +createEnrollment "7052" "admin" "adminpw" "" "${orderer_org_dir}/ca" "${orderer_org_tls}" + +# Enroll CA Admin for org1ca +createEnrollment "7053" "admin" "adminpw" "org1" "${org1_dir}/ca" "${org1_tls}" + +# Enroll CA Admin for org2ca +createEnrollment "7054" "admin" "adminpw" "org2" "${org2_dir}/ca" "${org2_tls}" + + +###################################################################################### +# Create admin and user certificates for the Organizations +###################################################################################### + +# Enroll Admin certificate for the ordering service org +registerAndEnroll "7052" "osadmin" "osadminpw" "admin" "" "${orderer_org_dir}/users/Admin@example.com" "${orderer_org_dir}" "${orderer_org_tls}" + +# Enroll Admin certificate for org1 +registerAndEnroll "7053" "org1admin" "org1adminpw" "admin" "org1" "${org1_dir}/users/Admin@org1.example.com" "${org1_dir}" "${org1_tls}" + +# Enroll User certificate for org1 +registerAndEnroll "7053" "org1user1" "org1user1pw" "client" "org1" "${org1_dir}/users/User1@org1.example.com" "${org1_dir}" "${org1_tls}" + +# Enroll Admin certificate for org2 +registerAndEnroll "7054" "org2admin" "org2adminpw" "admin" "org2" "${org2_dir}/users/Admin@org2.example.com" "${org2_dir}" "${org2_tls}" + +# Enroll User certificate for org1 +registerAndEnroll "7054" "org2user1" "org2user1pw" "client" "org2" "${org2_dir}/users/User1@org2.example.com" "${org2_dir}" "${org2_tls}" + +###################################################################################### +# Create the certificates for the Ordering Organization +###################################################################################### + +# Create enrollment and TLS certificates for orderer1 +registerAndEnroll "7052" "orderer1" "orderer1pw" "orderer" "" "${orderer1_dir}" "${orderer_org_dir}" "${orderer_org_tls}" + +# Create enrollment and TLS certificates for orderer2 +registerAndEnroll "7052" "orderer2" "orderer2pw" "orderer" "" "${orderer2_dir}" "${orderer_org_dir}" "${orderer_org_tls}" + +# Create enrollment and TLS certificates for orderer3 +registerAndEnroll "7052" "orderer3" "orderer3pw" "orderer" "" "${orderer3_dir}" "${orderer_org_dir}" "${orderer_org_tls}" + +# Create enrollment and TLS certificates for orderer4 +registerAndEnroll "7052" "orderer4" "orderer4pw" "orderer" "" "${orderer4_dir}" "${orderer_org}" "${orderer_org_tls}" + +# Create enrollment and TLS certificates for orderer5 +registerAndEnroll "7052" "orderer5" "orderer5pw" "orderer" "" "${orderer5_dir}" "${orderer_org_dir}" "${orderer_org_tls}" + + +###################################################################################### +# Create the certificates for Org1 +###################################################################################### + +# Create enrollment and TLS certificates for peer0org1 +registerAndEnroll "7053" "org1peer0" "org1peer0pw" "peer" "org1" "${peer0org1_dir}" "${org1_dir}" "${org1_tls}" + +# Create enrollment and TLS certificates for peer1org1 +registerAndEnroll "7053" "org1peer1" "org1peer1pw" "peer" "org1" "${peer1org1_dir}" "${org1_dir}" "${org1_tls}" + + +###################################################################################### +# Create the certificates for Org2 +###################################################################################### + +# Create enrollment and TLS certificates for peer0org2 +registerAndEnroll "7054" "org2peer0" "org2peer0pw" "peer" "org2" "${peer0org2_dir}" "${org2_dir}" "${org2_tls}" + +# Create enrollment and TLS certificates for peer1org2 +registerAndEnroll "7054" "org2peer1" "org2peer1pw" "peer" "org2" "${peer1org2_dir}" "${org2_dir}" "${org2_tls}" + + +###################################################################################### +# Create the Membership Service Providers (MSPs) +###################################################################################### + +# Create the MSP for the Orderering Org +createMSP "ordererca" "" "${orderer_org_dir}" + +# Create the MSP for Org1 +createMSP "org1ca" "org1" "${org1_dir}" + +# Create the MSP for Org2 +createMSP "org2ca" "org2" "${org2_dir}" diff --git a/test-network-nano-bash/ca_terminal_setup.png b/test-network-nano-bash/ca_terminal_setup.png new file mode 100644 index 0000000000000000000000000000000000000000..1ada8c63c3a8b7d4a9702254014a7699761f6483 GIT binary patch literal 61294 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 #jW?gaL9&-C>`4*7zazjlM6 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 DIQTD8 literal 0 HcmV?d00001 diff --git a/test-network-nano-bash/generate_artifacts.sh b/test-network-nano-bash/generate_artifacts.sh index 9ad8324068..daa8ed300e 100755 --- a/test-network-nano-bash/generate_artifacts.sh +++ b/test-network-nano-bash/generate_artifacts.sh @@ -4,6 +4,29 @@ # set -eu +ordererType="etcdraft" +INCLUDE_CA=false + +# parse flags +while [ $# -ge 1 ] ; do + key="$1" + case $key in + etcdraft ) + ordererType="etcdraft" + ;; + BFT ) + ordererType="BFT" + ;; + -ca ) + INCLUDE_CA=true + ;; + * ) + ;; + esac + shift +done + + # remove existing artifacts, or proceed on if the directories don't exist rm -r "${PWD}"/channel-artifacts || true rm -r "${PWD}"/crypto-config || true @@ -12,14 +35,26 @@ rm -r "${PWD}"/data || true # look for binaries in local dev environment /build/bin directory and then in local samples /bin directory export PATH="${PWD}"/../../fabric/build/bin:"${PWD}"/../bin:"$PATH" -echo "Generating MSP certificates using cryptogen tool" -cryptogen generate --config="${PWD}"/crypto-config.yaml +# if INCLUDE_CA is false (default), then use cryptogen +if [ "${INCLUDE_CA}" = false ]; then + + echo "Generating MSP certificates using cryptogen tool" + cryptogen generate --config="${PWD}"/crypto-config.yaml + +else + + mkdir -p "${PWD}"/logs + + # execute the script to configure the default set of enrollments + echo "Generating MSP certificates using the Fabric CAs" + ./ca/createEnrollments.sh > ./logs/createEnrollments.log 2>&1 + +fi # set FABRIC_CFG_PATH to configtx.yaml directory that contains the profiles export FABRIC_CFG_PATH="${PWD}" -ordererType="etcdraft" -if [ $# -gt 0 ] && [ "$1" = "BFT" ] +if [ "${ordererType}" = "BFT" ] then profile="ChannelUsingBFT" ordererType="BFT" diff --git a/test-network-nano-bash/network.sh b/test-network-nano-bash/network.sh index 288a1cf0b4..a478a18c33 100755 --- a/test-network-nano-bash/network.sh +++ b/test-network-nano-bash/network.sh @@ -14,7 +14,9 @@ printHelp() { echo " Starts the test network" echo echo " Flags:" - echo " -d - CLI delays for a certain number of seconds (defaults to 3)" + echo " -d - CLI delays for a certain number of seconds (defaults to 3)" + echo " -o - Specify the orderer type. BFT or etcdraft. (defaults to etcdraft)" + echo " -ca - Use CAs instead of cryptogen. (defaults to cryptogen)" echo " -h - Print this message" elif [ "$USAGE" = "clean" ]; then echo "Usage: " @@ -52,18 +54,31 @@ networkStart() { # shellcheck disable=SC2064 trap networkStop 0 1 2 3 15 + echo "Creating logs directory..." + mkdir -p "${PWD}"/logs + + if [ "${INCLUDE_CA}" = true ]; then + echo "Starting CAs..." + ./ordererca.sh > ./logs/ordererca.log 2>&1 & + ./org1ca.sh > ./logs/org1ca.log 2>&1 & + ./org2ca.sh > ./logs/org2ca.log 2>&1 & + echo "Waiting ${CLI_DELAY}s..." + sleep ${CLI_DELAY} + fi + if [ -d "${PWD}"/channel-artifacts ] && [ -d "${PWD}"/crypto-config ]; then echo "Using existing artifacts..." CREATE_CHANNEL=false else echo "Generating artifacts..." - ./generate_artifacts.sh "${ORDERER_TYPE}" + INCLUDE_CA_PARAM="" + if [ "${INCLUDE_CA}" = true ]; then + INCLUDE_CA_PARAM="-ca" + fi + ./generate_artifacts.sh "${ORDERER_TYPE}" "${INCLUDE_CA_PARAM}" CREATE_CHANNEL=true fi - echo "Creating logs directory..." - mkdir -p "${PWD}"/logs - echo "Starting orderers..." ./orderer1.sh "${ORDERER_TYPE}" > ./logs/orderer1.log 2>&1 & ./orderer2.sh "${ORDERER_TYPE}" > ./logs/orderer2.log 2>&1 & @@ -116,6 +131,7 @@ networkClean() { rm -r "${PWD}"/channel-artifacts || true rm -r "${PWD}"/crypto-config || true rm -r "${PWD}"/data || true + rm -r "${PWD}"/data_ca || true rm -r "${PWD}"/logs || true } @@ -131,6 +147,7 @@ else fi ORDERER_TYPE="etcdraft" +INCLUDE_CA=false # parse flags while [ $# -ge 1 ] ; do @@ -144,6 +161,9 @@ while [ $# -ge 1 ] ; do ORDERER_TYPE="$2" shift ;; + -ca ) + INCLUDE_CA=true + ;; -h ) printHelp "$MODE" exit 0 diff --git a/test-network-nano-bash/ordererca.sh b/test-network-nano-bash/ordererca.sh new file mode 100755 index 0000000000..679e12982f --- /dev/null +++ b/test-network-nano-bash/ordererca.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env sh +# +# SPDX-License-Identifier: Apache-2.0 +# +export PATH="${PWD}"/../../fabric/build/bin:"${PWD}"/../bin:"${PATH}" +export FABRIC_CFG_PATH="${PWD}"/../config + +#Configure the CA_NAME, CA_PORT, OPERATIONS_PORT and CSR_HOSTS for the CA +export CA_NAME=ordererca +export CA_PORT=7052 +export OPERATIONS_PORT=9843 +export CSR_HOSTS=ordererca,localhost,127.0.0.1 + +export CA_DIRECTORY="${PWD}"/data_ca/"${CA_NAME}" +export CA_HOME="${CA_DIRECTORY}"/ca +export TLSCA_HOME="${CA_DIRECTORY}"/tlsca +export DB_HOME="${CA_DIRECTORY}"/db +export TEMPLATE_DIR="${PWD}"/ca/ca_config + +# Check to see if the CA directory exists +if [ ! -d "${CA_DIRECTORY}" ]; then + + # Create the new CA directory + mkdir -p "${CA_HOME}" + mkdir -p "${TLSCA_HOME}" + mkdir -p "${DB_HOME}" + + # Copy the CA template files + cp "${TEMPLATE_DIR}"/ca/fabric-ca-server-config.yaml "${CA_HOME}"/fabric-ca-server-config.yaml + cp "${TEMPLATE_DIR}"/tlsca/fabric-ca-server-config.yaml "${TLSCA_HOME}"/fabric-ca-server-config.yaml + +fi + +export FABRIC_CA_SERVER_TLS_ENABLED=true +export FABRIC_CA_SERVER_CSR_CN="${CA_NAME}" +export FABRIC_CA_SERVER_CSR_HOSTS="${CSR_HOSTS}" +export FABRIC_CA_SERVER_DEBUG=true +export FABRIC_CA_SERVER_OPERATIONS_LISTENADDRESS=localhost:"${OPERATIONS_PORT}" +fabric-ca-server start -d -b admin:adminpw --port "${CA_PORT}" --home "${CA_HOME}" diff --git a/test-network-nano-bash/org1ca.sh b/test-network-nano-bash/org1ca.sh new file mode 100755 index 0000000000..bdfc918d2b --- /dev/null +++ b/test-network-nano-bash/org1ca.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env sh +# +# SPDX-License-Identifier: Apache-2.0 +# +export PATH="${PWD}"/../../fabric/build/bin:"${PWD}"/../bin:"${PATH}" +export FABRIC_CFG_PATH="${PWD}"/../config + +#Configure the CA_NAME, CA_PORT, OPERATIONS_PORT and CSR_HOSTS for the CA +export CA_NAME=org1ca +export CA_PORT=7053 +export OPERATIONS_PORT=9844 +export CSR_HOSTS=org1ca,localhost,127.0.0.1 + +export CA_DIRECTORY="${PWD}"/data_ca/"${CA_NAME}" +export CA_HOME="${CA_DIRECTORY}"/ca +export TLSCA_HOME="${CA_DIRECTORY}"/tlsca +export DB_HOME="${CA_DIRECTORY}"/db +export TEMPLATE_DIR="${PWD}"/ca/ca_config + +# Check to see if the CA directory exists +if [ ! -d "${CA_DIRECTORY}" ]; then + + # Create the new CA directory + mkdir -p "${CA_HOME}" + mkdir -p "${TLSCA_HOME}" + mkdir -p "${DB_HOME}" + + # Copy the CA template files + cp "${TEMPLATE_DIR}"/ca/fabric-ca-server-config.yaml "${CA_HOME}"/fabric-ca-server-config.yaml + cp "${TEMPLATE_DIR}"/tlsca/fabric-ca-server-config.yaml "${TLSCA_HOME}"/fabric-ca-server-config.yaml + +fi + +export FABRIC_CA_SERVER_TLS_ENABLED=true +export FABRIC_CA_SERVER_CSR_CN="${CA_NAME}" +export FABRIC_CA_SERVER_CSR_HOSTS="${CSR_HOSTS}" +export FABRIC_CA_SERVER_DEBUG=true +export FABRIC_CA_SERVER_OPERATIONS_LISTENADDRESS=localhost:"${OPERATIONS_PORT}" +fabric-ca-server start -d -b admin:adminpw --port "${CA_PORT}" --home "${CA_HOME}" diff --git a/test-network-nano-bash/org2ca.sh b/test-network-nano-bash/org2ca.sh new file mode 100755 index 0000000000..a325fd4c8b --- /dev/null +++ b/test-network-nano-bash/org2ca.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env sh +# +# SPDX-License-Identifier: Apache-2.0 +# +export PATH="${PWD}"/../../fabric/build/bin:"${PWD}"/../bin:"${PATH}" +export FABRIC_CFG_PATH="${PWD}"/../config + +#Configure the CA_NAME, CA_PORT, OPERATIONS_PORT and CSR_HOSTS for the CA +export CA_NAME=org2ca +export CA_PORT=7054 +export OPERATIONS_PORT=9845 +export CSR_HOSTS=org2ca,localhost,127.0.0.1 + +export CA_DIRECTORY="${PWD}"/data_ca/"${CA_NAME}" +export CA_HOME="${CA_DIRECTORY}"/ca +export TLSCA_HOME="${CA_DIRECTORY}"/tlsca +export DB_HOME="${CA_DIRECTORY}"/db +export TEMPLATE_DIR="${PWD}"/ca/ca_config + +# Check to see if the CA directory exists +if [ ! -d "${CA_DIRECTORY}" ]; then + + # Create the new CA directory + mkdir -p "${CA_HOME}" + mkdir -p "${TLSCA_HOME}" + mkdir -p "${DB_HOME}" + + # Copy the CA template files + cp "${TEMPLATE_DIR}"/ca/fabric-ca-server-config.yaml "${CA_HOME}"/fabric-ca-server-config.yaml + cp "${TEMPLATE_DIR}"/tlsca/fabric-ca-server-config.yaml "${TLSCA_HOME}"/fabric-ca-server-config.yaml + +fi + +export FABRIC_CA_SERVER_TLS_ENABLED=true +export FABRIC_CA_SERVER_CSR_CN="${CA_NAME}" +export FABRIC_CA_SERVER_CSR_HOSTS="${CSR_HOSTS}" +export FABRIC_CA_SERVER_DEBUG=true +export FABRIC_CA_SERVER_OPERATIONS_LISTENADDRESS=localhost:"${OPERATIONS_PORT}" +fabric-ca-server start -d -b admin:adminpw --port "${CA_PORT}" --home "${CA_HOME}"