github.com/binyushen/fabric@v2.1.1+incompatible/core/peer/config.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  // The 'viper' package for configuration handling is very flexible, but has
     8  // been found to have extremely poor performance when configuration values are
     9  // accessed repeatedly. The function CacheConfiguration() defined here caches
    10  // all configuration values that are accessed frequently.  These parameters
    11  // are now presented as function calls that access local configuration
    12  // variables.  This seems to be the most robust way to represent these
    13  // parameters in the face of the numerous ways that configuration files are
    14  // loaded and used (e.g, normal usage vs. test cases).
    15  
    16  // The CacheConfiguration() function is allowed to be called globally to
    17  // ensure that the correct values are always cached; See for example how
    18  // certain parameters are forced in 'ChaincodeDevMode' in main.go.
    19  
    20  package peer
    21  
    22  import (
    23  	"crypto/tls"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"net"
    27  	"path/filepath"
    28  	"runtime"
    29  	"time"
    30  
    31  	"github.com/hyperledger/fabric/core/config"
    32  	"github.com/hyperledger/fabric/internal/pkg/comm"
    33  	"github.com/pkg/errors"
    34  	"github.com/spf13/viper"
    35  )
    36  
    37  // ExternalBuilder represents the configuration structure of
    38  // a chaincode external builder
    39  type ExternalBuilder struct {
    40  	EnvironmentWhitelist []string `yaml:"environmentWhitelist"`
    41  	Name                 string   `yaml:"name"`
    42  	Path                 string   `yaml:"path"`
    43  }
    44  
    45  // Config is the struct that defines the Peer configurations.
    46  type Config struct {
    47  	// LocalMSPID is the identifier of the local MSP.
    48  	LocalMSPID string
    49  	// ListenAddress is the local address the peer will listen on. It must be
    50  	// formatted as [host | ipaddr]:port.
    51  	ListenAddress string
    52  	// PeerID provides a name for this peer instance. It is used when naming
    53  	// docker resources to segregate fabric networks and peers.
    54  	PeerID string
    55  	// PeerAddress is the address other peers and clients should use to
    56  	// communicate with the peer. It must be formatted as [host | ipaddr]:port.
    57  	// When used by the CLI, it represents the target peer endpoint.
    58  	PeerAddress string
    59  	// NetworkID specifies a name to use for logical separation of networks. It
    60  	// is used when naming docker resources to segregate fabric networks and
    61  	// peers.
    62  	NetworkID string
    63  	// ChaincodeListenAddress is the endpoint on which this peer will listen for
    64  	// chaincode connections. If omitted, it defaults to the host portion of
    65  	// PeerAddress and port 7052.
    66  	ChaincodeListenAddress string
    67  	// ChaincodeAddress specifies the endpoint chaincode launched by the peer
    68  	// should use to connect to the peer. If omitted, it defaults to
    69  	// ChaincodeListenAddress and falls back to ListenAddress.
    70  	ChaincodeAddress string
    71  	// ValidatorPoolSize indicates the number of goroutines that will execute
    72  	// transaction validation in parallel. If omitted, it defaults to number of
    73  	// hardware threads on the machine.
    74  	ValidatorPoolSize int
    75  
    76  	// ----- Peer Delivery Client Keepalive -----
    77  	// DeliveryClient Keepalive settings for communication with ordering nodes.
    78  	DeliverClientKeepaliveOptions comm.KeepaliveOptions
    79  
    80  	// ----- Profile -----
    81  	// TODO: create separate sub-struct for Profile config.
    82  
    83  	// ProfileEnabled determines if the go pprof endpoint is enabled in the peer.
    84  	ProfileEnabled bool
    85  	// ProfileListenAddress is the address the pprof server should accept
    86  	// connections on.
    87  	ProfileListenAddress string
    88  
    89  	// ----- Discovery -----
    90  
    91  	// The discovery service is used by clients to query information about peers,
    92  	// such as - which peers have joined a certain channel, what is the latest
    93  	// channel config, and most importantly - given a chaincode and a channel, what
    94  	// possible sets of peers satisfy the endorsement policy.
    95  	// TODO: create separate sub-struct for Discovery config.
    96  
    97  	// DiscoveryEnabled is used to enable the discovery service.
    98  	DiscoveryEnabled bool
    99  	// DiscoveryOrgMembersAllowed allows non-admins to perform non channel-scoped queries.
   100  	DiscoveryOrgMembersAllowed bool
   101  	// DiscoveryAuthCacheEnabled is used to enable the authentication cache.
   102  	DiscoveryAuthCacheEnabled bool
   103  	// DiscoveryAuthCacheMaxSize sets the maximum size of authentication cache.
   104  	DiscoveryAuthCacheMaxSize int
   105  	// DiscoveryAuthCachePurgeRetentionRatio set the proportion of entries remains in cache
   106  	// after overpopulation purge.
   107  	DiscoveryAuthCachePurgeRetentionRatio float64
   108  
   109  	// ----- Limits -----
   110  	// Limits is used to configure some internal resource limits.
   111  	// TODO: create separate sub-struct for Limits config.
   112  
   113  	// LimitsConcurrencyEndorserService sets the limits for concurrent requests sent to
   114  	// endorser service that handles chaincode deployment, query and invocation,
   115  	// including both user chaincodes and system chaincodes.
   116  	LimitsConcurrencyEndorserService int
   117  
   118  	// LimitsConcurrencyDeliverService sets the limits for concurrent event listeners
   119  	// registered to deliver service for blocks and transaction events.
   120  	LimitsConcurrencyDeliverService int
   121  
   122  	// ----- TLS -----
   123  	// Require server-side TLS.
   124  	// TODO: create separate sub-struct for PeerTLS config.
   125  
   126  	// PeerTLSEnabled enables/disables Peer TLS.
   127  	PeerTLSEnabled bool
   128  
   129  	// ----- Authentication -----
   130  	// Authentication contains configuration parameters related to authenticating
   131  	// client messages.
   132  	// TODO: create separate sub-struct for Authentication config.
   133  
   134  	// AuthenticationTimeWindow sets the acceptable time duration for current
   135  	// server time and client's time as specified in a client request message.
   136  	AuthenticationTimeWindow time.Duration
   137  
   138  	// Endpoint of the vm management system. For docker can be one of the following in general
   139  	// unix:///var/run/docker.sock
   140  	// http://localhost:2375
   141  	// https://localhost:2376
   142  	VMEndpoint string
   143  
   144  	// ----- vm.docker.tls -----
   145  	// TODO: create separate sub-struct for VM.Docker.TLS config.
   146  
   147  	// VMDockerTLSEnabled enables/disables TLS for dockers.
   148  	VMDockerTLSEnabled   bool
   149  	VMDockerAttachStdout bool
   150  	// VMNetworkMode sets the networking mode for the container.
   151  	VMNetworkMode string
   152  
   153  	// ChaincodePull enables/disables force pulling of the base docker image.
   154  	ChaincodePull bool
   155  	// ExternalBuilders represents the builders and launchers for
   156  	// chaincode. The external builder detection processing will iterate over the
   157  	// builders in the order specified below.
   158  	ExternalBuilders []ExternalBuilder
   159  
   160  	// ----- Operations config -----
   161  	// TODO: create separate sub-struct for Operations config.
   162  
   163  	// OperationsListenAddress provides the host and port for the operations server
   164  	OperationsListenAddress string
   165  	// OperationsTLSEnabled enables/disables TLS for operations.
   166  	OperationsTLSEnabled bool
   167  	// OperationsTLSCertFile provides the path to PEM encoded server certificate for
   168  	// the operations server.
   169  	OperationsTLSCertFile string
   170  	// OperationsTLSKeyFile provides the path to PEM encoded server key for the
   171  	// operations server.
   172  	OperationsTLSKeyFile string
   173  	// OperationsTLSClientAuthRequired enables/disables the requirements for client
   174  	// certificate authentication at the TLS layer to access all resource.
   175  	OperationsTLSClientAuthRequired bool
   176  	// OperationsTLSClientRootCAs provides the path to PEM encoded ca certiricates to
   177  	// trust for client authentication.
   178  	OperationsTLSClientRootCAs []string
   179  
   180  	// ----- Metrics config -----
   181  	// TODO: create separate sub-struct for Metrics config.
   182  
   183  	// MetricsProvider provides the categories of metrics providers, which is one of
   184  	// statsd, prometheus, or disabled.
   185  	MetricsProvider string
   186  	// StatsdNetwork indicate the network type used by statsd metrics. (tcp or udp).
   187  	StatsdNetwork string
   188  	// StatsdAaddress provides the address for statsd server.
   189  	StatsdAaddress string
   190  	// StatsdWriteInterval set the time interval at which locally cached counters and
   191  	// gauges are pushed.
   192  	StatsdWriteInterval time.Duration
   193  	// StatsdPrefix provides the prefix that prepended to all emitted statsd metrics.
   194  	StatsdPrefix string
   195  
   196  	// ----- Docker config ------
   197  
   198  	// DockerCert is the path to the PEM encoded TLS client certificate required to access
   199  	// the docker daemon.
   200  	DockerCert string
   201  	// DockerKey is the path to the PEM encoded key required to access the docker daemon.
   202  	DockerKey string
   203  	// DockerCA is the path to the PEM encoded CA certificate for the docker daemon.
   204  	DockerCA string
   205  }
   206  
   207  // GlobalConfig obtains a set of configuration from viper, build and returns
   208  // the config struct.
   209  func GlobalConfig() (*Config, error) {
   210  	c := &Config{}
   211  	if err := c.load(); err != nil {
   212  		return nil, err
   213  	}
   214  	return c, nil
   215  }
   216  
   217  func (c *Config) load() error {
   218  	preeAddress, err := getLocalAddress()
   219  	if err != nil {
   220  		return err
   221  	}
   222  
   223  	configDir := filepath.Dir(viper.ConfigFileUsed())
   224  
   225  	c.PeerAddress = preeAddress
   226  	c.PeerID = viper.GetString("peer.id")
   227  	c.LocalMSPID = viper.GetString("peer.localMspId")
   228  	c.ListenAddress = viper.GetString("peer.listenAddress")
   229  
   230  	c.AuthenticationTimeWindow = viper.GetDuration("peer.authentication.timewindow")
   231  	if c.AuthenticationTimeWindow == 0 {
   232  		defaultTimeWindow := 15 * time.Minute
   233  		logger.Warningf("`peer.authentication.timewindow` not set; defaulting to %s", defaultTimeWindow)
   234  		c.AuthenticationTimeWindow = defaultTimeWindow
   235  	}
   236  
   237  	c.PeerTLSEnabled = viper.GetBool("peer.tls.enabled")
   238  	c.NetworkID = viper.GetString("peer.networkId")
   239  	c.LimitsConcurrencyEndorserService = viper.GetInt("peer.limits.concurrency.endorserService")
   240  	c.LimitsConcurrencyDeliverService = viper.GetInt("peer.limits.concurrency.deliverService")
   241  	c.DiscoveryEnabled = viper.GetBool("peer.discovery.enabled")
   242  	c.ProfileEnabled = viper.GetBool("peer.profile.enabled")
   243  	c.ProfileListenAddress = viper.GetString("peer.profile.listenAddress")
   244  	c.DiscoveryOrgMembersAllowed = viper.GetBool("peer.discovery.orgMembersAllowedAccess")
   245  	c.DiscoveryAuthCacheEnabled = viper.GetBool("peer.discovery.authCacheEnabled")
   246  	c.DiscoveryAuthCacheMaxSize = viper.GetInt("peer.discovery.authCacheMaxSize")
   247  	c.DiscoveryAuthCachePurgeRetentionRatio = viper.GetFloat64("peer.discovery.authCachePurgeRetentionRatio")
   248  	c.ChaincodeListenAddress = viper.GetString("peer.chaincodeListenAddress")
   249  	c.ChaincodeAddress = viper.GetString("peer.chaincodeAddress")
   250  
   251  	c.ValidatorPoolSize = viper.GetInt("peer.validatorPoolSize")
   252  	if c.ValidatorPoolSize <= 0 {
   253  		c.ValidatorPoolSize = runtime.NumCPU()
   254  	}
   255  
   256  	c.DeliverClientKeepaliveOptions = comm.DefaultKeepaliveOptions
   257  	if viper.IsSet("peer.keepalive.deliveryClient.interval") {
   258  		c.DeliverClientKeepaliveOptions.ClientInterval = viper.GetDuration("peer.keepalive.deliveryClient.interval")
   259  	}
   260  	if viper.IsSet("peer.keepalive.deliveryClient.timeout") {
   261  		c.DeliverClientKeepaliveOptions.ClientTimeout = viper.GetDuration("peer.keepalive.deliveryClient.timeout")
   262  	}
   263  
   264  	c.VMEndpoint = viper.GetString("vm.endpoint")
   265  	c.VMDockerTLSEnabled = viper.GetBool("vm.docker.tls.enabled")
   266  	c.VMDockerAttachStdout = viper.GetBool("vm.docker.attachStdout")
   267  
   268  	c.VMNetworkMode = viper.GetString("vm.docker.hostConfig.NetworkMode")
   269  	if c.VMNetworkMode == "" {
   270  		c.VMNetworkMode = "host"
   271  	}
   272  
   273  	c.ChaincodePull = viper.GetBool("chaincode.pull")
   274  	var externalBuilders []ExternalBuilder
   275  	err = viper.UnmarshalKey("chaincode.externalBuilders", &externalBuilders)
   276  	if err != nil {
   277  		return err
   278  	}
   279  	for _, builder := range externalBuilders {
   280  		if builder.Path == "" {
   281  			return fmt.Errorf("invalid external builder configuration, path attribute missing in one or more builders")
   282  		}
   283  		if builder.Name == "" {
   284  			return fmt.Errorf("external builder at path %s has no name attribute", builder.Path)
   285  		}
   286  	}
   287  	c.ExternalBuilders = externalBuilders
   288  
   289  	c.OperationsListenAddress = viper.GetString("operations.listenAddress")
   290  	c.OperationsTLSEnabled = viper.GetBool("operations.tls.enabled")
   291  	c.OperationsTLSCertFile = config.GetPath("operations.tls.cert.file")
   292  	c.OperationsTLSKeyFile = config.GetPath("operations.tls.key.file")
   293  	c.OperationsTLSClientAuthRequired = viper.GetBool("operations.tls.clientAuthRequired")
   294  
   295  	for _, rca := range viper.GetStringSlice("operations.tls.clientRootCAs.files") {
   296  		c.OperationsTLSClientRootCAs = append(c.OperationsTLSClientRootCAs, config.TranslatePath(configDir, rca))
   297  	}
   298  
   299  	c.MetricsProvider = viper.GetString("metrics.provider")
   300  	c.StatsdNetwork = viper.GetString("metrics.statsd.network")
   301  	c.StatsdAaddress = viper.GetString("metrics.statsd.address")
   302  	c.StatsdWriteInterval = viper.GetDuration("metrics.statsd.writeInterval")
   303  	c.StatsdPrefix = viper.GetString("metrics.statsd.prefix")
   304  
   305  	c.DockerCert = config.GetPath("vm.docker.tls.cert.file")
   306  	c.DockerKey = config.GetPath("vm.docker.tls.key.file")
   307  	c.DockerCA = config.GetPath("vm.docker.tls.ca.file")
   308  
   309  	return nil
   310  }
   311  
   312  // getLocalAddress returns the address:port the local peer is operating on.  Affected by env:peer.addressAutoDetect
   313  func getLocalAddress() (string, error) {
   314  	peerAddress := viper.GetString("peer.address")
   315  	if peerAddress == "" {
   316  		return "", fmt.Errorf("peer.address isn't set")
   317  	}
   318  	host, port, err := net.SplitHostPort(peerAddress)
   319  	if err != nil {
   320  		return "", errors.Errorf("peer.address isn't in host:port format: %s", peerAddress)
   321  	}
   322  
   323  	localIP, err := comm.GetLocalIP()
   324  	if err != nil {
   325  		peerLogger.Errorf("local IP address not auto-detectable: %s", err)
   326  		return "", err
   327  	}
   328  	autoDetectedIPAndPort := net.JoinHostPort(localIP, port)
   329  	peerLogger.Info("Auto-detected peer address:", autoDetectedIPAndPort)
   330  	// If host is the IPv4 address "0.0.0.0" or the IPv6 address "::",
   331  	// then fallback to auto-detected address
   332  	if ip := net.ParseIP(host); ip != nil && ip.IsUnspecified() {
   333  		peerLogger.Info("Host is", host, ", falling back to auto-detected address:", autoDetectedIPAndPort)
   334  		return autoDetectedIPAndPort, nil
   335  	}
   336  
   337  	if viper.GetBool("peer.addressAutoDetect") {
   338  		peerLogger.Info("Auto-detect flag is set, returning", autoDetectedIPAndPort)
   339  		return autoDetectedIPAndPort, nil
   340  	}
   341  	peerLogger.Info("Returning", peerAddress)
   342  	return peerAddress, nil
   343  
   344  }
   345  
   346  // GetServerConfig returns the gRPC server configuration for the peer
   347  func GetServerConfig() (comm.ServerConfig, error) {
   348  	serverConfig := comm.ServerConfig{
   349  		ConnectionTimeout: viper.GetDuration("peer.connectiontimeout"),
   350  		SecOpts: comm.SecureOptions{
   351  			UseTLS: viper.GetBool("peer.tls.enabled"),
   352  		},
   353  	}
   354  	if serverConfig.SecOpts.UseTLS {
   355  		// get the certs from the file system
   356  		serverKey, err := ioutil.ReadFile(config.GetPath("peer.tls.key.file"))
   357  		if err != nil {
   358  			return serverConfig, fmt.Errorf("error loading TLS key (%s)", err)
   359  		}
   360  		serverCert, err := ioutil.ReadFile(config.GetPath("peer.tls.cert.file"))
   361  		if err != nil {
   362  			return serverConfig, fmt.Errorf("error loading TLS certificate (%s)", err)
   363  		}
   364  		serverConfig.SecOpts.Certificate = serverCert
   365  		serverConfig.SecOpts.Key = serverKey
   366  		serverConfig.SecOpts.RequireClientCert = viper.GetBool("peer.tls.clientAuthRequired")
   367  		if serverConfig.SecOpts.RequireClientCert {
   368  			var clientRoots [][]byte
   369  			for _, file := range viper.GetStringSlice("peer.tls.clientRootCAs.files") {
   370  				clientRoot, err := ioutil.ReadFile(
   371  					config.TranslatePath(filepath.Dir(viper.ConfigFileUsed()), file))
   372  				if err != nil {
   373  					return serverConfig,
   374  						fmt.Errorf("error loading client root CAs (%s)", err)
   375  				}
   376  				clientRoots = append(clientRoots, clientRoot)
   377  			}
   378  			serverConfig.SecOpts.ClientRootCAs = clientRoots
   379  		}
   380  		// check for root cert
   381  		if config.GetPath("peer.tls.rootcert.file") != "" {
   382  			rootCert, err := ioutil.ReadFile(config.GetPath("peer.tls.rootcert.file"))
   383  			if err != nil {
   384  				return serverConfig, fmt.Errorf("error loading TLS root certificate (%s)", err)
   385  			}
   386  			serverConfig.SecOpts.ServerRootCAs = [][]byte{rootCert}
   387  		}
   388  	}
   389  	// get the default keepalive options
   390  	serverConfig.KaOpts = comm.DefaultKeepaliveOptions
   391  	// check to see if interval is set for the env
   392  	if viper.IsSet("peer.keepalive.interval") {
   393  		serverConfig.KaOpts.ServerInterval = viper.GetDuration("peer.keepalive.interval")
   394  	}
   395  	// check to see if timeout is set for the env
   396  	if viper.IsSet("peer.keepalive.timeout") {
   397  		serverConfig.KaOpts.ServerTimeout = viper.GetDuration("peer.keepalive.timeout")
   398  	}
   399  	// check to see if minInterval is set for the env
   400  	if viper.IsSet("peer.keepalive.minInterval") {
   401  		serverConfig.KaOpts.ServerMinInterval = viper.GetDuration("peer.keepalive.minInterval")
   402  	}
   403  	return serverConfig, nil
   404  }
   405  
   406  // GetClientCertificate returns the TLS certificate to use for gRPC client
   407  // connections
   408  func GetClientCertificate() (tls.Certificate, error) {
   409  	cert := tls.Certificate{}
   410  
   411  	keyPath := viper.GetString("peer.tls.clientKey.file")
   412  	certPath := viper.GetString("peer.tls.clientCert.file")
   413  
   414  	if keyPath != "" || certPath != "" {
   415  		// need both keyPath and certPath to be set
   416  		if keyPath == "" || certPath == "" {
   417  			return cert, errors.New("peer.tls.clientKey.file and " +
   418  				"peer.tls.clientCert.file must both be set or must both be empty")
   419  		}
   420  		keyPath = config.GetPath("peer.tls.clientKey.file")
   421  		certPath = config.GetPath("peer.tls.clientCert.file")
   422  
   423  	} else {
   424  		// use the TLS server keypair
   425  		keyPath = viper.GetString("peer.tls.key.file")
   426  		certPath = viper.GetString("peer.tls.cert.file")
   427  
   428  		if keyPath != "" || certPath != "" {
   429  			// need both keyPath and certPath to be set
   430  			if keyPath == "" || certPath == "" {
   431  				return cert, errors.New("peer.tls.key.file and " +
   432  					"peer.tls.cert.file must both be set or must both be empty")
   433  			}
   434  			keyPath = config.GetPath("peer.tls.key.file")
   435  			certPath = config.GetPath("peer.tls.cert.file")
   436  		} else {
   437  			return cert, errors.New("must set either " +
   438  				"[peer.tls.key.file and peer.tls.cert.file] or " +
   439  				"[peer.tls.clientKey.file and peer.tls.clientCert.file]" +
   440  				"when peer.tls.clientAuthEnabled is set to true")
   441  		}
   442  	}
   443  	// get the keypair from the file system
   444  	clientKey, err := ioutil.ReadFile(keyPath)
   445  	if err != nil {
   446  		return cert, errors.WithMessage(err,
   447  			"error loading client TLS key")
   448  	}
   449  	clientCert, err := ioutil.ReadFile(certPath)
   450  	if err != nil {
   451  		return cert, errors.WithMessage(err,
   452  			"error loading client TLS certificate")
   453  	}
   454  	cert, err = tls.X509KeyPair(clientCert, clientKey)
   455  	if err != nil {
   456  		return cert, errors.WithMessage(err,
   457  			"error parsing client TLS key pair")
   458  	}
   459  	return cert, nil
   460  }